@ricsam/r5d-worker 0.0.137 → 0.0.139

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 (236) hide show
  1. package/README.md +4 -94
  2. package/dist/cjs/package.json +1 -1
  3. package/dist/mjs/main.mjs +46 -7543
  4. package/dist/mjs/package.json +1 -1
  5. package/dist/mjs/personal/action-scheduler.mjs +31 -0
  6. package/dist/mjs/personal/client.mjs +215 -0
  7. package/dist/mjs/personal/runtime.mjs +359 -0
  8. package/dist/mjs/personal/tcp.mjs +217 -0
  9. package/dist/mjs/runtime/adapter.mjs +42 -0
  10. package/dist/mjs/runtime/client.mjs +95 -0
  11. package/dist/mjs/runtime/daemon.mjs +131 -0
  12. package/dist/mjs/runtime/executor.mjs +436 -0
  13. package/dist/mjs/runtime/index.mjs +15 -0
  14. package/dist/mjs/runtime/protocol.mjs +139 -0
  15. package/dist/mjs/runtime/pty-host.mjs +156 -0
  16. package/dist/mjs/runtime/pty.mjs +127 -0
  17. package/dist/mjs/runtime/releases/artifacts.mjs +201 -0
  18. package/dist/mjs/runtime/releases/grants.mjs +93 -0
  19. package/dist/mjs/runtime/releases/main.mjs +101 -0
  20. package/dist/mjs/runtime/releases/manager.mjs +541 -0
  21. package/dist/mjs/runtime/releases/process.mjs +88 -0
  22. package/dist/mjs/runtime/releases/retirement.mjs +18 -0
  23. package/dist/mjs/runtime/releases/rpc-client.mjs +184 -0
  24. package/dist/mjs/runtime/releases/rpc-main.mjs +213 -0
  25. package/dist/mjs/runtime/releases/rpc-protocol.mjs +103 -0
  26. package/dist/mjs/runtime/storage.mjs +156 -0
  27. package/dist/mjs/runtime/workspace/artifacts.mjs +121 -0
  28. package/dist/mjs/runtime/workspace/authority.mjs +1355 -0
  29. package/dist/mjs/runtime/workspace/contracts.mjs +60 -0
  30. package/dist/mjs/runtime/workspace/file-write.mjs +23 -0
  31. package/dist/mjs/runtime/workspace/files.mjs +308 -0
  32. package/dist/mjs/runtime/workspace/http-body.mjs +45 -0
  33. package/dist/mjs/runtime/workspace/storage-client.mjs +59 -0
  34. package/dist/mjs/runtime/workspace/storage-wire.mjs +53 -0
  35. package/dist/types/main.d.ts +0 -1167
  36. package/dist/types/personal/action-scheduler.d.ts +23 -0
  37. package/dist/types/personal/client.d.ts +14 -0
  38. package/dist/types/personal/runtime.d.ts +121 -0
  39. package/dist/types/personal/tcp.d.ts +14 -0
  40. package/dist/types/runtime/client.d.ts +3 -1
  41. package/dist/types/runtime/executor.d.ts +6 -1
  42. package/dist/types/runtime/protocol.d.ts +176 -4
  43. package/dist/types/runtime/pty.d.ts +1 -0
  44. package/dist/types/runtime/releases/artifacts.d.ts +51 -0
  45. package/dist/types/runtime/releases/grants.d.ts +16 -0
  46. package/dist/types/runtime/releases/manager.d.ts +145 -0
  47. package/dist/types/runtime/releases/process.d.ts +14 -0
  48. package/dist/types/runtime/releases/retirement.d.ts +15 -0
  49. package/dist/types/runtime/releases/rpc-client.d.ts +52 -0
  50. package/dist/types/runtime/releases/rpc-main.d.ts +10 -0
  51. package/dist/types/runtime/releases/rpc-protocol.d.ts +1139 -0
  52. package/dist/types/runtime/workspace/artifacts.d.ts +30 -0
  53. package/dist/types/runtime/workspace/authority.d.ts +338 -0
  54. package/dist/types/runtime/workspace/contracts.d.ts +86 -0
  55. package/dist/types/runtime/workspace/file-write.d.ts +14 -0
  56. package/dist/types/runtime/workspace/files.d.ts +21 -0
  57. package/dist/types/runtime/workspace/http-body.d.ts +9 -0
  58. package/dist/types/runtime/workspace/storage-client.d.ts +23 -0
  59. package/dist/types/runtime/workspace/storage-wire.d.ts +138 -0
  60. package/package.json +8 -6
  61. package/dist/cjs/atomic-rename.cjs +0 -303
  62. package/dist/cjs/browser-upload.cjs +0 -73
  63. package/dist/cjs/cli-update.cjs +0 -108
  64. package/dist/cjs/command-launcher.cjs +0 -423
  65. package/dist/cjs/control-command-policy.cjs +0 -177
  66. package/dist/cjs/git-blob-hash.cjs +0 -41
  67. package/dist/cjs/git-identity.cjs +0 -39
  68. package/dist/cjs/git-process-environment.cjs +0 -133
  69. package/dist/cjs/inline-output.cjs +0 -62
  70. package/dist/cjs/main.cjs +0 -7517
  71. package/dist/cjs/managed-paths.cjs +0 -215
  72. package/dist/cjs/plan-lint.cjs +0 -165
  73. package/dist/cjs/plan-parser.cjs +0 -127
  74. package/dist/cjs/plan-paths.cjs +0 -53
  75. package/dist/cjs/port-forward-client.cjs +0 -206
  76. package/dist/cjs/process-tree.cjs +0 -117
  77. package/dist/cjs/project-checkout-garbage.cjs +0 -219
  78. package/dist/cjs/project-mirror-fetch-policy.cjs +0 -82
  79. package/dist/cjs/project-mirror-refs-token.cjs +0 -85
  80. package/dist/cjs/project-workspace-state.cjs +0 -972
  81. package/dist/cjs/project-worktrees.cjs +0 -1713
  82. package/dist/cjs/pty-output-coalescer.cjs +0 -87
  83. package/dist/cjs/recovery-journal-protocol.cjs +0 -56
  84. package/dist/cjs/recovery-journal-runtime.cjs +0 -133
  85. package/dist/cjs/recovery-journal-thread.cjs +0 -9
  86. package/dist/cjs/recovery-journal.cjs +0 -742
  87. package/dist/cjs/recovery-output-window.cjs +0 -77
  88. package/dist/cjs/recovery-protocol.cjs +0 -31
  89. package/dist/cjs/recovery-store.cjs +0 -252
  90. package/dist/cjs/registry-auth.cjs +0 -310
  91. package/dist/cjs/repository-transition-policy.cjs +0 -49
  92. package/dist/cjs/runtime-version.cjs +0 -88
  93. package/dist/cjs/session-file-mutations.cjs +0 -61
  94. package/dist/cjs/supervisor.cjs +0 -147
  95. package/dist/cjs/three-way-merge.cjs +0 -346
  96. package/dist/cjs/working-tree-mirror.cjs +0 -1872
  97. package/dist/cjs/workspace-automatic-sync-policy.cjs +0 -79
  98. package/dist/cjs/workspace-branch-incarnation-policy.cjs +0 -37
  99. package/dist/cjs/workspace-command-sync-policy.cjs +0 -112
  100. package/dist/cjs/workspace-command-targets.cjs +0 -63
  101. package/dist/cjs/workspace-filesystem-executor-thread.cjs +0 -36
  102. package/dist/cjs/workspace-filesystem-executor.cjs +0 -327
  103. package/dist/cjs/workspace-filesystem-job-types.cjs +0 -144
  104. package/dist/cjs/workspace-filesystem-jobs.cjs +0 -59
  105. package/dist/cjs/workspace-git-sync.cjs +0 -3595
  106. package/dist/cjs/workspace-hydration-ledger.cjs +0 -83
  107. package/dist/cjs/workspace-hydration-merge.cjs +0 -448
  108. package/dist/cjs/workspace-hydration-recovery-state.cjs +0 -53
  109. package/dist/cjs/workspace-incident-state.cjs +0 -37
  110. package/dist/cjs/workspace-merge-projection.cjs +0 -628
  111. package/dist/cjs/workspace-mount-boundary.cjs +0 -71
  112. package/dist/cjs/workspace-mount-hold-fence.cjs +0 -120
  113. package/dist/cjs/workspace-mutation-gate.cjs +0 -127
  114. package/dist/cjs/workspace-path-move.cjs +0 -195
  115. package/dist/cjs/workspace-preserve-only-policy.cjs +0 -36
  116. package/dist/cjs/workspace-project-config-policy.cjs +0 -53
  117. package/dist/cjs/workspace-projection-ledger.cjs +0 -81
  118. package/dist/cjs/workspace-publication-evidence.cjs +0 -46
  119. package/dist/cjs/workspace-sync-coalescer.cjs +0 -58
  120. package/dist/mjs/atomic-rename.mjs +0 -261
  121. package/dist/mjs/browser-upload.mjs +0 -42
  122. package/dist/mjs/command-launcher.mjs +0 -393
  123. package/dist/mjs/control-command-policy.mjs +0 -146
  124. package/dist/mjs/git-blob-hash.mjs +0 -16
  125. package/dist/mjs/git-identity.mjs +0 -15
  126. package/dist/mjs/git-process-environment.mjs +0 -105
  127. package/dist/mjs/inline-output.mjs +0 -37
  128. package/dist/mjs/managed-paths.mjs +0 -174
  129. package/dist/mjs/plan-lint.mjs +0 -129
  130. package/dist/mjs/plan-parser.mjs +0 -94
  131. package/dist/mjs/plan-paths.mjs +0 -25
  132. package/dist/mjs/port-forward-client.mjs +0 -172
  133. package/dist/mjs/project-checkout-garbage.mjs +0 -178
  134. package/dist/mjs/project-mirror-fetch-policy.mjs +0 -56
  135. package/dist/mjs/project-mirror-refs-token.mjs +0 -53
  136. package/dist/mjs/project-workspace-state.mjs +0 -937
  137. package/dist/mjs/project-worktrees.mjs +0 -1657
  138. package/dist/mjs/pty-output-coalescer.mjs +0 -61
  139. package/dist/mjs/recovery-journal-protocol.mjs +0 -30
  140. package/dist/mjs/recovery-journal-runtime.mjs +0 -112
  141. package/dist/mjs/recovery-journal-thread.mjs +0 -8
  142. package/dist/mjs/recovery-journal.mjs +0 -694
  143. package/dist/mjs/recovery-output-window.mjs +0 -53
  144. package/dist/mjs/recovery-protocol.mjs +0 -6
  145. package/dist/mjs/recovery-store.mjs +0 -217
  146. package/dist/mjs/registry-auth.mjs +0 -269
  147. package/dist/mjs/repository-transition-policy.mjs +0 -22
  148. package/dist/mjs/runtime-version.mjs +0 -54
  149. package/dist/mjs/session-file-mutations.mjs +0 -37
  150. package/dist/mjs/supervisor.mjs +0 -114
  151. package/dist/mjs/three-way-merge.mjs +0 -318
  152. package/dist/mjs/working-tree-mirror.mjs +0 -1817
  153. package/dist/mjs/workspace-automatic-sync-policy.mjs +0 -48
  154. package/dist/mjs/workspace-branch-incarnation-policy.mjs +0 -13
  155. package/dist/mjs/workspace-command-sync-policy.mjs +0 -84
  156. package/dist/mjs/workspace-command-targets.mjs +0 -37
  157. package/dist/mjs/workspace-filesystem-executor-thread.mjs +0 -38
  158. package/dist/mjs/workspace-filesystem-executor.mjs +0 -287
  159. package/dist/mjs/workspace-filesystem-job-types.mjs +0 -116
  160. package/dist/mjs/workspace-filesystem-jobs.mjs +0 -55
  161. package/dist/mjs/workspace-git-sync.mjs +0 -3553
  162. package/dist/mjs/workspace-hydration-ledger.mjs +0 -58
  163. package/dist/mjs/workspace-hydration-merge.mjs +0 -414
  164. package/dist/mjs/workspace-hydration-recovery-state.mjs +0 -29
  165. package/dist/mjs/workspace-incident-state.mjs +0 -12
  166. package/dist/mjs/workspace-merge-projection.mjs +0 -594
  167. package/dist/mjs/workspace-mount-boundary.mjs +0 -37
  168. package/dist/mjs/workspace-mount-hold-fence.mjs +0 -95
  169. package/dist/mjs/workspace-mutation-gate.mjs +0 -103
  170. package/dist/mjs/workspace-path-move.mjs +0 -160
  171. package/dist/mjs/workspace-preserve-only-policy.mjs +0 -11
  172. package/dist/mjs/workspace-project-config-policy.mjs +0 -27
  173. package/dist/mjs/workspace-projection-ledger.mjs +0 -57
  174. package/dist/mjs/workspace-publication-evidence.mjs +0 -21
  175. package/dist/mjs/workspace-sync-coalescer.mjs +0 -34
  176. package/dist/types/atomic-rename.d.ts +0 -78
  177. package/dist/types/browser-upload.d.ts +0 -7
  178. package/dist/types/command-launcher.d.ts +0 -104
  179. package/dist/types/control-command-policy.d.ts +0 -53
  180. package/dist/types/git-blob-hash.d.ts +0 -10
  181. package/dist/types/git-identity.d.ts +0 -9
  182. package/dist/types/git-process-environment.d.ts +0 -9
  183. package/dist/types/inline-output.d.ts +0 -3
  184. package/dist/types/managed-paths.d.ts +0 -19
  185. package/dist/types/plan-lint.d.ts +0 -16
  186. package/dist/types/plan-parser.d.ts +0 -38
  187. package/dist/types/plan-paths.d.ts +0 -36
  188. package/dist/types/port-forward-client.d.ts +0 -8
  189. package/dist/types/project-checkout-garbage.d.ts +0 -86
  190. package/dist/types/project-mirror-fetch-policy.d.ts +0 -84
  191. package/dist/types/project-mirror-refs-token.d.ts +0 -53
  192. package/dist/types/project-workspace-state.d.ts +0 -188
  193. package/dist/types/project-worktrees.d.ts +0 -397
  194. package/dist/types/pty-output-coalescer.d.ts +0 -20
  195. package/dist/types/recovery-journal-protocol.d.ts +0 -35
  196. package/dist/types/recovery-journal-runtime.d.ts +0 -12
  197. package/dist/types/recovery-journal-stall-fixture.d.ts +0 -1
  198. package/dist/types/recovery-journal-thread.d.ts +0 -1
  199. package/dist/types/recovery-journal.d.ts +0 -248
  200. package/dist/types/recovery-output-window.d.ts +0 -19
  201. package/dist/types/recovery-protocol.d.ts +0 -59
  202. package/dist/types/recovery-store.d.ts +0 -52
  203. package/dist/types/registry-auth.d.ts +0 -47
  204. package/dist/types/repository-transition-policy.d.ts +0 -26
  205. package/dist/types/runtime-version.d.ts +0 -1
  206. package/dist/types/session-file-mutations.d.ts +0 -22
  207. package/dist/types/supervisor-daemonized-fixture.d.ts +0 -1
  208. package/dist/types/supervisor-launcher-cleanup-fixture.d.ts +0 -1
  209. package/dist/types/supervisor-signal-fixture.d.ts +0 -1
  210. package/dist/types/supervisor.d.ts +0 -23
  211. package/dist/types/three-way-merge.d.ts +0 -77
  212. package/dist/types/working-tree-mirror.d.ts +0 -424
  213. package/dist/types/workspace-automatic-sync-policy.d.ts +0 -57
  214. package/dist/types/workspace-branch-incarnation-policy.d.ts +0 -14
  215. package/dist/types/workspace-command-sync-policy.d.ts +0 -57
  216. package/dist/types/workspace-command-targets.d.ts +0 -37
  217. package/dist/types/workspace-filesystem-executor-thread.d.ts +0 -1
  218. package/dist/types/workspace-filesystem-executor.d.ts +0 -123
  219. package/dist/types/workspace-filesystem-job-types.d.ts +0 -302
  220. package/dist/types/workspace-filesystem-jobs.d.ts +0 -7
  221. package/dist/types/workspace-git-sync.d.ts +0 -514
  222. package/dist/types/workspace-hydration-ledger.d.ts +0 -76
  223. package/dist/types/workspace-hydration-merge.d.ts +0 -103
  224. package/dist/types/workspace-hydration-recovery-state.d.ts +0 -10
  225. package/dist/types/workspace-incident-state.d.ts +0 -19
  226. package/dist/types/workspace-merge-projection.d.ts +0 -69
  227. package/dist/types/workspace-mount-boundary.d.ts +0 -10
  228. package/dist/types/workspace-mount-hold-fence.d.ts +0 -42
  229. package/dist/types/workspace-mutation-gate.d.ts +0 -42
  230. package/dist/types/workspace-path-move.d.ts +0 -21
  231. package/dist/types/workspace-preserve-only-policy.d.ts +0 -15
  232. package/dist/types/workspace-project-config-policy.d.ts +0 -48
  233. package/dist/types/workspace-projection-ledger.d.ts +0 -59
  234. package/dist/types/workspace-publication-evidence.d.ts +0 -17
  235. package/dist/types/workspace-sync-coalescer.d.ts +0 -28
  236. /package/dist/types/{credential-authority-lock-fixture.d.ts → runtime/releases/main.d.ts} +0 -0
@@ -0,0 +1,156 @@
1
+ // node-pty's Unix FD integration needs Node, not Bun. This helper belongs to the
2
+ // keeper lifetime, never the adapter. Its IPC is inherited anonymous pipes only.
3
+ import process from "node:process";
4
+ import { Buffer } from "node:buffer";
5
+ import { write, constants, openSync, closeSync, lstatSync, fstatSync, fchmodSync, readSync, realpathSync } from "node:fs";
6
+ import { createRequire } from "node:module";
7
+ import path from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ import { setTimeout as delay } from "node:timers/promises";
10
+ import pty from "node-pty";
11
+ import readline from "node:readline";
12
+
13
+ /** node-pty 1.1.0 ships Darwin's spawn-helper without executable permission. */
14
+ export function ensureDarwinSpawnHelperPermissions({ platform = process.platform, arch = process.arch, currentUid = process.getuid?.(), requirePty = createRequire(import.meta.url) } = {}) {
15
+ if (platform !== "darwin") return false;
16
+ if (!["x64", "arm64"].includes(arch) || !Number.isSafeInteger(currentUid)) throw new Error("pty_native_helper_unavailable");
17
+ const manifest = requirePty("node-pty/package.json");
18
+ if (manifest.name !== "node-pty" || manifest.version !== "1.1.0") throw new Error("pty_native_helper_version");
19
+ const packageRoot = path.dirname(requirePty.resolve("node-pty/package.json"));
20
+ const library = path.dirname(requirePty.resolve("node-pty/lib/unixTerminal.js"));
21
+ const selected = requirePty("node-pty/lib/utils.js").loadNativeModule("pty");
22
+ const directory = path.resolve(library, selected.dir);
23
+ const allowed = ["build/Release", "build/Debug", `prebuilds/darwin-${arch}`].flatMap(relative => [path.join(packageRoot, relative), path.join(packageRoot, "lib", relative)]);
24
+ if (!allowed.includes(directory)) throw new Error("pty_native_helper_path");
25
+ const filename = path.join(directory, "spawn-helper");
26
+ let ancestor = path.parse(filename).root;
27
+ for (const segment of path.dirname(filename).slice(ancestor.length).split(path.sep).filter(Boolean)) {
28
+ ancestor = path.join(ancestor, segment);
29
+ const info = lstatSync(ancestor);
30
+ if (!info.isDirectory() || info.isSymbolicLink() || ![0, currentUid].includes(info.uid) || ((info.mode & 0o022) && !(info.mode & 0o1000))) throw new Error("pty_native_helper_unsafe");
31
+ }
32
+ const fd = openSync(filename, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
33
+ try {
34
+ const before = fstatSync(fd);
35
+ if (!before.isFile() || before.nlink !== 1 || ![0, currentUid].includes(before.uid) || (before.mode & 0o7022) || before.size < 1 || before.size > 1024 * 1024) throw new Error("pty_native_helper_unsafe");
36
+ const target = before.mode | ((before.mode & 0o444) >> 2);
37
+ if (!(target & 0o111)) throw new Error("pty_native_helper_not_executable");
38
+ if (target === before.mode) return false;
39
+ if (before.uid !== currentUid) throw new Error("pty_native_helper_not_owned");
40
+ const bytes = Buffer.alloc(before.size);
41
+ if (readSync(fd, bytes, 0, bytes.length, 0) !== bytes.length) throw new Error("pty_native_helper_changed");
42
+ const named = lstatSync(filename);
43
+ if (named.dev !== before.dev || named.ino !== before.ino || named.isSymbolicLink()) throw new Error("pty_native_helper_changed");
44
+ fchmodSync(fd, target & 0o777);
45
+ const after = fstatSync(fd), sameName = lstatSync(filename), sameBytes = Buffer.alloc(before.size);
46
+ if (after.dev !== before.dev || after.ino !== before.ino || after.uid !== before.uid || after.nlink !== 1 || after.size !== before.size || after.mtimeMs !== before.mtimeMs || after.mode !== target || sameName.dev !== before.dev || sameName.ino !== before.ino || readSync(fd, sameBytes, 0, sameBytes.length, 0) !== sameBytes.length || !bytes.equals(sameBytes)) throw new Error("pty_native_helper_changed");
47
+ return true;
48
+ } finally { closeSync(fd); }
49
+ }
50
+
51
+ function startPtyHost() {
52
+ try { ensureDarwinSpawnHelperPermissions(); }
53
+ catch {
54
+ process.stderr.write("pty_native_helper_unavailable: use a supported, user-owned node-pty 1.1.0 installation with executable spawn-helper\n");
55
+ process.exit(78);
56
+ }
57
+ if (process.argv[2] === "--preflight") { process.stdout.write("pty-preflight-ok\n"); return; }
58
+
59
+ let terminal;
60
+ let initialized = false;
61
+ let terminalExited = false;
62
+ const send = (message) => process.stdout.write(JSON.stringify(message) + "\n");
63
+ const input = readline.createInterface({ input: process.stdin });
64
+ let serial = Promise.resolve();
65
+ input.on("line", (line) => {
66
+ serial = serial.then(async () => {
67
+ try {
68
+ const message = JSON.parse(line);
69
+ if (!initialized) {
70
+ if (message.method !== "start") throw new Error("Expected initialization");
71
+ initialized = true;
72
+ terminal = pty.spawn(message.argv[0], message.argv.slice(1), {
73
+ cwd: message.cwd,
74
+ env: message.env,
75
+ name: "xterm-256color",
76
+ cols: message.cols,
77
+ rows: message.rows,
78
+ encoding: null,
79
+ });
80
+ // node-pty 1.1.x UnixTerminal exposes fd. Fail closed if this pinned native
81
+ // contract changes; its public write() only enqueues and cannot ACK delivery.
82
+ if (!Number.isSafeInteger(terminal.fd) || terminal.fd < 0) throw new Error("PTY descriptor unavailable");
83
+ send({ event: "ready", pid: terminal.pid });
84
+ terminal.onData((data) => {
85
+ if (!Buffer.isBuffer(data)) {
86
+ shutdown();
87
+ return;
88
+ }
89
+ if (!send({ event: "data", data: data.toString("base64") })) terminal.pause();
90
+ });
91
+ process.stdout.on("drain", () => {
92
+ if (!terminalExited) terminal.resume();
93
+ });
94
+ terminal.onExit(({ exitCode, signal }) => {
95
+ terminalExited = true;
96
+ input.close();
97
+ process.stdin.destroy();
98
+ // Flush all queued output and the final event before process exit.
99
+ process.stdout.end(JSON.stringify({ event: "exit", exitCode, signal }) + "\n", () => process.exit(0));
100
+ });
101
+ return;
102
+ }
103
+ if (terminalExited) return;
104
+ try {
105
+ if (message.method === "write") await writeTerminal(message.data);
106
+ else if (message.method === "resize") terminal.resize(message.cols, message.rows);
107
+ else throw new Error("Unknown method");
108
+ if (!terminalExited) send({ event: "ack", id: message.id });
109
+ } catch {
110
+ if (!terminalExited) send({ event: "action-error", id: message.id });
111
+ }
112
+ } catch {
113
+ shutdown();
114
+ }
115
+ });
116
+ });
117
+ async function writeTerminal(data) {
118
+ const buffer = Buffer.from(data, "utf8");
119
+ let offset = 0;
120
+ const deadline = Date.now() + 1500;
121
+ while (offset < buffer.length) {
122
+ if (terminalExited || Date.now() > deadline) throw new Error("Unknown input outcome");
123
+ try {
124
+ offset += await new Promise((resolve, reject) =>
125
+ write(terminal.fd, buffer, offset, buffer.length - offset, (error, count) => (error ? reject(error) : resolve(count))),
126
+ );
127
+ } catch (error) {
128
+ if (error.code !== "EAGAIN" && error.code !== "EINTR") throw error;
129
+ await delay(5);
130
+ }
131
+ }
132
+ }
133
+ function shutdown() {
134
+ if (terminal && !terminalExited) {
135
+ try {
136
+ process.kill(-terminal.pid, "SIGKILL");
137
+ } catch {
138
+ /* Already gone. */
139
+ }
140
+ try {
141
+ terminal.kill("SIGKILL");
142
+ } catch {
143
+ /* Already gone. */
144
+ }
145
+ }
146
+ process.exit(1);
147
+ }
148
+ input.on("close", () => {
149
+ if (!terminalExited) shutdown();
150
+ });
151
+ process.on("SIGTERM", shutdown);
152
+ process.on("SIGINT", shutdown);
153
+ process.stdout.on("error", shutdown);
154
+ }
155
+
156
+ if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) startPtyHost();
@@ -0,0 +1,127 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { fileURLToPath } from "node:url";
3
+ import { ExecutorError } from "./protocol.mjs";
4
+ const nodeExecutable = typeof R5D_PACKAGED_NODE !== "undefined" && R5D_PACKAGED_NODE ? fileURLToPath(new URL("./bin/node", import.meta.url)) : Bun.which("node") ?? "node";
5
+ function preflightNativePty() {
6
+ if (process.platform !== "darwin") return;
7
+ const result = spawnSync(nodeExecutable, [fileURLToPath(new URL("./pty-host.mjs", import.meta.url)), "--preflight"], { encoding: "utf8", timeout: 5e3, env: { PATH: "/usr/local/bin:/usr/bin:/bin" } });
8
+ if (result.status !== 0 || typeof result.stdout !== "string" || result.stdout.trim() !== "pty-preflight-ok") throw new ExecutorError("pty_native_helper_unavailable", "Native PTY helper is unavailable; use a supported, user-owned node-pty 1.1.0 installation with executable spawn-helper");
9
+ }
10
+ class PtySession {
11
+ pid = 0;
12
+ sequence = 0;
13
+ pending = /* @__PURE__ */ new Map();
14
+ child;
15
+ onDataCallback;
16
+ onExitCallback;
17
+ buffered = [];
18
+ exit;
19
+ ready;
20
+ constructor(input) {
21
+ this.child = spawn(nodeExecutable, [fileURLToPath(new URL("./pty-host.mjs", import.meta.url))], {
22
+ env: { PATH: "/usr/local/bin:/usr/bin:/bin", HOME: input.cwd },
23
+ stdio: "pipe"
24
+ });
25
+ this.ready = new Promise((resolve, reject) => {
26
+ const timer = setTimeout(() => {
27
+ this.child.kill("SIGKILL");
28
+ reject(new ExecutorError("pty_start_timeout"));
29
+ }, 5e3);
30
+ let buffer = "";
31
+ const fail = () => {
32
+ clearTimeout(timer);
33
+ reject(new ExecutorError("pty_helper_failed"));
34
+ for (const pending of this.pending.values()) pending.reject(new ExecutorError("pty_helper_failed"));
35
+ this.pending.clear();
36
+ this.deliverExit({ exitCode: -1, signal: 0 });
37
+ };
38
+ this.child.on("error", fail);
39
+ this.child.stdin.on("error", fail);
40
+ this.child.stdout.setEncoding("utf8");
41
+ this.child.stdout.on("data", (data) => {
42
+ buffer += data;
43
+ if (buffer.length > 1024 * 1024) {
44
+ this.child.kill("SIGKILL");
45
+ fail();
46
+ return;
47
+ }
48
+ while (buffer.includes("\n")) {
49
+ const n = buffer.indexOf("\n");
50
+ const event = JSON.parse(buffer.slice(0, n));
51
+ buffer = buffer.slice(n + 1);
52
+ if (event.event === "ready") {
53
+ this.pid = event.pid;
54
+ clearTimeout(timer);
55
+ resolve();
56
+ } else if (event.event === "data") {
57
+ const bytes = Buffer.from(event.data, "base64");
58
+ if (this.onDataCallback) this.onDataCallback(bytes);
59
+ else this.buffered.push(bytes);
60
+ } else if (event.event === "exit") this.deliverExit({ exitCode: event.exitCode, signal: event.signal ?? 0 });
61
+ else if (event.event === "ack") {
62
+ this.pending.get(event.id)?.resolve();
63
+ this.pending.delete(event.id);
64
+ } else if (event.event === "action-error") {
65
+ this.pending.get(event.id)?.reject(new ExecutorError("pty_write_unknown"));
66
+ this.pending.delete(event.id);
67
+ } else fail();
68
+ }
69
+ });
70
+ this.child.once("close", () => {
71
+ if (!this.exit) fail();
72
+ });
73
+ this.child.stdin.write(JSON.stringify({ method: "start", ...input }) + "\n");
74
+ });
75
+ }
76
+ deliverExit(event) {
77
+ if (this.exit) return;
78
+ this.exit = event;
79
+ this.onExitCallback?.(event);
80
+ }
81
+ onData(callback) {
82
+ this.onDataCallback = callback;
83
+ for (const data of this.buffered.splice(0)) callback(data);
84
+ }
85
+ onExit(callback) {
86
+ this.onExitCallback = callback;
87
+ if (this.exit) callback(this.exit);
88
+ }
89
+ request(message) {
90
+ if (this.exit) return Promise.reject(new ExecutorError("pty_exited"));
91
+ const id = ++this.sequence;
92
+ return new Promise((resolve, reject) => {
93
+ const timer = setTimeout(() => {
94
+ this.pending.delete(id);
95
+ reject(new ExecutorError("pty_write_unknown"));
96
+ }, 2e3);
97
+ this.pending.set(id, {
98
+ resolve: () => {
99
+ clearTimeout(timer);
100
+ resolve();
101
+ },
102
+ reject: (error) => {
103
+ clearTimeout(timer);
104
+ reject(error);
105
+ }
106
+ });
107
+ this.child.stdin.write(JSON.stringify({ id, ...message }) + "\n");
108
+ });
109
+ }
110
+ write(data) {
111
+ return this.request({ method: "write", data });
112
+ }
113
+ resize(cols, rows) {
114
+ return this.request({ method: "resize", cols, rows });
115
+ }
116
+ kill(signal = "SIGTERM") {
117
+ if (!Number.isSafeInteger(this.pid) || this.pid <= 1 || this.exit) return;
118
+ try {
119
+ process.kill(-this.pid, signal);
120
+ } catch {
121
+ }
122
+ }
123
+ }
124
+ export {
125
+ PtySession,
126
+ preflightNativePty
127
+ };
@@ -0,0 +1,201 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import {
3
+ chmodSync,
4
+ closeSync,
5
+ constants,
6
+ existsSync,
7
+ fstatSync,
8
+ fsyncSync,
9
+ lstatSync,
10
+ mkdirSync,
11
+ openSync,
12
+ readFileSync,
13
+ readdirSync,
14
+ realpathSync,
15
+ renameSync,
16
+ rmSync,
17
+ writeFileSync
18
+ } from "node:fs";
19
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
20
+ import { z } from "zod";
21
+ import { RuntimeHello, RuntimeId } from "@ricsam/r5d-api/runtime-protocol";
22
+ import { ExecutorError } from "../protocol.mjs";
23
+ import { privateDirectory } from "../storage.mjs";
24
+ import { digest } from "./grants.mjs";
25
+ const sha256 = z.string().regex(/^[a-f0-9]{64}$/);
26
+ const memberPath = z.string().min(1).max(512).refine(
27
+ (path) => path.split("/").every((part) => /^[a-zA-Z0-9_.-]+$/.test(part) && part !== "." && part !== ".."),
28
+ "Unsafe artifact member"
29
+ );
30
+ const AdapterManifest = z.object({
31
+ version: z.literal(1),
32
+ installationId: RuntimeId,
33
+ workerId: RuntimeId,
34
+ hello: RuntimeHello,
35
+ entrypoint: memberPath,
36
+ files: z.array(
37
+ z.object({
38
+ path: memberPath,
39
+ sha256,
40
+ size: z.number().int().min(0).max(64 * 1024 * 1024),
41
+ executable: z.boolean()
42
+ }).strict()
43
+ ).min(1).max(4096)
44
+ }).strict().superRefine((manifest, ctx) => {
45
+ if (manifest.hello.installationId !== manifest.installationId || manifest.hello.role !== "worker-adapter" || new Set(manifest.files.map((file) => file.path)).size !== manifest.files.length || !manifest.files.some((file) => file.path === manifest.entrypoint) || manifest.files.reduce((total, file) => total + file.size, 0) > 256 * 1024 * 1024)
46
+ ctx.addIssue({ code: "custom", message: "Invalid adapter inventory or identity" });
47
+ });
48
+ const bytesHash = (bytes) => createHash("sha256").update(bytes).digest("hex");
49
+ function within(root, path) {
50
+ const rel = relative(root, path);
51
+ return rel === "" || !isAbsolute(rel) && rel !== ".." && !rel.startsWith("../");
52
+ }
53
+ function canonicalDirectory(path) {
54
+ if (!isAbsolute(path) || resolve(path) !== path) throw new ExecutorError("unsafe_release_path");
55
+ let cursor = path;
56
+ while (true) {
57
+ const stat = lstatSync(cursor);
58
+ if (!stat.isDirectory() || stat.isSymbolicLink() || (stat.mode & 18) !== 0 && !(stat.mode & 512))
59
+ throw new ExecutorError("unsafe_release_path");
60
+ if (cursor === dirname(cursor)) break;
61
+ cursor = dirname(cursor);
62
+ }
63
+ if (realpathSync(path) !== path) throw new ExecutorError("unsafe_release_path");
64
+ return path;
65
+ }
66
+ function outsideWorkspaces(path, workspaceRoots) {
67
+ canonicalDirectory(path);
68
+ for (const root of workspaceRoots) {
69
+ const workspace = realpathSync(root);
70
+ if (within(workspace, path) || within(path, workspace)) throw new ExecutorError("release_inside_workspace");
71
+ }
72
+ }
73
+ function syncDirectory(path) {
74
+ const fd = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
75
+ try {
76
+ fsyncSync(fd);
77
+ } finally {
78
+ closeSync(fd);
79
+ }
80
+ }
81
+ function readMember(path, immutable) {
82
+ const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
83
+ try {
84
+ const stat = fstatSync(fd);
85
+ if (!stat.isFile() || stat.nlink !== 1 || stat.uid !== process.getuid?.() || stat.size > 64 * 1024 * 1024 || stat.mode & (immutable ? 191 : 18))
86
+ throw new ExecutorError("unsafe_artifact_member");
87
+ return readFileSync(fd);
88
+ } finally {
89
+ closeSync(fd);
90
+ }
91
+ }
92
+ function inventory(root, immutable) {
93
+ const files = [];
94
+ let entries = 0;
95
+ function walk(dir) {
96
+ const directory = lstatSync(dir);
97
+ if (immutable && (directory.uid !== process.getuid?.() || (directory.mode & 511) !== 320))
98
+ throw new ExecutorError("artifact_mode_mismatch");
99
+ if (++entries > 8192) throw new ExecutorError("artifact_inventory_mismatch");
100
+ if (files.length > 4096) throw new ExecutorError("artifact_inventory_mismatch");
101
+ for (const name of readdirSync(dir)) {
102
+ const path = join(dir, name), stat = lstatSync(path);
103
+ if (stat.isSymbolicLink() || !stat.isDirectory() && !stat.isFile()) throw new ExecutorError("unsafe_artifact_member");
104
+ if (stat.isDirectory()) {
105
+ canonicalDirectory(path);
106
+ walk(path);
107
+ } else files.push(relative(root, path));
108
+ }
109
+ }
110
+ walk(root);
111
+ return files.sort();
112
+ }
113
+ function verifyTree(root, manifest, immutable) {
114
+ canonicalDirectory(root);
115
+ if (JSON.stringify(inventory(root, immutable)) !== JSON.stringify(manifest.files.map((file) => file.path).sort()))
116
+ throw new ExecutorError("artifact_inventory_mismatch");
117
+ for (const file of manifest.files) {
118
+ const bytes = readMember(join(root, file.path), immutable);
119
+ if (bytes.length !== file.size || bytesHash(bytes) !== file.sha256) throw new ExecutorError("artifact_digest_mismatch");
120
+ if (immutable && (lstatSync(join(root, file.path)).mode & 511) !== (file.executable ? 320 : 256))
121
+ throw new ExecutorError("artifact_mode_mismatch");
122
+ }
123
+ }
124
+ class ImmutableAdapterArtifacts {
125
+ constructor(root, installationId, workerId, workspaceRoots) {
126
+ this.root = root;
127
+ this.installationId = installationId;
128
+ this.workerId = workerId;
129
+ this.workspaceRoots = workspaceRoots;
130
+ privateDirectory(root);
131
+ outsideWorkspaces(root, workspaceRoots);
132
+ }
133
+ root;
134
+ installationId;
135
+ workerId;
136
+ workspaceRoots;
137
+ approved(input, approvalDigest) {
138
+ const manifest = AdapterManifest.parse(input);
139
+ if (manifest.installationId !== this.installationId || manifest.workerId !== this.workerId) throw new ExecutorError("wrong_resource");
140
+ if (digest(manifest) !== sha256.parse(approvalDigest)) throw new ExecutorError("unapproved_artifact");
141
+ return manifest;
142
+ }
143
+ stage(source, input, approvalDigest) {
144
+ const manifest = this.approved(input, approvalDigest);
145
+ outsideWorkspaces(source, this.workspaceRoots);
146
+ verifyTree(source, manifest, false);
147
+ const destination = join(this.root, approvalDigest);
148
+ if (existsSync(destination)) return this.verify(manifest, approvalDigest);
149
+ const temporary = join(this.root, `.staging-${randomUUID()}`);
150
+ privateDirectory(temporary);
151
+ try {
152
+ for (const file of manifest.files) {
153
+ const target = join(temporary, file.path);
154
+ mkdirSync(dirname(target), { recursive: true, mode: 448 });
155
+ const bytes = readMember(join(source, file.path), false);
156
+ if (bytes.length !== file.size || bytesHash(bytes) !== file.sha256) throw new ExecutorError("artifact_digest_mismatch");
157
+ const fd = openSync(target, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
158
+ try {
159
+ writeFileSync(fd, bytes);
160
+ fsyncSync(fd);
161
+ } finally {
162
+ closeSync(fd);
163
+ }
164
+ chmodSync(target, file.executable ? 320 : 256);
165
+ }
166
+ const seal = (dir) => {
167
+ for (const name of readdirSync(dir)) if (lstatSync(join(dir, name)).isDirectory()) seal(join(dir, name));
168
+ chmodSync(dir, 320);
169
+ syncDirectory(dir);
170
+ };
171
+ seal(temporary);
172
+ verifyTree(temporary, manifest, true);
173
+ renameSync(temporary, destination);
174
+ syncDirectory(this.root);
175
+ return destination;
176
+ } catch (error) {
177
+ if (existsSync(temporary)) {
178
+ const unseal = (dir) => {
179
+ chmodSync(dir, 448);
180
+ for (const name of readdirSync(dir)) if (lstatSync(join(dir, name)).isDirectory()) unseal(join(dir, name));
181
+ };
182
+ unseal(temporary);
183
+ rmSync(temporary, { recursive: true });
184
+ }
185
+ throw error;
186
+ }
187
+ }
188
+ verify(input, approvalDigest) {
189
+ const manifest = this.approved(input, approvalDigest), path = join(this.root, approvalDigest);
190
+ verifyTree(path, manifest, true);
191
+ return path;
192
+ }
193
+ }
194
+ export {
195
+ AdapterManifest,
196
+ ImmutableAdapterArtifacts,
197
+ bytesHash,
198
+ canonicalDirectory,
199
+ outsideWorkspaces,
200
+ within
201
+ };
@@ -0,0 +1,93 @@
1
+ import { createHash } from "node:crypto";
2
+ import { realpathSync } from "node:fs";
3
+ import { isAbsolute, relative } from "node:path";
4
+ import { canonicalJson, negotiateProtocol } from "@ricsam/r5d-api/runtime-protocol";
5
+ import {
6
+ AdapterGrant,
7
+ ExecutorError,
8
+ EXECUTOR_CAPABILITY
9
+ } from "../protocol.mjs";
10
+ const digest = (value) => createHash("sha256").update(canonicalJson(value)).digest("hex");
11
+ class AdapterGrantRegistry {
12
+ constructor(db, config) {
13
+ this.db = db;
14
+ this.config = config;
15
+ db.exec(`CREATE TABLE IF NOT EXISTS adapter_grants (
16
+ id TEXT PRIMARY KEY, owner TEXT NOT NULL UNIQUE, release TEXT NOT NULL UNIQUE,
17
+ grant_json TEXT NOT NULL, digest TEXT NOT NULL, state TEXT NOT NULL CHECK(state IN ('registered','retired')))`);
18
+ for (const row of this.rows()) {
19
+ const grant = AdapterGrant.parse(JSON.parse(row.grant_json));
20
+ if (digest(grant) !== row.digest || grant.installationId !== config.installationId || grant.workerId !== config.workerId || config.principals.some((p) => p.id === grant.principal.id || p.ownerId === grant.principal.ownerId))
21
+ throw new ExecutorError("grant_store_conflict");
22
+ }
23
+ }
24
+ db;
25
+ config;
26
+ rows() {
27
+ return this.db.query("SELECT * FROM adapter_grants").all();
28
+ }
29
+ find(id) {
30
+ const row = this.rows().find((row2) => row2.id === id && row2.state === "registered");
31
+ return row ? AdapterGrant.parse(JSON.parse(row.grant_json)) : void 0;
32
+ }
33
+ hasOwner(owner) {
34
+ return this.rows().some(
35
+ (row) => row.state === "registered" && AdapterGrant.parse(JSON.parse(row.grant_json)).principal.ownerId === owner
36
+ );
37
+ }
38
+ list() {
39
+ return this.rows().map((row) => {
40
+ const grant = AdapterGrant.parse(JSON.parse(row.grant_json));
41
+ return {
42
+ principalId: row.id,
43
+ ownerId: grant.principal.ownerId,
44
+ releaseId: grant.hello.releaseId,
45
+ artifactDigest: grant.artifactDigest,
46
+ grantDigest: row.digest,
47
+ state: row.state
48
+ };
49
+ });
50
+ }
51
+ register(controller, input, keeperHello) {
52
+ if (controller.role !== "controller") throw new ExecutorError("forbidden");
53
+ const grant = AdapterGrant.parse(input), p = grant.principal;
54
+ if (grant.installationId !== this.config.installationId || grant.workerId !== this.config.workerId)
55
+ throw new ExecutorError("wrong_resource");
56
+ negotiateProtocol(keeperHello, grant.hello, [EXECUTOR_CAPABILITY]);
57
+ if (grant.hello.role !== "worker-adapter" || grant.hello.instanceId !== p.ownerId) throw new ExecutorError("wrong_principal");
58
+ if (p.userIds.some((id) => !controller.userIds.includes(id)) || p.lanes.some((lane) => !controller.lanes.includes(lane)) || p.credentialIds.some((id) => !controller.credentialIds.includes(id)) || p.cwdRoots.some(
59
+ (root) => !controller.cwdRoots.some((allowed) => {
60
+ const rel = relative(realpathSync(allowed), realpathSync(root));
61
+ return rel === "" || !isAbsolute(rel) && rel !== ".." && !rel.startsWith("../");
62
+ })
63
+ ))
64
+ throw new ExecutorError("grant_scope_exceeded");
65
+ const hash = digest(grant), rows = this.rows(), old = rows.find((row) => row.id === p.id);
66
+ if (old) {
67
+ if (old.digest !== hash) throw new ExecutorError("grant_identity_conflict");
68
+ if (old.state === "retired") throw new ExecutorError("adapter_retired");
69
+ return this.list().find((r) => r.principalId === p.id);
70
+ }
71
+ if (this.config.principals.some((s) => s.id === p.id || s.ownerId === p.ownerId) || rows.some((row) => {
72
+ const prior = AdapterGrant.parse(JSON.parse(row.grant_json));
73
+ return prior.principal.ownerId === p.ownerId || prior.hello.releaseId === grant.hello.releaseId;
74
+ }))
75
+ throw new ExecutorError("grant_identity_conflict");
76
+ if (rows.filter((row) => row.state === "registered").length + this.config.principals.filter((p2) => p2.role === "adapter").length >= 2)
77
+ throw new ExecutorError("adapter_retention_blocked");
78
+ this.db.query("INSERT INTO adapter_grants VALUES (?,?,?,?,?,'registered')").run(p.id, p.ownerId, grant.hello.releaseId, canonicalJson(grant), hash);
79
+ return this.list().find((r) => r.principalId === p.id);
80
+ }
81
+ retire(controller, id, authoritativeOwner) {
82
+ if (controller.role !== "controller") throw new ExecutorError("forbidden");
83
+ const row = this.list().find((row2) => row2.principalId === id);
84
+ if (!row) throw new ExecutorError("adapter_not_found");
85
+ if (row.ownerId === authoritativeOwner) throw new ExecutorError("adapter_is_authority");
86
+ this.db.query("UPDATE adapter_grants SET state='retired' WHERE id=?").run(id);
87
+ return { ...row, state: "retired" };
88
+ }
89
+ }
90
+ export {
91
+ AdapterGrantRegistry,
92
+ digest
93
+ };
@@ -0,0 +1,101 @@
1
+ import { z } from "zod";
2
+ import { OwnershipFence, RuntimeId } from "@ricsam/r5d-api/runtime-protocol";
3
+ import { AdapterConfig, AdapterGrant, ExecutorCommand, ExecutorError, MAX_FRAME_BYTES } from "../protocol.mjs";
4
+ import { readPrivateJson } from "../storage.mjs";
5
+ import { AdapterManifest } from "./artifacts.mjs";
6
+ import { ReleaseManagerConfig, WorkerAdapterReleases } from "./manager.mjs";
7
+ const Command = z.discriminatedUnion("method", [
8
+ z.object({ method: z.literal("status") }).strict(),
9
+ z.object({
10
+ method: z.literal("stage"),
11
+ source: z.string(),
12
+ manifest: AdapterManifest,
13
+ approvalDigest: z.string(),
14
+ grant: AdapterGrant,
15
+ adapter: AdapterConfig
16
+ }).strict(),
17
+ z.object({ method: z.literal("launch"), principalId: RuntimeId }).strict(),
18
+ z.object({ method: z.literal("stop-adapter"), principalId: RuntimeId }).strict(),
19
+ z.object({ method: z.literal("promote"), principalId: RuntimeId, fence: OwnershipFence, validUntil: z.number().int().positive() }).strict(),
20
+ z.object({ method: z.literal("retire"), principalId: RuntimeId }).strict(),
21
+ z.object({ method: z.literal("request"), callId: RuntimeId, principalId: RuntimeId, command: ExecutorCommand }).strict(),
22
+ z.object({ method: z.literal("reconcile-call"), callId: RuntimeId, principalId: RuntimeId, command: ExecutorCommand }).strict(),
23
+ z.object({ method: z.literal("acknowledge"), callId: RuntimeId }).strict(),
24
+ z.object({ method: z.literal("reconcile") }).strict()
25
+ ]);
26
+ if (import.meta.main) {
27
+ const path = process.argv[2];
28
+ if (!path || process.argv.length !== 3) throw new Error("Usage: bun releases/main.ts /absolute/private/release-manager.json");
29
+ const manager = new WorkerAdapterReleases(ReleaseManagerConfig.parse(readPrivateJson(path)));
30
+ let buffer = Buffer.alloc(0), tail = Promise.resolve(), stopping = false;
31
+ async function handle(line) {
32
+ let id;
33
+ try {
34
+ const message = z.object({ id: RuntimeId, command: Command }).strict().parse(JSON.parse(line.toString("utf8")));
35
+ id = message.id;
36
+ const command = message.command;
37
+ let result;
38
+ switch (command.method) {
39
+ case "status":
40
+ result = { releases: manager.list(), calls: manager.calls(), transitions: manager.transitions() };
41
+ break;
42
+ case "stage":
43
+ result = await manager.stage(command);
44
+ break;
45
+ case "launch":
46
+ result = await manager.launch(command.principalId);
47
+ break;
48
+ case "stop-adapter":
49
+ result = await manager.stopAdapter(command.principalId);
50
+ break;
51
+ case "promote":
52
+ result = await manager.promote(command.principalId, command.fence, command.validUntil);
53
+ break;
54
+ case "retire":
55
+ result = await manager.retire(command.principalId);
56
+ break;
57
+ case "request":
58
+ result = await manager.request(command.callId, command.principalId, command.command);
59
+ break;
60
+ case "reconcile-call":
61
+ result = await manager.reconcileCall(command.callId, command.principalId, command.command);
62
+ break;
63
+ case "acknowledge":
64
+ result = await manager.acknowledge(command.callId);
65
+ break;
66
+ case "reconcile":
67
+ result = await manager.reconcile();
68
+ break;
69
+ }
70
+ process.stdout.write(JSON.stringify({ id, ok: true, result: result ?? null }) + "\n");
71
+ } catch (error) {
72
+ process.stdout.write(
73
+ JSON.stringify({ id, ok: false, error: { code: error instanceof ExecutorError ? error.code : "invalid_request" } }) + "\n"
74
+ );
75
+ }
76
+ }
77
+ const stop = () => {
78
+ if (stopping) return;
79
+ stopping = true;
80
+ void tail.then(() => manager.close()).then(
81
+ () => process.exit(0),
82
+ () => process.exit(1)
83
+ );
84
+ };
85
+ process.stdin.on("data", (chunk) => {
86
+ if (stopping) return;
87
+ buffer = Buffer.concat([buffer, chunk]);
88
+ if (buffer.length > MAX_FRAME_BYTES) {
89
+ stop();
90
+ return;
91
+ }
92
+ while (buffer.includes(10)) {
93
+ const end = buffer.indexOf(10), line = buffer.subarray(0, end);
94
+ buffer = buffer.subarray(end + 1);
95
+ tail = tail.then(() => handle(line));
96
+ }
97
+ });
98
+ process.stdin.once("end", stop);
99
+ process.on("SIGTERM", stop);
100
+ process.on("SIGINT", stop);
101
+ }