aicodeman 1.43.0 → 1.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/dist/case-rename.d.ts +65 -0
- package/dist/case-rename.d.ts.map +1 -0
- package/dist/case-rename.js +198 -0
- package/dist/case-rename.js.map +1 -0
- package/dist/cli-agent.d.ts +63 -4
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +233 -19
- package/dist/cli-agent.js.map +1 -1
- package/dist/config/auth-config.d.ts +16 -1
- package/dist/config/auth-config.d.ts.map +1 -1
- package/dist/config/auth-config.js +16 -1
- package/dist/config/auth-config.js.map +1 -1
- package/dist/config/cli-registry/stock.d.ts.map +1 -1
- package/dist/config/cli-registry/stock.js +8 -3
- package/dist/config/cli-registry/stock.js.map +1 -1
- package/dist/config/cli-registry/types.d.ts +2 -2
- package/dist/config/map-limits.d.ts +13 -1
- package/dist/config/map-limits.d.ts.map +1 -1
- package/dist/config/map-limits.js +28 -1
- package/dist/config/map-limits.js.map +1 -1
- package/dist/config-backup.d.ts +279 -0
- package/dist/config-backup.d.ts.map +1 -0
- package/dist/config-backup.js +794 -0
- package/dist/config-backup.js.map +1 -0
- package/dist/cron/cron-service.d.ts +5 -5
- package/dist/cron/cron-service.js +6 -6
- package/dist/cron/cron-service.js.map +1 -1
- package/dist/custom-model-hosts.d.ts +16 -0
- package/dist/custom-model-hosts.d.ts.map +1 -1
- package/dist/custom-model-hosts.js.map +1 -1
- package/dist/hooks-config.d.ts +15 -0
- package/dist/hooks-config.d.ts.map +1 -1
- package/dist/hooks-config.js +35 -0
- package/dist/hooks-config.js.map +1 -1
- package/dist/host-sensors.d.ts +115 -0
- package/dist/host-sensors.d.ts.map +1 -0
- package/dist/host-sensors.js +457 -0
- package/dist/host-sensors.js.map +1 -0
- package/dist/intent-store.d.ts +2 -0
- package/dist/intent-store.d.ts.map +1 -1
- package/dist/intent-store.js +9 -0
- package/dist/intent-store.js.map +1 -1
- package/dist/mux-interface.d.ts +21 -4
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/prompt-cache-keepwarm.d.ts +193 -0
- package/dist/prompt-cache-keepwarm.d.ts.map +1 -0
- package/dist/prompt-cache-keepwarm.js +359 -0
- package/dist/prompt-cache-keepwarm.js.map +1 -0
- package/dist/remote-hosts.d.ts +7 -0
- package/dist/remote-hosts.d.ts.map +1 -1
- package/dist/remote-hosts.js +17 -3
- package/dist/remote-hosts.js.map +1 -1
- package/dist/remote-pane-history.d.ts.map +1 -1
- package/dist/remote-pane-history.js +2 -2
- package/dist/remote-pane-history.js.map +1 -1
- package/dist/respawn-controller.d.ts.map +1 -1
- package/dist/respawn-controller.js +6 -5
- package/dist/respawn-controller.js.map +1 -1
- package/dist/session-auto-ops.d.ts.map +1 -1
- package/dist/session-auto-ops.js +6 -1
- package/dist/session-auto-ops.js.map +1 -1
- package/dist/session-cli-registry-bridge.d.ts +13 -0
- package/dist/session-cli-registry-bridge.d.ts.map +1 -1
- package/dist/session-cli-registry-bridge.js +20 -3
- package/dist/session-cli-registry-bridge.js.map +1 -1
- package/dist/session-submit-verifier.d.ts +19 -0
- package/dist/session-submit-verifier.d.ts.map +1 -1
- package/dist/session-submit-verifier.js +88 -0
- package/dist/session-submit-verifier.js.map +1 -1
- package/dist/session.d.ts +61 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +125 -7
- package/dist/session.js.map +1 -1
- package/dist/state-store.d.ts +10 -0
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js +35 -0
- package/dist/state-store.js.map +1 -1
- package/dist/tab-layout-service.d.ts +33 -0
- package/dist/tab-layout-service.d.ts.map +1 -1
- package/dist/tab-layout-service.js +75 -3
- package/dist/tab-layout-service.js.map +1 -1
- package/dist/tmux-manager.d.ts +57 -11
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +190 -42
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-prefix-table.d.ts +86 -0
- package/dist/tmux-prefix-table.d.ts.map +1 -0
- package/dist/tmux-prefix-table.js +172 -0
- package/dist/tmux-prefix-table.js.map +1 -0
- package/dist/tui/tui-app.d.ts +17 -2
- package/dist/tui/tui-app.d.ts.map +1 -1
- package/dist/tui/tui-app.js +70 -20
- package/dist/tui/tui-app.js.map +1 -1
- package/dist/tui/tui-client.d.ts +93 -5
- package/dist/tui/tui-client.d.ts.map +1 -1
- package/dist/tui/tui-client.js +198 -28
- package/dist/tui/tui-client.js.map +1 -1
- package/dist/types/app-state.d.ts +6 -0
- package/dist/types/app-state.d.ts.map +1 -1
- package/dist/types/app-state.js.map +1 -1
- package/dist/types/session.d.ts +40 -1
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/usage-telemetry.d.ts +15 -0
- package/dist/usage-telemetry.d.ts.map +1 -1
- package/dist/usage-telemetry.js +45 -2
- package/dist/usage-telemetry.js.map +1 -1
- package/dist/utils/copilot-plan-usage.d.ts +46 -0
- package/dist/utils/copilot-plan-usage.d.ts.map +1 -0
- package/dist/utils/copilot-plan-usage.js +95 -0
- package/dist/utils/copilot-plan-usage.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/single-line.d.ts +22 -0
- package/dist/utils/single-line.d.ts.map +1 -0
- package/dist/utils/single-line.js +30 -0
- package/dist/utils/single-line.js.map +1 -0
- package/dist/web/agent-inbox-persistence.d.ts +52 -0
- package/dist/web/agent-inbox-persistence.d.ts.map +1 -0
- package/dist/web/agent-inbox-persistence.js +139 -0
- package/dist/web/agent-inbox-persistence.js.map +1 -0
- package/dist/web/agent-inbox.d.ts +140 -0
- package/dist/web/agent-inbox.d.ts.map +1 -0
- package/dist/web/agent-inbox.js +258 -0
- package/dist/web/agent-inbox.js.map +1 -0
- package/dist/web/approval-inbox.d.ts +6 -0
- package/dist/web/approval-inbox.d.ts.map +1 -1
- package/dist/web/approval-inbox.js +2 -2
- package/dist/web/approval-inbox.js.map +1 -1
- package/dist/web/middleware/auth.d.ts +2 -0
- package/dist/web/middleware/auth.d.ts.map +1 -1
- package/dist/web/middleware/auth.js +145 -52
- package/dist/web/middleware/auth.js.map +1 -1
- package/dist/web/plan-usage-latest.d.ts +2 -1
- package/dist/web/plan-usage-latest.d.ts.map +1 -1
- package/dist/web/plan-usage-latest.js +12 -2
- package/dist/web/plan-usage-latest.js.map +1 -1
- package/dist/web/ports/index.d.ts +1 -0
- package/dist/web/ports/index.d.ts.map +1 -1
- package/dist/web/ports/keep-warm-port.d.ts +9 -0
- package/dist/web/ports/keep-warm-port.d.ts.map +1 -0
- package/dist/web/ports/keep-warm-port.js +6 -0
- package/dist/web/ports/keep-warm-port.js.map +1 -0
- package/dist/web/ports/session-port.d.ts +3 -1
- package/dist/web/ports/session-port.d.ts.map +1 -1
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.42d0abae.js +43 -0
- package/dist/web/public/app.42d0abae.js.br +0 -0
- package/dist/web/public/app.42d0abae.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.e5e588db.js → constants.0b3cdde9.js} +60 -1
- package/dist/web/public/constants.0b3cdde9.js.br +0 -0
- package/dist/web/public/constants.0b3cdde9.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/git-status-ui.js.gz +0 -0
- package/dist/web/public/header-divider.eb2d2463.js +1 -0
- package/dist/web/public/header-divider.eb2d2463.js.br +0 -0
- package/dist/web/public/header-divider.eb2d2463.js.gz +0 -0
- package/dist/web/public/home-sessions.js.gz +0 -0
- package/dist/web/public/host-wake-ui.js.gz +0 -0
- package/dist/web/public/i18n.1e68220d.js +2 -0
- package/dist/web/public/i18n.1e68220d.js.br +0 -0
- package/dist/web/public/i18n.1e68220d.js.gz +0 -0
- package/dist/web/public/image-input.941386e7.js.gz +0 -0
- package/dist/web/public/index.html +162 -14
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
- package/dist/web/public/{keyboard-accessory.c283e827.js → keyboard-accessory.4e8b20ac.js} +23 -5
- package/dist/web/public/keyboard-accessory.4e8b20ac.js.br +0 -0
- package/dist/web/public/keyboard-accessory.4e8b20ac.js.gz +0 -0
- package/dist/web/public/{mobile-handlers.0ef9c311.js → mobile-handlers.1c0cbcd8.js} +50 -16
- package/dist/web/public/mobile-handlers.1c0cbcd8.js.br +0 -0
- package/dist/web/public/mobile-handlers.1c0cbcd8.js.gz +0 -0
- package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.12fb18de.css +1 -0
- package/dist/web/public/mobile.12fb18de.css.br +0 -0
- package/dist/web/public/mobile.12fb18de.css.gz +0 -0
- package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.b7bfb9da.js → panels-ui.78c3defb.js} +19 -13
- package/dist/web/public/panels-ui.78c3defb.js.br +0 -0
- package/dist/web/public/panels-ui.78c3defb.js.gz +0 -0
- package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
- package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
- package/dist/web/public/readmymind-ui.js.gz +0 -0
- package/dist/web/public/reboot-restore-ui.js.gz +0 -0
- package/dist/web/public/respawn-ui.d7adcf6a.js.gz +0 -0
- package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/{session-ui.be076c88.js → session-ui.ea6b1994.js} +33 -27
- package/dist/web/public/session-ui.ea6b1994.js.br +0 -0
- package/dist/web/public/session-ui.ea6b1994.js.gz +0 -0
- package/dist/web/public/settings-ui.085dd202.js +106 -0
- package/dist/web/public/settings-ui.085dd202.js.br +0 -0
- package/dist/web/public/settings-ui.085dd202.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview.75c751de.js.gz +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
- package/dist/web/public/styles.3b59b41d.css +1 -0
- package/dist/web/public/styles.3b59b41d.css.br +0 -0
- package/dist/web/public/styles.3b59b41d.css.gz +0 -0
- package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
- package/dist/web/public/sw.js +2 -2
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/tab-layout-browser.2d6189f7.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.7157e0bd.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
- package/dist/web/public/terminal-split.js.gz +0 -0
- package/dist/web/public/terminal-tile.js +49 -23
- package/dist/web/public/terminal-tile.js.br +0 -0
- package/dist/web/public/terminal-tile.js.gz +0 -0
- package/dist/web/public/terminal-ui.fb2a27f5.js +2 -0
- package/dist/web/public/terminal-ui.fb2a27f5.js.br +0 -0
- package/dist/web/public/terminal-ui.fb2a27f5.js.gz +0 -0
- package/dist/web/public/tile-grid.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
- package/dist/web/public/vendor/fflate.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/public/whats-new.json +9 -9
- package/dist/web/route-helpers.d.ts +57 -6
- package/dist/web/route-helpers.d.ts.map +1 -1
- package/dist/web/route-helpers.js +72 -4
- package/dist/web/route-helpers.js.map +1 -1
- package/dist/web/routes/admin-routes.d.ts +2 -1
- package/dist/web/routes/admin-routes.d.ts.map +1 -1
- package/dist/web/routes/admin-routes.js +3 -0
- package/dist/web/routes/admin-routes.js.map +1 -1
- package/dist/web/routes/case-routes.d.ts +2 -2
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +263 -4
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/config-backup-routes.d.ts +54 -0
- package/dist/web/routes/config-backup-routes.d.ts.map +1 -0
- package/dist/web/routes/config-backup-routes.js +268 -0
- package/dist/web/routes/config-backup-routes.js.map +1 -0
- package/dist/web/routes/custom-model-routes.d.ts +17 -1
- package/dist/web/routes/custom-model-routes.d.ts.map +1 -1
- package/dist/web/routes/custom-model-routes.js +118 -13
- package/dist/web/routes/custom-model-routes.js.map +1 -1
- package/dist/web/routes/hook-event-routes.d.ts.map +1 -1
- package/dist/web/routes/hook-event-routes.js +26 -5
- package/dist/web/routes/hook-event-routes.js.map +1 -1
- package/dist/web/routes/inbox-routes.d.ts +43 -0
- package/dist/web/routes/inbox-routes.d.ts.map +1 -0
- package/dist/web/routes/inbox-routes.js +148 -0
- package/dist/web/routes/inbox-routes.js.map +1 -0
- package/dist/web/routes/index.d.ts +2 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +2 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/ralph-routes.js +2 -2
- package/dist/web/routes/ralph-routes.js.map +1 -1
- package/dist/web/routes/reboot-restore-routes.d.ts.map +1 -1
- package/dist/web/routes/reboot-restore-routes.js +2 -0
- package/dist/web/routes/reboot-restore-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts +2 -2
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +110 -61
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/status-telemetry-routes.d.ts +2 -2
- package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
- package/dist/web/routes/status-telemetry-routes.js +8 -4
- package/dist/web/routes/status-telemetry-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts +2 -2
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +30 -2
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/routes/webview-routes.d.ts.map +1 -1
- package/dist/web/routes/webview-routes.js +1 -0
- package/dist/web/routes/webview-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +45 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +79 -2
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/sensitive-path.d.ts.map +1 -1
- package/dist/web/sensitive-path.js +2 -0
- package/dist/web/sensitive-path.js.map +1 -1
- package/dist/web/server.d.ts +14 -2
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +176 -7
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-listener-wiring.d.ts +6 -0
- package/dist/web/session-listener-wiring.d.ts.map +1 -1
- package/dist/web/session-listener-wiring.js +10 -3
- package/dist/web/session-listener-wiring.js.map +1 -1
- package/dist/web/session-wait-registry.d.ts +48 -4
- package/dist/web/session-wait-registry.d.ts.map +1 -1
- package/dist/web/session-wait-registry.js +144 -6
- package/dist/web/session-wait-registry.js.map +1 -1
- package/dist/web/sse-events.d.ts +9 -3
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +9 -3
- package/dist/web/sse-events.js.map +1 -1
- package/dist/web/webview-proxy.d.ts +8 -3
- package/dist/web/webview-proxy.d.ts.map +1 -1
- package/dist/web/webview-proxy.js +23 -5
- package/dist/web/webview-proxy.js.map +1 -1
- package/package.json +1 -1
- package/skills/codeman/SKILL.md +27 -13
- package/skills/codeman/preamble.sh +15 -5
- package/skills/codeman/reference/endpoints.md +32 -10
- package/skills/codeman/reference/recipes.md +1 -1
- package/skills/codeman/reference/verbs.md +68 -13
- package/dist/web/public/app.ae50029a.js +0 -43
- package/dist/web/public/app.ae50029a.js.br +0 -0
- package/dist/web/public/app.ae50029a.js.gz +0 -0
- package/dist/web/public/constants.e5e588db.js.br +0 -0
- package/dist/web/public/constants.e5e588db.js.gz +0 -0
- package/dist/web/public/i18n.8f291f06.js +0 -2
- package/dist/web/public/i18n.8f291f06.js.br +0 -0
- package/dist/web/public/i18n.8f291f06.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.c283e827.js.br +0 -0
- package/dist/web/public/keyboard-accessory.c283e827.js.gz +0 -0
- package/dist/web/public/mobile-handlers.0ef9c311.js.br +0 -0
- package/dist/web/public/mobile-handlers.0ef9c311.js.gz +0 -0
- package/dist/web/public/mobile.13514863.css +0 -1
- package/dist/web/public/mobile.13514863.css.br +0 -0
- package/dist/web/public/mobile.13514863.css.gz +0 -0
- package/dist/web/public/panels-ui.b7bfb9da.js.br +0 -0
- package/dist/web/public/panels-ui.b7bfb9da.js.gz +0 -0
- package/dist/web/public/session-ui.be076c88.js.br +0 -0
- package/dist/web/public/session-ui.be076c88.js.gz +0 -0
- package/dist/web/public/settings-ui.fc4cc50a.js +0 -97
- package/dist/web/public/settings-ui.fc4cc50a.js.br +0 -0
- package/dist/web/public/settings-ui.fc4cc50a.js.gz +0 -0
- package/dist/web/public/styles.dcfa907f.css +0 -1
- package/dist/web/public/styles.dcfa907f.css.br +0 -0
- package/dist/web/public/styles.dcfa907f.css.gz +0 -0
- package/dist/web/public/terminal-ui.ae01a7d1.js +0 -2
- package/dist/web/public/terminal-ui.ae01a7d1.js.br +0 -0
- package/dist/web/public/terminal-ui.ae01a7d1.js.gz +0 -0
package/dist/web/server.js
CHANGED
|
@@ -44,6 +44,7 @@ import { looksLikeHostReboot, newestPersistedActivity, planRebootRestore } from
|
|
|
44
44
|
import { rebootRestoreRegistry } from './reboot-restore-registry.js';
|
|
45
45
|
import { dataPath, getDataDir, CODEMAN_INSTANCE } from '../config/instance.js';
|
|
46
46
|
import { WebhookNotifier, readWebhookConfig } from '../webhook-notify.js';
|
|
47
|
+
import { stopHostSensors } from '../host-sensors.js';
|
|
47
48
|
import { webviewFetch } from './webview-egress.js';
|
|
48
49
|
import { readRemoteHosts, rehydrateRemoteHostFields } from '../remote-hosts.js';
|
|
49
50
|
import { normalizeBasePath, stripBasePath, joinBasePath } from '../config/base-path.js';
|
|
@@ -77,7 +78,7 @@ import { createRequire } from 'node:module';
|
|
|
77
78
|
import { RunSummaryTracker } from '../run-summary.js';
|
|
78
79
|
import { OrchestratorLoop } from '../orchestrator-loop.js';
|
|
79
80
|
import { getLifecycleLog } from '../session-lifecycle-log.js';
|
|
80
|
-
import { applyWorkspaceHooks, pruneAgentSessionPreambles, removeAgentSessionPreamble } from '../hooks-config.js';
|
|
81
|
+
import { applyWorkspaceHooks, pruneAgentSessionPreambles, readKeepWarmSettingsOrThrow, readPlanUsageTelemetryEnabled, removeAgentSessionPreamble, } from '../hooks-config.js';
|
|
81
82
|
import { PushSubscriptionStore } from '../push-store.js';
|
|
82
83
|
import webpush from 'web-push';
|
|
83
84
|
import { SseStreamManager } from './sse-stream-manager.js';
|
|
@@ -86,9 +87,14 @@ import { deriveWebviewSseHint } from './webview-sse.js';
|
|
|
86
87
|
import { createSessionListeners, attachSessionListeners, detachSessionListeners, } from './session-listener-wiring.js';
|
|
87
88
|
import { sessionWaits } from './session-wait-registry.js';
|
|
88
89
|
import { intentStore } from '../intent-store.js';
|
|
90
|
+
import { agentInbox } from './agent-inbox.js';
|
|
91
|
+
import { agentInboxPersistence } from './agent-inbox-persistence.js';
|
|
89
92
|
import { AI_CHECK_MODEL } from '../config/ai-defaults.js';
|
|
90
93
|
import { approvalInbox } from './approval-inbox.js';
|
|
91
94
|
import { SCREEN_BLOCKED_SOURCE } from './screen-blocked.js';
|
|
95
|
+
import { PromptCacheKeepWarmService } from '../prompt-cache-keepwarm.js';
|
|
96
|
+
import { composerIsEmpty, plainFrame } from '../session-submit-verifier.js';
|
|
97
|
+
import { detectUsageLimitPause } from '../usage-limit-patterns.js';
|
|
92
98
|
import { stopDeepSeekWeb } from '../deepseek-web-server.js';
|
|
93
99
|
import { wireRespawnListeners, setupTimedRespawn, restoreRespawnController, saveRespawnConfig, } from './respawn-event-wiring.js';
|
|
94
100
|
import { reconcileUpdateOnBoot } from './self-update.js';
|
|
@@ -114,23 +120,31 @@ import { MAX_PASTE_IMAGE_BYTES } from '../config/buffer-limits.js';
|
|
|
114
120
|
import { resolveTerminalHistoryConfig } from '../config/terminal-history.js';
|
|
115
121
|
import { SseEvent } from './sse-events.js';
|
|
116
122
|
import { MAX_ROUTE_PARAM_LENGTH } from './route-helpers.js';
|
|
117
|
-
import { getLatestPlanUsage, setLatestCodexPlanUsage } from './plan-usage-latest.js';
|
|
123
|
+
import { getLatestPlanUsage, setLatestCodexPlanUsage, setLatestCopilotPlanUsage } from './plan-usage-latest.js';
|
|
118
124
|
import { telemetrySignature } from '../usage-telemetry.js';
|
|
119
125
|
import { readCodexPlanUsage, resolveCodexBinaryPath } from '../utils/codex-cli-resolver.js';
|
|
126
|
+
import { isCopilotAvailable } from '../utils/copilot-cli-resolver.js';
|
|
127
|
+
import { readCopilotPlanUsage, readCopilotPlanUsageIfEnabled, resolveCopilotToken, } from '../utils/copilot-plan-usage.js';
|
|
120
128
|
import { registerAuthMiddleware, registerSecurityHeaders, registerHostGuard, isLostWebviewRootFrame, sendLostWebviewFramePage, } from './middleware/auth.js';
|
|
121
129
|
import { isMultiUserMode } from '../config/multiuser.js';
|
|
122
130
|
import { bootstrapInitialAdmin, hasUsers, resolveClaudeModeForUsername } from '../user-store.js';
|
|
123
131
|
import { installRouteErrorHandler } from './route-error-handler.js';
|
|
124
132
|
import { isExplicitlyEnabled, isLoopbackBindHost, buildHostPolicy } from './network-auth-policy.js';
|
|
125
|
-
import { registerPushRoutes, registerTeamRoutes, registerMuxRoutes, registerFileRoutes, registerHtmlViewRoutes, registerScheduledRoutes, registerHookEventRoutes, registerApprovalRoutes, registerRebootRestoreRoutes, registerReadMyMindRoutes, registerGitStatusRoutes, registerStatusTelemetryRoutes, registerSystemRoutes, registerCaseRoutes, registerWorktreeRoutes, registerSessionRoutes, registerRespawnRoutes, registerRalphRoutes, registerPlanRoutes, registerClipboardRoutes, registerSearchRoutes, registerOrchestratorRoutes, registerCronRoutes, registerMeRoutes, registerAdminRoutes, registerWsRoutes, registerVoiceRoutes, registerWebviewRoutes, registerTabLayoutRoutes, registerMcpSyncRoutes, registerWebhookRoutes, registerDoctorRoutes, registerCustomModelRoutes, refreshAllCustomModelHosts, readCustomModelEndpointsEnabled, closeAllLlamaSwapLogTails, detectCustomModelSwapDisplacements, pruneIdleLlamaSwapLogTails, tryWebviewRefererFallback, registerCliRegistryRoutes, } from './routes/index.js';
|
|
133
|
+
import { registerPushRoutes, registerTeamRoutes, registerMuxRoutes, registerFileRoutes, registerHtmlViewRoutes, registerScheduledRoutes, registerHookEventRoutes, registerApprovalRoutes, registerInboxRoutes, registerRebootRestoreRoutes, registerReadMyMindRoutes, registerGitStatusRoutes, registerStatusTelemetryRoutes, registerSystemRoutes, registerCaseRoutes, registerWorktreeRoutes, registerSessionRoutes, registerRespawnRoutes, registerRalphRoutes, registerPlanRoutes, registerClipboardRoutes, registerSearchRoutes, registerOrchestratorRoutes, registerCronRoutes, registerMeRoutes, registerAdminRoutes, registerWsRoutes, registerVoiceRoutes, registerWebviewRoutes, registerTabLayoutRoutes, registerMcpSyncRoutes, registerConfigBackupRoutes, runConfigBackup, registerWebhookRoutes, registerDoctorRoutes, registerCustomModelRoutes, refreshAllCustomModelHosts, readCustomModelEndpointsEnabled, closeAllLlamaSwapLogTails, detectCustomModelSwapDisplacements, pruneIdleLlamaSwapLogTails, tryWebviewRefererFallback, registerCliRegistryRoutes, } from './routes/index.js';
|
|
126
134
|
import { isLostWebviewFrameNavigation } from './webview-proxy.js';
|
|
127
135
|
import { CronService } from '../cron/cron-service.js';
|
|
136
|
+
import { caseSessionSettled } from '../case-rename.js';
|
|
128
137
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
129
138
|
// Bounded, predictable shape for SSE client identifiers: alphanumerics, `_`, `-`.
|
|
130
139
|
// Length range covers crypto.randomUUID() (36 chars) plus any short stable IDs,
|
|
131
140
|
// while capping growth of `sseClientsById` and blocking pathological inputs.
|
|
132
141
|
const SSE_CLIENT_ID_RE = /^[A-Za-z0-9_-]{8,64}$/;
|
|
133
142
|
const CODEX_USAGE_POLL_INTERVAL_MS = 5 * 60_000;
|
|
143
|
+
// Copilot's quota is read over the network from GitHub, and a monthly figure moves slowly.
|
|
144
|
+
const COPILOT_USAGE_POLL_INTERVAL_MS = 10 * 60_000;
|
|
145
|
+
// Config backups: one cheap read-and-hash of a handful of small files; a snapshot is written only when
|
|
146
|
+
// their content changed since the latest one, so this is one entry per real change, not per tick.
|
|
147
|
+
const CONFIG_BACKUP_INTERVAL_MS = 5 * 60_000;
|
|
134
148
|
const CUSTOM_MODEL_REDISCOVER_INTERVAL_MS = 5 * 60_000;
|
|
135
149
|
// Much shorter than the model-LIST refresh above on purpose: this catches an actual
|
|
136
150
|
// eviction (a session's model no longer loaded, silently swapped out by another
|
|
@@ -196,6 +210,50 @@ export class WebServer extends EventEmitter {
|
|
|
196
210
|
_customModelDisplacedNotified = new Set();
|
|
197
211
|
/** Cron service (assigned in setupRoutes). */
|
|
198
212
|
cronService;
|
|
213
|
+
// Prompt-cache keep-warm (prompt-cache-keepwarm.ts): the rulebook and timers live in the
|
|
214
|
+
// service; the server only lends it the facts a decision needs. A field initializer, not a
|
|
215
|
+
// setupRoutes() step: cleanup and listener wiring reach it on servers that never set up routes
|
|
216
|
+
// (the pane-exit and discard tests build a bare WebServer), and every port reads `this.*` lazily.
|
|
217
|
+
keepWarmService = new PromptCacheKeepWarmService({
|
|
218
|
+
getSession: (id) => this.sessions.get(id),
|
|
219
|
+
sessionIds: () => this.sessions.keys(),
|
|
220
|
+
readSettings: readKeepWarmSettingsOrThrow,
|
|
221
|
+
isAutomationActive: (id) => {
|
|
222
|
+
const session = this.sessions.get(id);
|
|
223
|
+
if (!session)
|
|
224
|
+
return true;
|
|
225
|
+
return ((this.respawnControllers.get(id)?.isRunning ?? false) ||
|
|
226
|
+
session.ralphTracker.loopState.active ||
|
|
227
|
+
session.currentTaskId !== null ||
|
|
228
|
+
sessionWaits.signalWaiterCount(id) > 0);
|
|
229
|
+
},
|
|
230
|
+
hasPendingApproval: (id) => approvalInbox.listPending().some((item) => item.sessionId === id && item.kind !== 'idle'),
|
|
231
|
+
readPane: (view) => {
|
|
232
|
+
const session = view;
|
|
233
|
+
const muxName = session.muxName;
|
|
234
|
+
// `capture-pane -p -e`: the visible frame with its styles, which is how `composerIsEmpty()`
|
|
235
|
+
// tells Claude Code's faint prompt suggestion from a typed draft.
|
|
236
|
+
const text = muxName ? (this.mux.capturePaneText?.(muxName, undefined, { styles: true }) ?? null) : null;
|
|
237
|
+
if (!text)
|
|
238
|
+
return { composerEmpty: null, limitShown: false };
|
|
239
|
+
const limit = detectUsageLimitPause(plainFrame(text));
|
|
240
|
+
return {
|
|
241
|
+
composerEmpty: composerIsEmpty(text, getCli(session.mode)?.capabilities.workDetect?.promptGlyph ?? '') ?? null,
|
|
242
|
+
limitShown: limit !== null && limit.resetAt > Date.now(),
|
|
243
|
+
};
|
|
244
|
+
},
|
|
245
|
+
publish: (id, status) => {
|
|
246
|
+
const session = this.sessions.get(id);
|
|
247
|
+
if (!session?.setKeepWarmStatus(status))
|
|
248
|
+
return;
|
|
249
|
+
this.broadcast(SseEvent.SessionKeepWarm, { sessionId: id, ...status });
|
|
250
|
+
// Change-only, so one line per transition names whichever guard holds it now.
|
|
251
|
+
if (status.state !== 'off' || status.reason !== 'off') {
|
|
252
|
+
console.log(`[KeepWarm] ${id.slice(0, 8)}: ${status.state}${status.reason ? ` (${status.reason})` : ''}`);
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
log: (message) => console.log(message),
|
|
256
|
+
});
|
|
199
257
|
/**
|
|
200
258
|
* Wake-on-LAN registry, returned by `registerSessionRoutes`. Held for its LIFETIME
|
|
201
259
|
* only — `drop()` on session cleanup, `stop()` on shutdown. Waking from here would
|
|
@@ -232,6 +290,8 @@ export class WebServer extends EventEmitter {
|
|
|
232
290
|
lastRecordedTokens = new Map();
|
|
233
291
|
codexUsageRefreshInFlight = false;
|
|
234
292
|
lastCodexUsageSignature = null;
|
|
293
|
+
copilotUsageRefreshInFlight = false;
|
|
294
|
+
lastCopilotUsageSignature = null;
|
|
235
295
|
// Server startup time for respawn grace period calculation
|
|
236
296
|
serverStartTime = Date.now();
|
|
237
297
|
// Pending respawn start timers (for cleanup on shutdown)
|
|
@@ -249,6 +309,7 @@ export class WebServer extends EventEmitter {
|
|
|
249
309
|
qrAuthFailures = null;
|
|
250
310
|
hookSecretFailures = null;
|
|
251
311
|
userFailures = null;
|
|
312
|
+
userIpFailures = null;
|
|
252
313
|
pushStore = new PushSubscriptionStore();
|
|
253
314
|
/** ntfy / Slack / Discord / generic webhook for the push events; config in webhook.json (0600). */
|
|
254
315
|
webhookNotifier = new WebhookNotifier(() => readWebhookConfig(getDataDir()), webviewFetch);
|
|
@@ -315,6 +376,7 @@ export class WebServer extends EventEmitter {
|
|
|
315
376
|
// Fresh, not the 2s cache: the settings PUT does not invalidate it, and a
|
|
316
377
|
// flip must apply to the very next spawn.
|
|
317
378
|
childrenFollowParent: async () => (await this.readSettings(true)).spawnedTabsFollowParent === true,
|
|
379
|
+
caseDefaults: this.store,
|
|
318
380
|
});
|
|
319
381
|
if (this.testMode)
|
|
320
382
|
this.tabLayouts.markRestorationSkipped();
|
|
@@ -551,12 +613,14 @@ export class WebServer extends EventEmitter {
|
|
|
551
613
|
/**
|
|
552
614
|
* Add a tentative session only after its owner layout accepts the creation.
|
|
553
615
|
* The session enters the live map inside the layout service's owner lock, so
|
|
554
|
-
* the placement it asked for (`tabGroupId
|
|
555
|
-
* that first records it.
|
|
616
|
+
* the placement it asked for (`tabGroupId`, else its case's default group)
|
|
617
|
+
* lands in the same versioned write that first records it.
|
|
556
618
|
*/
|
|
557
619
|
async registerSessionWithLayout(session, placement) {
|
|
558
620
|
return this.tabLayouts.sessionCreated(ownerLayoutKey(session.owner), session.id, placement, () => {
|
|
559
621
|
this.sessions.set(session.id, session);
|
|
622
|
+
// In the map now, so a case rename lists (and on request stops) it instead of refusing the move.
|
|
623
|
+
caseSessionSettled(session);
|
|
560
624
|
return () => {
|
|
561
625
|
if (this.sessions.get(session.id) === session)
|
|
562
626
|
this.sessions.delete(session.id);
|
|
@@ -573,6 +637,7 @@ export class WebServer extends EventEmitter {
|
|
|
573
637
|
return {
|
|
574
638
|
// SessionPort
|
|
575
639
|
sessions: this.sessions,
|
|
640
|
+
keepWarm: this.keepWarmService,
|
|
576
641
|
addSession: this.registerSessionWithLayout.bind(this),
|
|
577
642
|
tabLayouts: this.tabLayouts,
|
|
578
643
|
cleanupSession: this.cleanupSession.bind(this),
|
|
@@ -709,6 +774,7 @@ export class WebServer extends EventEmitter {
|
|
|
709
774
|
this.qrAuthFailures = authState.qrAuthFailures;
|
|
710
775
|
this.hookSecretFailures = authState.hookSecretFailures;
|
|
711
776
|
this.userFailures = authState.userFailures;
|
|
777
|
+
this.userIpFailures = authState.userIpFailures;
|
|
712
778
|
}
|
|
713
779
|
// WebSocket support (terminal I/O — low-latency bidirectional channel)
|
|
714
780
|
await this.app.register(fastifyWebsocket);
|
|
@@ -972,6 +1038,7 @@ export class WebServer extends EventEmitter {
|
|
|
972
1038
|
registerScheduledRoutes(this.app, ctx);
|
|
973
1039
|
registerHookEventRoutes(this.app, ctx);
|
|
974
1040
|
registerApprovalRoutes(this.app, ctx);
|
|
1041
|
+
registerInboxRoutes(this.app, ctx);
|
|
975
1042
|
registerRebootRestoreRoutes(this.app, ctx);
|
|
976
1043
|
registerReadMyMindRoutes(this.app, ctx);
|
|
977
1044
|
registerGitStatusRoutes(this.app, ctx);
|
|
@@ -993,6 +1060,7 @@ export class WebServer extends EventEmitter {
|
|
|
993
1060
|
registerWebviewRoutes(this.app, ctx, this.basePath);
|
|
994
1061
|
registerTabLayoutRoutes(this.app, ctx);
|
|
995
1062
|
registerMcpSyncRoutes(this.app);
|
|
1063
|
+
registerConfigBackupRoutes(this.app);
|
|
996
1064
|
registerWebhookRoutes(this.app, {
|
|
997
1065
|
notifier: this.webhookNotifier,
|
|
998
1066
|
configDir: getDataDir(),
|
|
@@ -1203,6 +1271,7 @@ export class WebServer extends EventEmitter {
|
|
|
1203
1271
|
}
|
|
1204
1272
|
async _doCleanupSession(sessionId, killMux, reason, coordinateLayout = true) {
|
|
1205
1273
|
const session = this.sessions.get(sessionId);
|
|
1274
|
+
this.keepWarmService.remove(sessionId);
|
|
1206
1275
|
const pinned = session?.pinned === true || this.store.getSession(sessionId)?.pinned === true;
|
|
1207
1276
|
if (coordinateLayout && session && killMux && !pinned) {
|
|
1208
1277
|
return this.tabLayouts.runSessionDeletion([{ id: sessionId, owner: session.owner }], () => this._doCleanupSession(sessionId, killMux, reason, false));
|
|
@@ -1378,6 +1447,12 @@ export class WebServer extends EventEmitter {
|
|
|
1378
1447
|
// The 'exit' here is NOT redundant with the PTY-exit listener: listeners are
|
|
1379
1448
|
// detached a few lines above, before `session.stop()`, so on a delete the
|
|
1380
1449
|
// session's own exit event never reaches the registry.
|
|
1450
|
+
// The agent inbox goes with a DELETED session only. killMux=false is shutdown or
|
|
1451
|
+
// detach: mail posted before that is still there when the session is re-attached
|
|
1452
|
+
// (while detached, the routes answer 404, so nothing new arrives). Before
|
|
1453
|
+
// cancelAll, so a reader released by it reads an empty inbox.
|
|
1454
|
+
if (killMux)
|
|
1455
|
+
agentInbox.drop(sessionId);
|
|
1381
1456
|
sessionWaits.notifySignal(sessionId, 'exit');
|
|
1382
1457
|
sessionWaits.cancelAll(sessionId);
|
|
1383
1458
|
approvalInbox.resolveForSession(sessionId, 'session_ended');
|
|
@@ -1625,6 +1700,9 @@ export class WebServer extends EventEmitter {
|
|
|
1625
1700
|
});
|
|
1626
1701
|
}
|
|
1627
1702
|
async setupSessionListeners(session) {
|
|
1703
|
+
// First keep-warm evaluation: publishes the effective opt-in for the Session Options
|
|
1704
|
+
// readout even before any cache report arrives.
|
|
1705
|
+
void this.keepWarmService.schedule(session.id);
|
|
1628
1706
|
// Idempotent: the wiring exit handler detaches ALL listeners on every PTY exit
|
|
1629
1707
|
// (removeSessionListenerRefs), so the re-attach routes (/interactive,
|
|
1630
1708
|
// /interactive-respawn, /shell) call this again to restore observability
|
|
@@ -1653,6 +1731,7 @@ export class WebServer extends EventEmitter {
|
|
|
1653
1731
|
buildSessionListenerDeps() {
|
|
1654
1732
|
return {
|
|
1655
1733
|
broadcast: this.broadcast.bind(this),
|
|
1734
|
+
keepWarmSchedule: (id) => void this.keepWarmService.schedule(id),
|
|
1656
1735
|
batchTerminalData: this.batchTerminalData.bind(this),
|
|
1657
1736
|
batchTaskUpdate: this.batchTaskUpdate.bind(this),
|
|
1658
1737
|
broadcastSessionStateDebounced: this.broadcastSessionStateDebounced.bind(this),
|
|
@@ -1849,6 +1928,12 @@ export class WebServer extends EventEmitter {
|
|
|
1849
1928
|
const settings = await this.readSettings();
|
|
1850
1929
|
return settings.agentSkillEnabled === true;
|
|
1851
1930
|
}
|
|
1931
|
+
// Whether pending agent-inbox mail is snapshotted to disk (synced
|
|
1932
|
+
// `agentInboxPersistEnabled` setting). Default OFF: an absent key writes nothing.
|
|
1933
|
+
async getAgentInboxPersistEnabled() {
|
|
1934
|
+
const settings = await this.readSettings();
|
|
1935
|
+
return settings.agentInboxPersistEnabled === true;
|
|
1936
|
+
}
|
|
1852
1937
|
// Whether a Claude session installs Codeman's hooks block into its workspace
|
|
1853
1938
|
// (synced `workspaceHooksEnabled` setting). Default ON — an absent key means a
|
|
1854
1939
|
// user who has never seen this setting, and OFF for them would mean no tab
|
|
@@ -2552,6 +2637,36 @@ export class WebServer extends EventEmitter {
|
|
|
2552
2637
|
this.codexUsageRefreshInFlight = false;
|
|
2553
2638
|
}
|
|
2554
2639
|
}
|
|
2640
|
+
/**
|
|
2641
|
+
* Copilot's monthly premium-request quota for the shared plan-usage chip. Host-scoped and read-only
|
|
2642
|
+
* like the Codex poll, and additionally gated by `readCopilotPlanUsageIfEnabled()`: only while
|
|
2643
|
+
* plan-usage display is on (so turning the chip off also stops the outbound request), only with a
|
|
2644
|
+
* sign-in token and only with the Copilot CLI installed; nothing is sent to GitHub otherwise.
|
|
2645
|
+
*/
|
|
2646
|
+
async refreshCopilotPlanUsage() {
|
|
2647
|
+
if (this.copilotUsageRefreshInFlight)
|
|
2648
|
+
return;
|
|
2649
|
+
this.copilotUsageRefreshInFlight = true;
|
|
2650
|
+
try {
|
|
2651
|
+
// A null clears the row, so switching the chip off also takes the last quota off the page.
|
|
2652
|
+
const usage = await readCopilotPlanUsageIfEnabled({
|
|
2653
|
+
planUsageEnabled: readPlanUsageTelemetryEnabled,
|
|
2654
|
+
resolveToken: () => resolveCopilotToken(),
|
|
2655
|
+
copilotInstalled: isCopilotAvailable,
|
|
2656
|
+
read: (token) => readCopilotPlanUsage({ token }),
|
|
2657
|
+
});
|
|
2658
|
+
const signature = usage ? telemetrySignature(usage) : '';
|
|
2659
|
+
if (signature === this.lastCopilotUsageSignature)
|
|
2660
|
+
return;
|
|
2661
|
+
this.lastCopilotUsageSignature = signature;
|
|
2662
|
+
const snapshot = setLatestCopilotPlanUsage(usage);
|
|
2663
|
+
this.cachedLightState = null;
|
|
2664
|
+
this.broadcast(SseEvent.SessionStatusTelemetry, snapshot);
|
|
2665
|
+
}
|
|
2666
|
+
finally {
|
|
2667
|
+
this.copilotUsageRefreshInFlight = false;
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2555
2670
|
async start() {
|
|
2556
2671
|
// Multi-user first boot: create the initial admin from CODEMAN_USERNAME/PASSWORD
|
|
2557
2672
|
// if there are no users yet, else refuse to start (there would be no way in).
|
|
@@ -2580,6 +2695,11 @@ export class WebServer extends EventEmitter {
|
|
|
2580
2695
|
// This prevents race conditions where clients connect before state is ready
|
|
2581
2696
|
// CRITICAL: Skip in test mode to prevent tests from picking up user sessions
|
|
2582
2697
|
if (!this.testMode) {
|
|
2698
|
+
// Pending agent mail first (opt-in snapshot), so it is in place before any
|
|
2699
|
+
// route can answer. Pruned against the restored sessions in finalizeRestoredState.
|
|
2700
|
+
const restoredMail = agentInboxPersistence.load(await this.getAgentInboxPersistEnabled());
|
|
2701
|
+
if (restoredMail > 0)
|
|
2702
|
+
console.log(`[AgentInbox] restored ${restoredMail} pending message(s)`);
|
|
2583
2703
|
const restored = await this.restoreMuxSessions();
|
|
2584
2704
|
await this.finalizeRestoredState(restored);
|
|
2585
2705
|
// Instance-scoped reaper: after restore, `docker rm -f` managed containers of
|
|
@@ -2683,6 +2803,14 @@ export class WebServer extends EventEmitter {
|
|
|
2683
2803
|
// Ensure the COD-54 hook secret exists on disk before any session exports
|
|
2684
2804
|
// $CODEMAN_HOOK_SECRET_FILE — hook curls cat that path at execution time.
|
|
2685
2805
|
getHookSecret();
|
|
2806
|
+
// Config backups: a snapshot at start (the state the process found) and then on change.
|
|
2807
|
+
if (!this.testMode) {
|
|
2808
|
+
const backup = (reason) => void runConfigBackup(reason).catch((err) => console.warn('[config-backup] snapshot failed:', err instanceof Error ? err.message : err));
|
|
2809
|
+
backup('startup');
|
|
2810
|
+
this.cleanup.setInterval(() => backup('auto'), CONFIG_BACKUP_INTERVAL_MS, {
|
|
2811
|
+
description: 'Config backup',
|
|
2812
|
+
});
|
|
2813
|
+
}
|
|
2686
2814
|
// Main Codex subscription limits come from the signed-in local CLI. Keep
|
|
2687
2815
|
// this read-only and host-scoped; multi-user SSE routing makes it admin-only.
|
|
2688
2816
|
if (!this.testMode) {
|
|
@@ -2690,6 +2818,10 @@ export class WebServer extends EventEmitter {
|
|
|
2690
2818
|
this.cleanup.setInterval(() => void this.refreshCodexPlanUsage(), CODEX_USAGE_POLL_INTERVAL_MS, {
|
|
2691
2819
|
description: 'Codex plan-usage refresh',
|
|
2692
2820
|
});
|
|
2821
|
+
void this.refreshCopilotPlanUsage();
|
|
2822
|
+
this.cleanup.setInterval(() => void this.refreshCopilotPlanUsage(), COPILOT_USAGE_POLL_INTERVAL_MS, {
|
|
2823
|
+
description: 'Copilot plan-usage refresh',
|
|
2824
|
+
});
|
|
2693
2825
|
}
|
|
2694
2826
|
// Custom Model Endpoint Profiles (docs/custom-model-endpoints-plan.md): keeps
|
|
2695
2827
|
// each saved endpoint's discovered model list current with no manual
|
|
@@ -3182,6 +3314,10 @@ export class WebServer extends EventEmitter {
|
|
|
3182
3314
|
// the launch conversation until the user types again, even though
|
|
3183
3315
|
// the re-attached CLI is on a post-`/clear` one.
|
|
3184
3316
|
lastSubmitAt: savedState?.lastSubmitAt,
|
|
3317
|
+
// The keep-warm choice and its cap's clock; with the restored `promptCache`
|
|
3318
|
+
// below, a future `expiresAt` re-arms the timer once the pane is attached.
|
|
3319
|
+
lastRealPromptAt: savedState?.lastRealPromptAt,
|
|
3320
|
+
keepWarmEnabled: savedState?.keepWarmEnabled,
|
|
3185
3321
|
// Conversations this pane provably owned, oldest first. Its tail is
|
|
3186
3322
|
// the conversation the CLI was on when the server stopped, which is
|
|
3187
3323
|
// what a re-attach must point the viewer at instead of the launch id.
|
|
@@ -3356,6 +3492,7 @@ export class WebServer extends EventEmitter {
|
|
|
3356
3492
|
}
|
|
3357
3493
|
}
|
|
3358
3494
|
this.sessions.set(session.id, session);
|
|
3495
|
+
caseSessionSettled(session);
|
|
3359
3496
|
await this.setupSessionListeners(session);
|
|
3360
3497
|
// Auto-attach PTY to the surviving tmux session immediately.
|
|
3361
3498
|
// This ensures ALL sessions resume capturing output right away,
|
|
@@ -3428,6 +3565,21 @@ export class WebServer extends EventEmitter {
|
|
|
3428
3565
|
this.tabLayouts.markRestorationComplete();
|
|
3429
3566
|
await this.cleanupStaleSessions();
|
|
3430
3567
|
await this.tabLayouts.reconcileAfterRestoration();
|
|
3568
|
+
// Same gate as the tab layouts: only a restore that succeeded may decide which
|
|
3569
|
+
// inboxes are orphans. A failed one returns above and leaves every message alone.
|
|
3570
|
+
// Kept: live sessions plus every record state.json still holds (a session that
|
|
3571
|
+
// was not re-adopted this boot but is kept for recovery keeps its mail), plus the
|
|
3572
|
+
// sessions on the reboot-restore offer. `cleanupStaleSessions()` above has already
|
|
3573
|
+
// pruned their records, and a rebuild reuses the old id, so without them a restored
|
|
3574
|
+
// session would come back to an empty inbox. The 12 h TTL bounds what is never rebuilt.
|
|
3575
|
+
const keep = new Set([
|
|
3576
|
+
...this.sessions.keys(),
|
|
3577
|
+
...Object.keys(this.store.getSessions()),
|
|
3578
|
+
...rebootRestoreRegistry.list(() => true).map((entry) => entry.sessionId),
|
|
3579
|
+
]);
|
|
3580
|
+
const pruned = agentInbox.pruneExcept(keep);
|
|
3581
|
+
if (pruned > 0)
|
|
3582
|
+
console.log(`[AgentInbox] dropped ${pruned} inbox(es) of sessions that did not come back`);
|
|
3431
3583
|
}
|
|
3432
3584
|
/**
|
|
3433
3585
|
* Install Codeman's hooks into the workspaces of the sessions just recovered.
|
|
@@ -3582,6 +3734,8 @@ export class WebServer extends EventEmitter {
|
|
|
3582
3734
|
// port against the next start — the exact EADDRINUSE this feature already
|
|
3583
3735
|
// got wrong once.
|
|
3584
3736
|
void stopDeepSeekWeb();
|
|
3737
|
+
// The header's GPU / CPU clock sampler is a Windows child process under WSL, and its idle sweep a timer.
|
|
3738
|
+
stopHostSensors();
|
|
3585
3739
|
// Same teardown rule: the per-endpoint llama-swap log tails are otherwise closed
|
|
3586
3740
|
// only by the periodic idle sweep, whose interval is disposed just below.
|
|
3587
3741
|
closeAllLlamaSwapLogTails();
|
|
@@ -3594,8 +3748,12 @@ export class WebServer extends EventEmitter {
|
|
|
3594
3748
|
// response), so without this a 10-minute wait holds shutdown open.
|
|
3595
3749
|
sessionWaits.cancelEverything();
|
|
3596
3750
|
approvalInbox.stop();
|
|
3751
|
+
this.keepWarmService.stop();
|
|
3752
|
+
// A post within the debounce window before shutdown would otherwise not reach disk.
|
|
3753
|
+
// Flushed again after `app.close()` below, for a post that lands in between.
|
|
3754
|
+
agentInboxPersistence.flush();
|
|
3597
3755
|
// Same reason as `cancelEverything` above: an in-flight wake is awaited by a request,
|
|
3598
|
-
// and `app.close()` (the
|
|
3756
|
+
// and `app.close()` (at the end of this method) does not abort in-flight requests,
|
|
3599
3757
|
// so without this a restart during a wake waits out the readiness poll.
|
|
3600
3758
|
this.remoteWake?.stop();
|
|
3601
3759
|
this.lastRecordedTokens.clear();
|
|
@@ -3710,12 +3868,23 @@ export class WebServer extends EventEmitter {
|
|
|
3710
3868
|
this.userFailures.dispose();
|
|
3711
3869
|
this.userFailures = null;
|
|
3712
3870
|
}
|
|
3871
|
+
if (this.userIpFailures) {
|
|
3872
|
+
this.userIpFailures.dispose();
|
|
3873
|
+
this.userIpFailures = null;
|
|
3874
|
+
}
|
|
3713
3875
|
this.activePlanOrchestrators.clear();
|
|
3714
3876
|
this.cleaningUp.clear();
|
|
3715
3877
|
this.killingSessions.clear();
|
|
3716
3878
|
// Dispose push store (flush pending saves)
|
|
3717
3879
|
this.pushStore.dispose();
|
|
3718
|
-
|
|
3880
|
+
try {
|
|
3881
|
+
await this.app.close();
|
|
3882
|
+
}
|
|
3883
|
+
finally {
|
|
3884
|
+
// The routes answer until `app.close()` returns, so a post after the flush above
|
|
3885
|
+
// would otherwise sit in an unref'd debounce timer and never reach disk.
|
|
3886
|
+
agentInboxPersistence.flush();
|
|
3887
|
+
}
|
|
3719
3888
|
}
|
|
3720
3889
|
}
|
|
3721
3890
|
export async function startWebServer(port = 3000, https = false, testMode = false, host = '127.0.0.1', titleHostname, allowUnauthenticatedNetwork = false, basePath = '') {
|