@descryy/runtime-orchestrator 0.4.8 → 0.4.9

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 (49) hide show
  1. package/package.json +6 -6
  2. package/dist/attach-to-running-jvm-process.d.ts +0 -86
  3. package/dist/attach-to-running-jvm-process.d.ts.map +0 -1
  4. package/dist/attach-to-running-jvm-process.js +0 -408
  5. package/dist/attach-to-running-jvm-process.js.map +0 -1
  6. package/dist/attach-to-running-node-process.d.ts +0 -80
  7. package/dist/attach-to-running-node-process.d.ts.map +0 -1
  8. package/dist/attach-to-running-node-process.js +0 -290
  9. package/dist/attach-to-running-node-process.js.map +0 -1
  10. package/dist/attach-to-running-process.d.ts +0 -83
  11. package/dist/attach-to-running-process.d.ts.map +0 -1
  12. package/dist/attach-to-running-process.js +0 -86
  13. package/dist/attach-to-running-process.js.map +0 -1
  14. package/dist/cdp-client.d.ts +0 -27
  15. package/dist/cdp-client.d.ts.map +0 -1
  16. package/dist/cdp-client.js +0 -98
  17. package/dist/cdp-client.js.map +0 -1
  18. package/dist/cgroup-partial-restriction.d.ts +0 -80
  19. package/dist/cgroup-partial-restriction.d.ts.map +0 -1
  20. package/dist/cgroup-partial-restriction.js +0 -189
  21. package/dist/cgroup-partial-restriction.js.map +0 -1
  22. package/dist/collector-version.d.ts +0 -7
  23. package/dist/collector-version.d.ts.map +0 -1
  24. package/dist/collector-version.js +0 -9
  25. package/dist/collector-version.js.map +0 -1
  26. package/dist/index.d.ts +0 -19
  27. package/dist/index.d.ts.map +0 -1
  28. package/dist/index.js +0 -10
  29. package/dist/index.js.map +0 -1
  30. package/dist/instrumented-execution.d.ts +0 -135
  31. package/dist/instrumented-execution.d.ts.map +0 -1
  32. package/dist/instrumented-execution.js +0 -696
  33. package/dist/instrumented-execution.js.map +0 -1
  34. package/dist/jvm-agent/build.d.ts +0 -48
  35. package/dist/jvm-agent/build.d.ts.map +0 -1
  36. package/dist/jvm-agent/build.js +0 -129
  37. package/dist/jvm-agent/build.js.map +0 -1
  38. package/dist/polling-output-source.d.ts +0 -43
  39. package/dist/polling-output-source.d.ts.map +0 -1
  40. package/dist/polling-output-source.js +0 -64
  41. package/dist/polling-output-source.js.map +0 -1
  42. package/dist/profile-backend-observation.d.ts +0 -67
  43. package/dist/profile-backend-observation.d.ts.map +0 -1
  44. package/dist/profile-backend-observation.js +0 -107
  45. package/dist/profile-backend-observation.js.map +0 -1
  46. package/dist/respawn-and-supervise.d.ts +0 -86
  47. package/dist/respawn-and-supervise.d.ts.map +0 -1
  48. package/dist/respawn-and-supervise.js +0 -171
  49. package/dist/respawn-and-supervise.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descryy/runtime-orchestrator",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
4
4
  "type": "module",
5
5
  "description": "Assembles the runtime: controller-spawned services, live process output, collectors from a RuntimeAdapter, and a CollectorContext that can resolve script origins to source roots.",
6
6
  "license": "UNLICENSED",
@@ -25,13 +25,10 @@
25
25
  "registry": "https://registry.npmjs.org",
26
26
  "access": "public"
27
27
  },
28
- "scripts": {
29
- "build": "tsc -b"
30
- },
31
28
  "dependencies": {
32
29
  "@descryy/runtime-contracts": "0.3.3",
33
30
  "@descryy/runtime-backend-observation": "0.3.6",
34
- "@descryy/runtime-controller": "0.3.7",
31
+ "@descryy/runtime-controller": "0.3.8",
35
32
  "@descryy/runtime-evidence-store": "0.4.2",
36
33
  "@descryy/runtime-adapter-typescript": "0.3.7",
37
34
  "@descryy/runtime-environment-profile": "0.4.5",
@@ -47,5 +44,8 @@
47
44
  "@descryy/runtime-adapter-ruby": "0.0.4",
48
45
  "@descryy/runtime-adapter-rust": "0.1.5",
49
46
  "@descryy/runtime-adapter-swift": "0.1.5"
47
+ },
48
+ "scripts": {
49
+ "build": "tsc -b"
50
50
  }
51
- }
51
+ }
@@ -1,86 +0,0 @@
1
- /**
2
- * Retroactive attach to an already-running **JVM (Java/Kotlin/…)** process. Not a
3
- * full CDP-equivalent; see `RT-attach-to-running-jvm-process.md` (decisions-inbox)
4
- * for what was measured before this was written.
5
- *
6
- * **Mechanism, measured against a live JVM, not assumed from docs.** The JVM has
7
- * no `SIGUSR1`-equivalent, but ships (since JDK 6) HotSpot Dynamic Attach:
8
- * `VirtualMachine.attach(pid)` creates `.attach_pid<pid>`, signals `SIGQUIT`, and
9
- * the target's attach-listener thread (already running in every stock HotSpot
10
- * JVM, nothing to enable at launch) opens a Unix socket at `.java_pid<pid>` and
11
- * accepts commands — the same thing `jcmd`/`jstack`/`VisualVM` use. Measured:
12
- * attached to a plain `java QuietServer` with no special flags and no restart,
13
- * confirming this is genuinely retroactive.
14
- *
15
- * **Narrower than CDP.** `VirtualMachine.loadAgent(jar, args)` loads a real Java
16
- * agent (`Instrumentation`) — more powerful than CDP's remote-debugging protocol
17
- * in principle, but this slice only uses `Thread.setDefaultUncaughtExceptionHandler`
18
- * (`jvm-agent/Agent.java`), so it never sees an exception a framework already
19
- * caught (a try/catch, a servlet error page, `@ExceptionHandler`). RT-201 closed
20
- * one specific instance of that gap: `Agent.java` also installs a ByteBuddy
21
- * `ClassFileTransformer` tapping
22
- * `DispatcherServlet#processHandlerException` — Spring MVC's single stable
23
- * exception choke point — reported as `"framework-caught-exception"`, parsed
24
- * below and emitted with `payload.handled === true`. This closes only that one
25
- * choke point: a catch absorbed inside a `@Service` method, or any non-Spring
26
- * framework, is still invisible.
27
- *
28
- * **No console-output capture** — `System.out`/`err` aren't intercepted; that's
29
- * `attachToRunningProcess`'s job for a target already logging to a file.
30
- * `capabilities()` reports `backendLogAccess` unavailable for that reason.
31
- *
32
- * **Launcher is a real JDK tool process, not a library call** — no Node/native
33
- * Attach-API binding exists or is added. `jvm-agent/build.ts` compiles
34
- * `Attacher.java` once (mtime-cached) and this module spawns it as a short-lived
35
- * `java` child process.
36
- */
37
- import type { Collector } from "@descryy/runtime-contracts";
38
- export interface AttachToRunningJvmProcessOptions {
39
- readonly processId: string;
40
- /** The pid of the already-running target JVM. Verified alive before attach is attempted, and never spawned or supervised by this collector. */
41
- readonly pid: number;
42
- readonly service?: string;
43
- /** How long to wait for the Attacher helper process to complete the attach + loadAgent call. The attach itself is normally sub-second; generous default for a loaded machine. */
44
- readonly attachTimeoutMs?: number;
45
- /** How often to check the agent's output file for new events. */
46
- readonly pollIntervalMs?: number;
47
- /**
48
- * Repo-relative source roots plus the absolute repo root, so a JVM frame's bare
49
- * basename can be reconstructed into a repo-relative path graph correlation can
50
- * join on (RT-202, extending RT-162's fix from the log-tailing path to this one).
51
- *
52
- * The JVM prints a bare basename (`ProbeApplication.java`), never a path, so
53
- * `resolveSymbolNode` refuses to match on it alone (RT-031). This collector builds
54
- * `StackFrame.location` straight from the agent's captured `StackTraceElement`
55
- * fields, bypassing `createJvmStackTraceParser` entirely, so RT-162's earlier fix
56
- * there never reached this path — invisible until RT-202, since this mechanism's
57
- * only prior fixture was in the default package, where reconstruction is a no-op.
58
- *
59
- * `reconstructJvmSourcePath` below is a deliberate second copy of
60
- * `jvm-stack-trace-parser.ts`'s algorithm, not a shared import: this package
61
- * never takes a real dependency on any `@descryy/runtime-adapter-*` package
62
- * (JVM's included), and importing the JVM-specific function here would be a
63
- * bigger architectural change than this gap warrants. Both copies are tiny,
64
- * pure, and named after the one behaviour they implement.
65
- */
66
- readonly sourceRoots?: {
67
- readonly repoRoot: string;
68
- readonly roots: readonly string[];
69
- };
70
- }
71
- /**
72
- * Attach to an already-running JVM process via the HotSpot Dynamic Attach
73
- * mechanism, load a minimal Java agent, and start capturing genuinely
74
- * uncaught exceptions as `EXCEPTION` evidence from that moment forward.
75
- *
76
- * **Retroactive, not historical** — same as the Node slice: an exception
77
- * thrown before the agent finishes loading is not captured, because the
78
- * default handler was not installed yet. There is no backlog to catch up on.
79
- *
80
- * Discovery, build, and attach all happen inside `start()`, matching every
81
- * other `Collector` in this repo — a routine failure to attach resolves as
82
- * `{ available: false, reason }`, never a thrown exception a caller has to
83
- * remember to catch.
84
- */
85
- export declare function attachToRunningJvmProcess(options: AttachToRunningJvmProcessOptions): Collector;
86
- //# sourceMappingURL=attach-to-running-jvm-process.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"attach-to-running-jvm-process.d.ts","sourceRoot":"","sources":["../src/attach-to-running-jvm-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAOH,OAAO,KAAK,EAAE,SAAS,EAAyF,MAAM,4BAA4B,CAAC;AAMnJ,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,+IAA+I;IAC/I,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,iLAAiL;IACjL,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,iEAAiE;IACjE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE;QACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;KACnC,CAAC;CACH;AAoLD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gCAAgC,GAAG,SAAS,CA0N9F"}
@@ -1,408 +0,0 @@
1
- /**
2
- * Retroactive attach to an already-running **JVM (Java/Kotlin/…)** process. Not a
3
- * full CDP-equivalent; see `RT-attach-to-running-jvm-process.md` (decisions-inbox)
4
- * for what was measured before this was written.
5
- *
6
- * **Mechanism, measured against a live JVM, not assumed from docs.** The JVM has
7
- * no `SIGUSR1`-equivalent, but ships (since JDK 6) HotSpot Dynamic Attach:
8
- * `VirtualMachine.attach(pid)` creates `.attach_pid<pid>`, signals `SIGQUIT`, and
9
- * the target's attach-listener thread (already running in every stock HotSpot
10
- * JVM, nothing to enable at launch) opens a Unix socket at `.java_pid<pid>` and
11
- * accepts commands — the same thing `jcmd`/`jstack`/`VisualVM` use. Measured:
12
- * attached to a plain `java QuietServer` with no special flags and no restart,
13
- * confirming this is genuinely retroactive.
14
- *
15
- * **Narrower than CDP.** `VirtualMachine.loadAgent(jar, args)` loads a real Java
16
- * agent (`Instrumentation`) — more powerful than CDP's remote-debugging protocol
17
- * in principle, but this slice only uses `Thread.setDefaultUncaughtExceptionHandler`
18
- * (`jvm-agent/Agent.java`), so it never sees an exception a framework already
19
- * caught (a try/catch, a servlet error page, `@ExceptionHandler`). RT-201 closed
20
- * one specific instance of that gap: `Agent.java` also installs a ByteBuddy
21
- * `ClassFileTransformer` tapping
22
- * `DispatcherServlet#processHandlerException` — Spring MVC's single stable
23
- * exception choke point — reported as `"framework-caught-exception"`, parsed
24
- * below and emitted with `payload.handled === true`. This closes only that one
25
- * choke point: a catch absorbed inside a `@Service` method, or any non-Spring
26
- * framework, is still invisible.
27
- *
28
- * **No console-output capture** — `System.out`/`err` aren't intercepted; that's
29
- * `attachToRunningProcess`'s job for a target already logging to a file.
30
- * `capabilities()` reports `backendLogAccess` unavailable for that reason.
31
- *
32
- * **Launcher is a real JDK tool process, not a library call** — no Node/native
33
- * Attach-API binding exists or is added. `jvm-agent/build.ts` compiles
34
- * `Attacher.java` once (mtime-cached) and this module spawns it as a short-lived
35
- * `java` child process.
36
- */
37
- import { existsSync, mkdtempSync, readFileSync, writeFileSync, rmSync } from "node:fs";
38
- import { tmpdir } from "node:os";
39
- import { join } from "node:path";
40
- import { spawnSync } from "node:child_process";
41
- import { isProcessAlive } from "./attach-to-running-process.js";
42
- import { buildJvmAgentJar, buildAttacherClass, findToolsJarIfPresent } from "./jvm-agent/build.js";
43
- import { COLLECTOR_VERSION } from "./collector-version.js";
44
- /**
45
- * Reconstructs a repo-relative source path from a JVM frame's fully-qualified
46
- * declaring class name and basename, or `null` when it can't be done exactly
47
- * (no source roots, no basename, default-package class, or no candidate exists
48
- * on disk). See `sourceRoots` doc above for why this duplicates
49
- * `jvm-stack-trace-parser.ts`'s `reconstructPath` rather than importing it.
50
- */
51
- function reconstructJvmSourcePath(declaringType, file, sourceRoots) {
52
- if (sourceRoots === undefined || file === null)
53
- return null;
54
- if (file.includes("/"))
55
- return null;
56
- const lastDot = declaringType.lastIndexOf(".");
57
- if (lastDot === -1)
58
- return null;
59
- const packagePath = declaringType.slice(0, lastDot).split(".").join("/");
60
- for (const root of sourceRoots.roots) {
61
- const candidate = `${root}/${packagePath}/${file}`;
62
- if (existsSync(join(sourceRoots.repoRoot, candidate)))
63
- return candidate;
64
- }
65
- return null;
66
- }
67
- function isUncaughtExceptionEvent(value) {
68
- return typeof value === "object" && value !== null && value.type === "uncaught-exception";
69
- }
70
- function isFrameworkCaughtExceptionEvent(value) {
71
- return typeof value === "object" && value !== null && value.type === "framework-caught-exception";
72
- }
73
- /** A-F8: target's writer thread confirming detach completed (see `Agent.java`'s `detach()`); `stop()`'s poll loop waits for this. */
74
- function isAgentDetachedEvent(value) {
75
- return typeof value === "object" && value !== null && value.type === "agent-detached";
76
- }
77
- /** A-F8: target's writer thread disclosing its bounded queue overflowed and events were dropped — never silent (honest-degradation rule). `count` is cumulative since `agentmain` last ran, not a per-tick delta. */
78
- function isEventsDroppedEvent(value) {
79
- const v = value;
80
- return typeof value === "object" && value !== null && v.type === "events-dropped" && typeof v.count === "number";
81
- }
82
- /**
83
- * `StackTraceElement.getFileName()` is always a bare name (`"ProbeApplication.java"`,
84
- * never `"probe/ProbeApplication.java"`) — the JVM never records a package-relative
85
- * path, only a plain source-file attribute, so it resolves nothing against a graph
86
- * whose nodes carry repo-relative paths.
87
- *
88
- * `className` (fully-qualified, always present) recovers the missing directory.
89
- * `className.split("$")[0]` drops a nested/anonymous-class suffix (`Outer$Inner`,
90
- * `Outer$1`) to recover the OUTER class's package — nested classes live in their
91
- * outer class's file. Result is package-relative, not repo-relative — the fallback
92
- * `frameToStackFrame` uses when `reconstructJvmSourcePath` can't produce a
93
- * validated repo-relative path.
94
- *
95
- * Deliberately does NOT rename/reshape `frame.fileName` itself — Kotlin breaks the
96
- * "file name matches class name" assumption (a class `Foo` may live in `Bar.kt`),
97
- * so only the directory is derived from the class name.
98
- */
99
- function packageRelativeFile(frame) {
100
- if (frame.fileName === null)
101
- return null;
102
- const outerClassName = frame.className.split("$")[0];
103
- const lastDot = outerClassName.lastIndexOf(".");
104
- if (lastDot === -1)
105
- return frame.fileName; // default package — no directory to prepend
106
- const packagePath = outerClassName.slice(0, lastDot).replaceAll(".", "/");
107
- return `${packagePath}/${frame.fileName}`;
108
- }
109
- function frameToStackFrame(frame, sourceRoots) {
110
- return {
111
- location: {
112
- file: reconstructJvmSourcePath(frame.className, frame.fileName, sourceRoots) ?? packageRelativeFile(frame),
113
- line: frame.lineNumber > 0 ? frame.lineNumber : null,
114
- column: null,
115
- functionName: `${frame.className}.${frame.methodName}`,
116
- // self-contained: the JVM's own reported state, captured live, never a
117
- // side artifact resolved through -- same standing as CDP's frames
118
- reliability: "self-contained",
119
- resolvedVia: null,
120
- },
121
- raw: `${frame.className}.${frame.methodName}(${frame.fileName ?? "unknown"}:${String(frame.lineNumber)})`,
122
- };
123
- }
124
- /**
125
- * A-F10: a lighter check than the Node sibling's, deliberately — HotSpot's own
126
- * Dynamic Attach already enforces same-uid at the OS level (`VirtualMachine.attach`
127
- * fails for a different-user pid regardless). What's NOT already enforced is "this
128
- * pid actually looks like a JVM" — attaching against a non-JVM same-user pid
129
- * wastes a real attempt for a confusing downstream failure instead of a clear
130
- * refusal here. `ps`, not `/proc`, for the same Linux/macOS portability reason.
131
- */
132
- function verifyLikelyJvmProcess(pid) {
133
- const result = spawnSync("ps", ["-o", "comm=", "-p", String(pid)], { encoding: "utf8" });
134
- const comm = (result.stdout ?? "").trim().split("\n")[0]?.trim().toLowerCase();
135
- if (result.status !== 0 || comm === undefined || comm === "") {
136
- return { ok: false, reason: `could not verify pid ${String(pid)}'s command via ps (exit ${String(result.status)}) — refusing to attempt a JVM attach against a process this module cannot confirm the identity of` };
137
- }
138
- // covers "java", "javaw", and "java (someapp)" truncation -- allowlists the
139
- // launcher, never app names
140
- if (!comm.includes("java")) {
141
- return { ok: false, reason: `pid ${String(pid)}'s command is "${comm}", which does not look like a JVM — refusing to attempt a Dynamic Attach against a process that is unlikely to have one` };
142
- }
143
- return { ok: true };
144
- }
145
- function notThisCollectorsJob(what) {
146
- return { availability: "unavailable", reason: `attachToRunningJvmProcess observes only genuinely uncaught JVM exceptions via a loaded agent's default handler, not ${what}.` };
147
- }
148
- /** A-F8: the sentinel `agentmain` argument that means "detach, do not install" — must match `Agent.java`'s own `DETACH_COMMAND` exactly. */
149
- const DETACH_COMMAND = "DETACH";
150
- const CAPABILITIES = {
151
- domObservation: notThisCollectorsJob("DOM inspection"),
152
- consoleObservation: notThisCollectorsJob("a browser page's console"),
153
- networkObservation: notThisCollectorsJob("network traffic"),
154
- // unavailable unlike the Node slice: doesn't intercept System.out/err, that's
155
- // attachToRunningProcess's job for a target logging to a file
156
- backendLogAccess: notThisCollectorsJob("console/log output — see attachToRunningProcess for a target logging to a file"),
157
- distributedTrace: notThisCollectorsJob("trace-id/request-id correlation — the loaded agent extracts none"),
158
- sourceMapping: { availability: "available", reason: null },
159
- stackCapture: { availability: "available", reason: null },
160
- processLifecycle: notThisCollectorsJob("process lifecycle — spawn/ready/exit are ProcessCollector's to observe"),
161
- databaseObservation: notThisCollectorsJob("database queries"),
162
- externalServiceObservation: notThisCollectorsJob("outbound external requests"),
163
- };
164
- /**
165
- * Runs the compiled `Attacher` helper against `pid`, loading `agent.jar`
166
- * with `outputFilePath` as its argument. Resolves once the helper process
167
- * exits 0 (attach + loadAgent both succeeded); rejects with the helper's own
168
- * stderr otherwise — never guesses at success from silence.
169
- */
170
- function runAttacher(pid, agentJarPath, outputFilePath, attacherDir, attachTimeoutMs) {
171
- const toolsJar = findToolsJarIfPresent();
172
- const classpath = toolsJar !== null ? `${attacherDir}:${toolsJar}` : attacherDir;
173
- const result = spawnSync("java", ["-cp", classpath, "Attacher", String(pid), agentJarPath, outputFilePath], {
174
- encoding: "utf8",
175
- timeout: attachTimeoutMs,
176
- });
177
- if (result.error !== undefined) {
178
- return { ok: false, reason: `attachToRunningJvmProcess: could not launch the java Attacher helper: ${result.error.message}` };
179
- }
180
- if (result.status !== 0) {
181
- const detail = (result.stderr ?? "").trim() || (result.stdout ?? "").trim() || `exit code ${String(result.status)}`;
182
- return { ok: false, reason: `attachToRunningJvmProcess: Attacher failed to attach to pid ${String(pid)}: ${detail}` };
183
- }
184
- return { ok: true };
185
- }
186
- /**
187
- * Attach to an already-running JVM process via the HotSpot Dynamic Attach
188
- * mechanism, load a minimal Java agent, and start capturing genuinely
189
- * uncaught exceptions as `EXCEPTION` evidence from that moment forward.
190
- *
191
- * **Retroactive, not historical** — same as the Node slice: an exception
192
- * thrown before the agent finishes loading is not captured, because the
193
- * default handler was not installed yet. There is no backlog to catch up on.
194
- *
195
- * Discovery, build, and attach all happen inside `start()`, matching every
196
- * other `Collector` in this repo — a routine failure to attach resolves as
197
- * `{ available: false, reason }`, never a thrown exception a caller has to
198
- * remember to catch.
199
- */
200
- export function attachToRunningJvmProcess(options) {
201
- let stopped = false;
202
- // A-F8: set once start() actually attaches -- stop() uses this to know whether
203
- // there's anything to detach, and null means stop() is a pure no-op
204
- let attached = null;
205
- // captured so stop() can disclose a detach failure through the same evidence
206
- // stream start()'s poll loop uses
207
- let capturedContext = null;
208
- let detachConfirmed = false;
209
- function emitCollectorError(reason) {
210
- const context = capturedContext;
211
- if (context === null)
212
- return; // start() never ran far enough to have one
213
- context.emit({
214
- timestamp: new Date().toISOString(),
215
- source: "backend-process",
216
- service: options.service ?? null,
217
- process: options.processId,
218
- eventType: "COLLECTOR_ERROR",
219
- payload: { raw: reason, collectorId: `attach-jvm-agent:${options.processId}`, error: reason },
220
- traceId: null,
221
- requestId: null,
222
- correlationId: null,
223
- graphNodeId: null,
224
- sourceLocation: null,
225
- stackTrace: null,
226
- confidence: 1,
227
- redactionStatus: "pending-redaction",
228
- collectorVersion: COLLECTOR_VERSION,
229
- });
230
- }
231
- return {
232
- collectorId: `attach-jvm-agent:${options.processId}`,
233
- async start(context) {
234
- capturedContext = context;
235
- if (!isProcessAlive(options.pid)) {
236
- return { available: false, reason: `attachToRunningJvmProcess: pid ${String(options.pid)} is not running — cannot attach to a target that does not exist.` };
237
- }
238
- if (process.platform === "win32") {
239
- return { available: false, reason: "attachToRunningJvmProcess: HotSpot Dynamic Attach as used here relies on a Unix domain socket; not available on win32." };
240
- }
241
- // A-F10: checked before spawning the Attacher helper, not after —
242
- // see verifyLikelyJvmProcess's own doc for why this is a lighter check
243
- // than the Node sibling's (the OS already enforces same-uid here).
244
- const identity = verifyLikelyJvmProcess(options.pid);
245
- if (!identity.ok) {
246
- return { available: false, reason: `attachToRunningJvmProcess: ${identity.reason}.` };
247
- }
248
- let agentJarPath;
249
- let attacherDir;
250
- try {
251
- agentJarPath = await buildJvmAgentJar();
252
- attacherDir = buildAttacherClass();
253
- }
254
- catch (error) {
255
- return { available: false, reason: error instanceof Error ? error.message : String(error) };
256
- }
257
- const dir = mkdtempSync(join(tmpdir(), "descry-jvm-attach-"));
258
- const outFile = join(dir, "events.log");
259
- writeFileSync(outFile, "");
260
- const attachTimeoutMs = options.attachTimeoutMs ?? 10_000;
261
- const attachResult = runAttacher(options.pid, agentJarPath, outFile, attacherDir, attachTimeoutMs);
262
- if (!attachResult.ok) {
263
- return { available: false, reason: attachResult.reason };
264
- }
265
- // A-F8: recorded so stop() can run the same Attacher helper a second
266
- // time with DETACH_COMMAND, and know which output file to watch for
267
- // confirmation.
268
- attached = { agentJarPath, attacherDir, outFile, dir, attachTimeoutMs };
269
- const pollIntervalMs = options.pollIntervalMs ?? 50;
270
- let consumed = 0;
271
- const poll = async () => {
272
- for (;;) {
273
- if (stopped)
274
- return;
275
- if (!existsSync(outFile)) {
276
- await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
277
- continue;
278
- }
279
- const whole = readFileSync(outFile, "utf8");
280
- if (whole.length > consumed) {
281
- const chunk = whole.slice(consumed);
282
- consumed = whole.length;
283
- for (const rawLine of chunk.split("\n")) {
284
- const line = rawLine.trim();
285
- if (line === "")
286
- continue;
287
- let parsed;
288
- try {
289
- parsed = JSON.parse(line);
290
- }
291
- catch {
292
- // in practice cannot happen: Agent.java opens, writes one full
293
- // line, and closes the file per event, so a poll tick never
294
- // observes a half-written line. Dropped rather than guessed at
295
- // if it ever does.
296
- continue;
297
- }
298
- if (isUncaughtExceptionEvent(parsed) || isFrameworkCaughtExceptionEvent(parsed)) {
299
- const frames = parsed.frames.map((frame) => frameToStackFrame(frame, options.sourceRoots));
300
- const stackTrace = frames.length > 0
301
- ? { fidelity: "synchronous", frames, primaryFrameIndex: 0 }
302
- : { fidelity: "synchronous", frames: [], primaryFrameIndex: 0 };
303
- context.emit({
304
- timestamp: new Date().toISOString(),
305
- source: "backend-process",
306
- service: options.service ?? null,
307
- process: options.processId,
308
- eventType: "EXCEPTION",
309
- // `handled` distinguishes the two channels within one event type
310
- // rather than adding a new member to a closed vocabulary -- true
311
- // for RT-201's DispatcherServlet tap, false for genuinely
312
- // uncaught. Additive: existing .message/.stack consumers unaffected.
313
- payload: { message: `${parsed.exceptionClass}: ${parsed.message ?? ""}`.trim(), stack: null, handled: parsed.type === "framework-caught-exception" },
314
- traceId: null,
315
- requestId: null,
316
- correlationId: null,
317
- graphNodeId: null,
318
- sourceLocation: frames[0]?.location ?? null,
319
- stackTrace: frames.length > 0 ? stackTrace : null,
320
- confidence: 1,
321
- redactionStatus: "pending-redaction",
322
- collectorVersion: COLLECTOR_VERSION,
323
- });
324
- }
325
- else if (isAgentDetachedEvent(parsed)) {
326
- // A-F8: unblocks stop()'s wait loop -- internal bookkeeping, not
327
- // something observed about the target
328
- detachConfirmed = true;
329
- }
330
- else if (isEventsDroppedEvent(parsed)) {
331
- // A-F8: the target's bounded queue overflowed, real evidence was
332
- // lost -- disclosed, not silent. De-duplicated on the Java side
333
- // (marker written only when the count changes).
334
- emitCollectorError(`attachToRunningJvmProcess: the target's own event queue has dropped ${String(parsed.count)} event(s) so far (queue capacity exceeded) — some EXCEPTION evidence from this process was never captured.`);
335
- }
336
- // "agent-loaded", "framework-instrumentation-installed",
337
- // "framework-instrumentation-unavailable", "detach-started",
338
- // "detach-uncaught-handler-restored" / "-restore-failed",
339
- // "detach-transformer-reset" / "-incomplete" / "-failed", and
340
- // any future purely-internal marker type: acknowledged by
341
- // being parsed, not turned into evidence.
342
- }
343
- }
344
- await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
345
- }
346
- };
347
- void poll();
348
- return { available: true };
349
- },
350
- /**
351
- * A-F8: a real detach, not just halting this module's own poller. Runs the
352
- * Attacher helper again with `DETACH_COMMAND`, which drives `Agent.java`'s own
353
- * `detach()` — restoring the JVM's prior default uncaught-exception handler
354
- * and calling ByteBuddy's `reset()` to remove the `DispatcherServlet`
355
- * transformer — because leaving either installed for the rest of the target's
356
- * life is exactly the kind of state a "detach" must not leave behind.
357
- *
358
- * `stopped` is deliberately NOT set until after detach is attempted and
359
- * (bounded) awaited: `poll()` checks `stopped` first on every tick, so setting
360
- * it early would stop this module from ever reading the target's own
361
- * "agent-detached" confirmation line.
362
- *
363
- * Where a JVM limitation makes true removal impossible, this discloses it via
364
- * `COLLECTOR_ERROR` rather than pretending `stop()` stopped it. Two disclosed
365
- * failure shapes: the Attacher helper itself couldn't run (target unreachable,
366
- * permission denied), or it ran but the writer thread never confirmed detach
367
- * within the timeout (target under load, or the JVM's own handler/transformer
368
- * restoration genuinely failed inside `detach()` — narrower diagnostic detail
369
- * then only visible in the raw output file).
370
- *
371
- * A target that has already exited needs no detach — silently a no-op, not a
372
- * disclosed failure.
373
- */
374
- async stop() {
375
- const state = attached;
376
- if (state === null) {
377
- stopped = true;
378
- return;
379
- }
380
- if (isProcessAlive(options.pid)) {
381
- const detachAttempt = runAttacher(options.pid, state.agentJarPath, DETACH_COMMAND, state.attacherDir, state.attachTimeoutMs);
382
- if (detachAttempt.ok) {
383
- const deadline = Date.now() + Math.min(state.attachTimeoutMs, 5000);
384
- while (!detachConfirmed && Date.now() < deadline) {
385
- await new Promise((resolve) => setTimeout(resolve, 25));
386
- }
387
- if (!detachConfirmed) {
388
- emitCollectorError(`attachToRunningJvmProcess: detach was requested but never confirmed by the target within the timeout — its default uncaught-exception handler and DispatcherServlet instrumentation (if installed) may remain in place until the target process itself exits.`);
389
- }
390
- }
391
- else {
392
- emitCollectorError(`attachToRunningJvmProcess: detach could not be attempted (${detachAttempt.reason}) — the target's default uncaught-exception handler and DispatcherServlet instrumentation (if installed) remain in place until the target process itself exits.`);
393
- }
394
- }
395
- stopped = true; // safe now: any detach confirmation has already been drained by poll().
396
- try {
397
- rmSync(state.dir, { recursive: true, force: true });
398
- }
399
- catch {
400
- // best-effort cleanup; not worth failing stop() over a leftover temp dir
401
- }
402
- },
403
- capabilities() {
404
- return CAPABILITIES;
405
- },
406
- };
407
- }
408
- //# sourceMappingURL=attach-to-running-jvm-process.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"attach-to-running-jvm-process.js","sourceRoot":"","sources":["../src/attach-to-running-jvm-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACvF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAI/C,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAoC3D;;;;;;GAMG;AACH,SAAS,wBAAwB,CAC/B,aAAqB,EACrB,IAAmB,EACnB,WAA4D;IAE5D,IAAI,WAAW,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC5D,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpC,MAAM,OAAO,GAAG,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,OAAO,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,WAAW,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEzE,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;QACnD,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;IAC1E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AA0BD,SAAS,wBAAwB,CAAC,KAAc;IAC9C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAA4B,CAAC,IAAI,KAAK,oBAAoB,CAAC;AACpH,CAAC;AAED,SAAS,+BAA+B,CAAC,KAAc;IACrD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAA4B,CAAC,IAAI,KAAK,4BAA4B,CAAC;AAC5H,CAAC;AAED,qIAAqI;AACrI,SAAS,oBAAoB,CAAC,KAAc;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAA4B,CAAC,IAAI,KAAK,gBAAgB,CAAC;AAChH,CAAC;AAED,qNAAqN;AACrN,SAAS,oBAAoB,CAAC,KAAc;IAC1C,MAAM,CAAC,GAAG,KAA4C,CAAC;IACvD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,gBAAgB,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC;AACnH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,mBAAmB,CAAC,KAAwB;IACnD,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,cAAc,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACtD,MAAM,OAAO,GAAG,cAAc,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,OAAO,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC,QAAQ,CAAC,CAAC,4CAA4C;IACvF,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1E,OAAO,GAAG,WAAW,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;AAC5C,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAwB,EAAE,WAA4D;IAC/G,OAAO;QACL,QAAQ,EAAE;YACR,IAAI,EAAE,wBAAwB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,IAAI,mBAAmB,CAAC,KAAK,CAAC;YAC1G,IAAI,EAAE,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;YACpD,MAAM,EAAE,IAAI;YACZ,YAAY,EAAE,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,UAAU,EAAE;YACtD,uEAAuE;YACvE,kEAAkE;YAClE,WAAW,EAAE,gBAAgB;YAC7B,WAAW,EAAE,IAAI;SAClB;QACD,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,QAAQ,IAAI,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG;KAC1G,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAAC,GAAW;IACzC,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACzF,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAC7D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wBAAwB,MAAM,CAAC,GAAG,CAAC,2BAA2B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,mGAAmG,EAAE,CAAC;IACvN,CAAC;IACD,4EAA4E;IAC5E,4BAA4B;IAC5B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,MAAM,CAAC,GAAG,CAAC,kBAAkB,IAAI,yHAAyH,EAAE,CAAC;IAClM,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACxC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,uHAAuH,IAAI,GAAG,EAAE,CAAC;AACjL,CAAC;AAED,4IAA4I;AAC5I,MAAM,cAAc,GAAG,QAAQ,CAAC;AAEhC,MAAM,YAAY,GAA0B;IAC1C,cAAc,EAAE,oBAAoB,CAAC,gBAAgB,CAAC;IACtD,kBAAkB,EAAE,oBAAoB,CAAC,0BAA0B,CAAC;IACpE,kBAAkB,EAAE,oBAAoB,CAAC,iBAAiB,CAAC;IAC3D,8EAA8E;IAC9E,8DAA8D;IAC9D,gBAAgB,EAAE,oBAAoB,CAAC,gFAAgF,CAAC;IACxH,gBAAgB,EAAE,oBAAoB,CAAC,kEAAkE,CAAC;IAC1G,aAAa,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE;IAC1D,YAAY,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE;IACzD,gBAAgB,EAAE,oBAAoB,CAAC,wEAAwE,CAAC;IAChH,mBAAmB,EAAE,oBAAoB,CAAC,kBAAkB,CAAC;IAC7D,0BAA0B,EAAE,oBAAoB,CAAC,4BAA4B,CAAC;CAC/E,CAAC;AAEF;;;;;GAKG;AACH,SAAS,WAAW,CAAC,GAAW,EAAE,YAAoB,EAAE,cAAsB,EAAE,WAAmB,EAAE,eAAuB;IAC1H,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,WAAW,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC;IACjF,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,YAAY,EAAE,cAAc,CAAC,EAAE;QAC1G,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,eAAe;KACzB,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,yEAAyE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;IAChI,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACpH,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,+DAA+D,MAAM,CAAC,GAAG,CAAC,KAAK,MAAM,EAAE,EAAE,CAAC;IACxH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAyC;IACjF,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,+EAA+E;IAC/E,oEAAoE;IACpE,IAAI,QAAQ,GAA6J,IAAI,CAAC;IAC9K,6EAA6E;IAC7E,kCAAkC;IAClC,IAAI,eAAe,GAA4B,IAAI,CAAC;IACpD,IAAI,eAAe,GAAG,KAAK,CAAC;IAE5B,SAAS,kBAAkB,CAAC,MAAc;QACxC,MAAM,OAAO,GAAG,eAAe,CAAC;QAChC,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO,CAAC,2CAA2C;QACzE,OAAO,CAAC,IAAI,CAAC;YACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,MAAM,EAAE,iBAAiB;YACzB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI;YAChC,OAAO,EAAE,OAAO,CAAC,SAAS;YAC1B,SAAS,EAAE,iBAAiB;YAC5B,OAAO,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,oBAAoB,OAAO,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;YAC7F,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,IAAI;YACf,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,IAAI;YACjB,cAAc,EAAE,IAAI;YACpB,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,CAAC;YACb,eAAe,EAAE,mBAAmB;YACpC,gBAAgB,EAAE,iBAAiB;SACpC,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,WAAW,EAAE,oBAAoB,OAAO,CAAC,SAAS,EAAE;QAEpD,KAAK,CAAC,KAAK,CAAC,OAAyB;YACnC,eAAe,GAAG,OAAO,CAAC;YAC1B,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,kCAAkC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,kEAAkE,EAAE,CAAC;YAC/J,CAAC;YACD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;gBACjC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,wHAAwH,EAAE,CAAC;YAChK,CAAC;YACD,kEAAkE;YAClE,uEAAuE;YACvE,mEAAmE;YACnE,MAAM,QAAQ,GAAG,sBAAsB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACrD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,8BAA8B,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAC;YACxF,CAAC;YAED,IAAI,YAAoB,CAAC;YACzB,IAAI,WAAmB,CAAC;YACxB,IAAI,CAAC;gBACH,YAAY,GAAG,MAAM,gBAAgB,EAAE,CAAC;gBACxC,WAAW,GAAG,kBAAkB,EAAE,CAAC;YACrC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9F,CAAC;YAED,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC;YAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;YACxC,aAAa,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAE3B,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,MAAM,CAAC;YAC1D,MAAM,YAAY,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC;YACnG,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC;gBACrB,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC;YAC3D,CAAC;YACD,qEAAqE;YACrE,oEAAoE;YACpE,gBAAgB;YAChB,QAAQ,GAAG,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,CAAC;YAExE,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;YACpD,IAAI,QAAQ,GAAG,CAAC,CAAC;YAEjB,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;gBACrC,SAAS,CAAC;oBACR,IAAI,OAAO;wBAAE,OAAO;oBACpB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;wBACzB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;wBACpE,SAAS;oBACX,CAAC;oBACD,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBAC5C,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;wBAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;wBACpC,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC;wBACxB,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;4BACxC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;4BAC5B,IAAI,IAAI,KAAK,EAAE;gCAAE,SAAS;4BAC1B,IAAI,MAAe,CAAC;4BACpB,IAAI,CAAC;gCACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;4BAC5B,CAAC;4BAAC,MAAM,CAAC;gCACP,+DAA+D;gCAC/D,4DAA4D;gCAC5D,+DAA+D;gCAC/D,mBAAmB;gCACnB,SAAS;4BACX,CAAC;4BACD,IAAI,wBAAwB,CAAC,MAAM,CAAC,IAAI,+BAA+B,CAAC,MAAM,CAAC,EAAE,CAAC;gCAChF,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;gCAC3F,MAAM,UAAU,GAAe,MAAM,CAAC,MAAM,GAAG,CAAC;oCAC9C,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,EAAE;oCAC3D,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;gCAClE,OAAO,CAAC,IAAI,CAAC;oCACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oCACnC,MAAM,EAAE,iBAAiB;oCACzB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI;oCAChC,OAAO,EAAE,OAAO,CAAC,SAAS;oCAC1B,SAAS,EAAE,WAAW;oCACtB,iEAAiE;oCACjE,iEAAiE;oCACjE,0DAA0D;oCAC1D,qEAAqE;oCACrE,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,cAAc,KAAK,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,KAAK,4BAA4B,EAAE;oCACpJ,OAAO,EAAE,IAAI;oCACb,SAAS,EAAE,IAAI;oCACf,aAAa,EAAE,IAAI;oCACnB,WAAW,EAAE,IAAI;oCACjB,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,IAAI;oCAC3C,UAAU,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;oCACjD,UAAU,EAAE,CAAC;oCACb,eAAe,EAAE,mBAAmB;oCACpC,gBAAgB,EAAE,iBAAiB;iCACpC,CAAC,CAAC;4BACL,CAAC;iCAAM,IAAI,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC;gCACxC,iEAAiE;gCACjE,sCAAsC;gCACtC,eAAe,GAAG,IAAI,CAAC;4BACzB,CAAC;iCAAM,IAAI,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC;gCACxC,iEAAiE;gCACjE,gEAAgE;gCAChE,gDAAgD;gCAChD,kBAAkB,CAChB,uEAAuE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,4GAA4G,CACxM,CAAC;4BACJ,CAAC;4BACD,yDAAyD;4BACzD,6DAA6D;4BAC7D,0DAA0D;4BAC1D,8DAA8D;4BAC9D,0DAA0D;4BAC1D,0CAA0C;wBAC5C,CAAC;oBACH,CAAC;oBACD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;gBACtE,CAAC;YACH,CAAC,CAAC;YACF,KAAK,IAAI,EAAE,CAAC;YAEZ,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QAED;;;;;;;;;;;;;;;;;;;;;;;WAuBG;QACH,KAAK,CAAC,IAAI;YACR,MAAM,KAAK,GAAG,QAAQ,CAAC;YACvB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,OAAO,GAAG,IAAI,CAAC;gBACf,OAAO;YACT,CAAC;YAED,IAAI,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAChC,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,YAAY,EAAE,cAAc,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;gBAC7H,IAAI,aAAa,CAAC,EAAE,EAAE,CAAC;oBACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;oBACpE,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;wBACjD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;oBAC1D,CAAC;oBACD,IAAI,CAAC,eAAe,EAAE,CAAC;wBACrB,kBAAkB,CAChB,+PAA+P,CAChQ,CAAC;oBACJ,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,kBAAkB,CAChB,6DAA6D,aAAa,CAAC,MAAM,iKAAiK,CACnP,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,OAAO,GAAG,IAAI,CAAC,CAAC,wEAAwE;YAExF,IAAI,CAAC;gBACH,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACtD,CAAC;YAAC,MAAM,CAAC;gBACP,yEAAyE;YAC3E,CAAC;QACH,CAAC;QAED,YAAY;YACV,OAAO,YAAY,CAAC;QACtB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,80 +0,0 @@
1
- /**
2
- * Retroactive attach to an already-running **Node** process, via its own inspector
3
- * protocol (JVM Attach API and ptrace remain out of scope, per
4
- * `documents/decisions-inbox/RT-attach-to-running-process-scope.md`).
5
- *
6
- * Unlike the file-based `attachToRunningProcess`, this needs nothing declared at
7
- * launch: `SIGUSR1` (POSIX only) turns on the target's inspector after the fact,
8
- * making `http://127.0.0.1:<port>/json` reachable even on a process never started
9
- * with `--inspect`.
10
- *
11
- * **Disclosed limit:** `SIGUSR1` doesn't report which port the inspector bound —
12
- * Node prints that to the target's own stderr, unreadable for a process we didn't
13
- * spawn (RT-024). So discovery tries the default port (9229) unless the caller
14
- * supplies `discoveryPort`; a genuinely unknown non-default port cannot be
15
- * discovered (not guessed at by port-scanning, which risks attaching to the wrong
16
- * process).
17
- *
18
- * CDP client is `cdp-client.ts`, not Playwright's console API, which assumes a
19
- * Target/Page-domain session a Node inspector doesn't have (see that file).
20
- *
21
- * **Measured:** a freshly-spawned target has a brief window (sometimes under
22
- * 100ms) before Node installs its `SIGUSR1` handler; a signal sent inside it hits
23
- * POSIX's default terminate action instead of toggling the inspector. Irrelevant
24
- * to attaching to something already running, but real for a caller attaching
25
- * right after spawning.
26
- *
27
- * **Measured side effect:** once CDP connects, V8 no longer terminates on an
28
- * uncaught exception — it blocks ("Waiting for the debugger to disconnect...")
29
- * exactly as under `--inspect-brk`, until `stop()` closes the session. Standard
30
- * CDP/V8 behaviour, not worked around; a caller checking "did the target exit
31
- * yet?" must call `stop()` first.
32
- *
33
- * **Vocabulary:** `Runtime.consoleAPICalled`→`CONSOLE_MESSAGE`,
34
- * `Runtime.exceptionThrown`→`EXCEPTION`, same `RuntimeEventType`s
35
- * `browser-console-collector.ts` uses for the browser-page analogues.
36
- * `source: "backend-process"` (not a new value — `EvidenceSource` names what was
37
- * observed, not the mechanism). `capabilities()` reports `backendLogAccess`, not
38
- * `consoleObservation` (reserved for a browser page's console).
39
- */
40
- import type { Collector } from "@descryy/runtime-contracts";
41
- export interface AttachToRunningNodeProcessOptions {
42
- readonly processId: string;
43
- /** The pid of the already-running target. Verified alive before SIGUSR1 is sent, and never spawned or supervised by this collector. */
44
- readonly pid: number;
45
- readonly service?: string;
46
- /** Node's own default inspector bind address. Overridable for a target known to bind elsewhere. */
47
- readonly discoveryHost?: string;
48
- /** Node's default inspector port — the only one `SIGUSR1` reliably produces on an unmodified target. See module doc: a non-default port must be supplied here. */
49
- readonly discoveryPort?: number;
50
- /** How long to poll the discovery endpoint after sending SIGUSR1 before giving up. The inspector does not bind instantaneously. */
51
- readonly discoveryTimeoutMs?: number;
52
- }
53
- /**
54
- * Sends `SIGUSR1` to `pid`, then polls `http://<host>:<port>/json` until the
55
- * inspector answers or `timeoutMs` elapses. Resolves with the first entry's
56
- * `webSocketDebuggerUrl` — a Node process presents exactly one inspectable
57
- * target, unlike a browser's `/json` (one per tab).
58
- *
59
- * Rejects naming the exact blocker rather than hanging or guessing: dead pid,
60
- * non-POSIX platform, or a port that never answers (see module doc).
61
- */
62
- export declare function discoverNodeInspectorUrl(pid: number, options?: {
63
- readonly host?: string;
64
- readonly port?: number;
65
- readonly timeoutMs?: number;
66
- }): Promise<string>;
67
- /**
68
- * Attach to an already-running Node process via `SIGUSR1` + CDP, capturing
69
- * `CONSOLE_MESSAGE`/`EXCEPTION` evidence from that moment forward.
70
- *
71
- * **Retroactive, not historical.** Anything logged or thrown before attach is
72
- * gone — CDP is a live event stream with no buffer of its own (unlike the
73
- * file-based `attachToRunningProcess`, which recovers pre-attach lines from disk).
74
- *
75
- * Discovery/connection happen inside `start()`, matching every other `Collector`
76
- * here: a routine failure resolves as `{ available: false, reason }`, never a
77
- * thrown exception.
78
- */
79
- export declare function attachToRunningNodeProcess(options: AttachToRunningNodeProcessOptions): Collector;
80
- //# sourceMappingURL=attach-to-running-node-process.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"attach-to-running-node-process.d.ts","sourceRoot":"","sources":["../src/attach-to-running-node-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAyG,MAAM,4BAA4B,CAAC;AAsCnK,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uIAAuI;IACvI,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,mGAAmG;IACnG,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,kKAAkK;IAClK,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,mIAAmI;IACnI,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAOD;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5F,OAAO,CAAC,MAAM,CAAC,CA+CjB;AAsED;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,SAAS,CAmHhG"}