@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.
- package/package.json +6 -6
- package/dist/attach-to-running-jvm-process.d.ts +0 -86
- package/dist/attach-to-running-jvm-process.d.ts.map +0 -1
- package/dist/attach-to-running-jvm-process.js +0 -408
- package/dist/attach-to-running-jvm-process.js.map +0 -1
- package/dist/attach-to-running-node-process.d.ts +0 -80
- package/dist/attach-to-running-node-process.d.ts.map +0 -1
- package/dist/attach-to-running-node-process.js +0 -290
- package/dist/attach-to-running-node-process.js.map +0 -1
- package/dist/attach-to-running-process.d.ts +0 -83
- package/dist/attach-to-running-process.d.ts.map +0 -1
- package/dist/attach-to-running-process.js +0 -86
- package/dist/attach-to-running-process.js.map +0 -1
- package/dist/cdp-client.d.ts +0 -27
- package/dist/cdp-client.d.ts.map +0 -1
- package/dist/cdp-client.js +0 -98
- package/dist/cdp-client.js.map +0 -1
- package/dist/cgroup-partial-restriction.d.ts +0 -80
- package/dist/cgroup-partial-restriction.d.ts.map +0 -1
- package/dist/cgroup-partial-restriction.js +0 -189
- package/dist/cgroup-partial-restriction.js.map +0 -1
- package/dist/collector-version.d.ts +0 -7
- package/dist/collector-version.d.ts.map +0 -1
- package/dist/collector-version.js +0 -9
- package/dist/collector-version.js.map +0 -1
- package/dist/index.d.ts +0 -19
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -10
- package/dist/index.js.map +0 -1
- package/dist/instrumented-execution.d.ts +0 -135
- package/dist/instrumented-execution.d.ts.map +0 -1
- package/dist/instrumented-execution.js +0 -696
- package/dist/instrumented-execution.js.map +0 -1
- package/dist/jvm-agent/build.d.ts +0 -48
- package/dist/jvm-agent/build.d.ts.map +0 -1
- package/dist/jvm-agent/build.js +0 -129
- package/dist/jvm-agent/build.js.map +0 -1
- package/dist/polling-output-source.d.ts +0 -43
- package/dist/polling-output-source.d.ts.map +0 -1
- package/dist/polling-output-source.js +0 -64
- package/dist/polling-output-source.js.map +0 -1
- package/dist/profile-backend-observation.d.ts +0 -67
- package/dist/profile-backend-observation.d.ts.map +0 -1
- package/dist/profile-backend-observation.js +0 -107
- package/dist/profile-backend-observation.js.map +0 -1
- package/dist/respawn-and-supervise.d.ts +0 -86
- package/dist/respawn-and-supervise.d.ts.map +0 -1
- package/dist/respawn-and-supervise.js +0 -171
- package/dist/respawn-and-supervise.js.map +0 -1
|
@@ -1,290 +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 { spawnSync } from "node:child_process";
|
|
41
|
-
import { connectCdp } from "./cdp-client.js";
|
|
42
|
-
import { isProcessAlive } from "./attach-to-running-process.js";
|
|
43
|
-
import { COLLECTOR_VERSION } from "./collector-version.js";
|
|
44
|
-
/**
|
|
45
|
-
* A-F10: verifies `pid` is owned by this process's own user AND looks like a
|
|
46
|
-
* Node process, BEFORE `SIGUSR1` is sent — a wrong-kind pid with no handler hits
|
|
47
|
-
* POSIX's default terminate action; `isProcessAlive` alone doesn't rule that out.
|
|
48
|
-
*
|
|
49
|
-
* Uses `ps -o uid=,comm= -p <pid>` rather than `/proc/<pid>` since this module is
|
|
50
|
-
* POSIX-only and `ps` works on both Linux and macOS. Refuses rather than guesses
|
|
51
|
-
* when `ps` itself can't answer.
|
|
52
|
-
*
|
|
53
|
-
* Same-user, not same-session: the kernel lets any same-user process signal any
|
|
54
|
-
* other, so this check is the only backstop for `SIGUSR1` — unlike HotSpot's
|
|
55
|
-
* attach socket (the JVM sibling), which enforces uid at the OS level itself.
|
|
56
|
-
*/
|
|
57
|
-
function verifyLikelyNodeProcessOwnedBySelf(pid) {
|
|
58
|
-
const result = spawnSync("ps", ["-o", "uid=,comm=", "-p", String(pid)], { encoding: "utf8" });
|
|
59
|
-
const line = (result.stdout ?? "").trim().split("\n")[0]?.trim();
|
|
60
|
-
if (result.status !== 0 || line === undefined || line === "") {
|
|
61
|
-
return { ok: false, reason: `could not verify pid ${String(pid)}'s owner/command via ps (exit ${String(result.status)}) — refusing to signal a process this module cannot confirm the identity of` };
|
|
62
|
-
}
|
|
63
|
-
const [uidField, ...commFields] = line.split(/\s+/);
|
|
64
|
-
const uid = Number(uidField);
|
|
65
|
-
const comm = commFields.join(" ").toLowerCase();
|
|
66
|
-
const ownUid = process.getuid?.();
|
|
67
|
-
if (ownUid !== undefined && uid !== ownUid) {
|
|
68
|
-
return { ok: false, reason: `pid ${String(pid)} is owned by uid ${String(uid)}, not this process's own uid ${String(ownUid)} — refusing to signal a process this session does not own` };
|
|
69
|
-
}
|
|
70
|
-
if (!comm.includes("node")) {
|
|
71
|
-
return { ok: false, reason: `pid ${String(pid)}'s command is "${comm}", which does not look like a Node process — refusing to send SIGUSR1 to a process that may not handle it` };
|
|
72
|
-
}
|
|
73
|
-
return { ok: true };
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Sends `SIGUSR1` to `pid`, then polls `http://<host>:<port>/json` until the
|
|
77
|
-
* inspector answers or `timeoutMs` elapses. Resolves with the first entry's
|
|
78
|
-
* `webSocketDebuggerUrl` — a Node process presents exactly one inspectable
|
|
79
|
-
* target, unlike a browser's `/json` (one per tab).
|
|
80
|
-
*
|
|
81
|
-
* Rejects naming the exact blocker rather than hanging or guessing: dead pid,
|
|
82
|
-
* non-POSIX platform, or a port that never answers (see module doc).
|
|
83
|
-
*/
|
|
84
|
-
export async function discoverNodeInspectorUrl(pid, options = {}) {
|
|
85
|
-
if (!isProcessAlive(pid)) {
|
|
86
|
-
throw new Error(`discoverNodeInspectorUrl: pid ${String(pid)} is not running — cannot attach to a target that does not exist.`);
|
|
87
|
-
}
|
|
88
|
-
if (process.platform === "win32") {
|
|
89
|
-
throw new Error("discoverNodeInspectorUrl: SIGUSR1 inspector activation is POSIX-only; not available on win32.");
|
|
90
|
-
}
|
|
91
|
-
// A-F10: verified BEFORE the signal — a wrong pid must never reach process.kill
|
|
92
|
-
const identity = verifyLikelyNodeProcessOwnedBySelf(pid);
|
|
93
|
-
if (!identity.ok) {
|
|
94
|
-
throw new Error(`discoverNodeInspectorUrl: ${identity.reason}.`);
|
|
95
|
-
}
|
|
96
|
-
const host = options.host ?? "127.0.0.1";
|
|
97
|
-
const port = options.port ?? 9229;
|
|
98
|
-
const timeoutMs = options.timeoutMs ?? 5000;
|
|
99
|
-
const discoveryUrl = `http://${host}:${String(port)}/json`;
|
|
100
|
-
process.kill(pid, "SIGUSR1");
|
|
101
|
-
const deadline = Date.now() + timeoutMs;
|
|
102
|
-
let lastError = null;
|
|
103
|
-
while (Date.now() < deadline) {
|
|
104
|
-
if (!isProcessAlive(pid)) {
|
|
105
|
-
throw new Error(`discoverNodeInspectorUrl: pid ${String(pid)} exited while waiting for its inspector to come up.`);
|
|
106
|
-
}
|
|
107
|
-
try {
|
|
108
|
-
const response = await fetch(discoveryUrl);
|
|
109
|
-
if (response.ok) {
|
|
110
|
-
const targets = (await response.json());
|
|
111
|
-
const target = targets.find((candidate) => candidate.webSocketDebuggerUrl !== undefined);
|
|
112
|
-
if (target?.webSocketDebuggerUrl !== undefined)
|
|
113
|
-
return target.webSocketDebuggerUrl;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
lastError = error;
|
|
118
|
-
}
|
|
119
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
120
|
-
}
|
|
121
|
-
const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
|
|
122
|
-
throw new Error(`discoverNodeInspectorUrl: no inspector answered at ${discoveryUrl} within ${String(timeoutMs)}ms after SIGUSR1${detail}. ` +
|
|
123
|
-
"Most likely the target's inspector bound to a different port than the one checked here (already in use by another " +
|
|
124
|
-
"process, or the target was itself configured with --inspect-port/NODE_OPTIONS) — this module cannot discover a " +
|
|
125
|
-
"non-default port from outside; a caller who knows the real port must supply discoveryPort.");
|
|
126
|
-
}
|
|
127
|
-
/** Decodes a `file://` URL to a plain filesystem path, matching every other adapter's convention. Anything else is passed through verbatim. */
|
|
128
|
-
function pathFromCdpUrl(url) {
|
|
129
|
-
if (url === undefined || url === "")
|
|
130
|
-
return null;
|
|
131
|
-
if (!url.startsWith("file://"))
|
|
132
|
-
return url;
|
|
133
|
-
try {
|
|
134
|
-
return new URL(url).pathname;
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
return url;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
/** CDP reports 0-based line/column; converted to this codebase's 1-based convention here, once. */
|
|
141
|
-
function locationFromCallFrame(frame) {
|
|
142
|
-
return {
|
|
143
|
-
file: pathFromCdpUrl(frame.url),
|
|
144
|
-
line: typeof frame.lineNumber === "number" ? frame.lineNumber + 1 : null,
|
|
145
|
-
column: typeof frame.columnNumber === "number" ? frame.columnNumber + 1 : null,
|
|
146
|
-
functionName: frame.functionName !== undefined && frame.functionName !== "" ? frame.functionName : null,
|
|
147
|
-
// V8's own reported state, captured live -- never a side artifact resolved through
|
|
148
|
-
reliability: "self-contained",
|
|
149
|
-
resolvedVia: null,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
function formatConsoleArgs(args) {
|
|
153
|
-
return args
|
|
154
|
-
.map((arg) => {
|
|
155
|
-
if (arg.value !== undefined)
|
|
156
|
-
return typeof arg.value === "string" ? arg.value : JSON.stringify(arg.value);
|
|
157
|
-
if (arg.description !== undefined)
|
|
158
|
-
return arg.description;
|
|
159
|
-
return `<${arg.type ?? "unknown"}>`;
|
|
160
|
-
})
|
|
161
|
-
.join(" ");
|
|
162
|
-
}
|
|
163
|
-
function notThisCollectorsJob(what) {
|
|
164
|
-
return { availability: "unavailable", reason: `attachToRunningNodeProcess observes a Node process's Runtime domain (console, exceptions) only, not ${what}.` };
|
|
165
|
-
}
|
|
166
|
-
const CAPABILITIES = {
|
|
167
|
-
domObservation: notThisCollectorsJob("DOM inspection"),
|
|
168
|
-
// reserved for a browser page's console; this is a backend process's, reported
|
|
169
|
-
// under backendLogAccess instead, matching LogCollector's own split
|
|
170
|
-
consoleObservation: notThisCollectorsJob("a browser page's console"),
|
|
171
|
-
networkObservation: notThisCollectorsJob("network traffic"),
|
|
172
|
-
backendLogAccess: { availability: "available", reason: null },
|
|
173
|
-
distributedTrace: notThisCollectorsJob("trace-id/request-id correlation — CDP's Runtime domain carries no such fields to extract"),
|
|
174
|
-
// self-contained reliability: a bundled/compiled target still reports positions
|
|
175
|
-
// in that output verbatim, same limit every self-contained adapter carries
|
|
176
|
-
sourceMapping: { availability: "available", reason: null },
|
|
177
|
-
stackCapture: { availability: "available", reason: null },
|
|
178
|
-
processLifecycle: notThisCollectorsJob("process lifecycle — spawn/ready/exit are ProcessCollector's to observe"),
|
|
179
|
-
databaseObservation: notThisCollectorsJob("database queries"),
|
|
180
|
-
externalServiceObservation: notThisCollectorsJob("outbound external requests"),
|
|
181
|
-
};
|
|
182
|
-
/**
|
|
183
|
-
* Attach to an already-running Node process via `SIGUSR1` + CDP, capturing
|
|
184
|
-
* `CONSOLE_MESSAGE`/`EXCEPTION` evidence from that moment forward.
|
|
185
|
-
*
|
|
186
|
-
* **Retroactive, not historical.** Anything logged or thrown before attach is
|
|
187
|
-
* gone — CDP is a live event stream with no buffer of its own (unlike the
|
|
188
|
-
* file-based `attachToRunningProcess`, which recovers pre-attach lines from disk).
|
|
189
|
-
*
|
|
190
|
-
* Discovery/connection happen inside `start()`, matching every other `Collector`
|
|
191
|
-
* here: a routine failure resolves as `{ available: false, reason }`, never a
|
|
192
|
-
* thrown exception.
|
|
193
|
-
*/
|
|
194
|
-
export function attachToRunningNodeProcess(options) {
|
|
195
|
-
let session = null;
|
|
196
|
-
return {
|
|
197
|
-
collectorId: `attach-node-inspector:${options.processId}`,
|
|
198
|
-
async start(context) {
|
|
199
|
-
let webSocketDebuggerUrl;
|
|
200
|
-
try {
|
|
201
|
-
webSocketDebuggerUrl = await discoverNodeInspectorUrl(options.pid, {
|
|
202
|
-
...(options.discoveryHost !== undefined ? { host: options.discoveryHost } : {}),
|
|
203
|
-
...(options.discoveryPort !== undefined ? { port: options.discoveryPort } : {}),
|
|
204
|
-
...(options.discoveryTimeoutMs !== undefined ? { timeoutMs: options.discoveryTimeoutMs } : {}),
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
catch (error) {
|
|
208
|
-
return { available: false, reason: error instanceof Error ? error.message : String(error) };
|
|
209
|
-
}
|
|
210
|
-
try {
|
|
211
|
-
session = await connectCdp(webSocketDebuggerUrl);
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
return {
|
|
215
|
-
available: false,
|
|
216
|
-
reason: `attachToRunningNodeProcess: found the inspector but could not open a CDP connection to it: ${error instanceof Error ? error.message : String(error)}`,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
await session.send("Runtime.enable");
|
|
220
|
-
session.on("Runtime.consoleAPICalled", (params) => {
|
|
221
|
-
const p = params;
|
|
222
|
-
context.emit({
|
|
223
|
-
timestamp: new Date().toISOString(),
|
|
224
|
-
source: "backend-process",
|
|
225
|
-
service: options.service ?? null,
|
|
226
|
-
process: options.processId,
|
|
227
|
-
eventType: "CONSOLE_MESSAGE",
|
|
228
|
-
payload: { level: p.type ?? "log", text: formatConsoleArgs(p.args ?? []) },
|
|
229
|
-
traceId: null,
|
|
230
|
-
requestId: null,
|
|
231
|
-
correlationId: null,
|
|
232
|
-
graphNodeId: null,
|
|
233
|
-
sourceLocation: null,
|
|
234
|
-
stackTrace: null,
|
|
235
|
-
confidence: 1,
|
|
236
|
-
redactionStatus: "pending-redaction",
|
|
237
|
-
collectorVersion: COLLECTOR_VERSION,
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
session.on("Runtime.exceptionThrown", (params) => {
|
|
241
|
-
const p = params;
|
|
242
|
-
const details = p.exceptionDetails ?? {};
|
|
243
|
-
const description = details.exception?.description;
|
|
244
|
-
const message = description !== undefined
|
|
245
|
-
? (description.split("\n")[0] ?? description)
|
|
246
|
-
: details.exception?.value !== undefined
|
|
247
|
-
? String(details.exception.value)
|
|
248
|
-
: (details.text ?? "Uncaught exception");
|
|
249
|
-
const callFrames = details.stackTrace?.callFrames ?? [];
|
|
250
|
-
const frames = callFrames.map((frame) => ({ location: locationFromCallFrame(frame), raw: `${frame.functionName ?? "<anonymous>"} (${frame.url ?? ""}:${String(frame.lineNumber)}:${String(frame.columnNumber)})` }));
|
|
251
|
-
// no frames from CDP: fall back to the throw site itself as a synthetic
|
|
252
|
-
// one-frame trace rather than discarding it
|
|
253
|
-
const stackTrace = frames.length > 0
|
|
254
|
-
? { fidelity: "synchronous", frames, primaryFrameIndex: 0 }
|
|
255
|
-
: {
|
|
256
|
-
fidelity: "synchronous",
|
|
257
|
-
frames: [{ location: locationFromCallFrame({ url: details.url, lineNumber: details.lineNumber, columnNumber: details.columnNumber }), raw: description ?? message }],
|
|
258
|
-
primaryFrameIndex: 0,
|
|
259
|
-
};
|
|
260
|
-
context.emit({
|
|
261
|
-
timestamp: new Date().toISOString(),
|
|
262
|
-
source: "backend-process",
|
|
263
|
-
service: options.service ?? null,
|
|
264
|
-
process: options.processId,
|
|
265
|
-
eventType: "EXCEPTION",
|
|
266
|
-
payload: { message, stack: description ?? null },
|
|
267
|
-
traceId: null,
|
|
268
|
-
requestId: null,
|
|
269
|
-
correlationId: null,
|
|
270
|
-
graphNodeId: null,
|
|
271
|
-
sourceLocation: stackTrace.frames[stackTrace.primaryFrameIndex]?.location ?? null,
|
|
272
|
-
stackTrace,
|
|
273
|
-
confidence: 1,
|
|
274
|
-
redactionStatus: "pending-redaction",
|
|
275
|
-
collectorVersion: COLLECTOR_VERSION,
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
return { available: true };
|
|
279
|
-
},
|
|
280
|
-
stop() {
|
|
281
|
-
session?.close();
|
|
282
|
-
session = null;
|
|
283
|
-
return Promise.resolve();
|
|
284
|
-
},
|
|
285
|
-
capabilities() {
|
|
286
|
-
return CAPABILITIES;
|
|
287
|
-
},
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
//# sourceMappingURL=attach-to-running-node-process.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"attach-to-running-node-process.js","sourceRoot":"","sources":["../src/attach-to-running-node-process.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Attach observation to a process Descry did not spawn.
|
|
3
|
-
*
|
|
4
|
-
* Node/POSIX give no way to retroactively attach to another process's
|
|
5
|
-
* stdout/stderr fds after the fact — pipes are only wired at spawn time. So true
|
|
6
|
-
* generic attach (a process that logs to a TTY, or never wrote output anywhere
|
|
7
|
-
* reachable) is not implemented; it would need a per-language debugger/inspector
|
|
8
|
-
* protocol, scoped separately in `documents/decisions-inbox/RT-attach-to-running-process-scope.md`.
|
|
9
|
-
*
|
|
10
|
-
* What IS implemented: attaching to a process already directed to write output to
|
|
11
|
-
* a file Descry can read (e.g. `npm run dev > server.log 2>&1 &`).
|
|
12
|
-
* `attachToRunningProcess` verifies the target pid is alive, then reuses `execute`'s
|
|
13
|
-
* live-tail machinery, catching up on what the file already held and tailing new
|
|
14
|
-
* writes the same way.
|
|
15
|
-
*
|
|
16
|
-
* **Shares `execute`'s identity gap (RT-024).** `isProcessAlive` confirms a process
|
|
17
|
-
* with this pid exists, not that it's the one the caller thinks — pids are reused.
|
|
18
|
-
* Disclosed, not solved, same as `readiness.ts`.
|
|
19
|
-
*/
|
|
20
|
-
import type { ProcessOutputSource } from "@descryy/runtime-backend-observation";
|
|
21
|
-
import { type AttachFence } from "@descryy/runtime-controller";
|
|
22
|
-
export interface AttachToRunningProcessOptions {
|
|
23
|
-
readonly processId: string;
|
|
24
|
-
/** The pid of the already-running target. Verified alive before attaching, and re-checked on every poll to detect exit. */
|
|
25
|
-
readonly pid: number;
|
|
26
|
-
/** Path to a file the target process is already writing its stdout/stderr to. Read in full on every poll — same growing-buffer contract `ManagedProcess.readOutput()` has, not a delta. */
|
|
27
|
-
readonly logFilePath: string;
|
|
28
|
-
/**
|
|
29
|
-
* The declared service this pid is. Optional because a bare-pid attach may
|
|
30
|
-
* genuinely have none declared — omitting it resolves to `null`, a statement,
|
|
31
|
-
* not a gap. Without it, correlation can't look up a scope for this source.
|
|
32
|
-
*/
|
|
33
|
-
readonly serviceName?: string | null;
|
|
34
|
-
/**
|
|
35
|
-
* True once the caller's observation window has closed. Required: a source has
|
|
36
|
-
* two independent end conditions (target exited, or caller stopped watching),
|
|
37
|
-
* and the liveness check alone never fires for a healthy target — without this,
|
|
38
|
-
* the source polls forever, the final unflushed line is lost, and a consumer
|
|
39
|
-
* that stops iterating never gets its generator back. Same signal
|
|
40
|
-
* `instrumented-execution.ts` uses before `collector.stop()`.
|
|
41
|
-
*/
|
|
42
|
-
readonly stopObserving: () => boolean;
|
|
43
|
-
readonly pollIntervalMs?: number;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* `process.kill(pid, 0)` sends no signal — it only asks the kernel whether
|
|
47
|
-
* delivery would be possible. `ESRCH` means no such pid; `EPERM` means a
|
|
48
|
-
* pid we don't own but that unambiguously exists, still alive for this
|
|
49
|
-
* purpose. Any other error is treated as "exists" rather than guessed away.
|
|
50
|
-
*/
|
|
51
|
-
export declare function isProcessAlive(pid: number): boolean;
|
|
52
|
-
/**
|
|
53
|
-
* `attachToRunningProcess`'s return value: a `ProcessOutputSource` (structurally
|
|
54
|
-
* unchanged, so every existing consumer keeps working unmodified) plus the
|
|
55
|
-
* attach-time fence (RT-N3) that produced its `preExistingLineCount`. Exposed so
|
|
56
|
-
* a caller can disclose it -- the process's own start time, whether some other
|
|
57
|
-
* process also holds the log file open, and the recorded attach offset -- rather
|
|
58
|
-
* than that information existing only internally to this function.
|
|
59
|
-
*/
|
|
60
|
-
export interface AttachedProcessOutputSource extends ProcessOutputSource {
|
|
61
|
-
readonly attachFence: AttachFence;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Attach to an already-running process via a log file it is already writing to.
|
|
65
|
-
* Throws immediately if the pid isn't alive — refuses rather than returning a
|
|
66
|
-
* source that would silently never emit anything.
|
|
67
|
-
*
|
|
68
|
-
* Lines already in `logFilePath` at attach time are yielded first, followed by
|
|
69
|
-
* whatever the target writes afterward — indistinguishable from `execute`'s live
|
|
70
|
-
* tail once attached, EXCEPT that each pre-existing line carries
|
|
71
|
-
* `ProcessOutputLine.preExisting: true` (RT-N3, `computeAttachFence`) — real,
|
|
72
|
-
* captured evidence, but evidence this run did not watch happen. A consumer that
|
|
73
|
-
* turns these lines into `Evidence` (e.g. `createLogCollector`) is expected to
|
|
74
|
-
* carry that flag through to `Evidence.preExisting`, and a promotion path that
|
|
75
|
-
* would otherwise mint or confirm an R4 graph edge from it must refuse (see
|
|
76
|
-
* `resolveObservedCrossServiceCall`, `@descryy/runtime-graph-correlator`).
|
|
77
|
-
*
|
|
78
|
-
* Ends when the caller's window closes or the target exits, whichever comes
|
|
79
|
-
* first (see `stopObserving`) — both are needed, or a healthy target polls
|
|
80
|
-
* forever, or a dead one polls until the caller notices.
|
|
81
|
-
*/
|
|
82
|
-
export declare function attachToRunningProcess(options: AttachToRunningProcessOptions): AttachedProcessOutputSource;
|
|
83
|
-
//# sourceMappingURL=attach-to-running-process.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"attach-to-running-process.d.ts","sourceRoot":"","sources":["../src/attach-to-running-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAInF,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2HAA2H;IAC3H,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,2LAA2L;IAC3L,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;;;;;;;OAOG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,OAAO,CAAC;IACtC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,2BAA4B,SAAQ,mBAAmB;IACtE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,2BAA2B,CAkC1G"}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Attach observation to a process Descry did not spawn.
|
|
3
|
-
*
|
|
4
|
-
* Node/POSIX give no way to retroactively attach to another process's
|
|
5
|
-
* stdout/stderr fds after the fact — pipes are only wired at spawn time. So true
|
|
6
|
-
* generic attach (a process that logs to a TTY, or never wrote output anywhere
|
|
7
|
-
* reachable) is not implemented; it would need a per-language debugger/inspector
|
|
8
|
-
* protocol, scoped separately in `documents/decisions-inbox/RT-attach-to-running-process-scope.md`.
|
|
9
|
-
*
|
|
10
|
-
* What IS implemented: attaching to a process already directed to write output to
|
|
11
|
-
* a file Descry can read (e.g. `npm run dev > server.log 2>&1 &`).
|
|
12
|
-
* `attachToRunningProcess` verifies the target pid is alive, then reuses `execute`'s
|
|
13
|
-
* live-tail machinery, catching up on what the file already held and tailing new
|
|
14
|
-
* writes the same way.
|
|
15
|
-
*
|
|
16
|
-
* **Shares `execute`'s identity gap (RT-024).** `isProcessAlive` confirms a process
|
|
17
|
-
* with this pid exists, not that it's the one the caller thinks — pids are reused.
|
|
18
|
-
* Disclosed, not solved, same as `readiness.ts`.
|
|
19
|
-
*/
|
|
20
|
-
import { readFileSync } from "node:fs";
|
|
21
|
-
import { computeAttachFence } from "@descryy/runtime-controller";
|
|
22
|
-
import { createPollingProcessOutputSource } from "./polling-output-source.js";
|
|
23
|
-
/**
|
|
24
|
-
* `process.kill(pid, 0)` sends no signal — it only asks the kernel whether
|
|
25
|
-
* delivery would be possible. `ESRCH` means no such pid; `EPERM` means a
|
|
26
|
-
* pid we don't own but that unambiguously exists, still alive for this
|
|
27
|
-
* purpose. Any other error is treated as "exists" rather than guessed away.
|
|
28
|
-
*/
|
|
29
|
-
export function isProcessAlive(pid) {
|
|
30
|
-
try {
|
|
31
|
-
process.kill(pid, 0);
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
return error.code === "EPERM";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Attach to an already-running process via a log file it is already writing to.
|
|
40
|
-
* Throws immediately if the pid isn't alive — refuses rather than returning a
|
|
41
|
-
* source that would silently never emit anything.
|
|
42
|
-
*
|
|
43
|
-
* Lines already in `logFilePath` at attach time are yielded first, followed by
|
|
44
|
-
* whatever the target writes afterward — indistinguishable from `execute`'s live
|
|
45
|
-
* tail once attached, EXCEPT that each pre-existing line carries
|
|
46
|
-
* `ProcessOutputLine.preExisting: true` (RT-N3, `computeAttachFence`) — real,
|
|
47
|
-
* captured evidence, but evidence this run did not watch happen. A consumer that
|
|
48
|
-
* turns these lines into `Evidence` (e.g. `createLogCollector`) is expected to
|
|
49
|
-
* carry that flag through to `Evidence.preExisting`, and a promotion path that
|
|
50
|
-
* would otherwise mint or confirm an R4 graph edge from it must refuse (see
|
|
51
|
-
* `resolveObservedCrossServiceCall`, `@descryy/runtime-graph-correlator`).
|
|
52
|
-
*
|
|
53
|
-
* Ends when the caller's window closes or the target exits, whichever comes
|
|
54
|
-
* first (see `stopObserving`) — both are needed, or a healthy target polls
|
|
55
|
-
* forever, or a dead one polls until the caller notices.
|
|
56
|
-
*/
|
|
57
|
-
export function attachToRunningProcess(options) {
|
|
58
|
-
if (!isProcessAlive(options.pid)) {
|
|
59
|
-
throw new Error(`attachToRunningProcess: pid ${options.pid} for "${options.processId}" is not running — attach requires ` +
|
|
60
|
-
`a target that already exists. Use ExecutionController.run() to spawn a new process instead.`);
|
|
61
|
-
}
|
|
62
|
-
const attachFence = computeAttachFence(options.pid, options.logFilePath);
|
|
63
|
-
const source = createPollingProcessOutputSource({
|
|
64
|
-
processId: options.processId,
|
|
65
|
-
// an ad-hoc attach may have no declared service; null is the honest answer
|
|
66
|
-
serviceName: options.serviceName ?? null,
|
|
67
|
-
readOutput: () => {
|
|
68
|
-
try {
|
|
69
|
-
return readFileSync(options.logFilePath, "utf8");
|
|
70
|
-
}
|
|
71
|
-
catch (error) {
|
|
72
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
73
|
-
throw new Error(`attachToRunningProcess: could not read log file "${options.logFilePath}" for pid ${options.pid}: ${detail}`);
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
// either reason ends the source; dropping the liveness half would leave a
|
|
77
|
-
// target that dies mid-window polled until the caller noticed
|
|
78
|
-
isFinished: () => options.stopObserving() || !isProcessAlive(options.pid),
|
|
79
|
-
// RT-N3: everything already in the file at attach time is marked, not
|
|
80
|
-
// discarded -- see `AttachedProcessOutputSource`'s doc.
|
|
81
|
-
preExistingLineCount: attachFence.preExistingLineCount,
|
|
82
|
-
...(options.pollIntervalMs !== undefined ? { pollIntervalMs: options.pollIntervalMs } : {}),
|
|
83
|
-
});
|
|
84
|
-
return { ...source, attachFence };
|
|
85
|
-
}
|
|
86
|
-
//# sourceMappingURL=attach-to-running-process.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"attach-to-running-process.js","sourceRoot":"","sources":["../src/attach-to-running-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,EAAE,kBAAkB,EAAoB,MAAM,6BAA6B,CAAC;AAEnF,OAAO,EAAE,gCAAgC,EAAE,MAAM,4BAA4B,CAAC;AA0B9E;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAQ,KAA+B,CAAC,IAAI,KAAK,OAAO,CAAC;IAC3D,CAAC;AACH,CAAC;AAcD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAsC;IAC3E,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,+BAA+B,OAAO,CAAC,GAAG,SAAS,OAAO,CAAC,SAAS,qCAAqC;YACvG,6FAA6F,CAChG,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAEzE,MAAM,MAAM,GAAG,gCAAgC,CAAC;QAC9C,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,2EAA2E;QAC3E,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;QACxC,UAAU,EAAE,GAAG,EAAE;YACf,IAAI,CAAC;gBACH,OAAO,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACnD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACtE,MAAM,IAAI,KAAK,CACb,oDAAoD,OAAO,CAAC,WAAW,aAAa,OAAO,CAAC,GAAG,KAAK,MAAM,EAAE,CAC7G,CAAC;YACJ,CAAC;QACH,CAAC;QACD,0EAA0E;QAC1E,8DAA8D;QAC9D,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC;QACzE,sEAAsE;QACtE,wDAAwD;QACxD,oBAAoB,EAAE,WAAW,CAAC,oBAAoB;QACtD,GAAG,CAAC,OAAO,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5F,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,MAAM,EAAE,WAAW,EAAE,CAAC;AACpC,CAAC"}
|
package/dist/cdp-client.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A minimal Chrome DevTools Protocol client over a raw `ws://` URL.
|
|
3
|
-
*
|
|
4
|
-
* `packages/browser` speaks CDP only through Playwright's console API, which owns its
|
|
5
|
-
* own WebSocket and Target/Page framing and has no "connect to an arbitrary ws:// and
|
|
6
|
-
* read raw CDP frames" surface — a Node inspector session has no Target/Page domains
|
|
7
|
-
* at all, so that abstraction doesn't fit. Kept protocol-family-generic (no "Node" or
|
|
8
|
-
* "browser" knowledge) so either kind of CDP target can reuse it.
|
|
9
|
-
*
|
|
10
|
-
* CDP-over-WebSocket is JSON-RPC-shaped: a sent message carries `id` and gets exactly
|
|
11
|
-
* one `{id, result}` or `{id, error}` reply; an event has `method`/`params`, no `id`.
|
|
12
|
-
* This client does only that framing — method names and event shapes are the caller's.
|
|
13
|
-
*/
|
|
14
|
-
export interface CdpSession {
|
|
15
|
-
/** Sends a CDP command and resolves with its `result` once the matching `id` reply arrives. Rejects on a CDP-reported `error` or if the socket closes first. */
|
|
16
|
-
send<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T>;
|
|
17
|
-
/** Subscribes to a CDP event by method name (e.g. `"Runtime.consoleAPICalled"`). Returns an unsubscribe function. */
|
|
18
|
-
on(method: string, handler: (params: Record<string, unknown>) => void): () => void;
|
|
19
|
-
/** Closes the underlying WebSocket. Idempotent. Rejects every still-pending `send()`. */
|
|
20
|
-
close(): void;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Resolves once the socket is open, or rejects if it errors/closes first —
|
|
24
|
-
* so a caller never has to race `open` against `error` itself.
|
|
25
|
-
*/
|
|
26
|
-
export declare function connectCdp(webSocketUrl: string): Promise<CdpSession>;
|
|
27
|
-
//# sourceMappingURL=cdp-client.d.ts.map
|
package/dist/cdp-client.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cdp-client.d.ts","sourceRoot":"","sources":["../src/cdp-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,UAAU;IACzB,gKAAgK;IAChK,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAChF,qHAAqH;IACrH,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACnF,yFAAyF;IACzF,KAAK,IAAI,IAAI,CAAC;CACf;AAOD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAgFpE"}
|
package/dist/cdp-client.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A minimal Chrome DevTools Protocol client over a raw `ws://` URL.
|
|
3
|
-
*
|
|
4
|
-
* `packages/browser` speaks CDP only through Playwright's console API, which owns its
|
|
5
|
-
* own WebSocket and Target/Page framing and has no "connect to an arbitrary ws:// and
|
|
6
|
-
* read raw CDP frames" surface — a Node inspector session has no Target/Page domains
|
|
7
|
-
* at all, so that abstraction doesn't fit. Kept protocol-family-generic (no "Node" or
|
|
8
|
-
* "browser" knowledge) so either kind of CDP target can reuse it.
|
|
9
|
-
*
|
|
10
|
-
* CDP-over-WebSocket is JSON-RPC-shaped: a sent message carries `id` and gets exactly
|
|
11
|
-
* one `{id, result}` or `{id, error}` reply; an event has `method`/`params`, no `id`.
|
|
12
|
-
* This client does only that framing — method names and event shapes are the caller's.
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* Resolves once the socket is open, or rejects if it errors/closes first —
|
|
16
|
-
* so a caller never has to race `open` against `error` itself.
|
|
17
|
-
*/
|
|
18
|
-
export function connectCdp(webSocketUrl) {
|
|
19
|
-
return new Promise((resolveConnect, rejectConnect) => {
|
|
20
|
-
const socket = new WebSocket(webSocketUrl);
|
|
21
|
-
const pending = new Map();
|
|
22
|
-
const listeners = new Map();
|
|
23
|
-
let nextId = 1;
|
|
24
|
-
let settled = false;
|
|
25
|
-
function failAllPending(error) {
|
|
26
|
-
for (const { reject } of pending.values())
|
|
27
|
-
reject(error);
|
|
28
|
-
pending.clear();
|
|
29
|
-
}
|
|
30
|
-
socket.addEventListener("open", () => {
|
|
31
|
-
settled = true;
|
|
32
|
-
resolveConnect({
|
|
33
|
-
send(method, params = {}) {
|
|
34
|
-
const id = nextId++;
|
|
35
|
-
return new Promise((resolve, reject) => {
|
|
36
|
-
pending.set(id, { resolve: resolve, reject });
|
|
37
|
-
socket.send(JSON.stringify({ id, method, params }));
|
|
38
|
-
});
|
|
39
|
-
},
|
|
40
|
-
on(method, handler) {
|
|
41
|
-
let set = listeners.get(method);
|
|
42
|
-
if (set === undefined) {
|
|
43
|
-
set = new Set();
|
|
44
|
-
listeners.set(method, set);
|
|
45
|
-
}
|
|
46
|
-
set.add(handler);
|
|
47
|
-
return () => set.delete(handler);
|
|
48
|
-
},
|
|
49
|
-
close() {
|
|
50
|
-
socket.close();
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
socket.addEventListener("message", (event) => {
|
|
55
|
-
const raw = typeof event.data === "string" ? event.data : String(event.data);
|
|
56
|
-
let msg;
|
|
57
|
-
try {
|
|
58
|
-
msg = JSON.parse(raw);
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
// non-JSON frame: not a valid CDP message, dropped rather than thrown
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (msg.id !== undefined) {
|
|
65
|
-
const waiting = pending.get(msg.id);
|
|
66
|
-
if (waiting === undefined)
|
|
67
|
-
return;
|
|
68
|
-
pending.delete(msg.id);
|
|
69
|
-
if (msg.error !== undefined) {
|
|
70
|
-
waiting.reject(new Error(`CDP error for message ${String(msg.id)}: ${msg.error.message}`));
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
waiting.resolve(msg.result);
|
|
74
|
-
}
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
if (msg.method !== undefined) {
|
|
78
|
-
const set = listeners.get(msg.method);
|
|
79
|
-
if (set === undefined)
|
|
80
|
-
return;
|
|
81
|
-
for (const handler of set)
|
|
82
|
-
handler(msg.params ?? {});
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
socket.addEventListener("error", () => {
|
|
86
|
-
const error = new Error(`CDP WebSocket error connecting to ${webSocketUrl}`);
|
|
87
|
-
if (!settled) {
|
|
88
|
-
settled = true;
|
|
89
|
-
rejectConnect(error);
|
|
90
|
-
}
|
|
91
|
-
failAllPending(error);
|
|
92
|
-
});
|
|
93
|
-
socket.addEventListener("close", () => {
|
|
94
|
-
failAllPending(new Error(`CDP WebSocket to ${webSocketUrl} closed`));
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
//# sourceMappingURL=cdp-client.js.map
|
package/dist/cdp-client.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cdp-client.js","sourceRoot":"","sources":["../src/cdp-client.ts"],"names":[],"mappings":"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"}
|