tmux-ide 2.9.2 → 2.9.4

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.
Files changed (267) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +121 -140
  3. package/bin/cli.js +2457 -727
  4. package/package.json +23 -8
  5. package/packages/contracts/src/desktop-host.ts +3 -0
  6. package/packages/contracts/src/saved-machines.ts +19 -0
  7. package/packages/contracts/src/terminal-replica.ts +1 -1
  8. package/packages/core/package.json +3 -2
  9. package/packages/core/src/terminal-delivery.ts +51 -3
  10. package/packages/core/src/terminal-fnv64-wasm.ts +4 -3
  11. package/packages/core/src/terminal-replica-hash-cache.ts +129 -31
  12. package/packages/core/src/terminal-replica-owned-row.ts +27 -0
  13. package/packages/core/src/terminal-replica.ts +13 -28
  14. package/packages/daemon/dist/command-center/daemon-fleet-facts-observer.js +96 -10
  15. package/packages/daemon/dist/command-center/resources/saved-machine-route.js +40 -1
  16. package/packages/daemon/dist/command-center/ws-events.js +5 -24
  17. package/packages/daemon/dist/lib/canonical-daemon-bootstrap.js +8 -0
  18. package/packages/daemon/dist/lib/canonical-daemon.js +28 -2
  19. package/packages/daemon/dist/lib/daemon-embed.js +1 -1
  20. package/packages/daemon/dist/lib/daemon-service-manager.js +122 -0
  21. package/packages/daemon/dist/lib/daemon-service-plan.js +127 -0
  22. package/packages/daemon/dist/lib/daemon-service.js +259 -0
  23. package/packages/daemon/dist/lib/install-origin.js +2 -0
  24. package/packages/daemon/dist/lib/local-fleet-request.js +9 -3
  25. package/packages/daemon/dist/lib/log.js +6 -10
  26. package/packages/daemon/dist/lib/managed-installer-update.js +67 -0
  27. package/packages/daemon/dist/lib/{tui-download-lock.js → private-operation-lock.js} +11 -11
  28. package/packages/daemon/dist/lib/remote-daemon-info.js +3 -2
  29. package/packages/daemon/dist/lib/remote-daemon-lifecycle.js +4 -4
  30. package/packages/daemon/dist/lib/remote-tmux-command.js +10 -0
  31. package/packages/daemon/dist/lib/runtime-namespace.js +28 -16
  32. package/packages/daemon/dist/lib/saved-machines.js +40 -2
  33. package/packages/daemon/dist/lib/ssh-daemon-transport.js +24 -9
  34. package/packages/daemon/dist/lib/tmux-server-owner.js +1 -1
  35. package/packages/daemon/dist/lib/tmux-server-registration.js +21 -3
  36. package/packages/daemon/dist/lib/tui-binary.js +2 -2
  37. package/packages/daemon/dist/lib/update.js +18 -0
  38. package/packages/daemon/dist/lib/workspace-multiplexer-verbs.js +24 -1
  39. package/packages/daemon/dist/machines.js +40 -4
  40. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +2 -2
  41. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
  42. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
  43. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +2 -2
  44. package/packages/daemon/dist/terminal/attachments/direct-websocket.js +4 -1
  45. package/packages/daemon/dist/terminal/attachments/native-runtime.js +22 -13
  46. package/packages/daemon/dist/terminal/mirror/mirror-service.js +101 -3
  47. package/packages/daemon/dist/terminal/mirror/registered-window-guard.js +62 -0
  48. package/packages/daemon/dist/terminal/mirror/session-channel.js +75 -4
  49. package/packages/daemon/dist/terminal/mirror/shared-window-index.js +45 -0
  50. package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +11 -1
  51. package/packages/daemon/dist/terminal/session-runtime/multiplexer-backend.js +1 -0
  52. package/packages/daemon/dist/terminal/session-runtime/registry.js +56 -7
  53. package/packages/daemon/dist/terminal/session-runtime/semantic-mutation-executor.js +3 -1
  54. package/packages/daemon/dist/tui/integrations/claude-agent-teams.js +264 -0
  55. package/packages/daemon/dist/tui/mirror/dialog-model.js +2 -35
  56. package/packages/daemon/dist/tui/mirror/runtime/application-daemon-authority-owner.js +8 -0
  57. package/packages/daemon/dist/tui/mirror/runtime/application-home-fleet.js +20 -2
  58. package/packages/daemon/dist/tui/mirror/runtime/application-machine-agents.js +7 -1
  59. package/packages/daemon/dist/tui/mirror/runtime/application-machine-authority.js +59 -9
  60. package/packages/daemon/dist/tui/mirror/runtime/application-machine-navigation.js +27 -19
  61. package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +23 -18
  62. package/packages/daemon/dist/tui/mirror/runtime/application-machine-startup.js +31 -2
  63. package/packages/daemon/dist/tui/mirror/runtime/application-palette-search-owner.js +7 -3
  64. package/packages/daemon/dist/tui/mirror/runtime/application-performance-log.js +3 -0
  65. package/packages/daemon/dist/tui/mirror/runtime/application-terminal-input-ingress.js +146 -2
  66. package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +201 -50
  67. package/packages/daemon/dist/tui/mirror/runtime/bounded-performance-record-writer.js +50 -10
  68. package/packages/daemon/dist/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.js +6 -5
  69. package/packages/daemon/dist/tui/mirror/runtime/terminal-pane-input-router.js +3 -0
  70. package/packages/daemon/dist/tui/mirror/runtime/workspace-terminal-fast-lane.js +8 -2
  71. package/packages/daemon/dist/tui/mirror/terminal-text.js +35 -0
  72. package/packages/daemon/dist/tui/mirror/workspace/connection-feedback.js +5 -3
  73. package/packages/daemon/src/command-center/daemon-fleet-facts-observer.ts +100 -10
  74. package/packages/daemon/src/command-center/resources/saved-machine-route.ts +48 -1
  75. package/packages/daemon/src/command-center/ws-events.ts +5 -25
  76. package/packages/daemon/src/lib/canonical-daemon-bootstrap.ts +12 -0
  77. package/packages/daemon/src/lib/canonical-daemon.ts +44 -2
  78. package/packages/daemon/src/lib/daemon-embed.ts +1 -0
  79. package/packages/daemon/src/lib/daemon-service-manager.ts +152 -0
  80. package/packages/daemon/src/lib/daemon-service-plan.ts +153 -0
  81. package/packages/daemon/src/lib/daemon-service.ts +301 -0
  82. package/packages/daemon/src/lib/install-origin.ts +5 -0
  83. package/packages/daemon/src/lib/local-fleet-request.ts +19 -3
  84. package/packages/daemon/src/lib/log.ts +13 -9
  85. package/packages/daemon/src/lib/managed-installer-update.ts +79 -0
  86. package/packages/daemon/src/lib/{tui-download-lock.ts → private-operation-lock.ts} +15 -11
  87. package/packages/daemon/src/lib/remote-daemon-info.ts +3 -3
  88. package/packages/daemon/src/lib/remote-daemon-lifecycle.ts +4 -4
  89. package/packages/daemon/src/lib/remote-tmux-command.ts +12 -0
  90. package/packages/daemon/src/lib/runtime-namespace.ts +27 -18
  91. package/packages/daemon/src/lib/saved-machines.ts +40 -1
  92. package/packages/daemon/src/lib/ssh-daemon-transport.ts +25 -7
  93. package/packages/daemon/src/lib/tmux-server-owner.ts +1 -0
  94. package/packages/daemon/src/lib/tmux-server-registration.ts +21 -3
  95. package/packages/daemon/src/lib/tui-binary.ts +2 -2
  96. package/packages/daemon/src/lib/update.ts +24 -0
  97. package/packages/daemon/src/lib/workspace-multiplexer-verbs.ts +22 -0
  98. package/packages/daemon/src/machines.ts +53 -5
  99. package/packages/daemon/src/terminal/attachments/direct-websocket.ts +8 -2
  100. package/packages/daemon/src/terminal/attachments/native-runtime.ts +28 -13
  101. package/packages/daemon/src/terminal/mirror/mirror-service.ts +105 -3
  102. package/packages/daemon/src/terminal/mirror/registered-window-guard.ts +66 -0
  103. package/packages/daemon/src/terminal/mirror/session-channel.ts +92 -4
  104. package/packages/daemon/src/terminal/mirror/shared-window-index.ts +42 -0
  105. package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +12 -1
  106. package/packages/daemon/src/terminal/session-runtime/multiplexer-backend.ts +2 -0
  107. package/packages/daemon/src/terminal/session-runtime/registry.ts +70 -8
  108. package/packages/daemon/src/terminal/session-runtime/semantic-mutation-executor.ts +3 -1
  109. package/packages/daemon/src/tui/integrations/claude-agent-teams.ts +334 -0
  110. package/packages/daemon/src/tui/mirror/dialog-model.ts +2 -32
  111. package/packages/daemon/src/tui/mirror/runtime/application-daemon-authority-owner.ts +8 -0
  112. package/packages/daemon/src/tui/mirror/runtime/application-home-fleet.ts +19 -2
  113. package/packages/daemon/src/tui/mirror/runtime/application-machine-agents.ts +6 -1
  114. package/packages/daemon/src/tui/mirror/runtime/application-machine-authority.ts +60 -11
  115. package/packages/daemon/src/tui/mirror/runtime/application-machine-navigation.ts +39 -33
  116. package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +31 -23
  117. package/packages/daemon/src/tui/mirror/runtime/application-machine-startup.ts +38 -2
  118. package/packages/daemon/src/tui/mirror/runtime/application-palette-search-owner.ts +7 -4
  119. package/packages/daemon/src/tui/mirror/runtime/application-performance-log.ts +3 -0
  120. package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +1 -1
  121. package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +47 -46
  122. package/packages/daemon/src/tui/mirror/runtime/application-terminal-input-ingress.ts +165 -2
  123. package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +207 -53
  124. package/packages/daemon/src/tui/mirror/runtime/bounded-performance-record-writer.ts +49 -9
  125. package/packages/daemon/src/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.ts +6 -5
  126. package/packages/daemon/src/tui/mirror/runtime/terminal-pane-input-router.ts +4 -0
  127. package/packages/daemon/src/tui/mirror/runtime/workspace-terminal-fast-lane.ts +8 -3
  128. package/packages/daemon/src/tui/mirror/terminal-text.ts +32 -0
  129. package/packages/daemon/src/tui/mirror/workspace/connection-feedback.ts +6 -3
  130. package/packages/daemon-client/src/fleet-connection-status.ts +10 -1
  131. package/packages/daemon-client/src/workspace-client.ts +4 -1
  132. package/skill/SKILL.md +30 -30
  133. package/packages/contracts/src/__tests__/actions-contract.test.ts +0 -119
  134. package/packages/contracts/src/__tests__/agent-graph-overlay.test.ts +0 -428
  135. package/packages/contracts/src/__tests__/app-window-mutation.test.ts +0 -51
  136. package/packages/contracts/src/__tests__/app-window-state.test.ts +0 -34
  137. package/packages/contracts/src/__tests__/application-shell.test.ts +0 -645
  138. package/packages/contracts/src/__tests__/automation-operations.test.ts +0 -78
  139. package/packages/contracts/src/__tests__/causal-cell.test.ts +0 -47
  140. package/packages/contracts/src/__tests__/client-view-state.test.ts +0 -115
  141. package/packages/contracts/src/__tests__/cohesion-fixture.test.ts +0 -106
  142. package/packages/contracts/src/__tests__/commands.test.ts +0 -86
  143. package/packages/contracts/src/__tests__/control.test.ts +0 -154
  144. package/packages/contracts/src/__tests__/daemon-events.test.ts +0 -405
  145. package/packages/contracts/src/__tests__/daemon-resources.test.ts +0 -143
  146. package/packages/contracts/src/__tests__/daemon-wire.test.ts +0 -149
  147. package/packages/contracts/src/__tests__/desktop-host.test.ts +0 -269
  148. package/packages/contracts/src/__tests__/desktop-missions.test.ts +0 -215
  149. package/packages/contracts/src/__tests__/experience-conformance.test.ts +0 -153
  150. package/packages/contracts/src/__tests__/experience-kernel-boundary.test.ts +0 -113
  151. package/packages/contracts/src/__tests__/experience-shell.test.ts +0 -65
  152. package/packages/contracts/src/__tests__/fixtures/daemon-wire-v2.json +0 -35
  153. package/packages/contracts/src/__tests__/fixtures/daemon-wire-v3.json +0 -35
  154. package/packages/contracts/src/__tests__/fixtures/daemon-wire-v4.json +0 -35
  155. package/packages/contracts/src/__tests__/fixtures/scoped-receipt.ts +0 -62
  156. package/packages/contracts/src/__tests__/fleet-agent-graph.test.ts +0 -231
  157. package/packages/contracts/src/__tests__/fleet-catalog.test.ts +0 -270
  158. package/packages/contracts/src/__tests__/fleet-lifecycle.test.ts +0 -62
  159. package/packages/contracts/src/__tests__/focus-overlay.test.ts +0 -110
  160. package/packages/contracts/src/__tests__/ide-config.test.ts +0 -64
  161. package/packages/contracts/src/__tests__/interaction-evidence.test.ts +0 -288
  162. package/packages/contracts/src/__tests__/interaction-receipt-v2.test.ts +0 -72
  163. package/packages/contracts/src/__tests__/issue-error.test.ts +0 -78
  164. package/packages/contracts/src/__tests__/mission-projections.test.ts +0 -186
  165. package/packages/contracts/src/__tests__/missions.test.ts +0 -272
  166. package/packages/contracts/src/__tests__/multiplexer-verbs.test.ts +0 -242
  167. package/packages/contracts/src/__tests__/pane-appearance.test.ts +0 -146
  168. package/packages/contracts/src/__tests__/pane-read-result.test.ts +0 -55
  169. package/packages/contracts/src/__tests__/pane-stream.test.ts +0 -536
  170. package/packages/contracts/src/__tests__/pane-widget-marker.test.ts +0 -327
  171. package/packages/contracts/src/__tests__/performance-metrics.test.ts +0 -73
  172. package/packages/contracts/src/__tests__/performance-qualification.test.ts +0 -137
  173. package/packages/contracts/src/__tests__/rich-card-widget.test.ts +0 -29
  174. package/packages/contracts/src/__tests__/session-runtime.test.ts +0 -512
  175. package/packages/contracts/src/__tests__/startup-readiness.test.ts +0 -409
  176. package/packages/contracts/src/__tests__/terminal-attachment-stream.test.ts +0 -72
  177. package/packages/contracts/src/__tests__/terminal-attachments.test.ts +0 -329
  178. package/packages/contracts/src/__tests__/terminal-delivery.test.ts +0 -88
  179. package/packages/contracts/src/__tests__/terminal-runtime-inventory.test.ts +0 -68
  180. package/packages/contracts/src/__tests__/terminals.test.ts +0 -87
  181. package/packages/contracts/src/__tests__/tmux-server-scope.test.ts +0 -166
  182. package/packages/contracts/src/__tests__/transport-contract-loading.test.ts +0 -91
  183. package/packages/contracts/src/__tests__/visual-tokens.test.ts +0 -224
  184. package/packages/contracts/src/__tests__/window-links.test.ts +0 -243
  185. package/packages/contracts/src/__tests__/workspace-changes-resource.test.ts +0 -317
  186. package/packages/contracts/src/__tests__/workspace-changes-view.test.ts +0 -122
  187. package/packages/contracts/src/__tests__/workspace-config.test.ts +0 -294
  188. package/packages/contracts/src/__tests__/workspace-files-resource.test.ts +0 -261
  189. package/packages/contracts/src/__tests__/workspace-files-tree.test.ts +0 -147
  190. package/packages/contracts/src/__tests__/workspace-pane-creation.test.ts +0 -249
  191. package/packages/contracts/src/__tests__/workspace-promotion.test.ts +0 -94
  192. package/packages/contracts/src/__tests__/workspace-resource-identity.test.ts +0 -90
  193. package/packages/contracts/src/daemon-resource-request.test.ts +0 -192
  194. package/packages/contracts/src/native-operation-identity.test.ts +0 -23
  195. package/packages/contracts/src/workspace-admission.test.ts +0 -21
  196. package/packages/contracts/src/workspace-catalog-resource.test.ts +0 -146
  197. package/packages/core/src/agent-provisioning.test.ts +0 -42
  198. package/packages/core/src/application-shell-session.test.ts +0 -105
  199. package/packages/core/src/interaction-evidence.test.ts +0 -134
  200. package/packages/core/src/interaction-flow.test.ts +0 -66
  201. package/packages/core/src/interaction-receipts.test.ts +0 -671
  202. package/packages/core/src/navigator.test.ts +0 -34
  203. package/packages/core/src/optimistic-projection-conformance.test.ts +0 -75
  204. package/packages/core/src/optimistic-projection.test.ts +0 -118
  205. package/packages/core/src/performance-metrics.test.ts +0 -121
  206. package/packages/core/src/performance-qualification.test.ts +0 -283
  207. package/packages/core/src/saved-machines.test.ts +0 -80
  208. package/packages/core/src/terminal-compact-cold.test.ts +0 -87
  209. package/packages/core/src/terminal-conformance.test.ts +0 -48
  210. package/packages/core/src/terminal-delivery-cache-lifetime.test.ts +0 -167
  211. package/packages/core/src/terminal-delivery-encode-cooperative.test.ts +0 -140
  212. package/packages/core/src/terminal-delivery-representation-hash.test.ts +0 -77
  213. package/packages/core/src/terminal-delivery.test.ts +0 -1471
  214. package/packages/core/src/terminal-fnv64-wasm.test.ts +0 -61
  215. package/packages/core/src/terminal-replica-cold.test.ts +0 -31
  216. package/packages/core/src/terminal-replica-cooperative.test.ts +0 -170
  217. package/packages/core/src/terminal-replica-hash-cache.test.ts +0 -283
  218. package/packages/core/src/terminal-replica.test.ts +0 -347
  219. package/packages/core/src/workspace-selection.test.ts +0 -63
  220. package/packages/daemon/dist/__tests__/support.js +0 -33
  221. package/packages/daemon/dist/lib/__tests__/fixtures/workspace-state-writer.js +0 -58
  222. package/packages/daemon/dist/lib/__tests__/installed-recovery-fixture.js +0 -400
  223. package/packages/daemon/dist/terminal/__tests__/MockPtyAdapter.js +0 -182
  224. package/packages/daemon/dist/terminal/mirror/__tests__/scripted-channel.js +0 -98
  225. package/packages/daemon/dist/terminal/mirror/__tests__/simulated-channel.js +0 -156
  226. package/packages/daemon/src/tui/mirror/__snapshots__/activity-surface-renderer.test.tsx.snap +0 -42
  227. package/packages/daemon/src/tui/mirror/__snapshots__/changes-terminal-surface-renderer.test.tsx.snap +0 -267
  228. package/packages/daemon/src/tui/mirror/__snapshots__/home-files-surface-renderer.test.tsx.snap +0 -267
  229. package/packages/daemon/src/tui/mirror/__snapshots__/missions-surface-renderer.test.tsx.snap +0 -134
  230. package/packages/daemon/src/tui/mirror/__snapshots__/recipes-gallery-renderer.test.tsx.snap +0 -141
  231. package/packages/daemon/src/tui/mirror/__snapshots__/shell-chrome-renderer.test.tsx.snap +0 -134
  232. package/packages/daemon/src/tui/mirror/runtime/__snapshots__/application-shell-view-renderer.test.tsx.snap +0 -87
  233. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/agent-terminal-canvas-renderer.test.tsx.snap +0 -348
  234. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/application-shell-renderer.test.tsx.snap +0 -134
  235. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/command-palette-surface-renderer.test.tsx.snap +0 -255
  236. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/pane-frame-renderer.test.tsx.snap +0 -525
  237. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/terminal-pane-header-polish-renderer.test.tsx.snap +0 -31
  238. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/workbench-shell-renderer.test.tsx.snap +0 -134
  239. package/packages/daemon-client/src/application-shell-session.test.ts +0 -196
  240. package/packages/daemon-client/src/automation-client.test.ts +0 -214
  241. package/packages/daemon-client/src/bootstrap-coordinator.test.ts +0 -145
  242. package/packages/daemon-client/src/connection-supervisor.test.ts +0 -109
  243. package/packages/daemon-client/src/direct-application-shell-transport.test.ts +0 -941
  244. package/packages/daemon-client/src/first-latest-coordinator.test.ts +0 -175
  245. package/packages/daemon-client/src/fleet-dial-scheduler.test.ts +0 -114
  246. package/packages/daemon-client/src/fleet-environments.test.ts +0 -40
  247. package/packages/daemon-client/src/generation-bound-store-timers.test.ts +0 -74
  248. package/packages/daemon-client/src/owner-action-client.test.ts +0 -245
  249. package/packages/daemon-client/src/pane-stream-client.test.ts +0 -2841
  250. package/packages/daemon-client/src/pane-stream-clock-calibration.test.ts +0 -207
  251. package/packages/daemon-client/src/pane-stream-loopback-benchmark.test.ts +0 -51
  252. package/packages/daemon-client/src/push-resource-session.test.ts +0 -509
  253. package/packages/daemon-client/src/resource-replica.test.ts +0 -186
  254. package/packages/daemon-client/src/runtime-resource-ledger.test.ts +0 -18
  255. package/packages/daemon-client/src/scoped-tmux-server-transport.test.ts +0 -114
  256. package/packages/daemon-client/src/terminal-fast-lane.test.ts +0 -835
  257. package/packages/daemon-client/src/tmux-server-client.test.ts +0 -279
  258. package/packages/daemon-client/src/tmux-server-interaction-events.test.ts +0 -230
  259. package/packages/daemon-client/src/workspace-catalog-v2.test.ts +0 -109
  260. package/packages/daemon-client/src/workspace-client-boundaries.test.ts +0 -59
  261. package/packages/daemon-client/src/workspace-client-operations.test.ts +0 -96
  262. package/packages/daemon-client/src/workspace-client.test.ts +0 -2912
  263. package/packages/daemon-client/src/workspace-event-supervisor.test.ts +0 -1086
  264. package/packages/daemon-client/src/workspace-pane-client.test.ts +0 -80
  265. package/packages/presentation/src/presentation-contract.test.ts +0 -63
  266. package/packages/tmux-bridge/src/runner.test.ts +0 -614
  267. package/packages/tmux-bridge/src/targeting.test.ts +0 -107
@@ -1,279 +0,0 @@
1
- import { describe, expect, it, mock } from "bun:test";
2
- import {
3
- PANE_STREAM_PROTOCOL_VERSION,
4
- PANE_STREAM_REDEEM_PATH,
5
- PANE_STREAM_WEBSOCKET_SUBPROTOCOL,
6
- tmuxServerPaneStreamPath,
7
- } from "@tmux-ide/contracts";
8
- import {
9
- createTmuxServerClient,
10
- listTmuxServers,
11
- registerTmuxServer,
12
- removeTmuxServer,
13
- } from "./tmux-server-client.ts";
14
- const scope = {
15
- serverId: `tmux-server.${"a".repeat(32)}`,
16
- generation: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
17
- };
18
- const other = { ...scope, serverId: `tmux-server.${"b".repeat(32)}` };
19
- const requestId = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb";
20
- const base = {
21
- baseUrl: "http://127.0.0.1:4000",
22
- ownerToken: "owner",
23
- hostClientId: "client",
24
- origin: "http://localhost:3000",
25
- };
26
- const sessions = (server = scope) => ({
27
- version: 1,
28
- server,
29
- sessions: [
30
- {
31
- liveSessionId: `live-session.${"a".repeat(20)}`,
32
- sessionName: "main",
33
- workspaceName: "alias",
34
- paneCount: 1,
35
- },
36
- ],
37
- });
38
- const stream = {
39
- protocolVersion: PANE_STREAM_PROTOCOL_VERSION,
40
- workspaceName: "main",
41
- panes: ["pane.one"],
42
- viewerMode: "interactive" as const,
43
- };
44
- const issued = () => ({
45
- status: "issued",
46
- descriptor: {
47
- protocolVersion: PANE_STREAM_PROTOCOL_VERSION,
48
- webSocketUrl: `ws://127.0.0.1:4000${tmuxServerPaneStreamPath(scope)}`,
49
- subprotocol: PANE_STREAM_WEBSOCKET_SUBPROTOCOL,
50
- redemptionTicket: `ps2_${"a".repeat(43)}`,
51
- daemonInstanceId: scope.generation,
52
- requestId,
53
- expiresAt: Date.now() + 10000,
54
- panes: ["pane.one"],
55
- effectiveViewerMode: "interactive",
56
- },
57
- });
58
-
59
- describe("explicit tmux server SDK client", () => {
60
- it("validates versioned server listing without fallback", async () => {
61
- const fetcher = mock(async (url: string | URL | Request, init?: RequestInit) => {
62
- expect(String(url)).toBe(`${base.baseUrl}/api/v1/tmux-servers`);
63
- expect(init?.headers).toMatchObject({ Authorization: "Bearer owner" });
64
- return Response.json({
65
- version: 1,
66
- servers: [{ ...scope, state: "online", label: "default" }],
67
- });
68
- });
69
- expect(
70
- (await listTmuxServers({ ...base, fetch: fetcher as typeof fetch })).servers,
71
- ).toHaveLength(1);
72
- await expect(
73
- listTmuxServers({
74
- ...base,
75
- fetch: mock(async () => Response.json({ version: 2, servers: [] })) as typeof fetch,
76
- }),
77
- ).rejects.toThrow();
78
- });
79
-
80
- it("binds copied inputs and cache keys to an immutable exact scope", async () => {
81
- const input = { ...scope };
82
- const fetcher = mock(async (url: string | URL | Request) => {
83
- expect(String(url)).toContain(`/${scope.serverId}/${scope.generation}/sessions`);
84
- return Response.json(sessions());
85
- });
86
- const client = createTmuxServerClient({ ...base, fetch: fetcher as typeof fetch }, input);
87
- input.serverId = other.serverId;
88
- expect((await client.sessions()).server).toEqual(scope);
89
- expect(Object.isFrozen(client.scope)).toBe(true);
90
- expect(client.cacheKey("pane", "%0")).not.toBe(
91
- createTmuxServerClient(base, other).cacheKey("pane", "%0"),
92
- );
93
- });
94
-
95
- it("rejects scope mismatches and stale HTTP refusal without retries", async () => {
96
- const fetcher = mock(async () => Response.json(sessions(other)));
97
- await expect(
98
- createTmuxServerClient({ ...base, fetch: fetcher as typeof fetch }, scope).sessions(),
99
- ).rejects.toMatchObject({ code: "scope-mismatch" });
100
- const refused = mock(async () =>
101
- Response.json({ error: { code: "stale-generation" } }, { status: 409 }),
102
- );
103
- await expect(
104
- createTmuxServerClient({ ...base, fetch: refused as typeof fetch }, scope).sessions(),
105
- ).rejects.toMatchObject({ status: 409 });
106
- expect(refused).toHaveBeenCalledTimes(1);
107
- });
108
-
109
- it("drops an old selection's late response after disposal", async () => {
110
- let resolve!: (response: Response) => void;
111
- const pending = new Promise<Response>((done) => {
112
- resolve = done;
113
- });
114
- const fetcher = mock(async () => pending);
115
- const client = createTmuxServerClient({ ...base, fetch: fetcher as typeof fetch }, scope);
116
- const result = client.sessions();
117
- client.dispose();
118
- resolve(Response.json(sessions()));
119
- await expect(result).rejects.toMatchObject({ code: "disposed" });
120
- await expect(client.sessions()).rejects.toMatchObject({ code: "disposed" });
121
- expect(fetcher).toHaveBeenCalledTimes(1);
122
- });
123
-
124
- it("uses explicit owner authority for passive fleet close without a controller principal", async () => {
125
- const fetcher = mock(async (_url: RequestInfo | URL, init?: RequestInit) => {
126
- expect(new Headers(init?.headers).has("X-Tmux-Ide-Host-Client-Id")).toBe(false);
127
- expect(new Headers(init?.headers).get("Authorization")).toBe("Bearer owner");
128
- return Response.json({
129
- operationId: requestId,
130
- daemonInstanceId: scope.generation,
131
- workspaceName: "same",
132
- verb: "workspace.session.kill",
133
- outcome: "applied",
134
- });
135
- });
136
- await createTmuxServerClient({ ...base, fetch: fetcher as typeof fetch }, scope).mutate(
137
- requestId,
138
- {
139
- verb: "workspace.session.kill",
140
- workspaceName: "same",
141
- fleetTarget: {
142
- daemonInstanceId: scope.generation,
143
- liveSessionId: `live-session.${"a".repeat(20)}`,
144
- sessionName: "same",
145
- },
146
- },
147
- );
148
- expect(fetcher).toHaveBeenCalledTimes(1);
149
- });
150
-
151
- it("sends owner-scoped mutations and verifies generation and operation echoes", async () => {
152
- const fetcher = mock(async (_url: string | URL | Request, init?: RequestInit) => {
153
- const body = JSON.parse(String(init?.body));
154
- expect(body.expectedDaemonInstanceId).toBe(scope.generation);
155
- return Response.json({
156
- operationId: body.operationId,
157
- daemonInstanceId: scope.generation,
158
- outcome: "applied",
159
- workspaceName: "main",
160
- verb: "workspace.pane.select",
161
- semanticPaneId: "pane.one",
162
- });
163
- });
164
- const client = createTmuxServerClient({ ...base, fetch: fetcher as typeof fetch }, scope);
165
- const result = await client.mutate(requestId, {
166
- verb: "workspace.pane.select",
167
- workspaceName: "main",
168
- semanticPaneId: "pane.one",
169
- });
170
- expect(result.daemonInstanceId).toBe(scope.generation);
171
- });
172
-
173
- it("sends exact issue headers and validates scoped redemption", async () => {
174
- const fetcher = mock(async (url: string | URL | Request, init?: RequestInit) => {
175
- expect(String(url)).toEndWith(`/${scope.serverId}/${scope.generation}/pane-streams/issue`);
176
- expect(init?.headers).toMatchObject({
177
- Authorization: "Bearer owner",
178
- Origin: base.origin,
179
- "X-Tmux-Ide-Request-Id": requestId,
180
- "X-Tmux-Ide-Expected-Daemon-Instance-Id": scope.generation,
181
- "X-Tmux-Ide-Host-Client-Id": "client",
182
- });
183
- return Response.json(issued());
184
- });
185
- expect(
186
- (
187
- await createTmuxServerClient(
188
- { ...base, fetch: fetcher as typeof fetch },
189
- scope,
190
- ).issuePaneStream(requestId, stream)
191
- ).status,
192
- ).toBe("issued");
193
- const wrong = issued();
194
- wrong.descriptor.webSocketUrl = `ws://127.0.0.1:4000${tmuxServerPaneStreamPath(other)}`;
195
- await expect(
196
- createTmuxServerClient(
197
- { ...base, fetch: mock(async () => Response.json(wrong)) as typeof fetch },
198
- scope,
199
- ).issuePaneStream(requestId, stream),
200
- ).rejects.toMatchObject({ code: "scope-mismatch" });
201
- });
202
-
203
- it("rejects inventories from the wrong workspace", async () => {
204
- const fetcher = mock(async () =>
205
- Response.json({
206
- version: 1,
207
- server: scope,
208
- resource: {
209
- workspaceName: "other",
210
- workspaceId: "workspace.0123456789abcdefabcd",
211
- sessionId: "session.0123456789abcdefabcd",
212
- resourceRevision: 0,
213
- semanticPaneIds: ["pane.one"],
214
- },
215
- }),
216
- );
217
- await expect(
218
- createTmuxServerClient({ ...base, fetch: fetcher as typeof fetch }, scope).inventory("main"),
219
- ).rejects.toMatchObject({ code: "scope-mismatch" });
220
- });
221
- });
222
-
223
- describe("server registration and adopted default", () => {
224
- it("passes remote selector intent unchanged and removes by opaque registration", async () => {
225
- const calls: Array<{ url: string; init?: RequestInit }> = [];
226
- const fetcher = mock(async (url: string | URL | Request, init?: RequestInit) => {
227
- calls.push({ url: String(url), init });
228
- return Response.json(
229
- init?.method === "DELETE"
230
- ? { removed: true }
231
- : { ...scope, label: "remote", state: "online" },
232
- );
233
- });
234
- const options = { ...base, fetch: fetcher as typeof fetch };
235
- await registerTmuxServer(options, {
236
- label: "remote",
237
- selector: { kind: "path", path: "/remote/socket" },
238
- });
239
- expect(JSON.parse(String(calls[0]?.init?.body)).selector).toEqual({
240
- kind: "path",
241
- path: "/remote/socket",
242
- });
243
- await removeTmuxServer(options, scope.serverId);
244
- expect(calls[1]?.init?.method).toBe("DELETE");
245
- expect(calls[1]?.url).toEndWith(`/${scope.serverId}`);
246
- await expect(removeTmuxServer(options, "../default")).rejects.toThrow();
247
- expect(fetcher).toHaveBeenCalledTimes(2);
248
- });
249
-
250
- it("accepts default redemption only with an exact generation and request echo", async () => {
251
- const response = issued();
252
- response.descriptor.webSocketUrl = `ws://127.0.0.1:4000${PANE_STREAM_REDEEM_PATH}`;
253
- const fetcher = mock(async () => Response.json(response));
254
- const client = createTmuxServerClient({ ...base, fetch: fetcher as typeof fetch }, scope);
255
- expect((await client.issuePaneStream(requestId, stream)).status).toBe("issued");
256
- response.descriptor.daemonInstanceId = requestId;
257
- await expect(client.issuePaneStream(requestId, stream)).rejects.toMatchObject({
258
- code: "scope-mismatch",
259
- });
260
- });
261
-
262
- it("preserves authoritative workspace aliases and unavailable collisions", async () => {
263
- const response = sessions();
264
- const client = createTmuxServerClient(
265
- { ...base, fetch: mock(async () => Response.json(response)) as typeof fetch },
266
- scope,
267
- );
268
- expect((await client.sessions()).sessions[0]?.workspaceName).toBe("alias");
269
- const unavailable = {
270
- ...response,
271
- sessions: response.sessions.map((session) => ({ ...session, workspaceName: null })),
272
- };
273
- const otherClient = createTmuxServerClient(
274
- { ...base, fetch: mock(async () => Response.json(unavailable)) as typeof fetch },
275
- scope,
276
- );
277
- expect((await otherClient.sessions()).sessions[0]?.workspaceName).toBeNull();
278
- });
279
- });
@@ -1,230 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { subscribeTmuxServerInteractions } from "./tmux-server-interaction-events.ts";
3
- const server = {
4
- serverId: `tmux-server.${"a".repeat(32)}`,
5
- generation: "11111111-1111-4111-8111-111111111111",
6
- };
7
- const observationStatus = {
8
- schemaVersion: 1,
9
- environmentId: "00000000-0000-4000-8000-000000000001",
10
- serverScope: server,
11
- method: "unavailable",
12
- capabilityVersion: null,
13
- commands: [],
14
- effects: [],
15
- coverage: "unavailable",
16
- cursor: null,
17
- lastGap: null,
18
- droppedCount: "0",
19
- };
20
- const ready = { version: 1, server, type: "ready", after: 0, observationStatus };
21
- const receipt = {
22
- type: "interaction.receipt",
23
- sequence: 1,
24
- operationId: "afbc7eaf-604a-4117-8296-aef44b889af1",
25
- origin: "external",
26
- workspaceName: "same",
27
- sourceSemanticPaneId: null,
28
- target: { kind: "pane", semanticPaneId: "pane.same" },
29
- operationKind: "workspace.pane.send",
30
- phase: "observed",
31
- summary: { operationKind: "workspace.pane.send", observedOnly: true },
32
- proof: { operationKind: "workspace.pane.send", observed: true, semanticPaneId: "pane.same" },
33
- at: "2026-09-28T00:00:00.000Z",
34
- resourceRevision: null,
35
- evidence: {
36
- schemaVersion: 1,
37
- interactionId: "afbc7eaf-604a-4117-8296-aef44b889af1",
38
- revision: 0,
39
- endpoints: {
40
- destination: {
41
- kind: "pane",
42
- environmentId: observationStatus.environmentId,
43
- serverScope: server,
44
- paneLifetimeId: "00000000-0000-4000-8000-000000000003",
45
- workspaceName: "same",
46
- semanticPaneId: "pane.same",
47
- },
48
- source: null,
49
- },
50
- actor: { kind: "unknown", reason: "stock-hook" },
51
- observation: { kind: "stock-hook", command: "send-keys" },
52
- effect: { kind: "unknown" },
53
- occurredAt: null,
54
- timeBasis: "unknown",
55
- receivedAt: "2026-09-28T00:00:00.000Z",
56
- },
57
- };
58
- const batch = {
59
- version: 1,
60
- server,
61
- type: "batch",
62
- after: 0,
63
- cursor: 1,
64
- gap: null,
65
- receipts: [receipt],
66
- };
67
- function fake(frames: unknown[]) {
68
- return (async () =>
69
- new Response(
70
- new ReadableStream({
71
- start(c) {
72
- for (const f of frames)
73
- c.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(f)}\n\n`));
74
- c.close();
75
- },
76
- }),
77
- )) as typeof fetch;
78
- }
79
- function subscribe(frames: unknown[], onBatch = () => {}) {
80
- return subscribeTmuxServerInteractions({
81
- baseUrl: "http://localhost",
82
- ownerToken: "token",
83
- server,
84
- fetch: fake(frames),
85
- onBatch,
86
- });
87
- }
88
- describe("interaction subscriber fences", () => {
89
- it("rejects status scope mismatch and missing initial coverage", async () => {
90
- for (const frames of [
91
- [{ ...ready, observationStatus: undefined }],
92
- [
93
- ready,
94
- {
95
- version: 1,
96
- server,
97
- type: "status",
98
- observationStatus: {
99
- ...observationStatus,
100
- serverScope: { ...server, generation: "22222222-2222-4222-8222-222222222222" },
101
- },
102
- },
103
- ],
104
- ]) {
105
- const stream = subscribe(frames);
106
- stream.ready.catch(() => {});
107
- await expect(stream.done).rejects.toThrow();
108
- }
109
- });
110
- it("rejects evidence destination scope different from its enclosing stream", async () => {
111
- const foreign = structuredClone(batch);
112
- foreign.receipts[0]!.evidence.endpoints.destination.serverScope.generation =
113
- "22222222-2222-4222-8222-222222222222";
114
- const stream = subscribe([ready, foreign]);
115
- await stream.ready;
116
- await expect(stream.done).rejects.toThrow();
117
- });
118
- it("delivers status-only frames without advancing receipt cursor", async () => {
119
- const seen: string[] = [];
120
- const stream = subscribeTmuxServerInteractions({
121
- baseUrl: "http://localhost",
122
- ownerToken: "token",
123
- server,
124
- fetch: fake([
125
- ready,
126
- {
127
- version: 1,
128
- server,
129
- type: "status",
130
- observationStatus: {
131
- ...observationStatus,
132
- method: "stock-hooks",
133
- capabilityVersion: 1,
134
- coverage: "partial",
135
- commands: ["send-keys", "capture-pane"],
136
- },
137
- },
138
- ]),
139
- onBatch: () => {
140
- throw Error("Unexpected batch");
141
- },
142
- onStatus: (status) => seen.push(status.method),
143
- });
144
- await stream.ready;
145
- await stream.done.catch(() => {});
146
- expect(seen).toEqual(["unavailable", "stock-hooks"]);
147
- expect(stream.getCursor().cursor).toBe(0);
148
- });
149
- it("closes while a consumer is stalled without acknowledging its batch", async () => {
150
- let started!: () => void;
151
- const called = new Promise<void>((resolve) => {
152
- started = resolve;
153
- });
154
- let consumerSignal: AbortSignal | undefined;
155
- const stream = subscribeTmuxServerInteractions({
156
- baseUrl: "http://localhost",
157
- ownerToken: "token",
158
- server,
159
- fetch: fake([ready, batch]),
160
- onBatch: (_batch, signal) => {
161
- consumerSignal = signal;
162
- started();
163
- return new Promise<void>(() => {});
164
- },
165
- });
166
- await called;
167
- stream.close();
168
- await stream.done;
169
- expect(consumerSignal?.aborted).toBe(true);
170
- expect(stream.getCursor().cursor).toBe(0);
171
- });
172
- it("rejects scoped resume reuse before making a request", () => {
173
- expect(() =>
174
- subscribeTmuxServerInteractions({
175
- baseUrl: "http://localhost",
176
- ownerToken: "token",
177
- server,
178
- resume: {
179
- server: { ...server, generation: "22222222-2222-4222-8222-222222222222" },
180
- cursor: 0,
181
- },
182
- onBatch: () => {},
183
- }),
184
- ).toThrow("another owner");
185
- });
186
- it.each(["owner", "generation", "repeated ready", "version", "before ready", "regression"])(
187
- "rejects %s frames",
188
- async (kind) => {
189
- let frames: unknown[] = [ready];
190
- if (kind === "owner")
191
- frames.push({ ...batch, server: { ...server, serverId: `tmux-server.${"b".repeat(32)}` } });
192
- if (kind === "generation")
193
- frames.push({
194
- ...batch,
195
- server: { ...server, generation: "22222222-2222-4222-8222-222222222222" },
196
- });
197
- if (kind === "repeated ready") frames.push(ready);
198
- if (kind === "version") frames.push({ ...batch, version: 2 });
199
- if (kind === "before ready") frames = [batch];
200
- if (kind === "regression") frames.push({ ...batch, cursor: 0 });
201
- let calls = 0;
202
- const stream = subscribe(frames, () => {
203
- calls++;
204
- });
205
- await expect(stream.done).rejects.toThrow();
206
- expect(calls).toBe(0);
207
- },
208
- );
209
- it("rejects repeated receipts without delivering them twice", async () => {
210
- let calls = 0;
211
- const stream = subscribe([ready, batch, batch], () => {
212
- calls++;
213
- });
214
- await expect(stream.done).rejects.toThrow("Repeated");
215
- expect(calls).toBe(1);
216
- expect(stream.getCursor().cursor).toBe(1);
217
- });
218
- it("retains only acknowledged callback progress for reconnect", async () => {
219
- const stream = subscribe([ready, batch], () => {
220
- throw Error("consumer failed");
221
- });
222
- await expect(stream.done).rejects.toThrow("consumer failed");
223
- expect(stream.getCursor().cursor).toBe(0);
224
- });
225
- it("rejects a fabricated gap and noncontiguous receipts", async () => {
226
- const stream = subscribe([ready, { ...batch, gap: { from: 2, through: 2 } }]);
227
- await expect(stream.done).rejects.toThrow();
228
- expect(stream.getCursor().cursor).toBe(0);
229
- });
230
- });
@@ -1,109 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
-
3
- import {
4
- initialWorkspaceCatalogV2State,
5
- replaceWorkspaceCatalogV2,
6
- } from "./workspace-catalog-v2.ts";
7
-
8
- const daemon = (instanceId: string) => ({
9
- protocolVersion: 1,
10
- productVersion: "test",
11
- instanceId,
12
- startedAt: "2026-08-12T00:00:00.000Z",
13
- });
14
- const retiredFleetSessionId = "session.aaaaaaaaaaaaaaaaaaaa";
15
- const sameFleetSessionId = "session.bbbbbbbbbbbbbbbbbbbb";
16
-
17
- describe("workspace catalog V2 state", () => {
18
- it("keeps stopped durable intent separate from observed live tmux sessions", () => {
19
- const state = replaceWorkspaceCatalogV2(initialWorkspaceCatalogV2State(), {
20
- version: 2,
21
- daemon: daemon("11111111-1111-4111-8111-111111111111"),
22
- intents: [
23
- {
24
- workspaceName: "saved-workspace",
25
- sessionName: "saved-session",
26
- source: "workspace",
27
- availability: "live",
28
- },
29
- ],
30
- liveSessions: [],
31
- });
32
-
33
- expect(state.intents).toEqual([
34
- expect.objectContaining({ workspaceName: "saved-workspace", availability: "stopped" }),
35
- ]);
36
- expect(state.liveSessions).toEqual([]);
37
- });
38
-
39
- it("replaces rather than merges state across daemon generations", () => {
40
- const first = replaceWorkspaceCatalogV2(initialWorkspaceCatalogV2State(), {
41
- version: 2,
42
- daemon: daemon("11111111-1111-4111-8111-111111111111"),
43
- intents: [
44
- {
45
- workspaceName: "retired",
46
- sessionName: "retired",
47
- source: "project",
48
- availability: "live",
49
- },
50
- ],
51
- liveSessions: [
52
- { sessionName: "retired", fleetSessionId: retiredFleetSessionId, paneCount: 2 },
53
- ],
54
- });
55
- const replacement = replaceWorkspaceCatalogV2(first, {
56
- version: 2,
57
- daemon: daemon("22222222-2222-4222-8222-222222222222"),
58
- intents: [],
59
- liveSessions: [],
60
- });
61
-
62
- expect(replacement).toEqual({
63
- daemonInstanceId: "22222222-2222-4222-8222-222222222222",
64
- intents: [],
65
- liveSessions: [],
66
- });
67
- });
68
-
69
- it("rejects duplicate observed session identities", () => {
70
- expect(() =>
71
- replaceWorkspaceCatalogV2(initialWorkspaceCatalogV2State(), {
72
- version: 2,
73
- daemon: daemon("11111111-1111-4111-8111-111111111111"),
74
- intents: [],
75
- liveSessions: [
76
- { sessionName: "same", fleetSessionId: sameFleetSessionId, paneCount: 1 },
77
- { sessionName: "same", fleetSessionId: sameFleetSessionId, paneCount: 2 },
78
- ],
79
- }),
80
- ).toThrow("duplicate live session");
81
- });
82
-
83
- it("retains the opaque promotion identity on the exact live route", () => {
84
- const state = replaceWorkspaceCatalogV2(initialWorkspaceCatalogV2State(), {
85
- version: 2,
86
- daemon: daemon("11111111-1111-4111-8111-111111111111"),
87
- intents: [],
88
- liveSessions: [{ sessionName: "alpha", fleetSessionId: retiredFleetSessionId, paneCount: 1 }],
89
- });
90
-
91
- expect(state.liveSessions).toEqual([
92
- { sessionName: "alpha", fleetSessionId: retiredFleetSessionId, paneCount: 1 },
93
- ]);
94
- });
95
-
96
- it("rejects one opaque promotion identity assigned to two routing names", () => {
97
- expect(() =>
98
- replaceWorkspaceCatalogV2(initialWorkspaceCatalogV2State(), {
99
- version: 2,
100
- daemon: daemon("11111111-1111-4111-8111-111111111111"),
101
- intents: [],
102
- liveSessions: [
103
- { sessionName: "alpha", fleetSessionId: sameFleetSessionId, paneCount: 1 },
104
- { sessionName: "beta", fleetSessionId: sameFleetSessionId, paneCount: 1 },
105
- ],
106
- }),
107
- ).toThrow("duplicate fleet session");
108
- });
109
- });
@@ -1,59 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { readFileSync, readdirSync } from "node:fs";
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
7
- const workspaceRoot = join(packageRoot, "..", "..");
8
- const productionSources = readdirSync(join(packageRoot, "src"))
9
- .filter((name) => name.endsWith(".ts") && !name.endsWith(".test.ts"))
10
- .map((name) => [name, readFileSync(join(packageRoot, "src", name), "utf8")] as const);
11
-
12
- describe("daemon-client dependency direction", () => {
13
- it("keeps the WorkspaceClient kernel renderer and host neutral", () => {
14
- const forbidden = [
15
- "@opentui/",
16
- "solid-js",
17
- "electron",
18
- "node:fs",
19
- "node:child_process",
20
- "globalThis.WebSocket",
21
- "new WebSocket",
22
- "fetch(",
23
- "packages/daemon/src",
24
- "@tmux-ide/tmux-bridge",
25
- "process.env",
26
- ];
27
- const kernel = productionSources.filter(
28
- ([name]) =>
29
- name.startsWith("workspace-client") ||
30
- name === "terminal-fast-lane.ts" ||
31
- name === "first-latest-coordinator.ts",
32
- );
33
- for (const [name, source] of kernel) {
34
- for (const specifier of forbidden) {
35
- expect(source, `${name} imports or reads ${specifier}`).not.toContain(specifier);
36
- }
37
- }
38
- });
39
-
40
- it("keeps the terminal lane as a composition over the canonical reducer, not a wire protocol", () => {
41
- const lane = readFileSync(join(packageRoot, "src", "terminal-fast-lane.ts"), "utf8");
42
- expect(lane).toContain("applyTerminalReplicaUpdate");
43
- expect(lane).toContain("CanonicalTerminalSubscriptionPort");
44
- expect(lane).not.toContain("TerminalDeliveryEnvelope");
45
- expect(lane).not.toContain("PaneStreamServerFrame");
46
- expect(lane).not.toContain("SchemaZ.parse");
47
- expect(lane).not.toContain("as CanonicalTerminalReplicaUpdate");
48
- });
49
-
50
- it("permits daemon-client -> core while forbidding the reverse edge", () => {
51
- const coreSources = readdirSync(join(workspaceRoot, "packages", "core", "src"))
52
- .filter((name) => name.endsWith(".ts") && !name.endsWith(".test.ts"))
53
- .map((name) => readFileSync(join(workspaceRoot, "packages", "core", "src", name), "utf8"));
54
- expect(productionSources.some(([, source]) => source.includes('from "@tmux-ide/core"'))).toBe(
55
- true,
56
- );
57
- for (const source of coreSources) expect(source).not.toContain("@tmux-ide/daemon-client");
58
- });
59
- });