@mahekara28/context-relay 0.0.0-stage → 0.3.0-beta.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 (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +128 -2
  3. package/README.python.md +246 -0
  4. package/dist/adapters/capture.d.ts +4 -0
  5. package/dist/adapters/capture.js +286 -0
  6. package/dist/adapters/capture.js.map +1 -0
  7. package/dist/adapters/config.d.ts +11 -0
  8. package/dist/adapters/config.js +139 -0
  9. package/dist/adapters/config.js.map +1 -0
  10. package/dist/adapters/manager.d.ts +57 -0
  11. package/dist/adapters/manager.js +220 -0
  12. package/dist/adapters/manager.js.map +1 -0
  13. package/dist/adapters/normalize.d.ts +9 -0
  14. package/dist/adapters/normalize.js +166 -0
  15. package/dist/adapters/normalize.js.map +1 -0
  16. package/dist/adapters/rpc.d.ts +12 -0
  17. package/dist/adapters/rpc.js +100 -0
  18. package/dist/adapters/rpc.js.map +1 -0
  19. package/dist/adapters/types.d.ts +59 -0
  20. package/dist/adapters/types.js +51 -0
  21. package/dist/adapters/types.js.map +1 -0
  22. package/dist/cli.d.ts +2 -0
  23. package/dist/cli.js +427 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/client.d.ts +11 -0
  26. package/dist/client.js +136 -0
  27. package/dist/client.js.map +1 -0
  28. package/dist/delivery.d.ts +9 -0
  29. package/dist/delivery.js +35 -0
  30. package/dist/delivery.js.map +1 -0
  31. package/dist/index.d.ts +5 -0
  32. package/dist/index.js +5 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/mcp.d.ts +3 -0
  35. package/dist/mcp.js +109 -0
  36. package/dist/mcp.js.map +1 -0
  37. package/dist/model.d.ts +169 -0
  38. package/dist/model.js +79 -0
  39. package/dist/model.js.map +1 -0
  40. package/dist/proposals.d.ts +65 -0
  41. package/dist/proposals.js +134 -0
  42. package/dist/proposals.js.map +1 -0
  43. package/dist/server.d.ts +21 -0
  44. package/dist/server.js +375 -0
  45. package/dist/server.js.map +1 -0
  46. package/dist/store.d.ts +33 -0
  47. package/dist/store.js +393 -0
  48. package/dist/store.js.map +1 -0
  49. package/dist/ui/app.js +845 -0
  50. package/dist/ui/assets/THIRD_PARTY_LICENSE.txt +25 -0
  51. package/dist/ui/assets/claude.svg +1 -0
  52. package/dist/ui/assets/codex.svg +1 -0
  53. package/dist/ui/assets/goose.svg +1 -0
  54. package/dist/ui/assets/opencode.svg +1 -0
  55. package/dist/ui/assets/relay.svg +1 -0
  56. package/dist/ui/index.html +23 -0
  57. package/dist/ui/style.css +989 -0
  58. package/dist/util.d.ts +16 -0
  59. package/dist/util.js +86 -0
  60. package/dist/util.js.map +1 -0
  61. package/docs/HARNESS_CONNECTIONS.md +53 -0
  62. package/docs/NPM_QUICKSTART.md +150 -0
  63. package/docs/WORKSPACE.md +61 -0
  64. package/docs/releases/NPM_PUBLISHING.md +60 -0
  65. package/package.json +78 -4
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import { Harness } from "../model.js";
3
+ export const ConnectionInput = z
4
+ .object({
5
+ projectId: z.string().min(1),
6
+ harness: Harness.exclude(["manual"]),
7
+ source: z.string().min(1).max(2000).optional(),
8
+ executable: z.string().min(1).max(2000).optional(),
9
+ capture: z.boolean().default(true),
10
+ configureMcp: z.boolean().default(true),
11
+ })
12
+ .strict();
13
+ export const CAPABILITIES = [
14
+ {
15
+ harness: "claude",
16
+ name: "Claude Code",
17
+ discovery: "Project-matched JSONL transcripts",
18
+ capture: "Explicit sync; stable transcript events",
19
+ mcp: "Project .mcp.json",
20
+ delivery: "Reviewed packet file / MCP retrieval",
21
+ launch: true,
22
+ },
23
+ {
24
+ harness: "codex",
25
+ name: "Codex",
26
+ discovery: "App-server thread/list filtered by cwd",
27
+ capture: "Completed turns via app-server",
28
+ mcp: "Project .codex/config.toml",
29
+ delivery: "Reviewed packet file / MCP retrieval",
30
+ launch: true,
31
+ },
32
+ {
33
+ harness: "opencode",
34
+ name: "OpenCode",
35
+ discovery: "Local session API filtered by directory",
36
+ capture: "Completed session messages",
37
+ mcp: "Project opencode.json",
38
+ delivery: "Reviewed packet file / MCP retrieval",
39
+ launch: false,
40
+ },
41
+ {
42
+ harness: "goose",
43
+ name: "Goose",
44
+ discovery: "CLI session list filtered by working directory",
45
+ capture: "CLI JSON session export",
46
+ mcp: "Explicit extension command",
47
+ delivery: "Reviewed packet file / MCP retrieval",
48
+ launch: false,
49
+ },
50
+ ];
51
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../packages/relay/src/adapters/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC;KAC7B,MAAM,CAAC;IACN,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IAC9C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IAClD,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAClC,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CACxC,CAAC;KACD,MAAM,EAAE,CAAC;AAaZ,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B;QACE,OAAO,EAAE,QAAQ;QACjB,IAAI,EAAE,aAAa;QACnB,SAAS,EAAE,mCAAmC;QAC9C,OAAO,EAAE,yCAAyC;QAClD,GAAG,EAAE,mBAAmB;QACxB,QAAQ,EAAE,sCAAsC;QAChD,MAAM,EAAE,IAAI;KACb;IACD;QACE,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,wCAAwC;QACnD,OAAO,EAAE,gCAAgC;QACzC,GAAG,EAAE,4BAA4B;QACjC,QAAQ,EAAE,sCAAsC;QAChD,MAAM,EAAE,IAAI;KACb;IACD;QACE,OAAO,EAAE,UAAU;QACnB,IAAI,EAAE,UAAU;QAChB,SAAS,EAAE,yCAAyC;QACpD,OAAO,EAAE,4BAA4B;QACrC,GAAG,EAAE,uBAAuB;QAC5B,QAAQ,EAAE,sCAAsC;QAChD,MAAM,EAAE,KAAK;KACd;IACD;QACE,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,gDAAgD;QAC3D,OAAO,EAAE,yBAAyB;QAClC,GAAG,EAAE,4BAA4B;QACjC,QAAQ,EAAE,sCAAsC;QAChD,MAAM,EAAE,KAAK;KACd;CACO,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,427 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { parseArgs } from "node:util";
4
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
5
+ import { join, resolve } from "node:path";
6
+ import { connect, ensureService, recoverDeadLock } from "./client.js";
7
+ import { startService, VERSION } from "./server.js";
8
+ import { dataDirectory } from "./util.js";
9
+ import { launchPlan } from "./delivery.js";
10
+ import { serveMcp } from "./mcp.js";
11
+ import { RelayError, } from "./model.js";
12
+ const help = `Context Relay ${VERSION}
13
+ Review and carry working context between coding sessions.
14
+
15
+ Usage: relay <command> [options]
16
+ ui [--no-open] Open the private local workspace
17
+ init Start a private local workspace
18
+ serve [--port 0] Run the loopback service in the foreground
19
+ status | stop | doctor Inspect, stop or diagnose this workspace
20
+ project add --root PATH Register a local project
21
+ project list List registered projects
22
+ connect --project ID --harness claude [--source PATH] [--capture-only]
23
+ connections [--project ID] List project connections
24
+ discover --id CONNECTION List matching native sessions
25
+ sync --id CONNECTION Capture sessions; wait for job results
26
+ disconnect --id CONNECTION Remove only the managed MCP entry
27
+ sessions [--project ID] List captured sessions
28
+ import --file session.json Import normalized source events
29
+ checkpoint create --file FILE Create a reviewed, sourced checkpoint
30
+ checkpoint show --id ID Inspect a checkpoint
31
+ checkpoint packet --id ID Compile a bounded continuation packet
32
+ handoff offer --id ID --recipient codex
33
+ handoff show|export|accept|cancel --id ID [--recipient codex]
34
+ launch --id HANDOFF Start a fresh interactive Claude/Codex session
35
+ mcp --project ID --harness codex
36
+ demo Create an explicitly synthetic local example
37
+
38
+ Options: --data-dir PATH, --root PATH, --name NAME, --project ID,
39
+ --id ID, --file FILE, --recipient NAME, --harness NAME,
40
+ --max-bytes 24000, --port 0, --help, --version
41
+ The npm runtime uses Node 22.16+; it does not require Python.
42
+ `;
43
+ async function main() {
44
+ const { values, positionals } = parseArgs({
45
+ allowPositionals: true,
46
+ options: {
47
+ "data-dir": { type: "string" },
48
+ source: { type: "string" },
49
+ executable: { type: "string" },
50
+ "no-open": { type: "boolean" },
51
+ "capture-only": { type: "boolean" },
52
+ root: { type: "string" },
53
+ name: { type: "string" },
54
+ project: { type: "string" },
55
+ id: { type: "string" },
56
+ file: { type: "string" },
57
+ recipient: { type: "string" },
58
+ harness: { type: "string" },
59
+ "max-bytes": { type: "string" },
60
+ port: { type: "string" },
61
+ help: { type: "boolean" },
62
+ version: { type: "boolean" },
63
+ },
64
+ });
65
+ const [command, action] = positionals;
66
+ const directory = dataDirectory(values["data-dir"]);
67
+ const print = (value) => console.log(JSON.stringify(value, null, 2));
68
+ const need = (name) => {
69
+ const value = values[name];
70
+ if (typeof value !== "string" || !value)
71
+ throw new RelayError("missing_option", `--${name} is required.`);
72
+ return value;
73
+ };
74
+ const input = () => {
75
+ const path = resolve(need("file"));
76
+ const text = readFileSync(path, "utf8");
77
+ if (Buffer.byteLength(text) > 4 * 1024 * 1024)
78
+ throw new RelayError("file_too_large", "Input exceeds 4 MiB. Import in smaller batches.");
79
+ return JSON.parse(text);
80
+ };
81
+ if (values.version) {
82
+ console.log(VERSION);
83
+ return;
84
+ }
85
+ if (values.help || !command || command === "help") {
86
+ console.log(help);
87
+ return;
88
+ }
89
+ if (command === "serve") {
90
+ const port = Number(values.port ?? 0);
91
+ if (!Number.isInteger(port) || port < 0 || port > 65535)
92
+ throw new RelayError("invalid_port", "Port must be an integer from 0 through 65535.");
93
+ const service = await startService(directory, port);
94
+ print({ ...service.connection, dataDirectory: directory });
95
+ const shutdown = () => {
96
+ void service.close();
97
+ };
98
+ process.once("SIGINT", shutdown);
99
+ process.once("SIGTERM", shutdown);
100
+ await service.closed;
101
+ return;
102
+ }
103
+ if (command === "doctor") {
104
+ const recovered = recoverDeadLock(directory);
105
+ let service = null;
106
+ try {
107
+ service = (await connect(directory)).connection;
108
+ }
109
+ catch {
110
+ /* Doctor works while stopped. */
111
+ }
112
+ print({
113
+ version: VERSION,
114
+ node: process.version,
115
+ dataDirectory: directory,
116
+ service,
117
+ recoveredDeadLock: recovered,
118
+ lockPresent: existsSync(join(directory, "service.lock")),
119
+ });
120
+ return;
121
+ }
122
+ if (command === "status" || command === "stop") {
123
+ const client = await connect(directory);
124
+ print(command === "status"
125
+ ? await client.request("/api/health")
126
+ : await client.request("/api/shutdown", {}));
127
+ return;
128
+ }
129
+ const supported = [
130
+ "connect",
131
+ "connections",
132
+ "discover",
133
+ "sync",
134
+ "disconnect",
135
+ "launch",
136
+ "ui",
137
+ "init",
138
+ "project",
139
+ "sessions",
140
+ "import",
141
+ "checkpoint",
142
+ "handoff",
143
+ "mcp",
144
+ "demo",
145
+ ];
146
+ if (!supported.includes(command))
147
+ throw new RelayError("unknown_command", `Unknown command: ${command}. Run relay --help.`);
148
+ const client = await ensureService(directory);
149
+ if (command === "connect") {
150
+ print(await client.request("/api/connections", {
151
+ projectId: need("project"),
152
+ harness: need("harness"),
153
+ configureMcp: !values["capture-only"],
154
+ ...(values.source ? { source: values.source } : {}),
155
+ ...(values.executable ? { executable: values.executable } : {}),
156
+ }));
157
+ return;
158
+ }
159
+ if (command === "connections") {
160
+ print(await client.request("/api/connections" +
161
+ (values.project
162
+ ? "?projectId=" + encodeURIComponent(values.project)
163
+ : "")));
164
+ return;
165
+ }
166
+ if (command === "discover" || command === "disconnect") {
167
+ print(await client.request("/api/connections/" + encodeURIComponent(need("id")) + "/" + command, command === "disconnect" ? {} : undefined));
168
+ return;
169
+ }
170
+ if (command === "sync") {
171
+ let job = await client.request("/api/connections/" + encodeURIComponent(need("id")) + "/sync", {});
172
+ while (job.state === "running") {
173
+ await new Promise((resolve) => setTimeout(resolve, 300));
174
+ job = await client.request("/api/jobs/" + job.id);
175
+ }
176
+ print(job);
177
+ if (job.state !== "completed" || job.errors.length)
178
+ process.exitCode = 1;
179
+ return;
180
+ }
181
+ if (command === "launch") {
182
+ const handoff = await client.request("/api/handoffs/" + encodeURIComponent(need("id")));
183
+ const projects = await client.request("/api/projects");
184
+ const project = projects.find((project) => project.id === handoff.packet.project.id);
185
+ if (!project)
186
+ throw new RelayError("not_found", "The handoff project is unavailable.");
187
+ const connections = await client.request("/api/connections?projectId=" + encodeURIComponent(project.id));
188
+ const connection = connections.find((c) => c.harness === handoff.recipient && c.configureMcp);
189
+ if (!connection)
190
+ throw new RelayError("connection_required", "Connect the destination harness with project MCP configuration first.");
191
+ const fresh = await client.request("/api/checkpoints/" + handoff.checkpointId + "/freshness");
192
+ if (!fresh.current)
193
+ throw new RelayError("stale_source", fresh.reasons.join(" "));
194
+ const plan = launchPlan(handoff, project.root, connection.executable);
195
+ if (process.platform === "win32" &&
196
+ !plan.command.toLowerCase().endsWith(".exe"))
197
+ throw new RelayError("native_executable_required", "On Windows, connect a native .exe path with --executable, or use the continuation prompt.");
198
+ const code = await new Promise((resolve, reject) => {
199
+ const child = spawn(plan.command, plan.args, {
200
+ cwd: plan.cwd,
201
+ stdio: "inherit",
202
+ shell: false,
203
+ });
204
+ child.once("error", () => reject(new RelayError("launch_failed", "Harness could not start. Check its executable path and installation.")));
205
+ child.once("exit", (code) => resolve(code ?? 1));
206
+ });
207
+ process.exitCode = code;
208
+ return;
209
+ }
210
+ if (command === "ui") {
211
+ const url = client.connection.url + "/#token=" + client.token;
212
+ if (!values["no-open"]) {
213
+ const launcher = process.platform === "darwin"
214
+ ? "open"
215
+ : process.platform === "win32"
216
+ ? "rundll32.exe"
217
+ : "xdg-open";
218
+ const args = process.platform === "win32"
219
+ ? ["url.dll,FileProtocolHandler", url]
220
+ : [url];
221
+ const child = spawn(launcher, args, { stdio: "ignore", detached: true });
222
+ child.on("error", () => console.error("Could not open a browser. Use the local URL printed below."));
223
+ child.unref();
224
+ }
225
+ console.log(url);
226
+ return;
227
+ }
228
+ if (command === "init") {
229
+ print({
230
+ ...client.connection,
231
+ dataDirectory: directory,
232
+ next: "relay project add --root /path/to/project",
233
+ });
234
+ return;
235
+ }
236
+ if (command === "project" && action === "add") {
237
+ print(await client.request("/api/projects", {
238
+ root: need("root"),
239
+ ...(values.name ? { name: values.name } : {}),
240
+ }));
241
+ return;
242
+ }
243
+ if (command === "project" && action === "list") {
244
+ print(await client.request("/api/projects"));
245
+ return;
246
+ }
247
+ if (command === "sessions") {
248
+ print(await client.request("/api/sessions" +
249
+ (values.project
250
+ ? "?projectId=" + encodeURIComponent(values.project)
251
+ : "")));
252
+ return;
253
+ }
254
+ if (command === "import") {
255
+ print(await client.request("/api/sessions/import", input()));
256
+ return;
257
+ }
258
+ if (command === "checkpoint") {
259
+ if (action === "create") {
260
+ print(await client.request("/api/checkpoints", input()));
261
+ return;
262
+ }
263
+ if (action === "show") {
264
+ print(await client.request("/api/checkpoints/" + encodeURIComponent(need("id"))));
265
+ return;
266
+ }
267
+ if (action === "packet") {
268
+ print(await client.request("/api/checkpoints/" +
269
+ encodeURIComponent(need("id")) +
270
+ "/packet?maxBytes=" +
271
+ encodeURIComponent(values["max-bytes"] ?? "24000")));
272
+ return;
273
+ }
274
+ }
275
+ if (command === "handoff") {
276
+ const handoffId = need("id");
277
+ if (action === "offer") {
278
+ print(await client.request("/api/handoffs", {
279
+ checkpointId: handoffId,
280
+ recipient: need("recipient"),
281
+ maxBytes: Number(values["max-bytes"] ?? 24000),
282
+ }));
283
+ return;
284
+ }
285
+ if (action === "export") {
286
+ const exported = await client.request("/api/handoffs/" + encodeURIComponent(handoffId) + "/export");
287
+ console.log(exported.markdown);
288
+ return;
289
+ }
290
+ if (action === "show") {
291
+ print(await client.request("/api/handoffs/" + encodeURIComponent(handoffId)));
292
+ return;
293
+ }
294
+ if (action === "accept") {
295
+ print(await client.request("/api/handoffs/" + encodeURIComponent(handoffId) + "/accept", { recipient: need("recipient") }));
296
+ return;
297
+ }
298
+ if (action === "cancel") {
299
+ print(await client.request("/api/handoffs/" + encodeURIComponent(handoffId) + "/cancel", {}));
300
+ return;
301
+ }
302
+ }
303
+ if (command === "mcp") {
304
+ await client.request("/api/projects").then((projects) => {
305
+ if (!projects.some((p) => p.id === need("project")))
306
+ throw new RelayError("not_found", "Register the MCP project first.");
307
+ });
308
+ await serveMcp(client, need("harness"), need("project"));
309
+ return;
310
+ }
311
+ if (command === "demo") {
312
+ const root = join(directory, "demo-project");
313
+ mkdirSync(root, { recursive: true });
314
+ const project = await client.request("/api/projects", {
315
+ root,
316
+ name: "Example · webhook recovery",
317
+ });
318
+ const imported = await client.request("/api/sessions/import", {
319
+ projectId: project.id,
320
+ harness: "claude",
321
+ externalId: "relay-synthetic-demo",
322
+ title: "Example · duplicate webhook investigation",
323
+ events: [
324
+ {
325
+ id: "user-1",
326
+ role: "user",
327
+ text: "Prevent duplicate webhook charges. Keep idempotency keys for 24 hours.",
328
+ },
329
+ {
330
+ id: "user-2",
331
+ role: "user",
332
+ text: "Correction: retain idempotency keys for 7 days.",
333
+ },
334
+ {
335
+ id: "tool-3",
336
+ role: "tool",
337
+ kind: "tool_result",
338
+ text: "Synthetic fixture: duplicate-delivery test is failing; retry implementation remains open.",
339
+ },
340
+ ],
341
+ });
342
+ await client.request("/api/sessions/import", {
343
+ projectId: project.id,
344
+ harness: "codex",
345
+ externalId: "relay-synthetic-codex",
346
+ title: "Example · retry boundary review",
347
+ events: [
348
+ {
349
+ id: "codex-1",
350
+ role: "user",
351
+ text: "Synthetic example: inspect retry boundaries for webhook delivery. Preserve the provider event ID as the idempotency key.",
352
+ },
353
+ {
354
+ id: "codex-2",
355
+ role: "assistant",
356
+ text: "The receiver needs a durable claim before applying a charge. A crash after the charge must not produce a second charge on retry.",
357
+ },
358
+ {
359
+ id: "codex-3",
360
+ role: "tool",
361
+ kind: "tool_result",
362
+ text: "Synthetic fixture: concurrency and crash-recovery scenarios have not been executed. These remain acceptance criteria.",
363
+ },
364
+ ],
365
+ });
366
+ await client.request("/api/sessions/import", {
367
+ projectId: project.id,
368
+ harness: "goose",
369
+ externalId: "relay-synthetic-goose",
370
+ title: "Example · recovery acceptance checklist",
371
+ events: [
372
+ {
373
+ id: "goose-1",
374
+ role: "user",
375
+ text: "Synthetic example: prepare acceptance criteria for the next agent. Include simultaneous duplicate deliveries and a retry after worker restart.",
376
+ },
377
+ {
378
+ id: "goose-2",
379
+ role: "assistant",
380
+ text: "Pending checks: exactly one charge per provider event, retention for seven days, and safe replay after a process restart. No implementation or test execution is claimed.",
381
+ },
382
+ ],
383
+ });
384
+ const session = imported.session;
385
+ const checkpoint = await client.request("/api/checkpoints", {
386
+ projectId: project.id,
387
+ title: "Example · resume webhook recovery",
388
+ goal: "Prevent duplicate webhook charges.",
389
+ sources: [{ sessionId: session.id, revision: session.revision }],
390
+ instructions: [
391
+ {
392
+ key: "retention",
393
+ text: "Retain idempotency keys for 7 days.",
394
+ sources: [{ sessionId: session.id, eventId: "user-2" }],
395
+ },
396
+ ],
397
+ criteria: ["Duplicate-delivery checks pass."],
398
+ openWork: ["Implement retry handling.", "Run the webhook tests."],
399
+ evidence: [{ sessionId: session.id, eventId: "tool-3" }],
400
+ });
401
+ const handoff = await client.request("/api/handoffs", {
402
+ checkpointId: checkpoint.id,
403
+ recipient: "codex",
404
+ });
405
+ print({
406
+ synthetic: true,
407
+ projectId: project.id,
408
+ sessionId: session.id,
409
+ checkpointId: checkpoint.id,
410
+ handoffId: handoff.id,
411
+ status: handoff.status,
412
+ note: "No model calls or real task verification. Use handoff accept or the MCP tools to inspect the receipt.",
413
+ });
414
+ return;
415
+ }
416
+ throw new RelayError("unknown_command", "Unknown command or action. Run relay --help.");
417
+ }
418
+ main().catch((error) => {
419
+ console.error(JSON.stringify({
420
+ error: {
421
+ code: error instanceof RelayError ? error.code : "failed",
422
+ message: error instanceof Error ? error.message : "Operation failed.",
423
+ },
424
+ }));
425
+ process.exitCode = 1;
426
+ });
427
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../packages/relay/src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type Connection } from "./server.js";
2
+ export declare class RelayClient {
3
+ readonly connection: Connection;
4
+ readonly token: string;
5
+ constructor(connection: Connection, token: string);
6
+ request<T = unknown>(path: string, body?: unknown): Promise<T>;
7
+ }
8
+ export declare function connect(directory: string): Promise<RelayClient>;
9
+ /** Only remove a dead process's lock. Never signal or kill an existing process. */
10
+ export declare function recoverDeadLock(directory: string): boolean;
11
+ export declare function ensureService(directory: string): Promise<RelayClient>;
package/dist/client.js ADDED
@@ -0,0 +1,136 @@
1
+ import { spawn } from "node:child_process";
2
+ import { closeSync, existsSync, openSync, lstatSync, readFileSync, unlinkSync, } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { RelayError } from "./model.js";
6
+ import { secret } from "./server.js";
7
+ import { privateDirectory } from "./util.js";
8
+ export class RelayClient {
9
+ connection;
10
+ token;
11
+ constructor(connection, token) {
12
+ this.connection = connection;
13
+ this.token = token;
14
+ const url = new URL(connection.url);
15
+ if (url.protocol !== "http:" ||
16
+ url.hostname !== "127.0.0.1" ||
17
+ url.pathname !== "/" ||
18
+ url.username ||
19
+ url.password ||
20
+ url.search ||
21
+ url.hash)
22
+ throw new RelayError("invalid_connection", "Local service descriptor has an invalid URL.");
23
+ }
24
+ async request(path, body) {
25
+ if (!path.startsWith("/api/"))
26
+ throw new RelayError("invalid_path", "Relay API path required.");
27
+ const response = await fetch(this.connection.url + path, {
28
+ method: body === undefined ? "GET" : "POST",
29
+ headers: {
30
+ Authorization: `Bearer ${this.token}`,
31
+ ...(body === undefined ? {} : { "Content-Type": "application/json" }),
32
+ },
33
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
34
+ signal: AbortSignal.timeout(15000),
35
+ redirect: "error",
36
+ });
37
+ const data = (await response.json());
38
+ if (!response.ok)
39
+ throw new RelayError(data.error?.code ?? "service_error", data.error?.message ?? "Local service request failed.", response.status);
40
+ return data;
41
+ }
42
+ }
43
+ export async function connect(directory) {
44
+ const connection = JSON.parse(readFileSync(join(directory, "connection.json"), "utf8"));
45
+ const client = new RelayClient(connection, secret(directory));
46
+ const health = await client.request("/api/health");
47
+ if (health.instance !== connection.instance ||
48
+ health.name !== "context-relay")
49
+ throw new RelayError("wrong_service", "The local service identity does not match.");
50
+ return client;
51
+ }
52
+ /** Only remove a dead process's lock. Never signal or kill an existing process. */
53
+ export function recoverDeadLock(directory) {
54
+ if (!existsSync(directory))
55
+ return false;
56
+ const recovery = join(directory, "recovery.lock");
57
+ let fd;
58
+ try {
59
+ fd = openSync(recovery, "wx", 0o600);
60
+ }
61
+ catch (error) {
62
+ if (error.code === "EEXIST")
63
+ return false;
64
+ throw error;
65
+ }
66
+ try {
67
+ return recoverLocked(directory);
68
+ }
69
+ finally {
70
+ closeSync(fd);
71
+ unlinkSync(recovery);
72
+ }
73
+ }
74
+ function recoverLocked(directory) {
75
+ const path = join(directory, "service.lock");
76
+ if (!existsSync(path))
77
+ return false;
78
+ let lock;
79
+ try {
80
+ lock = JSON.parse(readFileSync(path, "utf8"));
81
+ }
82
+ catch {
83
+ throw new RelayError("invalid_lock", "Service lock is unreadable; inspect it before recovery.");
84
+ }
85
+ if (!Number.isSafeInteger(lock.pid) || lock.pid <= 0)
86
+ throw new RelayError("invalid_lock", "Service lock has an invalid process ID.");
87
+ try {
88
+ process.kill(lock.pid, 0);
89
+ return false;
90
+ }
91
+ catch (error) {
92
+ if (error.code !== "ESRCH")
93
+ return false;
94
+ }
95
+ unlinkSync(path);
96
+ const connection = join(directory, "connection.json");
97
+ if (existsSync(connection))
98
+ unlinkSync(connection);
99
+ return true;
100
+ }
101
+ export async function ensureService(directory) {
102
+ try {
103
+ return await connect(directory);
104
+ }
105
+ catch {
106
+ /* Start only this profile's service. */
107
+ }
108
+ privateDirectory(directory);
109
+ recoverDeadLock(directory);
110
+ const logPath = join(directory, "service.log");
111
+ if (existsSync(logPath) && lstatSync(logPath).isSymbolicLink())
112
+ throw new RelayError("unsafe_path", "Service log cannot be a symlink.");
113
+ const log = openSync(logPath, "a", 0o600);
114
+ const child = spawn(process.execPath, [
115
+ fileURLToPath(new URL("./cli.js", import.meta.url)),
116
+ "serve",
117
+ "--data-dir",
118
+ directory,
119
+ "--port",
120
+ "0",
121
+ ], { detached: true, stdio: ["ignore", log, log], windowsHide: true });
122
+ child.on("error", () => { });
123
+ child.unref();
124
+ closeSync(log);
125
+ for (let attempt = 0; attempt < 50; attempt++) {
126
+ await new Promise((resolve) => setTimeout(resolve, 100));
127
+ try {
128
+ return await connect(directory);
129
+ }
130
+ catch {
131
+ /* The descriptor is written after listen. */
132
+ }
133
+ }
134
+ throw new RelayError("startup_failed", "Local service did not start. Run relay doctor and inspect service.log in this data directory.");
135
+ }
136
+ //# sourceMappingURL=client.js.map