debuganything 0.0.0-stage → 1.0.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 (59) hide show
  1. package/.claude-plugin/plugin.json +15 -0
  2. package/DISCLAIMER.md +101 -0
  3. package/GEMINI.md +97 -0
  4. package/LICENSE +21 -0
  5. package/README.md +283 -2
  6. package/assets/README.md +9 -0
  7. package/assets/debuganything-logo.png +0 -0
  8. package/assets/debuganything-mark-512.png +0 -0
  9. package/dist/chunk-32IDKAZK.js +9 -0
  10. package/dist/chunk-32IDKAZK.js.map +1 -0
  11. package/dist/chunk-5BXTDASF.js +135 -0
  12. package/dist/chunk-5BXTDASF.js.map +1 -0
  13. package/dist/chunk-5RAKTYW5.js +292 -0
  14. package/dist/chunk-5RAKTYW5.js.map +1 -0
  15. package/dist/chunk-7MMXFUUN.js +79 -0
  16. package/dist/chunk-7MMXFUUN.js.map +1 -0
  17. package/dist/chunk-D742WJPT.js +436 -0
  18. package/dist/chunk-D742WJPT.js.map +1 -0
  19. package/dist/chunk-EMR42ZJQ.js +102 -0
  20. package/dist/chunk-EMR42ZJQ.js.map +1 -0
  21. package/dist/chunk-MLDOO673.js +1416 -0
  22. package/dist/chunk-MLDOO673.js.map +1 -0
  23. package/dist/chunk-NFL22T3H.js +309 -0
  24. package/dist/chunk-NFL22T3H.js.map +1 -0
  25. package/dist/chunk-STFC7C5C.js +245 -0
  26. package/dist/chunk-STFC7C5C.js.map +1 -0
  27. package/dist/chunk-VGYXHXTY.js +1015 -0
  28. package/dist/chunk-VGYXHXTY.js.map +1 -0
  29. package/dist/chunk-YY6M5RIJ.js +215 -0
  30. package/dist/chunk-YY6M5RIJ.js.map +1 -0
  31. package/dist/cli.js +254 -0
  32. package/dist/cli.js.map +1 -0
  33. package/dist/docker-ORXYMES4.js +23 -0
  34. package/dist/docker-ORXYMES4.js.map +1 -0
  35. package/dist/hook-QYLLBUNK.js +14 -0
  36. package/dist/hook-QYLLBUNK.js.map +1 -0
  37. package/dist/import-CT55AEEE.js +268 -0
  38. package/dist/import-CT55AEEE.js.map +1 -0
  39. package/dist/investigate-2GFD6EWB.js +102 -0
  40. package/dist/investigate-2GFD6EWB.js.map +1 -0
  41. package/dist/lsof-GBLZJBJR.js +22 -0
  42. package/dist/lsof-GBLZJBJR.js.map +1 -0
  43. package/dist/psmem-UB3VCMG3.js +20 -0
  44. package/dist/psmem-UB3VCMG3.js.map +1 -0
  45. package/dist/report_html-2ENM7HLC.js +99 -0
  46. package/dist/report_html-2ENM7HLC.js.map +1 -0
  47. package/dist/server.js +190 -0
  48. package/dist/server.js.map +1 -0
  49. package/dist/setup-NTPRUTMT.js +265 -0
  50. package/dist/setup-NTPRUTMT.js.map +1 -0
  51. package/dist/target-WJE4WFYA.js +36 -0
  52. package/dist/target-WJE4WFYA.js.map +1 -0
  53. package/gemini-extension.json +9 -0
  54. package/hooks/debuganything-hook.mjs +36 -0
  55. package/hooks/hooks.json +10 -0
  56. package/package.json +85 -3
  57. package/server.json +30 -0
  58. package/skills/debug-anything/SKILL.md +102 -0
  59. package/skills/inspect-product/SKILL.md +138 -0
@@ -0,0 +1,436 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ killTrackedChildren
4
+ } from "./chunk-STFC7C5C.js";
5
+
6
+ // src/contracts.ts
7
+ import { z } from "zod";
8
+ var TargetKind = z.enum(["pid", "container", "pod", "command"]);
9
+ var Target = z.object({
10
+ kind: TargetKind,
11
+ /** Opaque identifier as given by the user: "1234", "api-1", "ns/pod", "node app.js". */
12
+ id: z.string().min(1),
13
+ /** Host pid once resolved (for containers: the host-side pid of PID 1). */
14
+ pid: z.number().int().positive().optional(),
15
+ /** Process command name (comm). */
16
+ comm: z.string().optional(),
17
+ /** Full command line if known. */
18
+ cmdline: z.string().optional(),
19
+ /** Child pids that belong to the target tree at attach time. */
20
+ pids: z.array(z.number().int().positive()).optional(),
21
+ /** Detected runtime, used to pick an adapter: python → py-spy, node → inspector, ... */
22
+ runtime: z.enum(["python", "node", "java", "go", "native", "unknown"]).optional(),
23
+ /** Where the target lives. "local" today; "ssh://host" / "k8s://ctx" tomorrow. */
24
+ host: z.string().default("local"),
25
+ /** Container id / pod metadata when kind is container or pod. */
26
+ container: z.object({
27
+ runtime: z.enum(["docker", "podman", "containerd", "k8s"]),
28
+ id: z.string(),
29
+ name: z.string().optional(),
30
+ image: z.string().optional(),
31
+ namespace: z.string().optional()
32
+ }).optional()
33
+ });
34
+ var EvidenceKind = z.enum([
35
+ "syscall_stat",
36
+ "syscall_slow",
37
+ "syscall_error",
38
+ "stack",
39
+ "stack_summary",
40
+ "lock_wait",
41
+ "frame_hot",
42
+ "socket",
43
+ "socket_summary",
44
+ "fd",
45
+ "fd_summary",
46
+ "packet_summary",
47
+ "dns_query",
48
+ "tls_sni",
49
+ "metric",
50
+ "metric_series",
51
+ "heap_retainer",
52
+ "heap_summary",
53
+ "log_excerpt",
54
+ "process",
55
+ "process_list",
56
+ "container_state",
57
+ "cgroup",
58
+ "diff",
59
+ "readiness",
60
+ "note"
61
+ ]);
62
+ var Evidence = z.object({
63
+ /** Short stable id within a result, e.g. "e1", "e2". Findings reference these. */
64
+ id: z.string().min(1),
65
+ kind: EvidenceKind,
66
+ /** Human-readable one-liner so an auditor can read the evidence list alone. */
67
+ summary: z.string().min(1),
68
+ /** Structured, raw-ish facts. Kept small (the whole payload is capped). */
69
+ data: z.unknown(),
70
+ /** Path to the artifact this evidence was derived from, if any. */
71
+ artifact: z.string().optional(),
72
+ /** 1-based inclusive line range in the artifact backing this evidence. */
73
+ lines: z.tuple([z.number().int().min(1), z.number().int().min(1)]).optional()
74
+ });
75
+ var Severity = z.enum(["info", "warn", "high"]);
76
+ var Confidence = z.enum(["observed", "inferred"]);
77
+ var Finding = z.object({
78
+ id: z.string().min(1),
79
+ severity: Severity,
80
+ summary: z.string().min(1),
81
+ /** MUST be non-empty. A finding that cites nothing is not a finding. */
82
+ evidence_ids: z.array(z.string().min(1)).min(1, "a finding must cite at least one evidence id"),
83
+ /** "observed" = directly read from tool output; "inferred" = a reasoned step, must be stated. */
84
+ confidence: Confidence,
85
+ /** Optional longer explanation / reasoning. */
86
+ detail: z.string().optional()
87
+ });
88
+ var ArtifactKind = z.enum([
89
+ "raw_text",
90
+ "flamegraph_svg",
91
+ "folded_stacks",
92
+ "heapsnapshot",
93
+ "pcap",
94
+ "json",
95
+ "markdown",
96
+ "log"
97
+ ]);
98
+ var Artifact = z.object({
99
+ path: z.string().min(1),
100
+ kind: ArtifactKind,
101
+ bytes: z.number().int().min(0),
102
+ /** True if the output cap was hit and the artifact is truncated. */
103
+ truncated: z.boolean().optional(),
104
+ description: z.string().optional()
105
+ });
106
+ var ToolName = z.enum([
107
+ "readiness",
108
+ "attach",
109
+ "detach",
110
+ "list_processes",
111
+ "trace_syscalls",
112
+ "profile_cpu",
113
+ "dump_stacks",
114
+ "inspect_sockets",
115
+ "inspect_files",
116
+ "capture_traffic",
117
+ "snapshot_memory",
118
+ "diff_runs",
119
+ "container_info",
120
+ "report",
121
+ "import_artifact"
122
+ ]);
123
+ var ToolResult = z.object({
124
+ ok: z.boolean(),
125
+ tool: z.string().min(1),
126
+ /** "" for tools that run without a session (readiness, list_processes). */
127
+ session_id: z.string(),
128
+ target: Target,
129
+ duration_ms: z.number().int().min(0),
130
+ findings: z.array(Finding),
131
+ evidence: z.array(Evidence),
132
+ limitations: z.array(z.string()),
133
+ suggested_next: z.array(z.string()),
134
+ artifacts: z.array(Artifact),
135
+ /** Set when ok === false. */
136
+ error: z.string().optional(),
137
+ /** ISO timestamp of when the tool started. */
138
+ started_at: z.string().optional(),
139
+ /** Monotonic index within the session; used by diff_runs to address prior results. */
140
+ run_id: z.string().optional()
141
+ }).superRefine((r, ctx) => {
142
+ const ids = new Set(r.evidence.map((e) => e.id));
143
+ r.findings.forEach((f, i) => {
144
+ for (const eid of f.evidence_ids) {
145
+ if (!ids.has(eid)) {
146
+ ctx.addIssue({
147
+ code: "custom",
148
+ path: ["findings", i, "evidence_ids"],
149
+ message: `finding ${f.id} cites unknown evidence id "${eid}"`
150
+ });
151
+ }
152
+ }
153
+ });
154
+ });
155
+ var SessionState = z.enum(["attached", "detached"]);
156
+ var Session = z.object({
157
+ id: z.string().min(1),
158
+ state: SessionState,
159
+ target: Target,
160
+ created_at: z.string(),
161
+ detached_at: z.string().optional(),
162
+ /** Absolute path of the session directory under ~/.debuganything/sessions/. */
163
+ dir: z.string(),
164
+ /** Platform the session was created on. */
165
+ platform: z.enum(["linux", "darwin", "win32"]),
166
+ /** Ordered list of run ids, each one a saved ToolResult json in <dir>/runs/. */
167
+ runs: z.array(
168
+ z.object({
169
+ run_id: z.string(),
170
+ tool: z.string(),
171
+ ok: z.boolean(),
172
+ started_at: z.string(),
173
+ duration_ms: z.number().int().min(0),
174
+ file: z.string()
175
+ })
176
+ ),
177
+ /** For kind=command: pid of the spawned child, and where its output is tee'd. */
178
+ spawned: z.object({
179
+ pid: z.number().int().positive(),
180
+ stdout: z.string(),
181
+ stderr: z.string(),
182
+ exit_code: z.number().int().nullable().optional()
183
+ }).optional()
184
+ });
185
+ var ProviderStatus = z.enum(["ready", "missing", "no_permission", "unsupported_platform", "not_implemented"]);
186
+ var ProviderReadiness = z.object({
187
+ name: z.string(),
188
+ tools: z.array(z.string()),
189
+ status: ProviderStatus,
190
+ /** Binaries this provider needs, with resolution. */
191
+ requires: z.array(z.object({ binary: z.string(), path: z.string().nullable() })),
192
+ /** How to fix, if not ready. */
193
+ install_hint: z.string().optional(),
194
+ /** Extra detail, e.g. "ptrace_scope=1: can only trace children; run with sudo or set to 0". */
195
+ notes: z.array(z.string()).default([])
196
+ });
197
+ var Readiness = z.object({
198
+ os: z.enum(["linux", "darwin", "win32"]),
199
+ arch: z.string(),
200
+ kernel: z.string().optional(),
201
+ node: z.string(),
202
+ dea_version: z.string(),
203
+ is_root: z.boolean(),
204
+ has_sudo_noninteractive: z.boolean(),
205
+ permissions: z.object({
206
+ /** Linux: contents of /proc/sys/kernel/yama/ptrace_scope, or null when absent. */
207
+ ptrace_scope: z.number().int().nullable(),
208
+ /** Linux: CAP_SYS_PTRACE / CAP_NET_RAW effective, when determinable. */
209
+ capabilities: z.array(z.string()).default([]),
210
+ /** macOS: System Integrity Protection enabled. */
211
+ sip_enabled: z.boolean().nullable(),
212
+ /** macOS: whether the Developer Tools group lets us attach without sudo. */
213
+ developer_mode: z.boolean().nullable(),
214
+ /** Linux: perf_event_paranoid value. */
215
+ perf_event_paranoid: z.number().int().nullable()
216
+ }),
217
+ providers: z.array(ProviderReadiness),
218
+ /** Flattened: which MCP tools can be served right now. */
219
+ tools_available: z.array(z.string()),
220
+ tools_unavailable: z.array(z.object({ tool: z.string(), reason: z.string() })),
221
+ sessions_dir: z.string()
222
+ });
223
+ var ImportTool = z.enum(["strace", "fs_usage", "perf_script", "folded", "pyspy_dump", "sample", "gdb", "lldb", "lsof", "tcpdump", "jstack", "goroutines"]);
224
+ var SyscallCategory = z.enum(["network", "file", "process", "signal", "memory", "all"]);
225
+ var ToolArgs = {
226
+ readiness: z.object({}),
227
+ attach: z.object({
228
+ target: z.string().min(1).describe("pid, container name/id, k8s pod (ns/name), or a command line (with kind=command)"),
229
+ kind: TargetKind.optional().describe("Defaults to auto-detect: numeric \u2192 pid, else container, else command")
230
+ }),
231
+ detach: z.object({ session_id: z.string().min(1) }),
232
+ list_processes: z.object({
233
+ filter: z.string().optional().describe("Substring match on command / args / user"),
234
+ limit: z.number().int().min(1).max(500).default(100)
235
+ }),
236
+ trace_syscalls: z.object({
237
+ session_id: z.string().min(1),
238
+ duration_s: z.number().int().min(1).max(60).default(10),
239
+ categories: z.array(SyscallCategory).default(["all"])
240
+ }),
241
+ profile_cpu: z.object({
242
+ session_id: z.string().min(1),
243
+ duration_s: z.number().int().min(1).max(60).default(15),
244
+ /** Required when the profiler will use perf on a system the user flagged as production; always safe to pass. */
245
+ consent: z.boolean().optional()
246
+ }),
247
+ dump_stacks: z.object({ session_id: z.string().min(1) }),
248
+ inspect_sockets: z.object({ session_id: z.string().min(1) }),
249
+ inspect_files: z.object({
250
+ session_id: z.string().min(1),
251
+ window_s: z.number().int().min(0).max(30).default(3).describe("Observe fd growth over this many seconds (0 = single snapshot)")
252
+ }),
253
+ capture_traffic: z.object({
254
+ session_id: z.string().min(1),
255
+ duration_s: z.number().int().min(1).max(30).default(10),
256
+ filter: z.string().optional().describe("Extra BPF filter ANDed with the target's ports"),
257
+ consent: z.boolean().optional().describe("Required. Packet capture may contain secrets; ask the user first.")
258
+ }),
259
+ snapshot_memory: z.object({
260
+ session_id: z.string().min(1),
261
+ heap: z.boolean().default(true).describe("Take a heap snapshot where supported (Node). Set false for metrics only."),
262
+ window_s: z.number().int().min(0).max(30).default(3).describe("Sample RSS over this many seconds")
263
+ }),
264
+ diff_runs: z.object({
265
+ session_id: z.string().min(1),
266
+ a: z.string().min(1).describe("run id of the earlier result; '<session_id>:<run>' reads a baseline run from another session (e.g. a recorded healthy run)"),
267
+ b: z.string().min(1).describe("run id of the later result; '<session_id>:<run>' works here too")
268
+ }),
269
+ container_info: z.object({ session_id: z.string().min(1) }),
270
+ report: z.object({
271
+ session_id: z.string().min(1),
272
+ out: z.string().optional().describe("Optional path to also write the markdown to"),
273
+ html: z.boolean().optional().describe("Also render a self-contained report.html (shareable: ranked findings, expandable evidence, embedded flamegraphs)")
274
+ }),
275
+ import_artifact: z.object({
276
+ session_id: z.string().min(1),
277
+ tool: ImportTool.describe("What produced the file"),
278
+ path: z.string().min(1).describe("Path to the raw output file you captured yourself (e.g. with sudo)"),
279
+ duration_s: z.number().int().min(1).max(3600).optional().describe("How long the capture ran, when the file has no timestamps (profiles)")
280
+ })
281
+ };
282
+ function assertToolResult(r) {
283
+ const parsed = ToolResult.safeParse(r);
284
+ if (!parsed.success) {
285
+ throw new Error(`invalid ToolResult: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`);
286
+ }
287
+ return parsed.data;
288
+ }
289
+
290
+ // src/session.ts
291
+ import { mkdir, readFile, writeFile, readdir, rm, stat } from "fs/promises";
292
+ import { existsSync } from "fs";
293
+ import { homedir } from "os";
294
+ import { join } from "path";
295
+ import { randomBytes } from "crypto";
296
+ function deaHome() {
297
+ return process.env.DEA_HOME ?? join(homedir(), ".debuganything");
298
+ }
299
+ function sessionsDir() {
300
+ return join(deaHome(), "sessions");
301
+ }
302
+ function newSessionId() {
303
+ const d = /* @__PURE__ */ new Date();
304
+ const stamp = d.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "").replace("T", "-");
305
+ return `${stamp}-${randomBytes(3).toString("hex")}`;
306
+ }
307
+ function platform() {
308
+ const p = process.platform;
309
+ return p === "linux" || p === "darwin" || p === "win32" ? p : "linux";
310
+ }
311
+ async function createSession(target) {
312
+ const id = newSessionId();
313
+ const dir = join(sessionsDir(), id);
314
+ await mkdir(join(dir, "runs"), { recursive: true });
315
+ await mkdir(join(dir, "artifacts"), { recursive: true });
316
+ const session = {
317
+ id,
318
+ state: "attached",
319
+ target,
320
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
321
+ dir,
322
+ platform: platform(),
323
+ runs: []
324
+ };
325
+ await saveSession(session);
326
+ return session;
327
+ }
328
+ async function saveSession(session) {
329
+ await writeFile(join(session.dir, "session.json"), JSON.stringify(session, null, 2));
330
+ }
331
+ async function loadSession(id) {
332
+ if (!/^[A-Za-z0-9_-]{1,80}$/.test(id)) throw new Error(`invalid session id: ${JSON.stringify(id)}`);
333
+ const file = join(sessionsDir(), id, "session.json");
334
+ if (!existsSync(file)) throw new Error(`session not found: ${id}. Call attach first, or list sessions with \`debuganything sessions list\`.`);
335
+ const raw = JSON.parse(await readFile(file, "utf8"));
336
+ return Session.parse(raw);
337
+ }
338
+ async function requireAttached(id) {
339
+ const s = await loadSession(id);
340
+ if (s.state !== "attached") throw new Error(`session ${id} is detached; attach again to run tools`);
341
+ return s;
342
+ }
343
+ async function recordRun(session, result) {
344
+ const n = session.runs.length + 1;
345
+ const run_id = `${result.tool}#${n}`;
346
+ const file = join(session.dir, "runs", `${String(n).padStart(3, "0")}-${result.tool}.json`);
347
+ const withId = { ...result, run_id };
348
+ await writeFile(file, JSON.stringify(withId, null, 2));
349
+ session.runs.push({
350
+ run_id,
351
+ tool: result.tool,
352
+ ok: result.ok,
353
+ started_at: result.started_at ?? (/* @__PURE__ */ new Date()).toISOString(),
354
+ duration_ms: result.duration_ms,
355
+ file
356
+ });
357
+ await saveSession(session);
358
+ return withId;
359
+ }
360
+ async function loadRun(session, ref) {
361
+ let entry = session.runs.find((r) => r.run_id === ref);
362
+ if (!entry && /^\d+$/.test(ref)) entry = session.runs[Number(ref) - 1];
363
+ if (!entry) entry = [...session.runs].reverse().find((r) => r.tool === ref);
364
+ if (!entry) {
365
+ throw new Error(`run "${ref}" not found in session ${session.id}. Known runs: ${session.runs.map((r) => r.run_id).join(", ") || "(none)"}`);
366
+ }
367
+ return JSON.parse(await readFile(entry.file, "utf8"));
368
+ }
369
+ async function detachSession(session) {
370
+ killTrackedChildren();
371
+ session.state = "detached";
372
+ session.detached_at = (/* @__PURE__ */ new Date()).toISOString();
373
+ await saveSession(session);
374
+ return session;
375
+ }
376
+ async function listSessions() {
377
+ const dir = sessionsDir();
378
+ if (!existsSync(dir)) return [];
379
+ const ids = (await readdir(dir)).filter((d) => !d.startsWith("."));
380
+ const out = [];
381
+ for (const id of ids) {
382
+ try {
383
+ const s = await loadSession(id);
384
+ out.push({
385
+ id,
386
+ state: s.state,
387
+ target: `${s.target.kind}:${s.target.id}${s.target.comm ? ` (${s.target.comm})` : ""}`,
388
+ created_at: s.created_at,
389
+ runs: s.runs.length,
390
+ bytes: await dirSize(s.dir)
391
+ });
392
+ } catch {
393
+ }
394
+ }
395
+ return out.sort((a, b) => a.created_at.localeCompare(b.created_at));
396
+ }
397
+ async function dirSize(dir) {
398
+ let total = 0;
399
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
400
+ const p = join(dir, entry.name);
401
+ if (entry.isDirectory()) total += await dirSize(p);
402
+ else total += (await stat(p)).size;
403
+ }
404
+ return total;
405
+ }
406
+ async function pruneSessions(opts = {}) {
407
+ const days = opts.olderThanDays ?? 7;
408
+ const cutoff = Date.now() - days * 864e5;
409
+ const removed = [];
410
+ for (const s of await listSessions()) {
411
+ const old = new Date(s.created_at).getTime() < cutoff;
412
+ if (opts.all || s.state === "detached" && old) {
413
+ await rm(join(sessionsDir(), s.id), { recursive: true, force: true });
414
+ removed.push(s.id);
415
+ }
416
+ }
417
+ return removed;
418
+ }
419
+
420
+ export {
421
+ ToolName,
422
+ ToolArgs,
423
+ assertToolResult,
424
+ sessionsDir,
425
+ platform,
426
+ createSession,
427
+ saveSession,
428
+ loadSession,
429
+ requireAttached,
430
+ recordRun,
431
+ loadRun,
432
+ detachSession,
433
+ listSessions,
434
+ pruneSessions
435
+ };
436
+ //# sourceMappingURL=chunk-D742WJPT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/contracts.ts","../src/session.ts"],"sourcesContent":["/**\n * Debug Anything contracts — the single most important file.\n *\n * Every tool, whether invoked over MCP or from the CLI, returns a `ToolResult`.\n * Findings MUST cite evidence ids; the schema rejects findings that do not.\n * Agents only get to claim what the evidence supports.\n *\n * Nothing here assumes \"localhost\": targets are abstract (pid today, container\n * and pod now, remote host next), and every result carries the same shape.\n */\nimport { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Targets\n// ---------------------------------------------------------------------------\n\nexport const TargetKind = z.enum([\"pid\", \"container\", \"pod\", \"command\"]);\nexport type TargetKind = z.infer<typeof TargetKind>;\n\nexport const Target = z.object({\n kind: TargetKind,\n /** Opaque identifier as given by the user: \"1234\", \"api-1\", \"ns/pod\", \"node app.js\". */\n id: z.string().min(1),\n /** Host pid once resolved (for containers: the host-side pid of PID 1). */\n pid: z.number().int().positive().optional(),\n /** Process command name (comm). */\n comm: z.string().optional(),\n /** Full command line if known. */\n cmdline: z.string().optional(),\n /** Child pids that belong to the target tree at attach time. */\n pids: z.array(z.number().int().positive()).optional(),\n /** Detected runtime, used to pick an adapter: python → py-spy, node → inspector, ... */\n runtime: z.enum([\"python\", \"node\", \"java\", \"go\", \"native\", \"unknown\"]).optional(),\n /** Where the target lives. \"local\" today; \"ssh://host\" / \"k8s://ctx\" tomorrow. */\n host: z.string().default(\"local\"),\n /** Container id / pod metadata when kind is container or pod. */\n container: z\n .object({\n runtime: z.enum([\"docker\", \"podman\", \"containerd\", \"k8s\"]),\n id: z.string(),\n name: z.string().optional(),\n image: z.string().optional(),\n namespace: z.string().optional(),\n })\n .optional(),\n});\nexport type Target = z.infer<typeof Target>;\n\n// ---------------------------------------------------------------------------\n// Evidence, findings, artifacts\n// ---------------------------------------------------------------------------\n\nexport const EvidenceKind = z.enum([\n \"syscall_stat\",\n \"syscall_slow\",\n \"syscall_error\",\n \"stack\",\n \"stack_summary\",\n \"lock_wait\",\n \"frame_hot\",\n \"socket\",\n \"socket_summary\",\n \"fd\",\n \"fd_summary\",\n \"packet_summary\",\n \"dns_query\",\n \"tls_sni\",\n \"metric\",\n \"metric_series\",\n \"heap_retainer\",\n \"heap_summary\",\n \"log_excerpt\",\n \"process\",\n \"process_list\",\n \"container_state\",\n \"cgroup\",\n \"diff\",\n \"readiness\",\n \"note\",\n]);\nexport type EvidenceKind = z.infer<typeof EvidenceKind>;\n\nexport const Evidence = z.object({\n /** Short stable id within a result, e.g. \"e1\", \"e2\". Findings reference these. */\n id: z.string().min(1),\n kind: EvidenceKind,\n /** Human-readable one-liner so an auditor can read the evidence list alone. */\n summary: z.string().min(1),\n /** Structured, raw-ish facts. Kept small (the whole payload is capped). */\n data: z.unknown(),\n /** Path to the artifact this evidence was derived from, if any. */\n artifact: z.string().optional(),\n /** 1-based inclusive line range in the artifact backing this evidence. */\n lines: z.tuple([z.number().int().min(1), z.number().int().min(1)]).optional(),\n});\nexport type Evidence = z.infer<typeof Evidence>;\n\nexport const Severity = z.enum([\"info\", \"warn\", \"high\"]);\nexport type Severity = z.infer<typeof Severity>;\n\nexport const Confidence = z.enum([\"observed\", \"inferred\"]);\nexport type Confidence = z.infer<typeof Confidence>;\n\nexport const Finding = z.object({\n id: z.string().min(1),\n severity: Severity,\n summary: z.string().min(1),\n /** MUST be non-empty. A finding that cites nothing is not a finding. */\n evidence_ids: z.array(z.string().min(1)).min(1, \"a finding must cite at least one evidence id\"),\n /** \"observed\" = directly read from tool output; \"inferred\" = a reasoned step, must be stated. */\n confidence: Confidence,\n /** Optional longer explanation / reasoning. */\n detail: z.string().optional(),\n});\nexport type Finding = z.infer<typeof Finding>;\n\nexport const ArtifactKind = z.enum([\n \"raw_text\",\n \"flamegraph_svg\",\n \"folded_stacks\",\n \"heapsnapshot\",\n \"pcap\",\n \"json\",\n \"markdown\",\n \"log\",\n]);\nexport type ArtifactKind = z.infer<typeof ArtifactKind>;\n\nexport const Artifact = z.object({\n path: z.string().min(1),\n kind: ArtifactKind,\n bytes: z.number().int().min(0),\n /** True if the output cap was hit and the artifact is truncated. */\n truncated: z.boolean().optional(),\n description: z.string().optional(),\n});\nexport type Artifact = z.infer<typeof Artifact>;\n\n// ---------------------------------------------------------------------------\n// ToolResult — every tool returns exactly this\n// ---------------------------------------------------------------------------\n\nexport const ToolName = z.enum([\n \"readiness\",\n \"attach\",\n \"detach\",\n \"list_processes\",\n \"trace_syscalls\",\n \"profile_cpu\",\n \"dump_stacks\",\n \"inspect_sockets\",\n \"inspect_files\",\n \"capture_traffic\",\n \"snapshot_memory\",\n \"diff_runs\",\n \"container_info\",\n \"report\",\n \"import_artifact\",\n]);\nexport type ToolName = z.infer<typeof ToolName>;\n\nexport const ToolResult = z\n .object({\n ok: z.boolean(),\n tool: z.string().min(1),\n /** \"\" for tools that run without a session (readiness, list_processes). */\n session_id: z.string(),\n target: Target,\n duration_ms: z.number().int().min(0),\n findings: z.array(Finding),\n evidence: z.array(Evidence),\n limitations: z.array(z.string()),\n suggested_next: z.array(z.string()),\n artifacts: z.array(Artifact),\n /** Set when ok === false. */\n error: z.string().optional(),\n /** ISO timestamp of when the tool started. */\n started_at: z.string().optional(),\n /** Monotonic index within the session; used by diff_runs to address prior results. */\n run_id: z.string().optional(),\n })\n .superRefine((r, ctx) => {\n const ids = new Set(r.evidence.map((e) => e.id));\n r.findings.forEach((f, i) => {\n for (const eid of f.evidence_ids) {\n if (!ids.has(eid)) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"findings\", i, \"evidence_ids\"],\n message: `finding ${f.id} cites unknown evidence id \"${eid}\"`,\n });\n }\n }\n });\n });\nexport type ToolResult = z.infer<typeof ToolResult>;\n\n// ---------------------------------------------------------------------------\n// Session\n// ---------------------------------------------------------------------------\n\nexport const SessionState = z.enum([\"attached\", \"detached\"]);\n\nexport const Session = z.object({\n id: z.string().min(1),\n state: SessionState,\n target: Target,\n created_at: z.string(),\n detached_at: z.string().optional(),\n /** Absolute path of the session directory under ~/.debuganything/sessions/. */\n dir: z.string(),\n /** Platform the session was created on. */\n platform: z.enum([\"linux\", \"darwin\", \"win32\"]),\n /** Ordered list of run ids, each one a saved ToolResult json in <dir>/runs/. */\n runs: z.array(\n z.object({\n run_id: z.string(),\n tool: z.string(),\n ok: z.boolean(),\n started_at: z.string(),\n duration_ms: z.number().int().min(0),\n file: z.string(),\n }),\n ),\n /** For kind=command: pid of the spawned child, and where its output is tee'd. */\n spawned: z\n .object({\n pid: z.number().int().positive(),\n stdout: z.string(),\n stderr: z.string(),\n exit_code: z.number().int().nullable().optional(),\n })\n .optional(),\n});\nexport type Session = z.infer<typeof Session>;\n\n// ---------------------------------------------------------------------------\n// Readiness\n// ---------------------------------------------------------------------------\n\nexport const ProviderStatus = z.enum([\"ready\", \"missing\", \"no_permission\", \"unsupported_platform\", \"not_implemented\"]);\n\nexport const ProviderReadiness = z.object({\n name: z.string(),\n tools: z.array(z.string()),\n status: ProviderStatus,\n /** Binaries this provider needs, with resolution. */\n requires: z.array(z.object({ binary: z.string(), path: z.string().nullable() })),\n /** How to fix, if not ready. */\n install_hint: z.string().optional(),\n /** Extra detail, e.g. \"ptrace_scope=1: can only trace children; run with sudo or set to 0\". */\n notes: z.array(z.string()).default([]),\n});\nexport type ProviderReadiness = z.infer<typeof ProviderReadiness>;\n\nexport const Readiness = z.object({\n os: z.enum([\"linux\", \"darwin\", \"win32\"]),\n arch: z.string(),\n kernel: z.string().optional(),\n node: z.string(),\n dea_version: z.string(),\n is_root: z.boolean(),\n has_sudo_noninteractive: z.boolean(),\n permissions: z.object({\n /** Linux: contents of /proc/sys/kernel/yama/ptrace_scope, or null when absent. */\n ptrace_scope: z.number().int().nullable(),\n /** Linux: CAP_SYS_PTRACE / CAP_NET_RAW effective, when determinable. */\n capabilities: z.array(z.string()).default([]),\n /** macOS: System Integrity Protection enabled. */\n sip_enabled: z.boolean().nullable(),\n /** macOS: whether the Developer Tools group lets us attach without sudo. */\n developer_mode: z.boolean().nullable(),\n /** Linux: perf_event_paranoid value. */\n perf_event_paranoid: z.number().int().nullable(),\n }),\n providers: z.array(ProviderReadiness),\n /** Flattened: which MCP tools can be served right now. */\n tools_available: z.array(z.string()),\n tools_unavailable: z.array(z.object({ tool: z.string(), reason: z.string() })),\n sessions_dir: z.string(),\n});\nexport type Readiness = z.infer<typeof Readiness>;\n\n// ---------------------------------------------------------------------------\n// Tool argument schemas (shared by MCP server and CLI)\n// ---------------------------------------------------------------------------\n\nexport const ImportTool = z.enum([\"strace\", \"fs_usage\", \"perf_script\", \"folded\", \"pyspy_dump\", \"sample\", \"gdb\", \"lldb\", \"lsof\", \"tcpdump\", \"jstack\", \"goroutines\"]);\nexport type ImportTool = z.infer<typeof ImportTool>;\n\nexport const SyscallCategory = z.enum([\"network\", \"file\", \"process\", \"signal\", \"memory\", \"all\"]);\nexport type SyscallCategory = z.infer<typeof SyscallCategory>;\n\nexport const ToolArgs = {\n readiness: z.object({}),\n attach: z.object({\n target: z.string().min(1).describe(\"pid, container name/id, k8s pod (ns/name), or a command line (with kind=command)\"),\n kind: TargetKind.optional().describe(\"Defaults to auto-detect: numeric → pid, else container, else command\"),\n }),\n detach: z.object({ session_id: z.string().min(1) }),\n list_processes: z.object({\n filter: z.string().optional().describe(\"Substring match on command / args / user\"),\n limit: z.number().int().min(1).max(500).default(100),\n }),\n trace_syscalls: z.object({\n session_id: z.string().min(1),\n duration_s: z.number().int().min(1).max(60).default(10),\n categories: z.array(SyscallCategory).default([\"all\"]),\n }),\n profile_cpu: z.object({\n session_id: z.string().min(1),\n duration_s: z.number().int().min(1).max(60).default(15),\n /** Required when the profiler will use perf on a system the user flagged as production; always safe to pass. */\n consent: z.boolean().optional(),\n }),\n dump_stacks: z.object({ session_id: z.string().min(1) }),\n inspect_sockets: z.object({ session_id: z.string().min(1) }),\n inspect_files: z.object({\n session_id: z.string().min(1),\n window_s: z.number().int().min(0).max(30).default(3).describe(\"Observe fd growth over this many seconds (0 = single snapshot)\"),\n }),\n capture_traffic: z.object({\n session_id: z.string().min(1),\n duration_s: z.number().int().min(1).max(30).default(10),\n filter: z.string().optional().describe(\"Extra BPF filter ANDed with the target's ports\"),\n consent: z.boolean().optional().describe(\"Required. Packet capture may contain secrets; ask the user first.\"),\n }),\n snapshot_memory: z.object({\n session_id: z.string().min(1),\n heap: z.boolean().default(true).describe(\"Take a heap snapshot where supported (Node). Set false for metrics only.\"),\n window_s: z.number().int().min(0).max(30).default(3).describe(\"Sample RSS over this many seconds\"),\n }),\n diff_runs: z.object({\n session_id: z.string().min(1),\n a: z.string().min(1).describe(\"run id of the earlier result; '<session_id>:<run>' reads a baseline run from another session (e.g. a recorded healthy run)\"),\n b: z.string().min(1).describe(\"run id of the later result; '<session_id>:<run>' works here too\"),\n }),\n container_info: z.object({ session_id: z.string().min(1) }),\n report: z.object({\n session_id: z.string().min(1),\n out: z.string().optional().describe(\"Optional path to also write the markdown to\"),\n html: z.boolean().optional().describe(\"Also render a self-contained report.html (shareable: ranked findings, expandable evidence, embedded flamegraphs)\"),\n }),\n import_artifact: z.object({\n session_id: z.string().min(1),\n tool: ImportTool.describe(\"What produced the file\"),\n path: z.string().min(1).describe(\"Path to the raw output file you captured yourself (e.g. with sudo)\"),\n duration_s: z.number().int().min(1).max(3600).optional().describe(\"How long the capture ran, when the file has no timestamps (profiles)\"),\n }),\n} as const;\n\nexport type ToolArgsOf<K extends keyof typeof ToolArgs> = z.infer<(typeof ToolArgs)[K]>;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Validate a ToolResult; throws with a readable message if findings cite nothing. */\nexport function assertToolResult(r: unknown): ToolResult {\n const parsed = ToolResult.safeParse(r);\n if (!parsed.success) {\n throw new Error(`invalid ToolResult: ${parsed.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\"; \")}`);\n }\n return parsed.data;\n}\n\n/** JSON Schemas for docs/mcp-contracts.md. */\nexport function exportJsonSchemas(): Record<string, unknown> {\n return {\n ToolResult: z.toJSONSchema(ToolResult),\n Finding: z.toJSONSchema(Finding),\n Evidence: z.toJSONSchema(Evidence),\n Artifact: z.toJSONSchema(Artifact),\n Target: z.toJSONSchema(Target),\n Session: z.toJSONSchema(Session),\n Readiness: z.toJSONSchema(Readiness),\n ToolArgs: Object.fromEntries(Object.entries(ToolArgs).map(([k, v]) => [k, z.toJSONSchema(v)])),\n };\n}\n","/**\n * Session lifecycle: create under ~/.debuganything/sessions/<id>/, persist metadata,\n * save every ToolResult as a run, clean up children on detach.\n */\nimport { mkdir, readFile, writeFile, readdir, rm, stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { randomBytes } from \"node:crypto\";\nimport { Session, type Target, type ToolResult } from \"./contracts.js\";\nimport { killTrackedChildren } from \"./safety.js\";\n\nexport function deaHome(): string {\n return process.env.DEA_HOME ?? join(homedir(), \".debuganything\");\n}\n\nexport function sessionsDir(): string {\n return join(deaHome(), \"sessions\");\n}\n\nexport function newSessionId(): string {\n const d = new Date();\n const stamp = d.toISOString().replace(/[-:]/g, \"\").replace(/\\.\\d+Z$/, \"\").replace(\"T\", \"-\");\n return `${stamp}-${randomBytes(3).toString(\"hex\")}`;\n}\n\nexport function platform(): \"linux\" | \"darwin\" | \"win32\" {\n const p = process.platform;\n return p === \"linux\" || p === \"darwin\" || p === \"win32\" ? p : \"linux\";\n}\n\nexport async function createSession(target: Target): Promise<Session> {\n const id = newSessionId();\n const dir = join(sessionsDir(), id);\n await mkdir(join(dir, \"runs\"), { recursive: true });\n await mkdir(join(dir, \"artifacts\"), { recursive: true });\n const session: Session = {\n id,\n state: \"attached\",\n target,\n created_at: new Date().toISOString(),\n dir,\n platform: platform(),\n runs: [],\n };\n await saveSession(session);\n return session;\n}\n\nexport async function saveSession(session: Session): Promise<void> {\n await writeFile(join(session.dir, \"session.json\"), JSON.stringify(session, null, 2));\n}\n\nexport async function loadSession(id: string): Promise<Session> {\n if (!/^[A-Za-z0-9_-]{1,80}$/.test(id)) throw new Error(`invalid session id: ${JSON.stringify(id)}`);\n const file = join(sessionsDir(), id, \"session.json\");\n if (!existsSync(file)) throw new Error(`session not found: ${id}. Call attach first, or list sessions with \\`debuganything sessions list\\`.`);\n const raw = JSON.parse(await readFile(file, \"utf8\"));\n return Session.parse(raw);\n}\n\nexport async function requireAttached(id: string): Promise<Session> {\n const s = await loadSession(id);\n if (s.state !== \"attached\") throw new Error(`session ${id} is detached; attach again to run tools`);\n return s;\n}\n\n/** Persist a ToolResult into the session and assign it a run id. Returns the result with run_id set. */\nexport async function recordRun(session: Session, result: ToolResult): Promise<ToolResult> {\n const n = session.runs.length + 1;\n const run_id = `${result.tool}#${n}`;\n const file = join(session.dir, \"runs\", `${String(n).padStart(3, \"0\")}-${result.tool}.json`);\n const withId: ToolResult = { ...result, run_id };\n await writeFile(file, JSON.stringify(withId, null, 2));\n session.runs.push({\n run_id,\n tool: result.tool,\n ok: result.ok,\n started_at: result.started_at ?? new Date().toISOString(),\n duration_ms: result.duration_ms,\n file,\n });\n await saveSession(session);\n return withId;\n}\n\n/** Load a prior run by run id (\"profile_cpu#2\"), by ordinal (\"2\"), or by tool name (latest of that tool). */\nexport async function loadRun(session: Session, ref: string): Promise<ToolResult> {\n let entry = session.runs.find((r) => r.run_id === ref);\n if (!entry && /^\\d+$/.test(ref)) entry = session.runs[Number(ref) - 1];\n if (!entry) entry = [...session.runs].reverse().find((r) => r.tool === ref);\n if (!entry) {\n throw new Error(`run \"${ref}\" not found in session ${session.id}. Known runs: ${session.runs.map((r) => r.run_id).join(\", \") || \"(none)\"}`);\n }\n return JSON.parse(await readFile(entry.file, \"utf8\")) as ToolResult;\n}\n\nexport async function detachSession(session: Session): Promise<Session> {\n killTrackedChildren();\n session.state = \"detached\";\n session.detached_at = new Date().toISOString();\n await saveSession(session);\n return session;\n}\n\nexport interface SessionSummary {\n id: string;\n state: string;\n target: string;\n created_at: string;\n runs: number;\n bytes: number;\n}\n\nexport async function listSessions(): Promise<SessionSummary[]> {\n const dir = sessionsDir();\n if (!existsSync(dir)) return [];\n const ids = (await readdir(dir)).filter((d) => !d.startsWith(\".\"));\n const out: SessionSummary[] = [];\n for (const id of ids) {\n try {\n const s = await loadSession(id);\n out.push({\n id,\n state: s.state,\n target: `${s.target.kind}:${s.target.id}${s.target.comm ? ` (${s.target.comm})` : \"\"}`,\n created_at: s.created_at,\n runs: s.runs.length,\n bytes: await dirSize(s.dir),\n });\n } catch {\n /* skip corrupt */\n }\n }\n return out.sort((a, b) => a.created_at.localeCompare(b.created_at));\n}\n\nasync function dirSize(dir: string): Promise<number> {\n let total = 0;\n for (const entry of await readdir(dir, { withFileTypes: true })) {\n const p = join(dir, entry.name);\n if (entry.isDirectory()) total += await dirSize(p);\n else total += (await stat(p)).size;\n }\n return total;\n}\n\n/** Remove detached sessions older than `olderThanDays` (default 7). Returns removed ids. */\nexport async function pruneSessions(opts: { olderThanDays?: number; all?: boolean } = {}): Promise<string[]> {\n const days = opts.olderThanDays ?? 7;\n const cutoff = Date.now() - days * 86_400_000;\n const removed: string[] = [];\n for (const s of await listSessions()) {\n const old = new Date(s.created_at).getTime() < cutoff;\n if (opts.all || (s.state === \"detached\" && old)) {\n await rm(join(sessionsDir(), s.id), { recursive: true, force: true });\n removed.push(s.id);\n }\n }\n return removed;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ResultBuilder,
4
+ humanBytes
5
+ } from "./chunk-5BXTDASF.js";
6
+ import {
7
+ targetPids
8
+ } from "./chunk-5RAKTYW5.js";
9
+ import {
10
+ platform
11
+ } from "./chunk-D742WJPT.js";
12
+ import {
13
+ runCapped
14
+ } from "./chunk-STFC7C5C.js";
15
+
16
+ // src/adapters/psmem.ts
17
+ import { readFile } from "fs/promises";
18
+ function parsePsMem(raw) {
19
+ const m = /^\s*(\d+)\s+(\d+)\s+([\d.]+)/.exec(raw);
20
+ if (!m) return void 0;
21
+ return { rss_kb: Number(m[1]), vsz_kb: Number(m[2]), cpu: Number(m[3]) };
22
+ }
23
+ function parseProcStatus(text) {
24
+ const out = {};
25
+ for (const key of ["VmRSS", "VmHWM", "VmSize", "VmSwap", "RssAnon", "RssFile", "RssShmem", "Threads"]) {
26
+ const m = new RegExp(`^${key}:\\s+(\\d+)`, "m").exec(text);
27
+ if (m) out[key] = Number(m[1]);
28
+ }
29
+ return out;
30
+ }
31
+ async function sampleMemory(pids, windowS, intervalMs = 500) {
32
+ const samples = [];
33
+ const start = Date.now();
34
+ const n = windowS <= 0 ? 1 : Math.max(2, Math.floor(windowS * 1e3 / intervalMs) + 1);
35
+ for (let i = 0; i < n; i++) {
36
+ const r = await runCapped("ps", ["-o", "rss=,vsz=,%cpu=", "-p", pids.join(",")], { timeoutMs: 5e3 });
37
+ let rss = 0;
38
+ let vsz = 0;
39
+ let cpu = 0;
40
+ for (const line of r.stdout.split("\n")) {
41
+ const p = parsePsMem(line);
42
+ if (p) {
43
+ rss += p.rss_kb;
44
+ vsz += p.vsz_kb;
45
+ cpu += p.cpu;
46
+ }
47
+ }
48
+ samples.push({ t_ms: Date.now() - start, rss_kb: rss, vsz_kb: vsz, cpu });
49
+ if (i < n - 1) await new Promise((res) => setTimeout(res, intervalMs));
50
+ }
51
+ return samples;
52
+ }
53
+ async function snapshotMemoryGeneric(ctx, args) {
54
+ const { session, target } = ctx;
55
+ const b = new ResultBuilder("snapshot_memory", session, target);
56
+ const pids = targetPids(target);
57
+ if (!pids.length) return b.fail("no pid").build();
58
+ const window = Math.min(args.window_s ?? 3, 30);
59
+ const samples = await sampleMemory(pids, window);
60
+ if (!samples.length || samples.every((s) => s.rss_kb === 0)) return b.fail("ps returned no memory data (process gone?)").build();
61
+ const first = samples[0];
62
+ const last = samples[samples.length - 1];
63
+ const art = await b.writeArtifact("mem-samples.json", JSON.stringify(samples, null, 2), "json", "RSS/VSZ samples");
64
+ const rssEv = b.evidenceOf("metric", `rss=${humanBytes(last.rss_kb * 1024)} vsz=${humanBytes(last.vsz_kb * 1024)} cpu=${last.cpu}% (${pids.length} pid${pids.length > 1 ? "s" : ""})`, { name: "rss", rss_kb: last.rss_kb, vsz_kb: last.vsz_kb, cpu: last.cpu, pids: pids.length }, { artifact: art });
65
+ const seriesEv = b.evidenceOf("metric_series", `rss ${humanBytes(first.rss_kb * 1024)} \u2192 ${humanBytes(last.rss_kb * 1024)} over ${window}s (${samples.length} samples)`, { label: "rss_series", start_kb: first.rss_kb, end_kb: last.rss_kb, delta_kb: last.rss_kb - first.rss_kb, window_s: window, n: samples.length }, { artifact: art });
66
+ if (platform() === "linux" && target.pid) {
67
+ try {
68
+ const st = parseProcStatus(await readFile(`/proc/${target.pid}/status`, "utf8"));
69
+ b.evidenceOf("metric", `proc status: HWM=${humanBytes((st.VmHWM ?? 0) * 1024)} anon=${humanBytes((st.RssAnon ?? 0) * 1024)} file=${humanBytes((st.RssFile ?? 0) * 1024)} swap=${humanBytes((st.VmSwap ?? 0) * 1024)} threads=${st.Threads ?? "?"}`, { name: "proc_status", ...st });
70
+ } catch {
71
+ }
72
+ }
73
+ const delta = last.rss_kb - first.rss_kb;
74
+ if (window > 0 && delta > Math.max(10 * 1024, first.rss_kb * 0.05)) {
75
+ b.finding("warn", `RSS grew ${humanBytes(delta * 1024)} in ${window}s (${humanBytes(first.rss_kb * 1024)} \u2192 ${humanBytes(last.rss_kb * 1024)})`, [seriesEv], "observed", "Take another snapshot_memory after exercising the target and diff_runs them.");
76
+ } else {
77
+ b.finding("info", `RSS ${humanBytes(last.rss_kb * 1024)}, stable over ${window}s`, [rssEv, seriesEv]);
78
+ }
79
+ b.limitation(`RSS sampled ${samples.length}\xD7 over ${window}s via ps; no allocation-site attribution for runtime ${target.runtime ?? "unknown"}`);
80
+ b.next("snapshot_memory (again, after load)", "diff_runs");
81
+ return b.build();
82
+ }
83
+ var psmemAdapter = {
84
+ name: "ps",
85
+ tools: ["snapshot_memory"],
86
+ platforms: ["linux", "darwin"],
87
+ requires: ["ps"],
88
+ priority: 1,
89
+ async run(tool, args, ctx) {
90
+ if (tool !== "snapshot_memory") throw new Error(`ps adapter cannot run ${tool}`);
91
+ return snapshotMemoryGeneric(ctx, args);
92
+ }
93
+ };
94
+
95
+ export {
96
+ parsePsMem,
97
+ parseProcStatus,
98
+ sampleMemory,
99
+ snapshotMemoryGeneric,
100
+ psmemAdapter
101
+ };
102
+ //# sourceMappingURL=chunk-EMR42ZJQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/psmem.ts"],"sourcesContent":["/**\n * psmem adapter: generic snapshot_memory for any runtime — RSS/VSZ sampled\n * over a window via ps (works everywhere, no root). Node and Python adapters\n * build on top of it.\n */\nimport { readFile } from \"node:fs/promises\";\nimport type { Adapter, AdapterContext } from \"./types.js\";\nimport type { ToolResult } from \"../contracts.js\";\nimport { ResultBuilder, humanBytes } from \"../evidence.js\";\nimport { runCapped } from \"../safety.js\";\nimport { platform } from \"../session.js\";\nimport { targetPids } from \"../target.js\";\n\nexport interface MemSample {\n t_ms: number;\n rss_kb: number;\n vsz_kb: number;\n cpu: number;\n threads?: number;\n}\n\n/** Parse `ps -o rss=,vsz=,%cpu= -p PID` (one line). Pure. */\nexport function parsePsMem(raw: string): { rss_kb: number; vsz_kb: number; cpu: number } | undefined {\n const m = /^\\s*(\\d+)\\s+(\\d+)\\s+([\\d.]+)/.exec(raw);\n if (!m) return undefined;\n return { rss_kb: Number(m[1]), vsz_kb: Number(m[2]), cpu: Number(m[3]) };\n}\n\n/** Parse Linux /proc/<pid>/status for memory fields. Pure. */\nexport function parseProcStatus(text: string): Record<string, number> {\n const out: Record<string, number> = {};\n for (const key of [\"VmRSS\", \"VmHWM\", \"VmSize\", \"VmSwap\", \"RssAnon\", \"RssFile\", \"RssShmem\", \"Threads\"]) {\n const m = new RegExp(`^${key}:\\\\s+(\\\\d+)`, \"m\").exec(text);\n if (m) out[key] = Number(m[1]);\n }\n return out;\n}\n\nexport async function sampleMemory(pids: number[], windowS: number, intervalMs = 500): Promise<MemSample[]> {\n const samples: MemSample[] = [];\n const start = Date.now();\n const n = windowS <= 0 ? 1 : Math.max(2, Math.floor((windowS * 1000) / intervalMs) + 1);\n for (let i = 0; i < n; i++) {\n const r = await runCapped(\"ps\", [\"-o\", \"rss=,vsz=,%cpu=\", \"-p\", pids.join(\",\")], { timeoutMs: 5000 });\n let rss = 0;\n let vsz = 0;\n let cpu = 0;\n for (const line of r.stdout.split(\"\\n\")) {\n const p = parsePsMem(line);\n if (p) {\n rss += p.rss_kb;\n vsz += p.vsz_kb;\n cpu += p.cpu;\n }\n }\n samples.push({ t_ms: Date.now() - start, rss_kb: rss, vsz_kb: vsz, cpu });\n if (i < n - 1) await new Promise((res) => setTimeout(res, intervalMs));\n }\n return samples;\n}\n\nexport async function snapshotMemoryGeneric(ctx: AdapterContext, args: { window_s?: number }): Promise<ToolResult> {\n const { session, target } = ctx;\n const b = new ResultBuilder(\"snapshot_memory\", session, target);\n const pids = targetPids(target);\n if (!pids.length) return b.fail(\"no pid\").build();\n const window = Math.min(args.window_s ?? 3, 30);\n const samples = await sampleMemory(pids, window);\n if (!samples.length || samples.every((s) => s.rss_kb === 0)) return b.fail(\"ps returned no memory data (process gone?)\").build();\n const first = samples[0]!;\n const last = samples[samples.length - 1]!;\n const art = await b.writeArtifact(\"mem-samples.json\", JSON.stringify(samples, null, 2), \"json\", \"RSS/VSZ samples\");\n const rssEv = b.evidenceOf(\"metric\", `rss=${humanBytes(last.rss_kb * 1024)} vsz=${humanBytes(last.vsz_kb * 1024)} cpu=${last.cpu}% (${pids.length} pid${pids.length > 1 ? \"s\" : \"\"})`, { name: \"rss\", rss_kb: last.rss_kb, vsz_kb: last.vsz_kb, cpu: last.cpu, pids: pids.length }, { artifact: art });\n const seriesEv = b.evidenceOf(\"metric_series\", `rss ${humanBytes(first.rss_kb * 1024)} → ${humanBytes(last.rss_kb * 1024)} over ${window}s (${samples.length} samples)`, { label: \"rss_series\", start_kb: first.rss_kb, end_kb: last.rss_kb, delta_kb: last.rss_kb - first.rss_kb, window_s: window, n: samples.length }, { artifact: art });\n if (platform() === \"linux\" && target.pid) {\n try {\n const st = parseProcStatus(await readFile(`/proc/${target.pid}/status`, \"utf8\"));\n b.evidenceOf(\"metric\", `proc status: HWM=${humanBytes((st.VmHWM ?? 0) * 1024)} anon=${humanBytes((st.RssAnon ?? 0) * 1024)} file=${humanBytes((st.RssFile ?? 0) * 1024)} swap=${humanBytes((st.VmSwap ?? 0) * 1024)} threads=${st.Threads ?? \"?\"}`, { name: \"proc_status\", ...st });\n } catch {\n /* ignore */\n }\n }\n const delta = last.rss_kb - first.rss_kb;\n if (window > 0 && delta > Math.max(10 * 1024, first.rss_kb * 0.05)) {\n b.finding(\"warn\", `RSS grew ${humanBytes(delta * 1024)} in ${window}s (${humanBytes(first.rss_kb * 1024)} → ${humanBytes(last.rss_kb * 1024)})`, [seriesEv], \"observed\", \"Take another snapshot_memory after exercising the target and diff_runs them.\");\n } else {\n b.finding(\"info\", `RSS ${humanBytes(last.rss_kb * 1024)}, stable over ${window}s`, [rssEv, seriesEv]);\n }\n b.limitation(`RSS sampled ${samples.length}× over ${window}s via ps; no allocation-site attribution for runtime ${target.runtime ?? \"unknown\"}`);\n b.next(\"snapshot_memory (again, after load)\", \"diff_runs\");\n return b.build();\n}\n\nexport const psmemAdapter: Adapter = {\n name: \"ps\",\n tools: [\"snapshot_memory\"],\n platforms: [\"linux\", \"darwin\"],\n requires: [\"ps\"],\n priority: 1,\n async run(tool, args, ctx) {\n if (tool !== \"snapshot_memory\") throw new Error(`ps adapter cannot run ${tool}`);\n return snapshotMemoryGeneric(ctx, args as { window_s?: number });\n },\n};\n"],"mappings":"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","names":[]}