tmux-ide 2.9.2 → 2.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/cli.js +2071 -625
- package/package.json +14 -5
- package/packages/contracts/src/desktop-host.ts +3 -0
- package/packages/contracts/src/saved-machines.ts +19 -0
- package/packages/contracts/src/terminal-replica.ts +1 -1
- package/packages/core/package.json +3 -2
- package/packages/core/src/terminal-delivery.ts +51 -3
- package/packages/core/src/terminal-fnv64-wasm.ts +4 -3
- package/packages/core/src/terminal-replica-hash-cache.ts +129 -31
- package/packages/core/src/terminal-replica-owned-row.ts +27 -0
- package/packages/core/src/terminal-replica.ts +13 -28
- package/packages/daemon/dist/command-center/daemon-fleet-facts-observer.js +96 -10
- package/packages/daemon/dist/command-center/resources/saved-machine-route.js +40 -1
- package/packages/daemon/dist/command-center/ws-events.js +5 -24
- package/packages/daemon/dist/lib/canonical-daemon-bootstrap.js +8 -0
- package/packages/daemon/dist/lib/canonical-daemon.js +28 -2
- package/packages/daemon/dist/lib/daemon-embed.js +1 -1
- package/packages/daemon/dist/lib/daemon-service-manager.js +122 -0
- package/packages/daemon/dist/lib/daemon-service-plan.js +127 -0
- package/packages/daemon/dist/lib/daemon-service.js +259 -0
- package/packages/daemon/dist/lib/install-origin.js +2 -0
- package/packages/daemon/dist/lib/local-fleet-request.js +9 -3
- package/packages/daemon/dist/lib/log.js +6 -10
- package/packages/daemon/dist/lib/managed-installer-update.js +67 -0
- package/packages/daemon/dist/lib/{tui-download-lock.js → private-operation-lock.js} +11 -11
- package/packages/daemon/dist/lib/remote-daemon-info.js +3 -2
- package/packages/daemon/dist/lib/remote-daemon-lifecycle.js +4 -4
- package/packages/daemon/dist/lib/remote-tmux-command.js +10 -0
- package/packages/daemon/dist/lib/runtime-namespace.js +28 -16
- package/packages/daemon/dist/lib/saved-machines.js +40 -2
- package/packages/daemon/dist/lib/ssh-daemon-transport.js +24 -9
- package/packages/daemon/dist/lib/tmux-server-owner.js +1 -1
- package/packages/daemon/dist/lib/tmux-server-registration.js +21 -3
- package/packages/daemon/dist/lib/tui-binary.js +2 -2
- package/packages/daemon/dist/lib/update.js +18 -0
- package/packages/daemon/dist/lib/workspace-multiplexer-verbs.js +24 -1
- package/packages/daemon/dist/machines.js +40 -4
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +2 -2
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +2 -2
- package/packages/daemon/dist/terminal/attachments/direct-websocket.js +4 -1
- package/packages/daemon/dist/terminal/attachments/native-runtime.js +22 -13
- package/packages/daemon/dist/terminal/mirror/mirror-service.js +101 -3
- package/packages/daemon/dist/terminal/mirror/registered-window-guard.js +62 -0
- package/packages/daemon/dist/terminal/mirror/session-channel.js +75 -4
- package/packages/daemon/dist/terminal/mirror/shared-window-index.js +45 -0
- package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +11 -1
- package/packages/daemon/dist/terminal/session-runtime/multiplexer-backend.js +1 -0
- package/packages/daemon/dist/terminal/session-runtime/registry.js +56 -7
- package/packages/daemon/dist/terminal/session-runtime/semantic-mutation-executor.js +3 -1
- package/packages/daemon/dist/tui/mirror/dialog-model.js +2 -35
- package/packages/daemon/dist/tui/mirror/runtime/application-daemon-authority-owner.js +8 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-home-fleet.js +20 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-agents.js +7 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-authority.js +59 -9
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-navigation.js +27 -19
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +23 -18
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-startup.js +31 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-palette-search-owner.js +7 -3
- package/packages/daemon/dist/tui/mirror/runtime/application-performance-log.js +3 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-input-ingress.js +146 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +201 -50
- package/packages/daemon/dist/tui/mirror/runtime/bounded-performance-record-writer.js +50 -10
- package/packages/daemon/dist/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.js +6 -5
- package/packages/daemon/dist/tui/mirror/runtime/terminal-pane-input-router.js +3 -0
- package/packages/daemon/dist/tui/mirror/runtime/workspace-terminal-fast-lane.js +8 -2
- package/packages/daemon/dist/tui/mirror/terminal-text.js +35 -0
- package/packages/daemon/dist/tui/mirror/workspace/connection-feedback.js +5 -3
- package/packages/daemon/src/command-center/daemon-fleet-facts-observer.ts +100 -10
- package/packages/daemon/src/command-center/resources/saved-machine-route.ts +48 -1
- package/packages/daemon/src/command-center/ws-events.ts +5 -25
- package/packages/daemon/src/lib/canonical-daemon-bootstrap.ts +12 -0
- package/packages/daemon/src/lib/canonical-daemon.ts +44 -2
- package/packages/daemon/src/lib/daemon-embed.ts +1 -0
- package/packages/daemon/src/lib/daemon-service-manager.ts +152 -0
- package/packages/daemon/src/lib/daemon-service-plan.ts +153 -0
- package/packages/daemon/src/lib/daemon-service.ts +301 -0
- package/packages/daemon/src/lib/install-origin.ts +5 -0
- package/packages/daemon/src/lib/local-fleet-request.ts +19 -3
- package/packages/daemon/src/lib/log.ts +13 -9
- package/packages/daemon/src/lib/managed-installer-update.ts +79 -0
- package/packages/daemon/src/lib/{tui-download-lock.ts → private-operation-lock.ts} +15 -11
- package/packages/daemon/src/lib/remote-daemon-info.ts +3 -3
- package/packages/daemon/src/lib/remote-daemon-lifecycle.ts +4 -4
- package/packages/daemon/src/lib/remote-tmux-command.ts +12 -0
- package/packages/daemon/src/lib/runtime-namespace.ts +27 -18
- package/packages/daemon/src/lib/saved-machines.ts +40 -1
- package/packages/daemon/src/lib/ssh-daemon-transport.ts +25 -7
- package/packages/daemon/src/lib/tmux-server-owner.ts +1 -0
- package/packages/daemon/src/lib/tmux-server-registration.ts +21 -3
- package/packages/daemon/src/lib/tui-binary.ts +2 -2
- package/packages/daemon/src/lib/update.ts +24 -0
- package/packages/daemon/src/lib/workspace-multiplexer-verbs.ts +22 -0
- package/packages/daemon/src/machines.ts +53 -5
- package/packages/daemon/src/terminal/attachments/direct-websocket.ts +8 -2
- package/packages/daemon/src/terminal/attachments/native-runtime.ts +28 -13
- package/packages/daemon/src/terminal/mirror/mirror-service.ts +105 -3
- package/packages/daemon/src/terminal/mirror/registered-window-guard.ts +66 -0
- package/packages/daemon/src/terminal/mirror/session-channel.ts +92 -4
- package/packages/daemon/src/terminal/mirror/shared-window-index.ts +42 -0
- package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +12 -1
- package/packages/daemon/src/terminal/session-runtime/multiplexer-backend.ts +2 -0
- package/packages/daemon/src/terminal/session-runtime/registry.ts +70 -8
- package/packages/daemon/src/terminal/session-runtime/semantic-mutation-executor.ts +3 -1
- package/packages/daemon/src/tui/mirror/dialog-model.ts +2 -32
- package/packages/daemon/src/tui/mirror/runtime/application-daemon-authority-owner.ts +8 -0
- package/packages/daemon/src/tui/mirror/runtime/application-home-fleet.ts +19 -2
- package/packages/daemon/src/tui/mirror/runtime/application-machine-agents.ts +6 -1
- package/packages/daemon/src/tui/mirror/runtime/application-machine-authority.ts +60 -11
- package/packages/daemon/src/tui/mirror/runtime/application-machine-navigation.ts +39 -33
- package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +31 -23
- package/packages/daemon/src/tui/mirror/runtime/application-machine-startup.ts +38 -2
- package/packages/daemon/src/tui/mirror/runtime/application-palette-search-owner.ts +7 -4
- package/packages/daemon/src/tui/mirror/runtime/application-performance-log.ts +3 -0
- package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +1 -1
- package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +47 -46
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-input-ingress.ts +165 -2
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +207 -53
- package/packages/daemon/src/tui/mirror/runtime/bounded-performance-record-writer.ts +49 -9
- package/packages/daemon/src/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.ts +6 -5
- package/packages/daemon/src/tui/mirror/runtime/terminal-pane-input-router.ts +4 -0
- package/packages/daemon/src/tui/mirror/runtime/workspace-terminal-fast-lane.ts +8 -3
- package/packages/daemon/src/tui/mirror/terminal-text.ts +32 -0
- package/packages/daemon/src/tui/mirror/workspace/connection-feedback.ts +6 -3
- package/packages/daemon-client/src/fleet-connection-status.ts +10 -1
- package/packages/daemon-client/src/workspace-client.ts +4 -1
- package/packages/contracts/src/__tests__/actions-contract.test.ts +0 -119
- package/packages/contracts/src/__tests__/agent-graph-overlay.test.ts +0 -428
- package/packages/contracts/src/__tests__/app-window-mutation.test.ts +0 -51
- package/packages/contracts/src/__tests__/app-window-state.test.ts +0 -34
- package/packages/contracts/src/__tests__/application-shell.test.ts +0 -645
- package/packages/contracts/src/__tests__/automation-operations.test.ts +0 -78
- package/packages/contracts/src/__tests__/causal-cell.test.ts +0 -47
- package/packages/contracts/src/__tests__/client-view-state.test.ts +0 -115
- package/packages/contracts/src/__tests__/cohesion-fixture.test.ts +0 -106
- package/packages/contracts/src/__tests__/commands.test.ts +0 -86
- package/packages/contracts/src/__tests__/control.test.ts +0 -154
- package/packages/contracts/src/__tests__/daemon-events.test.ts +0 -405
- package/packages/contracts/src/__tests__/daemon-resources.test.ts +0 -143
- package/packages/contracts/src/__tests__/daemon-wire.test.ts +0 -149
- package/packages/contracts/src/__tests__/desktop-host.test.ts +0 -269
- package/packages/contracts/src/__tests__/desktop-missions.test.ts +0 -215
- package/packages/contracts/src/__tests__/experience-conformance.test.ts +0 -153
- package/packages/contracts/src/__tests__/experience-kernel-boundary.test.ts +0 -113
- package/packages/contracts/src/__tests__/experience-shell.test.ts +0 -65
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v2.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v3.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v4.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/scoped-receipt.ts +0 -62
- package/packages/contracts/src/__tests__/fleet-agent-graph.test.ts +0 -231
- package/packages/contracts/src/__tests__/fleet-catalog.test.ts +0 -270
- package/packages/contracts/src/__tests__/fleet-lifecycle.test.ts +0 -62
- package/packages/contracts/src/__tests__/focus-overlay.test.ts +0 -110
- package/packages/contracts/src/__tests__/ide-config.test.ts +0 -64
- package/packages/contracts/src/__tests__/interaction-evidence.test.ts +0 -288
- package/packages/contracts/src/__tests__/interaction-receipt-v2.test.ts +0 -72
- package/packages/contracts/src/__tests__/issue-error.test.ts +0 -78
- package/packages/contracts/src/__tests__/mission-projections.test.ts +0 -186
- package/packages/contracts/src/__tests__/missions.test.ts +0 -272
- package/packages/contracts/src/__tests__/multiplexer-verbs.test.ts +0 -242
- package/packages/contracts/src/__tests__/pane-appearance.test.ts +0 -146
- package/packages/contracts/src/__tests__/pane-read-result.test.ts +0 -55
- package/packages/contracts/src/__tests__/pane-stream.test.ts +0 -536
- package/packages/contracts/src/__tests__/pane-widget-marker.test.ts +0 -327
- package/packages/contracts/src/__tests__/performance-metrics.test.ts +0 -73
- package/packages/contracts/src/__tests__/performance-qualification.test.ts +0 -137
- package/packages/contracts/src/__tests__/rich-card-widget.test.ts +0 -29
- package/packages/contracts/src/__tests__/session-runtime.test.ts +0 -512
- package/packages/contracts/src/__tests__/startup-readiness.test.ts +0 -409
- package/packages/contracts/src/__tests__/terminal-attachment-stream.test.ts +0 -72
- package/packages/contracts/src/__tests__/terminal-attachments.test.ts +0 -329
- package/packages/contracts/src/__tests__/terminal-delivery.test.ts +0 -88
- package/packages/contracts/src/__tests__/terminal-runtime-inventory.test.ts +0 -68
- package/packages/contracts/src/__tests__/terminals.test.ts +0 -87
- package/packages/contracts/src/__tests__/tmux-server-scope.test.ts +0 -166
- package/packages/contracts/src/__tests__/transport-contract-loading.test.ts +0 -91
- package/packages/contracts/src/__tests__/visual-tokens.test.ts +0 -224
- package/packages/contracts/src/__tests__/window-links.test.ts +0 -243
- package/packages/contracts/src/__tests__/workspace-changes-resource.test.ts +0 -317
- package/packages/contracts/src/__tests__/workspace-changes-view.test.ts +0 -122
- package/packages/contracts/src/__tests__/workspace-config.test.ts +0 -294
- package/packages/contracts/src/__tests__/workspace-files-resource.test.ts +0 -261
- package/packages/contracts/src/__tests__/workspace-files-tree.test.ts +0 -147
- package/packages/contracts/src/__tests__/workspace-pane-creation.test.ts +0 -249
- package/packages/contracts/src/__tests__/workspace-promotion.test.ts +0 -94
- package/packages/contracts/src/__tests__/workspace-resource-identity.test.ts +0 -90
- package/packages/contracts/src/daemon-resource-request.test.ts +0 -192
- package/packages/contracts/src/native-operation-identity.test.ts +0 -23
- package/packages/contracts/src/workspace-admission.test.ts +0 -21
- package/packages/contracts/src/workspace-catalog-resource.test.ts +0 -146
- package/packages/core/src/agent-provisioning.test.ts +0 -42
- package/packages/core/src/application-shell-session.test.ts +0 -105
- package/packages/core/src/interaction-evidence.test.ts +0 -134
- package/packages/core/src/interaction-flow.test.ts +0 -66
- package/packages/core/src/interaction-receipts.test.ts +0 -671
- package/packages/core/src/navigator.test.ts +0 -34
- package/packages/core/src/optimistic-projection-conformance.test.ts +0 -75
- package/packages/core/src/optimistic-projection.test.ts +0 -118
- package/packages/core/src/performance-metrics.test.ts +0 -121
- package/packages/core/src/performance-qualification.test.ts +0 -283
- package/packages/core/src/saved-machines.test.ts +0 -80
- package/packages/core/src/terminal-compact-cold.test.ts +0 -87
- package/packages/core/src/terminal-conformance.test.ts +0 -48
- package/packages/core/src/terminal-delivery-cache-lifetime.test.ts +0 -167
- package/packages/core/src/terminal-delivery-encode-cooperative.test.ts +0 -140
- package/packages/core/src/terminal-delivery-representation-hash.test.ts +0 -77
- package/packages/core/src/terminal-delivery.test.ts +0 -1471
- package/packages/core/src/terminal-fnv64-wasm.test.ts +0 -61
- package/packages/core/src/terminal-replica-cold.test.ts +0 -31
- package/packages/core/src/terminal-replica-cooperative.test.ts +0 -170
- package/packages/core/src/terminal-replica-hash-cache.test.ts +0 -283
- package/packages/core/src/terminal-replica.test.ts +0 -347
- package/packages/core/src/workspace-selection.test.ts +0 -63
- package/packages/daemon/dist/__tests__/support.js +0 -33
- package/packages/daemon/dist/lib/__tests__/fixtures/workspace-state-writer.js +0 -58
- package/packages/daemon/dist/lib/__tests__/installed-recovery-fixture.js +0 -400
- package/packages/daemon/dist/terminal/__tests__/MockPtyAdapter.js +0 -182
- package/packages/daemon/dist/terminal/mirror/__tests__/scripted-channel.js +0 -98
- package/packages/daemon/dist/terminal/mirror/__tests__/simulated-channel.js +0 -156
- package/packages/daemon/src/tui/mirror/__snapshots__/activity-surface-renderer.test.tsx.snap +0 -42
- package/packages/daemon/src/tui/mirror/__snapshots__/changes-terminal-surface-renderer.test.tsx.snap +0 -267
- package/packages/daemon/src/tui/mirror/__snapshots__/home-files-surface-renderer.test.tsx.snap +0 -267
- package/packages/daemon/src/tui/mirror/__snapshots__/missions-surface-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/__snapshots__/recipes-gallery-renderer.test.tsx.snap +0 -141
- package/packages/daemon/src/tui/mirror/__snapshots__/shell-chrome-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/runtime/__snapshots__/application-shell-view-renderer.test.tsx.snap +0 -87
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/agent-terminal-canvas-renderer.test.tsx.snap +0 -348
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/application-shell-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/command-palette-surface-renderer.test.tsx.snap +0 -255
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/pane-frame-renderer.test.tsx.snap +0 -525
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/terminal-pane-header-polish-renderer.test.tsx.snap +0 -31
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/workbench-shell-renderer.test.tsx.snap +0 -134
- package/packages/daemon-client/src/application-shell-session.test.ts +0 -196
- package/packages/daemon-client/src/automation-client.test.ts +0 -214
- package/packages/daemon-client/src/bootstrap-coordinator.test.ts +0 -145
- package/packages/daemon-client/src/connection-supervisor.test.ts +0 -109
- package/packages/daemon-client/src/direct-application-shell-transport.test.ts +0 -941
- package/packages/daemon-client/src/first-latest-coordinator.test.ts +0 -175
- package/packages/daemon-client/src/fleet-dial-scheduler.test.ts +0 -114
- package/packages/daemon-client/src/fleet-environments.test.ts +0 -40
- package/packages/daemon-client/src/generation-bound-store-timers.test.ts +0 -74
- package/packages/daemon-client/src/owner-action-client.test.ts +0 -245
- package/packages/daemon-client/src/pane-stream-client.test.ts +0 -2841
- package/packages/daemon-client/src/pane-stream-clock-calibration.test.ts +0 -207
- package/packages/daemon-client/src/pane-stream-loopback-benchmark.test.ts +0 -51
- package/packages/daemon-client/src/push-resource-session.test.ts +0 -509
- package/packages/daemon-client/src/resource-replica.test.ts +0 -186
- package/packages/daemon-client/src/runtime-resource-ledger.test.ts +0 -18
- package/packages/daemon-client/src/scoped-tmux-server-transport.test.ts +0 -114
- package/packages/daemon-client/src/terminal-fast-lane.test.ts +0 -835
- package/packages/daemon-client/src/tmux-server-client.test.ts +0 -279
- package/packages/daemon-client/src/tmux-server-interaction-events.test.ts +0 -230
- package/packages/daemon-client/src/workspace-catalog-v2.test.ts +0 -109
- package/packages/daemon-client/src/workspace-client-boundaries.test.ts +0 -59
- package/packages/daemon-client/src/workspace-client-operations.test.ts +0 -96
- package/packages/daemon-client/src/workspace-client.test.ts +0 -2912
- package/packages/daemon-client/src/workspace-event-supervisor.test.ts +0 -1086
- package/packages/daemon-client/src/workspace-pane-client.test.ts +0 -80
- package/packages/presentation/src/presentation-contract.test.ts +0 -63
- package/packages/tmux-bridge/src/runner.test.ts +0 -614
- package/packages/tmux-bridge/src/targeting.test.ts +0 -107
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots
|
|
2
|
-
|
|
3
|
-
exports[`WorkbenchShell OpenTUI renderer renders the %sx%s compact agent canvas and bottom dock 1`] = `
|
|
4
|
-
"▌● Agent canvas compact · 3 agents
|
|
5
|
-
› Fable · project manager working
|
|
6
|
-
○ Codex implementer 1 · terminal %7 task M31.3
|
|
7
|
-
○ Codex implementer 2 · ready gpt-5.5
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
▤ Files ± Changes ●◆ Missions !◷ Activity ▾ □
|
|
19
|
-
│● missions dock open · preferred 10 rows
|
|
20
|
-
○ Full application parity 7 / 20 cards
|
|
21
|
-
● Workbench dock projection in progress
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
`;
|
|
29
|
-
|
|
30
|
-
exports[`WorkbenchShell OpenTUI renderer renders the %sx%s standard agent canvas and bottom dock 1`] = `
|
|
31
|
-
"▌● Agent canvas standard · 3 agents
|
|
32
|
-
› Fable · project manager working
|
|
33
|
-
○ Codex implementer 1 · terminal %7 task M31.3
|
|
34
|
-
○ Codex implementer 2 · ready gpt-5.5
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
tools ▤ Files ± Changes ●◆ Missions !◷ Activity ▾ close □ max
|
|
58
|
-
│● missions dock open · preferred 14 rows
|
|
59
|
-
○ Full application parity 7 / 20 cards
|
|
60
|
-
● Workbench dock projection in progress
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
`;
|
|
72
|
-
|
|
73
|
-
exports[`WorkbenchShell OpenTUI renderer renders the %sx%s wide agent canvas and bottom dock 1`] = `
|
|
74
|
-
"▌● Agent canvas wide · 3 agents
|
|
75
|
-
› Fable · project manager working
|
|
76
|
-
○ Codex implementer 1 · terminal %7 task M31.3
|
|
77
|
-
○ Codex implementer 2 · ready gpt-5.5
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
tools F3 ▤ Files F4 ± Changes F6 ●◆ Missions F9 !◷ Activity ▾ close □ max
|
|
115
|
-
│● missions dock open · preferred 20 rows
|
|
116
|
-
○ Full application parity 7 / 20 cards
|
|
117
|
-
● Workbench dock projection in progress
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"
|
|
134
|
-
`;
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
ApplicationShellProjectionInputV3SchemaZ,
|
|
4
|
-
COHESION_FIXTURE_V1,
|
|
5
|
-
DAEMON_WIRE_PROTOCOL_VERSION,
|
|
6
|
-
DesktopApplicationShellTargetSchemaZ,
|
|
7
|
-
type ApplicationShellProjectionInputV1,
|
|
8
|
-
type DesktopApplicationShellTarget,
|
|
9
|
-
} from "@tmux-ide/contracts";
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
createApplicationShellSession,
|
|
13
|
-
type ApplicationShellEventHandlers,
|
|
14
|
-
type ApplicationShellTransport,
|
|
15
|
-
} from "./application-shell-session.ts";
|
|
16
|
-
|
|
17
|
-
const daemon = {
|
|
18
|
-
protocolVersion: DAEMON_WIRE_PROTOCOL_VERSION,
|
|
19
|
-
productVersion: "2.8.0",
|
|
20
|
-
instanceId: "9bcf33b0-c837-4a94-b5e8-c0977f54464f",
|
|
21
|
-
startedAt: "2026-08-09T12:00:00.000Z",
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
function target(workspaceName: string): DesktopApplicationShellTarget {
|
|
25
|
-
return { daemon, workspaceName };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function resource(name: string): ApplicationShellProjectionInputV1 {
|
|
29
|
-
return ApplicationShellProjectionInputV3SchemaZ.parse({
|
|
30
|
-
project: { ...COHESION_FIXTURE_V1.project, name },
|
|
31
|
-
workspace: {
|
|
32
|
-
...COHESION_FIXTURE_V1.workspace,
|
|
33
|
-
sidebar: {
|
|
34
|
-
...COHESION_FIXTURE_V1.workspace.sidebar,
|
|
35
|
-
agents: COHESION_FIXTURE_V1.workspace.sidebar.agents.map((agent) => ({
|
|
36
|
-
...agent,
|
|
37
|
-
paneId: null,
|
|
38
|
-
})),
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
dock: COHESION_FIXTURE_V1.dock,
|
|
42
|
-
focus: { ...COHESION_FIXTURE_V1.focus, overlays: [] },
|
|
43
|
-
connection: COHESION_FIXTURE_V1.connection,
|
|
44
|
-
terminalInventory: { activeResourceId: null, resources: [] },
|
|
45
|
-
appWindows: {
|
|
46
|
-
version: 1,
|
|
47
|
-
revision: 0,
|
|
48
|
-
updatedAt: "2026-08-09T12:00:00.000Z",
|
|
49
|
-
windows: {},
|
|
50
|
-
dockRoot: null,
|
|
51
|
-
dockState: { mode: "collapsed", preferredHeight: null, focusZone: "canvas" },
|
|
52
|
-
floatingOrder: [],
|
|
53
|
-
focusedWindowId: null,
|
|
54
|
-
activeLayoutId: null,
|
|
55
|
-
layouts: {},
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function settle(): Promise<void> {
|
|
61
|
-
await Promise.resolve();
|
|
62
|
-
await Promise.resolve();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function deferred<T>() {
|
|
66
|
-
let resolve!: (value: T) => void;
|
|
67
|
-
const promise = new Promise<T>((resolvePromise) => {
|
|
68
|
-
resolve = resolvePromise;
|
|
69
|
-
});
|
|
70
|
-
return { promise, resolve };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
interface ConnectionRecord {
|
|
74
|
-
readonly target: DesktopApplicationShellTarget;
|
|
75
|
-
readonly handlers: ApplicationShellEventHandlers;
|
|
76
|
-
closed: boolean;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function broker(initial: Readonly<Record<string, ApplicationShellProjectionInputV1>>) {
|
|
80
|
-
const resources = new Map(Object.entries(initial));
|
|
81
|
-
const fetches: string[] = [];
|
|
82
|
-
const connections: ConnectionRecord[] = [];
|
|
83
|
-
const transport: ApplicationShellTransport = {
|
|
84
|
-
validateTarget: (value) => DesktopApplicationShellTargetSchemaZ.parse(value),
|
|
85
|
-
fetchApplicationShell: async (applicationTarget) => {
|
|
86
|
-
fetches.push(applicationTarget.workspaceName);
|
|
87
|
-
const snapshot = resources.get(applicationTarget.workspaceName);
|
|
88
|
-
if (!snapshot) throw new Error(`missing ${applicationTarget.workspaceName}`);
|
|
89
|
-
return snapshot;
|
|
90
|
-
},
|
|
91
|
-
connectEvents: (applicationTarget, handlers) => {
|
|
92
|
-
const connection = { target: applicationTarget, handlers, closed: false };
|
|
93
|
-
connections.push(connection);
|
|
94
|
-
return { close: () => (connection.closed = true) };
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
return { resources, fetches, connections, transport };
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
describe("shared application-shell session", () => {
|
|
101
|
-
it("keeps multiple clients isolated while converging invalidated authority", async () => {
|
|
102
|
-
const runtime = broker({ alpha: resource("alpha-1"), beta: resource("beta-1") });
|
|
103
|
-
const alphaReceipts: string[] = [];
|
|
104
|
-
const betaReceipts: string[] = [];
|
|
105
|
-
const alpha = createApplicationShellSession({
|
|
106
|
-
target: target("alpha"),
|
|
107
|
-
transport: runtime.transport,
|
|
108
|
-
onOperationAcknowledged: (receipt) => alphaReceipts.push(receipt.operationId),
|
|
109
|
-
});
|
|
110
|
-
const beta = createApplicationShellSession({
|
|
111
|
-
target: target("beta"),
|
|
112
|
-
transport: runtime.transport,
|
|
113
|
-
onOperationAcknowledged: (receipt) => betaReceipts.push(receipt.operationId),
|
|
114
|
-
});
|
|
115
|
-
runtime.connections[0]!.handlers.onVerifiedOpen();
|
|
116
|
-
runtime.connections[1]!.handlers.onVerifiedOpen();
|
|
117
|
-
await settle();
|
|
118
|
-
|
|
119
|
-
expect(alpha.getState()).toMatchObject({
|
|
120
|
-
status: "live",
|
|
121
|
-
data: { project: { name: "alpha-1" } },
|
|
122
|
-
});
|
|
123
|
-
expect(beta.getState()).toMatchObject({
|
|
124
|
-
status: "live",
|
|
125
|
-
data: { project: { name: "beta-1" } },
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
runtime.resources.set("alpha", resource("alpha-2"));
|
|
129
|
-
runtime.connections[0]!.handlers.onInvalidate();
|
|
130
|
-
runtime.connections[0]!.handlers.onOperationAcknowledged?.({
|
|
131
|
-
daemonInstanceId: daemon.instanceId,
|
|
132
|
-
operationId: "op-alpha",
|
|
133
|
-
sequence: 7,
|
|
134
|
-
revision: 3,
|
|
135
|
-
});
|
|
136
|
-
await settle();
|
|
137
|
-
await settle();
|
|
138
|
-
|
|
139
|
-
expect(alpha.getState()).toMatchObject({ data: { project: { name: "alpha-2" } } });
|
|
140
|
-
expect(beta.getState()).toMatchObject({ data: { project: { name: "beta-1" } } });
|
|
141
|
-
expect(runtime.fetches).toEqual(["alpha", "beta", "alpha"]);
|
|
142
|
-
expect(alphaReceipts).toEqual(["op-alpha"]);
|
|
143
|
-
expect(betaReceipts).toEqual([]);
|
|
144
|
-
expect(alpha.getMetrics()).toMatchObject({
|
|
145
|
-
idleWakeups: 0,
|
|
146
|
-
activeInterests: 1,
|
|
147
|
-
fetchesStarted: 2,
|
|
148
|
-
fetchesSettled: 2,
|
|
149
|
-
invalidationsObserved: 1,
|
|
150
|
-
subscriptionsOpened: 1,
|
|
151
|
-
});
|
|
152
|
-
alpha.dispose();
|
|
153
|
-
beta.dispose();
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it("retires the old request and event connection across target generations", async () => {
|
|
157
|
-
const requests: Array<ReturnType<typeof deferred<ApplicationShellProjectionInputV1>>> = [];
|
|
158
|
-
const signals: AbortSignal[] = [];
|
|
159
|
-
const connections: ConnectionRecord[] = [];
|
|
160
|
-
const transport: ApplicationShellTransport = {
|
|
161
|
-
validateTarget: (value) => DesktopApplicationShellTargetSchemaZ.parse(value),
|
|
162
|
-
fetchApplicationShell: (_applicationTarget, signal) => {
|
|
163
|
-
const request = deferred<ApplicationShellProjectionInputV1>();
|
|
164
|
-
requests.push(request);
|
|
165
|
-
signals.push(signal);
|
|
166
|
-
return request.promise;
|
|
167
|
-
},
|
|
168
|
-
connectEvents: (applicationTarget, handlers) => {
|
|
169
|
-
const connection = { target: applicationTarget, handlers, closed: false };
|
|
170
|
-
connections.push(connection);
|
|
171
|
-
return { close: () => (connection.closed = true) };
|
|
172
|
-
},
|
|
173
|
-
};
|
|
174
|
-
const session = createApplicationShellSession({ target: target("alpha"), transport });
|
|
175
|
-
const firstGeneration = session.getState().generation;
|
|
176
|
-
|
|
177
|
-
session.setTarget(target("beta"));
|
|
178
|
-
expect(signals[0]?.aborted).toBe(true);
|
|
179
|
-
expect(connections[0]?.closed).toBe(true);
|
|
180
|
-
requests[0]!.resolve(resource("stale-alpha"));
|
|
181
|
-
connections[0]!.handlers.onVerifiedOpen();
|
|
182
|
-
connections[0]!.handlers.onInvalidate();
|
|
183
|
-
requests[1]!.resolve(resource("current-beta"));
|
|
184
|
-
connections[1]!.handlers.onVerifiedOpen();
|
|
185
|
-
await settle();
|
|
186
|
-
|
|
187
|
-
expect(session.getState()).toMatchObject({
|
|
188
|
-
status: "live",
|
|
189
|
-
generation: firstGeneration + 1,
|
|
190
|
-
target: { workspaceName: "beta" },
|
|
191
|
-
data: { project: { name: "current-beta" } },
|
|
192
|
-
});
|
|
193
|
-
session.dispose();
|
|
194
|
-
expect(connections[1]?.closed).toBe(true);
|
|
195
|
-
});
|
|
196
|
-
});
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import type { AutomationOperationIntent } from "@tmux-ide/contracts";
|
|
3
|
-
import { createAutomationClient, AutomationInvocationError } from "./automation-client.ts";
|
|
4
|
-
|
|
5
|
-
const id = "00000000-0000-4000-8000-000000000001";
|
|
6
|
-
const other = "00000000-0000-4000-8000-000000000002";
|
|
7
|
-
const handle = { generation: id, operationId: other };
|
|
8
|
-
const intent: AutomationOperationIntent = {
|
|
9
|
-
kind: "send",
|
|
10
|
-
source: null,
|
|
11
|
-
text: "private input",
|
|
12
|
-
enter: true,
|
|
13
|
-
target: {
|
|
14
|
-
kind: "pane",
|
|
15
|
-
environmentId: id,
|
|
16
|
-
paneLifetimeId: id,
|
|
17
|
-
serverScope: { serverId: `tmux-server.${"a".repeat(32)}`, generation: id },
|
|
18
|
-
workspaceName: "workspace.project",
|
|
19
|
-
semanticPaneId: "pane.editor",
|
|
20
|
-
},
|
|
21
|
-
};
|
|
22
|
-
const sent = { version: 1, handle, result: { kind: "send", submitted: true } };
|
|
23
|
-
function client(fetcher: typeof fetch) {
|
|
24
|
-
return createAutomationClient({
|
|
25
|
-
baseUrl: "http://localhost/",
|
|
26
|
-
ownerToken: "owner",
|
|
27
|
-
sourceCredential: "source",
|
|
28
|
-
fetch: fetcher,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
describe("shared automation transport", () => {
|
|
33
|
-
it("uses the declared adapter origin for both reservation and execution", async () => {
|
|
34
|
-
const bodies: Record<string, unknown>[] = [];
|
|
35
|
-
const api = createAutomationClient({
|
|
36
|
-
baseUrl: "http://localhost",
|
|
37
|
-
ownerToken: "owner",
|
|
38
|
-
origin: "mcp",
|
|
39
|
-
fetch: (async (url, init) => {
|
|
40
|
-
bodies.push(JSON.parse(String(init?.body)));
|
|
41
|
-
return Response.json(String(url).endsWith("/reserve") ? { version: 1, handle } : sent);
|
|
42
|
-
}) as typeof fetch,
|
|
43
|
-
});
|
|
44
|
-
await api.reserve(intent);
|
|
45
|
-
await api.execute(handle, intent);
|
|
46
|
-
expect(bodies.map((body) => body.origin)).toEqual(["mcp", "mcp"]);
|
|
47
|
-
});
|
|
48
|
-
it("retries uncertain execution with the exact handle/body and never reserves", async () => {
|
|
49
|
-
const requests: { path: string; init?: RequestInit }[] = [];
|
|
50
|
-
const api = client((async (input, init) => {
|
|
51
|
-
requests.push({ path: String(input), init });
|
|
52
|
-
if (requests.length === 1) throw new Error("lost response after effect");
|
|
53
|
-
return Response.json(sent);
|
|
54
|
-
}) as typeof fetch);
|
|
55
|
-
expect(await api.execute(handle, intent)).toEqual(sent);
|
|
56
|
-
expect(requests).toHaveLength(2);
|
|
57
|
-
expect(requests[0]!.init?.body).toBe(requests[1]!.init?.body);
|
|
58
|
-
expect(
|
|
59
|
-
requests.every(({ path }) => path === "http://localhost/api/v1/automation/execute"),
|
|
60
|
-
).toBe(true);
|
|
61
|
-
expect(requests[0]!.init?.redirect).toBe("error");
|
|
62
|
-
expect(requests[0]!.init?.headers).toEqual({
|
|
63
|
-
Authorization: "Bearer owner",
|
|
64
|
-
"Content-Type": "application/json",
|
|
65
|
-
"X-Tmux-Ide-Pane-Source-Credential": "source",
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("treats a daemon refusal as terminal", async () => {
|
|
70
|
-
let calls = 0;
|
|
71
|
-
const api = client((async () => {
|
|
72
|
-
calls++;
|
|
73
|
-
return Response.json({ error: { code: "operation-unavailable" } }, { status: 409 });
|
|
74
|
-
}) as typeof fetch);
|
|
75
|
-
await expect(api.execute(handle, intent)).rejects.toMatchObject({
|
|
76
|
-
code: "operation-unavailable",
|
|
77
|
-
handle,
|
|
78
|
-
});
|
|
79
|
-
expect(calls).toBe(1);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("returns uncertain execution with its original handle and no private input", async () => {
|
|
83
|
-
let calls = 0;
|
|
84
|
-
const api = client((async () => {
|
|
85
|
-
calls++;
|
|
86
|
-
throw Error("private input");
|
|
87
|
-
}) as typeof fetch);
|
|
88
|
-
const error = await api.execute(handle, intent).catch((reason: unknown) => reason);
|
|
89
|
-
expect(error).toBeInstanceOf(AutomationInvocationError);
|
|
90
|
-
expect(error).toMatchObject({ code: "response-unconfirmed", handle });
|
|
91
|
-
expect(String(error)).not.toContain(intent.text);
|
|
92
|
-
expect(calls).toBe(2);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("does not replay a lost reservation automatically", async () => {
|
|
96
|
-
let calls = 0;
|
|
97
|
-
const api = client((async () => {
|
|
98
|
-
calls++;
|
|
99
|
-
throw Error("lost");
|
|
100
|
-
}) as typeof fetch);
|
|
101
|
-
await expect(api.reserve(intent)).rejects.toMatchObject({
|
|
102
|
-
code: "response-unconfirmed",
|
|
103
|
-
handle: null,
|
|
104
|
-
});
|
|
105
|
-
expect(calls).toBe(1);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("rejects response identity substitution", async () => {
|
|
109
|
-
const api = client((async () =>
|
|
110
|
-
Response.json({ ...sent, handle: { ...handle, generation: other } })) as typeof fetch);
|
|
111
|
-
await expect(api.execute(handle, intent)).rejects.toMatchObject({
|
|
112
|
-
code: "response-unconfirmed",
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("rejects content in status and byte-count mismatches in snapshots", async () => {
|
|
117
|
-
const api = client((async () =>
|
|
118
|
-
Response.json({
|
|
119
|
-
version: 1,
|
|
120
|
-
handle,
|
|
121
|
-
status: "completed",
|
|
122
|
-
result: { kind: "send", submitted: true, text: "private" },
|
|
123
|
-
})) as typeof fetch);
|
|
124
|
-
await expect(api.status(handle)).rejects.toMatchObject({ code: "response-unconfirmed" });
|
|
125
|
-
const badRead = client((async () =>
|
|
126
|
-
Response.json({
|
|
127
|
-
version: 1,
|
|
128
|
-
handle,
|
|
129
|
-
result: { kind: "read", capturedBytes: 1, returnedBytes: 1, truncated: false },
|
|
130
|
-
read: { availability: "available", text: "not one byte" },
|
|
131
|
-
})) as typeof fetch);
|
|
132
|
-
await expect(
|
|
133
|
-
badRead.execute(handle, { kind: "read", target: intent.target, source: null }),
|
|
134
|
-
).rejects.toMatchObject({ code: "response-unconfirmed" });
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("validates before transport and snapshots mutable request values", async () => {
|
|
138
|
-
const bodies: unknown[] = [];
|
|
139
|
-
const mutable = { ...intent };
|
|
140
|
-
const api = client((async (_input, init) => {
|
|
141
|
-
bodies.push(JSON.parse(String(init?.body)));
|
|
142
|
-
return Response.json(sent);
|
|
143
|
-
}) as typeof fetch);
|
|
144
|
-
const pending = api.execute(handle, mutable);
|
|
145
|
-
mutable.text = "changed";
|
|
146
|
-
await pending;
|
|
147
|
-
expect((bodies[0] as { intent: { text: string } }).intent.text).toBe("private input");
|
|
148
|
-
await expect(api.execute({ ...handle, operationId: "invalid" }, intent)).rejects.toThrow();
|
|
149
|
-
expect(bodies).toHaveLength(1);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
describe("automation cancellation", () => {
|
|
154
|
-
it("prevents dispatch when already cancelled", async () => {
|
|
155
|
-
let calls = 0;
|
|
156
|
-
const controller = new AbortController();
|
|
157
|
-
controller.abort();
|
|
158
|
-
const api = client((async () => {
|
|
159
|
-
calls++;
|
|
160
|
-
return Response.json(sent);
|
|
161
|
-
}) as typeof fetch);
|
|
162
|
-
await expect(api.execute(handle, intent, { signal: controller.signal })).rejects.toMatchObject({
|
|
163
|
-
code: "request-cancelled",
|
|
164
|
-
handle,
|
|
165
|
-
});
|
|
166
|
-
expect(calls).toBe(0);
|
|
167
|
-
});
|
|
168
|
-
it("keeps the original handle and never retries cancellation after dispatch", async () => {
|
|
169
|
-
let calls = 0;
|
|
170
|
-
const controller = new AbortController();
|
|
171
|
-
const api = client((async (_input, init) => {
|
|
172
|
-
calls++;
|
|
173
|
-
controller.abort();
|
|
174
|
-
init?.signal?.throwIfAborted();
|
|
175
|
-
return Response.json(sent);
|
|
176
|
-
}) as typeof fetch);
|
|
177
|
-
await expect(api.execute(handle, intent, { signal: controller.signal })).rejects.toMatchObject({
|
|
178
|
-
code: "response-unconfirmed",
|
|
179
|
-
handle,
|
|
180
|
-
});
|
|
181
|
-
expect(calls).toBe(1);
|
|
182
|
-
});
|
|
183
|
-
it("cancels a stalled response body without retaining or exposing received chunks", async () => {
|
|
184
|
-
const controller = new AbortController();
|
|
185
|
-
let cancelCount = 0;
|
|
186
|
-
let connected!: () => void;
|
|
187
|
-
const ready = new Promise<void>((resolve) => {
|
|
188
|
-
connected = resolve;
|
|
189
|
-
});
|
|
190
|
-
let calls = 0;
|
|
191
|
-
const api = client((async () => {
|
|
192
|
-
calls++;
|
|
193
|
-
return new Response(
|
|
194
|
-
new ReadableStream({
|
|
195
|
-
start(stream) {
|
|
196
|
-
stream.enqueue(new TextEncoder().encode("private partial content"));
|
|
197
|
-
connected();
|
|
198
|
-
},
|
|
199
|
-
cancel() {
|
|
200
|
-
cancelCount++;
|
|
201
|
-
},
|
|
202
|
-
}),
|
|
203
|
-
);
|
|
204
|
-
}) as typeof fetch);
|
|
205
|
-
const execution = api.execute(handle, intent, { signal: controller.signal });
|
|
206
|
-
await ready;
|
|
207
|
-
controller.abort();
|
|
208
|
-
const error = await execution.catch((reason: unknown) => reason);
|
|
209
|
-
expect(error).toMatchObject({ code: "response-unconfirmed", handle });
|
|
210
|
-
expect(String(error)).not.toContain("private partial content");
|
|
211
|
-
expect(calls).toBe(1);
|
|
212
|
-
expect(cancelCount).toBe(1);
|
|
213
|
-
});
|
|
214
|
-
});
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { DaemonBootstrapCoordinator, type DaemonBootstrapPhase } from "./bootstrap-coordinator.ts";
|
|
4
|
-
|
|
5
|
-
interface Candidate {
|
|
6
|
-
readonly generation: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
describe("DaemonBootstrapCoordinator", () => {
|
|
10
|
-
it("returns an already compatible generation without spawning", async () => {
|
|
11
|
-
const candidate = { generation: "existing" };
|
|
12
|
-
let starts = 0;
|
|
13
|
-
const phases: DaemonBootstrapPhase[] = [];
|
|
14
|
-
const coordinator = new DaemonBootstrapCoordinator<Candidate>({
|
|
15
|
-
probe: () => ({ status: "compatible", candidate }),
|
|
16
|
-
spawn: () => {
|
|
17
|
-
starts += 1;
|
|
18
|
-
},
|
|
19
|
-
onPhaseChanged: ({ phase }) => phases.push(phase),
|
|
20
|
-
});
|
|
21
|
-
const result = await coordinator.ensure();
|
|
22
|
-
expect(result).toMatchObject({ candidate, source: "existing" });
|
|
23
|
-
expect(result.timings.controlReadyAt).not.toBeNull();
|
|
24
|
-
expect(starts).toBe(0);
|
|
25
|
-
expect(phases).toEqual(["probing", "control-ready", "ready"]);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("single-flights concurrent starters through control and empty inventory", async () => {
|
|
29
|
-
let candidate: Candidate | null = null;
|
|
30
|
-
let starts = 0;
|
|
31
|
-
let release!: () => void;
|
|
32
|
-
const barrier = new Promise<void>((resolve) => {
|
|
33
|
-
release = resolve;
|
|
34
|
-
});
|
|
35
|
-
const phases: DaemonBootstrapPhase[] = [];
|
|
36
|
-
const coordinator = new DaemonBootstrapCoordinator<Candidate, string[]>({
|
|
37
|
-
probe: () =>
|
|
38
|
-
candidate ? { status: "compatible", candidate } : { status: "absent-or-stale" },
|
|
39
|
-
spawn: async () => {
|
|
40
|
-
starts += 1;
|
|
41
|
-
await barrier;
|
|
42
|
-
candidate = { generation: "winner" };
|
|
43
|
-
},
|
|
44
|
-
reconcileInventory: () => ({ status: "empty", inventory: [] }),
|
|
45
|
-
onPhaseChanged: ({ phase }) => phases.push(phase),
|
|
46
|
-
});
|
|
47
|
-
const first = coordinator.ensure();
|
|
48
|
-
const second = coordinator.ensure();
|
|
49
|
-
expect(first).toBe(second);
|
|
50
|
-
release();
|
|
51
|
-
await expect(first).resolves.toMatchObject({ source: "started", inventory: [] });
|
|
52
|
-
expect(starts).toBe(1);
|
|
53
|
-
expect(phases).toEqual([
|
|
54
|
-
"probing",
|
|
55
|
-
"spawning",
|
|
56
|
-
"control-ready",
|
|
57
|
-
"inventory-reconciling",
|
|
58
|
-
"ready",
|
|
59
|
-
]);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("preserves an incompatible probe reason without spawning", async () => {
|
|
63
|
-
let starts = 0;
|
|
64
|
-
const coordinator = new DaemonBootstrapCoordinator<Candidate, never, "protocol-mismatch">({
|
|
65
|
-
probe: () => ({ status: "incompatible", reason: "protocol-mismatch" }),
|
|
66
|
-
spawn: () => {
|
|
67
|
-
starts += 1;
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
await expect(coordinator.ensure()).rejects.toMatchObject({
|
|
71
|
-
code: "incompatible",
|
|
72
|
-
reason: "protocol-mismatch",
|
|
73
|
-
});
|
|
74
|
-
expect(starts).toBe(0);
|
|
75
|
-
expect(coordinator.snapshot()).toMatchObject({
|
|
76
|
-
phase: "incompatible",
|
|
77
|
-
reason: "protocol-mismatch",
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("waits on a living published generation without spawning a contender", async () => {
|
|
82
|
-
const candidate = { generation: "elected" };
|
|
83
|
-
let probes = 0;
|
|
84
|
-
let starts = 0;
|
|
85
|
-
const coordinator = new DaemonBootstrapCoordinator<Candidate>({
|
|
86
|
-
probe: () =>
|
|
87
|
-
++probes === 1
|
|
88
|
-
? { status: "control-pending", candidate }
|
|
89
|
-
: { status: "compatible", candidate },
|
|
90
|
-
spawn: () => {
|
|
91
|
-
starts += 1;
|
|
92
|
-
},
|
|
93
|
-
sleep: async () => undefined,
|
|
94
|
-
});
|
|
95
|
-
await expect(coordinator.ensure()).resolves.toMatchObject({
|
|
96
|
-
source: "existing",
|
|
97
|
-
candidate,
|
|
98
|
-
});
|
|
99
|
-
expect(starts).toBe(0);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("times out deterministically and permits a later retry", async () => {
|
|
103
|
-
let clock = 0;
|
|
104
|
-
let starts = 0;
|
|
105
|
-
const coordinator = new DaemonBootstrapCoordinator<Candidate>({
|
|
106
|
-
probe: () => ({ status: "absent-or-stale" }),
|
|
107
|
-
spawn: () => {
|
|
108
|
-
starts += 1;
|
|
109
|
-
},
|
|
110
|
-
timeoutMs: 3,
|
|
111
|
-
pollMs: () => 1,
|
|
112
|
-
now: () => clock,
|
|
113
|
-
sleep: async (milliseconds) => {
|
|
114
|
-
clock += milliseconds;
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
await expect(coordinator.ensure()).rejects.toMatchObject({ code: "control-timeout" });
|
|
118
|
-
await expect(coordinator.ensure()).rejects.toMatchObject({ code: "control-timeout" });
|
|
119
|
-
expect(starts).toBe(2);
|
|
120
|
-
expect(coordinator.snapshot()).toMatchObject({ phase: "failed", attempt: 2 });
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("waits for an externally owned startup without inventing a candidate or spawning", async () => {
|
|
125
|
-
let now = 0;
|
|
126
|
-
let spawns = 0;
|
|
127
|
-
const coordinator = new DaemonBootstrapCoordinator<Candidate, never, string>({
|
|
128
|
-
probe: () =>
|
|
129
|
-
now >= 25
|
|
130
|
-
? { status: "compatible", candidate: { generation: "supervised" } }
|
|
131
|
-
: { status: "owner-pending" },
|
|
132
|
-
spawn: () => {
|
|
133
|
-
spawns++;
|
|
134
|
-
},
|
|
135
|
-
now: () => now,
|
|
136
|
-
sleep: async (ms) => {
|
|
137
|
-
now += ms;
|
|
138
|
-
},
|
|
139
|
-
timeoutMs: 100,
|
|
140
|
-
});
|
|
141
|
-
const result = await coordinator.ensure();
|
|
142
|
-
expect(result.candidate.generation).toBe("supervised");
|
|
143
|
-
expect(result.source).toBe("existing");
|
|
144
|
-
expect(spawns).toBe(0);
|
|
145
|
-
});
|