@the-open-engine/zeroshot 6.38.1 → 6.39.1

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 (383) hide show
  1. package/README.md +6 -0
  2. package/cli/event-copy.js +8 -7
  3. package/cli/event-copy.ts +12 -0
  4. package/cli/index.js +67 -48
  5. package/cli/json-export.js +61 -64
  6. package/cli/json-export.ts +112 -0
  7. package/cli/message-formatter-utils.js +68 -56
  8. package/cli/message-formatter-utils.ts +121 -0
  9. package/cli/message-formatters-normal.js +124 -259
  10. package/cli/message-formatters-normal.ts +250 -0
  11. package/cli/message-formatters-watch.js +81 -84
  12. package/cli/message-formatters-watch.ts +189 -0
  13. package/lib/cluster/generated/protocol-schema.cjs +1 -1
  14. package/lib/cluster/generated/protocol-schema.mjs +1 -1
  15. package/lib/cluster/generated/protocol.d.cts +8 -0
  16. package/lib/cluster/generated/protocol.d.mts +8 -0
  17. package/lib/cluster/generated/protocol.d.ts +8 -0
  18. package/lib/clusters-registry.d.ts +23 -0
  19. package/lib/clusters-registry.js +17 -29
  20. package/lib/completion.d.ts +12 -0
  21. package/lib/completion.js +84 -94
  22. package/lib/compose-utils.d.ts +32 -0
  23. package/lib/compose-utils.js +57 -72
  24. package/lib/detached-startup.d.ts +84 -0
  25. package/lib/detached-startup.js +232 -302
  26. package/lib/docker-config.d.ts +43 -0
  27. package/lib/docker-config.js +150 -325
  28. package/lib/docker-env-auth.d.ts +17 -0
  29. package/lib/docker-env-auth.js +75 -0
  30. package/lib/git-remote-utils.d.ts +48 -0
  31. package/lib/git-remote-utils.js +147 -213
  32. package/lib/id-detector.d.ts +15 -0
  33. package/lib/id-detector.js +37 -41
  34. package/lib/path-check.d.ts +30 -0
  35. package/lib/path-check.js +42 -45
  36. package/lib/process-liveness.d.ts +16 -0
  37. package/lib/process-liveness.js +19 -12
  38. package/lib/provider-credential-path.d.ts +16 -0
  39. package/lib/provider-credential-path.js +31 -29
  40. package/lib/provider-defaults.d.ts +30 -0
  41. package/lib/provider-defaults.js +47 -42
  42. package/lib/provider-detection.d.ts +11 -0
  43. package/lib/provider-detection.js +91 -54
  44. package/lib/provider-names.d.ts +38 -0
  45. package/lib/provider-names.js +56 -64
  46. package/lib/repo-settings.d.ts +28 -0
  47. package/lib/repo-settings.js +83 -59
  48. package/lib/run-mode.d.ts +21 -0
  49. package/lib/run-mode.js +21 -20
  50. package/lib/run-plan.d.ts +28 -0
  51. package/lib/run-plan.js +17 -16
  52. package/lib/settings/claude-auth.d.ts +35 -0
  53. package/lib/settings/claude-auth.js +30 -47
  54. package/lib/settings-defaults.d.ts +20 -0
  55. package/lib/settings-defaults.js +149 -0
  56. package/lib/settings-error.d.ts +7 -0
  57. package/lib/settings-error.js +8 -0
  58. package/lib/settings-issue-providers.d.ts +10 -0
  59. package/lib/settings-issue-providers.js +12 -0
  60. package/lib/settings-models.d.ts +22 -0
  61. package/lib/settings-models.js +93 -0
  62. package/lib/settings-storage.d.ts +23 -0
  63. package/lib/settings-storage.js +205 -0
  64. package/lib/settings-validation.d.ts +7 -0
  65. package/lib/settings-validation.js +182 -0
  66. package/lib/settings.d.ts +29 -0
  67. package/lib/settings.js +41 -736
  68. package/lib/setup-apply-types.d.ts +66 -0
  69. package/lib/setup-apply-types.js +2 -0
  70. package/lib/setup-apply.d.ts +39 -0
  71. package/lib/setup-apply.js +267 -264
  72. package/lib/setup-journal.d.ts +41 -0
  73. package/lib/setup-journal.js +119 -76
  74. package/lib/setup-plan-types.d.ts +102 -0
  75. package/lib/setup-plan-types.js +2 -0
  76. package/lib/setup-plan.d.ts +30 -0
  77. package/lib/setup-plan.js +300 -324
  78. package/lib/setup-undo.d.ts +44 -0
  79. package/lib/setup-undo.js +61 -74
  80. package/lib/start-cluster-config.d.ts +26 -0
  81. package/lib/start-cluster-config.js +56 -0
  82. package/lib/start-cluster-environment.d.ts +41 -0
  83. package/lib/start-cluster-environment.js +134 -0
  84. package/lib/start-cluster-run-options.d.ts +54 -0
  85. package/lib/start-cluster-run-options.js +148 -0
  86. package/lib/start-cluster.d.ts +64 -0
  87. package/lib/start-cluster.js +98 -454
  88. package/lib/stream-json-parser.d.ts +17 -0
  89. package/lib/stream-json-parser.js +51 -52
  90. package/npm-shrinkwrap.json +6 -8
  91. package/package.json +14 -8
  92. package/scripts/postinstall.js +27 -0
  93. package/scripts/rust-distribution.js +36 -45
  94. package/src/agent/agent-command-proofs-context.js +35 -45
  95. package/src/agent/agent-command-proofs-context.ts +87 -0
  96. package/src/agent/agent-config-settings.js +19 -0
  97. package/src/agent/agent-config-settings.ts +41 -0
  98. package/src/agent/agent-config.js +122 -231
  99. package/src/agent/agent-config.ts +285 -0
  100. package/src/agent/agent-context-builder.js +38 -273
  101. package/src/agent/agent-context-builder.ts +51 -0
  102. package/src/agent/agent-context-environment-sections.js +105 -0
  103. package/src/agent/agent-context-environment-sections.ts +134 -0
  104. package/src/agent/agent-context-packs.js +165 -0
  105. package/src/agent/agent-context-packs.ts +296 -0
  106. package/src/agent/agent-context-prompt-sections.js +100 -0
  107. package/src/agent/agent-context-prompt-sections.ts +148 -0
  108. package/src/agent/agent-context-sections.js +25 -334
  109. package/src/agent/agent-context-sections.ts +36 -0
  110. package/src/agent/agent-context-sources.js +96 -149
  111. package/src/agent/agent-context-sources.ts +253 -0
  112. package/src/agent/agent-context-types.js +2 -0
  113. package/src/agent/agent-context-types.ts +132 -0
  114. package/src/agent/agent-context-validation-sections.js +73 -0
  115. package/src/agent/agent-context-validation-sections.ts +129 -0
  116. package/src/agent/agent-input-injector.js +99 -118
  117. package/src/agent/agent-input-injector.ts +195 -0
  118. package/src/agent/agent-liveness-poll.js +74 -78
  119. package/src/agent/agent-liveness-poll.ts +163 -0
  120. package/src/agent/agent-quality-gate-schema.js +77 -86
  121. package/src/agent/agent-quality-gate-schema.ts +135 -0
  122. package/src/agent/agent-quality-gates-context.js +44 -50
  123. package/src/agent/agent-quality-gates-context.ts +91 -0
  124. package/src/agent/agent-stuck-detector.js +171 -239
  125. package/src/agent/agent-stuck-detector.ts +288 -0
  126. package/src/agent/agent-trigger-evaluator.js +29 -46
  127. package/src/agent/agent-trigger-evaluator.ts +98 -0
  128. package/src/agent/context-metrics.js +110 -141
  129. package/src/agent/context-metrics.ts +261 -0
  130. package/src/agent/context-pack-builder.js +72 -358
  131. package/src/agent/context-pack-builder.ts +105 -0
  132. package/src/agent/context-pack-core.js +70 -0
  133. package/src/agent/context-pack-core.ts +96 -0
  134. package/src/agent/context-pack-selection.js +119 -0
  135. package/src/agent/context-pack-selection.ts +187 -0
  136. package/src/agent/context-pack-truncation.js +139 -0
  137. package/src/agent/context-pack-truncation.ts +208 -0
  138. package/src/agent/context-pack-types.js +2 -0
  139. package/src/agent/context-pack-types.ts +98 -0
  140. package/src/agent/context-replay-policy.js +30 -39
  141. package/src/agent/context-replay-policy.ts +71 -0
  142. package/src/agent/critical-agent-policy.js +6 -10
  143. package/src/agent/critical-agent-policy.ts +20 -0
  144. package/src/agent/guidance-queue.js +74 -87
  145. package/src/agent/guidance-queue.ts +166 -0
  146. package/src/agent/output-extraction-error-detail.js +45 -0
  147. package/src/agent/output-extraction-error-detail.ts +49 -0
  148. package/src/agent/output-extraction-failures.js +125 -0
  149. package/src/agent/output-extraction-failures.ts +149 -0
  150. package/src/agent/output-extraction-json.js +234 -0
  151. package/src/agent/output-extraction-json.ts +235 -0
  152. package/src/agent/output-extraction-pi.js +124 -0
  153. package/src/agent/output-extraction-pi.ts +137 -0
  154. package/src/agent/output-extraction-types.js +2 -0
  155. package/src/agent/output-extraction-types.ts +49 -0
  156. package/src/agent/output-extraction.js +17 -617
  157. package/src/agent/output-extraction.ts +30 -0
  158. package/src/agent/output-reformatter-errors.js +83 -0
  159. package/src/agent/output-reformatter-errors.ts +106 -0
  160. package/src/agent/output-reformatter-types.js +2 -0
  161. package/src/agent/output-reformatter-types.ts +86 -0
  162. package/src/agent/output-reformatter.js +140 -185
  163. package/src/agent/output-reformatter.ts +238 -0
  164. package/src/agent/pi-terminal-lifecycle-types.js +2 -0
  165. package/src/agent/pi-terminal-lifecycle-types.ts +41 -0
  166. package/src/agent/pi-terminal-lifecycle.js +186 -206
  167. package/src/agent/pi-terminal-lifecycle.ts +297 -0
  168. package/src/agent/provider-control-plane.js +22 -21
  169. package/src/agent/provider-control-plane.ts +52 -0
  170. package/src/agent/provider-terminal-failure-types.js +2 -0
  171. package/src/agent/provider-terminal-failure-types.ts +60 -0
  172. package/src/agent/provider-terminal-failure.js +194 -191
  173. package/src/agent/provider-terminal-failure.ts +262 -0
  174. package/src/agent/rate-limit-backoff.js +31 -74
  175. package/src/agent/rate-limit-backoff.ts +71 -0
  176. package/src/agent/schema-utils.js +85 -125
  177. package/src/agent/schema-utils.ts +148 -0
  178. package/src/agent/structured-output-error.js +34 -33
  179. package/src/agent/structured-output-error.ts +105 -0
  180. package/src/agent/validation-platform.js +27 -27
  181. package/src/agent/validation-platform.ts +49 -0
  182. package/src/agents/git-pusher-template.js +17 -34
  183. package/src/attach/attach-client.js +232 -405
  184. package/src/attach/attach-client.ts +300 -0
  185. package/src/attach/attach-server-cleanup.js +43 -0
  186. package/src/attach/attach-server-cleanup.ts +39 -0
  187. package/src/attach/attach-server-clients.js +117 -0
  188. package/src/attach/attach-server-clients.ts +122 -0
  189. package/src/attach/attach-server-events.js +38 -0
  190. package/src/attach/attach-server-events.ts +36 -0
  191. package/src/attach/attach-server-pty.js +45 -0
  192. package/src/attach/attach-server-pty.ts +64 -0
  193. package/src/attach/attach-server-runtime.js +73 -0
  194. package/src/attach/attach-server-runtime.ts +62 -0
  195. package/src/attach/attach-server-socket.js +28 -0
  196. package/src/attach/attach-server-socket.ts +23 -0
  197. package/src/attach/attach-server-types.js +2 -0
  198. package/src/attach/attach-server-types.ts +82 -0
  199. package/src/attach/attach-server.js +106 -523
  200. package/src/attach/attach-server.ts +158 -0
  201. package/src/attach/index.js +18 -35
  202. package/src/attach/index.ts +18 -0
  203. package/src/attach/protocol.js +95 -184
  204. package/src/attach/protocol.ts +181 -0
  205. package/src/attach/ring-buffer.js +76 -102
  206. package/src/attach/ring-buffer.ts +109 -0
  207. package/src/attach/send-input.js +64 -75
  208. package/src/attach/send-input.ts +94 -0
  209. package/src/attach/socket-discovery.js +164 -218
  210. package/src/attach/socket-discovery.ts +230 -0
  211. package/src/attach/socket-paths.js +42 -61
  212. package/src/attach/socket-paths.ts +81 -0
  213. package/src/claude-credentials.js +51 -54
  214. package/src/claude-credentials.ts +89 -0
  215. package/src/cluster/generated/protocol-schema.ts +1 -1
  216. package/src/cluster/generated/protocol.ts +2 -1
  217. package/src/command-proofs.js +110 -141
  218. package/src/command-proofs.ts +216 -0
  219. package/src/config-router.js +81 -73
  220. package/src/config-router.ts +131 -0
  221. package/src/copy-containment.js +191 -0
  222. package/src/copy-containment.ts +259 -0
  223. package/src/copy-worker.js +53 -30
  224. package/src/copy-worker.ts +88 -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/isolation-manager.js +59 -20
  232. package/src/ledger-sequence.js +45 -48
  233. package/src/ledger-sequence.ts +63 -0
  234. package/src/legacy-lib/clusters-registry.ts +40 -0
  235. package/src/legacy-lib/completion.ts +148 -0
  236. package/src/legacy-lib/compose-utils.ts +102 -0
  237. package/src/legacy-lib/detached-startup.ts +451 -0
  238. package/src/legacy-lib/docker-config.ts +282 -0
  239. package/src/legacy-lib/docker-env-auth.ts +116 -0
  240. package/src/legacy-lib/git-remote-utils.ts +244 -0
  241. package/src/legacy-lib/id-detector.ts +57 -0
  242. package/src/legacy-lib/js-yaml.d.ts +3 -0
  243. package/src/legacy-lib/omelette.d.ts +15 -0
  244. package/src/legacy-lib/path-check.ts +89 -0
  245. package/src/legacy-lib/process-liveness.ts +36 -0
  246. package/src/legacy-lib/provider-credential-path.ts +57 -0
  247. package/src/legacy-lib/provider-defaults.ts +114 -0
  248. package/src/legacy-lib/provider-detection.ts +67 -0
  249. package/src/legacy-lib/provider-names.ts +126 -0
  250. package/src/legacy-lib/repo-settings.ts +88 -0
  251. package/src/legacy-lib/run-mode.ts +58 -0
  252. package/src/legacy-lib/run-plan.ts +49 -0
  253. package/src/legacy-lib/settings/claude-auth.ts +80 -0
  254. package/src/legacy-lib/settings-defaults.ts +212 -0
  255. package/src/legacy-lib/settings-error.ts +8 -0
  256. package/src/legacy-lib/settings-issue-providers.ts +18 -0
  257. package/src/legacy-lib/settings-models.ts +124 -0
  258. package/src/legacy-lib/settings-storage.ts +277 -0
  259. package/src/legacy-lib/settings-validation.ts +220 -0
  260. package/src/legacy-lib/settings.ts +87 -0
  261. package/src/legacy-lib/setup-apply-types.ts +70 -0
  262. package/src/legacy-lib/setup-apply.ts +456 -0
  263. package/src/legacy-lib/setup-journal.ts +185 -0
  264. package/src/legacy-lib/setup-plan-types.ts +96 -0
  265. package/src/legacy-lib/setup-plan.ts +500 -0
  266. package/src/legacy-lib/setup-undo.ts +142 -0
  267. package/src/legacy-lib/start-cluster-config.ts +124 -0
  268. package/src/legacy-lib/start-cluster-environment.ts +166 -0
  269. package/src/legacy-lib/start-cluster-run-options.ts +281 -0
  270. package/src/legacy-lib/start-cluster.ts +237 -0
  271. package/src/legacy-lib/stream-json-parser.ts +88 -0
  272. package/src/message-buffer.js +55 -71
  273. package/src/message-buffer.ts +107 -0
  274. package/src/message-bus-bridge.js +92 -133
  275. package/src/message-bus-bridge.ts +149 -0
  276. package/src/name-generator-shared.js +220 -0
  277. package/src/name-generator-shared.ts +231 -0
  278. package/src/name-generator.js +8 -217
  279. package/src/name-generator.ts +27 -0
  280. package/src/omp-blob-root.js +83 -64
  281. package/src/omp-blob-root.ts +116 -0
  282. package/src/omp-config-overlay.js +65 -38
  283. package/src/omp-config-overlay.ts +110 -0
  284. package/src/omp-execution-fingerprint.js +38 -43
  285. package/src/omp-execution-fingerprint.ts +92 -0
  286. package/src/omp-session-limits.js +10 -15
  287. package/src/omp-session-limits.ts +53 -0
  288. package/src/omp-session-partition.js +2 -2
  289. package/src/omp-session-verifier.js +1 -1
  290. package/src/orchestrator.js +32 -7
  291. package/src/pr-body-template.js +71 -0
  292. package/src/providers/anthropic/index.js +3 -1
  293. package/src/providers/anthropic/index.ts +13 -0
  294. package/src/providers/capabilities.js +19 -27
  295. package/src/providers/capabilities.ts +44 -0
  296. package/src/providers/google/index.js +3 -1
  297. package/src/providers/google/index.ts +13 -0
  298. package/src/providers/openai/index.js +3 -1
  299. package/src/providers/openai/index.ts +13 -0
  300. package/src/providers/opencode/index.js +3 -1
  301. package/src/providers/opencode/index.ts +13 -0
  302. package/src/quality-gates.js +67 -127
  303. package/src/quality-gates.ts +149 -0
  304. package/src/repo-settings-access.js +40 -0
  305. package/src/repo-settings-access.ts +64 -0
  306. package/src/schemas/sub-cluster.js +127 -183
  307. package/src/schemas/sub-cluster.ts +238 -0
  308. package/src/state-snapshot-normalization.js +224 -0
  309. package/src/state-snapshot-normalization.ts +222 -0
  310. package/src/state-snapshot.js +192 -359
  311. package/src/state-snapshot.ts +291 -0
  312. package/src/state-snapshotter.js +118 -130
  313. package/src/state-snapshotter.ts +167 -0
  314. package/src/sub-cluster-wrapper.js +4 -13
  315. package/src/task-run-model-args.js +87 -124
  316. package/src/task-run-model-args.ts +204 -0
  317. package/src/task-runner.js +5 -25
  318. package/src/task-runner.ts +28 -0
  319. package/src/task-startup-error.js +73 -58
  320. package/src/task-startup-error.ts +126 -0
  321. package/src/template-validation/consensus-gate-contracts.js +2 -0
  322. package/src/template-validation/consensus-gate-contracts.ts +70 -0
  323. package/src/template-validation/consensus-gate-scenarios.js +76 -0
  324. package/src/template-validation/consensus-gate-scenarios.ts +130 -0
  325. package/src/template-validation/consensus-gate-stage.js +34 -0
  326. package/src/template-validation/consensus-gate-stage.ts +56 -0
  327. package/src/template-validation/random-topology-contracts.js +2 -0
  328. package/src/template-validation/random-topology-contracts.ts +54 -0
  329. package/src/template-validation/random-topology-dispatch.js +25 -0
  330. package/src/template-validation/random-topology-dispatch.ts +33 -0
  331. package/src/template-validation/random-topology-message-loop.js +53 -0
  332. package/src/template-validation/random-topology-message-loop.ts +73 -0
  333. package/src/template-validation/random-topology-operations.js +79 -0
  334. package/src/template-validation/random-topology-operations.ts +101 -0
  335. package/src/template-validation/random-topology-runtime.js +25 -0
  336. package/src/template-validation/random-topology-runtime.ts +43 -0
  337. package/src/template-validation/random-topology-scenario.js +52 -0
  338. package/src/template-validation/random-topology-scenario.ts +57 -0
  339. package/src/template-validation/random-topology-schema.js +159 -0
  340. package/src/template-validation/random-topology-schema.ts +190 -0
  341. package/src/template-validation/random-topology-state.js +28 -0
  342. package/src/template-validation/random-topology-state.ts +30 -0
  343. package/src/template-validation/random-topology-trigger-evaluation.js +45 -0
  344. package/src/template-validation/random-topology-trigger-evaluation.ts +65 -0
  345. package/src/template-validation/random-topology-trigger.js +75 -0
  346. package/src/template-validation/random-topology-trigger.ts +88 -0
  347. package/src/template-validation/report-formatter.js +40 -46
  348. package/src/template-validation/report-formatter.ts +101 -0
  349. package/src/template-validation/simulate-consensus-gates.js +111 -302
  350. package/src/template-validation/simulate-consensus-gates.ts +196 -0
  351. package/src/template-validation/simulate-random-topology.js +26 -519
  352. package/src/template-validation/simulate-random-topology.ts +57 -0
  353. package/src/template-validation/simulate-two-stage-validation.js +41 -265
  354. package/src/template-validation/simulate-two-stage-validation.ts +55 -0
  355. package/src/template-validation/simulation-agent-runtime.js +25 -0
  356. package/src/template-validation/simulation-agent-runtime.ts +40 -0
  357. package/src/template-validation/simulation-agent.js +29 -0
  358. package/src/template-validation/simulation-agent.ts +73 -0
  359. package/src/template-validation/simulation-runtime.js +34 -0
  360. package/src/template-validation/simulation-runtime.ts +72 -0
  361. package/src/template-validation/two-stage-contracts.js +2 -0
  362. package/src/template-validation/two-stage-contracts.ts +41 -0
  363. package/src/template-validation/two-stage-inputs.js +21 -0
  364. package/src/template-validation/two-stage-inputs.ts +29 -0
  365. package/src/template-validation/two-stage-results.js +30 -0
  366. package/src/template-validation/two-stage-results.ts +40 -0
  367. package/src/template-validation/two-stage-scenario.js +86 -0
  368. package/src/template-validation/two-stage-scenario.ts +120 -0
  369. package/src/worktree-claude-config.js +182 -220
  370. package/src/worktree-claude-config.ts +272 -0
  371. package/src/worktree-tooling-env.js +103 -125
  372. package/src/worktree-tooling-env.ts +142 -0
  373. package/task-lib/completion.js +2 -3
  374. package/task-lib/completion.ts +9 -0
  375. package/task-lib/config.js +1 -4
  376. package/task-lib/config.ts +11 -0
  377. package/task-lib/name-generator.js +4 -226
  378. package/task-lib/name-generator.ts +11 -0
  379. package/task-lib/omp-session-cleanup.js +1 -1
  380. package/task-lib/omp-storage-root.js +8 -12
  381. package/task-lib/omp-storage-root.ts +62 -0
  382. package/task-lib/process-termination.js +143 -166
  383. 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
+ }