@crouton-kit/crouter 0.3.28 → 0.3.29
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/dist/build-root.js +4 -2
- package/dist/builtin-memory/crouter-development/marketplaces.md +7 -6
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +1 -1
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +1 -1
- package/dist/builtin-memory/crouter-development/personas.md +1 -1
- package/dist/builtin-memory/crouter-development/plugins.md +20 -18
- package/dist/builtin-memory/design.md +1 -1
- package/dist/builtin-memory/development.md +1 -1
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/imessage-assistant.md +2 -2
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-memory/planning.md +1 -1
- package/dist/builtin-memory/spec.md +6 -4
- package/dist/builtin-personas/orchestration-kernel.md +4 -5
- package/dist/builtin-personas/runtime-base.md +5 -4
- package/dist/builtin-personas/spec/PERSONA.md +8 -4
- package/dist/builtin-personas/spec/orchestrator.md +5 -3
- package/dist/builtin-personas/spec/requirements/PERSONA.md +10 -0
- package/dist/builtin-views/canvas/core.mjs +586 -0
- package/dist/builtin-views/canvas/text.mjs +58 -0
- package/dist/builtin-views/canvas/tui.mjs +165 -0
- package/dist/builtin-views/canvas/web.jsx +120 -0
- package/dist/builtin-views/git-pr/core.mjs +673 -0
- package/dist/builtin-views/git-pr/text.mjs +84 -0
- package/dist/builtin-views/git-pr/tui.mjs +302 -0
- package/dist/builtin-views/git-pr/web.jsx +216 -0
- package/dist/builtin-views/inbox/_lib/render.mjs +1 -3
- package/dist/builtin-views/inbox/core.mjs +1273 -0
- package/dist/builtin-views/inbox/text.mjs +73 -0
- package/dist/builtin-views/inbox/tui.mjs +312 -0
- package/dist/builtin-views/inbox/web.jsx +188 -0
- package/dist/builtin-views/linkedin/core.mjs +906 -0
- package/dist/builtin-views/linkedin/text.mjs +69 -0
- package/dist/builtin-views/linkedin/tui.mjs +427 -0
- package/dist/builtin-views/linkedin/web.jsx +206 -0
- package/dist/builtin-views/workspace-sidebar/core.mjs +621 -0
- package/dist/builtin-views/workspace-sidebar/text.mjs +53 -0
- package/dist/builtin-views/workspace-sidebar/tui.mjs +142 -0
- package/dist/builtin-views/workspace-sidebar/web.jsx +109 -0
- package/dist/cli.js +3 -3
- package/dist/clients/attach/__tests__/action-parity.test.js +48 -0
- package/dist/clients/attach/__tests__/clipboard-image.test.js +76 -0
- package/dist/clients/attach/__tests__/editor-newline.test.js +57 -0
- package/dist/clients/attach/__tests__/git-info.test.js +31 -0
- package/dist/clients/attach/__tests__/onrequest-wired.test.js +20 -0
- package/dist/clients/attach/__tests__/slash-quit-copy.test.js +42 -0
- package/dist/clients/attach/__tests__/titled-editor.test.js +39 -0
- package/dist/clients/attach/attach-cmd.js +282 -46
- package/dist/clients/attach/auth-pickers.d.ts +9 -0
- package/dist/clients/attach/auth-pickers.js +194 -0
- package/dist/clients/attach/canvas-panels.d.ts +4 -2
- package/dist/clients/attach/canvas-panels.js +45 -17
- package/dist/clients/attach/chat-view.d.ts +27 -2
- package/dist/clients/attach/chat-view.js +98 -9
- package/dist/clients/attach/clipboard-image.d.ts +47 -8
- package/dist/clients/attach/clipboard-image.js +150 -26
- package/dist/clients/attach/clipboard-text.d.ts +3 -0
- package/dist/clients/attach/clipboard-text.js +39 -0
- package/dist/clients/attach/config-load.d.ts +54 -7
- package/dist/clients/attach/config-load.js +99 -3
- package/dist/clients/attach/context-message.d.ts +12 -0
- package/dist/clients/attach/context-message.js +72 -0
- package/dist/clients/attach/git-info.d.ts +20 -0
- package/dist/clients/attach/git-info.js +44 -0
- package/dist/clients/attach/graph-overlay.d.ts +13 -4
- package/dist/clients/attach/graph-overlay.js +59 -19
- package/dist/clients/attach/input-controller.d.ts +75 -13
- package/dist/clients/attach/input-controller.js +305 -67
- package/dist/clients/attach/pickers.d.ts +58 -0
- package/dist/clients/attach/pickers.js +171 -0
- package/dist/clients/attach/slash-commands.d.ts +22 -0
- package/dist/clients/attach/slash-commands.js +91 -25
- package/dist/clients/attach/titled-editor.d.ts +33 -0
- package/dist/clients/attach/titled-editor.js +84 -0
- package/dist/clients/attach/view-socket.d.ts +18 -1
- package/dist/clients/attach/view-socket.js +70 -1
- package/dist/clients/web/dev-server.d.ts +7 -0
- package/dist/clients/web/dev-server.js +59 -0
- package/dist/clients/web/events.d.ts +14 -0
- package/dist/clients/web/events.js +151 -0
- package/dist/clients/web/server.d.ts +18 -0
- package/dist/clients/web/server.js +450 -0
- package/dist/clients/web/web-cmd.d.ts +2 -0
- package/dist/clients/web/web-cmd.js +120 -0
- package/dist/commands/canvas.js +1 -2
- package/dist/commands/chord.js +1 -1
- package/dist/commands/dashboard.js +5 -1
- package/dist/commands/memory/__tests__/lint-schema.test.js +6 -6
- package/dist/commands/memory/lint.js +14 -2
- package/dist/commands/memory/read.js +2 -2
- package/dist/commands/memory/shared.d.ts +7 -1
- package/dist/commands/memory/shared.js +18 -3
- package/dist/commands/memory/write.js +8 -4
- package/dist/commands/memory.js +4 -4
- package/dist/commands/node.d.ts +1 -1
- package/dist/commands/node.js +64 -65
- package/dist/commands/pkg.js +1 -2
- package/dist/commands/revive.js +11 -3
- package/dist/commands/sys/sync.d.ts +1 -0
- package/dist/commands/sys/sync.js +187 -0
- package/dist/commands/sys.js +3 -2
- package/dist/commands/view-cycle.js +2 -2
- package/dist/commands/view-list.js +8 -8
- package/dist/commands/view-new.js +21 -17
- package/dist/commands/view-pick.js +1 -1
- package/dist/commands/view-run.js +35 -14
- package/dist/commands/view.js +8 -6
- package/dist/commands/workspace.d.ts +2 -0
- package/dist/commands/workspace.js +161 -0
- package/dist/core/__tests__/broker-double-spawn.test.d.ts +1 -0
- package/dist/core/__tests__/broker-double-spawn.test.js +143 -0
- package/dist/core/__tests__/broker-fork-seam.test.d.ts +1 -0
- package/dist/core/__tests__/broker-fork-seam.test.js +102 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.js +2 -0
- package/dist/core/__tests__/child-death-wake.test.js +56 -111
- package/dist/core/__tests__/close.test.js +14 -20
- package/dist/core/__tests__/context-intro.test.js +19 -19
- package/dist/core/__tests__/daemon-boot.test.js +30 -23
- package/dist/core/__tests__/draw-style.test.js +1 -1
- package/dist/core/__tests__/error-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/error-stall-recycle.test.js +141 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +17 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +22 -2
- package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
- package/dist/core/__tests__/focuses.test.js +25 -13
- package/dist/core/__tests__/fork.test.js +22 -5
- package/dist/core/__tests__/full/broker-control-preempt.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-control-preempt.test.js +61 -0
- package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +57 -0
- package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +80 -0
- package/dist/core/__tests__/full/broker-pane-resolution.test.js +90 -0
- package/dist/core/__tests__/full/cascade-close.test.js +22 -8
- package/dist/core/__tests__/full/detach-focus.test.js +66 -169
- package/dist/core/__tests__/helpers/harness.d.ts +6 -9
- package/dist/core/__tests__/helpers/harness.js +19 -36
- package/dist/core/__tests__/human-node-not-supervised.test.d.ts +1 -0
- package/dist/core/__tests__/human-node-not-supervised.test.js +83 -0
- package/dist/core/__tests__/lifecycle.test.js +1 -19
- package/dist/core/__tests__/live-mutation-verbs.test.js +5 -5
- package/dist/core/__tests__/memory-resolver.test.js +1 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -2
- package/dist/core/__tests__/refresh-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/refresh-stall-recycle.test.js +88 -0
- package/dist/core/__tests__/relaunch-root.test.d.ts +1 -0
- package/dist/core/__tests__/relaunch-root.test.js +114 -0
- package/dist/core/__tests__/reset.test.js +19 -79
- package/dist/core/__tests__/steer-note.test.js +9 -7
- package/dist/core/__tests__/tmux-surface.test.js +13 -7
- package/dist/core/bootstrap.d.ts +0 -3
- package/dist/core/bootstrap.js +1 -143
- package/dist/core/canvas/browse/__tests__/model.test.js +86 -1
- package/dist/core/canvas/browse/__tests__/render.test.js +2 -1
- package/dist/core/canvas/browse/app.js +131 -8
- package/dist/core/canvas/browse/model.d.ts +23 -5
- package/dist/core/canvas/browse/model.js +68 -13
- package/dist/core/canvas/browse/render.d.ts +7 -1
- package/dist/core/canvas/browse/render.js +178 -47
- package/dist/core/canvas/canvas.js +1 -1
- package/dist/core/canvas/focuses.d.ts +4 -4
- package/dist/core/canvas/focuses.js +7 -7
- package/dist/core/canvas/nav-model.js +8 -7
- package/dist/core/canvas/render.d.ts +68 -9
- package/dist/core/canvas/render.js +195 -61
- package/dist/core/canvas/types.d.ts +16 -23
- package/dist/core/config.js +1 -4
- package/dist/core/memory-resolver.js +43 -5
- package/dist/core/runtime/bearings.d.ts +1 -1
- package/dist/core/runtime/bearings.js +8 -8
- package/dist/core/runtime/branded-host.d.ts +16 -0
- package/dist/core/runtime/branded-host.js +127 -0
- package/dist/core/runtime/broker-protocol.d.ts +212 -6
- package/dist/core/runtime/broker.d.ts +4 -1
- package/dist/core/runtime/broker.js +494 -58
- package/dist/core/runtime/close.d.ts +12 -2
- package/dist/core/runtime/close.js +37 -16
- package/dist/core/runtime/front-door.js +6 -14
- package/dist/core/runtime/host.d.ts +14 -34
- package/dist/core/runtime/host.js +14 -50
- package/dist/core/runtime/launch.d.ts +12 -9
- package/dist/core/runtime/launch.js +27 -15
- package/dist/core/runtime/lifecycle.d.ts +1 -1
- package/dist/core/runtime/lifecycle.js +15 -19
- package/dist/core/runtime/nodes.d.ts +0 -55
- package/dist/core/runtime/nodes.js +6 -74
- package/dist/core/runtime/placement.d.ts +91 -335
- package/dist/core/runtime/placement.js +262 -840
- package/dist/core/runtime/promote.d.ts +2 -0
- package/dist/core/runtime/promote.js +24 -6
- package/dist/core/runtime/recap.d.ts +8 -0
- package/dist/core/runtime/recap.js +107 -0
- package/dist/core/runtime/recycle.js +25 -61
- package/dist/core/runtime/reset.d.ts +43 -37
- package/dist/core/runtime/reset.js +131 -218
- package/dist/core/runtime/revive.d.ts +9 -29
- package/dist/core/runtime/revive.js +40 -139
- package/dist/core/runtime/spawn.d.ts +15 -16
- package/dist/core/runtime/spawn.js +165 -194
- package/dist/core/runtime/surface-bg.d.ts +11 -0
- package/dist/core/runtime/surface-bg.js +68 -0
- package/dist/core/runtime/tmux.d.ts +13 -50
- package/dist/core/runtime/tmux.js +33 -87
- package/dist/core/scope.d.ts +1 -2
- package/dist/core/scope.js +1 -5
- package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts +1 -0
- package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +57 -0
- package/dist/core/skill-sync/builtins.d.ts +42 -0
- package/dist/core/skill-sync/builtins.js +112 -0
- package/dist/core/skill-sync/claude-plugins.d.ts +23 -0
- package/dist/core/skill-sync/claude-plugins.js +71 -0
- package/dist/core/skill-sync/engine.d.ts +42 -0
- package/dist/core/skill-sync/engine.js +633 -0
- package/dist/core/skill-sync/export.d.ts +23 -0
- package/dist/core/skill-sync/export.js +86 -0
- package/dist/core/skill-sync/manifest.d.ts +64 -0
- package/dist/core/skill-sync/manifest.js +181 -0
- package/dist/core/skill-sync/profile.d.ts +76 -0
- package/dist/core/skill-sync/profile.js +173 -0
- package/dist/core/skill-sync/snapshot.d.ts +57 -0
- package/dist/core/skill-sync/snapshot.js +120 -0
- package/dist/core/substrate/index.d.ts +1 -1
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/injected-store.js +3 -3
- package/dist/core/substrate/on-read.js +68 -6
- package/dist/core/substrate/render.d.ts +8 -11
- package/dist/core/substrate/render.js +29 -43
- package/dist/core/substrate/schema.d.ts +10 -3
- package/dist/core/substrate/schema.js +6 -3
- package/dist/core/tui/host.d.ts +12 -4
- package/dist/core/tui/host.js +280 -149
- package/dist/core/view/bridge.d.ts +10 -0
- package/dist/core/view/bridge.js +31 -0
- package/dist/core/view/chrome.d.ts +9 -0
- package/dist/core/view/chrome.js +22 -0
- package/dist/core/view/contract.d.ts +171 -0
- package/dist/core/view/contract.js +23 -0
- package/dist/core/view/loader.d.ts +31 -0
- package/dist/core/view/loader.js +188 -0
- package/dist/core/view/transport-local.d.ts +7 -0
- package/dist/core/view/transport-local.js +70 -0
- package/dist/core/view/transport.d.ts +4 -0
- package/dist/core/view/transport.js +15 -0
- package/dist/daemon/crtrd.d.ts +38 -27
- package/dist/daemon/crtrd.js +303 -376
- package/dist/daemon/manage.js +6 -1
- package/dist/index.js +1 -1
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +28 -20
- package/dist/pi-extensions/canvas-context-intro.js +4 -4
- package/dist/pi-extensions/canvas-doc-substrate.js +16 -16
- package/dist/pi-extensions/canvas-nav.js +5 -0
- package/dist/pi-extensions/canvas-recap.d.ts +37 -0
- package/dist/pi-extensions/canvas-recap.js +236 -0
- package/dist/pi-extensions/canvas-resume.js +2 -1
- package/dist/pi-extensions/canvas-stophook.d.ts +14 -12
- package/dist/pi-extensions/canvas-stophook.js +87 -101
- package/dist/pi-extensions/canvas-view.js +2 -1
- package/dist/pi-extensions/widget-order-bus.d.ts +6 -0
- package/dist/pi-extensions/widget-order-bus.js +34 -0
- package/dist/prompts/view.d.ts +2 -2
- package/dist/prompts/view.js +145 -73
- package/dist/types.d.ts +1 -6
- package/dist/types.js +1 -3
- package/dist/web/ViewChrome.d.ts +7 -0
- package/dist/web/ViewChrome.js +28 -0
- package/dist/web/ViewPane.d.ts +39 -0
- package/dist/web/ViewPane.js +48 -0
- package/dist/web/index.d.ts +6 -0
- package/dist/web/index.js +16 -0
- package/dist/web/runtime.d.ts +39 -0
- package/dist/web/runtime.js +133 -0
- package/dist/web/states.d.ts +24 -0
- package/dist/web/states.js +24 -0
- package/dist/web/transport-http.d.ts +5 -0
- package/dist/web/transport-http.js +28 -0
- package/dist/web-client/assets/index-BUvQb4hR.css +2 -0
- package/dist/web-client/assets/index-ClLQXYAE.js +10 -0
- package/dist/web-client/index.html +13 -0
- package/package.json +20 -6
- package/dist/builtin-views/canvas/client.mjs +0 -303
- package/dist/builtin-views/canvas/view.mjs +0 -576
- package/dist/builtin-views/git-pr/client.mjs +0 -440
- package/dist/builtin-views/git-pr/view.mjs +0 -675
- package/dist/builtin-views/inbox/sources/gmail.mjs +0 -965
- package/dist/builtin-views/inbox/sources/linkedin.mjs +0 -427
- package/dist/builtin-views/inbox/view.mjs +0 -889
- package/dist/builtin-views/linkedin/client.mjs +0 -610
- package/dist/builtin-views/linkedin/view.mjs +0 -1171
- package/dist/commands/pkg/bridge.d.ts +0 -1
- package/dist/commands/pkg/bridge.js +0 -137
- package/dist/commands/skill/author.d.ts +0 -3
- package/dist/commands/skill/author.js +0 -140
- package/dist/commands/skill/shared.d.ts +0 -3
- package/dist/commands/skill/shared.js +0 -19
- package/dist/commands/skill.d.ts +0 -2
- package/dist/commands/skill.js +0 -21
- package/dist/commands/tmux-spread.d.ts +0 -2
- package/dist/commands/tmux-spread.js +0 -144
- package/dist/core/__tests__/full/daemon-liveness-pane.full.test.js +0 -292
- package/dist/core/__tests__/full/placement-focus.test.js +0 -309
- package/dist/core/__tests__/full/placement-reconcile.test.js +0 -212
- package/dist/core/__tests__/full/placement-revive.test.js +0 -238
- package/dist/core/__tests__/full/placement-teardown.test.js +0 -297
- package/dist/core/__tests__/home-session.test.js +0 -186
- package/dist/core/__tests__/relaunch.test.js +0 -335
- package/dist/core/bridge-map.d.ts +0 -19
- package/dist/core/bridge-map.js +0 -73
- package/dist/core/tui/contract.d.ts +0 -83
- package/dist/core/tui/contract.js +0 -8
- package/dist/core/tui/loader.d.ts +0 -16
- package/dist/core/tui/loader.js +0 -94
- package/dist/prompts/skill.d.ts +0 -2
- package/dist/prompts/skill.js +0 -650
- /package/dist/{core/__tests__/full/daemon-liveness-pane.full.test.d.ts → clients/attach/__tests__/action-parity.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-focus.test.d.ts → clients/attach/__tests__/clipboard-image.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-reconcile.test.d.ts → clients/attach/__tests__/editor-newline.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-revive.test.d.ts → clients/attach/__tests__/git-info.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-teardown.test.d.ts → clients/attach/__tests__/onrequest-wired.test.d.ts} +0 -0
- /package/dist/{core/__tests__/home-session.test.d.ts → clients/attach/__tests__/slash-quit-copy.test.d.ts} +0 -0
- /package/dist/{core/__tests__/relaunch.test.d.ts → clients/attach/__tests__/titled-editor.test.d.ts} +0 -0
|
@@ -18,10 +18,15 @@
|
|
|
18
18
|
// `CLIENT_READ_CAPS`; a broker that somehow sends an oversized frame surfaces as
|
|
19
19
|
// a clean error+exit, not a crash.
|
|
20
20
|
import { EventEmitter } from 'node:events';
|
|
21
|
+
import { randomUUID } from 'node:crypto';
|
|
21
22
|
import { createConnection } from 'node:net';
|
|
22
23
|
import { join } from 'node:path';
|
|
23
24
|
import { nodeDir } from '../../core/canvas/paths.js';
|
|
24
25
|
import { CLIENT_READ_CAPS, encodeFrame, FrameDecoder, FrameOverflowError, } from '../../core/runtime/broker-protocol.js';
|
|
26
|
+
/** How long {@link ViewSocketClient.request} waits for the correlated reply before
|
|
27
|
+
* rejecting — bounds a pending picker fetch if the broker drops the request
|
|
28
|
+
* (it should always reply with `data` or a correlated `error`). */
|
|
29
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
25
30
|
/** Surfaced when the node has no reachable broker at connect time (no socket
|
|
26
31
|
* file, or a stale socket with nothing listening). The command catches this to
|
|
27
32
|
* exit non-zero with a focus/revive hint. */
|
|
@@ -51,10 +56,48 @@ export class ViewSocketClient extends EventEmitter {
|
|
|
51
56
|
socket;
|
|
52
57
|
decoder = new FrameDecoder(CLIENT_READ_CAPS);
|
|
53
58
|
closeEmitted = false;
|
|
59
|
+
/** In-flight correlated read-ops, keyed by the `id` minted in {@link request}.
|
|
60
|
+
* Resolved by the matching `data` frame / rejected by the matching `error`
|
|
61
|
+
* frame in {@link onData}, the request's timeout, or socket teardown. */
|
|
62
|
+
pending = new Map();
|
|
54
63
|
constructor(nodeId) {
|
|
55
64
|
super();
|
|
56
65
|
this.nodeId = nodeId;
|
|
57
66
|
}
|
|
67
|
+
/** Issue a correlated read-op and resolve with the broker's `data` reply (or
|
|
68
|
+
* reject on the correlated `error`, a timeout, or socket teardown). Mints the
|
|
69
|
+
* `id`, sends `{...frame, id}`, and parks a resolver consumed by {@link onData}.
|
|
70
|
+
* The reply is narrowed by the caller on its `kind`. */
|
|
71
|
+
request(frame) {
|
|
72
|
+
const id = randomUUID();
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
// Fail fast on a dead/absent socket rather than parking a promise that the
|
|
75
|
+
// reply will never reach (it would otherwise hang the full timeout). A
|
|
76
|
+
// post-`close` request lands here too — `rejectAllPending` already ran.
|
|
77
|
+
const sock = this.socket;
|
|
78
|
+
if (sock === undefined || sock.destroyed) {
|
|
79
|
+
reject(new Error(`cannot issue '${frame.type}': no live broker connection`));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const timer = setTimeout(() => {
|
|
83
|
+
this.pending.delete(id);
|
|
84
|
+
reject(new Error(`request '${frame.type}' timed out after ${REQUEST_TIMEOUT_MS}ms`));
|
|
85
|
+
}, REQUEST_TIMEOUT_MS);
|
|
86
|
+
if (typeof timer.unref === 'function')
|
|
87
|
+
timer.unref();
|
|
88
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
89
|
+
this.send({ ...frame, id });
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/** Reject + clear every in-flight request (socket gone / decode error) so a
|
|
93
|
+
* picker fetch never hangs past the connection it rode on. */
|
|
94
|
+
rejectAllPending(reason) {
|
|
95
|
+
for (const { reject, timer } of this.pending.values()) {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
reject(new Error(reason));
|
|
98
|
+
}
|
|
99
|
+
this.pending.clear();
|
|
100
|
+
}
|
|
58
101
|
/** The broker binds `join(nodeDir(id), 'view.sock')` — resolve it the same way. */
|
|
59
102
|
get socketPath() {
|
|
60
103
|
return join(nodeDir(this.nodeId), 'view.sock');
|
|
@@ -138,12 +181,37 @@ export class ViewSocketClient extends EventEmitter {
|
|
|
138
181
|
const msg = err instanceof FrameOverflowError
|
|
139
182
|
? `broker sent an oversized frame (${err.message}) — disconnecting`
|
|
140
183
|
: `failed to decode a broker frame: ${String(err)}`;
|
|
184
|
+
this.rejectAllPending(msg);
|
|
141
185
|
this.emitError(new Error(msg));
|
|
142
186
|
this.destroy();
|
|
143
187
|
return;
|
|
144
188
|
}
|
|
145
189
|
for (const raw of frames) {
|
|
146
|
-
|
|
190
|
+
const frame = raw;
|
|
191
|
+
// Correlated replies (read-ops + dequeue) are consumed by the pending-by-id
|
|
192
|
+
// resolver, NOT re-emitted as a generic 'frame' (the attach frame router
|
|
193
|
+
// would otherwise treat a `data` frame as an AgentSessionEvent). A `data`
|
|
194
|
+
// frame is ALWAYS a reply, so it is swallowed unconditionally — even a late
|
|
195
|
+
// post-timeout one (no pending entry) is dropped, never leaked to the
|
|
196
|
+
// router. An `error` is correlated only when its `id` matches an in-flight
|
|
197
|
+
// request; an uncorrelated error still flows through.
|
|
198
|
+
if (frame.type === 'data') {
|
|
199
|
+
const entry = typeof frame.id === 'string' ? this.pending.get(frame.id) : undefined;
|
|
200
|
+
if (entry) {
|
|
201
|
+
this.pending.delete(frame.id);
|
|
202
|
+
clearTimeout(entry.timer);
|
|
203
|
+
entry.resolve(frame);
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (frame.type === 'error' && typeof frame.id === 'string' && this.pending.has(frame.id)) {
|
|
208
|
+
const entry = this.pending.get(frame.id);
|
|
209
|
+
this.pending.delete(frame.id);
|
|
210
|
+
clearTimeout(entry.timer);
|
|
211
|
+
entry.reject(new Error(frame.message || `request failed: ${frame.code}`));
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
this.emit('frame', frame);
|
|
147
215
|
}
|
|
148
216
|
}
|
|
149
217
|
onError(err) {
|
|
@@ -159,6 +227,7 @@ export class ViewSocketClient extends EventEmitter {
|
|
|
159
227
|
if (this.closeEmitted)
|
|
160
228
|
return;
|
|
161
229
|
this.closeEmitted = true;
|
|
230
|
+
this.rejectAllPending('broker connection closed');
|
|
162
231
|
this.emit('close');
|
|
163
232
|
}
|
|
164
233
|
destroy() {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Server as HttpServer } from 'node:http';
|
|
2
|
+
import type { ViteDevServer } from 'vite';
|
|
3
|
+
/** Create a Vite dev server in middleware mode, HMR bound to `httpServer`.
|
|
4
|
+
* Mirrors the old `view serve` resolve setup so the shell's bare imports
|
|
5
|
+
* (react, the `@crouton-kit/crouter/web` self-subpath) resolve from the
|
|
6
|
+
* generated/shell root. Lazily imports vite so the shipped path never loads it. */
|
|
7
|
+
export declare function createDevServer(httpServer: HttpServer): Promise<ViteDevServer>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// dev-server.ts — `crtr web serve --dev` Vite middleware mode.
|
|
2
|
+
//
|
|
3
|
+
// The shipped server serves a prebuilt static shell bundle from dist/web-client/
|
|
4
|
+
// (§11). In --dev a contributor iterating on the shell or a builtin view gets
|
|
5
|
+
// HMR instead: a Vite dev server in MIDDLEWARE mode mounted on the SAME HTTP
|
|
6
|
+
// server as the bridge + WS relay. This is the one place Vite runs at runtime —
|
|
7
|
+
// the mainstream "Vite middleware in dev, static dist in prod" split.
|
|
8
|
+
//
|
|
9
|
+
// The bridge (POST /__crtr/source), the SSE lane (GET /__crtr/events) and the
|
|
10
|
+
// WS relay are owned by server.ts and checked BEFORE Vite's middlewares; this
|
|
11
|
+
// module only supplies the asset/HMR middleware. Vite's HMR WebSocket shares
|
|
12
|
+
// the HTTP server (server.hmr.server) — server.ts's upgrade handler routes
|
|
13
|
+
// `/node/<id>` to the broker relay and leaves every other upgrade for Vite.
|
|
14
|
+
import { resolve, dirname, join } from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
/** The shell SPA Vite root — the in-tree shell project (`src/clients/web/web-client/`).
|
|
18
|
+
* --dev is a from-source contributor loop, so this resolves against the source
|
|
19
|
+
* tree (HERE is `dist/clients/web` when built, `src/clients/web` under tsx);
|
|
20
|
+
* walk to the package root and into src. */
|
|
21
|
+
function resolveShellRoot(packageRoot) {
|
|
22
|
+
return join(packageRoot, 'src', 'clients', 'web', 'web-client');
|
|
23
|
+
}
|
|
24
|
+
/** Create a Vite dev server in middleware mode, HMR bound to `httpServer`.
|
|
25
|
+
* Mirrors the old `view serve` resolve setup so the shell's bare imports
|
|
26
|
+
* (react, the `@crouton-kit/crouter/web` self-subpath) resolve from the
|
|
27
|
+
* generated/shell root. Lazily imports vite so the shipped path never loads it. */
|
|
28
|
+
export async function createDevServer(httpServer) {
|
|
29
|
+
const packageRoot = resolve(HERE, '../../..');
|
|
30
|
+
const nodeModules = join(packageRoot, 'node_modules');
|
|
31
|
+
const shellRoot = resolveShellRoot(packageRoot);
|
|
32
|
+
const { createServer } = await import('vite');
|
|
33
|
+
const react = (await import('@vitejs/plugin-react')).default;
|
|
34
|
+
const tailwindcss = (await import('@tailwindcss/vite')).default;
|
|
35
|
+
return createServer({
|
|
36
|
+
configFile: false,
|
|
37
|
+
root: shellRoot,
|
|
38
|
+
plugins: [react(), tailwindcss()],
|
|
39
|
+
appType: 'spa',
|
|
40
|
+
resolve: {
|
|
41
|
+
alias: {
|
|
42
|
+
react: join(nodeModules, 'react'),
|
|
43
|
+
'react-dom': join(nodeModules, 'react-dom'),
|
|
44
|
+
// The crtr web runtime is a self-subpath of crouter's own package;
|
|
45
|
+
// pin it to the built barrel so the shell can import it from any root.
|
|
46
|
+
'@crouton-kit/crouter/web': join(packageRoot, 'dist', 'web', 'index.js'),
|
|
47
|
+
},
|
|
48
|
+
dedupe: ['react', 'react-dom'],
|
|
49
|
+
},
|
|
50
|
+
server: {
|
|
51
|
+
middlewareMode: true,
|
|
52
|
+
// Share our HTTP server for HMR; server.ts's upgrade handler hands every
|
|
53
|
+
// non-`/node/<id>` upgrade to Vite (the vite-hmr WebSocket).
|
|
54
|
+
hmr: { server: httpServer },
|
|
55
|
+
fs: { allow: [shellRoot, packageRoot] },
|
|
56
|
+
},
|
|
57
|
+
logLevel: 'warn',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ServerResponse } from 'node:http';
|
|
2
|
+
export type ChangeKind = 'nodes' | 'inbox';
|
|
3
|
+
export interface EventHub {
|
|
4
|
+
/** Register an SSE client: writes the event-stream headers, streams events
|
|
5
|
+
* until the connection closes, and self-removes on close. */
|
|
6
|
+
addClient: (res: ServerResponse) => void;
|
|
7
|
+
/** Tear down watchers + heartbeat and end all open streams. */
|
|
8
|
+
close: () => void;
|
|
9
|
+
}
|
|
10
|
+
/** Start the change-event hub: open the two fs watchers once, fan their
|
|
11
|
+
* debounced invalidations out to every connected SSE client. Watchers run for
|
|
12
|
+
* the server's lifetime (cheap — two inotify/FSEvents registrations), not
|
|
13
|
+
* per-client, so N browsers share one watch. */
|
|
14
|
+
export declare function startEventHub(): EventHub;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// events.ts — the `crtr web` realtime push lane (the §7 SSE seam, built for v1).
|
|
2
|
+
//
|
|
3
|
+
// ONE server-sent-events stream, `GET /__crtr/events`, that carries CHANGE
|
|
4
|
+
// INVALIDATIONS — never data. The shell / its ViewPanes subscribe and, on an
|
|
5
|
+
// event, re-pull through their existing Sources (the bridge) instead of waiting
|
|
6
|
+
// on a fixed poll tick. SSE carries "something in this class changed"; the
|
|
7
|
+
// consumer decides what to re-read. This keeps the event vocabulary tiny and
|
|
8
|
+
// generic and the contract unchanged (a ViewPane still just runs `refresh`).
|
|
9
|
+
//
|
|
10
|
+
// Event vocabulary (deliberately minimal): { kind: 'nodes' | 'inbox', ts }.
|
|
11
|
+
// - 'nodes' — the node graph changed (topology, status/intent transitions,
|
|
12
|
+
// naming). Source: canvas.db (+ its WAL sidecar) mtime. Every transition()
|
|
13
|
+
// and canvas mutation writes the WAL, so a watch on the home dir's
|
|
14
|
+
// `canvas.db*` files detects all of them with one cheap watcher.
|
|
15
|
+
// - 'inbox' — a node's inbox.jsonl gained a message. Source: a recursive watch
|
|
16
|
+
// on the nodes/ tree, filtered to `inbox.jsonl` writes.
|
|
17
|
+
//
|
|
18
|
+
// WHY these sources (the §7 seam left the event source open): they are the
|
|
19
|
+
// cheapest things that detect node-graph + inbox changes without a daemon hook
|
|
20
|
+
// or a db trigger — pure fs.watch on the two on-disk authorities (canvas.db for
|
|
21
|
+
// the graph, inbox.jsonl appends for messages). No polling, no canvas.db reads.
|
|
22
|
+
import { watch } from 'node:fs';
|
|
23
|
+
import { crtrHome, nodesRoot, ensureHome } from '../../core/canvas/paths.js';
|
|
24
|
+
/** Coalesce a burst of fs events into one emission per kind. WAL writes and
|
|
25
|
+
* inbox appends arrive in clusters (a single transition can touch -wal/-shm);
|
|
26
|
+
* a short trailing debounce collapses each cluster into a single invalidation,
|
|
27
|
+
* which is all a re-pull consumer needs. */
|
|
28
|
+
const DEBOUNCE_MS = 150;
|
|
29
|
+
/** SSE keepalive comment cadence — keeps proxies/clients from idling the stream
|
|
30
|
+
* shut. A bare `:` comment line is ignored by EventSource. */
|
|
31
|
+
const HEARTBEAT_MS = 25_000;
|
|
32
|
+
/** Start the change-event hub: open the two fs watchers once, fan their
|
|
33
|
+
* debounced invalidations out to every connected SSE client. Watchers run for
|
|
34
|
+
* the server's lifetime (cheap — two inotify/FSEvents registrations), not
|
|
35
|
+
* per-client, so N browsers share one watch. */
|
|
36
|
+
export function startEventHub() {
|
|
37
|
+
ensureHome();
|
|
38
|
+
const clients = new Set();
|
|
39
|
+
const watchers = [];
|
|
40
|
+
const timers = new Map();
|
|
41
|
+
const broadcast = (kind) => {
|
|
42
|
+
const line = `data: ${JSON.stringify({ kind, ts: Date.now() })}\n\n`;
|
|
43
|
+
for (const res of clients) {
|
|
44
|
+
try {
|
|
45
|
+
res.write(line);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
/* a dead stream is reaped by its own 'close' handler */
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
// Debounce per kind: an fs event arms a trailing timer; a fresh event within
|
|
53
|
+
// the window resets it, so a write cluster emits exactly once.
|
|
54
|
+
const emit = (kind) => {
|
|
55
|
+
const existing = timers.get(kind);
|
|
56
|
+
if (existing !== undefined)
|
|
57
|
+
clearTimeout(existing);
|
|
58
|
+
timers.set(kind, setTimeout(() => {
|
|
59
|
+
timers.delete(kind);
|
|
60
|
+
broadcast(kind);
|
|
61
|
+
}, DEBOUNCE_MS));
|
|
62
|
+
};
|
|
63
|
+
// Watcher 1 — the canvas home dir (non-recursive). canvas.db lives in WAL
|
|
64
|
+
// mode, so graph writes land in canvas.db-wal and checkpoints in canvas.db;
|
|
65
|
+
// watching the dir catches every `canvas.db*` sidecar touch → 'nodes'.
|
|
66
|
+
try {
|
|
67
|
+
const homeWatcher = watch(crtrHome(), (_event, filename) => {
|
|
68
|
+
if (filename !== null && filename.startsWith('canvas.db'))
|
|
69
|
+
emit('nodes');
|
|
70
|
+
});
|
|
71
|
+
homeWatcher.on('error', () => {
|
|
72
|
+
/* watch dropped (dir replaced) — non-fatal; the poll cadence still backs us */
|
|
73
|
+
});
|
|
74
|
+
watchers.push(homeWatcher);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
/* fs.watch unavailable on this platform — degrade to poll-only (no SSE) */
|
|
78
|
+
}
|
|
79
|
+
// Watcher 2 — the nodes/ tree (recursive). An inbox.jsonl append is a message
|
|
80
|
+
// landing; a meta.json write is a graph/identity change (naming, polymorph).
|
|
81
|
+
// Recursive fs.watch is supported on macOS/Windows always and Linux ≥ v20.
|
|
82
|
+
try {
|
|
83
|
+
const nodesWatcher = watch(nodesRoot(), { recursive: true }, (_event, filename) => {
|
|
84
|
+
if (filename === null)
|
|
85
|
+
return;
|
|
86
|
+
if (filename.endsWith('inbox.jsonl'))
|
|
87
|
+
emit('inbox');
|
|
88
|
+
else if (filename.endsWith('meta.json'))
|
|
89
|
+
emit('nodes');
|
|
90
|
+
});
|
|
91
|
+
nodesWatcher.on('error', () => {
|
|
92
|
+
/* recursive watch refused/dropped — non-fatal */
|
|
93
|
+
});
|
|
94
|
+
watchers.push(nodesWatcher);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
/* recursive watch unsupported — inbox push degrades to poll-only */
|
|
98
|
+
}
|
|
99
|
+
const heartbeat = setInterval(() => {
|
|
100
|
+
for (const res of clients) {
|
|
101
|
+
try {
|
|
102
|
+
res.write(': ping\n\n');
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
/* reaped by its 'close' handler */
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}, HEARTBEAT_MS);
|
|
109
|
+
heartbeat.unref();
|
|
110
|
+
return {
|
|
111
|
+
addClient(res) {
|
|
112
|
+
res.writeHead(200, {
|
|
113
|
+
'content-type': 'text/event-stream; charset=utf-8',
|
|
114
|
+
'cache-control': 'no-cache, no-transform',
|
|
115
|
+
connection: 'keep-alive',
|
|
116
|
+
// The browser EventSource is same-origin here; no CORS header needed.
|
|
117
|
+
});
|
|
118
|
+
// An initial comment flushes headers and confirms the stream is open.
|
|
119
|
+
res.write(': connected\n\n');
|
|
120
|
+
clients.add(res);
|
|
121
|
+
const drop = () => {
|
|
122
|
+
clients.delete(res);
|
|
123
|
+
};
|
|
124
|
+
res.on('close', drop);
|
|
125
|
+
res.on('error', drop);
|
|
126
|
+
},
|
|
127
|
+
close() {
|
|
128
|
+
clearInterval(heartbeat);
|
|
129
|
+
for (const t of timers.values())
|
|
130
|
+
clearTimeout(t);
|
|
131
|
+
timers.clear();
|
|
132
|
+
for (const w of watchers) {
|
|
133
|
+
try {
|
|
134
|
+
w.close();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
/* ignore */
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
for (const res of clients) {
|
|
141
|
+
try {
|
|
142
|
+
res.end();
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
/* ignore */
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
clients.clear();
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface WebServerOptions {
|
|
2
|
+
port: number;
|
|
3
|
+
host?: string;
|
|
4
|
+
/** --dev: mount a Vite dev server (middleware mode, HMR) for asset serving
|
|
5
|
+
* instead of the static dist/web-client/ bundle. The bridge + SSE + WS relay
|
|
6
|
+
* are identical in both modes. */
|
|
7
|
+
dev?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface RunningWebServer {
|
|
10
|
+
url: string;
|
|
11
|
+
port: number;
|
|
12
|
+
clientDir: string;
|
|
13
|
+
close: () => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/** Build + start the unified web server. Binds 127.0.0.1 only (remote access is
|
|
16
|
+
* out of scope — §9). Resolves once the server is listening. Async because
|
|
17
|
+
* --dev awaits a Vite middleware server before listening. */
|
|
18
|
+
export declare function startWebServer(opts: WebServerOptions): Promise<RunningWebServer>;
|