mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 08:29:42 +02:00
fix: tunnel button stuck on "Connecting..." due to settings validation
Root cause: toggleTunnelFromWelcome() sent the entire settings object
back to PUT /api/settings, but the Zod schema uses .strict() which
rejects unknown fields (lastUsedCase, localEchoEnabled, etc.). The PUT
silently failed, so the tunnel never started.
Fix: send only {tunnelEnabled: true/false} instead of the full blob.
Also added polling fallback for tunnel status and server-side re-broadcast
when tunnel is already running.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
# codeman
|
# codeman
|
||||||
|
|
||||||
|
## 0.2.4
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Fix tunnel button not working: settings PUT was rejected by strict Zod validation when sending full settings blob; now sends only `{tunnelEnabled}`. Added polling fallback for tunnel status in case SSE events are missed.
|
||||||
|
|
||||||
## 0.2.3
|
## 0.2.3
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ When user says "COM":
|
|||||||
4. **Sync CLAUDE.md version**: Update the `**Version**` line below to match the new version from `package.json`
|
4. **Sync CLAUDE.md version**: Update the `**Version**` line below to match the new version from `package.json`
|
||||||
5. **Commit and deploy**: `git add -A && git commit -m "chore: version packages" && git push && npm run build && systemctl --user restart codeman-web`
|
5. **Commit and deploy**: `git add -A && git commit -m "chore: version packages" && git push && npm run build && systemctl --user restart codeman-web`
|
||||||
|
|
||||||
**Version**: 0.2.3 (must match `package.json`)
|
**Version**: 0.2.4 (must match `package.json`)
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "codeman",
|
"name": "codeman",
|
||||||
"version": "0.2.3",
|
"version": "0.2.4",
|
||||||
"description": "The missing control plane for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
|
"description": "The missing control plane for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
+31
-4
@@ -8946,17 +8946,16 @@ class CodemanApp {
|
|||||||
const isActive = btn.classList.contains('active');
|
const isActive = btn.classList.contains('active');
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/settings');
|
|
||||||
const current = res.ok ? await res.json() : {};
|
|
||||||
const newEnabled = !isActive;
|
const newEnabled = !isActive;
|
||||||
current.tunnelEnabled = newEnabled;
|
|
||||||
await fetch('/api/settings', {
|
await fetch('/api/settings', {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(current),
|
body: JSON.stringify({ tunnelEnabled: newEnabled }),
|
||||||
});
|
});
|
||||||
if (newEnabled) {
|
if (newEnabled) {
|
||||||
this._showTunnelConnecting();
|
this._showTunnelConnecting();
|
||||||
|
// Poll tunnel status as fallback in case SSE event is missed
|
||||||
|
this._pollTunnelStatus();
|
||||||
} else {
|
} else {
|
||||||
this._dismissTunnelConnecting();
|
this._dismissTunnelConnecting();
|
||||||
this.showToast('Tunnel stopped', 'info');
|
this.showToast('Tunnel stopped', 'info');
|
||||||
@@ -9001,6 +9000,8 @@ class CodemanApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_dismissTunnelConnecting() {
|
_dismissTunnelConnecting() {
|
||||||
|
clearTimeout(this._tunnelPollTimer);
|
||||||
|
this._tunnelPollTimer = null;
|
||||||
const toast = document.getElementById('tunnelConnectingToast');
|
const toast = document.getElementById('tunnelConnectingToast');
|
||||||
if (toast) {
|
if (toast) {
|
||||||
toast.classList.remove('show');
|
toast.classList.remove('show');
|
||||||
@@ -9010,6 +9011,32 @@ class CodemanApp {
|
|||||||
if (btn) btn.classList.remove('connecting');
|
if (btn) btn.classList.remove('connecting');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_pollTunnelStatus(attempt = 0) {
|
||||||
|
if (attempt > 15) return; // give up after ~30s
|
||||||
|
this._tunnelPollTimer = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/tunnel/status');
|
||||||
|
const status = await res.json();
|
||||||
|
if (status.running && status.url) {
|
||||||
|
// Tunnel is up — update UI
|
||||||
|
this._dismissTunnelConnecting();
|
||||||
|
this._updateTunnelUrlDisplay(status.url);
|
||||||
|
const welcomeVisible = document.getElementById('welcomeOverlay')?.classList.contains('visible');
|
||||||
|
if (welcomeVisible) {
|
||||||
|
this._updateWelcomeTunnelBtn(true, status.url, true);
|
||||||
|
this.showToast('Tunnel active', 'success');
|
||||||
|
} else {
|
||||||
|
this._updateWelcomeTunnelBtn(true, status.url);
|
||||||
|
this.showToast(`Tunnel active: ${status.url}`, 'success');
|
||||||
|
this.showTunnelQR();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
this._pollTunnelStatus(attempt + 1);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
_updateWelcomeTunnelBtn(active, url, firstAppear = false) {
|
_updateWelcomeTunnelBtn(active, url, firstAppear = false) {
|
||||||
const btn = document.getElementById('welcomeTunnelBtn');
|
const btn = document.getElementById('welcomeTunnelBtn');
|
||||||
if (btn) {
|
if (btn) {
|
||||||
|
|||||||
Reference in New Issue
Block a user