@north-light/crouter 0.3.172 → 0.3.174
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/bin/runtime-selector.mjs +8 -1
- package/dist/api/__tests__/{client.test.js → serial/client.test.js} +2 -135
- package/dist/api/client.d.ts +12 -21
- package/dist/api/client.js +22 -31
- package/dist/api/dto/broker-ops.d.ts +158 -0
- package/dist/api/dto/broker-ops.js +6 -0
- package/dist/api/dto/crons.d.ts +6 -0
- package/dist/api/dto/human.d.ts +7 -1
- package/dist/api/dto/inbox.d.ts +1 -71
- package/dist/api/dto/inbox.js +1 -9
- package/dist/api/dto/nodes.d.ts +6 -7
- package/dist/api/dto/reviews.d.ts +1 -11
- package/dist/api/routes.d.ts +7 -4
- package/dist/api/routes.js +8 -5
- package/dist/build-root.js +3 -3
- package/dist/builtin-memory/04-base-worker.md +1 -4
- package/dist/builtin-memory/05-kinds/advisor/00-base.md +1 -5
- package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +2 -7
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -6
- package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -6
- package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -4
- package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +1 -2
- package/dist/builtin-memory/05-kinds/general/00-base.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/00-base.md +1 -6
- package/dist/builtin-memory/05-kinds/plan/reviewers/00-base.md +1 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +1 -4
- package/dist/builtin-memory/05-kinds/review/companion/00-base.md +2 -3
- package/dist/builtin-memory/05-kinds/spec/00-base.md +1 -2
- package/dist/builtin-memory/advisor/council.md +1 -6
- package/dist/builtin-memory/design.md +2 -7
- package/dist/builtin-memory/development.md +9 -14
- package/dist/builtin-memory/internal/marketplaces.md +2 -6
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/plugins.md +2 -8
- package/dist/builtin-memory/internal/storage-tiers.md +1 -2
- package/dist/builtin-memory/testing.md +41 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{provider-rotation.test.ts → serial/provider-rotation.test.ts} +106 -177
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +5 -15
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +1 -28
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +43 -88
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +55 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +60 -0
- package/dist/cli.js +2 -2
- package/dist/clients/attach/__tests__/attach-remote-readonly.test.js +37 -131
- package/dist/clients/attach/__tests__/chat-view-snapshot-ordering.test.js +71 -152
- package/dist/clients/attach/__tests__/context-message.test.js +16 -174
- package/dist/clients/attach/__tests__/crtr-output.test.js +36 -2069
- package/dist/clients/attach/__tests__/diagram.test.js +2 -2
- package/dist/clients/attach/__tests__/edit-diff.test.js +0 -4
- package/dist/clients/attach/__tests__/file-review-focus.test.js +2 -1
- package/dist/clients/attach/__tests__/group-activity.test.js +40 -229
- package/dist/clients/attach/__tests__/oauth-dialog-lifecycle.test.js +26 -76
- package/dist/clients/attach/__tests__/prompt-preview.test.js +4 -70
- package/dist/clients/attach/__tests__/ref-autocomplete.test.js +11 -125
- package/dist/clients/attach/__tests__/titled-editor-preview.test.js +14 -205
- package/dist/clients/attach/command.d.ts +2 -1
- package/dist/clients/attach/command.js +81 -76
- package/dist/clients/attach/input/capabilities.d.ts +27 -0
- package/dist/clients/attach/input/capabilities.js +96 -0
- package/dist/clients/attach/input/controller.d.ts +20 -182
- package/dist/clients/attach/input/controller.js +128 -537
- package/dist/clients/attach/input/overlay-owner.d.ts +41 -0
- package/dist/clients/attach/input/overlay-owner.js +199 -0
- package/dist/clients/attach/overlays/file-review.d.ts +0 -4
- package/dist/clients/attach/overlays/file-review.js +13 -5
- package/dist/clients/attach/overlays/graph.js +2 -10
- package/dist/clients/attach/render/chat-view.d.ts +4 -3
- package/dist/clients/attach/render/chat-view.js +39 -33
- package/dist/clients/attach/render/group-activity.d.ts +1 -0
- package/dist/clients/attach/render/group-activity.js +20 -13
- package/dist/clients/attach/render/group-recap.js +12 -3
- package/dist/clients/attach/session/connection.js +9 -15
- package/dist/clients/attach/session/editor-inventory.d.ts +6 -5
- package/dist/clients/attach/session/editor-inventory.js +7 -8
- package/dist/clients/attach/session/file-links.d.ts +13 -0
- package/dist/clients/attach/session/file-links.js +107 -0
- package/dist/clients/attach/session/frame.d.ts +3 -0
- package/dist/clients/attach/session/frame.js +10 -1
- package/dist/clients/attach/session/frames.js +2 -3
- package/dist/clients/attach/session/input-wiring.d.ts +4 -1
- package/dist/clients/attach/session/input-wiring.js +15 -14
- package/dist/clients/attach/session/mouse.d.ts +6 -1
- package/dist/clients/attach/session/mouse.js +8 -5
- package/dist/clients/attach/session/profile-files.js +18 -12
- package/dist/clients/attach/session/reconnect.js +1 -1
- package/dist/clients/attach/session/state-sync.js +1 -2
- package/dist/clients/attach/session/surface-host.d.ts +3 -2
- package/dist/clients/attach/session/surface-host.js +4 -4
- package/dist/clients/attach/slash/dispatch.d.ts +8 -31
- package/dist/clients/attach/slash/dispatch.js +34 -81
- package/dist/clients/attach/viewer.js +820 -860
- package/dist/clients/conversation/projection.d.ts +79 -0
- package/dist/clients/conversation/projection.js +341 -0
- package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +159 -0
- package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +151 -0
- package/dist/clients/inbox/controller.d.ts +0 -4
- package/dist/clients/inbox/controller.js +0 -2
- package/dist/clients/inbox/review/__tests__/review-core.test.js +4 -293
- package/dist/clients/inbox/review/__tests__/teardown-notice.test.js +100 -0
- package/dist/clients/inbox/review/comments-client.d.ts +3 -2
- package/dist/clients/inbox/review/comments-client.js +12 -12
- package/dist/clients/inbox/review/companion-pane.d.ts +30 -0
- package/dist/clients/inbox/review/companion-pane.js +86 -0
- package/dist/clients/inbox/review/document-surface.d.ts +182 -0
- package/dist/clients/inbox/review/{document-region.js → document-surface.js} +252 -48
- package/dist/clients/inbox/review/frame.js +5 -4
- package/dist/clients/inbox/review/launch.d.ts +8 -8
- package/dist/clients/inbox/review/launch.js +52 -92
- package/dist/clients/inbox/review/nvim.d.ts +2 -0
- package/dist/clients/inbox/review/nvim.js +14 -12
- package/dist/clients/inbox/review/review-client.d.ts +12 -8
- package/dist/clients/inbox/review/review-client.js +46 -35
- package/dist/clients/inbox/review-adapter.d.ts +3 -10
- package/dist/clients/inbox/review-adapter.js +27 -19
- package/dist/clients/inbox/surface.d.ts +0 -4
- package/dist/clients/inbox/surface.js +0 -2
- package/dist/clients/surfaces/host.d.ts +11 -3
- package/dist/commands/__tests__/api-client-cold-start-diagnostic.test.js +7 -34
- package/dist/commands/__tests__/human.test.js +11 -322
- package/dist/commands/api-client.d.ts +5 -5
- package/dist/commands/api-client.js +6 -85
- package/dist/commands/canvas-browse.js +3 -29
- package/dist/commands/canvas-config.js +1 -1
- package/dist/commands/canvas-use.js +3 -3
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/cron.js +5 -1
- package/dist/commands/human/queue.d.ts +0 -4
- package/dist/commands/human/queue.js +7 -40
- package/dist/commands/memory/list.d.ts +14 -0
- package/dist/commands/memory/list.js +35 -31
- package/dist/commands/memory/read.d.ts +7 -0
- package/dist/commands/memory/read.js +14 -8
- package/dist/commands/node/bash.d.ts +1 -0
- package/dist/commands/node/bash.js +198 -0
- package/dist/commands/node/create.d.ts +23 -0
- package/dist/commands/node/create.js +207 -0
- package/dist/commands/node/inspect.d.ts +2 -0
- package/dist/commands/node/inspect.js +302 -0
- package/dist/commands/node/lifecycle.d.ts +4 -0
- package/dist/commands/node/lifecycle.js +415 -0
- package/dist/commands/node/message.d.ts +1 -0
- package/dist/commands/node/message.js +164 -0
- package/dist/commands/node/pyramid.d.ts +2 -0
- package/dist/commands/node/pyramid.js +79 -0
- package/dist/commands/node/subscription.d.ts +1 -0
- package/dist/commands/node/subscription.js +82 -0
- package/dist/commands/node/wait.d.ts +1 -0
- package/dist/commands/node/wait.js +159 -0
- package/dist/commands/node-lifecycle-revive.js +10 -57
- package/dist/commands/node.d.ts +0 -42
- package/dist/commands/node.js +11 -2065
- package/dist/commands/search.js +1 -1
- package/dist/commands/surface/node/cycle.d.ts +1 -0
- package/dist/commands/surface/node/cycle.js +83 -0
- package/dist/commands/surface/node/focus.d.ts +1 -0
- package/dist/commands/surface/node/focus.js +67 -0
- package/dist/commands/surface/node/id.d.ts +1 -0
- package/dist/commands/surface/node/id.js +38 -0
- package/dist/commands/surface/node/pin.d.ts +1 -0
- package/dist/commands/surface/node/pin.js +42 -0
- package/dist/commands/surface/node/placement.d.ts +46 -0
- package/dist/commands/surface/node/placement.js +124 -0
- package/dist/commands/surface-node.js +4 -1
- package/dist/commands/surface-reload.js +1 -2
- package/dist/commands/surface-tmux-spread.js +1 -1
- package/dist/commands/surface.js +5 -20
- package/dist/commands/sys/__tests__/setup-core.test.js +18 -271
- package/dist/commands/sys/config.js +6 -6
- package/dist/commands/sys/logs.js +1 -1
- package/dist/commands/sys/panels/broker-limits-panel.d.ts +17 -0
- package/dist/commands/sys/panels/broker-limits-panel.js +104 -0
- package/dist/commands/sys/panels/dependencies-panel.d.ts +14 -0
- package/dist/commands/sys/panels/dependencies-panel.js +80 -0
- package/dist/commands/sys/panels/keybindings-panel.d.ts +56 -0
- package/dist/commands/sys/panels/keybindings-panel.js +703 -0
- package/dist/commands/sys/panels/keys-panel.d.ts +16 -0
- package/dist/commands/sys/panels/keys-panel.js +60 -0
- package/dist/commands/sys/panels/models-panel.d.ts +77 -0
- package/dist/commands/sys/panels/models-panel.js +647 -0
- package/dist/commands/sys/panels/panel.d.ts +45 -0
- package/dist/commands/sys/panels/panel.js +137 -0
- package/dist/commands/sys/panels/plugins-panel.d.ts +16 -0
- package/dist/commands/sys/panels/plugins-panel.js +80 -0
- package/dist/commands/sys/panels/prefs-panel.d.ts +21 -0
- package/dist/commands/sys/panels/prefs-panel.js +85 -0
- package/dist/commands/sys/panels/provider-panel.d.ts +62 -0
- package/dist/commands/sys/panels/provider-panel.js +538 -0
- package/dist/commands/sys/settings-shell.d.ts +55 -0
- package/dist/commands/sys/settings-shell.js +400 -0
- package/dist/commands/sys/settings.d.ts +2 -0
- package/dist/commands/sys/settings.js +31 -0
- package/dist/commands/sys/setup-core.d.ts +3 -33
- package/dist/commands/sys/setup-core.js +27 -78
- package/dist/commands/sys/setup-settings-runner.d.ts +24 -0
- package/dist/commands/sys/setup-settings-runner.js +63 -0
- package/dist/commands/sys/setup-wizard.d.ts +26 -125
- package/dist/commands/sys/setup-wizard.js +209 -2473
- package/dist/commands/sys/setup.js +5 -7
- package/dist/commands/sys.js +3 -2
- package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +50 -0
- package/dist/core/__tests__/broker-launch-candidates.test.js +20 -9
- package/dist/core/__tests__/broker-preflight.test.js +1 -1
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +108 -27
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +14 -0
- package/dist/core/__tests__/canvas.test.js +2 -36
- package/dist/core/__tests__/close.test.js +14 -3
- package/dist/core/__tests__/context-intro.test.js +32 -143
- package/dist/core/__tests__/cron-broker-capacity.test.js +22 -3
- package/dist/core/__tests__/daemon-boot.test.js +2 -20
- package/dist/core/__tests__/daemon-wedge.test.js +1 -1
- package/dist/core/__tests__/dead-node-policy-table.test.js +1 -18
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +2 -2
- package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
- package/dist/core/__tests__/focuses.test.js +4 -98
- package/dist/core/__tests__/helpers/broker-clients.d.ts +0 -3
- package/dist/core/__tests__/helpers/broker-clients.js +0 -14
- package/dist/core/__tests__/helpers/harness.d.ts +0 -8
- package/dist/core/__tests__/helpers/harness.js +7 -87
- package/dist/core/__tests__/helpers/viewer-panes.js +1 -1
- package/dist/core/__tests__/inline-memory-refs.test.js +5 -134
- package/dist/core/__tests__/kickoff.test.js +1 -1
- package/dist/core/__tests__/memory-resolver-precedence.test.js +29 -1
- package/dist/core/__tests__/migration.test.js +35 -48
- package/dist/core/__tests__/on-read-dedup-resume.test.js +22 -12
- package/dist/core/__tests__/passive-subscription.test.js +26 -49
- package/dist/core/__tests__/preview-registry-sync.test.js +1 -2
- package/dist/core/__tests__/prune.test.js +94 -1
- package/dist/core/__tests__/review-model-floor.test.js +0 -32
- package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +255 -0
- package/dist/core/__tests__/{full → seam}/broker-attach-stream.test.js +39 -7
- package/dist/core/__tests__/{full → seam}/broker-crash-teardown.test.js +30 -29
- package/dist/core/__tests__/{full → seam}/broker-pane-resolution.test.js +54 -5
- package/dist/core/__tests__/{full → seam}/broker-provider-retry.test.js +134 -82
- package/dist/core/__tests__/{full → seam}/broker-whip-interrupt.test.js +15 -4
- package/dist/core/__tests__/{full/idle-release-settlement.test.js → seam/dormancy-release.test.js} +69 -17
- package/dist/core/__tests__/{full/yield-message-transaction.test.js → seam/yield-refresh-transaction.test.js} +31 -9
- package/dist/core/__tests__/secrets.test.js +1 -7
- package/dist/core/__tests__/{broker-fork-seam.test.js → serial/broker-fork-seam.test.js} +58 -7
- package/dist/core/__tests__/{broker-sdk-wiring.test.js → serial/broker-sdk-wiring.test.js} +18 -162
- package/dist/core/__tests__/{broker-snapshot-history.test.js → serial/broker-snapshot-history.test.js} +9 -9
- package/dist/core/__tests__/{command-plugins.test.js → serial/command-plugins.test.js} +105 -15
- package/dist/core/__tests__/{deferred-no-wake.test.js → serial/deferred-no-wake.test.js} +23 -20
- package/dist/core/__tests__/{flagship-lifecycle.test.js → serial/flagship-lifecycle.test.js} +7 -7
- package/dist/core/__tests__/{host-teardown-process-group.test.js → serial/host-teardown-process-group.test.js} +6 -6
- package/dist/core/__tests__/{human-deliver-e2e.test.js → serial/human-deliver-e2e.test.js} +12 -12
- package/dist/core/__tests__/{live-mutation.test.js → serial/live-mutation.test.js} +5 -5
- package/dist/core/__tests__/{refresh-stall-recycle.test.js → serial/refresh-stall-recycle.test.js} +5 -14
- package/dist/core/__tests__/{revive.test.js → serial/revive.test.js} +15 -100
- package/dist/core/__tests__/{spawn-root.test.js → serial/spawn-root.test.js} +32 -15
- package/dist/core/__tests__/serial/subscription-delivery.test.js +126 -0
- package/dist/core/__tests__/serial/tmux-surface.test.js +256 -0
- package/dist/core/__tests__/{worktree.test.js → serial/worktree.test.js} +15 -15
- package/dist/core/__tests__/session-model.test.js +0 -18
- package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +9 -0
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +50 -0
- package/dist/core/broker-client/__tests__/transport-relay.test.js +15 -16
- package/dist/core/broker-client/__tests__/transport-socket.test.js +28 -0
- package/dist/core/broker-client/client.d.ts +2 -2
- package/dist/core/broker-client/client.js +4 -7
- package/dist/core/broker-client/transport-relay.d.ts +13 -19
- package/dist/core/broker-client/transport-relay.js +19 -34
- package/dist/core/broker-client/transport-socket.d.ts +2 -2
- package/dist/core/broker-client/transport-socket.js +9 -2
- package/dist/core/broker-client/transport.d.ts +7 -3
- package/dist/core/broker-client/transport.js +4 -4
- package/dist/core/canvas/__tests__/attention.test.js +1 -18
- package/dist/core/canvas/__tests__/remote-canvas-source.test.js +7 -64
- package/dist/core/canvas/__tests__/remote-transport.test.js +2 -3
- package/dist/core/canvas/__tests__/render-remote.test.js +7 -52
- package/dist/core/canvas/browse/app.d.ts +2 -17
- package/dist/core/canvas/browse/app.js +16 -88
- package/dist/core/canvas/browse/model.d.ts +0 -4
- package/dist/core/canvas/browse/model.js +1 -12
- package/dist/core/canvas/browse/render.js +6 -3
- package/dist/core/canvas/crons.d.ts +8 -5
- package/dist/core/canvas/crons.js +7 -6
- package/dist/core/canvas/extensions.d.ts +7 -3
- package/dist/core/canvas/extensions.js +16 -4
- package/dist/core/canvas/nav-model.d.ts +0 -4
- package/dist/core/canvas/nav-model.js +2 -14
- package/dist/core/canvas/node-order.d.ts +3 -0
- package/dist/core/canvas/node-order.js +12 -0
- package/dist/core/canvas/remote-canvas-source.d.ts +3 -6
- package/dist/core/canvas/remote-canvas-source.js +8 -15
- package/dist/core/command.js +11 -4
- package/dist/core/config.js +3 -1
- package/dist/core/events/envelope.js +1 -1
- package/dist/core/events/read.js +7 -21
- package/dist/core/events/source.d.ts +0 -2
- package/dist/core/events/source.js +0 -16
- package/dist/core/events/types.d.ts +1 -1
- package/dist/core/fault-classifier.d.ts +1 -1
- package/dist/core/feed/passive.d.ts +0 -11
- package/dist/core/feed/passive.js +1 -62
- package/dist/core/human/__tests__/{inbox-core.test.js → serial/inbox-core.test.js} +6 -6
- package/dist/core/keybindings/__tests__/resolve.test.js +1 -96
- package/dist/core/keybindings/background-bash.js +3 -4
- package/dist/core/keybindings/catalog.js +1 -1
- package/dist/core/memory/inline-ref-grammar.js +3 -3
- package/dist/core/memory-resolver.d.ts +11 -25
- package/dist/core/memory-resolver.js +51 -33
- package/dist/core/model-routes.d.ts +15 -0
- package/dist/core/model-routes.js +12 -0
- package/dist/core/pool-credential-store.js +1 -1
- package/dist/core/preview-registry.js +8 -9
- package/dist/core/profiles/__tests__/root-profile.test.js +4 -14
- package/dist/core/review/realize.d.ts +0 -13
- package/dist/core/review/realize.js +0 -40
- package/dist/core/review/types.d.ts +1 -1
- package/dist/core/runtime/__tests__/engine-route.test.js +2 -2
- package/dist/core/runtime/bearings.js +1 -11
- package/dist/core/runtime/broker/client-registry.d.ts +33 -0
- package/dist/core/runtime/broker/client-registry.js +164 -0
- package/dist/core/runtime/broker/daemon-ops.d.ts +19 -0
- package/dist/core/runtime/broker/daemon-ops.js +49 -0
- package/dist/core/runtime/broker/event-projection.d.ts +42 -0
- package/dist/core/runtime/broker/event-projection.js +178 -0
- package/dist/core/runtime/broker/fault-retry.d.ts +39 -0
- package/dist/core/runtime/broker/fault-retry.js +277 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +238 -0
- package/dist/core/runtime/broker/frame-dispatch.js +1537 -0
- package/dist/core/runtime/broker/inbox.d.ts +22 -0
- package/dist/core/runtime/broker/inbox.js +167 -0
- package/dist/core/runtime/broker/passive.d.ts +16 -0
- package/dist/core/runtime/broker/passive.js +44 -0
- package/dist/core/runtime/broker/read-ops.d.ts +23 -0
- package/dist/core/runtime/broker/read-ops.js +150 -0
- package/dist/core/runtime/broker/rebind.d.ts +74 -0
- package/dist/core/runtime/broker/rebind.js +211 -0
- package/dist/core/runtime/broker/tool-groups.d.ts +39 -0
- package/dist/core/runtime/broker/tool-groups.js +244 -0
- package/dist/core/runtime/broker-extension-render.d.ts +11 -0
- package/dist/core/runtime/broker-extension-render.js +181 -0
- package/dist/core/runtime/broker-persona-guidance.d.ts +13 -0
- package/dist/core/runtime/broker-persona-guidance.js +58 -0
- package/dist/core/runtime/broker-protocol.d.ts +1 -1
- package/dist/core/runtime/broker-protocol.js +1 -1
- package/dist/core/runtime/broker.d.ts +5 -196
- package/dist/core/runtime/broker.js +276 -3059
- package/dist/core/runtime/canvas-extensions.js +11 -2
- package/dist/core/runtime/fault.js +1 -1
- package/dist/core/runtime/model-selection.d.ts +9 -0
- package/dist/core/runtime/model-selection.js +12 -0
- package/dist/core/runtime/model-swap.d.ts +3 -3
- package/dist/core/runtime/model-swap.js +33 -25
- package/dist/core/runtime/placement-tmux.d.ts +3 -14
- package/dist/core/runtime/placement-tmux.js +11 -80
- package/dist/core/runtime/placement.d.ts +4 -2
- package/dist/core/runtime/placement.js +7 -12
- package/dist/core/runtime/promote.d.ts +14 -1
- package/dist/core/runtime/promote.js +1 -1
- package/dist/core/runtime/reset.d.ts +1 -1
- package/dist/core/runtime/roadmap.js +1 -1
- package/dist/core/runtime/spawn.d.ts +8 -0
- package/dist/core/runtime/spawn.js +36 -6
- package/dist/core/runtime/stop-guard.d.ts +4 -3
- package/dist/core/runtime/stop-guard.js +36 -18
- package/dist/core/runtime/stop-signals.d.ts +4 -1
- package/dist/core/runtime/stream-watchdog.js +4 -5
- package/dist/core/runtime/structured-output.d.ts +15 -2
- package/dist/core/runtime/structured-output.js +35 -12
- package/dist/core/runtime/tmux-bindings.d.ts +25 -0
- package/dist/core/runtime/tmux-bindings.js +682 -0
- package/dist/core/runtime/tmux-chrome.d.ts +2 -1
- package/dist/core/runtime/tmux-chrome.js +3 -2
- package/dist/core/runtime/{tmux.d.ts → tmux-driver.d.ts} +18 -26
- package/dist/core/runtime/tmux-driver.js +598 -0
- package/dist/core/runtime/view-socket.d.ts +14 -0
- package/dist/core/runtime/view-socket.js +83 -0
- package/dist/core/session-model/session-state.d.ts +1 -2
- package/dist/core/session-model/session-state.js +5 -8
- package/dist/core/spawn.d.ts +1 -1
- package/dist/core/spawn.js +2 -2
- package/dist/core/substrate/gate.d.ts +1 -1
- package/dist/core/substrate/on-read-pure.d.ts +7 -0
- package/dist/core/substrate/on-read-pure.js +272 -0
- package/dist/core/termrender/termrender.js +32 -2
- package/dist/core/user-settings.d.ts +23 -1
- package/dist/core/user-settings.js +18 -1
- package/dist/daemon/api/__tests__/node-model-validation.test.js +134 -0
- package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +98 -0
- package/dist/daemon/api/__tests__/{full → seam}/api-server.test.js +6 -48
- package/dist/daemon/api/__tests__/{full → seam}/b10-attach-modelauth.test.js +17 -44
- package/dist/daemon/api/__tests__/{full/push-parity.test.js → seam/leaf-api-parity.test.js} +61 -42
- package/dist/daemon/api/bridge.js +40 -81
- package/dist/daemon/api/handlers/broker-ops.d.ts +2 -0
- package/dist/daemon/api/handlers/broker-ops.js +383 -0
- package/dist/daemon/api/handlers/crons.js +9 -4
- package/dist/daemon/api/handlers/files.js +6 -7
- package/dist/daemon/api/handlers/inbox.js +2 -340
- package/dist/daemon/api/handlers/nodes.d.ts +10 -1
- package/dist/daemon/api/handlers/nodes.js +74 -39
- package/dist/daemon/api/handlers/review-comments.js +5 -1
- package/dist/daemon/api/handlers/reviews.js +0 -19
- package/dist/daemon/api/map.js +25 -4
- package/dist/daemon/api/router.js +2 -3
- package/dist/daemon/api/server.js +5 -7
- package/dist/daemon/cron-run.d.ts +4 -0
- package/dist/daemon/cron-run.js +30 -13
- package/dist/daemon/crtrd.d.ts +3 -58
- package/dist/daemon/crtrd.js +28 -1244
- package/dist/daemon/manage.js +0 -1
- package/dist/daemon/reconcilers/broker-supervision.d.ts +50 -0
- package/dist/daemon/reconcilers/broker-supervision.js +549 -0
- package/dist/daemon/reconcilers/cron-lane.d.ts +10 -0
- package/dist/daemon/reconcilers/cron-lane.js +32 -0
- package/dist/daemon/reconcilers/dormant-inbox.d.ts +18 -0
- package/dist/daemon/reconcilers/dormant-inbox.js +143 -0
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +22 -0
- package/dist/daemon/reconcilers/storage-maintenance.js +213 -0
- package/dist/daemon/review/finish.js +2 -5
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +34 -53
- package/dist/pi-extensions/__tests__/canvas-recap.test.js +16 -113
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +72 -99
- package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +29 -12
- package/dist/pi-extensions/broker-local.d.ts +20 -0
- package/dist/pi-extensions/broker-local.js +89 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +3 -3
- package/dist/pi-extensions/canvas-context-intro.js +22 -11
- package/dist/pi-extensions/canvas-doc-substrate.js +17 -34
- package/dist/pi-extensions/canvas-goal-capture.js +7 -12
- package/dist/pi-extensions/canvas-inbox-watcher.js +124 -139
- package/dist/pi-extensions/canvas-passive-context.d.ts +3 -3
- package/dist/pi-extensions/canvas-passive-context.js +22 -18
- package/dist/pi-extensions/canvas-recap.d.ts +2 -2
- package/dist/pi-extensions/canvas-recap.js +48 -21
- package/dist/pi-extensions/canvas-review-boundary.js +10 -4
- package/dist/pi-extensions/canvas-stophook.d.ts +0 -5
- package/dist/pi-extensions/canvas-stophook.js +149 -431
- package/dist/pi-extensions/canvas-structured-output.js +52 -30
- package/dist/pi-extensions/envelope-merge.d.ts +13 -0
- package/dist/pi-extensions/envelope-merge.js +29 -0
- package/dist/shared/working-activity.d.ts +3 -2
- package/dist/shared/working-activity.js +3 -2
- package/dist/types.d.ts +6 -5
- package/dist/types.js +2 -2
- package/docs/compat/hearth-crtr-v1.md +4 -12
- package/docs/compat/hearth-crtr-v2.md +3 -11
- package/docs/compat/hearth-crtr-v3.md +4 -12
- package/docs/compat/hearth-crtr-v4.md +3 -11
- package/docs/compat/hearth-crtr-v5.md +3 -11
- package/docs/public-api.md +3 -7
- package/package.json +7 -49
- package/runtime.lock.json +2766 -14533
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/claude-plugin-commands.test.ts +0 -49
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/memory-slash-commands.test.ts +0 -194
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/statusline.test.ts +0 -68
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/test.ts +0 -119
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +0 -439
- package/dist/clients/attach/__tests__/attach-keybindings.test.js +0 -170
- package/dist/clients/attach/__tests__/crtr-output-coverage.test.js +0 -57
- package/dist/clients/attach/__tests__/editor-frame-title.test.js +0 -19
- package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +0 -32
- package/dist/clients/attach/__tests__/markdown-headings.test.js +0 -35
- package/dist/clients/attach/__tests__/session-identity-refresh.test.js +0 -32
- package/dist/clients/conversation/conversation.d.ts +0 -65
- package/dist/clients/conversation/conversation.js +0 -318
- package/dist/clients/conversation/frames.d.ts +0 -30
- package/dist/clients/conversation/frames.js +0 -52
- package/dist/clients/conversation/region.d.ts +0 -51
- package/dist/clients/conversation/region.js +0 -265
- package/dist/clients/inbox/__tests__/inbox-controller.test.js +0 -247
- package/dist/clients/inbox/__tests__/mount-panel.test.js +0 -776
- package/dist/clients/inbox/review/__tests__/remap.test.js +0 -196
- package/dist/clients/inbox/review/columns.d.ts +0 -22
- package/dist/clients/inbox/review/columns.js +0 -40
- package/dist/clients/inbox/review/composition.d.ts +0 -60
- package/dist/clients/inbox/review/composition.js +0 -278
- package/dist/clients/inbox/review/document-region.d.ts +0 -120
- package/dist/clients/surfaces/region.d.ts +0 -16
- package/dist/clients/web/__tests__/push-engine.test.js +0 -158
- package/dist/clients/web/__tests__/push-registry.test.d.ts +0 -1
- package/dist/clients/web/__tests__/push-registry.test.js +0 -177
- package/dist/clients/web/dev-server.d.ts +0 -7
- package/dist/clients/web/dev-server.js +0 -46
- package/dist/clients/web/events.d.ts +0 -19
- package/dist/clients/web/events.js +0 -163
- package/dist/clients/web/push-engine.d.ts +0 -44
- package/dist/clients/web/push-engine.js +0 -125
- package/dist/clients/web/push-registry.d.ts +0 -85
- package/dist/clients/web/push-registry.js +0 -373
- package/dist/clients/web/server.d.ts +0 -19
- package/dist/clients/web/server.js +0 -661
- package/dist/clients/web/web-client/shared/protocol.d.ts +0 -214
- package/dist/clients/web/web-client/shared/protocol.js +0 -8
- package/dist/clients/web/web-cmd.d.ts +0 -2
- package/dist/clients/web/web-cmd.js +0 -141
- package/dist/commands/__tests__/api-canvas-source.test.d.ts +0 -1
- package/dist/commands/__tests__/api-canvas-source.test.js +0 -37
- package/dist/commands/__tests__/canvas-config.test.d.ts +0 -1
- package/dist/commands/__tests__/canvas-config.test.js +0 -107
- package/dist/commands/__tests__/node-no-kickoff-flag.test.d.ts +0 -1
- package/dist/commands/__tests__/node-no-kickoff-flag.test.js +0 -73
- package/dist/commands/__tests__/node-worktree-flag.test.d.ts +0 -1
- package/dist/commands/__tests__/node-worktree-flag.test.js +0 -49
- package/dist/commands/__tests__/search-contents.test.d.ts +0 -1
- package/dist/commands/__tests__/search-contents.test.js +0 -52
- package/dist/commands/memory/__tests__/lint.test.d.ts +0 -1
- package/dist/commands/memory/__tests__/lint.test.js +0 -15
- package/dist/commands/sys/__tests__/config-broker-thresholds.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/config-broker-thresholds.test.js +0 -95
- package/dist/commands/sys/__tests__/config-keybindings.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/config-keybindings.test.js +0 -55
- package/dist/commands/sys/__tests__/helpers/setup-runtime.d.ts +0 -5
- package/dist/commands/sys/__tests__/helpers/setup-runtime.js +0 -19
- package/dist/commands/sys/__tests__/setup-front-door.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/setup-front-door.test.js +0 -102
- package/dist/commands/sys/__tests__/setup-models.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/setup-models.test.js +0 -205
- package/dist/commands/sys/__tests__/setup-providers-viewport.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/setup-providers-viewport.test.js +0 -155
- package/dist/commands/sys/__tests__/sync-deps.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/sync-deps.test.js +0 -179
- package/dist/commands/sys/__tests__/sync-import.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/sync-import.test.js +0 -315
- package/dist/core/__tests__/base-worker-prompt.test.d.ts +0 -1
- package/dist/core/__tests__/base-worker-prompt.test.js +0 -57
- package/dist/core/__tests__/command-plugins-surfaces.test.d.ts +0 -1
- package/dist/core/__tests__/command-plugins-surfaces.test.js +0 -359
- package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +0 -1
- package/dist/core/__tests__/connection-reconnect-resume.test.js +0 -36
- package/dist/core/__tests__/finalization-migration.test.d.ts +0 -1
- package/dist/core/__tests__/finalization-migration.test.js +0 -38
- package/dist/core/__tests__/fixtures/extra-extension.d.ts +0 -1
- package/dist/core/__tests__/fixtures/extra-extension.js +0 -3
- package/dist/core/__tests__/fixtures/spawn-proof-target.test.d.ts +0 -1
- package/dist/core/__tests__/fixtures/spawn-proof-target.test.js +0 -15
- package/dist/core/__tests__/fork.test.d.ts +0 -1
- package/dist/core/__tests__/fork.test.js +0 -108
- package/dist/core/__tests__/full/broker-attach-limits.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-attach-limits.test.js +0 -147
- package/dist/core/__tests__/full/broker-deliver-ack.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-deliver-ack.test.js +0 -73
- package/dist/core/__tests__/full/broker-dialogs.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-dialogs.test.js +0 -180
- package/dist/core/__tests__/full/broker-display-snapshot.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-display-snapshot.test.js +0 -75
- package/dist/core/__tests__/full/broker-dormant-wake.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-dormant-wake.test.js +0 -130
- package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +0 -57
- package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +0 -80
- package/dist/core/__tests__/full/cascade-close.test.d.ts +0 -1
- package/dist/core/__tests__/full/cascade-close.test.js +0 -240
- package/dist/core/__tests__/full/detach-focus.test.d.ts +0 -1
- package/dist/core/__tests__/full/detach-focus.test.js +0 -126
- package/dist/core/__tests__/full/idle-release-settlement.test.d.ts +0 -1
- package/dist/core/__tests__/full/issue-146-yield-spawn-focus.test.d.ts +0 -1
- package/dist/core/__tests__/full/issue-146-yield-spawn-focus.test.js +0 -107
- package/dist/core/__tests__/full/park-clock-release.test.d.ts +0 -1
- package/dist/core/__tests__/full/park-clock-release.test.js +0 -56
- package/dist/core/__tests__/full/yield-message-transaction.test.d.ts +0 -1
- package/dist/core/__tests__/memory-index-render.test.d.ts +0 -1
- package/dist/core/__tests__/memory-index-render.test.js +0 -60
- package/dist/core/__tests__/model-routes.test.d.ts +0 -1
- package/dist/core/__tests__/model-routes.test.js +0 -41
- package/dist/core/__tests__/on-read-identity.test.d.ts +0 -1
- package/dist/core/__tests__/on-read-identity.test.js +0 -61
- package/dist/core/__tests__/phase4-review-migration.test.d.ts +0 -1
- package/dist/core/__tests__/phase4-review-migration.test.js +0 -100
- package/dist/core/__tests__/prune-to-limit.test.d.ts +0 -1
- package/dist/core/__tests__/prune-to-limit.test.js +0 -126
- package/dist/core/__tests__/reset.test.d.ts +0 -1
- package/dist/core/__tests__/reset.test.js +0 -69
- package/dist/core/__tests__/spawn-no-kickoff.test.d.ts +0 -1
- package/dist/core/__tests__/spawn-no-kickoff.test.js +0 -115
- package/dist/core/__tests__/steer-note.test.d.ts +0 -1
- package/dist/core/__tests__/steer-note.test.js +0 -41
- package/dist/core/__tests__/subcommand-tier.test.d.ts +0 -1
- package/dist/core/__tests__/subcommand-tier.test.js +0 -99
- package/dist/core/__tests__/subscription-delivery.test.js +0 -211
- package/dist/core/__tests__/test-runner-cheap-sharding.test.d.ts +0 -1
- package/dist/core/__tests__/test-runner-cheap-sharding.test.js +0 -97
- package/dist/core/__tests__/tmux-surface.test.js +0 -484
- package/dist/core/__tests__/wake-bearings.test.d.ts +0 -1
- package/dist/core/__tests__/wake-bearings.test.js +0 -102
- package/dist/core/canvas/__tests__/remote-event-stream.test.d.ts +0 -1
- package/dist/core/canvas/__tests__/remote-event-stream.test.js +0 -146
- package/dist/core/canvas/__tests__/source-resolve.test.d.ts +0 -1
- package/dist/core/canvas/__tests__/source-resolve.test.js +0 -77
- package/dist/core/canvas/browse/__tests__/model.test.d.ts +0 -1
- package/dist/core/canvas/browse/__tests__/model.test.js +0 -290
- package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.d.ts +0 -1
- package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.js +0 -134
- package/dist/core/canvas/browse/__tests__/render.test.d.ts +0 -1
- package/dist/core/canvas/browse/__tests__/render.test.js +0 -111
- package/dist/core/canvas/browse/rebuild-coalescer.d.ts +0 -7
- package/dist/core/canvas/browse/rebuild-coalescer.js +0 -61
- package/dist/core/canvas/remote-event-stream.d.ts +0 -24
- package/dist/core/canvas/remote-event-stream.js +0 -94
- package/dist/core/feed/__tests__/coalesce-human.test.d.ts +0 -1
- package/dist/core/feed/__tests__/coalesce-human.test.js +0 -36
- package/dist/core/feed/__tests__/empty-node-guard.test.d.ts +0 -1
- package/dist/core/feed/__tests__/empty-node-guard.test.js +0 -45
- package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +0 -1
- package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +0 -84
- package/dist/core/human/__tests__/visible-paths.test.d.ts +0 -1
- package/dist/core/human/__tests__/visible-paths.test.js +0 -28
- package/dist/core/keybindings/__tests__/inbox-affordance.test.d.ts +0 -1
- package/dist/core/keybindings/__tests__/inbox-affordance.test.js +0 -38
- package/dist/core/review/__tests__/project.test.d.ts +0 -1
- package/dist/core/review/__tests__/project.test.js +0 -71
- package/dist/core/runtime/__tests__/command-expansion.test.d.ts +0 -1
- package/dist/core/runtime/__tests__/command-expansion.test.js +0 -25
- package/dist/core/runtime/__tests__/session-visibility.test.d.ts +0 -1
- package/dist/core/runtime/__tests__/session-visibility.test.js +0 -119
- package/dist/core/runtime/__tests__/spawn-at-given-id.test.d.ts +0 -1
- package/dist/core/runtime/__tests__/spawn-at-given-id.test.js +0 -34
- package/dist/core/runtime/tmux.js +0 -1314
- package/dist/daemon/api/__tests__/full/push-parity.test.d.ts +0 -1
- package/dist/daemon/api/__tests__/full/worktree-parity.test.d.ts +0 -1
- package/dist/daemon/api/__tests__/full/worktree-parity.test.js +0 -160
- package/dist/daemon/api/__tests__/inbox.test.d.ts +0 -1
- package/dist/daemon/api/__tests__/inbox.test.js +0 -489
- package/dist/web-client/apple-touch-icon.png +0 -0
- package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
- package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
- package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
- package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
- package/dist/web-client/assets/index-CCPaLeW7.css +0 -2
- package/dist/web-client/assets/index-kyOWsnZK.js +0 -86
- package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
- package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
- package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
- package/dist/web-client/assets/workbox-window.prod.es5-Bd17z0YL.js +0 -2
- package/dist/web-client/icon-192.png +0 -0
- package/dist/web-client/icon-512.png +0 -0
- package/dist/web-client/icon-maskable-512.png +0 -0
- package/dist/web-client/index.html +0 -17
- package/dist/web-client/manifest.webmanifest +0 -1
- package/dist/web-client/sw.js +0 -1
- /package/dist/api/__tests__/{client.test.d.ts → serial/client.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{inbox-controller.test.d.ts → serial/inbox-controller.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{mount-panel.test.d.ts → serial/mount-panel.test.d.ts} +0 -0
- /package/dist/clients/{attach/__tests__/attach-chrome-remote.test.d.ts → inbox/review/__tests__/teardown-notice.test.d.ts} +0 -0
- /package/dist/{clients/attach/__tests__/attach-keybindings.test.d.ts → core/__tests__/broker-extension-canvas-db-boundary.test.d.ts} +0 -0
- /package/dist/{clients/attach/__tests__/crtr-output-coverage.test.d.ts → core/__tests__/seam/broker-attach-multiclient.test.d.ts} +0 -0
- /package/dist/core/__tests__/{full → seam}/broker-attach-stream.test.d.ts +0 -0
- /package/dist/core/__tests__/{full → seam}/broker-crash-teardown.test.d.ts +0 -0
- /package/dist/core/__tests__/{full → seam}/broker-pane-resolution.test.d.ts +0 -0
- /package/dist/core/__tests__/{full → seam}/broker-provider-retry.test.d.ts +0 -0
- /package/dist/core/__tests__/{full → seam}/broker-whip-interrupt.test.d.ts +0 -0
- /package/dist/{clients/attach/__tests__/editor-frame-title.test.d.ts → core/__tests__/seam/dormancy-release.test.d.ts} +0 -0
- /package/dist/{clients/attach/__tests__/input-controller-extension-command.test.d.ts → core/__tests__/seam/yield-refresh-transaction.test.d.ts} +0 -0
- /package/dist/core/__tests__/{broker-fork-seam.test.d.ts → serial/broker-fork-seam.test.d.ts} +0 -0
- /package/dist/core/__tests__/{broker-sdk-wiring.test.d.ts → serial/broker-sdk-wiring.test.d.ts} +0 -0
- /package/dist/core/__tests__/{broker-snapshot-history.test.d.ts → serial/broker-snapshot-history.test.d.ts} +0 -0
- /package/dist/core/__tests__/{command-plugins.test.d.ts → serial/command-plugins.test.d.ts} +0 -0
- /package/dist/core/__tests__/{deferred-no-wake.test.d.ts → serial/deferred-no-wake.test.d.ts} +0 -0
- /package/dist/core/__tests__/{flagship-lifecycle.test.d.ts → serial/flagship-lifecycle.test.d.ts} +0 -0
- /package/dist/core/__tests__/{host-teardown-process-group.test.d.ts → serial/host-teardown-process-group.test.d.ts} +0 -0
- /package/dist/core/__tests__/{human-deliver-e2e.test.d.ts → serial/human-deliver-e2e.test.d.ts} +0 -0
- /package/dist/core/__tests__/{live-mutation.test.d.ts → serial/live-mutation.test.d.ts} +0 -0
- /package/dist/core/__tests__/{refresh-stall-recycle.test.d.ts → serial/refresh-stall-recycle.test.d.ts} +0 -0
- /package/dist/core/__tests__/{revive.test.d.ts → serial/revive.test.d.ts} +0 -0
- /package/dist/core/__tests__/{spawn-root.test.d.ts → serial/spawn-root.test.d.ts} +0 -0
- /package/dist/core/__tests__/{subscription-delivery.test.d.ts → serial/subscription-delivery.test.d.ts} +0 -0
- /package/dist/core/__tests__/{tmux-surface.test.d.ts → serial/tmux-surface.test.d.ts} +0 -0
- /package/dist/core/__tests__/{worktree.test.d.ts → serial/worktree.test.d.ts} +0 -0
- /package/dist/{clients/attach/__tests__/markdown-headings.test.d.ts → core/__tests__/watchdog-abort-arms-retry.test.d.ts} +0 -0
- /package/dist/{clients/attach/__tests__/session-identity-refresh.test.d.ts → core/broker-client/__tests__/transport-socket.test.d.ts} +0 -0
- /package/dist/core/human/__tests__/{inbox-core.test.d.ts → serial/inbox-core.test.d.ts} +0 -0
- /package/dist/{clients/inbox/review/__tests__/remap.test.d.ts → daemon/api/__tests__/node-model-validation.test.d.ts} +0 -0
- /package/dist/{clients/surfaces/region.js → daemon/api/__tests__/review-terminal-conflict.test.d.ts} +0 -0
- /package/dist/daemon/api/__tests__/{full → seam}/api-server.test.d.ts +0 -0
- /package/dist/daemon/api/__tests__/{full → seam}/b10-attach-modelauth.test.d.ts +0 -0
- /package/dist/{clients/web/__tests__/push-engine.test.d.ts → daemon/api/__tests__/seam/leaf-api-parity.test.d.ts} +0 -0
|
@@ -1,661 +0,0 @@
|
|
|
1
|
-
// server.ts — the `surface web serve` dev shell: a thin reverse proxy in front
|
|
2
|
-
// of crtrd (spec §7.5, C-1). It hosts NO canvas writer of its own — crtrd is the
|
|
3
|
-
// sole writer. This process opens NO `canvas.db`, spawns NO subprocess bridge,
|
|
4
|
-
// and terminates NO WebSocket. It serves, on one origin:
|
|
5
|
-
// • /v1/* → REVERSE PROXY to crtrd's unix socket. The SPA (and
|
|
6
|
-
// any /v1 client) reaches the REST API THROUGH this
|
|
7
|
-
// shell; the shell owns the auth boundary (crtrd
|
|
8
|
-
// applies none), then streams the request/response
|
|
9
|
-
// verbatim to/from the socket.
|
|
10
|
-
// • WS /node/<id> → TRANSPARENT TUNNEL to crtrd's
|
|
11
|
-
// `GET /v1/nodes/{id}/attach`. The shell does NOT
|
|
12
|
-
// terminate the WebSocket — it forwards the upgrade
|
|
13
|
-
// rewritten to the attach path and pipes bytes both
|
|
14
|
-
// directions. crtrd owns the handshake and
|
|
15
|
-
// attach-implies-revive; the browser is the SAME
|
|
16
|
-
// protocol peer as `crtr surface attach`.
|
|
17
|
-
// • GET /* → the shell SPA (static dist/web-client/, or Vite
|
|
18
|
-
// middleware in --dev) + SPA fallback.
|
|
19
|
-
// • GET /__crtr/events → the SSE change-invalidation lane (events.ts).
|
|
20
|
-
// canvas.db-free; explicitly out of P1 scope, kept.
|
|
21
|
-
// • Web push (§push) → GET /__crtr/push/vapid-public-key, POST/DELETE
|
|
22
|
-
// /__crtr/push/subscribe. A server-side diff engine
|
|
23
|
-
// (push-engine.ts) watches the SAME SSE invalidations
|
|
24
|
-
// above and sends web-push to a persisted, auth-scoped
|
|
25
|
-
// registry (push-registry.ts). Its canvas reads go
|
|
26
|
-
// through the crtrd API (via `cliClient()` command
|
|
27
|
-
// leaves), never `canvas.db`. Out of P1 scope, kept.
|
|
28
|
-
//
|
|
29
|
-
// §0 ONE-WRITER INVARIANT: this process never mutates the canvas. Every write
|
|
30
|
-
// and every canvas read flows to crtrd — the `/v1/*` proxy and the `/node/<id>`
|
|
31
|
-
// attach tunnel both terminate at crtrd's unix socket, and the push engine's
|
|
32
|
-
// reads route through the crtrd-backed command leaves. There is no local
|
|
33
|
-
// transport, no `canvas.db` handle, and no direct broker socket here.
|
|
34
|
-
import { createServer as createHttpServer, request as httpRequest } from 'node:http';
|
|
35
|
-
import { createConnection, isIP } from 'node:net';
|
|
36
|
-
import { createHash } from 'node:crypto';
|
|
37
|
-
import { createReadStream, existsSync, statSync } from 'node:fs';
|
|
38
|
-
import { fileURLToPath } from 'node:url';
|
|
39
|
-
import { dirname, extname, join, normalize, resolve } from 'node:path';
|
|
40
|
-
import webpush from 'web-push';
|
|
41
|
-
import { apiSocketPath, isSafeNodeId } from '../../core/canvas/paths.js';
|
|
42
|
-
import { emitEvent } from '../../core/events/emit.js';
|
|
43
|
-
import { bindWebEventSource } from '../../core/events/source.js';
|
|
44
|
-
import { canvasRosterLeaf } from '../../commands/canvas-roster.js';
|
|
45
|
-
import { startEventHub } from './events.js';
|
|
46
|
-
import { createDevServer } from './dev-server.js';
|
|
47
|
-
import { PushLastSeenStore, PushRegistryStore, PushRequestError, loadOrCreateVapidKeypair, parsePushDeleteBody, parsePushSubscriptionBody, pushBodyMaxBytes, } from './push-registry.js';
|
|
48
|
-
import { PushDiffEngine } from './push-engine.js';
|
|
49
|
-
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
50
|
-
/** Candidate dirs to serve the shell SPA bundle from, in priority order: an
|
|
51
|
-
* explicit override, the compiled-module-relative `dist/web-client/` (what
|
|
52
|
-
* `vite build` emits and what we serve in production), and the placeholder
|
|
53
|
-
* copied from source. The first that exists wins. (--dev bypasses this entirely
|
|
54
|
-
* — Vite middleware owns asset serving in that mode.) */
|
|
55
|
-
function resolveClientDir() {
|
|
56
|
-
const candidates = [
|
|
57
|
-
process.env['CRTR_WEB_CLIENT_DIR'],
|
|
58
|
-
join(HERE, '../../web-client'),
|
|
59
|
-
resolve(HERE, '../../../dist/web-client'),
|
|
60
|
-
].filter((p) => p !== undefined && p !== '');
|
|
61
|
-
for (const dir of candidates) {
|
|
62
|
-
if (existsSync(dir))
|
|
63
|
-
return dir;
|
|
64
|
-
}
|
|
65
|
-
// Fall back to the module-relative path even if absent; missing-file handling
|
|
66
|
-
// below returns a clear 503 so a not-yet-built shell is diagnosable.
|
|
67
|
-
return join(HERE, '../../web-client');
|
|
68
|
-
}
|
|
69
|
-
const CONTENT_TYPES = {
|
|
70
|
-
'.html': 'text/html; charset=utf-8',
|
|
71
|
-
'.js': 'text/javascript; charset=utf-8',
|
|
72
|
-
'.mjs': 'text/javascript; charset=utf-8',
|
|
73
|
-
'.css': 'text/css; charset=utf-8',
|
|
74
|
-
'.json': 'application/json; charset=utf-8',
|
|
75
|
-
'.webmanifest': 'application/manifest+json',
|
|
76
|
-
'.svg': 'image/svg+xml',
|
|
77
|
-
'.png': 'image/png',
|
|
78
|
-
'.jpg': 'image/jpeg',
|
|
79
|
-
'.jpeg': 'image/jpeg',
|
|
80
|
-
'.gif': 'image/gif',
|
|
81
|
-
'.webp': 'image/webp',
|
|
82
|
-
'.ico': 'image/x-icon',
|
|
83
|
-
'.woff': 'font/woff',
|
|
84
|
-
'.woff2': 'font/woff2',
|
|
85
|
-
'.map': 'application/json; charset=utf-8',
|
|
86
|
-
};
|
|
87
|
-
function contentTypeFor(path) {
|
|
88
|
-
return CONTENT_TYPES[extname(path).toLowerCase()] ?? 'application/octet-stream';
|
|
89
|
-
}
|
|
90
|
-
/** Match a WS/GET path of the shape `/node/<id>` (with any number of leading
|
|
91
|
-
* segments, future-proofing a mount prefix). Returns the decoded node id, or
|
|
92
|
-
* null if the path doesn't match or the id is undecodable (malformed `%`). The
|
|
93
|
-
* caller must still validate the decoded id with the shared persisted-node-ID
|
|
94
|
-
* predicate before building a filesystem path — see {@link isSafeNodeId}. */
|
|
95
|
-
function nodeIdFromPath(pathname) {
|
|
96
|
-
const m = /\/node\/([^/?#]+)\/?$/.exec(pathname);
|
|
97
|
-
if (m === null)
|
|
98
|
-
return null;
|
|
99
|
-
try {
|
|
100
|
-
return decodeURIComponent(m[1]);
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
return null; // malformed percent-encoding — treat as no match
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
function isLoopbackHost(host) {
|
|
107
|
-
const lower = host.toLowerCase();
|
|
108
|
-
const ipVersion = isIP(host);
|
|
109
|
-
if (lower === 'localhost' || lower === '::1')
|
|
110
|
-
return true;
|
|
111
|
-
if (ipVersion === 4 && host.startsWith('127.'))
|
|
112
|
-
return true;
|
|
113
|
-
if (lower === '::ffff:127.0.0.1')
|
|
114
|
-
return true;
|
|
115
|
-
if (ipVersion === 6 && lower.startsWith('::ffff:127.'))
|
|
116
|
-
return true;
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
function hostForUrl(host) {
|
|
120
|
-
return host.includes(':') ? `[${host}]` : host;
|
|
121
|
-
}
|
|
122
|
-
function requestTokens(req) {
|
|
123
|
-
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
124
|
-
const tokens = [];
|
|
125
|
-
const queryToken = url.searchParams.get('token');
|
|
126
|
-
if (queryToken !== null && queryToken !== '')
|
|
127
|
-
tokens.push(queryToken);
|
|
128
|
-
const authorization = req.headers.authorization;
|
|
129
|
-
if (typeof authorization === 'string') {
|
|
130
|
-
const match = /^Bearer\s+(.+)$/.exec(authorization);
|
|
131
|
-
if (match !== null && match[1] !== '')
|
|
132
|
-
tokens.push(match[1]);
|
|
133
|
-
}
|
|
134
|
-
return tokens;
|
|
135
|
-
}
|
|
136
|
-
function requestTokenMatches(req, secret) {
|
|
137
|
-
if (secret === undefined)
|
|
138
|
-
return false;
|
|
139
|
-
return requestTokens(req).some((token) => token === secret);
|
|
140
|
-
}
|
|
141
|
-
/** Read a request body into a string, aborting the moment the accumulated body exceeds
|
|
142
|
-
* `maxBytes` — so a push endpoint never buffers arbitrary attacker memory
|
|
143
|
-
* before validation rejects it. Rejects with PushRequestError(413), which the
|
|
144
|
-
* push error handler maps to a 413 response. */
|
|
145
|
-
function readBodyCapped(req, maxBytes) {
|
|
146
|
-
return new Promise((resolve, reject) => {
|
|
147
|
-
const chunks = [];
|
|
148
|
-
let total = 0;
|
|
149
|
-
req.on('data', (c) => {
|
|
150
|
-
total += c.length;
|
|
151
|
-
if (total > maxBytes) {
|
|
152
|
-
reject(new PushRequestError(413, 'push subscription body is too large'));
|
|
153
|
-
req.destroy();
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
chunks.push(c);
|
|
157
|
-
});
|
|
158
|
-
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
|
159
|
-
req.on('error', reject);
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
/** Derive the persisted auth scope from a bearer token: a short, stable hash so
|
|
163
|
-
* the raw secret never touches disk. */
|
|
164
|
-
function scopeForToken(secret) {
|
|
165
|
-
return `token:${createHash('sha256').update(secret).digest('base64url').slice(0, 16)}`;
|
|
166
|
-
}
|
|
167
|
-
/** Serve a static file from the client dir, with SPA fallback to index.html for
|
|
168
|
-
* any unmatched (non-asset) GET — so `/` and `/node/<id>` both boot the app
|
|
169
|
-
* shell. Returns true if it wrote a response. */
|
|
170
|
-
function serveStatic(clientDir, req, res) {
|
|
171
|
-
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
172
|
-
let pathname;
|
|
173
|
-
try {
|
|
174
|
-
pathname = decodeURIComponent(url.pathname);
|
|
175
|
-
}
|
|
176
|
-
catch {
|
|
177
|
-
// Malformed percent-encoding (e.g. `GET /%`) — never let it throw out of
|
|
178
|
-
// the request listener and crash the whole multiplexing server (C1).
|
|
179
|
-
res.writeHead(400, { 'content-type': 'text/plain; charset=utf-8' });
|
|
180
|
-
res.end('bad request\n');
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
// Resolve within clientDir; reject traversal.
|
|
184
|
-
const rel = normalize(pathname).replace(/^(\.\.(\/|\\|$))+/, '');
|
|
185
|
-
let filePath = join(clientDir, rel);
|
|
186
|
-
if (!filePath.startsWith(clientDir)) {
|
|
187
|
-
res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
|
|
188
|
-
res.end('forbidden\n');
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
// Directory or root → index.html. A path with no file extension that does not
|
|
192
|
-
// exist → SPA fallback to index.html (so deep links like /node/<id> boot).
|
|
193
|
-
const isFile = existsSync(filePath) && statSync(filePath).isFile();
|
|
194
|
-
if (!isFile) {
|
|
195
|
-
filePath = join(clientDir, 'index.html');
|
|
196
|
-
}
|
|
197
|
-
if (!existsSync(filePath)) {
|
|
198
|
-
res.writeHead(503, { 'content-type': 'text/plain; charset=utf-8' });
|
|
199
|
-
res.end('crtr surface web shell bundle not found — run `npm run build` to emit it into ' +
|
|
200
|
-
`${clientDir} (or pass --dev for Vite middleware)\n`);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
res.writeHead(200, { 'content-type': contentTypeFor(filePath), 'cache-control': 'no-cache' });
|
|
204
|
-
if (req.method === 'HEAD') {
|
|
205
|
-
res.end();
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const stream = createReadStream(filePath);
|
|
209
|
-
stream.on('error', () => {
|
|
210
|
-
if (!res.headersSent)
|
|
211
|
-
res.writeHead(500);
|
|
212
|
-
res.end();
|
|
213
|
-
});
|
|
214
|
-
stream.pipe(res);
|
|
215
|
-
}
|
|
216
|
-
/** RFC 7230 §6.1 hop-by-hop headers: meaningful only for a single transport hop,
|
|
217
|
-
* never forwarded through a proxy. Stripping them keeps this shell's browser↔shell
|
|
218
|
-
* and shell↔crtrd connection lifetimes independent instead of leaning on Node's
|
|
219
|
-
* re-framing to paper over a forwarded `Transfer-Encoding`/`Connection` (m5). */
|
|
220
|
-
const HOP_BY_HOP_HEADERS = new Set([
|
|
221
|
-
'connection',
|
|
222
|
-
'keep-alive',
|
|
223
|
-
'proxy-authenticate',
|
|
224
|
-
'proxy-authorization',
|
|
225
|
-
'te',
|
|
226
|
-
'trailer',
|
|
227
|
-
'transfer-encoding',
|
|
228
|
-
'upgrade',
|
|
229
|
-
]);
|
|
230
|
-
/** Copy incoming headers minus the hop-by-hop set. Node lowercases incoming
|
|
231
|
-
* header names, so a lowercase-keyed delete is exhaustive. */
|
|
232
|
-
function withoutHopByHop(headers) {
|
|
233
|
-
const out = { ...headers };
|
|
234
|
-
for (const key of HOP_BY_HOP_HEADERS)
|
|
235
|
-
delete out[key];
|
|
236
|
-
return out;
|
|
237
|
-
}
|
|
238
|
-
/** Parse a request URL and strip the `token` auth param — it authorizes THIS
|
|
239
|
-
* shell hop only; forwarding it into crtrd's request path would leak the secret
|
|
240
|
-
* into crtrd's (loggable) request line, and crtrd doesn't consume it (m4). Any
|
|
241
|
-
* OTHER query survives (e.g. crtrd's browser-only `?revive=0` opt-out). */
|
|
242
|
-
function stripTokenParam(rawUrl) {
|
|
243
|
-
const url = new URL(rawUrl, 'http://127.0.0.1');
|
|
244
|
-
url.searchParams.delete('token');
|
|
245
|
-
return url;
|
|
246
|
-
}
|
|
247
|
-
/** Proxy a `/v1/*` REST request to crtrd's unix socket. The dev shell adds only
|
|
248
|
-
* its auth boundary (crtrd applies none) then streams the request body up and
|
|
249
|
-
* the response back verbatim. No `canvas.db`, no subprocess: pure reverse proxy. */
|
|
250
|
-
function proxyToCrtrd(req, res) {
|
|
251
|
-
const target = stripTokenParam(req.url ?? '/');
|
|
252
|
-
const upstream = httpRequest({
|
|
253
|
-
socketPath: apiSocketPath(),
|
|
254
|
-
method: req.method,
|
|
255
|
-
path: `${target.pathname}${target.search}`,
|
|
256
|
-
headers: withoutHopByHop(req.headers),
|
|
257
|
-
}, (upstreamRes) => {
|
|
258
|
-
res.writeHead(upstreamRes.statusCode ?? 502, withoutHopByHop(upstreamRes.headers));
|
|
259
|
-
upstreamRes.pipe(res);
|
|
260
|
-
});
|
|
261
|
-
upstream.on('error', () => {
|
|
262
|
-
// Once headers are on the wire we can no longer signal an error in-band —
|
|
263
|
-
// appending a JSON error to a partially-streamed body would corrupt it, so
|
|
264
|
-
// truncate the response instead (m1). Before headersSent, a clean 502.
|
|
265
|
-
if (res.headersSent) {
|
|
266
|
-
res.destroy();
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
res.writeHead(502, { 'content-type': 'application/json; charset=utf-8' });
|
|
270
|
-
res.end(JSON.stringify({ error: { code: 'daemon_unavailable', message: 'crtrd is not reachable' } }));
|
|
271
|
-
});
|
|
272
|
-
// Propagate a browser abort/response-close upstream: without this an aborted
|
|
273
|
-
// request leaves the crtrd response paused and its in-flight work pinned until
|
|
274
|
-
// upstream completes on its own (M1). `destroy` on a finished exchange no-ops.
|
|
275
|
-
res.on('close', () => upstream.destroy());
|
|
276
|
-
req.pipe(upstream);
|
|
277
|
-
}
|
|
278
|
-
/** Transparently tunnel a browser `/node/<id>` WS upgrade to crtrd's
|
|
279
|
-
* `GET /v1/nodes/{id}/attach`. The dev shell does NOT terminate the WebSocket:
|
|
280
|
-
* it opens a raw connection to crtrd's unix socket, forwards the upgrade request
|
|
281
|
-
* rewritten to the attach path (headers verbatim), and pipes bytes both
|
|
282
|
-
* directions. crtrd performs the 101 handshake (which flows back through the
|
|
283
|
-
* tunnel) and owns attach-implies-revive — so the browser is the SAME protocol
|
|
284
|
-
* peer as `crtr surface attach` and this process only relays. Idempotent
|
|
285
|
-
* teardown; the tunneled socket is tracked so shutdown can drop it. */
|
|
286
|
-
function tunnelAttachUpgrade(req, socket, head, nodeId, track) {
|
|
287
|
-
if (!isSafeNodeId(nodeId)) {
|
|
288
|
-
try {
|
|
289
|
-
socket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
290
|
-
socket.destroy();
|
|
291
|
-
}
|
|
292
|
-
catch {
|
|
293
|
-
/* ignore */
|
|
294
|
-
}
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
const upstream = createConnection(apiSocketPath());
|
|
298
|
-
track.add(socket);
|
|
299
|
-
let tornDown = false;
|
|
300
|
-
let relayStarted = false;
|
|
301
|
-
const teardown = () => {
|
|
302
|
-
if (tornDown)
|
|
303
|
-
return;
|
|
304
|
-
tornDown = true;
|
|
305
|
-
track.delete(socket);
|
|
306
|
-
if (!socket.destroyed)
|
|
307
|
-
socket.destroy();
|
|
308
|
-
if (!upstream.destroyed)
|
|
309
|
-
upstream.destroy();
|
|
310
|
-
};
|
|
311
|
-
upstream.on('connect', () => {
|
|
312
|
-
// Preserve any non-token query (crtrd honors a browser-only `?revive=0`
|
|
313
|
-
// opt-out here — a WS handshake can't set the equivalent request header, so
|
|
314
|
-
// this query is the browser's ONLY channel for it); the token is stripped
|
|
315
|
-
// for the same reason as the REST proxy (m2/m4).
|
|
316
|
-
const path = `/v1/nodes/${encodeURIComponent(nodeId)}/attach${stripTokenParam(req.url ?? '/').search}`;
|
|
317
|
-
const lines = [`GET ${path} HTTP/1.1`];
|
|
318
|
-
for (let i = 0; i + 1 < req.rawHeaders.length; i += 2) {
|
|
319
|
-
lines.push(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}`);
|
|
320
|
-
}
|
|
321
|
-
try {
|
|
322
|
-
upstream.write(lines.join('\r\n') + '\r\n\r\n');
|
|
323
|
-
if (head.length > 0)
|
|
324
|
-
upstream.write(head);
|
|
325
|
-
}
|
|
326
|
-
catch {
|
|
327
|
-
teardown();
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
relayStarted = true;
|
|
331
|
-
socket.pipe(upstream);
|
|
332
|
-
upstream.pipe(socket);
|
|
333
|
-
});
|
|
334
|
-
upstream.on('error', () => {
|
|
335
|
-
// A pre-handshake upstream failure (crtrd down) leaves the browser with a
|
|
336
|
-
// bare TCP reset, indistinguishable from any other failure. Before any byte
|
|
337
|
-
// has been relayed, answer a 502 so the browser sees a distinct gateway
|
|
338
|
-
// error; once relaying is underway a bare reset is the correct WS signal (m3).
|
|
339
|
-
if (!relayStarted && !tornDown && !socket.destroyed) {
|
|
340
|
-
try {
|
|
341
|
-
socket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n');
|
|
342
|
-
}
|
|
343
|
-
catch {
|
|
344
|
-
/* ignore */
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
teardown();
|
|
348
|
-
});
|
|
349
|
-
socket.on('error', teardown);
|
|
350
|
-
upstream.on('close', teardown);
|
|
351
|
-
socket.on('close', teardown);
|
|
352
|
-
}
|
|
353
|
-
/** Build + start the unified web server. Binds the requested host/port and
|
|
354
|
-
* resolves once the server is listening. Async because --dev awaits a Vite
|
|
355
|
-
* middleware server before listening. */
|
|
356
|
-
export async function startWebServer(opts) {
|
|
357
|
-
const host = opts.host ?? '127.0.0.1';
|
|
358
|
-
const token = opts.token !== undefined && opts.token !== '' ? opts.token : undefined;
|
|
359
|
-
const dev = opts.dev ?? false;
|
|
360
|
-
const tokenRequired = !isLoopbackHost(host);
|
|
361
|
-
if (tokenRequired && (token === undefined || token === '')) {
|
|
362
|
-
throw new Error(`crtr surface web serve --host ${host} requires --token for remote-capable endpoints`);
|
|
363
|
-
}
|
|
364
|
-
const clientDir = resolveClientDir();
|
|
365
|
-
// The SSE change lane — one hub, fanned to every connected EventSource.
|
|
366
|
-
const eventHub = startEventHub();
|
|
367
|
-
// Live `/node/<id>` attach tunnels to crtrd — tracked so shutdown drops them.
|
|
368
|
-
const activeTunnels = new Set();
|
|
369
|
-
// Same-origin allowlist: tokenless localhost pages are implicitly allowed,
|
|
370
|
-
// while a valid relay token can authorize remote/guest route origins that would
|
|
371
|
-
// otherwise be rejected by originAllowed().
|
|
372
|
-
let allowedOrigins = new Set();
|
|
373
|
-
const originAllowed = (origin) => {
|
|
374
|
-
if (origin === undefined || origin === '')
|
|
375
|
-
return true;
|
|
376
|
-
return allowedOrigins.has(origin);
|
|
377
|
-
};
|
|
378
|
-
const authorizeRequest = (req) => {
|
|
379
|
-
if (token !== undefined && requestTokenMatches(req, token)) {
|
|
380
|
-
return { allowed: true, scope: scopeForToken(token) };
|
|
381
|
-
}
|
|
382
|
-
if (tokenRequired)
|
|
383
|
-
return { allowed: false };
|
|
384
|
-
if (!originAllowed(req.headers.origin))
|
|
385
|
-
return { allowed: false };
|
|
386
|
-
return { allowed: true, scope: 'local' };
|
|
387
|
-
};
|
|
388
|
-
// The set of auth scopes this server actually authorizes — the push diff
|
|
389
|
-
// engine sends ONLY to subscriptions whose authScope is in this set (the same
|
|
390
|
-
// auth boundary that gates the /v1 proxy, SSE, and attach tunnel). Mirrors authorizeRequest:
|
|
391
|
-
// a token authorizes scopeForToken(token); a tokenless request is authorized
|
|
392
|
-
// only when !tokenRequired, as scope 'local'. A record left by a rotated/old
|
|
393
|
-
// token has a different digest and is therefore excluded.
|
|
394
|
-
const targetScopes = new Set();
|
|
395
|
-
if (token !== undefined)
|
|
396
|
-
targetScopes.add(scopeForToken(token));
|
|
397
|
-
if (!tokenRequired)
|
|
398
|
-
targetScopes.add('local');
|
|
399
|
-
// §push — the server-side web-push registry + diff engine (design D8). One
|
|
400
|
-
// persisted, auth-scoped subscription registry; one locally-generated VAPID
|
|
401
|
-
// identity signing every send; one diff engine that re-reads the canvas on
|
|
402
|
-
// node invalidations received through eventHub.subscribe (no HTTP self-loop)
|
|
403
|
-
// and pushes done-transition notifications.
|
|
404
|
-
const pushRegistry = new PushRegistryStore();
|
|
405
|
-
const pushLastSeen = new PushLastSeenStore();
|
|
406
|
-
const vapid = loadOrCreateVapidKeypair();
|
|
407
|
-
webpush.setVapidDetails(vapid.subject, vapid.publicKey, vapid.privateKey);
|
|
408
|
-
const pushEngine = new PushDiffEngine({
|
|
409
|
-
registry: pushRegistry,
|
|
410
|
-
lastSeen: pushLastSeen,
|
|
411
|
-
// The lean roster (node_id/parent/status/attention_count is exactly the
|
|
412
|
-
// CanvasNodeRow subset this diff engine needs) instead of the enriched
|
|
413
|
-
// snapshot — this fires on every 'nodes' invalidation, not on a bounded
|
|
414
|
-
// interval, so it must never force the enrichRows/per-row getNode pass.
|
|
415
|
-
readCanvasNodes: async () => {
|
|
416
|
-
const snap = (await canvasRosterLeaf.run({}));
|
|
417
|
-
return snap.nodes;
|
|
418
|
-
},
|
|
419
|
-
isTargetScope: (s) => targetScopes.has(s),
|
|
420
|
-
sendPush: async (record, payload) => {
|
|
421
|
-
try {
|
|
422
|
-
await webpush.sendNotification({ endpoint: record.endpoint, keys: record.keys }, JSON.stringify(payload));
|
|
423
|
-
return 'sent';
|
|
424
|
-
}
|
|
425
|
-
catch (err) {
|
|
426
|
-
const statusCode = err.statusCode;
|
|
427
|
-
return statusCode === 404 || statusCode === 410 ? 'gone' : 'error';
|
|
428
|
-
}
|
|
429
|
-
},
|
|
430
|
-
});
|
|
431
|
-
// A restart is not an attention event — record current state, send nothing.
|
|
432
|
-
await pushEngine.seed();
|
|
433
|
-
const unsubscribePush = eventHub.subscribe((kind) => {
|
|
434
|
-
if (kind === 'nodes')
|
|
435
|
-
void pushEngine.onNodesInvalidation();
|
|
436
|
-
});
|
|
437
|
-
// Assigned before listen() when --dev; the request handler closes over it.
|
|
438
|
-
let vite;
|
|
439
|
-
const httpServer = createHttpServer((req, res) => {
|
|
440
|
-
try {
|
|
441
|
-
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
442
|
-
const pathname = url.pathname;
|
|
443
|
-
// The crtrd REST proxy: /v1/* → reverse-proxy to crtrd's unix socket. The
|
|
444
|
-
// SPA (and any /v1 client) reaches the API THROUGH this dev shell. The
|
|
445
|
-
// shell owns the auth boundary (crtrd applies none); token-or-origin gated
|
|
446
|
-
// like the SSE lane and the attach tunnel. No canvas.db, no subprocess.
|
|
447
|
-
if (pathname === '/v1' || pathname.startsWith('/v1/')) {
|
|
448
|
-
if (!authorizeRequest(req).allowed) {
|
|
449
|
-
res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
|
|
450
|
-
res.end('forbidden\n');
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
proxyToCrtrd(req, res);
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
// The SSE change-invalidation lane: GET /__crtr/events. Token-or-origin
|
|
457
|
-
// gated too — a foreign page should not even learn the user's graph is
|
|
458
|
-
// mutating.
|
|
459
|
-
if (pathname === '/__crtr/events') {
|
|
460
|
-
if (req.method !== 'GET') {
|
|
461
|
-
res.writeHead(405, { 'content-type': 'text/plain; charset=utf-8', allow: 'GET' });
|
|
462
|
-
res.end('method not allowed\n');
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
if (!authorizeRequest(req).allowed) {
|
|
466
|
-
res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
|
|
467
|
-
res.end('forbidden\n');
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
eventHub.addClient(res);
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
// §push — the VAPID public key a browser needs to subscribe. Read-only,
|
|
474
|
-
// non-secret, but behind the same auth boundary as everything else so a
|
|
475
|
-
// foreign page can't even enumerate the surface.
|
|
476
|
-
if (pathname === '/__crtr/push/vapid-public-key') {
|
|
477
|
-
if (req.method !== 'GET') {
|
|
478
|
-
res.writeHead(405, { 'content-type': 'text/plain; charset=utf-8', allow: 'GET' });
|
|
479
|
-
res.end('method not allowed\n');
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
if (!authorizeRequest(req).allowed) {
|
|
483
|
-
res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
|
|
484
|
-
res.end('forbidden\n');
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
res.writeHead(200, { 'content-type': 'application/json' });
|
|
488
|
-
res.end(JSON.stringify({ publicKey: vapid.publicKey }));
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
// §push — subscribe (POST) / unsubscribe (DELETE). Validation lives in
|
|
492
|
-
// parsePush*Body, which rejects oversized/malformed/private/http/file
|
|
493
|
-
// shapes BEFORE any persistence, throwing PushRequestError with a status.
|
|
494
|
-
if (pathname === '/__crtr/push/subscribe') {
|
|
495
|
-
const auth = authorizeRequest(req);
|
|
496
|
-
if (!auth.allowed) {
|
|
497
|
-
res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
|
|
498
|
-
res.end('forbidden\n');
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
501
|
-
const handlePushError = (e) => {
|
|
502
|
-
if (e instanceof PushRequestError) {
|
|
503
|
-
res.writeHead(e.statusCode, { 'content-type': 'application/json' });
|
|
504
|
-
res.end(JSON.stringify({ ok: false, error: e.message }));
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
res.writeHead(500, { 'content-type': 'application/json' });
|
|
508
|
-
res.end(JSON.stringify({ ok: false, error: e instanceof Error ? e.message : String(e) }));
|
|
509
|
-
};
|
|
510
|
-
if (req.method === 'POST') {
|
|
511
|
-
void readBodyCapped(req, pushBodyMaxBytes())
|
|
512
|
-
.then((body) => {
|
|
513
|
-
const record = parsePushSubscriptionBody(body, auth.scope);
|
|
514
|
-
const stored = pushRegistry.upsert(record);
|
|
515
|
-
res.writeHead(200, { 'content-type': 'application/json' });
|
|
516
|
-
res.end(JSON.stringify({ ok: true, id: stored.id, authScope: stored.authScope }));
|
|
517
|
-
})
|
|
518
|
-
.catch(handlePushError);
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
if (req.method === 'DELETE') {
|
|
522
|
-
void readBodyCapped(req, pushBodyMaxBytes())
|
|
523
|
-
.then((body) => {
|
|
524
|
-
const { endpoint } = parsePushDeleteBody(body);
|
|
525
|
-
// Scoped: a caller may only delete a subscription it owns.
|
|
526
|
-
const deleted = pushRegistry.deleteByEndpoint(endpoint, auth.scope);
|
|
527
|
-
res.writeHead(200, { 'content-type': 'application/json' });
|
|
528
|
-
res.end(JSON.stringify({ ok: true, deleted }));
|
|
529
|
-
})
|
|
530
|
-
.catch(handlePushError);
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
res.writeHead(405, { 'content-type': 'text/plain; charset=utf-8', allow: 'POST, DELETE' });
|
|
534
|
-
res.end('method not allowed\n');
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
// --dev: Vite middleware owns all remaining asset/HTML serving (incl. its
|
|
538
|
-
// own SPA fallback). It is mounted AFTER the bridge + SSE checks above, so
|
|
539
|
-
// those never fall through to Vite.
|
|
540
|
-
if (vite !== undefined) {
|
|
541
|
-
vite.middlewares(req, res);
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
// Shipped: static shell bundle + SPA fallback.
|
|
545
|
-
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
546
|
-
res.writeHead(405, { 'content-type': 'text/plain; charset=utf-8', allow: 'GET, HEAD' });
|
|
547
|
-
res.end('method not allowed\n');
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
serveStatic(clientDir, req, res);
|
|
551
|
-
}
|
|
552
|
-
catch {
|
|
553
|
-
// Belt-and-suspenders: nothing in the handler should throw now (C1), but a
|
|
554
|
-
// request listener that throws crashes the whole daemon — never allow it.
|
|
555
|
-
if (!res.headersSent)
|
|
556
|
-
res.writeHead(500, { 'content-type': 'text/plain; charset=utf-8' });
|
|
557
|
-
res.end('internal error\n');
|
|
558
|
-
}
|
|
559
|
-
});
|
|
560
|
-
httpServer.on('upgrade', (req, socket, head) => {
|
|
561
|
-
// A raw upgrade socket with no 'error' listener throws on a write to a reset
|
|
562
|
-
// peer — attach one before we ever write/destroy it (n8).
|
|
563
|
-
socket.on('error', () => {
|
|
564
|
-
/* peer reset mid-handshake — destroy below or already destroyed */
|
|
565
|
-
});
|
|
566
|
-
try {
|
|
567
|
-
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
568
|
-
const nodeId = nodeIdFromPath(url.pathname);
|
|
569
|
-
if (nodeId === null) {
|
|
570
|
-
// --dev: Vite's HMR WebSocket shares this HTTP server. Vite registers its
|
|
571
|
-
// OWN 'upgrade' listener (via server.hmr.server); leave every non-`/node/`
|
|
572
|
-
// upgrade for it instead of destroying the socket.
|
|
573
|
-
if (vite !== undefined)
|
|
574
|
-
return;
|
|
575
|
-
socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
|
|
576
|
-
socket.destroy();
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
if (!authorizeRequest(req).allowed) {
|
|
580
|
-
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
|
|
581
|
-
socket.destroy();
|
|
582
|
-
return;
|
|
583
|
-
}
|
|
584
|
-
// Transparent tunnel to crtrd's attach endpoint — the shell never
|
|
585
|
-
// terminates the WS; crtrd owns the handshake and attach-implies-revive.
|
|
586
|
-
tunnelAttachUpgrade(req, socket, head, nodeId, activeTunnels);
|
|
587
|
-
}
|
|
588
|
-
catch {
|
|
589
|
-
// Never let an upgrade-handler throw escape and crash the daemon (C1).
|
|
590
|
-
try {
|
|
591
|
-
socket.destroy();
|
|
592
|
-
}
|
|
593
|
-
catch {
|
|
594
|
-
/* ignore */
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
});
|
|
598
|
-
// --dev: create the Vite middleware server BEFORE listen so the request
|
|
599
|
-
// handler's `vite` is set and Vite's HMR upgrade listener is attached (ours
|
|
600
|
-
// was added first, so a vite-hmr upgrade hits ours → nodeId null → returns →
|
|
601
|
-
// Vite's listener handles it).
|
|
602
|
-
if (dev) {
|
|
603
|
-
vite = await createDevServer(httpServer);
|
|
604
|
-
}
|
|
605
|
-
return new Promise((resolveListening, rejectListening) => {
|
|
606
|
-
httpServer.once('error', rejectListening);
|
|
607
|
-
httpServer.listen(opts.port, host, () => {
|
|
608
|
-
let port;
|
|
609
|
-
try {
|
|
610
|
-
const addr = httpServer.address();
|
|
611
|
-
if (typeof addr !== 'object' || addr === null) {
|
|
612
|
-
throw new Error('web server address is unavailable after listen');
|
|
613
|
-
}
|
|
614
|
-
port = addr.port;
|
|
615
|
-
bindWebEventSource(host, port);
|
|
616
|
-
}
|
|
617
|
-
catch (error) {
|
|
618
|
-
httpServer.close(() => {
|
|
619
|
-
httpServer.off('error', rejectListening);
|
|
620
|
-
rejectListening(error);
|
|
621
|
-
});
|
|
622
|
-
httpServer.closeAllConnections();
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
httpServer.off('error', rejectListening);
|
|
626
|
-
// A server error AFTER listen (e.g. an accept failure) must not be an
|
|
627
|
-
// unhandled 'error' that crashes the process (n9) — emit + carry on.
|
|
628
|
-
httpServer.on('error', (err) => {
|
|
629
|
-
emitEvent({ event: 'web.server.failed', level: 'error', error: err });
|
|
630
|
-
});
|
|
631
|
-
const hostUrl = hostForUrl(host);
|
|
632
|
-
const url = `http://${hostUrl}:${port}`;
|
|
633
|
-
allowedOrigins = new Set([
|
|
634
|
-
`http://${hostUrl}:${port}`,
|
|
635
|
-
`http://127.0.0.1:${port}`,
|
|
636
|
-
`http://localhost:${port}`,
|
|
637
|
-
]);
|
|
638
|
-
const close = () => new Promise((done) => {
|
|
639
|
-
unsubscribePush();
|
|
640
|
-
eventHub.close();
|
|
641
|
-
void vite?.close().catch(() => {
|
|
642
|
-
/* best-effort */
|
|
643
|
-
});
|
|
644
|
-
for (const s of activeTunnels) {
|
|
645
|
-
try {
|
|
646
|
-
s.destroy();
|
|
647
|
-
}
|
|
648
|
-
catch {
|
|
649
|
-
/* ignore */
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
activeTunnels.clear();
|
|
653
|
-
httpServer.close(() => done());
|
|
654
|
-
// Idle keep-alive HTTP connections would otherwise keep close()'s
|
|
655
|
-
// callback from ever firing — drop them so shutdown completes (m4).
|
|
656
|
-
httpServer.closeAllConnections();
|
|
657
|
-
});
|
|
658
|
-
resolveListening({ url, port, clientDir, close });
|
|
659
|
-
});
|
|
660
|
-
});
|
|
661
|
-
}
|