@the-open-engine/zeroshot 6.38.0 → 6.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/cli/event-copy.js +8 -7
  2. package/cli/event-copy.ts +12 -0
  3. package/cli/index.js +7 -31
  4. package/cli/json-export.js +61 -64
  5. package/cli/json-export.ts +112 -0
  6. package/cli/message-formatter-utils.js +68 -56
  7. package/cli/message-formatter-utils.ts +121 -0
  8. package/cli/message-formatters-normal.js +124 -259
  9. package/cli/message-formatters-normal.ts +250 -0
  10. package/cli/message-formatters-watch.js +81 -84
  11. package/cli/message-formatters-watch.ts +189 -0
  12. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
  13. package/lib/agent-cli-provider/adapters/codex.js +3 -1
  14. package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
  15. package/lib/cluster/generated/protocol-schema.cjs +1 -1
  16. package/lib/cluster/generated/protocol-schema.mjs +1 -1
  17. package/lib/cluster/generated/protocol.d.cts +8 -0
  18. package/lib/cluster/generated/protocol.d.mts +8 -0
  19. package/lib/cluster/generated/protocol.d.ts +8 -0
  20. package/lib/clusters-registry.d.ts +23 -0
  21. package/lib/clusters-registry.js +17 -29
  22. package/lib/completion.d.ts +12 -0
  23. package/lib/completion.js +84 -94
  24. package/lib/compose-utils.d.ts +32 -0
  25. package/lib/compose-utils.js +57 -72
  26. package/lib/detached-startup.d.ts +83 -0
  27. package/lib/detached-startup.js +231 -302
  28. package/lib/docker-config.d.ts +43 -0
  29. package/lib/docker-config.js +150 -325
  30. package/lib/docker-env-auth.d.ts +17 -0
  31. package/lib/docker-env-auth.js +75 -0
  32. package/lib/git-remote-utils.d.ts +48 -0
  33. package/lib/git-remote-utils.js +147 -213
  34. package/lib/id-detector.d.ts +15 -0
  35. package/lib/id-detector.js +37 -41
  36. package/lib/path-check.d.ts +30 -0
  37. package/lib/path-check.js +42 -45
  38. package/lib/process-liveness.d.ts +16 -0
  39. package/lib/process-liveness.js +19 -12
  40. package/lib/provider-credential-path.d.ts +16 -0
  41. package/lib/provider-credential-path.js +31 -29
  42. package/lib/provider-defaults.d.ts +30 -0
  43. package/lib/provider-defaults.js +47 -42
  44. package/lib/provider-detection.d.ts +11 -0
  45. package/lib/provider-detection.js +91 -54
  46. package/lib/provider-names.d.ts +38 -0
  47. package/lib/provider-names.js +56 -64
  48. package/lib/repo-settings.d.ts +28 -0
  49. package/lib/repo-settings.js +83 -59
  50. package/lib/run-mode.d.ts +21 -0
  51. package/lib/run-mode.js +21 -20
  52. package/lib/run-plan.d.ts +28 -0
  53. package/lib/run-plan.js +17 -16
  54. package/lib/settings/claude-auth.d.ts +35 -0
  55. package/lib/settings/claude-auth.js +30 -47
  56. package/lib/settings-defaults.d.ts +20 -0
  57. package/lib/settings-defaults.js +149 -0
  58. package/lib/settings-error.d.ts +7 -0
  59. package/lib/settings-error.js +8 -0
  60. package/lib/settings-issue-providers.d.ts +10 -0
  61. package/lib/settings-issue-providers.js +12 -0
  62. package/lib/settings-models.d.ts +22 -0
  63. package/lib/settings-models.js +93 -0
  64. package/lib/settings-storage.d.ts +23 -0
  65. package/lib/settings-storage.js +205 -0
  66. package/lib/settings-validation.d.ts +7 -0
  67. package/lib/settings-validation.js +182 -0
  68. package/lib/settings.d.ts +29 -0
  69. package/lib/settings.js +41 -736
  70. package/lib/setup-apply-types.d.ts +66 -0
  71. package/lib/setup-apply-types.js +2 -0
  72. package/lib/setup-apply.d.ts +39 -0
  73. package/lib/setup-apply.js +267 -264
  74. package/lib/setup-journal.d.ts +41 -0
  75. package/lib/setup-journal.js +119 -76
  76. package/lib/setup-plan-types.d.ts +102 -0
  77. package/lib/setup-plan-types.js +2 -0
  78. package/lib/setup-plan.d.ts +30 -0
  79. package/lib/setup-plan.js +300 -324
  80. package/lib/setup-undo.d.ts +44 -0
  81. package/lib/setup-undo.js +61 -74
  82. package/lib/start-cluster-config.d.ts +26 -0
  83. package/lib/start-cluster-config.js +56 -0
  84. package/lib/start-cluster-environment.d.ts +40 -0
  85. package/lib/start-cluster-environment.js +134 -0
  86. package/lib/start-cluster-run-options.d.ts +53 -0
  87. package/lib/start-cluster-run-options.js +147 -0
  88. package/lib/start-cluster.d.ts +64 -0
  89. package/lib/start-cluster.js +98 -454
  90. package/lib/stream-json-parser.d.ts +17 -0
  91. package/lib/stream-json-parser.js +51 -52
  92. package/npm-shrinkwrap.json +2 -2
  93. package/package.json +12 -6
  94. package/scripts/postinstall.js +27 -0
  95. package/scripts/rust-distribution.js +23 -14
  96. package/src/agent/agent-command-proofs-context.js +35 -45
  97. package/src/agent/agent-command-proofs-context.ts +87 -0
  98. package/src/agent/agent-config-settings.js +19 -0
  99. package/src/agent/agent-config-settings.ts +41 -0
  100. package/src/agent/agent-config.js +122 -231
  101. package/src/agent/agent-config.ts +285 -0
  102. package/src/agent/agent-context-builder.js +38 -273
  103. package/src/agent/agent-context-builder.ts +51 -0
  104. package/src/agent/agent-context-environment-sections.js +105 -0
  105. package/src/agent/agent-context-environment-sections.ts +134 -0
  106. package/src/agent/agent-context-packs.js +165 -0
  107. package/src/agent/agent-context-packs.ts +296 -0
  108. package/src/agent/agent-context-prompt-sections.js +100 -0
  109. package/src/agent/agent-context-prompt-sections.ts +148 -0
  110. package/src/agent/agent-context-sections.js +25 -334
  111. package/src/agent/agent-context-sections.ts +36 -0
  112. package/src/agent/agent-context-sources.js +96 -149
  113. package/src/agent/agent-context-sources.ts +253 -0
  114. package/src/agent/agent-context-types.js +2 -0
  115. package/src/agent/agent-context-types.ts +132 -0
  116. package/src/agent/agent-context-validation-sections.js +73 -0
  117. package/src/agent/agent-context-validation-sections.ts +129 -0
  118. package/src/agent/agent-input-injector.js +99 -118
  119. package/src/agent/agent-input-injector.ts +195 -0
  120. package/src/agent/agent-liveness-poll.js +74 -78
  121. package/src/agent/agent-liveness-poll.ts +163 -0
  122. package/src/agent/agent-quality-gate-schema.js +77 -86
  123. package/src/agent/agent-quality-gate-schema.ts +135 -0
  124. package/src/agent/agent-quality-gates-context.js +44 -50
  125. package/src/agent/agent-quality-gates-context.ts +91 -0
  126. package/src/agent/agent-stuck-detector.js +171 -239
  127. package/src/agent/agent-stuck-detector.ts +288 -0
  128. package/src/agent/agent-trigger-evaluator.js +29 -46
  129. package/src/agent/agent-trigger-evaluator.ts +98 -0
  130. package/src/agent/context-metrics.js +110 -141
  131. package/src/agent/context-metrics.ts +261 -0
  132. package/src/agent/context-pack-builder.js +72 -358
  133. package/src/agent/context-pack-builder.ts +105 -0
  134. package/src/agent/context-pack-core.js +70 -0
  135. package/src/agent/context-pack-core.ts +96 -0
  136. package/src/agent/context-pack-selection.js +119 -0
  137. package/src/agent/context-pack-selection.ts +187 -0
  138. package/src/agent/context-pack-truncation.js +139 -0
  139. package/src/agent/context-pack-truncation.ts +208 -0
  140. package/src/agent/context-pack-types.js +2 -0
  141. package/src/agent/context-pack-types.ts +98 -0
  142. package/src/agent/context-replay-policy.js +30 -39
  143. package/src/agent/context-replay-policy.ts +71 -0
  144. package/src/agent/critical-agent-policy.js +6 -10
  145. package/src/agent/critical-agent-policy.ts +20 -0
  146. package/src/agent/guidance-queue.js +74 -87
  147. package/src/agent/guidance-queue.ts +166 -0
  148. package/src/agent/output-extraction-error-detail.js +45 -0
  149. package/src/agent/output-extraction-error-detail.ts +49 -0
  150. package/src/agent/output-extraction-failures.js +125 -0
  151. package/src/agent/output-extraction-failures.ts +149 -0
  152. package/src/agent/output-extraction-json.js +234 -0
  153. package/src/agent/output-extraction-json.ts +235 -0
  154. package/src/agent/output-extraction-pi.js +124 -0
  155. package/src/agent/output-extraction-pi.ts +137 -0
  156. package/src/agent/output-extraction-types.js +2 -0
  157. package/src/agent/output-extraction-types.ts +49 -0
  158. package/src/agent/output-extraction.js +17 -617
  159. package/src/agent/output-extraction.ts +30 -0
  160. package/src/agent/output-reformatter-errors.js +83 -0
  161. package/src/agent/output-reformatter-errors.ts +106 -0
  162. package/src/agent/output-reformatter-types.js +2 -0
  163. package/src/agent/output-reformatter-types.ts +86 -0
  164. package/src/agent/output-reformatter.js +140 -185
  165. package/src/agent/output-reformatter.ts +238 -0
  166. package/src/agent/pi-terminal-lifecycle-types.js +2 -0
  167. package/src/agent/pi-terminal-lifecycle-types.ts +41 -0
  168. package/src/agent/pi-terminal-lifecycle.js +186 -206
  169. package/src/agent/pi-terminal-lifecycle.ts +297 -0
  170. package/src/agent/provider-control-plane.js +22 -21
  171. package/src/agent/provider-control-plane.ts +52 -0
  172. package/src/agent/provider-terminal-failure-types.js +2 -0
  173. package/src/agent/provider-terminal-failure-types.ts +60 -0
  174. package/src/agent/provider-terminal-failure.js +194 -191
  175. package/src/agent/provider-terminal-failure.ts +262 -0
  176. package/src/agent/rate-limit-backoff.js +31 -74
  177. package/src/agent/rate-limit-backoff.ts +71 -0
  178. package/src/agent/schema-utils.js +85 -125
  179. package/src/agent/schema-utils.ts +148 -0
  180. package/src/agent/structured-output-error.js +34 -33
  181. package/src/agent/structured-output-error.ts +105 -0
  182. package/src/agent/validation-platform.js +27 -27
  183. package/src/agent/validation-platform.ts +49 -0
  184. package/src/agent-cli-provider/adapters/codex.ts +3 -1
  185. package/src/attach/attach-client.js +232 -405
  186. package/src/attach/attach-client.ts +300 -0
  187. package/src/attach/attach-server-cleanup.js +43 -0
  188. package/src/attach/attach-server-cleanup.ts +39 -0
  189. package/src/attach/attach-server-clients.js +117 -0
  190. package/src/attach/attach-server-clients.ts +122 -0
  191. package/src/attach/attach-server-events.js +38 -0
  192. package/src/attach/attach-server-events.ts +36 -0
  193. package/src/attach/attach-server-pty.js +45 -0
  194. package/src/attach/attach-server-pty.ts +64 -0
  195. package/src/attach/attach-server-runtime.js +73 -0
  196. package/src/attach/attach-server-runtime.ts +62 -0
  197. package/src/attach/attach-server-socket.js +28 -0
  198. package/src/attach/attach-server-socket.ts +23 -0
  199. package/src/attach/attach-server-types.js +2 -0
  200. package/src/attach/attach-server-types.ts +82 -0
  201. package/src/attach/attach-server.js +106 -523
  202. package/src/attach/attach-server.ts +158 -0
  203. package/src/attach/index.js +18 -35
  204. package/src/attach/index.ts +18 -0
  205. package/src/attach/protocol.js +95 -184
  206. package/src/attach/protocol.ts +181 -0
  207. package/src/attach/ring-buffer.js +76 -102
  208. package/src/attach/ring-buffer.ts +109 -0
  209. package/src/attach/send-input.js +64 -75
  210. package/src/attach/send-input.ts +94 -0
  211. package/src/attach/socket-discovery.js +164 -218
  212. package/src/attach/socket-discovery.ts +230 -0
  213. package/src/attach/socket-paths.js +42 -61
  214. package/src/attach/socket-paths.ts +81 -0
  215. package/src/claude-credentials.js +51 -54
  216. package/src/claude-credentials.ts +89 -0
  217. package/src/cluster/generated/protocol-schema.ts +1 -1
  218. package/src/cluster/generated/protocol.ts +2 -1
  219. package/src/command-proofs.js +110 -141
  220. package/src/command-proofs.ts +216 -0
  221. package/src/config-router.js +81 -73
  222. package/src/config-router.ts +131 -0
  223. package/src/copy-worker.js +53 -29
  224. package/src/copy-worker.ts +75 -0
  225. package/src/darwin-keychain-boundary.js +80 -111
  226. package/src/darwin-keychain-boundary.ts +148 -0
  227. package/src/guidance-topics.js +4 -5
  228. package/src/guidance-topics.ts +10 -0
  229. package/src/input-helpers.js +33 -54
  230. package/src/input-helpers.ts +58 -0
  231. package/src/ledger-sequence.js +45 -48
  232. package/src/ledger-sequence.ts +63 -0
  233. package/src/legacy-lib/clusters-registry.ts +40 -0
  234. package/src/legacy-lib/completion.ts +148 -0
  235. package/src/legacy-lib/compose-utils.ts +102 -0
  236. package/src/legacy-lib/detached-startup.ts +449 -0
  237. package/src/legacy-lib/docker-config.ts +282 -0
  238. package/src/legacy-lib/docker-env-auth.ts +116 -0
  239. package/src/legacy-lib/git-remote-utils.ts +244 -0
  240. package/src/legacy-lib/id-detector.ts +57 -0
  241. package/src/legacy-lib/js-yaml.d.ts +3 -0
  242. package/src/legacy-lib/omelette.d.ts +15 -0
  243. package/src/legacy-lib/path-check.ts +89 -0
  244. package/src/legacy-lib/process-liveness.ts +36 -0
  245. package/src/legacy-lib/provider-credential-path.ts +57 -0
  246. package/src/legacy-lib/provider-defaults.ts +114 -0
  247. package/src/legacy-lib/provider-detection.ts +67 -0
  248. package/src/legacy-lib/provider-names.ts +126 -0
  249. package/src/legacy-lib/repo-settings.ts +88 -0
  250. package/src/legacy-lib/run-mode.ts +58 -0
  251. package/src/legacy-lib/run-plan.ts +49 -0
  252. package/src/legacy-lib/settings/claude-auth.ts +80 -0
  253. package/src/legacy-lib/settings-defaults.ts +212 -0
  254. package/src/legacy-lib/settings-error.ts +8 -0
  255. package/src/legacy-lib/settings-issue-providers.ts +18 -0
  256. package/src/legacy-lib/settings-models.ts +124 -0
  257. package/src/legacy-lib/settings-storage.ts +277 -0
  258. package/src/legacy-lib/settings-validation.ts +220 -0
  259. package/src/legacy-lib/settings.ts +87 -0
  260. package/src/legacy-lib/setup-apply-types.ts +70 -0
  261. package/src/legacy-lib/setup-apply.ts +456 -0
  262. package/src/legacy-lib/setup-journal.ts +185 -0
  263. package/src/legacy-lib/setup-plan-types.ts +96 -0
  264. package/src/legacy-lib/setup-plan.ts +500 -0
  265. package/src/legacy-lib/setup-undo.ts +142 -0
  266. package/src/legacy-lib/start-cluster-config.ts +124 -0
  267. package/src/legacy-lib/start-cluster-environment.ts +165 -0
  268. package/src/legacy-lib/start-cluster-run-options.ts +279 -0
  269. package/src/legacy-lib/start-cluster.ts +237 -0
  270. package/src/legacy-lib/stream-json-parser.ts +88 -0
  271. package/src/message-buffer.js +55 -71
  272. package/src/message-buffer.ts +107 -0
  273. package/src/message-bus-bridge.js +92 -133
  274. package/src/message-bus-bridge.ts +149 -0
  275. package/src/name-generator-shared.js +220 -0
  276. package/src/name-generator-shared.ts +231 -0
  277. package/src/name-generator.js +8 -217
  278. package/src/name-generator.ts +27 -0
  279. package/src/omp-blob-root.js +83 -64
  280. package/src/omp-blob-root.ts +116 -0
  281. package/src/omp-config-overlay.js +65 -38
  282. package/src/omp-config-overlay.ts +110 -0
  283. package/src/omp-execution-fingerprint.js +38 -43
  284. package/src/omp-execution-fingerprint.ts +92 -0
  285. package/src/omp-session-limits.js +10 -15
  286. package/src/omp-session-limits.ts +53 -0
  287. package/src/omp-session-partition.js +2 -2
  288. package/src/omp-session-verifier.js +1 -1
  289. package/src/orchestrator.js +19 -4
  290. package/src/providers/anthropic/index.js +3 -1
  291. package/src/providers/anthropic/index.ts +13 -0
  292. package/src/providers/capabilities.js +19 -27
  293. package/src/providers/capabilities.ts +44 -0
  294. package/src/providers/google/index.js +3 -1
  295. package/src/providers/google/index.ts +13 -0
  296. package/src/providers/openai/index.js +3 -1
  297. package/src/providers/openai/index.ts +13 -0
  298. package/src/providers/opencode/index.js +3 -1
  299. package/src/providers/opencode/index.ts +13 -0
  300. package/src/quality-gates.js +67 -127
  301. package/src/quality-gates.ts +149 -0
  302. package/src/repo-settings-access.js +40 -0
  303. package/src/repo-settings-access.ts +64 -0
  304. package/src/schemas/sub-cluster.js +127 -183
  305. package/src/schemas/sub-cluster.ts +238 -0
  306. package/src/state-snapshot-normalization.js +224 -0
  307. package/src/state-snapshot-normalization.ts +222 -0
  308. package/src/state-snapshot.js +192 -359
  309. package/src/state-snapshot.ts +291 -0
  310. package/src/state-snapshotter.js +118 -130
  311. package/src/state-snapshotter.ts +167 -0
  312. package/src/sub-cluster-wrapper.js +4 -13
  313. package/src/task-run-model-args.js +87 -124
  314. package/src/task-run-model-args.ts +204 -0
  315. package/src/task-runner.js +5 -25
  316. package/src/task-runner.ts +28 -0
  317. package/src/task-startup-error.js +73 -58
  318. package/src/task-startup-error.ts +126 -0
  319. package/src/template-validation/consensus-gate-contracts.js +2 -0
  320. package/src/template-validation/consensus-gate-contracts.ts +70 -0
  321. package/src/template-validation/consensus-gate-scenarios.js +76 -0
  322. package/src/template-validation/consensus-gate-scenarios.ts +130 -0
  323. package/src/template-validation/consensus-gate-stage.js +34 -0
  324. package/src/template-validation/consensus-gate-stage.ts +56 -0
  325. package/src/template-validation/random-topology-contracts.js +2 -0
  326. package/src/template-validation/random-topology-contracts.ts +54 -0
  327. package/src/template-validation/random-topology-dispatch.js +25 -0
  328. package/src/template-validation/random-topology-dispatch.ts +33 -0
  329. package/src/template-validation/random-topology-message-loop.js +53 -0
  330. package/src/template-validation/random-topology-message-loop.ts +73 -0
  331. package/src/template-validation/random-topology-operations.js +79 -0
  332. package/src/template-validation/random-topology-operations.ts +101 -0
  333. package/src/template-validation/random-topology-runtime.js +25 -0
  334. package/src/template-validation/random-topology-runtime.ts +43 -0
  335. package/src/template-validation/random-topology-scenario.js +52 -0
  336. package/src/template-validation/random-topology-scenario.ts +57 -0
  337. package/src/template-validation/random-topology-schema.js +159 -0
  338. package/src/template-validation/random-topology-schema.ts +190 -0
  339. package/src/template-validation/random-topology-state.js +28 -0
  340. package/src/template-validation/random-topology-state.ts +30 -0
  341. package/src/template-validation/random-topology-trigger-evaluation.js +45 -0
  342. package/src/template-validation/random-topology-trigger-evaluation.ts +65 -0
  343. package/src/template-validation/random-topology-trigger.js +75 -0
  344. package/src/template-validation/random-topology-trigger.ts +88 -0
  345. package/src/template-validation/report-formatter.js +40 -46
  346. package/src/template-validation/report-formatter.ts +101 -0
  347. package/src/template-validation/simulate-consensus-gates.js +111 -302
  348. package/src/template-validation/simulate-consensus-gates.ts +196 -0
  349. package/src/template-validation/simulate-random-topology.js +26 -519
  350. package/src/template-validation/simulate-random-topology.ts +57 -0
  351. package/src/template-validation/simulate-two-stage-validation.js +41 -265
  352. package/src/template-validation/simulate-two-stage-validation.ts +55 -0
  353. package/src/template-validation/simulation-agent-runtime.js +25 -0
  354. package/src/template-validation/simulation-agent-runtime.ts +40 -0
  355. package/src/template-validation/simulation-agent.js +29 -0
  356. package/src/template-validation/simulation-agent.ts +73 -0
  357. package/src/template-validation/simulation-runtime.js +34 -0
  358. package/src/template-validation/simulation-runtime.ts +72 -0
  359. package/src/template-validation/two-stage-contracts.js +2 -0
  360. package/src/template-validation/two-stage-contracts.ts +41 -0
  361. package/src/template-validation/two-stage-inputs.js +21 -0
  362. package/src/template-validation/two-stage-inputs.ts +29 -0
  363. package/src/template-validation/two-stage-results.js +30 -0
  364. package/src/template-validation/two-stage-results.ts +40 -0
  365. package/src/template-validation/two-stage-scenario.js +86 -0
  366. package/src/template-validation/two-stage-scenario.ts +120 -0
  367. package/src/worktree-claude-config.js +182 -220
  368. package/src/worktree-claude-config.ts +272 -0
  369. package/src/worktree-tooling-env.js +103 -125
  370. package/src/worktree-tooling-env.ts +142 -0
  371. package/task-lib/completion.js +2 -3
  372. package/task-lib/completion.ts +9 -0
  373. package/task-lib/config.js +1 -4
  374. package/task-lib/config.ts +11 -0
  375. package/task-lib/name-generator.js +4 -226
  376. package/task-lib/name-generator.ts +11 -0
  377. package/task-lib/omp-session-cleanup.js +1 -1
  378. package/task-lib/omp-storage-root.js +8 -12
  379. package/task-lib/omp-storage-root.ts +62 -0
  380. package/task-lib/process-termination.js +143 -166
  381. package/task-lib/process-termination.ts +268 -0
@@ -0,0 +1,300 @@
1
+ /** Terminal client for attaching to a running task or agent. */
2
+ import crypto from 'node:crypto';
3
+ import { EventEmitter } from 'node:events';
4
+ import net from 'node:net';
5
+
6
+ import protocol from './protocol';
7
+
8
+ const CTRL_B = '\x02';
9
+ const CTRL_C = '\x03';
10
+ const CTRL_Z = '\x1a';
11
+ const DETACH_TIMEOUT = 500;
12
+
13
+ interface AttachInput extends NodeJS.ReadableStream {
14
+ isRaw?: boolean;
15
+ isTTY?: boolean;
16
+ setRawMode?: (mode: boolean) => AttachInput;
17
+ }
18
+
19
+ interface AttachOutput extends NodeJS.WritableStream {
20
+ columns: number;
21
+ isTTY?: boolean;
22
+ rows: number;
23
+ }
24
+
25
+ interface AttachClientOptions {
26
+ socketPath: string;
27
+ stdin?: AttachInput;
28
+ stdout?: AttachOutput;
29
+ }
30
+
31
+ interface ServerMessage {
32
+ code: unknown;
33
+ data: unknown;
34
+ message: unknown;
35
+ signal: unknown;
36
+ type: unknown;
37
+ }
38
+
39
+ function readServerMessage(message: unknown): ServerMessage {
40
+ if ((typeof message !== 'object' && typeof message !== 'function') || message === null) {
41
+ throw new TypeError(`Cannot read properties of ${message}`);
42
+ }
43
+ return {
44
+ code: Reflect.get(message, 'code'),
45
+ data: Reflect.get(message, 'data'),
46
+ message: Reflect.get(message, 'message'),
47
+ signal: Reflect.get(message, 'signal'),
48
+ type: Reflect.get(message, 'type'),
49
+ };
50
+ }
51
+
52
+ function dataMessage(data: unknown): { data?: string | null } {
53
+ if (data === undefined || !data) return {};
54
+ if (typeof data === 'string') return { data };
55
+ throw new TypeError('Attach protocol data must be a base64 string');
56
+ }
57
+
58
+ class AttachClient extends EventEmitter {
59
+ readonly socketPath: string;
60
+ readonly stdin: AttachInput;
61
+ readonly stdout: AttachOutput;
62
+ readonly clientId: string;
63
+ socket: net.Socket | null = null;
64
+ decoder = new protocol.MessageDecoder();
65
+ connected = false;
66
+ wasRawMode: boolean | undefined | null = null;
67
+ ctrlBPressed = false;
68
+ ctrlBTimeout: NodeJS.Timeout | null = null;
69
+
70
+ constructor(options: AttachClientOptions) {
71
+ super();
72
+ if (!options.socketPath) {
73
+ throw new Error('AttachClient: socketPath is required');
74
+ }
75
+ this.socketPath = options.socketPath;
76
+ this.stdin = options.stdin || process.stdin;
77
+ this.stdout = options.stdout || process.stdout;
78
+ this.clientId = crypto.randomUUID();
79
+
80
+ this._onSocketData = this._onSocketData.bind(this);
81
+ this._onSocketClose = this._onSocketClose.bind(this);
82
+ this._onSocketError = this._onSocketError.bind(this);
83
+ this._onStdinData = this._onStdinData.bind(this);
84
+ this._onResize = this._onResize.bind(this);
85
+ }
86
+
87
+ connect(): Promise<void> {
88
+ if (this.connected) {
89
+ throw new Error('AttachClient: Already connected');
90
+ }
91
+
92
+ return new Promise((resolve, reject) => {
93
+ const socket = net.createConnection(this.socketPath);
94
+ this.socket = socket;
95
+
96
+ socket.on('connect', () => {
97
+ this.connected = true;
98
+ const cols = this.stdout.columns || 80;
99
+ const rows = this.stdout.rows || 24;
100
+ socket.write(protocol.encode(protocol.createAttachMessage(this.clientId, cols, rows)));
101
+ this._setupTerminal();
102
+ socket.on('data', this._onSocketData);
103
+ socket.on('close', this._onSocketClose);
104
+ socket.on('error', this._onSocketError);
105
+ resolve();
106
+ });
107
+
108
+ socket.on('error', (error: Error) => {
109
+ if (!this.connected) reject(error);
110
+ });
111
+
112
+ const timeout = setTimeout(() => {
113
+ if (!this.connected) {
114
+ socket.destroy();
115
+ reject(new Error('Connection timeout'));
116
+ }
117
+ }, 5000);
118
+ socket.on('connect', () => clearTimeout(timeout));
119
+ });
120
+ }
121
+
122
+ disconnect(): void {
123
+ if (!this.connected) return;
124
+ try {
125
+ this.socket?.write(protocol.encode(protocol.createDetachMessage(this.clientId)));
126
+ } catch {
127
+ // Ignore transport failures during detach.
128
+ }
129
+ this._cleanup();
130
+ this.emit('detach');
131
+ }
132
+
133
+ sendSignal(signal: string): void {
134
+ if (!this.connected) return;
135
+ try {
136
+ this.socket?.write(protocol.encode(protocol.createSignalMessage(signal)));
137
+ } catch {
138
+ // Ignore transport failures while signaling.
139
+ }
140
+ }
141
+
142
+ private _setupTerminal(): void {
143
+ if (this.stdin.isTTY && this.stdin.setRawMode) {
144
+ this.wasRawMode = this.stdin.isRaw;
145
+ this.stdin.setRawMode(true);
146
+ }
147
+ this.stdin.resume();
148
+ this.stdin.on('data', this._onStdinData);
149
+ if (this.stdout.isTTY) this.stdout.on('resize', this._onResize);
150
+
151
+ process.on('SIGINT', () => this.disconnect());
152
+ process.on('SIGTERM', () => {
153
+ this._cleanup();
154
+ process.exit(0);
155
+ });
156
+ }
157
+
158
+ private _restoreTerminal(): void {
159
+ if (this.stdin.isTTY && this.stdin.setRawMode && this.wasRawMode !== null) {
160
+ Reflect.apply(this.stdin.setRawMode, this.stdin, [this.wasRawMode]);
161
+ }
162
+ this.stdin.removeListener('data', this._onStdinData);
163
+ if (this.stdout.isTTY) this.stdout.removeListener('resize', this._onResize);
164
+ this.stdin.pause();
165
+ }
166
+
167
+ private _onSocketData(data: Buffer): void {
168
+ try {
169
+ for (const message of this.decoder.feed(data)) this._handleMessage(message);
170
+ } catch (error: unknown) {
171
+ const reason = error instanceof Error ? error.message : String(error);
172
+ this.emit('error', new Error(`Protocol error: ${reason}`));
173
+ this._cleanup();
174
+ }
175
+ }
176
+
177
+ private _handleMessage(message: unknown): void {
178
+ const serverMessage = readServerMessage(message);
179
+ switch (serverMessage.type) {
180
+ case protocol.MessageType.OUTPUT:
181
+ case protocol.MessageType.HISTORY: {
182
+ const data = protocol.decodeData(dataMessage(serverMessage.data));
183
+ if (data) this.stdout.write(data);
184
+ break;
185
+ }
186
+ case protocol.MessageType.STATE:
187
+ this.emit('state', message);
188
+ break;
189
+ case protocol.MessageType.EXIT: {
190
+ const { code, signal } = serverMessage;
191
+ this.emit('exit', { code, signal });
192
+ this._cleanup();
193
+ break;
194
+ }
195
+ case protocol.MessageType.ERROR:
196
+ this.emit(
197
+ 'error',
198
+ new Error(serverMessage.message === undefined ? undefined : String(serverMessage.message))
199
+ );
200
+ break;
201
+ }
202
+ }
203
+
204
+ private _onStdinData(data: Buffer | string): void {
205
+ const input = data.toString();
206
+ if (this.ctrlBPressed) {
207
+ this.ctrlBPressed = false;
208
+ if (this.ctrlBTimeout) {
209
+ clearTimeout(this.ctrlBTimeout);
210
+ this.ctrlBTimeout = null;
211
+ }
212
+ if (input === 'd' || input === 'D') return this.disconnect();
213
+ if (input === 'c' || input === 'C') {
214
+ this.stdout.write('\r\n⚠️ Sending SIGINT to agent (interrupting task)...\r\n');
215
+ return this.sendSignal('SIGINT');
216
+ }
217
+ if (input === '?') return this._showHelp();
218
+ this._forwardInput(Buffer.from([0x02]));
219
+ this._forwardInput(data);
220
+ return;
221
+ }
222
+
223
+ if (input === CTRL_B) {
224
+ this.ctrlBPressed = true;
225
+ this.ctrlBTimeout = setTimeout(() => {
226
+ if (this.ctrlBPressed) {
227
+ this.ctrlBPressed = false;
228
+ this._forwardInput(data);
229
+ }
230
+ }, DETACH_TIMEOUT);
231
+ return;
232
+ }
233
+ if (input === CTRL_C) return this.disconnect();
234
+ if (input === CTRL_Z) return this.sendSignal('SIGTSTP');
235
+ this._forwardInput(data);
236
+ }
237
+
238
+ private _forwardInput(data: Buffer | string): void {
239
+ if (!this.connected) return;
240
+ try {
241
+ this.socket?.write(protocol.encode(protocol.createStdinMessage(data)));
242
+ } catch {
243
+ // Ignore transport failures while forwarding terminal input.
244
+ }
245
+ }
246
+
247
+ private _onResize(): void {
248
+ if (!this.connected) return;
249
+ try {
250
+ this.socket?.write(
251
+ protocol.encode(protocol.createResizeMessage(this.stdout.columns, this.stdout.rows))
252
+ );
253
+ } catch {
254
+ // Ignore transport failures while forwarding terminal size.
255
+ }
256
+ }
257
+
258
+ private _onSocketClose(): void {
259
+ if (this.connected) {
260
+ this.emit('close');
261
+ this._cleanup();
262
+ }
263
+ }
264
+
265
+ private _onSocketError(error: Error): void {
266
+ this.emit('error', error);
267
+ this._cleanup();
268
+ }
269
+
270
+ private _showHelp(): void {
271
+ const horizontal = '─'.repeat(58);
272
+ this.stdout.write(`
273
+ \r\n╭${horizontal}╮
274
+ \r\n│ Vibe Attach - Key Bindings │
275
+ \r\n├${horizontal}┤
276
+ \r\n│ Ctrl+C Detach (task continues running) │
277
+ \r\n│ Ctrl+B d Also detach (for tmux muscle memory) │
278
+ \r\n│ Ctrl+B ? Show this help │
279
+ \r\n│ Ctrl+B c ⚠️ Interrupt agent (sends SIGINT) │
280
+ \r\n│ Ctrl+Z Suspend process (sends SIGTSTP) │
281
+ \r\n╰${horizontal}╯
282
+ \r\n`);
283
+ }
284
+
285
+ private _cleanup(): void {
286
+ this.connected = false;
287
+ if (this.ctrlBTimeout) {
288
+ clearTimeout(this.ctrlBTimeout);
289
+ this.ctrlBTimeout = null;
290
+ }
291
+ this._restoreTerminal();
292
+ if (this.socket) {
293
+ this.socket.removeAllListeners();
294
+ this.socket.destroy();
295
+ this.socket = null;
296
+ }
297
+ }
298
+ }
299
+
300
+ export = AttachClient;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.cleanupAttachServer = cleanupAttachServer;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ async function cleanupAttachServer(host) {
10
+ for (const [, client] of host.clients) {
11
+ try {
12
+ client.socket.destroy();
13
+ }
14
+ catch {
15
+ // Ignore client cleanup failures.
16
+ }
17
+ }
18
+ host.clients.clear();
19
+ if (host.server) {
20
+ const server = host.server;
21
+ await new Promise((resolve) => server.close(() => resolve()));
22
+ host.server = null;
23
+ }
24
+ if (node_fs_1.default.existsSync(host.socketPath)) {
25
+ try {
26
+ node_fs_1.default.unlinkSync(host.socketPath);
27
+ }
28
+ catch {
29
+ // Ignore socket cleanup failures.
30
+ }
31
+ }
32
+ const socketDir = node_path_1.default.dirname(host.socketPath);
33
+ if (socketDir.includes('cluster-')) {
34
+ try {
35
+ if (node_fs_1.default.readdirSync(socketDir).length === 0)
36
+ node_fs_1.default.rmdirSync(socketDir);
37
+ }
38
+ catch {
39
+ // Ignore directory cleanup failures.
40
+ }
41
+ }
42
+ host.emit('cleanup');
43
+ }
@@ -0,0 +1,39 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ import type { AttachServerHost } from './attach-server-types';
5
+
6
+ export async function cleanupAttachServer(host: AttachServerHost): Promise<void> {
7
+ for (const [, client] of host.clients) {
8
+ try {
9
+ client.socket.destroy();
10
+ } catch {
11
+ // Ignore client cleanup failures.
12
+ }
13
+ }
14
+ host.clients.clear();
15
+
16
+ if (host.server) {
17
+ const server = host.server;
18
+ await new Promise<void>((resolve) => server.close(() => resolve()));
19
+ host.server = null;
20
+ }
21
+
22
+ if (fs.existsSync(host.socketPath)) {
23
+ try {
24
+ fs.unlinkSync(host.socketPath);
25
+ } catch {
26
+ // Ignore socket cleanup failures.
27
+ }
28
+ }
29
+
30
+ const socketDir = path.dirname(host.socketPath);
31
+ if (socketDir.includes('cluster-')) {
32
+ try {
33
+ if (fs.readdirSync(socketDir).length === 0) fs.rmdirSync(socketDir);
34
+ } catch {
35
+ // Ignore directory cleanup failures.
36
+ }
37
+ }
38
+ host.emit('cleanup');
39
+ }
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handleClientConnection = handleClientConnection;
7
+ exports.handleClientMessage = handleClientMessage;
8
+ exports.removeClient = removeClient;
9
+ exports.sendClientError = sendClientError;
10
+ const protocol_1 = __importDefault(require("./protocol"));
11
+ function messageField(message, field) {
12
+ if ((typeof message !== 'object' && typeof message !== 'function') || message === null) {
13
+ throw new TypeError(`Cannot read properties of ${message}`);
14
+ }
15
+ return Reflect.get(message, field);
16
+ }
17
+ function decodeClientData(data) {
18
+ if (!data)
19
+ return null;
20
+ if (typeof data !== 'string') {
21
+ throw new TypeError('Attach protocol data must be a base64 string');
22
+ }
23
+ return Buffer.from(data, 'base64');
24
+ }
25
+ function attachClient(host, socket, message, setClientId) {
26
+ const clientId = messageField(message, 'clientId');
27
+ if (!clientId) {
28
+ host._sendError(socket, 'ATTACH requires clientId');
29
+ return;
30
+ }
31
+ host.clients.set(clientId, { socket, decoder: new protocol_1.default.MessageDecoder() });
32
+ setClientId(clientId);
33
+ const history = host.outputBuffer.read();
34
+ if (history.length > 0) {
35
+ socket.write(protocol_1.default.encode(protocol_1.default.createHistoryMessage(history)));
36
+ }
37
+ socket.write(protocol_1.default.encode(protocol_1.default.createStateMessage(host.getState())));
38
+ const cols = messageField(message, 'cols');
39
+ const rows = messageField(message, 'rows');
40
+ if (cols && rows)
41
+ Reflect.apply(host.resize, host, [cols, rows]);
42
+ host.emit('clientAttach', { clientId });
43
+ }
44
+ function resizeClient(host, message) {
45
+ const cols = messageField(message, 'cols');
46
+ const rows = messageField(message, 'rows');
47
+ if (cols && rows)
48
+ Reflect.apply(host.resize, host, [cols, rows]);
49
+ }
50
+ function writeClientInput(host, message) {
51
+ const data = decodeClientData(messageField(message, 'data'));
52
+ if (data)
53
+ host.write(data);
54
+ }
55
+ function handleClientConnection(host, socket) {
56
+ const decoder = new protocol_1.default.MessageDecoder();
57
+ let clientId = null;
58
+ socket.on('data', (data) => {
59
+ try {
60
+ for (const message of decoder.feed(data)) {
61
+ host._handleClientMessage(socket, message, (id) => {
62
+ clientId = id;
63
+ });
64
+ }
65
+ }
66
+ catch (error) {
67
+ const reason = error instanceof Error ? error.message : String(error);
68
+ host._sendError(socket, `Protocol error: ${reason}`);
69
+ socket.end();
70
+ }
71
+ });
72
+ socket.on('close', () => {
73
+ if (clientId)
74
+ host._removeClient(clientId);
75
+ });
76
+ socket.on('error', () => {
77
+ if (clientId)
78
+ host._removeClient(clientId);
79
+ });
80
+ }
81
+ function handleClientMessage(host, socket, message, setClientId) {
82
+ const type = messageField(message, 'type');
83
+ switch (type) {
84
+ case protocol_1.default.MessageType.ATTACH:
85
+ attachClient(host, socket, message, setClientId);
86
+ break;
87
+ case protocol_1.default.MessageType.DETACH:
88
+ host._removeClient(messageField(message, 'clientId'));
89
+ socket.end();
90
+ break;
91
+ case protocol_1.default.MessageType.RESIZE:
92
+ resizeClient(host, message);
93
+ break;
94
+ case protocol_1.default.MessageType.SIGNAL:
95
+ Reflect.apply(host.sendSignal, host, [messageField(message, 'signal')]);
96
+ break;
97
+ case protocol_1.default.MessageType.STDIN:
98
+ writeClientInput(host, message);
99
+ break;
100
+ default:
101
+ host._sendError(socket, `Unknown message type: ${type}`);
102
+ }
103
+ }
104
+ function removeClient(host, clientId) {
105
+ if (host.clients.has(clientId)) {
106
+ host.clients.delete(clientId);
107
+ host.emit('clientDetach', { clientId });
108
+ }
109
+ }
110
+ function sendClientError(socket, message) {
111
+ try {
112
+ socket.write(protocol_1.default.encode(protocol_1.default.createErrorMessage(message)));
113
+ }
114
+ catch {
115
+ // Client disconnected.
116
+ }
117
+ }
@@ -0,0 +1,122 @@
1
+ import type net from 'node:net';
2
+
3
+ import protocol from './protocol';
4
+ import type { AttachServerHost } from './attach-server-types';
5
+
6
+ function messageField(message: unknown, field: string): unknown {
7
+ if ((typeof message !== 'object' && typeof message !== 'function') || message === null) {
8
+ throw new TypeError(`Cannot read properties of ${message}`);
9
+ }
10
+ return Reflect.get(message, field);
11
+ }
12
+
13
+ function decodeClientData(data: unknown): Buffer | null {
14
+ if (!data) return null;
15
+ if (typeof data !== 'string') {
16
+ throw new TypeError('Attach protocol data must be a base64 string');
17
+ }
18
+ return Buffer.from(data, 'base64');
19
+ }
20
+
21
+ function attachClient(
22
+ host: AttachServerHost,
23
+ socket: net.Socket,
24
+ message: unknown,
25
+ setClientId: (id: unknown) => void
26
+ ): void {
27
+ const clientId = messageField(message, 'clientId');
28
+ if (!clientId) {
29
+ host._sendError(socket, 'ATTACH requires clientId');
30
+ return;
31
+ }
32
+ host.clients.set(clientId, { socket, decoder: new protocol.MessageDecoder() });
33
+ setClientId(clientId);
34
+ const history = host.outputBuffer.read();
35
+ if (history.length > 0) {
36
+ socket.write(protocol.encode(protocol.createHistoryMessage(history)));
37
+ }
38
+ socket.write(protocol.encode(protocol.createStateMessage(host.getState())));
39
+ const cols = messageField(message, 'cols');
40
+ const rows = messageField(message, 'rows');
41
+ if (cols && rows) Reflect.apply(host.resize, host, [cols, rows]);
42
+ host.emit('clientAttach', { clientId });
43
+ }
44
+
45
+ function resizeClient(host: AttachServerHost, message: unknown): void {
46
+ const cols = messageField(message, 'cols');
47
+ const rows = messageField(message, 'rows');
48
+ if (cols && rows) Reflect.apply(host.resize, host, [cols, rows]);
49
+ }
50
+
51
+ function writeClientInput(host: AttachServerHost, message: unknown): void {
52
+ const data = decodeClientData(messageField(message, 'data'));
53
+ if (data) host.write(data);
54
+ }
55
+
56
+ export function handleClientConnection(host: AttachServerHost, socket: net.Socket): void {
57
+ const decoder = new protocol.MessageDecoder();
58
+ let clientId: unknown = null;
59
+ socket.on('data', (data: Buffer) => {
60
+ try {
61
+ for (const message of decoder.feed(data)) {
62
+ host._handleClientMessage(socket, message, (id) => {
63
+ clientId = id;
64
+ });
65
+ }
66
+ } catch (error: unknown) {
67
+ const reason = error instanceof Error ? error.message : String(error);
68
+ host._sendError(socket, `Protocol error: ${reason}`);
69
+ socket.end();
70
+ }
71
+ });
72
+ socket.on('close', () => {
73
+ if (clientId) host._removeClient(clientId);
74
+ });
75
+ socket.on('error', () => {
76
+ if (clientId) host._removeClient(clientId);
77
+ });
78
+ }
79
+
80
+ export function handleClientMessage(
81
+ host: AttachServerHost,
82
+ socket: net.Socket,
83
+ message: unknown,
84
+ setClientId: (id: unknown) => void
85
+ ): void {
86
+ const type = messageField(message, 'type');
87
+ switch (type) {
88
+ case protocol.MessageType.ATTACH:
89
+ attachClient(host, socket, message, setClientId);
90
+ break;
91
+ case protocol.MessageType.DETACH:
92
+ host._removeClient(messageField(message, 'clientId'));
93
+ socket.end();
94
+ break;
95
+ case protocol.MessageType.RESIZE:
96
+ resizeClient(host, message);
97
+ break;
98
+ case protocol.MessageType.SIGNAL:
99
+ Reflect.apply(host.sendSignal, host, [messageField(message, 'signal')]);
100
+ break;
101
+ case protocol.MessageType.STDIN:
102
+ writeClientInput(host, message);
103
+ break;
104
+ default:
105
+ host._sendError(socket, `Unknown message type: ${type}`);
106
+ }
107
+ }
108
+
109
+ export function removeClient(host: AttachServerHost, clientId: unknown): void {
110
+ if (host.clients.has(clientId)) {
111
+ host.clients.delete(clientId);
112
+ host.emit('clientDetach', { clientId });
113
+ }
114
+ }
115
+
116
+ export function sendClientError(socket: net.Socket, message: string): void {
117
+ try {
118
+ socket.write(protocol.encode(protocol.createErrorMessage(message)));
119
+ } catch {
120
+ // Client disconnected.
121
+ }
122
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handlePtyOutput = handlePtyOutput;
7
+ exports.handleProcessExit = handleProcessExit;
8
+ const protocol_1 = __importDefault(require("./protocol"));
9
+ function handlePtyOutput(host, data) {
10
+ host.outputBuffer.write(data);
11
+ const message = protocol_1.default.encode(protocol_1.default.createOutputMessage(data));
12
+ for (const [clientId, client] of host.clients) {
13
+ try {
14
+ client.socket.write(message);
15
+ }
16
+ catch {
17
+ host._removeClient(clientId);
18
+ }
19
+ }
20
+ host.emit('output', data);
21
+ }
22
+ function handleProcessExit(host, exitCode, signal) {
23
+ host.exitCode = exitCode;
24
+ host.exitSignal = signal;
25
+ host.state = 'exited';
26
+ const exitMessage = protocol_1.default.encode(protocol_1.default.createExitMessage(exitCode, signal));
27
+ for (const [, client] of host.clients) {
28
+ try {
29
+ client.socket.write(exitMessage);
30
+ client.socket.end();
31
+ }
32
+ catch {
33
+ // Client already disconnected.
34
+ }
35
+ }
36
+ host.emit('exit', { exitCode, signal });
37
+ setTimeout(() => void host._cleanup(), 500);
38
+ }
@@ -0,0 +1,36 @@
1
+ import protocol from './protocol';
2
+ import type { AttachServerHost, ExitSignal } from './attach-server-types';
3
+
4
+ export function handlePtyOutput(host: AttachServerHost, data: Buffer | string): void {
5
+ host.outputBuffer.write(data);
6
+ const message = protocol.encode(protocol.createOutputMessage(data));
7
+ for (const [clientId, client] of host.clients) {
8
+ try {
9
+ client.socket.write(message);
10
+ } catch {
11
+ host._removeClient(clientId);
12
+ }
13
+ }
14
+ host.emit('output', data);
15
+ }
16
+
17
+ export function handleProcessExit(
18
+ host: AttachServerHost,
19
+ exitCode: number,
20
+ signal: ExitSignal
21
+ ): void {
22
+ host.exitCode = exitCode;
23
+ host.exitSignal = signal;
24
+ host.state = 'exited';
25
+ const exitMessage = protocol.encode(protocol.createExitMessage(exitCode, signal));
26
+ for (const [, client] of host.clients) {
27
+ try {
28
+ client.socket.write(exitMessage);
29
+ client.socket.end();
30
+ } catch {
31
+ // Client already disconnected.
32
+ }
33
+ }
34
+ host.emit('exit', { exitCode, signal });
35
+ setTimeout(() => void host._cleanup(), 500);
36
+ }