@maximtop/opencode-debug-mode 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 (71) hide show
  1. package/ATTRIBUTION.md +5 -0
  2. package/CONTRIBUTING.md +48 -0
  3. package/LICENSE +21 -0
  4. package/README.md +76 -0
  5. package/SECURITY.md +17 -0
  6. package/assets/debug-agent.md +71 -0
  7. package/dist/chunk-2PCBVVWX.js +20 -0
  8. package/dist/chunk-2PCBVVWX.js.map +1 -0
  9. package/dist/cleanup/export.d.ts +23 -0
  10. package/dist/cleanup/service.d.ts +33 -0
  11. package/dist/cleanup/types.d.ts +235 -0
  12. package/dist/collector/auth.d.ts +3 -0
  13. package/dist/collector/body.d.ts +7 -0
  14. package/dist/collector/ingest.d.ts +8 -0
  15. package/dist/collector/router.d.ts +8 -0
  16. package/dist/collector/server.d.ts +25 -0
  17. package/dist/core/clock.d.ts +5 -0
  18. package/dist/core/constants.d.ts +20 -0
  19. package/dist/core/errors.d.ts +12 -0
  20. package/dist/core/result.d.ts +24 -0
  21. package/dist/core/schemas.d.ts +9 -0
  22. package/dist/errors-IQTPGK5R.js +7 -0
  23. package/dist/errors-IQTPGK5R.js.map +1 -0
  24. package/dist/evidence/read.d.ts +8 -0
  25. package/dist/evidence/sanitize.d.ts +9 -0
  26. package/dist/evidence/store.d.ts +65 -0
  27. package/dist/evidence/types.d.ts +83 -0
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.js +3652 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/investigation/schema.d.ts +184 -0
  32. package/dist/investigation/store.d.ts +30 -0
  33. package/dist/plugin.d.ts +8 -0
  34. package/dist/probes/extension-permissions.d.ts +8 -0
  35. package/dist/probes/helper.d.ts +22 -0
  36. package/dist/probes/registry.d.ts +18 -0
  37. package/dist/probes/remove.d.ts +16 -0
  38. package/dist/probes/template.d.ts +21 -0
  39. package/dist/probes/types.d.ts +24 -0
  40. package/dist/process/line-decoder.d.ts +26 -0
  41. package/dist/process/protocol.d.ts +40 -0
  42. package/dist/process/service.d.ts +46 -0
  43. package/dist/process/tree.d.ts +16 -0
  44. package/dist/process-supervisor.js +276 -0
  45. package/dist/process-supervisor.js.map +1 -0
  46. package/dist/run/service.d.ts +21 -0
  47. package/dist/session/atomic-json.d.ts +1 -0
  48. package/dist/session/manifest-store.d.ts +22 -0
  49. package/dist/session/orphan-recovery.d.ts +16 -0
  50. package/dist/session/paths.d.ts +11 -0
  51. package/dist/session/registry.d.ts +48 -0
  52. package/dist/session/secret-store.d.ts +7 -0
  53. package/dist/session/types.d.ts +296 -0
  54. package/dist/tools/cleanup-tool.d.ts +4 -0
  55. package/dist/tools/collector-tools.d.ts +17 -0
  56. package/dist/tools/common.d.ts +4 -0
  57. package/dist/tools/evidence-tools.d.ts +4 -0
  58. package/dist/tools/index.d.ts +30 -0
  59. package/dist/tools/probe-tools.d.ts +5 -0
  60. package/dist/tools/run-tools.d.ts +12 -0
  61. package/dist/tools/session-tools.d.ts +4 -0
  62. package/dist/tools/state-tools.d.ts +4 -0
  63. package/docs/architecture.md +15 -0
  64. package/docs/lifecycle.md +11 -0
  65. package/examples/chrome-extension/background.js +5 -0
  66. package/examples/chrome-extension/content.js +3 -0
  67. package/examples/cli/run.mjs +3 -0
  68. package/examples/firefox-extension/background.js +4 -0
  69. package/examples/firefox-extension/content.js +3 -0
  70. package/examples/web/app.js +5 -0
  71. package/package.json +80 -0
@@ -0,0 +1,276 @@
1
+ // src/process/supervisor-entry.ts
2
+ import { spawn as spawn2 } from "child_process";
3
+
4
+ // src/process/protocol.ts
5
+ import { z } from "zod";
6
+ var MAX_MESSAGE_BYTES = 64 * 1024;
7
+ var BoundedString = z.string().max(8192);
8
+ var StartMessageSchema = z.object({
9
+ type: z.literal("start"),
10
+ executable: BoundedString.min(1),
11
+ args: z.array(BoundedString).max(256),
12
+ cwd: BoundedString.min(1),
13
+ env: z.record(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), BoundedString).refine((value) => Object.keys(value).length <= 256),
14
+ timeoutMs: z.number().int().min(1).max(3e5),
15
+ ownerNonce: z.string().min(32).max(128).regex(/^[A-Za-z0-9_-]+$/)
16
+ }).strict();
17
+ var TerminateMessageSchema = z.object({ type: z.literal("terminate"), reason: BoundedString }).strict();
18
+ var ParentMessageSchema = z.discriminatedUnion("type", [StartMessageSchema, TerminateMessageSchema]);
19
+ var ChildMessageSchema = z.discriminatedUnion("type", [
20
+ z.object({ type: z.literal("ready") }).strict(),
21
+ z.object({ type: z.literal("started"), targetPid: z.number().int().positive() }).strict(),
22
+ z.object({
23
+ type: z.literal("result"),
24
+ targetPid: z.number().int().positive(),
25
+ exitCode: z.number().int().nullable(),
26
+ signal: z.string().max(64).nullable(),
27
+ timedOut: z.boolean(),
28
+ termination: z.object({
29
+ graceful: z.boolean(),
30
+ forced: z.boolean(),
31
+ remaining: z.boolean(),
32
+ durationMs: z.number().nonnegative(),
33
+ errors: z.array(z.string().max(256)).max(20)
34
+ }).strict().optional()
35
+ }).strict(),
36
+ z.object({ type: z.literal("failure"), code: z.string().max(128), message: z.string().max(512) }).strict()
37
+ ]);
38
+ function assertSize(value) {
39
+ let serialized;
40
+ try {
41
+ serialized = JSON.stringify(value);
42
+ } catch {
43
+ throw new Error("IPC message is not serializable");
44
+ }
45
+ if (Buffer.byteLength(serialized) > MAX_MESSAGE_BYTES) throw new Error("IPC message exceeds 64 KiB");
46
+ }
47
+ function parseParentMessage(value) {
48
+ assertSize(value);
49
+ return ParentMessageSchema.parse(value);
50
+ }
51
+ function parseChildMessage(value) {
52
+ assertSize(value);
53
+ return ChildMessageSchema.parse(value);
54
+ }
55
+
56
+ // src/process/tree.ts
57
+ import { spawn } from "child_process";
58
+ import { performance } from "perf_hooks";
59
+
60
+ // src/core/constants.ts
61
+ var LIMITS = Object.freeze({
62
+ requestBytes: 64 * 1024,
63
+ scalarBytes: 8 * 1024,
64
+ events: 25e3,
65
+ evidenceBytes: 25 * 1024 * 1024,
66
+ checkpointBytes: 256 * 1024,
67
+ eventsPerBatch: 100,
68
+ idleMs: 30 * 60 * 1e3,
69
+ collectorReadyMs: 2e3,
70
+ cleanupMs: 5e3,
71
+ gracefulKillMs: 750,
72
+ forcedKillMs: 1500,
73
+ noSignalIterations: 3
74
+ });
75
+
76
+ // src/process/tree.ts
77
+ var defaultExecute = (executable, args) => new Promise((resolve, reject) => {
78
+ const child = spawn(executable, args, { shell: false, windowsHide: true, stdio: "ignore" });
79
+ child.once("error", reject);
80
+ child.once("exit", (exitCode) => resolve({ exitCode }));
81
+ });
82
+ function isAlive(pid) {
83
+ try {
84
+ process.kill(pid, 0);
85
+ return true;
86
+ } catch (error) {
87
+ return error.code !== "ESRCH";
88
+ }
89
+ }
90
+ async function waitForExit(pid, milliseconds) {
91
+ const deadline = performance.now() + milliseconds;
92
+ while (performance.now() < deadline) {
93
+ if (!isAlive(pid)) return true;
94
+ await new Promise((resolve) => setTimeout(resolve, 20));
95
+ }
96
+ return !isAlive(pid);
97
+ }
98
+ function safeError(error) {
99
+ const code = error.code;
100
+ return typeof code === "string" ? code.slice(0, 64) : "termination-failed";
101
+ }
102
+ async function terminateTree(targetPid, options = {}) {
103
+ const started2 = performance.now();
104
+ const errors = [];
105
+ let graceful = false;
106
+ let forced = false;
107
+ const platform = options.platform ?? process.platform;
108
+ const gracefulMs = options.gracefulMs ?? LIMITS.gracefulKillMs;
109
+ const forceMs = options.forceMs ?? LIMITS.forcedKillMs;
110
+ if (!Number.isInteger(targetPid) || targetPid <= 0) {
111
+ return {
112
+ graceful: false,
113
+ forced: false,
114
+ remaining: false,
115
+ durationMs: performance.now() - started2,
116
+ errors: ["invalid-pid"]
117
+ };
118
+ }
119
+ if (platform === "win32") {
120
+ const execute = options.execute ?? defaultExecute;
121
+ try {
122
+ const result = await execute("taskkill", ["/PID", String(targetPid), "/T"]);
123
+ graceful = result.exitCode === 0;
124
+ } catch (error) {
125
+ errors.push(safeError(error));
126
+ }
127
+ await waitForExit(targetPid, gracefulMs);
128
+ try {
129
+ const result = await execute("taskkill", ["/PID", String(targetPid), "/T", "/F"]);
130
+ forced = result.exitCode === 0;
131
+ } catch (error) {
132
+ errors.push(safeError(error));
133
+ }
134
+ await waitForExit(targetPid, forceMs);
135
+ } else {
136
+ try {
137
+ process.kill(-targetPid, "SIGTERM");
138
+ graceful = true;
139
+ } catch (error) {
140
+ if (error.code === "ESRCH") {
141
+ return { graceful: false, forced: false, remaining: false, durationMs: performance.now() - started2, errors };
142
+ }
143
+ errors.push(safeError(error));
144
+ }
145
+ if (!await waitForExit(targetPid, gracefulMs)) {
146
+ try {
147
+ process.kill(-targetPid, "SIGKILL");
148
+ forced = true;
149
+ } catch (error) {
150
+ if (error.code !== "ESRCH") errors.push(safeError(error));
151
+ }
152
+ await waitForExit(targetPid, forceMs);
153
+ }
154
+ }
155
+ return {
156
+ graceful,
157
+ forced,
158
+ remaining: isAlive(targetPid),
159
+ durationMs: performance.now() - started2,
160
+ errors
161
+ };
162
+ }
163
+
164
+ // src/process/supervisor-entry.ts
165
+ var target;
166
+ var started = false;
167
+ var finishing = false;
168
+ var timedOut = false;
169
+ var timeout;
170
+ var targetResult;
171
+ function safeSend(message) {
172
+ const parsed = parseChildMessage(message);
173
+ if (process.connected && process.send !== void 0) process.send(parsed);
174
+ }
175
+ function fail(code, message) {
176
+ safeSend({ type: "failure", code: code.slice(0, 128), message: message.slice(0, 512) });
177
+ }
178
+ async function finish(termination) {
179
+ if (finishing) return;
180
+ finishing = true;
181
+ if (timeout !== void 0) clearTimeout(timeout);
182
+ if (target === void 0 || target.pid === void 0 || targetResult === void 0) {
183
+ process.exitCode = 1;
184
+ return;
185
+ }
186
+ const result = await targetResult;
187
+ safeSend({
188
+ type: "result",
189
+ targetPid: target.pid,
190
+ exitCode: result.exitCode,
191
+ signal: result.signal,
192
+ timedOut,
193
+ ...termination === void 0 ? {} : { termination }
194
+ });
195
+ process.exit(0);
196
+ }
197
+ async function terminate(reason) {
198
+ if (finishing) return;
199
+ if (target === void 0 || target.pid === void 0) {
200
+ fail("NO_TARGET", reason);
201
+ process.exit(1);
202
+ }
203
+ const result = await terminateTree(target.pid);
204
+ await finish(result);
205
+ }
206
+ function startTarget(message) {
207
+ if (started) {
208
+ fail("SECOND_START", "The supervisor accepts exactly one start message");
209
+ void terminate("second-start");
210
+ return;
211
+ }
212
+ started = true;
213
+ try {
214
+ const child = spawn2(message.executable, message.args, {
215
+ cwd: message.cwd,
216
+ env: { ...process.env, ...message.env },
217
+ detached: true,
218
+ shell: false,
219
+ windowsHide: true,
220
+ stdio: ["ignore", "pipe", "pipe"]
221
+ });
222
+ target = child;
223
+ child.stdout?.on("data", (chunk) => process.stdout.write(chunk));
224
+ child.stderr?.on("data", (chunk) => process.stderr.write(chunk));
225
+ targetResult = new Promise((resolve) => {
226
+ child.once("exit", (exitCode, signal) => resolve({ exitCode, signal }));
227
+ });
228
+ child.once("error", () => {
229
+ fail("TARGET_ERROR", "Target process emitted an error");
230
+ void terminate("target-error");
231
+ });
232
+ if (child.pid === void 0) {
233
+ fail("SPAWN_FAILED", "Target process did not receive a PID");
234
+ process.exit(1);
235
+ }
236
+ safeSend({ type: "started", targetPid: child.pid });
237
+ } catch {
238
+ fail("SPAWN_FAILED", "Target process could not be started");
239
+ process.exit(1);
240
+ }
241
+ timeout = setTimeout(() => {
242
+ timedOut = true;
243
+ void terminate("timeout");
244
+ }, message.timeoutMs);
245
+ timeout.unref();
246
+ void targetResult.then(() => finish());
247
+ }
248
+ if (process.send === void 0) {
249
+ process.exitCode = 1;
250
+ } else {
251
+ safeSend({ type: "ready" });
252
+ const startDeadline = setTimeout(() => {
253
+ fail("START_TIMEOUT", "No start message arrived within two seconds");
254
+ process.exit(1);
255
+ }, 2e3);
256
+ startDeadline.unref();
257
+ process.on("message", (value) => {
258
+ try {
259
+ const message = parseParentMessage(value);
260
+ if (message.type === "start") {
261
+ clearTimeout(startDeadline);
262
+ startTarget(message);
263
+ } else {
264
+ void terminate(message.reason);
265
+ }
266
+ } catch {
267
+ fail("INVALID_MESSAGE", "Supervisor message was invalid");
268
+ void terminate("invalid-message");
269
+ }
270
+ });
271
+ process.once("disconnect", () => void terminate("parent-disconnect"));
272
+ process.once("SIGTERM", () => void terminate("supervisor-sigterm"));
273
+ process.once("uncaughtException", () => void terminate("uncaught-exception"));
274
+ process.once("unhandledRejection", () => void terminate("unhandled-rejection"));
275
+ }
276
+ //# sourceMappingURL=process-supervisor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/process/supervisor-entry.ts","../src/process/protocol.ts","../src/process/tree.ts","../src/core/constants.ts"],"sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\"\nimport { type ChildMessage, type ParentMessage, parseChildMessage, parseParentMessage } from \"./protocol.js\"\nimport { type TerminationResult, terminateTree } from \"./tree.js\"\n\nlet target: ChildProcess | undefined\nlet started = false\nlet finishing = false\nlet timedOut = false\nlet timeout: NodeJS.Timeout | undefined\nlet targetResult: Promise<{ exitCode: number | null; signal: NodeJS.Signals | null }> | undefined\n\nfunction safeSend(message: ChildMessage): void {\n const parsed = parseChildMessage(message)\n if (process.connected && process.send !== undefined) process.send(parsed)\n}\n\nfunction fail(code: string, message: string): void {\n safeSend({ type: \"failure\", code: code.slice(0, 128), message: message.slice(0, 512) })\n}\n\nasync function finish(termination?: TerminationResult): Promise<void> {\n if (finishing) return\n finishing = true\n if (timeout !== undefined) clearTimeout(timeout)\n if (target === undefined || target.pid === undefined || targetResult === undefined) {\n process.exitCode = 1\n return\n }\n const result = await targetResult\n safeSend({\n type: \"result\",\n targetPid: target.pid,\n exitCode: result.exitCode,\n signal: result.signal,\n timedOut,\n ...(termination === undefined ? {} : { termination }),\n })\n process.exit(0)\n}\n\nasync function terminate(reason: string): Promise<void> {\n if (finishing) return\n if (target === undefined || target.pid === undefined) {\n fail(\"NO_TARGET\", reason)\n process.exit(1)\n }\n const result = await terminateTree(target.pid)\n await finish(result)\n}\n\nfunction startTarget(message: Extract<ParentMessage, { type: \"start\" }>): void {\n if (started) {\n fail(\"SECOND_START\", \"The supervisor accepts exactly one start message\")\n void terminate(\"second-start\")\n return\n }\n started = true\n try {\n const child = spawn(message.executable, message.args, {\n cwd: message.cwd,\n env: { ...process.env, ...message.env },\n detached: true,\n shell: false,\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n })\n target = child\n child.stdout?.on(\"data\", (chunk: Buffer) => process.stdout.write(chunk))\n child.stderr?.on(\"data\", (chunk: Buffer) => process.stderr.write(chunk))\n targetResult = new Promise((resolve) => {\n child.once(\"exit\", (exitCode, signal) => resolve({ exitCode, signal }))\n })\n child.once(\"error\", () => {\n fail(\"TARGET_ERROR\", \"Target process emitted an error\")\n void terminate(\"target-error\")\n })\n if (child.pid === undefined) {\n fail(\"SPAWN_FAILED\", \"Target process did not receive a PID\")\n process.exit(1)\n }\n safeSend({ type: \"started\", targetPid: child.pid })\n } catch {\n fail(\"SPAWN_FAILED\", \"Target process could not be started\")\n process.exit(1)\n }\n timeout = setTimeout(() => {\n timedOut = true\n void terminate(\"timeout\")\n }, message.timeoutMs)\n timeout.unref()\n void targetResult.then(() => finish())\n}\n\nif (process.send === undefined) {\n process.exitCode = 1\n} else {\n safeSend({ type: \"ready\" })\n const startDeadline = setTimeout(() => {\n fail(\"START_TIMEOUT\", \"No start message arrived within two seconds\")\n process.exit(1)\n }, 2_000)\n startDeadline.unref()\n\n process.on(\"message\", (value: unknown) => {\n try {\n const message = parseParentMessage(value)\n if (message.type === \"start\") {\n clearTimeout(startDeadline)\n startTarget(message)\n } else {\n void terminate(message.reason)\n }\n } catch {\n fail(\"INVALID_MESSAGE\", \"Supervisor message was invalid\")\n void terminate(\"invalid-message\")\n }\n })\n process.once(\"disconnect\", () => void terminate(\"parent-disconnect\"))\n process.once(\"SIGTERM\", () => void terminate(\"supervisor-sigterm\"))\n process.once(\"uncaughtException\", () => void terminate(\"uncaught-exception\"))\n process.once(\"unhandledRejection\", () => void terminate(\"unhandled-rejection\"))\n}\n","import { z } from \"zod\"\n\nconst MAX_MESSAGE_BYTES = 64 * 1024\nconst BoundedString = z.string().max(8_192)\n\nconst StartMessageSchema = z\n .object({\n type: z.literal(\"start\"),\n executable: BoundedString.min(1),\n args: z.array(BoundedString).max(256),\n cwd: BoundedString.min(1),\n env: z\n .record(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), BoundedString)\n .refine((value) => Object.keys(value).length <= 256),\n timeoutMs: z.number().int().min(1).max(300_000),\n ownerNonce: z\n .string()\n .min(32)\n .max(128)\n .regex(/^[A-Za-z0-9_-]+$/),\n })\n .strict()\n\nconst TerminateMessageSchema = z.object({ type: z.literal(\"terminate\"), reason: BoundedString }).strict()\n\nexport const ParentMessageSchema = z.discriminatedUnion(\"type\", [StartMessageSchema, TerminateMessageSchema])\n\nexport const ChildMessageSchema = z.discriminatedUnion(\"type\", [\n z.object({ type: z.literal(\"ready\") }).strict(),\n z.object({ type: z.literal(\"started\"), targetPid: z.number().int().positive() }).strict(),\n z\n .object({\n type: z.literal(\"result\"),\n targetPid: z.number().int().positive(),\n exitCode: z.number().int().nullable(),\n signal: z.string().max(64).nullable(),\n timedOut: z.boolean(),\n termination: z\n .object({\n graceful: z.boolean(),\n forced: z.boolean(),\n remaining: z.boolean(),\n durationMs: z.number().nonnegative(),\n errors: z.array(z.string().max(256)).max(20),\n })\n .strict()\n .optional(),\n })\n .strict(),\n z.object({ type: z.literal(\"failure\"), code: z.string().max(128), message: z.string().max(512) }).strict(),\n])\n\nexport type ParentMessage = z.infer<typeof ParentMessageSchema>\nexport type ChildMessage = z.infer<typeof ChildMessageSchema>\n\nfunction assertSize(value: unknown): void {\n let serialized: string\n try {\n serialized = JSON.stringify(value)\n } catch {\n throw new Error(\"IPC message is not serializable\")\n }\n if (Buffer.byteLength(serialized) > MAX_MESSAGE_BYTES) throw new Error(\"IPC message exceeds 64 KiB\")\n}\n\nexport function parseParentMessage(value: unknown): ParentMessage {\n assertSize(value)\n return ParentMessageSchema.parse(value)\n}\n\nexport function parseChildMessage(value: unknown): ChildMessage {\n assertSize(value)\n return ChildMessageSchema.parse(value)\n}\n","import { spawn } from \"node:child_process\"\nimport { performance } from \"node:perf_hooks\"\nimport { LIMITS } from \"../core/constants.js\"\n\nexport type Execute = (executable: string, args: string[]) => Promise<{ exitCode: number | null }>\n\nexport type TerminationResult = Readonly<{\n graceful: boolean\n forced: boolean\n remaining: boolean\n durationMs: number\n errors: string[]\n}>\n\nconst defaultExecute: Execute = (executable, args) =>\n new Promise((resolve, reject) => {\n const child = spawn(executable, args, { shell: false, windowsHide: true, stdio: \"ignore\" })\n child.once(\"error\", reject)\n child.once(\"exit\", (exitCode) => resolve({ exitCode }))\n })\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code !== \"ESRCH\"\n }\n}\n\nasync function waitForExit(pid: number, milliseconds: number): Promise<boolean> {\n const deadline = performance.now() + milliseconds\n while (performance.now() < deadline) {\n if (!isAlive(pid)) return true\n await new Promise<void>((resolve) => setTimeout(resolve, 20))\n }\n return !isAlive(pid)\n}\n\nfunction safeError(error: unknown): string {\n const code = (error as NodeJS.ErrnoException).code\n return typeof code === \"string\" ? code.slice(0, 64) : \"termination-failed\"\n}\n\nexport async function terminateTree(\n targetPid: number,\n options: {\n platform?: NodeJS.Platform\n execute?: Execute\n gracefulMs?: number\n forceMs?: number\n } = {},\n): Promise<TerminationResult> {\n const started = performance.now()\n const errors: string[] = []\n let graceful = false\n let forced = false\n const platform = options.platform ?? process.platform\n const gracefulMs = options.gracefulMs ?? LIMITS.gracefulKillMs\n const forceMs = options.forceMs ?? LIMITS.forcedKillMs\n\n if (!Number.isInteger(targetPid) || targetPid <= 0) {\n return {\n graceful: false,\n forced: false,\n remaining: false,\n durationMs: performance.now() - started,\n errors: [\"invalid-pid\"],\n }\n }\n\n if (platform === \"win32\") {\n const execute = options.execute ?? defaultExecute\n try {\n const result = await execute(\"taskkill\", [\"/PID\", String(targetPid), \"/T\"])\n graceful = result.exitCode === 0\n } catch (error) {\n errors.push(safeError(error))\n }\n await waitForExit(targetPid, gracefulMs)\n try {\n const result = await execute(\"taskkill\", [\"/PID\", String(targetPid), \"/T\", \"/F\"])\n forced = result.exitCode === 0\n } catch (error) {\n errors.push(safeError(error))\n }\n await waitForExit(targetPid, forceMs)\n } else {\n try {\n process.kill(-targetPid, \"SIGTERM\")\n graceful = true\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ESRCH\") {\n return { graceful: false, forced: false, remaining: false, durationMs: performance.now() - started, errors }\n }\n errors.push(safeError(error))\n }\n if (!(await waitForExit(targetPid, gracefulMs))) {\n try {\n process.kill(-targetPid, \"SIGKILL\")\n forced = true\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ESRCH\") errors.push(safeError(error))\n }\n await waitForExit(targetPid, forceMs)\n }\n }\n\n return {\n graceful,\n forced,\n remaining: isAlive(targetPid),\n durationMs: performance.now() - started,\n errors,\n }\n}\n","export const PACKAGE_ID = \"opencode-debug-mode\" as const\nexport const MANIFEST_SCHEMA_VERSION = 1 as const\nexport const STATE_SCHEMA_VERSION = 1 as const\nexport const EVENT_SCHEMA_VERSION = 1 as const\nexport const TEMP_BASE_NAME = \"opencode-debug-mode-v1\" as const\nexport const PROCESS_EVENT_PREFIX = \"__OPENCODE_DEBUG_EVENT_V1__\" as const\n\nexport const LIMITS = Object.freeze({\n requestBytes: 64 * 1024,\n scalarBytes: 8 * 1024,\n events: 25_000,\n evidenceBytes: 25 * 1024 * 1024,\n checkpointBytes: 256 * 1024,\n eventsPerBatch: 100,\n idleMs: 30 * 60 * 1000,\n collectorReadyMs: 2_000,\n cleanupMs: 5_000,\n gracefulKillMs: 750,\n forcedKillMs: 1_500,\n noSignalIterations: 3,\n})\n"],"mappings":"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","names":["spawn","started","spawn"]}
@@ -0,0 +1,21 @@
1
+ import type { Clock } from "../core/clock.js";
2
+ import type { CleanupManifest, ManifestRun } from "../session/types.js";
3
+ export interface RunPersistence {
4
+ read(): Promise<CleanupManifest>;
5
+ update(expectedRevision: number, mutate: (value: CleanupManifest) => CleanupManifest): Promise<CleanupManifest>;
6
+ }
7
+ export type LeaseFactory = (kind: "process" | "waiting") => Promise<() => void> | (() => void);
8
+ export declare class RunService {
9
+ private readonly persistence;
10
+ private readonly clock;
11
+ private readonly acquireLease?;
12
+ private readonly waitingReleases;
13
+ constructor(persistence: RunPersistence, clock?: Clock, acquireLease?: LeaseFactory | undefined);
14
+ start(input: {
15
+ label: "pre-fix" | "post-fix";
16
+ reproduction: string;
17
+ waitingForUser: boolean;
18
+ }): Promise<ManifestRun>;
19
+ require(runId: string): Promise<ManifestRun>;
20
+ complete(runId: string, status: "completed" | "failed" | "timed_out" | "cancelled"): Promise<ManifestRun>;
21
+ }
@@ -0,0 +1 @@
1
+ export declare function atomicWriteJson(filename: string, value: unknown, maximumBytes: number): Promise<number>;
@@ -0,0 +1,22 @@
1
+ import { type CleanupManifest } from "./types.js";
2
+ export declare function createInitialManifest(input: {
3
+ sessionId: string;
4
+ trustedSessionHash: string;
5
+ projectRoot: string;
6
+ sessionDir: string;
7
+ now: string;
8
+ keepArtifacts?: boolean;
9
+ retentionDestination?: string;
10
+ }): CleanupManifest;
11
+ export declare class ManifestStore {
12
+ private readonly filename;
13
+ private tail;
14
+ constructor(filename: string);
15
+ create(value: CleanupManifest): Promise<CleanupManifest>;
16
+ read(): Promise<CleanupManifest>;
17
+ update(expectedRevision: number, mutate: (value: CleanupManifest) => CleanupManifest): Promise<CleanupManifest>;
18
+ modify(mutate: (value: CleanupManifest) => CleanupManifest): Promise<CleanupManifest>;
19
+ private writeNext;
20
+ private verifyPaths;
21
+ private exclusive;
22
+ }
@@ -0,0 +1,16 @@
1
+ import type { DebugSession } from "./registry.js";
2
+ import type { CleanupManifest } from "./types.js";
3
+ export type OrphanRecoveryOptions = Readonly<{
4
+ tempBase: string;
5
+ now?: Date;
6
+ activeSessionDirs?: Set<string>;
7
+ cleanup?: (session: DebugSession, manifest: CleanupManifest) => Promise<void>;
8
+ }>;
9
+ export declare function recoverOrphans(options: OrphanRecoveryOptions): Promise<{
10
+ cleaned: string[];
11
+ ignored: string[];
12
+ errors: Array<{
13
+ directory: string;
14
+ reason: string;
15
+ }>;
16
+ }>;
@@ -0,0 +1,11 @@
1
+ export type SessionPaths = Readonly<{
2
+ baseDir: string;
3
+ sessionDir: string;
4
+ projectRoot: string;
5
+ manifestFile: string;
6
+ secretFile: string;
7
+ stateFile: string;
8
+ evidenceFile: string;
9
+ }>;
10
+ export declare function isContained(parent: string, child: string): boolean;
11
+ export declare function createSessionPaths(tempBase: string, projectRoot: string): Promise<SessionPaths>;
@@ -0,0 +1,48 @@
1
+ import type { Clock } from "../core/clock.js";
2
+ import { InvestigationStore } from "../investigation/store.js";
3
+ import { ManifestStore } from "./manifest-store.js";
4
+ import { type SessionPaths } from "./paths.js";
5
+ import { SecretStore } from "./secret-store.js";
6
+ export declare function trustedSessionHash(openCodeSessionId: string): string;
7
+ export type ProjectContext = Readonly<{
8
+ directory: string;
9
+ worktree: string;
10
+ }>;
11
+ export type DebugSession = Readonly<{
12
+ publicId: string;
13
+ trustedHash: string;
14
+ projectRoot: string;
15
+ directory: string;
16
+ paths: SessionPaths;
17
+ manifestStore: ManifestStore;
18
+ investigationStore: InvestigationStore;
19
+ secretStore: SecretStore;
20
+ secret: string;
21
+ }>;
22
+ export type RegistryCleanup = (session: DebugSession, reason: "idle-expired") => Promise<void>;
23
+ export declare class SessionRegistry {
24
+ private readonly tempBase;
25
+ private readonly clock;
26
+ private readonly cleanup?;
27
+ private readonly sessions;
28
+ private readonly timer;
29
+ private closed;
30
+ constructor(tempBase: string, clock?: Clock, cleanup?: RegistryCleanup | undefined);
31
+ start(trustedId: string, context: ProjectContext, options?: {
32
+ keepArtifacts?: boolean;
33
+ retentionDestination?: string;
34
+ }): Promise<DebugSession>;
35
+ requireOwned(trustedId: string): Promise<DebugSession>;
36
+ hasTrusted(trustedId: string): Promise<boolean>;
37
+ touch(trustedId: string): Promise<void>;
38
+ touchSession(sessionValue: DebugSession): Promise<void>;
39
+ acquireLease(trustedId: string, kind: "process" | "waiting"): Promise<() => void>;
40
+ acquireLeaseForSession(sessionValue: DebugSession, kind: "process" | "waiting"): () => void;
41
+ sweep(): Promise<void>;
42
+ listActive(): DebugSession[];
43
+ forgetTrusted(trustedId: string): void;
44
+ closeAll(): Promise<void>;
45
+ private findPersisted;
46
+ private pathsFromManifest;
47
+ private assertOpen;
48
+ }
@@ -0,0 +1,7 @@
1
+ export declare class SecretStore {
2
+ private readonly filename;
3
+ constructor(filename: string);
4
+ create(): Promise<string>;
5
+ read(): Promise<string>;
6
+ remove(): Promise<"success" | "already-clean">;
7
+ }