@wjarka/cezarion 0.15.1 → 0.16.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 +167 -46
- package/dist/agent-config/catalog.d.ts +6 -2
- package/dist/agent-config/catalog.js +78 -3
- package/dist/agent-config/catalog.js.map +1 -1
- package/dist/agent-config/model-settings/shared.js +6 -3
- package/dist/agent-config/model-settings/shared.js.map +1 -1
- package/dist/agent-config/models.js +4 -0
- package/dist/agent-config/models.js.map +1 -1
- package/dist/agent-config/validate.js +6 -0
- package/dist/agent-config/validate.js.map +1 -1
- package/dist/artifacts/cli.js +10 -2
- package/dist/artifacts/cli.js.map +1 -1
- package/dist/automations/coordinator.d.ts +7 -0
- package/dist/automations/coordinator.js +22 -2
- package/dist/automations/coordinator.js.map +1 -1
- package/dist/automations/github-poller.d.ts +4 -4
- package/dist/automations/github-poller.js.map +1 -1
- package/dist/automations/schedule-runner.d.ts +130 -0
- package/dist/automations/schedule-runner.js +326 -0
- package/dist/automations/schedule-runner.js.map +1 -0
- package/dist/automations/scheduler.d.ts +38 -7
- package/dist/automations/scheduler.js +90 -22
- package/dist/automations/scheduler.js.map +1 -1
- package/dist/automations/store.d.ts +80 -3
- package/dist/automations/store.js +208 -16
- package/dist/automations/store.js.map +1 -1
- package/dist/automations/task-template.d.ts +42 -6
- package/dist/automations/task-template.js +152 -24
- package/dist/automations/task-template.js.map +1 -1
- package/dist/automations/types.d.ts +142 -6
- package/dist/automations/types.js +64 -6
- package/dist/automations/types.js.map +1 -1
- package/dist/autosave-holders.js +8 -12
- package/dist/autosave-holders.js.map +1 -1
- package/dist/ci-wait/client.d.ts +8 -1
- package/dist/ci-wait/client.js +43 -1
- package/dist/ci-wait/client.js.map +1 -1
- package/dist/ci-wait/controller.d.ts +83 -4
- package/dist/ci-wait/controller.js +76 -11
- package/dist/ci-wait/controller.js.map +1 -1
- package/dist/ci-wait/errors.d.ts +7 -1
- package/dist/ci-wait/errors.js +36 -0
- package/dist/ci-wait/errors.js.map +1 -1
- package/dist/ci-wait/github.d.ts +2 -0
- package/dist/ci-wait/github.js +3 -1
- package/dist/ci-wait/github.js.map +1 -1
- package/dist/ci-wait/mcp.d.ts +2 -98
- package/dist/ci-wait/mcp.js +11 -25
- package/dist/ci-wait/mcp.js.map +1 -1
- package/dist/ci-wait/supervisor.d.ts +2 -1
- package/dist/ci-wait/supervisor.js +2 -1
- package/dist/ci-wait/supervisor.js.map +1 -1
- package/dist/ci-wait/tools.d.ts +307 -0
- package/dist/ci-wait/tools.js +63 -0
- package/dist/ci-wait/tools.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +7 -0
- package/dist/config.js.map +1 -1
- package/dist/contract/agent-config.d.ts +5 -0
- package/dist/contract/agent-profiles.d.ts +15 -0
- package/dist/contract/automation-schedule.d.ts +60 -0
- package/dist/contract/automations.d.ts +208 -36
- package/dist/contract/delegation.d.ts +96 -0
- package/dist/contract/discovery.d.ts +7 -0
- package/dist/contract/github.d.ts +1 -0
- package/dist/contract/health.d.ts +6 -0
- package/dist/contract/index.d.ts +3 -0
- package/dist/contract/index.js +1445 -745
- package/dist/contract/preview.d.ts +508 -0
- package/dist/contract/runs.d.ts +716 -15
- package/dist/contract/skills.d.ts +39 -0
- package/dist/contract/workflows.d.ts +6 -0
- package/dist/contract/workspace.d.ts +65 -0
- package/dist/contract/zoned-time.d.ts +35 -0
- package/dist/core/agent-env.d.ts +11 -0
- package/dist/core/agent-env.js +27 -5
- package/dist/core/agent-env.js.map +1 -1
- package/dist/core/agent-profiles.js +7 -0
- package/dist/core/agent-profiles.js.map +1 -1
- package/dist/core/agent-runner.d.ts +49 -1
- package/dist/core/agent-runner.js +2 -1
- package/dist/core/agent-runner.js.map +1 -1
- package/dist/core/backend-detect.d.ts +3 -2
- package/dist/core/backend-detect.js +27 -1
- package/dist/core/backend-detect.js.map +1 -1
- package/dist/core/claude-cli-runner.d.ts +1 -0
- package/dist/core/claude-cli-runner.js +69 -19
- package/dist/core/claude-cli-runner.js.map +1 -1
- package/dist/core/codex-app-server-runner.d.ts +1 -0
- package/dist/core/codex-app-server-runner.js +200 -25
- package/dist/core/codex-app-server-runner.js.map +1 -1
- package/dist/core/codex-app-server-transport.d.ts +7 -3
- package/dist/core/codex-app-server-transport.js +11 -9
- package/dist/core/codex-app-server-transport.js.map +1 -1
- package/dist/core/cursor-acp-runner.d.ts +1 -0
- package/dist/core/cursor-acp-runner.js +24 -9
- package/dist/core/cursor-acp-runner.js.map +1 -1
- package/dist/core/host-model-catalog.js +2 -0
- package/dist/core/host-model-catalog.js.map +1 -1
- package/dist/core/managed-session.js +2 -0
- package/dist/core/managed-session.js.map +1 -1
- package/dist/core/model-identity.js +2 -0
- package/dist/core/model-identity.js.map +1 -1
- package/dist/core/model-presets.js +3 -0
- package/dist/core/model-presets.js.map +1 -1
- package/dist/core/omp-model-catalog.d.ts +43 -0
- package/dist/core/omp-model-catalog.js +205 -0
- package/dist/core/omp-model-catalog.js.map +1 -0
- package/dist/core/omp-runner.d.ts +73 -0
- package/dist/core/omp-runner.js +990 -0
- package/dist/core/omp-runner.js.map +1 -0
- package/dist/core/omp-ui-mapper.d.ts +106 -0
- package/dist/core/omp-ui-mapper.js +882 -0
- package/dist/core/omp-ui-mapper.js.map +1 -0
- package/dist/core/opencode-server-runner.d.ts +18 -4
- package/dist/core/opencode-server-runner.js +337 -96
- package/dist/core/opencode-server-runner.js.map +1 -1
- package/dist/core/opencode-session-error.d.ts +11 -0
- package/dist/core/opencode-session-error.js +26 -0
- package/dist/core/opencode-session-error.js.map +1 -0
- package/dist/core/opencode-ui-mapper.js +4 -36
- package/dist/core/opencode-ui-mapper.js.map +1 -1
- package/dist/core/pi-runner.d.ts +1 -0
- package/dist/core/pi-runner.js +75 -12
- package/dist/core/pi-runner.js.map +1 -1
- package/dist/core/provider-auth.d.ts +9 -2
- package/dist/core/provider-auth.js +43 -6
- package/dist/core/provider-auth.js.map +1 -1
- package/dist/core/runner-factory.js +3 -0
- package/dist/core/runner-factory.js.map +1 -1
- package/dist/core/runner-model-catalog.js +1 -0
- package/dist/core/runner-model-catalog.js.map +1 -1
- package/dist/core/runner-stderr.d.ts +2 -0
- package/dist/core/runner-stderr.js +20 -0
- package/dist/core/runner-stderr.js.map +1 -0
- package/dist/core/session-process.d.ts +22 -0
- package/dist/core/session-process.js +202 -0
- package/dist/core/session-process.js.map +1 -0
- package/dist/core/ui-events.d.ts +1 -1
- package/dist/core/usage-limit.js +3 -60
- package/dist/core/usage-limit.js.map +1 -1
- package/dist/data-gitignore.d.ts +6 -0
- package/dist/data-gitignore.js +57 -0
- package/dist/data-gitignore.js.map +1 -0
- package/dist/delegation/capacity.d.ts +19 -0
- package/dist/delegation/capacity.js +34 -0
- package/dist/delegation/capacity.js.map +1 -0
- package/dist/delegation/cli.js +13 -4
- package/dist/delegation/cli.js.map +1 -1
- package/dist/delegation/conversations.d.ts +4 -2
- package/dist/delegation/conversations.js +8 -12
- package/dist/delegation/conversations.js.map +1 -1
- package/dist/delegation/credentials.d.ts +2 -0
- package/dist/delegation/credentials.js +10 -0
- package/dist/delegation/credentials.js.map +1 -1
- package/dist/delegation/destroy-observation.d.ts +42 -0
- package/dist/delegation/destroy-observation.js +115 -0
- package/dist/delegation/destroy-observation.js.map +1 -0
- package/dist/delegation/execution-identity.d.ts +6 -0
- package/dist/delegation/execution-identity.js +1 -1
- package/dist/delegation/execution-identity.js.map +1 -1
- package/dist/delegation/policy.d.ts +3 -2
- package/dist/delegation/policy.js +7 -5
- package/dist/delegation/policy.js.map +1 -1
- package/dist/delegation/process-liveness.d.ts +57 -20
- package/dist/delegation/process-liveness.js +114 -92
- package/dist/delegation/process-liveness.js.map +1 -1
- package/dist/delegation/provision.js +6 -6
- package/dist/delegation/provision.js.map +1 -1
- package/dist/delegation/results.js +4 -2
- package/dist/delegation/results.js.map +1 -1
- package/dist/delegation/retry-backoff.d.ts +23 -0
- package/dist/delegation/retry-backoff.js +18 -0
- package/dist/delegation/retry-backoff.js.map +1 -0
- package/dist/delegation/routes.d.ts +64 -58
- package/dist/delegation/scratch-cleanup.d.ts +29 -0
- package/dist/delegation/scratch-cleanup.js +143 -0
- package/dist/delegation/scratch-cleanup.js.map +1 -0
- package/dist/delegation/service.d.ts +20 -1
- package/dist/delegation/service.js +233 -70
- package/dist/delegation/service.js.map +1 -1
- package/dist/delegation/transport.d.ts +12 -6
- package/dist/delegation/workspace.d.ts +23 -4
- package/dist/delegation/workspace.js +147 -31
- package/dist/delegation/workspace.js.map +1 -1
- package/dist/git-worktree-lock-helper.js +8 -3
- package/dist/git-worktree-lock-helper.js.map +1 -1
- package/dist/git-worktree-lock.d.ts +6 -1
- package/dist/git-worktree-lock.js +10 -4
- package/dist/git-worktree-lock.js.map +1 -1
- package/dist/git-worktree-release.d.ts +20 -0
- package/dist/git-worktree-release.js +27 -0
- package/dist/git-worktree-release.js.map +1 -0
- package/dist/git-worktree.d.ts +8 -2
- package/dist/git-worktree.js +19 -2
- package/dist/git-worktree.js.map +1 -1
- package/dist/handoff.d.ts +2 -2
- package/dist/handoff.js +1 -1
- package/dist/index.js +109 -76
- package/dist/index.js.map +1 -1
- package/dist/paths.d.ts +1 -1
- package/dist/paths.js +5 -1
- package/dist/paths.js.map +1 -1
- package/dist/preview/cdp.d.ts +13 -0
- package/dist/preview/cdp.js +69 -0
- package/dist/preview/cdp.js.map +1 -0
- package/dist/preview/chromium.d.ts +75 -0
- package/dist/preview/chromium.js +394 -0
- package/dist/preview/chromium.js.map +1 -0
- package/dist/preview/dev-server.d.ts +68 -0
- package/dist/preview/dev-server.js +341 -0
- package/dist/preview/dev-server.js.map +1 -0
- package/dist/preview/host.d.ts +154 -0
- package/dist/preview/host.js +681 -0
- package/dist/preview/host.js.map +1 -0
- package/dist/preview/registration.d.ts +63 -0
- package/dist/preview/registration.js +109 -0
- package/dist/preview/registration.js.map +1 -0
- package/dist/preview/session.d.ts +73 -0
- package/dist/preview/session.js +347 -0
- package/dist/preview/session.js.map +1 -0
- package/dist/preview/url.d.ts +6 -0
- package/dist/preview/url.js +17 -0
- package/dist/preview/url.js.map +1 -0
- package/dist/runs/agent-tmpdir-removal.d.ts +15 -0
- package/dist/runs/agent-tmpdir-removal.js +60 -0
- package/dist/runs/agent-tmpdir-removal.js.map +1 -0
- package/dist/runs/agent-tmpdir.d.ts +12 -1
- package/dist/runs/agent-tmpdir.js +123 -34
- package/dist/runs/agent-tmpdir.js.map +1 -1
- package/dist/runs/delegation-state.d.ts +5 -1
- package/dist/runs/delegation-state.js +9 -0
- package/dist/runs/delegation-state.js.map +1 -1
- package/dist/runs/event-history.d.ts +4 -4
- package/dist/runs/event-history.js +97 -117
- package/dist/runs/event-history.js.map +1 -1
- package/dist/runs/history-compressor.d.ts +29 -0
- package/dist/runs/history-compressor.js +116 -0
- package/dist/runs/history-compressor.js.map +1 -0
- package/dist/runs/history-file.d.ts +36 -0
- package/dist/runs/history-file.js +463 -0
- package/dist/runs/history-file.js.map +1 -0
- package/dist/runs/human-ask-summary.js +18 -13
- package/dist/runs/human-ask-summary.js.map +1 -1
- package/dist/runs/legacy-index.d.ts +62 -0
- package/dist/runs/legacy-index.js +168 -0
- package/dist/runs/legacy-index.js.map +1 -0
- package/dist/runs/raw-record.d.ts +52 -0
- package/dist/runs/raw-record.js +145 -0
- package/dist/runs/raw-record.js.map +1 -0
- package/dist/runs/retention.d.ts +13 -6
- package/dist/runs/retention.js +42 -13
- package/dist/runs/retention.js.map +1 -1
- package/dist/runs/run-claims.d.ts +23 -0
- package/dist/runs/run-claims.js +24 -0
- package/dist/runs/run-claims.js.map +1 -0
- package/dist/runs/run-database.d.ts +385 -0
- package/dist/runs/run-database.js +930 -0
- package/dist/runs/run-database.js.map +1 -0
- package/dist/runs/run-index.d.ts +35 -9
- package/dist/runs/run-index.js +200 -25
- package/dist/runs/run-index.js.map +1 -1
- package/dist/runs/run-row.d.ts +44 -0
- package/dist/runs/run-row.js +84 -0
- package/dist/runs/run-row.js.map +1 -0
- package/dist/runs/run-search.d.ts +29 -0
- package/dist/runs/run-search.js +63 -0
- package/dist/runs/run-search.js.map +1 -0
- package/dist/runs/store-open-error.d.ts +24 -0
- package/dist/runs/store-open-error.js +61 -0
- package/dist/runs/store-open-error.js.map +1 -0
- package/dist/runs/store.d.ts +667 -32
- package/dist/runs/store.js +2153 -324
- package/dist/runs/store.js.map +1 -1
- package/dist/runs/transcript-facts.d.ts +77 -0
- package/dist/runs/transcript-facts.js +302 -0
- package/dist/runs/transcript-facts.js.map +1 -0
- package/dist/runs/webhook.js +5 -4
- package/dist/runs/webhook.js.map +1 -1
- package/dist/runs/worker-execution.d.ts +22 -0
- package/dist/runs/worker-execution.js +30 -0
- package/dist/runs/worker-execution.js.map +1 -0
- package/dist/server/capabilities.d.ts +1 -0
- package/dist/server/capabilities.js +2 -0
- package/dist/server/capabilities.js.map +1 -1
- package/dist/server/cockpit-ownership.d.ts +8 -0
- package/dist/server/cockpit-ownership.js +19 -0
- package/dist/server/cockpit-ownership.js.map +1 -1
- package/dist/server/forge/github.d.ts +1 -4
- package/dist/server/forge/github.js +47 -23
- package/dist/server/forge/github.js.map +1 -1
- package/dist/server/git.js +1 -1
- package/dist/server/git.js.map +1 -1
- package/dist/server/open-in-app.js +1 -0
- package/dist/server/open-in-app.js.map +1 -1
- package/dist/server/preview-socket.d.ts +36 -0
- package/dist/server/preview-socket.js +126 -0
- package/dist/server/preview-socket.js.map +1 -0
- package/dist/server/project-context.d.ts +31 -4
- package/dist/server/project-context.js +73 -9
- package/dist/server/project-context.js.map +1 -1
- package/dist/server/provider-action-gate.js +1 -0
- package/dist/server/provider-action-gate.js.map +1 -1
- package/dist/server/repo-branches.d.ts +4 -3
- package/dist/server/repo-branches.js +5 -4
- package/dist/server/repo-branches.js.map +1 -1
- package/dist/server/server.d.ts +4500 -2727
- package/dist/server/server.js +651 -202
- package/dist/server/server.js.map +1 -1
- package/dist/server/upgrade-router.d.ts +23 -0
- package/dist/server/upgrade-router.js +53 -0
- package/dist/server/upgrade-router.js.map +1 -0
- package/dist/server/validators.d.ts +6 -1
- package/dist/server/validators.js +4 -4
- package/dist/server/validators.js.map +1 -1
- package/dist/server/ws.d.ts +16 -16
- package/dist/server/ws.js +56 -54
- package/dist/server/ws.js.map +1 -1
- package/dist/skills-remote.d.ts +14 -0
- package/dist/skills-remote.js +91 -15
- package/dist/skills-remote.js.map +1 -1
- package/dist/task-cli/cli.js +40 -16
- package/dist/task-cli/cli.js.map +1 -1
- package/dist/task-cli/discovery.d.ts +4 -3
- package/dist/task-cli/discovery.js +4 -3
- package/dist/task-cli/discovery.js.map +1 -1
- package/dist/task-cli/http.d.ts +2 -0
- package/dist/task-cli/http.js +10 -4
- package/dist/task-cli/http.js.map +1 -1
- package/dist/task-cli/projections.d.ts +5 -2
- package/dist/task-cli/projections.js +13 -0
- package/dist/task-cli/projections.js.map +1 -1
- package/dist/task-cli/watch.d.ts +15 -2
- package/dist/task-cli/watch.js +31 -9
- package/dist/task-cli/watch.js.map +1 -1
- package/dist/ui-state.d.ts +4 -3
- package/dist/ui-state.js +8 -3
- package/dist/ui-state.js.map +1 -1
- package/dist/workflows/grouped-command.d.ts +31 -0
- package/dist/workflows/grouped-command.js +143 -0
- package/dist/workflows/grouped-command.js.map +1 -0
- package/dist/workflows/run.d.ts +223 -42
- package/dist/workflows/run.js +1316 -398
- package/dist/workflows/run.js.map +1 -1
- package/dist/workflows/types.d.ts +12 -0
- package/dist/workflows/types.js +20 -0
- package/dist/workflows/types.js.map +1 -1
- package/dist/workspace/agent-accounts.d.ts +5 -1
- package/dist/workspace/agent-accounts.js +1 -0
- package/dist/workspace/agent-accounts.js.map +1 -1
- package/dist/workspace/agent-profiles.js +1 -1
- package/dist/workspace/agent-profiles.js.map +1 -1
- package/dist/workspace/config.d.ts +3 -1
- package/dist/workspace/config.js +1 -0
- package/dist/workspace/config.js.map +1 -1
- package/dist/worktree-setup.d.ts +85 -0
- package/dist/worktree-setup.js +164 -0
- package/dist/worktree-setup.js.map +1 -0
- package/package.json +7 -5
- package/scripts/benchmark-run-store-fixture.ts +428 -0
- package/scripts/benchmark-run-store.ts +743 -0
- package/scripts/benchmark-stuck-destroys.ts +143 -0
- package/scripts/mock-autonomous.mjs +11 -0
- package/scripts/mock-ci-tool.mjs +20 -5
- package/scripts/mock-claude.mjs +110 -1
- package/scripts/mock-codex-app-server.mjs +117 -2
- package/scripts/mock-codex-coalesce-steer.mjs +59 -0
- package/scripts/mock-codex-reverse-steer.mjs +130 -0
- package/scripts/mock-cursor-acp.mjs +75 -1
- package/scripts/mock-omp-rpc.mjs +605 -0
- package/scripts/mock-opencode-serve.mjs +312 -22
- package/scripts/mock-pi-rpc.mjs +109 -3
- package/scripts/mock-runner-crash.mjs +42 -0
- package/scripts/mock-silent-tail.mjs +78 -0
- package/scripts/mock-turn-messages.mjs +5 -0
- package/scripts/omp-ci-wait.mjs +29 -0
- package/scripts/omp-restrict-delegation.yml +4 -0
- package/scripts/pi-ci-wait.mjs +12 -9
- package/scripts/seed-archive-profile.ts +3 -3
- package/web/dist/assets/agent-accounts-CFOBMVHc.js +1 -0
- package/web/dist/assets/alert-dialog-BrpNUwMt.js +1 -0
- package/web/dist/assets/app-window-CSHwDX9N.js +1 -0
- package/web/dist/assets/button-C_rd7CPp.js +43 -0
- package/web/dist/assets/compare-variants-D6WXhvXA.js +1 -0
- package/web/dist/assets/diff-DrEARXTV.js +3 -0
- package/web/dist/assets/diff-controls-B1-j7nFg.js +1 -0
- package/web/dist/assets/{diff-view-DPn6bQ5W.js → diff-view-DvGLjdTk.js} +3 -3
- package/web/dist/assets/dropdown-menu-BegOZFrj.js +1 -0
- package/web/dist/assets/format-DpNe3S0A.js +1 -0
- package/web/dist/assets/git-toolbar-Cw4ljmhB.js +1 -0
- package/web/dist/assets/github-CfV5sqL0.js +1 -0
- package/web/dist/assets/{image-preview-DJ8cgb1Z.js → image-preview-FOEfqpZP.js} +1 -1
- package/web/dist/assets/index-DgWWlOLy.js +13 -0
- package/web/dist/assets/index-fBFnuvxu.css +1 -0
- package/web/dist/assets/{nav-row-styles-3j64kH5s.js → nav-row-styles-vp2JbhuJ.js} +1 -1
- package/web/dist/assets/play-CmVAj_fu.js +1 -0
- package/web/dist/assets/popover-BohP6WTx.js +1 -0
- package/web/dist/assets/preview-pane-connected-qTA5KNoJ.js +3 -0
- package/web/dist/assets/preview-state-vuMviq6q.js +1 -0
- package/web/dist/assets/prompt-templates-Cubdut_C.js +15 -0
- package/web/dist/assets/repo-git-Cf4t5PD0.js +1 -0
- package/web/dist/assets/{run-diff-5-RRdcVz.js → run-diff-BpkUW37Z.js} +3 -3
- package/web/dist/assets/run-header-68WHf9wV.js +1 -0
- package/web/dist/assets/skills-DiVIgKeU.js +1 -0
- package/web/dist/assets/square-terminal-B6RYYieR.js +1 -0
- package/web/dist/assets/status-dot-CqTJHSP9.js +1 -0
- package/web/dist/assets/{tab-link-K0hjzEXR.js → tab-link-B07Hu4QX.js} +1 -1
- package/web/dist/assets/task-changes--lQF6EYf.js +1 -0
- package/web/dist/assets/task-commits-Cx0qy4DN.js +1 -0
- package/web/dist/assets/task-files-D50s-gKW.js +2 -0
- package/web/dist/assets/task-github-item-DaqmSwYT.js +1 -0
- package/web/dist/assets/task-thread-BPQ64lui.js +7 -0
- package/web/dist/assets/{task-thread-Bw_nQuTx.css → task-thread-zNE2Q9E-.css} +1 -1
- package/web/dist/assets/textarea-D20O4Pw0.js +1 -0
- package/web/dist/assets/thread-state-DWcFMrvb.js +4 -0
- package/web/dist/assets/use-hand-to-agent-state-BLIGmwbd.css +1 -0
- package/web/dist/assets/use-hand-to-agent-state-Cy9eyyfL.js +1 -0
- package/web/dist/assets/{utils-fzNfbPzA.js → utils-JsL3ZsJf.js} +1 -1
- package/web/dist/assets/workflows-jwG7uxqi.js +11 -0
- package/web/dist/assets/{zoomable-image-CDmZ_Rtl.js → zoomable-image-BJJ64KGc.js} +1 -1
- package/web/dist/cezarion-lockup-dark.svg +1 -13
- package/web/dist/cezarion-lockup-light.svg +1 -13
- package/web/dist/cezarion-mark-dark.svg +1 -12
- package/web/dist/cezarion-mark-light.svg +1 -12
- package/web/dist/index.html +16 -21
- package/web/dist/assets/agent-accounts-CU2ZDv24.js +0 -1
- package/web/dist/assets/alert-dialog-C-xYE_dg.js +0 -43
- package/web/dist/assets/chevron-right-BENZ-1ZN.js +0 -1
- package/web/dist/assets/collapsible-DD9J91Iv.js +0 -1
- package/web/dist/assets/compare-variants-5FmqpFSN.js +0 -1
- package/web/dist/assets/diff-controls-Bj0YrioE.js +0 -1
- package/web/dist/assets/diff-rE8rm75J2.js +0 -3
- package/web/dist/assets/diff-stat-BKDCSVfJ.js +0 -1
- package/web/dist/assets/dropdown-menu-Ch5BYQh_.js +0 -1
- package/web/dist/assets/format-dBiNDNVe.js +0 -1
- package/web/dist/assets/git-toolbar-ClVvf5iF.js +0 -1
- package/web/dist/assets/github-BkNOyour.css +0 -1
- package/web/dist/assets/github-DeZMeMwJ.js +0 -1
- package/web/dist/assets/github-item-detail-W5F6GVjF.js +0 -1
- package/web/dist/assets/github-sidebar-model-DcMu-IK2.js +0 -1
- package/web/dist/assets/highlighter-CdHqIwFr.js +0 -3
- package/web/dist/assets/index-CatemBEL.js +0 -8
- package/web/dist/assets/index-MvfD8bZJ.css +0 -1
- package/web/dist/assets/markdown-zdKeLIYA.js +0 -2
- package/web/dist/assets/mobile-run-bar-BCfmFi8V.js +0 -1
- package/web/dist/assets/prompt-templates-CBPp39IU.js +0 -15
- package/web/dist/assets/reference-chip-D6owncJT.js +0 -1
- package/web/dist/assets/repo-git-CtdkZFey.js +0 -1
- package/web/dist/assets/run-header-BcjoDi0W.js +0 -1
- package/web/dist/assets/sheet-ChnYVrp1.js +0 -1
- package/web/dist/assets/skills-C8YKp2vr.js +0 -1
- package/web/dist/assets/task-changes-BW8YdWG8.js +0 -1
- package/web/dist/assets/task-commits-q2kB2y6L.js +0 -1
- package/web/dist/assets/task-files-pPL6_3Cr.js +0 -2
- package/web/dist/assets/task-github-item-BXF5R52U.js +0 -1
- package/web/dist/assets/task-thread-DtkFL3EC.js +0 -10
- package/web/dist/assets/textarea-DxlkE-Yd.js +0 -1
- package/web/dist/assets/token-metrics-DIAjrPlM.js +0 -1
- package/web/dist/assets/use-desktop-CrDny2uG.js +0 -1
- package/web/dist/assets/use-submit-shortcut-KWJsC906.js +0 -1
- package/web/dist/assets/workflows-TmcPEj2K.js +0 -11
- /package/web/dist/assets/{lib-BQXq3kEf2.js → lib-BQXq3kEf.js} +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
import type { Duplex } from 'node:stream';
|
|
3
|
+
/**
|
|
4
|
+
* The HTTP server's one `upgrade` listener (#781, spec 2026-10-02-live-preview-v1 "WebSocket").
|
|
5
|
+
* Each WebSocket endpoint is a route; the first whose `match` returns params handles the upgrade,
|
|
6
|
+
* and an unmatched path is destroyed. Upgrade-only endpoints never reach Hono, so each one is
|
|
7
|
+
* inventoried by hand in BACKWARD_COMPATIBILITY.md §2.
|
|
8
|
+
*/
|
|
9
|
+
export interface UpgradeRoute {
|
|
10
|
+
/** Path params when `pathname` is this route's, else undefined. */
|
|
11
|
+
match(pathname: string): Record<string, string> | undefined;
|
|
12
|
+
handle(req: IncomingMessage, socket: Duplex, head: Buffer, params: Record<string, string>): void;
|
|
13
|
+
}
|
|
14
|
+
/** The server surface the router needs: the `http.Server` that `serve()` returns has it. */
|
|
15
|
+
export interface UpgradeCapableServer {
|
|
16
|
+
on(event: 'upgrade', listener: (req: IncomingMessage, socket: Duplex, head: Buffer) => void): unknown;
|
|
17
|
+
}
|
|
18
|
+
export declare function attachUpgradeRouter(server: UpgradeCapableServer, routes: readonly UpgradeRoute[]): void;
|
|
19
|
+
/**
|
|
20
|
+
* A `match` for one or more `/a/:param/b` patterns. A param is one non-empty segment, decoded;
|
|
21
|
+
* one that does not decode is no match.
|
|
22
|
+
*/
|
|
23
|
+
export declare function matchPath(patterns: readonly string[]): UpgradeRoute['match'];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export function attachUpgradeRouter(server, routes) {
|
|
2
|
+
server.on('upgrade', (req, socket, head) => {
|
|
3
|
+
let pathname;
|
|
4
|
+
try {
|
|
5
|
+
// `req.url` on an upgrade is the path (+query); the base only satisfies URL parsing.
|
|
6
|
+
pathname = new URL(req.url ?? '', 'http://localhost').pathname;
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
socket.destroy();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
for (const route of routes) {
|
|
13
|
+
const params = route.match(pathname);
|
|
14
|
+
if (params)
|
|
15
|
+
return route.handle(req, socket, head, params);
|
|
16
|
+
}
|
|
17
|
+
// With an `upgrade` listener installed, Node no longer destroys unhandled upgrades.
|
|
18
|
+
socket.destroy();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A `match` for one or more `/a/:param/b` patterns. A param is one non-empty segment, decoded;
|
|
23
|
+
* one that does not decode is no match.
|
|
24
|
+
*/
|
|
25
|
+
export function matchPath(patterns) {
|
|
26
|
+
const compiled = patterns.map(pattern => pattern.split('/'));
|
|
27
|
+
return pathname => {
|
|
28
|
+
const parts = pathname.split('/');
|
|
29
|
+
for (const segments of compiled) {
|
|
30
|
+
if (segments.length !== parts.length)
|
|
31
|
+
continue;
|
|
32
|
+
const params = {};
|
|
33
|
+
const matched = segments.every((segment, i) => {
|
|
34
|
+
const part = parts[i];
|
|
35
|
+
if (!segment.startsWith(':'))
|
|
36
|
+
return segment === part;
|
|
37
|
+
if (!part)
|
|
38
|
+
return false;
|
|
39
|
+
try {
|
|
40
|
+
params[segment.slice(1)] = decodeURIComponent(part);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
if (matched)
|
|
48
|
+
return params;
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=upgrade-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade-router.js","sourceRoot":"","sources":["../../src/server/upgrade-router.ts"],"names":[],"mappings":"AAqBA,MAAM,UAAU,mBAAmB,CAAC,MAA4B,EAAE,MAA+B;IAC/F,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;QACzC,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,qFAAqF;YACrF,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC;QACjE,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACrC,IAAI,MAAM;gBAAE,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7D,CAAC;QACD,oFAAoF;QACpF,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,QAA2B;IACnD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,OAAO,QAAQ,CAAC,EAAE;QAChB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;YAChC,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;gBAAE,SAAS;YAC/C,MAAM,MAAM,GAA2B,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE;gBAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;gBACvB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,OAAO,OAAO,KAAK,IAAI,CAAC;gBACtD,IAAI,CAAC,IAAI;oBAAE,OAAO,KAAK,CAAC;gBACxB,IAAI,CAAC;oBACH,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;oBACpD,OAAO,IAAI,CAAC;gBACd,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,KAAK,CAAC;gBACf,CAAC;YACH,CAAC,CAAC,CAAC;YACH,IAAI,OAAO;gBAAE,OAAO,MAAM,CAAC;QAC7B,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Env, MiddlewareHandler } from 'hono';
|
|
1
|
+
import type { Context, Env, MiddlewareHandler } from 'hono';
|
|
2
2
|
import type { z } from 'zod';
|
|
3
3
|
/**
|
|
4
4
|
* Request validation as ROUTE MIDDLEWARE — the `json` / `param` / `query` trio every mutating
|
|
@@ -50,6 +50,11 @@ type JsonOptions = ErrorOptions & {
|
|
|
50
50
|
* behaviour moved with it rather than being lost in the move.
|
|
51
51
|
*/
|
|
52
52
|
malformed?: unknown;
|
|
53
|
+
/**
|
|
54
|
+
* Builds the rejection itself, from the issues and the parsed input. The private preview tool
|
|
55
|
+
* route (#781) answers its agent a typed 200 result naming the field, not this API's 400.
|
|
56
|
+
*/
|
|
57
|
+
invalid?: (c: Context, error: z.ZodError, input: unknown) => Response;
|
|
53
58
|
};
|
|
54
59
|
/**
|
|
55
60
|
* The request/response split, spelled out rather than inferred.
|
|
@@ -25,13 +25,13 @@ schema, options = {}) {
|
|
|
25
25
|
// `= null` default would silently overwrite exactly that case.
|
|
26
26
|
const absent = 'absent' in options ? options.absent : null;
|
|
27
27
|
const malformed = 'malformed' in options ? options.malformed : absent;
|
|
28
|
-
const { message, code } = options;
|
|
28
|
+
const { message, code, invalid } = options;
|
|
29
29
|
const check = (input, c) => {
|
|
30
30
|
const resolved = typeof schema === 'function' ? schema() : schema;
|
|
31
31
|
const parsed = resolved.safeParse(input);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
if (parsed.success)
|
|
33
|
+
return { ok: true, data: parsed.data };
|
|
34
|
+
return { ok: false, response: invalid ? invalid(c, parsed.error, input) : reject(c, parsed.error, message, code) };
|
|
35
35
|
};
|
|
36
36
|
const validate = validator('json', (value, c) => {
|
|
37
37
|
const result = check(value, c);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/server/validators.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/server/validators.ts"],"names":[],"mappings":"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"}
|
package/dist/server/ws.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'node:http';
|
|
2
2
|
import type { Duplex } from 'node:stream';
|
|
3
|
+
import type { UpgradeRoute } from './upgrade-router.ts';
|
|
3
4
|
/**
|
|
4
5
|
* The cockpit's WebSocket subscription bus (`GET /api/v1/ws`, upgrade-only).
|
|
5
6
|
* Design + the subscribe/unsubscribe discipline: spec
|
|
@@ -16,10 +17,14 @@ import type { Duplex } from 'node:stream';
|
|
|
16
17
|
* to mean N polls every 5 s forever; now they mean one publisher while at
|
|
17
18
|
* least one tab subscribes, and none afterwards.
|
|
18
19
|
*
|
|
19
|
-
* Security: the hub itself never decides who may connect — `
|
|
20
|
-
* upgrade guard
|
|
21
|
-
* the `/api/*` request-origin guard (#426). See `verifyWsUpgrade` there for
|
|
20
|
+
* Security: the hub itself never decides who may connect — `handleUpgrade`
|
|
21
|
+
* takes the upgrade guard's verdict, and server.ts supplies the WebSocket twin
|
|
22
|
+
* of the `/api/*` request-origin guard (#426). See `verifyWsUpgrade` there for
|
|
22
23
|
* what it admits, and the caveat about which topics may live on this hub.
|
|
24
|
+
*
|
|
25
|
+
* The hub does not listen on the HTTP server itself: the one `upgrade` listener
|
|
26
|
+
* is `attachUpgradeRouter` (upgrade-router.ts, #781), and `socketHubRoute` is
|
|
27
|
+
* this hub's route in it.
|
|
23
28
|
*/
|
|
24
29
|
/** The one upgrade path. Workspace-level like `/api/workspace/events` — never
|
|
25
30
|
* mirrored under `/api/p/:projectId` (topics carry workspace data). */
|
|
@@ -56,25 +61,18 @@ export interface TopicOptions {
|
|
|
56
61
|
export type WsUpgradeVerdict = false | {
|
|
57
62
|
trusted: boolean;
|
|
58
63
|
};
|
|
59
|
-
/** The minimal server surface `attach` needs — satisfied by the `http.Server`
|
|
60
|
-
* that `@hono/node-server`'s `serve()` returns. */
|
|
61
|
-
export interface UpgradeCapableServer {
|
|
62
|
-
on(event: 'upgrade', listener: (req: IncomingMessage, socket: Duplex, head: Buffer) => void): unknown;
|
|
63
|
-
on(event: 'close', listener: () => void): unknown;
|
|
64
|
-
}
|
|
65
64
|
export interface SocketHub {
|
|
66
65
|
/** Register a topic clients may subscribe to. Registration is boot-time
|
|
67
66
|
* wiring (createApp), so a duplicate name is a programming error: throw.
|
|
68
67
|
* `options.loopbackReadable` defaults to `false` (topic legible to trusted
|
|
69
68
|
* connections only) — see `TopicOptions`. */
|
|
70
69
|
registerTopic(name: string, publisher: TopicPublisher, options?: TopicOptions): void;
|
|
71
|
-
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
* on the attached server's own `close`. */
|
|
70
|
+
/** Accept one `WS_PATH` upgrade under the request-origin guard's verdict:
|
|
71
|
+
* `false` answers 403 before the handshake, otherwise its `trusted` flag
|
|
72
|
+
* decides which topics the connection may read. */
|
|
73
|
+
handleUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer, verdict: WsUpgradeVerdict): void;
|
|
74
|
+
/** Stop publishers, terminate clients, clear timers. Idempotent; the server
|
|
75
|
+
* that routes upgrades here calls it on its own `close`. */
|
|
78
76
|
close(): void;
|
|
79
77
|
}
|
|
80
78
|
export interface SocketHubOptions {
|
|
@@ -83,3 +81,5 @@ export interface SocketHubOptions {
|
|
|
83
81
|
heartbeatMs?: number;
|
|
84
82
|
}
|
|
85
83
|
export declare function createSocketHub(options?: SocketHubOptions): SocketHub;
|
|
84
|
+
/** The hub's route in the upgrade router: `WS_PATH`, under `verifyUpgrade`. */
|
|
85
|
+
export declare function socketHubRoute(hub: SocketHub, verifyUpgrade: (req: IncomingMessage) => WsUpgradeVerdict): UpgradeRoute;
|
package/dist/server/ws.js
CHANGED
|
@@ -16,10 +16,14 @@ import { z } from 'zod';
|
|
|
16
16
|
* to mean N polls every 5 s forever; now they mean one publisher while at
|
|
17
17
|
* least one tab subscribes, and none afterwards.
|
|
18
18
|
*
|
|
19
|
-
* Security: the hub itself never decides who may connect — `
|
|
20
|
-
* upgrade guard
|
|
21
|
-
* the `/api/*` request-origin guard (#426). See `verifyWsUpgrade` there for
|
|
19
|
+
* Security: the hub itself never decides who may connect — `handleUpgrade`
|
|
20
|
+
* takes the upgrade guard's verdict, and server.ts supplies the WebSocket twin
|
|
21
|
+
* of the `/api/*` request-origin guard (#426). See `verifyWsUpgrade` there for
|
|
22
22
|
* what it admits, and the caveat about which topics may live on this hub.
|
|
23
|
+
*
|
|
24
|
+
* The hub does not listen on the HTTP server itself: the one `upgrade` listener
|
|
25
|
+
* is `attachUpgradeRouter` (upgrade-router.ts, #781), and `socketHubRoute` is
|
|
26
|
+
* this hub's route in it.
|
|
23
27
|
*/
|
|
24
28
|
/** The one upgrade path. Workspace-level like `/api/workspace/events` — never
|
|
25
29
|
* mirrored under `/api/p/:projectId` (topics carry workspace data). */
|
|
@@ -99,6 +103,33 @@ export function createSocketHub(options = {}) {
|
|
|
99
103
|
state.stop = null;
|
|
100
104
|
}
|
|
101
105
|
};
|
|
106
|
+
/** Started by the first upgrade, so a hub nobody connects to holds no timer. */
|
|
107
|
+
const startHeartbeat = () => {
|
|
108
|
+
if (heartbeat !== undefined)
|
|
109
|
+
return;
|
|
110
|
+
heartbeat = setInterval(() => {
|
|
111
|
+
for (const [ws, client] of clients) {
|
|
112
|
+
if (!client.alive) {
|
|
113
|
+
// Missed the previous beat's pong — the peer is gone. Terminate (not
|
|
114
|
+
// a polite close, there is nobody to handshake with); 'close' fires
|
|
115
|
+
// synchronously and dropClient releases its topics.
|
|
116
|
+
ws.terminate();
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
client.alive = false;
|
|
120
|
+
ws.ping(); // protocol ping — a live browser auto-pongs, flipping `alive` back
|
|
121
|
+
// App-level beat too. The browser NEVER surfaces the protocol ping to
|
|
122
|
+
// page JS, so a client cannot tell a silent-dead socket (sleep, network
|
|
123
|
+
// partition — no FIN ever arrives) from an idle-but-healthy one on the
|
|
124
|
+
// protocol ping alone. This visible frame is what the client's own
|
|
125
|
+
// watchdog watches to decide to reconnect. Sent to every client,
|
|
126
|
+
// subscribed or not; the client ignores it beyond noting liveness.
|
|
127
|
+
send(ws, { type: 'ping' });
|
|
128
|
+
}
|
|
129
|
+
}, heartbeatMs);
|
|
130
|
+
// Never the reason the process stays up — the HTTP server owns lifetime.
|
|
131
|
+
heartbeat.unref?.();
|
|
132
|
+
};
|
|
102
133
|
const dropClient = (ws) => {
|
|
103
134
|
const client = clients.get(ws);
|
|
104
135
|
if (!client)
|
|
@@ -108,7 +139,7 @@ export function createSocketHub(options = {}) {
|
|
|
108
139
|
clients.delete(ws);
|
|
109
140
|
};
|
|
110
141
|
wss.on('connection', (ws, _req, trusted) => {
|
|
111
|
-
// `trusted` is emitted by `
|
|
142
|
+
// `trusted` is emitted by `handleUpgrade` from the upgrade verdict; default false is
|
|
112
143
|
// the safe read for any path that reaches here without one.
|
|
113
144
|
const client = { alive: true, topics: new Set(), trusted: trusted === true };
|
|
114
145
|
clients.set(ws, client);
|
|
@@ -150,56 +181,20 @@ export function createSocketHub(options = {}) {
|
|
|
150
181
|
loopbackReadable: options?.loopbackReadable ?? false,
|
|
151
182
|
});
|
|
152
183
|
},
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
socket.destroy();
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
const verdict = verifyUpgrade(req);
|
|
171
|
-
if (!verdict) {
|
|
172
|
-
socket.write('HTTP/1.1 403 Forbidden\r\nconnection: close\r\n\r\n');
|
|
173
|
-
socket.destroy();
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
// Carry the verdict's trust flag onto the connection — `subscribe` reads
|
|
177
|
-
// it to gate non-`loopbackReadable` topics.
|
|
178
|
-
wss.handleUpgrade(req, socket, head, (ws) => wss.emit('connection', ws, req, verdict.trusted));
|
|
179
|
-
});
|
|
180
|
-
server.on('close', () => hub.close());
|
|
181
|
-
heartbeat = setInterval(() => {
|
|
182
|
-
for (const [ws, client] of clients) {
|
|
183
|
-
if (!client.alive) {
|
|
184
|
-
// Missed the previous beat's pong — the peer is gone. Terminate (not
|
|
185
|
-
// a polite close, there is nobody to handshake with); 'close' fires
|
|
186
|
-
// synchronously and dropClient releases its topics.
|
|
187
|
-
ws.terminate();
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
client.alive = false;
|
|
191
|
-
ws.ping(); // protocol ping — a live browser auto-pongs, flipping `alive` back
|
|
192
|
-
// App-level beat too. The browser NEVER surfaces the protocol ping to
|
|
193
|
-
// page JS, so a client cannot tell a silent-dead socket (sleep, network
|
|
194
|
-
// partition — no FIN ever arrives) from an idle-but-healthy one on the
|
|
195
|
-
// protocol ping alone. This visible frame is what the client's own
|
|
196
|
-
// watchdog watches to decide to reconnect. Sent to every client,
|
|
197
|
-
// subscribed or not; the client ignores it beyond noting liveness.
|
|
198
|
-
send(ws, { type: 'ping' });
|
|
199
|
-
}
|
|
200
|
-
}, heartbeatMs);
|
|
201
|
-
// Never the reason the process stays up — the HTTP server owns lifetime.
|
|
202
|
-
heartbeat.unref?.();
|
|
184
|
+
handleUpgrade(req, socket, head, verdict) {
|
|
185
|
+
if (closed) {
|
|
186
|
+
socket.destroy();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (!verdict) {
|
|
190
|
+
socket.write('HTTP/1.1 403 Forbidden\r\nconnection: close\r\n\r\n');
|
|
191
|
+
socket.destroy();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
startHeartbeat();
|
|
195
|
+
// Carry the verdict's trust flag onto the connection — `subscribe` reads
|
|
196
|
+
// it to gate non-`loopbackReadable` topics.
|
|
197
|
+
wss.handleUpgrade(req, socket, head, (ws) => wss.emit('connection', ws, req, verdict.trusted));
|
|
203
198
|
},
|
|
204
199
|
close() {
|
|
205
200
|
if (closed)
|
|
@@ -215,4 +210,11 @@ export function createSocketHub(options = {}) {
|
|
|
215
210
|
};
|
|
216
211
|
return hub;
|
|
217
212
|
}
|
|
213
|
+
/** The hub's route in the upgrade router: `WS_PATH`, under `verifyUpgrade`. */
|
|
214
|
+
export function socketHubRoute(hub, verifyUpgrade) {
|
|
215
|
+
return {
|
|
216
|
+
match: (pathname) => (pathname === WS_PATH ? {} : undefined),
|
|
217
|
+
handle: (req, socket, head) => hub.handleUpgrade(req, socket, head, verifyUpgrade(req)),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
218
220
|
//# sourceMappingURL=ws.js.map
|
package/dist/server/ws.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../../src/server/ws.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAkB,MAAM,IAAI,CAAC;AACrD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../../src/server/ws.ts"],"names":[],"mappings":"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"}
|
package/dist/skills-remote.d.ts
CHANGED
|
@@ -96,6 +96,13 @@ export declare function shouldRecordFetchFailure(opts: {
|
|
|
96
96
|
}): boolean;
|
|
97
97
|
/** Test hook: simulate a process-local failed attempt for this source. */
|
|
98
98
|
export declare function __markCloneAttemptedForTests(repo: string): void;
|
|
99
|
+
/** Test hook: hold this source's clone/fetch open until `until` settles (#859). */
|
|
100
|
+
export declare function __holdFetchForTests(repo: string, until: Promise<void>): void;
|
|
101
|
+
/**
|
|
102
|
+
* Test hook: park this project's next passive load right after its list-only
|
|
103
|
+
* pass until `until` settles (#859). Resolves once the load has parked there.
|
|
104
|
+
*/
|
|
105
|
+
export declare function __pauseAfterCachedListForTests(repoRoot: string, until: Promise<void>): Promise<void>;
|
|
99
106
|
/**
|
|
100
107
|
* The current team-skill list for this project, straight from memory. The
|
|
101
108
|
* first call per `repoRoot` kicks off an async background load (clone + list)
|
|
@@ -109,5 +116,12 @@ export declare function getTeamSkillsCached(repoRoot: string): Skill[];
|
|
|
109
116
|
* background convergence read after they have already rendered local skills.
|
|
110
117
|
*/
|
|
111
118
|
export declare function waitForTeamSkills(repoRoot: string): Promise<Skill[]>;
|
|
119
|
+
/**
|
|
120
|
+
* The first load's list of the bare clones already on disk, published before
|
|
121
|
+
* any passive fetch (#859). Never waits on the network: no clone yet means an
|
|
122
|
+
* empty list, not a clone. When a refresh started this project's first load
|
|
123
|
+
* there is no list-only pass, and this answers with the current list.
|
|
124
|
+
*/
|
|
125
|
+
export declare function waitForCachedTeamSkills(repoRoot: string): Promise<Skill[]>;
|
|
112
126
|
/** Refresh: clone missing sources, `git fetch` existing ones, reload the list. */
|
|
113
127
|
export declare function refreshTeamSkills(repoRoot: string): Promise<Skill[]>;
|
package/dist/skills-remote.js
CHANGED
|
@@ -452,16 +452,41 @@ export function shouldRecordFetchFailure(opts) {
|
|
|
452
452
|
export function __markCloneAttemptedForTests(repo) {
|
|
453
453
|
cloneAttempted.add(repo);
|
|
454
454
|
}
|
|
455
|
+
/** Test hook: hold this source's clone/fetch open until `until` settles (#859). */
|
|
456
|
+
export function __holdFetchForTests(repo, until) {
|
|
457
|
+
const held = until.finally(() => {
|
|
458
|
+
if (fetchInFlight.get(repo) === held)
|
|
459
|
+
fetchInFlight.delete(repo);
|
|
460
|
+
});
|
|
461
|
+
fetchInFlight.set(repo, held);
|
|
462
|
+
}
|
|
463
|
+
const cachedListPauses = new Map();
|
|
464
|
+
/**
|
|
465
|
+
* Test hook: park this project's next passive load right after its list-only
|
|
466
|
+
* pass until `until` settles (#859). Resolves once the load has parked there.
|
|
467
|
+
*/
|
|
468
|
+
export function __pauseAfterCachedListForTests(repoRoot, until) {
|
|
469
|
+
return new Promise((reached) => { cachedListPauses.set(repoRoot, { reached, until }); });
|
|
470
|
+
}
|
|
455
471
|
// Both maps are keyed by `repoRoot` (multi-project workspace, step 2.6): each
|
|
456
472
|
// project resolves its own `.ai/cezar/config.json` → `skillsRepos`, so one
|
|
457
473
|
// project's team-skill list must never be served under another project's scope.
|
|
458
474
|
const teamSkillsByRoot = new Map();
|
|
459
475
|
const firstLoadByRoot = new Map();
|
|
476
|
+
// The first load's list-only pass over the clones already on disk (#859).
|
|
477
|
+
const cachedListByRoot = new Map();
|
|
478
|
+
// Bumped by every post-fetch publication, so a list-only pass that finishes
|
|
479
|
+
// late never replaces a newer list (a concurrent refresh) in `teamSkillsByRoot`.
|
|
480
|
+
const postFetchListsByRoot = new Map();
|
|
460
481
|
function initialTeamSkillsLoad(repoRoot) {
|
|
461
482
|
const existing = firstLoadByRoot.get(repoRoot);
|
|
462
483
|
if (existing)
|
|
463
484
|
return existing;
|
|
464
|
-
|
|
485
|
+
let listed;
|
|
486
|
+
cachedListByRoot.set(repoRoot, new Promise((resolve) => { listed = resolve; }));
|
|
487
|
+
const load = loadTeamSkills(repoRoot, false, listed).catch(() => teamSkillsByRoot.get(repoRoot) ?? []);
|
|
488
|
+
// A load that failed before its list-only pass still settles the cached wait.
|
|
489
|
+
void load.then((skills) => listed(skills));
|
|
465
490
|
firstLoadByRoot.set(repoRoot, load);
|
|
466
491
|
return load;
|
|
467
492
|
}
|
|
@@ -483,6 +508,16 @@ export function getTeamSkillsCached(repoRoot) {
|
|
|
483
508
|
export function waitForTeamSkills(repoRoot) {
|
|
484
509
|
return initialTeamSkillsLoad(repoRoot);
|
|
485
510
|
}
|
|
511
|
+
/**
|
|
512
|
+
* The first load's list of the bare clones already on disk, published before
|
|
513
|
+
* any passive fetch (#859). Never waits on the network: no clone yet means an
|
|
514
|
+
* empty list, not a clone. When a refresh started this project's first load
|
|
515
|
+
* there is no list-only pass, and this answers with the current list.
|
|
516
|
+
*/
|
|
517
|
+
export function waitForCachedTeamSkills(repoRoot) {
|
|
518
|
+
void initialTeamSkillsLoad(repoRoot);
|
|
519
|
+
return cachedListByRoot.get(repoRoot) ?? Promise.resolve(teamSkillsByRoot.get(repoRoot) ?? []);
|
|
520
|
+
}
|
|
486
521
|
/** Refresh: clone missing sources, `git fetch` existing ones, reload the list. */
|
|
487
522
|
export async function refreshTeamSkills(repoRoot) {
|
|
488
523
|
const load = loadTeamSkills(repoRoot, true).catch(() => teamSkillsByRoot.get(repoRoot) ?? []);
|
|
@@ -528,16 +563,55 @@ function fetchBareSource(repo) {
|
|
|
528
563
|
fetchInFlight.set(repo, work);
|
|
529
564
|
return work;
|
|
530
565
|
}
|
|
531
|
-
|
|
532
|
-
|
|
566
|
+
/** First source wins a name, in configured source order. */
|
|
567
|
+
function mergeSourceLists(lists) {
|
|
533
568
|
const out = [];
|
|
534
569
|
const seen = new Set();
|
|
535
|
-
for (const
|
|
570
|
+
for (const skill of lists.flatMap((list) => list ?? [])) {
|
|
571
|
+
if (seen.has(skill.name))
|
|
572
|
+
continue;
|
|
573
|
+
seen.add(skill.name);
|
|
574
|
+
out.push(skill);
|
|
575
|
+
}
|
|
576
|
+
return out;
|
|
577
|
+
}
|
|
578
|
+
async function loadTeamSkills(repoRoot, refresh, onCachedList) {
|
|
579
|
+
const config = await loadConfig(repoRoot);
|
|
580
|
+
// Passive loads list the clones already on disk BEFORE any fetch (#859): the
|
|
581
|
+
// first restart past the TTL otherwise hides every team skill — and starves a
|
|
582
|
+
// recovered Continue of its playbook — for as long as the fetch takes.
|
|
583
|
+
let cached = [];
|
|
584
|
+
// Each source's stamp as the snapshot saw it. The bare clone and its stamp
|
|
585
|
+
// are shared by every project and process, so a fetch made for another
|
|
586
|
+
// project never bumps this one's generation; a moved stamp is how it shows.
|
|
587
|
+
let snapshotStamps = [];
|
|
588
|
+
const generation = postFetchListsByRoot.get(repoRoot) ?? 0;
|
|
589
|
+
// True once another load has published a post-fetch list since this one
|
|
590
|
+
// started: its snapshot may predate that list, so it must not win (#859).
|
|
591
|
+
const superseded = () => (postFetchListsByRoot.get(repoRoot) ?? 0) !== generation;
|
|
592
|
+
if (!refresh) {
|
|
593
|
+
snapshotStamps = config.skillsRepos.map((src) => readLastFetchAt(bareDirFor(src.repo)));
|
|
594
|
+
cached = await Promise.all(config.skillsRepos.map((src) => listRemoteSkills(src).catch(() => undefined)));
|
|
595
|
+
const pause = cachedListPauses.get(repoRoot);
|
|
596
|
+
if (pause) {
|
|
597
|
+
cachedListPauses.delete(repoRoot);
|
|
598
|
+
pause.reached();
|
|
599
|
+
await pause.until;
|
|
600
|
+
}
|
|
601
|
+
const listed = mergeSourceLists(cached);
|
|
602
|
+
if (!superseded())
|
|
603
|
+
teamSkillsByRoot.set(repoRoot, listed);
|
|
604
|
+
onCachedList?.(listed);
|
|
605
|
+
}
|
|
606
|
+
const lists = [];
|
|
607
|
+
for (const [index, src] of config.skillsRepos.entries()) {
|
|
536
608
|
const now = Date.now();
|
|
537
609
|
if (stampIsFresh(src.repo, now))
|
|
538
610
|
cloneAttempted.delete(src.repo);
|
|
611
|
+
let fetched = false;
|
|
539
612
|
try {
|
|
540
613
|
if (refresh) {
|
|
614
|
+
fetched = true;
|
|
541
615
|
await fetchBareSource(src.repo);
|
|
542
616
|
}
|
|
543
617
|
else if (shouldPassiveFetch({
|
|
@@ -546,24 +620,26 @@ async function loadTeamSkills(repoRoot, refresh) {
|
|
|
546
620
|
ttlMs: PASSIVE_FETCH_TTL_MS,
|
|
547
621
|
}) &&
|
|
548
622
|
!cloneAttempted.has(src.repo)) {
|
|
623
|
+
fetched = true;
|
|
549
624
|
await fetchBareSource(src.repo);
|
|
550
625
|
}
|
|
551
626
|
}
|
|
552
627
|
catch {
|
|
553
628
|
// offline / no access — list whatever an older clone has (or nothing)
|
|
554
629
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
catch {
|
|
564
|
-
// degrade: this source contributes nothing
|
|
565
|
-
}
|
|
630
|
+
// An unfetched source has not moved since the list-only pass unless
|
|
631
|
+
// another load fetched it meanwhile; reuse the snapshot only then.
|
|
632
|
+
const unmoved = !superseded() && readLastFetchAt(bareDirFor(src.repo)) === snapshotStamps[index];
|
|
633
|
+
const listed = fetched || !unmoved ? undefined : cached[index];
|
|
634
|
+
// degrade: a source that fails to list contributes nothing
|
|
635
|
+
lists.push(listed ?? (await listRemoteSkills(src).catch(() => undefined)));
|
|
566
636
|
}
|
|
637
|
+
const out = mergeSourceLists(lists);
|
|
638
|
+
// A refresh always publishes. A passive load that a newer post-fetch list
|
|
639
|
+
// overtook defers to it rather than replacing it with an older view.
|
|
640
|
+
if (!refresh && superseded())
|
|
641
|
+
return teamSkillsByRoot.get(repoRoot) ?? out;
|
|
642
|
+
postFetchListsByRoot.set(repoRoot, (postFetchListsByRoot.get(repoRoot) ?? 0) + 1);
|
|
567
643
|
teamSkillsByRoot.set(repoRoot, out);
|
|
568
644
|
return out;
|
|
569
645
|
}
|