zcode-acp-server 0.1.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 (108) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +167 -0
  3. package/README.zh-CN.md +163 -0
  4. package/dist/backend/client.d.ts +103 -0
  5. package/dist/backend/client.d.ts.map +1 -0
  6. package/dist/backend/client.js +344 -0
  7. package/dist/backend/client.js.map +1 -0
  8. package/dist/backend/credentials.d.ts +31 -0
  9. package/dist/backend/credentials.d.ts.map +1 -0
  10. package/dist/backend/credentials.js +93 -0
  11. package/dist/backend/credentials.js.map +1 -0
  12. package/dist/backend/index.d.ts +7 -0
  13. package/dist/backend/index.d.ts.map +1 -0
  14. package/dist/backend/index.js +6 -0
  15. package/dist/backend/index.js.map +1 -0
  16. package/dist/backend/listener.d.ts +63 -0
  17. package/dist/backend/listener.d.ts.map +1 -0
  18. package/dist/backend/listener.js +138 -0
  19. package/dist/backend/listener.js.map +1 -0
  20. package/dist/backend/resolve.d.ts +11 -0
  21. package/dist/backend/resolve.d.ts.map +1 -0
  22. package/dist/backend/resolve.js +116 -0
  23. package/dist/backend/resolve.js.map +1 -0
  24. package/dist/backend/types.d.ts +164 -0
  25. package/dist/backend/types.d.ts.map +1 -0
  26. package/dist/backend/types.js +15 -0
  27. package/dist/backend/types.js.map +1 -0
  28. package/dist/config/model-cache.d.ts +20 -0
  29. package/dist/config/model-cache.d.ts.map +1 -0
  30. package/dist/config/model-cache.js +62 -0
  31. package/dist/config/model-cache.js.map +1 -0
  32. package/dist/config/options.d.ts +36 -0
  33. package/dist/config/options.d.ts.map +1 -0
  34. package/dist/config/options.js +171 -0
  35. package/dist/config/options.js.map +1 -0
  36. package/dist/config/runtime-model.d.ts +33 -0
  37. package/dist/config/runtime-model.d.ts.map +1 -0
  38. package/dist/config/runtime-model.js +96 -0
  39. package/dist/config/runtime-model.js.map +1 -0
  40. package/dist/handlers/dispatch.d.ts +15 -0
  41. package/dist/handlers/dispatch.d.ts.map +1 -0
  42. package/dist/handlers/dispatch.js +183 -0
  43. package/dist/handlers/dispatch.js.map +1 -0
  44. package/dist/handlers/extensions.d.ts +43 -0
  45. package/dist/handlers/extensions.d.ts.map +1 -0
  46. package/dist/handlers/extensions.js +310 -0
  47. package/dist/handlers/extensions.js.map +1 -0
  48. package/dist/handlers/io.d.ts +40 -0
  49. package/dist/handlers/io.d.ts.map +1 -0
  50. package/dist/handlers/io.js +55 -0
  51. package/dist/handlers/io.js.map +1 -0
  52. package/dist/handlers/server-requests.d.ts +34 -0
  53. package/dist/handlers/server-requests.d.ts.map +1 -0
  54. package/dist/handlers/server-requests.js +357 -0
  55. package/dist/handlers/server-requests.js.map +1 -0
  56. package/dist/handlers/session.d.ts +46 -0
  57. package/dist/handlers/session.d.ts.map +1 -0
  58. package/dist/handlers/session.js +738 -0
  59. package/dist/handlers/session.js.map +1 -0
  60. package/dist/handlers/slash.d.ts +16 -0
  61. package/dist/handlers/slash.d.ts.map +1 -0
  62. package/dist/handlers/slash.js +107 -0
  63. package/dist/handlers/slash.js.map +1 -0
  64. package/dist/index.d.ts +11 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +93 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/interaction/adapter.d.ts +136 -0
  69. package/dist/interaction/adapter.d.ts.map +1 -0
  70. package/dist/interaction/adapter.js +353 -0
  71. package/dist/interaction/adapter.js.map +1 -0
  72. package/dist/server.d.ts +73 -0
  73. package/dist/server.d.ts.map +1 -0
  74. package/dist/server.js +97 -0
  75. package/dist/server.js.map +1 -0
  76. package/dist/tasks-index.d.ts +39 -0
  77. package/dist/tasks-index.d.ts.map +1 -0
  78. package/dist/tasks-index.js +152 -0
  79. package/dist/tasks-index.js.map +1 -0
  80. package/dist/translators/event-translator.d.ts +40 -0
  81. package/dist/translators/event-translator.d.ts.map +1 -0
  82. package/dist/translators/event-translator.js +214 -0
  83. package/dist/translators/event-translator.js.map +1 -0
  84. package/dist/translators/index.d.ts +6 -0
  85. package/dist/translators/index.d.ts.map +1 -0
  86. package/dist/translators/index.js +5 -0
  87. package/dist/translators/index.js.map +1 -0
  88. package/dist/translators/projection-differ.d.ts +48 -0
  89. package/dist/translators/projection-differ.d.ts.map +1 -0
  90. package/dist/translators/projection-differ.js +239 -0
  91. package/dist/translators/projection-differ.js.map +1 -0
  92. package/dist/translators/tool-helpers.d.ts +60 -0
  93. package/dist/translators/tool-helpers.d.ts.map +1 -0
  94. package/dist/translators/tool-helpers.js +308 -0
  95. package/dist/translators/tool-helpers.js.map +1 -0
  96. package/dist/translators/types.d.ts +58 -0
  97. package/dist/translators/types.d.ts.map +1 -0
  98. package/dist/translators/types.js +27 -0
  99. package/dist/translators/types.js.map +1 -0
  100. package/dist/utils.d.ts +111 -0
  101. package/dist/utils.d.ts.map +1 -0
  102. package/dist/utils.js +110 -0
  103. package/dist/utils.js.map +1 -0
  104. package/docs/ARCHITECTURE.md +299 -0
  105. package/docs/DEVELOPMENT.md +193 -0
  106. package/docs/PROTOCOL.md +649 -0
  107. package/docs/TROUBLESHOOTING.md +251 -0
  108. package/package.json +66 -0
@@ -0,0 +1,344 @@
1
+ /**
2
+ * ZCode subprocess client: spawn, read-loop multiplexer, async request/response.
3
+ *
4
+ * The ZCode app-server is launched as a subprocess (`zcode app-server --stdio`)
5
+ * speaking line-delimited JSON over stdio. A single async read loop demultiplexes
6
+ * inbound messages into three channels:
7
+ * - responses (id, no method) → resolve the matching pending request promise
8
+ * - server→client requests (id + method, id not pending) → server-request queue
9
+ * - notifications (no id):
10
+ * - `session/event` → routed to the registered session listener
11
+ * - anything else → general notification queue
12
+ *
13
+ * Process-group isolation: the subprocess is its own process-group leader
14
+ * (`detached: true`) so `close()` can kill the whole tree (zcode + its model
15
+ * workers) with `process.kill(-pid)` and leave no orphans.
16
+ */
17
+ import { spawn } from "node:child_process";
18
+ import { createInterface } from "node:readline";
19
+ import process from "node:process";
20
+ import { log, warn } from "../utils.js";
21
+ export class ZcodeBackend {
22
+ proc;
23
+ pending = new Map();
24
+ serverRequests = [];
25
+ listeners = new Map();
26
+ readerDead = false;
27
+ /** Monotonic id for fire-and-forget sends (send()). Uses a high range to
28
+ * avoid collisions with the server's request ids (low range). */
29
+ sendIdCounter = 1_000_000_000;
30
+ /** Watchdog process that kills the zcode group if this bridge dies (SIGKILL). */
31
+ watchdog = null;
32
+ constructor(argv, env) {
33
+ this.proc = spawn(argv[0], argv.slice(1), {
34
+ stdio: ["pipe", "pipe", "ignore"],
35
+ env,
36
+ detached: true, // own process group → kill(-pid) reaps the whole tree
37
+ });
38
+ // Node stream write errors (EPIPE on a closed stdin) are emitted as async
39
+ // 'error' events, NOT thrown synchronously — without a listener the process
40
+ // crashes with an unhandled 'error' event. Catch them here and mark the
41
+ // reader dead so the rest of the bridge stops talking to a gone backend.
42
+ this.proc.stdin?.on("error", (err) => {
43
+ this.readerDead = true;
44
+ warn(`backend: stdin error: ${err.message}`);
45
+ });
46
+ this.startReader();
47
+ this.startWatchdog();
48
+ log(`backend: started zcode app-server (pid=${this.proc.pid})`);
49
+ }
50
+ /**
51
+ * Spawn a tiny detached watchdog that kills the zcode process group if this
52
+ * bridge process disappears.
53
+ *
54
+ * The detached/kill(-pid) cleanup in `close()` only runs when the bridge
55
+ * exits cleanly enough for the signal handlers to fire (SIGTERM/SIGINT/etc).
56
+ * If the bridge is SIGKILLed (Zed force-kill on reconnect, crash, OOM), the
57
+ * handler never runs and the zcode subprocess group is orphaned. The
58
+ * watchdog closes that gap: it polls the bridge pid every 2s and, once the
59
+ * bridge is gone, sends SIGKILL to the zcode process group, then exits.
60
+ *
61
+ * The watchdog is its own process-group leader (detached) and `unref`'d, so
62
+ * it never holds the event loop open and is not part of the zcode group it
63
+ * kills. It self-terminates as soon as the zcode process exits, so a normal
64
+ * shutdown leaves no lingering watchdog.
65
+ */
66
+ startWatchdog() {
67
+ const bridgePid = process.pid;
68
+ const zcodePid = this.proc.pid;
69
+ if (!bridgePid || !zcodePid)
70
+ return;
71
+ // Inline script: poll bridge liveness, kill zcode group on bridge death.
72
+ const script = `
73
+ const bridgePid = ${bridgePid};
74
+ const zcodePid = ${zcodePid};
75
+ const tick = () => {
76
+ // Bridge gone? → reap the whole zcode process group, then exit.
77
+ try { process.kill(bridgePid, 0); }
78
+ catch {
79
+ try { process.kill(-zcodePid, 'SIGKILL'); } catch {}
80
+ process.exit(0);
81
+ }
82
+ // zcode already exited? → watchdog has no job left.
83
+ try { process.kill(-zcodePid, 0); }
84
+ catch { process.exit(0); }
85
+ };
86
+ setInterval(tick, 2000);
87
+ tick();
88
+ `;
89
+ this.watchdog = spawn(process.execPath, ["-e", script], {
90
+ stdio: "ignore",
91
+ detached: true, // own process group, not part of the zcode group
92
+ });
93
+ this.watchdog.unref();
94
+ }
95
+ // ---------- read loop ----------
96
+ startReader() {
97
+ const stdout = this.proc.stdout;
98
+ if (!stdout) {
99
+ this.markReaderDead("no stdout");
100
+ return;
101
+ }
102
+ const rl = createInterface({ input: stdout });
103
+ rl.on("line", (line) => {
104
+ const trimmed = line.trim();
105
+ if (!trimmed)
106
+ return;
107
+ let msg;
108
+ try {
109
+ msg = JSON.parse(trimmed);
110
+ }
111
+ catch {
112
+ return; // unparseable line: ignore
113
+ }
114
+ this.route(msg);
115
+ });
116
+ rl.on("close", () => this.markReaderDead("stdout closed"));
117
+ }
118
+ route(msg) {
119
+ const method = msg.method;
120
+ const id = msg.id;
121
+ if (id !== undefined && method === undefined) {
122
+ // Response (id, no method) → resolve pending request.
123
+ this.resolvePending(id, msg);
124
+ return;
125
+ }
126
+ if (id !== undefined && method !== undefined) {
127
+ // id + method: our pending response wins the race; else it's a server→client request.
128
+ if (this.pending.has(id)) {
129
+ this.resolvePending(id, msg);
130
+ }
131
+ else {
132
+ this.serverRequests.push({
133
+ id,
134
+ method,
135
+ params: (msg.params ?? {}),
136
+ });
137
+ }
138
+ return;
139
+ }
140
+ if (method !== undefined) {
141
+ // Notification.
142
+ if (method === "session/event") {
143
+ const ev = (msg.params ?? {});
144
+ const sid = ev.sessionId;
145
+ const listener = sid ? this.listeners.get(sid) : undefined;
146
+ listener?.handleEvent(ev);
147
+ }
148
+ // Other notifications are currently ignored (state.updated, etc.).
149
+ }
150
+ }
151
+ resolvePending(id, resp) {
152
+ const p = this.pending.get(id);
153
+ if (!p)
154
+ return;
155
+ clearTimeout(p.timer);
156
+ this.pending.delete(id);
157
+ p.resolve(resp);
158
+ }
159
+ markReaderDead(reason) {
160
+ if (this.readerDead)
161
+ return;
162
+ this.readerDead = true;
163
+ warn(`backend: reader exited (${reason})`);
164
+ for (const [, p] of this.pending) {
165
+ clearTimeout(p.timer);
166
+ p.resolve({
167
+ id: 0,
168
+ error: { message: "zcode backend reader exited (backend dead)" },
169
+ });
170
+ }
171
+ this.pending.clear();
172
+ }
173
+ // ---------- listeners / server requests ----------
174
+ registerEventListener(zcodeSid, listener) {
175
+ this.listeners.set(zcodeSid, listener);
176
+ }
177
+ unregisterEventListener(zcodeSid) {
178
+ this.listeners.delete(zcodeSid);
179
+ }
180
+ /** Non-blocking drain of pending server→client requests. */
181
+ pollServerRequests() {
182
+ if (this.serverRequests.length === 0)
183
+ return [];
184
+ return this.serverRequests.splice(0, this.serverRequests.length);
185
+ }
186
+ /** Reply to a zcode server→client request with a result (id + result). */
187
+ sendReply(id, result) {
188
+ const stdin = this.proc.stdin;
189
+ if (!stdin || stdin.destroyed) {
190
+ warn("backend: sendReply dropped (stdin closed)");
191
+ return;
192
+ }
193
+ // Write errors (EPIPE) are delivered via the stdin 'error' listener
194
+ // installed in the constructor (synchronous try/catch cannot catch them);
195
+ // no try/catch needed here.
196
+ stdin.write(JSON.stringify({ id, result }) + "\n");
197
+ }
198
+ /** Reply to a zcode server→client request with an error. */
199
+ sendError(id, code, message) {
200
+ const stdin = this.proc.stdin;
201
+ if (!stdin || stdin.destroyed) {
202
+ warn("backend: sendError dropped (stdin closed)");
203
+ return;
204
+ }
205
+ stdin.write(JSON.stringify({ id, error: { code, message } }) + "\n");
206
+ }
207
+ // ---------- send / request ----------
208
+ /** Fire-and-forget notification to ZCode (no id, no response). */
209
+ notify(method, params) {
210
+ const stdin = this.proc.stdin;
211
+ if (!stdin || stdin.destroyed) {
212
+ warn("backend: notify dropped (stdin closed)");
213
+ return;
214
+ }
215
+ stdin.write(JSON.stringify({ method, params }) + "\n");
216
+ }
217
+ /**
218
+ * Send a message with an id but WITHOUT registering a pending response
219
+ * (fire-and-forget). Mirrors Python's `_backend.send({"id": ..., ...})` for
220
+ * `session/stop`: some backends route by id presence, so carrying an id is
221
+ * more robust than a bare notify. If the backend replies, the reader's
222
+ * `resolvePending` finds no pending entry and safely discards it.
223
+ */
224
+ send(method, params) {
225
+ const stdin = this.proc.stdin;
226
+ if (!stdin || stdin.destroyed) {
227
+ warn("backend: send dropped (stdin closed)");
228
+ return;
229
+ }
230
+ const id = this.sendIdCounter++;
231
+ stdin.write(JSON.stringify({ id, method, params: params ?? {} }) + "\n");
232
+ }
233
+ /**
234
+ * Synchronous request/response: register a pending promise, send, await.
235
+ * Other notifications arriving during the wait are routed async by the
236
+ * reader loop (they don't get swallowed).
237
+ *
238
+ * Returns `{error}` on dead backend, broken pipe, or timeout — never throws.
239
+ */
240
+ async request(id, method, params, timeoutMs = 30000) {
241
+ if (this.readerDead) {
242
+ return { id, error: { message: "zcode backend reader exited (backend dead)" } };
243
+ }
244
+ const promise = new Promise((resolve) => {
245
+ const timer = setTimeout(() => {
246
+ if (this.pending.delete(id)) {
247
+ resolve({ id, error: { message: "timeout" } });
248
+ }
249
+ }, timeoutMs);
250
+ this.pending.set(id, { resolve, timer });
251
+ });
252
+ try {
253
+ const stdin = this.proc.stdin;
254
+ if (!stdin || stdin.destroyed)
255
+ throw new Error("stdin closed");
256
+ stdin.write(JSON.stringify({ id, method, params: params ?? {} }) + "\n");
257
+ }
258
+ catch (e) {
259
+ this.pending.delete(id);
260
+ this.readerDead = true;
261
+ return {
262
+ id,
263
+ error: {
264
+ message: `zcode backend pipe broken: ${e instanceof Error ? e.message : String(e)}`,
265
+ },
266
+ };
267
+ }
268
+ return promise;
269
+ }
270
+ // ---------- lifecycle ----------
271
+ /**
272
+ * Kill the whole zcode process group and wait for it to die.
273
+ *
274
+ * SIGTERM → wait up to 3s → SIGKILL if still alive. Mirrors the Python
275
+ * `os.killpg` + `proc.wait(3)` + SIGKILL escalation. Note `proc.killed` is
276
+ * NOT set by `process.kill(-pid)` (group signal), so we track liveness via
277
+ * `exitCode === null` instead. Async so the caller can `await` a full reap
278
+ * before the parent exits (an unref'd timer could be skipped on fast exit,
279
+ * leaving orphans).
280
+ */
281
+ async close() {
282
+ const proc = this.proc;
283
+ if (!proc.pid)
284
+ return;
285
+ try {
286
+ // Already exited?
287
+ if (proc.exitCode !== null || proc.signalCode)
288
+ return;
289
+ try {
290
+ process.kill(-proc.pid, "SIGTERM");
291
+ }
292
+ catch {
293
+ return; // group already gone
294
+ }
295
+ // Wait up to 3s for a clean exit.
296
+ const exited = await new Promise((resolve) => {
297
+ let timer;
298
+ const done = () => {
299
+ clearTimeout(timer); // don't let the timeout keep the event loop alive
300
+ resolve(true);
301
+ };
302
+ proc.once("exit", done);
303
+ timer = setTimeout(() => {
304
+ proc.removeListener("exit", done);
305
+ resolve(false);
306
+ }, 3000);
307
+ });
308
+ if (exited)
309
+ return;
310
+ // Still alive → SIGKILL the whole group.
311
+ try {
312
+ if (proc.pid && proc.exitCode === null)
313
+ process.kill(-proc.pid, "SIGKILL");
314
+ }
315
+ catch {
316
+ // already gone
317
+ }
318
+ }
319
+ finally {
320
+ // Stop the watchdog — it would self-exit on its next tick once the zcode
321
+ // group is gone, but killing it here avoids the up-to-2s delay.
322
+ this.killWatchdog();
323
+ }
324
+ }
325
+ /** Terminate the watchdog process if it is still running. */
326
+ killWatchdog() {
327
+ const wd = this.watchdog;
328
+ if (!wd)
329
+ return;
330
+ this.watchdog = null;
331
+ if (wd.pid && wd.exitCode === null) {
332
+ try {
333
+ process.kill(wd.pid, "SIGTERM");
334
+ }
335
+ catch {
336
+ // already gone
337
+ }
338
+ }
339
+ }
340
+ get isDead() {
341
+ return this.readerDead;
342
+ }
343
+ }
344
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/backend/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * ZCode credential and environment handling.
3
+ *
4
+ * Vendored from the reference Python implementation so this package stays
5
+ * self-contained. The ZCode desktop app stores provider credentials in
6
+ * `~/.zcode/v2/config.json`; GUI-launched processes don't inherit shell env
7
+ * vars, so we read the config and inject the active provider's settings into
8
+ * the subprocess environment.
9
+ */
10
+ /** Credentials extracted from the active provider. */
11
+ export interface ZcodeCredentials {
12
+ ZCODE_MODEL?: string;
13
+ ZCODE_BASE_URL?: string;
14
+ ANTHROPIC_API_KEY?: string;
15
+ }
16
+ /** Read the active provider's credentials from config.json. Best-effort. */
17
+ export declare function loadZcodeCredentials(): ZcodeCredentials;
18
+ /**
19
+ * Merge process.env with config credentials.
20
+ *
21
+ * Explicit non-empty env vars override config (so `ZCODE_MODEL=foo` works as a
22
+ * temporary override). Empty-string env vars are treated as unset so they
23
+ * don't clobber the config value.
24
+ *
25
+ * Also self-heals a stale `ZCODE_BASE_URL`: if the env value is the endpoint
26
+ * of a *different* provider in config (a leftover from switching plans in the
27
+ * App), revert to the enabled provider's URL. User-defined custom endpoints
28
+ * are respected.
29
+ */
30
+ export declare function mergeEnvWithCreds(creds: ZcodeCredentials): NodeJS.ProcessEnv;
31
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/backend/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkBH,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,IAAI,gBAAgB,CAoBvD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAAC,UAAU,CAuB5E"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * ZCode credential and environment handling.
3
+ *
4
+ * Vendored from the reference Python implementation so this package stays
5
+ * self-contained. The ZCode desktop app stores provider credentials in
6
+ * `~/.zcode/v2/config.json`; GUI-launched processes don't inherit shell env
7
+ * vars, so we read the config and inject the active provider's settings into
8
+ * the subprocess environment.
9
+ */
10
+ import { readFileSync } from "node:fs";
11
+ import process from "node:process";
12
+ import { log, ZCODE_CREDS_PATH } from "../utils.js";
13
+ /** Read the active provider's credentials from config.json. Best-effort. */
14
+ export function loadZcodeCredentials() {
15
+ try {
16
+ const cfg = JSON.parse(readFileSync(ZCODE_CREDS_PATH, "utf8"));
17
+ for (const [, p] of Object.entries(cfg.provider ?? {})) {
18
+ if (p?.enabled) {
19
+ const opts = p.options ?? {};
20
+ const models = p.models ?? {};
21
+ return {
22
+ ZCODE_MODEL: Object.keys(models)[0] ?? "GLM-5.2",
23
+ ZCODE_BASE_URL: opts.baseURL ?? "",
24
+ ANTHROPIC_API_KEY: opts.apiKey ?? "",
25
+ };
26
+ }
27
+ }
28
+ }
29
+ catch (e) {
30
+ log(`credentials: failed to read ${ZCODE_CREDS_PATH}: ${e instanceof Error ? e.message : String(e)}`);
31
+ }
32
+ return {};
33
+ }
34
+ /**
35
+ * Merge process.env with config credentials.
36
+ *
37
+ * Explicit non-empty env vars override config (so `ZCODE_MODEL=foo` works as a
38
+ * temporary override). Empty-string env vars are treated as unset so they
39
+ * don't clobber the config value.
40
+ *
41
+ * Also self-heals a stale `ZCODE_BASE_URL`: if the env value is the endpoint
42
+ * of a *different* provider in config (a leftover from switching plans in the
43
+ * App), revert to the enabled provider's URL. User-defined custom endpoints
44
+ * are respected.
45
+ */
46
+ export function mergeEnvWithCreds(creds) {
47
+ const merged = { ...process.env, ...creds };
48
+ for (const k of ["ZCODE_MODEL", "ZCODE_BASE_URL", "ANTHROPIC_API_KEY"]) {
49
+ const v = process.env[k];
50
+ if (v)
51
+ merged[k] = v;
52
+ }
53
+ // Stale-baseURL self-heal.
54
+ const envBu = process.env.ZCODE_BASE_URL ?? "";
55
+ const configBu = creds.ZCODE_BASE_URL ?? "";
56
+ if (envBu && configBu && envBu !== configBu) {
57
+ const allHosts = collectProviderHosts();
58
+ const envHost = hostOf(envBu);
59
+ const configHost = hostOf(configBu);
60
+ if (envHost && configHost && envHost !== configHost && allHosts.has(envHost)) {
61
+ merged.ZCODE_BASE_URL = configBu;
62
+ log(`credentials: ZCODE_BASE_URL stale-host detected (env='${envBu}' is another ` +
63
+ `provider's endpoint); reverted to config='${configBu}'.`);
64
+ }
65
+ }
66
+ return merged;
67
+ }
68
+ function collectProviderHosts() {
69
+ const hosts = new Set();
70
+ try {
71
+ const cfg = JSON.parse(readFileSync(ZCODE_CREDS_PATH, "utf8"));
72
+ for (const p of Object.values(cfg.provider ?? {})) {
73
+ const bu = p?.options?.baseURL ?? "";
74
+ const h = hostOf(bu);
75
+ if (h)
76
+ hosts.add(h);
77
+ }
78
+ }
79
+ catch {
80
+ // ignore — stale detection just degrades gracefully
81
+ }
82
+ return hosts;
83
+ }
84
+ function hostOf(url) {
85
+ try {
86
+ const u = new URL(url);
87
+ return `${u.protocol}//${u.host}`;
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/backend/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /** Backend layer barrel: ZCode subprocess client + listener + resolve + credentials. */
2
+ export { ZcodeBackend, type ServerRequest, type EventListener } from "./client.js";
3
+ export { EventStreamListener, TurnMonitor, type NextId } from "./listener.js";
4
+ export { resolveZcodeCommand } from "./resolve.js";
5
+ export { loadZcodeCredentials, mergeEnvWithCreds, type ZcodeCredentials } from "./credentials.js";
6
+ export type { ZcodeRequest, ZcodeNotification, ZcodeResponse, ZcodeInbound, ZcodeSessionInfo, ZcodeCreateResult, ZcodeSessionListItem, ZcodeListResult, ZcodeEvent, ZcodeEventType, ZcodeSubscribeResult, ZcodeSnapshot, ZcodeProjection, ZcodeMessage, ZcodeMessagePart, ZcodeMessagesResult, ZcodeReadResult, ZcodeInteractionPermissionParams, ZcodeInteractionUserInputParams, ZcodeInteractionResponse, } from "./types.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClG,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,gCAAgC,EAChC,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** Backend layer barrel: ZCode subprocess client + listener + resolve + credentials. */
2
+ export { ZcodeBackend } from "./client.js";
3
+ export { EventStreamListener, TurnMonitor } from "./listener.js";
4
+ export { resolveZcodeCommand } from "./resolve.js";
5
+ export { loadZcodeCredentials, mergeEnvWithCreds } from "./credentials.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,EAAE,YAAY,EAA0C,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAe,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAyB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Event-stream listener and turn monitor.
3
+ *
4
+ * `EventStreamListener` subscribes to ZCode's `session/subscribe` event push
5
+ * (deliveryKind `desktop-continuous`) and queues `session/event` notifications
6
+ * for the turn loop to consume. It tracks a `lastSeq` watermark and can
7
+ * resubscribe from it to recover missed events after a stall.
8
+ *
9
+ * `TurnMonitor` is the legacy snapshot path: a one-shot `session/read` that
10
+ * returns the authoritative projection. Used for stall reconciliation and
11
+ * lock-release probing.
12
+ */
13
+ import type { ZcodeBackend } from "./client.js";
14
+ import type { ZcodeEvent, ZcodeProjection, ZcodeSnapshot } from "./types.js";
15
+ /** ID generator function (the server's `_next_id`). */
16
+ export type NextId = () => number;
17
+ export declare class EventStreamListener {
18
+ private readonly backend;
19
+ readonly sid: string;
20
+ /** High-watermark of consumed event sequence numbers. */
21
+ lastSeq: number;
22
+ subscribed: boolean;
23
+ private readonly queue;
24
+ private readonly waiters;
25
+ constructor(backend: ZcodeBackend, zcodeSid: string);
26
+ /**
27
+ * Subscribe and capture the initial snapshot + eventSeq watermark.
28
+ *
29
+ * Returns the snapshot (with projection/messages) or null on failure. ZCode
30
+ * CLI 0.14.8+ always supports this; null means an old/ broken backend and
31
+ * the caller should surface an error (polling fallback was removed).
32
+ */
33
+ subscribe(nextId: NextId): Promise<ZcodeSnapshot | null>;
34
+ /** Called by the backend reader when a `session/event` arrives. */
35
+ handleEvent(event: ZcodeEvent): void;
36
+ /**
37
+ * Wait for the next event, resolving once one arrives or `timeoutMs` elapses
38
+ * (resolves null on timeout). Events arriving with no active waiter are
39
+ * queued. A timed-out waiter is marked `done` so a later event skips it
40
+ * instead of being silently dropped on a settled promise.
41
+ */
42
+ pollEvent(timeoutMs?: number): Promise<ZcodeEvent | null>;
43
+ /**
44
+ * Stall recovery: resubscribe from `lastSeq` so the server replays missed
45
+ * events. Failure is logged but non-fatal — the caller degrades to polling.
46
+ * The snapshot (if returned despite `includeSnapshot:false`) is intentionally
47
+ * not consumed; resubscribe only refreshes the watermark + resumes the push.
48
+ */
49
+ resubscribe(nextId: NextId): Promise<boolean>;
50
+ }
51
+ /**
52
+ * Legacy snapshot path: a single `session/read` returning the authoritative
53
+ * projection. Used in stall reconciliation and lock-release probing.
54
+ */
55
+ export declare class TurnMonitor {
56
+ private readonly backend;
57
+ private readonly zcodeSid;
58
+ private readonly nextId;
59
+ constructor(backend: ZcodeBackend, zcodeSid: string, nextId: NextId);
60
+ /** Returns the projection snapshot, or null on error. */
61
+ pollOnce(): Promise<ZcodeProjection | null>;
62
+ }
63
+ //# sourceMappingURL=listener.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../../src/backend/listener.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAwB,MAAM,YAAY,CAAC;AAEnG,uDAAuD;AACvD,MAAM,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC;AAUlC,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,OAAO,SAAK;IACZ,UAAU,UAAS;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;gBAE5B,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM;IAKnD;;;;;;OAMG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAqB9D,mEAAmE;IACnE,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAepC;;;;;OAKG;IACH,SAAS,CAAC,SAAS,SAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAoBtD;;;;;OAKG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAqBpD;AAED;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAMnE,yDAAyD;IACnD,QAAQ,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;CAWlD"}