@descryy/runtime-orchestrator 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +6 -0
- package/dist/attach-to-running-jvm-process.d.ts +45 -98
- package/dist/attach-to-running-jvm-process.d.ts.map +1 -1
- package/dist/attach-to-running-jvm-process.js +206 -136
- package/dist/attach-to-running-jvm-process.js.map +1 -1
- package/dist/attach-to-running-node-process.d.ts +45 -103
- package/dist/attach-to-running-node-process.d.ts.map +1 -1
- package/dist/attach-to-running-node-process.js +90 -115
- package/dist/attach-to-running-node-process.js.map +1 -1
- package/dist/attach-to-running-process.d.ts +51 -71
- package/dist/attach-to-running-process.d.ts.map +1 -1
- package/dist/attach-to-running-process.js +39 -55
- package/dist/attach-to-running-process.js.map +1 -1
- package/dist/cdp-client.d.ts +8 -18
- package/dist/cdp-client.d.ts.map +1 -1
- package/dist/cdp-client.js +9 -22
- package/dist/cdp-client.js.map +1 -1
- package/dist/cgroup-partial-restriction.d.ts +42 -89
- package/dist/cgroup-partial-restriction.d.ts.map +1 -1
- package/dist/cgroup-partial-restriction.js +44 -91
- package/dist/cgroup-partial-restriction.js.map +1 -1
- package/dist/collector-version.d.ts +3 -8
- package/dist/collector-version.d.ts.map +1 -1
- package/dist/collector-version.js +3 -8
- package/dist/collector-version.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/instrumented-execution.d.ts +30 -73
- package/dist/instrumented-execution.d.ts.map +1 -1
- package/dist/instrumented-execution.js +103 -251
- package/dist/instrumented-execution.js.map +1 -1
- package/dist/jvm-agent/build.d.ts +29 -53
- package/dist/jvm-agent/build.d.ts.map +1 -1
- package/dist/jvm-agent/build.js +34 -61
- package/dist/jvm-agent/build.js.map +1 -1
- package/dist/polling-output-source.d.ts +27 -19
- package/dist/polling-output-source.d.ts.map +1 -1
- package/dist/polling-output-source.js +23 -33
- package/dist/polling-output-source.js.map +1 -1
- package/dist/profile-backend-observation.d.ts +26 -53
- package/dist/profile-backend-observation.d.ts.map +1 -1
- package/dist/profile-backend-observation.js +26 -55
- package/dist/profile-backend-observation.js.map +1 -1
- package/dist/respawn-and-supervise.d.ts +39 -72
- package/dist/respawn-and-supervise.d.ts.map +1 -1
- package/dist/respawn-and-supervise.js +43 -84
- package/dist/respawn-and-supervise.js.map +1 -1
- package/package.json +14 -9
|
@@ -1,57 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Partial going-forward resource restriction for an already-running,
|
|
3
|
-
*
|
|
4
|
-
* next to respawn-and-supervise (`respawn-and-supervise.ts`).
|
|
2
|
+
* Partial going-forward resource restriction for an already-running, attached-to
|
|
3
|
+
* process, via cgroups v2 — narrower alternative to `respawn-and-supervise.ts`.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* the test suite). **It does NOT, and structurally cannot, give the moved
|
|
14
|
-
* process any filesystem or network isolation.** Changing which mount or
|
|
15
|
-
* network namespace an already-running process belongs to needs `setns()`
|
|
16
|
-
* against a live pid -- a fundamentally different, far more invasive
|
|
17
|
-
* operation than a cgroup move, not attempted here and out of scope for
|
|
18
|
-
* this pass. Every value this module returns, and every place this
|
|
19
|
-
* capability is described, says so: `CGROUP_PARTIAL_RESTRICTION_SCOPE`
|
|
20
|
-
* below is the literal, load-bearing string every result carries, precisely
|
|
21
|
-
* so nothing downstream can round it up to "sandboxed" or "isolated" --
|
|
22
|
-
* words that would misrepresent this as equivalent to `sandbox.ts`'s real
|
|
23
|
-
* spawn-time bwrap isolation.
|
|
5
|
+
* Moving a live pid into a delegated cgroup applies real kernel-enforced limits
|
|
6
|
+
* (`memory.max`, `cpu.max`, `pids.max`) without killing/restarting it. **It does
|
|
7
|
+
* NOT and cannot give filesystem or network isolation** — that needs `setns()`
|
|
8
|
+
* against a live pid, out of scope here. `CGROUP_PARTIAL_RESTRICTION_SCOPE` is the
|
|
9
|
+
* literal string every result carries so nothing downstream rounds this up to
|
|
10
|
+
* "sandboxed"/"isolated" (which would misrepresent it as `sandbox.ts`'s real
|
|
11
|
+
* spawn-time bwrap isolation).
|
|
24
12
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* itself runs in) generally cannot have its `subtree_control` extended
|
|
35
|
-
* further -- writing to it fails with `EBUSY`, measured directly against
|
|
36
|
-
* this repo's own dev host while building this module. `findDelegatedCgroupAncestor`
|
|
37
|
-
* therefore does not trust `cgroup.subtree_control` text or permission
|
|
38
|
-
* bits alone; it walks up from this process's own `/proc/self/cgroup` path
|
|
39
|
-
* and, at each ancestor, creates a real, temporary probe child directory
|
|
40
|
-
* and checks whether the kernel actually gave it a `memory.max` file --
|
|
41
|
-
* the only way to honestly know a given ancestor's delegation reaches that
|
|
42
|
-
* far, empirically, the same "prove it against the real mechanism" standard
|
|
43
|
-
* `sandbox.ts`'s own escape tests hold spawn-mode isolation to.
|
|
13
|
+
* Finding the delegated ancestor: a non-root user can only write under
|
|
14
|
+
* `/sys/fs/cgroup` where a systemd user session delegates it (`Delegate=yes`).
|
|
15
|
+
* Delegation isn't uniform all the way down — a leaf cgroup already holding a
|
|
16
|
+
* process (e.g. this Node process's own scope) generally can't extend
|
|
17
|
+
* `subtree_control` further; writing fails `EBUSY` (measured on this repo's dev
|
|
18
|
+
* host). So `findDelegatedCgroupAncestor` doesn't trust subtree_control text or
|
|
19
|
+
* permission bits — it walks up from `/proc/self/cgroup` and, at each ancestor,
|
|
20
|
+
* creates a real temporary probe child and checks whether the kernel actually gave
|
|
21
|
+
* it a `memory.max` file.
|
|
44
22
|
*/
|
|
45
23
|
import { existsSync, mkdirSync, readFileSync, rmdirSync, writeFileSync } from "node:fs";
|
|
46
24
|
import { dirname, join } from "node:path";
|
|
47
25
|
import { isProcessAlive } from "./attach-to-running-process.js";
|
|
48
26
|
const CGROUP_ROOT = "/sys/fs/cgroup";
|
|
49
|
-
/**
|
|
50
|
-
* The literal, load-bearing scope string. Every `CgroupMoveResult` carries
|
|
51
|
-
* this exact value under `scope` -- never restated by hand elsewhere, so a
|
|
52
|
-
* caller (or a future capability-report string, or a decisions-inbox entry)
|
|
53
|
-
* that quotes `.scope` can never accidentally drift from this wording.
|
|
54
|
-
*/
|
|
27
|
+
/** Literal scope string every `CgroupMoveResult` carries under `scope` — never restated by hand, so quotes of it can't drift. */
|
|
55
28
|
export const CGROUP_PARTIAL_RESTRICTION_SCOPE = "partial -- resource limits only, no filesystem or network isolation";
|
|
56
29
|
function ownUnifiedCgroupPath() {
|
|
57
30
|
let content;
|
|
@@ -62,10 +35,7 @@ function ownUnifiedCgroupPath() {
|
|
|
62
35
|
return null;
|
|
63
36
|
}
|
|
64
37
|
for (const line of content.trim().split("\n")) {
|
|
65
|
-
//
|
|
66
|
-
// no named controllers (that's a v1 concept), then the path. A host
|
|
67
|
-
// still running v1 controllers alongside v2 has other numbered lines
|
|
68
|
-
// here too; only the "0::" one is this module's concern.
|
|
38
|
+
// unified-hierarchy lines read "0::<path>"; only the "0::" line matters here
|
|
69
39
|
const firstColon = line.indexOf(":");
|
|
70
40
|
const secondColon = line.indexOf(":", firstColon + 1);
|
|
71
41
|
if (firstColon === -1 || secondColon === -1)
|
|
@@ -77,13 +47,10 @@ function ownUnifiedCgroupPath() {
|
|
|
77
47
|
return null;
|
|
78
48
|
}
|
|
79
49
|
/**
|
|
80
|
-
* Real
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* leaf scope is exactly this shape, measured directly). The only honest
|
|
85
|
-
* test is creating a real probe child and checking what the kernel actually
|
|
86
|
-
* gave it.
|
|
50
|
+
* Real empirical probe, not a permission-bit read — a directory can be
|
|
51
|
+
* `mkdir`-able yet its child still lack `memory.max` if the controller was never
|
|
52
|
+
* enabled in `subtree_control` (this process's own leaf scope is exactly this
|
|
53
|
+
* shape, measured directly).
|
|
87
54
|
*/
|
|
88
55
|
function probeControllableChild(candidateDir) {
|
|
89
56
|
const probe = join(candidateDir, `.descry-cgroup-probe-${String(process.pid)}-${String(Date.now())}-${String(Math.random()).slice(2, 8)}`);
|
|
@@ -98,18 +65,15 @@ function probeControllableChild(candidateDir) {
|
|
|
98
65
|
rmdirSync(probe);
|
|
99
66
|
}
|
|
100
67
|
catch {
|
|
101
|
-
//
|
|
102
|
-
// this module's problem if something else raced to touch it first.
|
|
68
|
+
// best-effort cleanup; not our problem if something raced to touch it first
|
|
103
69
|
}
|
|
104
70
|
return controllable;
|
|
105
71
|
}
|
|
106
72
|
/**
|
|
107
|
-
* Walks up from this process's
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* mounted, this process's own cgroup path cannot be determined, or no
|
|
112
|
-
* ancestor all the way to the root passes the probe (no delegation at all).
|
|
73
|
+
* Walks up from this process's cgroup toward `/sys/fs/cgroup`, returning the
|
|
74
|
+
* deepest ancestor whose children genuinely get a real `memory.max` — the actual
|
|
75
|
+
* delegation boundary. `null` if cgroups v2 isn't mounted, own path can't be
|
|
76
|
+
* determined, or no ancestor passes the probe.
|
|
113
77
|
*/
|
|
114
78
|
export function findDelegatedCgroupAncestor() {
|
|
115
79
|
if (process.platform !== "linux")
|
|
@@ -120,10 +84,7 @@ export function findDelegatedCgroupAncestor() {
|
|
|
120
84
|
if (ownPath === null)
|
|
121
85
|
return null;
|
|
122
86
|
let dir = join(CGROUP_ROOT, ownPath.replace(/^\/+/, ""));
|
|
123
|
-
//
|
|
124
|
-
// limits at the true root would apply host-wide and is root-only on
|
|
125
|
-
// every real system anyway, so it is deliberately excluded rather than
|
|
126
|
-
// reached as a fallback.
|
|
87
|
+
// never test CGROUP_ROOT itself: root-wide limits are root-only anyway
|
|
127
88
|
while (dir !== CGROUP_ROOT && dirname(dir) !== dir) {
|
|
128
89
|
if (existsSync(dir) && probeControllableChild(dir))
|
|
129
90
|
return dir;
|
|
@@ -150,16 +111,13 @@ export function cgroupV2Capability() {
|
|
|
150
111
|
return { availability: "available", reason: null };
|
|
151
112
|
}
|
|
152
113
|
/**
|
|
153
|
-
* Moves `options.pid`
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* reports unavailable, or when a declared limit's controller file does not
|
|
157
|
-
* actually exist under the delegated ancestor found for it.
|
|
114
|
+
* Moves `options.pid` (a process this runtime did not spawn) into a newly created,
|
|
115
|
+
* resource-limited cgroup. Throws if the target isn't alive, `cgroupV2Capability()`
|
|
116
|
+
* is unavailable, or a declared limit's controller file doesn't exist.
|
|
158
117
|
*
|
|
159
|
-
* Limits are written
|
|
160
|
-
* a
|
|
161
|
-
*
|
|
162
|
-
* the error is rethrown rather than left as an orphaned, unlimited group.
|
|
118
|
+
* Limits are written before the pid is moved in, so a process is never observed
|
|
119
|
+
* with a half-applied limit set; on any write failure the still-empty cgroup is
|
|
120
|
+
* removed and the error rethrown, never left orphaned.
|
|
163
121
|
*/
|
|
164
122
|
export function moveProcessIntoRestrictedCgroup(options) {
|
|
165
123
|
if (!isProcessAlive(options.pid)) {
|
|
@@ -171,8 +129,7 @@ export function moveProcessIntoRestrictedCgroup(options) {
|
|
|
171
129
|
}
|
|
172
130
|
const ancestor = findDelegatedCgroupAncestor();
|
|
173
131
|
if (ancestor === null) {
|
|
174
|
-
//
|
|
175
|
-
// narrows the type honestly rather than a non-null assertion.
|
|
132
|
+
// unreachable given availability check above; narrows the type honestly
|
|
176
133
|
throw new Error("moveProcessIntoRestrictedCgroup: no delegated cgroup ancestor found despite capability reporting available -- internal inconsistency.");
|
|
177
134
|
}
|
|
178
135
|
const cgroupPath = join(ancestor, `descry-restrict-${options.nameHint ?? String(options.pid)}-${String(Date.now())}`);
|
|
@@ -184,17 +141,14 @@ export function moveProcessIntoRestrictedCgroup(options) {
|
|
|
184
141
|
}
|
|
185
142
|
if (options.limits.memoryMaxBytes !== undefined) {
|
|
186
143
|
writeFileSync(join(cgroupPath, "memory.max"), String(options.limits.memoryMaxBytes));
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
// no memory.swap.max file at all (swap accounting compiled out, or no
|
|
191
|
-
// swap controller delegated) still gets a real memory.max, just
|
|
192
|
-
// without this hardening; not a reason to refuse the whole call.
|
|
144
|
+
// see memoryMaxBytes doc: without this, swap makes memory.max a soft limit.
|
|
145
|
+
// best-effort -- no memory.swap.max file (swap accounting disabled/not
|
|
146
|
+
// delegated) still gets a real memory.max, just without this hardening
|
|
193
147
|
try {
|
|
194
148
|
writeFileSync(join(cgroupPath, "memory.swap.max"), "0");
|
|
195
149
|
}
|
|
196
150
|
catch {
|
|
197
|
-
//
|
|
151
|
+
// disclosed above, not pretended to have succeeded
|
|
198
152
|
}
|
|
199
153
|
}
|
|
200
154
|
if (options.limits.maxProcesses !== undefined) {
|
|
@@ -203,7 +157,7 @@ export function moveProcessIntoRestrictedCgroup(options) {
|
|
|
203
157
|
if (options.limits.cpuMax !== undefined) {
|
|
204
158
|
writeFileSync(join(cgroupPath, "cpu.max"), options.limits.cpuMax);
|
|
205
159
|
}
|
|
206
|
-
//
|
|
160
|
+
// moved last, only once every requested limit is confirmed written
|
|
207
161
|
writeFileSync(join(cgroupPath, "cgroup.procs"), String(options.pid));
|
|
208
162
|
}
|
|
209
163
|
catch (error) {
|
|
@@ -211,9 +165,8 @@ export function moveProcessIntoRestrictedCgroup(options) {
|
|
|
211
165
|
rmdirSync(cgroupPath);
|
|
212
166
|
}
|
|
213
167
|
catch {
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
// catch can honestly do about that.
|
|
168
|
+
// best-effort: if the pid was already moved in, the dir is non-empty
|
|
169
|
+
// and can't be removed
|
|
217
170
|
}
|
|
218
171
|
throw new Error(`moveProcessIntoRestrictedCgroup: failed to apply limits or move pid ${String(options.pid)} into ${cgroupPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
219
172
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cgroup-partial-restriction.js","sourceRoot":"","sources":["../src/cgroup-partial-restriction.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cgroup-partial-restriction.js","sourceRoot":"","sources":["../src/cgroup-partial-restriction.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `Evidence.collectorVersion`
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* to): this package's own `package.json` "version", read from the artifact
|
|
6
|
-
* itself rather than hand-duplicated into a second string that can drift
|
|
7
|
-
* from what actually shipped. Same idea as `nodeVersion` elsewhere in this
|
|
8
|
-
* repo (`process.version`, always known, never probed) applied to a
|
|
9
|
-
* package instead of the Node runtime.
|
|
2
|
+
* `Evidence.collectorVersion` for orchestrator-emitted evidence (e.g. `COLLECTOR_ERROR`
|
|
3
|
+
* before any collector ran). Reads `package.json` directly so it can't drift from
|
|
4
|
+
* what shipped, same approach as `nodeVersion`'s use of `process.version`.
|
|
10
5
|
*/
|
|
11
6
|
export declare const COLLECTOR_VERSION: string;
|
|
12
7
|
//# sourceMappingURL=collector-version.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector-version.d.ts","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"collector-version.d.ts","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,eAAO,MAAM,iBAAiB,EAAE,MAA6E,CAAC"}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `Evidence.collectorVersion`
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* to): this package's own `package.json` "version", read from the artifact
|
|
6
|
-
* itself rather than hand-duplicated into a second string that can drift
|
|
7
|
-
* from what actually shipped. Same idea as `nodeVersion` elsewhere in this
|
|
8
|
-
* repo (`process.version`, always known, never probed) applied to a
|
|
9
|
-
* package instead of the Node runtime.
|
|
2
|
+
* `Evidence.collectorVersion` for orchestrator-emitted evidence (e.g. `COLLECTOR_ERROR`
|
|
3
|
+
* before any collector ran). Reads `package.json` directly so it can't drift from
|
|
4
|
+
* what shipped, same approach as `nodeVersion`'s use of `process.version`.
|
|
10
5
|
*/
|
|
11
6
|
import { createRequire } from "node:module";
|
|
12
7
|
const require = createRequire(import.meta.url);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector-version.js","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"collector-version.js","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,iBAAiB,GAAY,OAAO,CAAC,iBAAiB,CAAkC,CAAC,OAAO,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { PollingProcessOutputSourceOptions } from "./polling-output-source.ts";
|
|
2
2
|
export { createPollingProcessOutputSource } from "./polling-output-source.ts";
|
|
3
|
-
export type { AttachToRunningProcessOptions } from "./attach-to-running-process.ts";
|
|
3
|
+
export type { AttachToRunningProcessOptions, AttachedProcessOutputSource } from "./attach-to-running-process.ts";
|
|
4
4
|
export { attachToRunningProcess, isProcessAlive } from "./attach-to-running-process.ts";
|
|
5
5
|
export type { AttachToRunningNodeProcessOptions } from "./attach-to-running-node-process.ts";
|
|
6
6
|
export { attachToRunningNodeProcess, discoverNodeInspectorUrl } from "./attach-to-running-node-process.ts";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EAAE,gCAAgC,EAAE,MAAM,4BAA4B,CAAC;AAC9E,YAAY,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EAAE,gCAAgC,EAAE,MAAM,4BAA4B,CAAC;AAC9E,YAAY,EAAE,6BAA6B,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AACjH,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACxF,YAAY,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AAC7F,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC3G,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AAC3F,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAC9F,YAAY,EACV,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,gCAAgC,EAChC,kBAAkB,EAClB,2BAA2B,EAC3B,+BAA+B,GAChC,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACnI,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACtH,YAAY,EACV,2BAA2B,EAC3B,4BAA4B,EAC5B,6BAA6B,GAC9B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC"}
|
|
@@ -1,30 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The assembly wiring the runtime's pieces together in production code (previously
|
|
3
|
+
* only connected ad hoc by tests). Runs an `ExecutionController` over a `services`
|
|
4
|
+
* configuration; wraps each spawned process in a live `ProcessOutputSource`
|
|
5
|
+
* (polls the buffer, yields each line once); builds a `CollectorContext` with a
|
|
6
|
+
* real `resolveSourceRoot` closed over the execution's own processes; starts,
|
|
7
|
+
* drains and stops the adapter's collectors; writes every emitted item through
|
|
8
|
+
* `EvidenceStore` so redaction runs (plan §28).
|
|
3
9
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* could build collectors, `resolveServiceRootForOrigin` could map an origin
|
|
7
|
-
* to a source root, and `EvidenceStore` could persist. Only tests ever
|
|
8
|
-
* connected them, each in its own way, which meant the thing the product
|
|
9
|
-
* actually does had no single implementation to be right or wrong.
|
|
10
|
-
*
|
|
11
|
-
* This module is that implementation. It:
|
|
12
|
-
*
|
|
13
|
-
* 1. runs an `ExecutionController` over a `services` configuration;
|
|
14
|
-
* 2. wraps each spawned process in a **live** `ProcessOutputSource` that
|
|
15
|
-
* polls the accumulated buffer and yields each line exactly once;
|
|
16
|
-
* 3. builds a `CollectorContext` carrying a real `resolveSourceRoot`,
|
|
17
|
-
* closed over the execution's own processes — the channel RT-033 added
|
|
18
|
-
* and RT-029 built the lookup for, which until now nothing populated;
|
|
19
|
-
* 4. starts the adapter's collectors, drains them, stops them;
|
|
20
|
-
* 5. writes every emitted item through `EvidenceStore`, so redaction runs
|
|
21
|
-
* rather than being assumed (plan §28).
|
|
22
|
-
*
|
|
23
|
-
* **What it deliberately does not do.** It does not decide whether a
|
|
24
|
-
* finding exists, does not correlate to graph nodes, and does not drive a
|
|
25
|
-
* browser. Those are separate stages with separate contracts, and folding
|
|
26
|
-
* them in here would make the one place that assembles the runtime also the
|
|
27
|
-
* place that judges it.
|
|
10
|
+
* Deliberately does not decide whether a finding exists, correlate to graph
|
|
11
|
+
* nodes, or drive a browser — separate stages, separate contracts.
|
|
28
12
|
*/
|
|
29
13
|
import type { Evidence, Execution, ExecutionConfiguration, ProcessHandle, ServiceRootLookup } from "@descryy/runtime-contracts";
|
|
30
14
|
import type { RuntimeAdapter, OutboundHttpLaunch } from "@descryy/runtime-backend-observation";
|
|
@@ -46,38 +30,23 @@ export interface InstrumentedExecutionInput {
|
|
|
46
30
|
*/
|
|
47
31
|
readonly observeForMs: number;
|
|
48
32
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* produced (`ExecutionController.serviceStartResults`), the same
|
|
54
|
-
* "readiness confirmed on the app's real port FIRST, proxy started AFTER"
|
|
55
|
-
* sequencing `cross-service-trace.test.ts` performs by hand.
|
|
33
|
+
* Opt-in, per service name: front that service with `InboundProxy` (RT-192,
|
|
34
|
+
* DEC-271) once its own readiness has already succeeded — reads the answer
|
|
35
|
+
* `controller.run()` already produced (`serviceStartResults`), never re-asks.
|
|
36
|
+
* Absent or unnamed service: unchanged behaviour, no proxy.
|
|
56
37
|
*
|
|
57
|
-
*
|
|
58
|
-
* no
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* **This does not close any per-language "HTTP observation" checklist
|
|
62
|
-
* row.** Every one of those rows uses a self-initiated fixture — the app
|
|
63
|
-
* calls itself via the exact `PORT` it bound — which has no separate
|
|
64
|
-
* public port for a proxy to sit in front of. This flag makes
|
|
65
|
-
* `InboundProxy` available for a *future externally-driven* scenario
|
|
66
|
-
* (the shape RT-196/`cross-service-trace.test.ts` already proved by
|
|
67
|
-
* hand); it does not retroactively make any current fixture
|
|
68
|
-
* externally-driven.
|
|
38
|
+
* Does not close any per-language "HTTP observation" checklist row — those use
|
|
39
|
+
* self-initiated fixtures with no separate public port for a proxy to front.
|
|
40
|
+
* This only makes `InboundProxy` available for a future externally-driven
|
|
41
|
+
* scenario.
|
|
69
42
|
*/
|
|
70
43
|
readonly frontWithInboundProxy?: Readonly<Record<string, boolean>>;
|
|
71
44
|
/**
|
|
72
|
-
* Fires once every collector
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* is only returned after the whole run has already stopped every
|
|
78
|
-
* process, which is too late for that. A throwing callback is not caught
|
|
79
|
-
* here, matching `RunOptions.onProcessLifecycleEvent`'s own "must not
|
|
80
|
-
* hang, may not silently swallow its own failure" rule.
|
|
45
|
+
* Fires once every collector (including any fronting proxy) has started, before
|
|
46
|
+
* the observation window's sleep. Lets a caller drive real traffic through a
|
|
47
|
+
* just-started proxy while it's still alive — `InstrumentedExecutionResult` is
|
|
48
|
+
* only returned after the run has stopped everything, too late for that. A
|
|
49
|
+
* throwing callback is not caught here.
|
|
81
50
|
*/
|
|
82
51
|
readonly onReady?: (info: {
|
|
83
52
|
readonly execution: Execution;
|
|
@@ -98,19 +67,11 @@ export interface InstrumentedExecutionResult {
|
|
|
98
67
|
/**
|
|
99
68
|
* What the declared whole-execution budget actually did to this run.
|
|
100
69
|
*
|
|
101
|
-
* `
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* theoretical — a 1500ms budget with a 12000ms window returned after
|
|
107
|
-
* 12288ms with zero evidence, because the services had been dead for the
|
|
108
|
-
* last 10.8s of a window nothing shortened.
|
|
109
|
-
*
|
|
110
|
-
* Reported as a struct rather than left implicit in the elapsed time so a
|
|
111
|
-
* caller can say *which* of the two it got: a full window that found
|
|
112
|
-
* nothing, or a window cut short before it could. Those are the same two
|
|
113
|
-
* claims the five report categories exist to keep apart, one layer down.
|
|
70
|
+
* `timeoutMs` used to bound only the service, not the observation window: a
|
|
71
|
+
* 1500ms budget with a 12000ms window measurably returned after 12288ms with
|
|
72
|
+
* zero evidence, since services had been dead for the last 10.8s. Reported as a
|
|
73
|
+
* struct so a caller can tell a full window that found nothing from one cut
|
|
74
|
+
* short before it could.
|
|
114
75
|
*/
|
|
115
76
|
readonly budget: ExecutionBudgetOutcome;
|
|
116
77
|
}
|
|
@@ -129,13 +90,9 @@ export interface ExecutionBudgetOutcome {
|
|
|
129
90
|
}
|
|
130
91
|
/**
|
|
131
92
|
* Builds the origin → source-root lookup a collector reads through
|
|
132
|
-
* `CollectorContext.resolveSourceRoot`.
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* lookup and nothing called it; RT-033 added the channel and nothing filled
|
|
136
|
-
* it. It closes over the execution's *own* processes, which is what makes
|
|
137
|
-
* the port→service join possible at all — under ephemeral ports the
|
|
138
|
-
* configuration cannot know the port, so only the running process can say.
|
|
93
|
+
* `CollectorContext.resolveSourceRoot`. Closes over the execution's own
|
|
94
|
+
* processes, which is what makes the port→service join possible at all — under
|
|
95
|
+
* ephemeral ports only the running process knows its own port.
|
|
139
96
|
*/
|
|
140
97
|
export declare function createSourceRootResolver(processes: readonly ProcessHandle[], configuration: ExecutionConfiguration): (origin: string) => ServiceRootLookup;
|
|
141
98
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumented-execution.d.ts","sourceRoot":"","sources":["../src/instrumented-execution.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"instrumented-execution.d.ts","sourceRoot":"","sources":["../src/instrumented-execution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAEV,QAAQ,EACR,SAAS,EACT,sBAAsB,EACtB,aAAa,EACb,iBAAiB,EAClB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,cAAc,EAAuB,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAGpH,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,UAAU,EAGhB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAKhE,MAAM,WAAW,0BAA0B;IACzC,oIAAoI;IACpI,QAAQ,CAAC,SAAS,EAAE,oBAAoB,CAAC;IACzC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,8FAA8F;IAC9F,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,qIAAqI;IACrI,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnE;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;QAC9B,mHAAmH;QACnH,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;KACvD,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,QAAQ,EAAE,CAAC;IACvC,6GAA6G;IAC7G,QAAQ,CAAC,qBAAqB,EAAE,SAAS;QAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,4BAA4B,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC,CAAA;KAAE,EAAE,CAAC;IAC/K,8HAA8H;IAC9H,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;CACzC;AAED,gDAAgD;AAChD,MAAM,WAAW,sBAAsB;IACrC,oDAAoD;IACpD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qCAAqC;IACrC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,sGAAsG;IACtG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,yHAAyH;IACzH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,SAAS,aAAa,EAAE,EACnC,aAAa,EAAE,sBAAsB,GACpC,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAEvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,oBAAoB,EAC/B,MAAM,EAAE,kBAAkB,GACzB,oBAAoB,CAwBtB;AAED,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,2BAA2B,CAAC,CA8etH"}
|