@descryy/runtime-orchestrator 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/attach-to-running-jvm-process.d.ts +139 -0
  2. package/dist/attach-to-running-jvm-process.d.ts.map +1 -0
  3. package/dist/attach-to-running-jvm-process.js +338 -0
  4. package/dist/attach-to-running-jvm-process.js.map +1 -0
  5. package/dist/attach-to-running-node-process.d.ts +138 -0
  6. package/dist/attach-to-running-node-process.d.ts.map +1 -0
  7. package/dist/attach-to-running-node-process.js +315 -0
  8. package/dist/attach-to-running-node-process.js.map +1 -0
  9. package/dist/attach-to-running-process.d.ts +70 -0
  10. package/dist/attach-to-running-process.d.ts.map +1 -0
  11. package/dist/attach-to-running-process.js +90 -0
  12. package/dist/attach-to-running-process.js.map +1 -0
  13. package/dist/cdp-client.d.ts +37 -0
  14. package/dist/cdp-client.d.ts.map +1 -0
  15. package/dist/cdp-client.js +111 -0
  16. package/dist/cdp-client.js.map +1 -0
  17. package/dist/cgroup-partial-restriction.d.ts +127 -0
  18. package/dist/cgroup-partial-restriction.d.ts.map +1 -0
  19. package/dist/cgroup-partial-restriction.js +236 -0
  20. package/dist/cgroup-partial-restriction.js.map +1 -0
  21. package/dist/collector-version.d.ts +12 -0
  22. package/dist/collector-version.d.ts.map +1 -0
  23. package/dist/collector-version.js +14 -0
  24. package/dist/collector-version.js.map +1 -0
  25. package/dist/index.d.ts +19 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +10 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/instrumented-execution.d.ts +111 -0
  30. package/dist/instrumented-execution.d.ts.map +1 -0
  31. package/dist/instrumented-execution.js +363 -0
  32. package/dist/instrumented-execution.js.map +1 -0
  33. package/dist/jvm-agent/build.d.ts +72 -0
  34. package/dist/jvm-agent/build.d.ts.map +1 -0
  35. package/dist/jvm-agent/build.js +156 -0
  36. package/dist/jvm-agent/build.js.map +1 -0
  37. package/dist/polling-output-source.d.ts +28 -0
  38. package/dist/polling-output-source.d.ts.map +1 -0
  39. package/dist/polling-output-source.js +73 -0
  40. package/dist/polling-output-source.js.map +1 -0
  41. package/dist/profile-backend-observation.d.ts +94 -0
  42. package/dist/profile-backend-observation.d.ts.map +1 -0
  43. package/dist/profile-backend-observation.js +136 -0
  44. package/dist/profile-backend-observation.js.map +1 -0
  45. package/dist/respawn-and-supervise.d.ts +119 -0
  46. package/dist/respawn-and-supervise.d.ts.map +1 -0
  47. package/dist/respawn-and-supervise.js +212 -0
  48. package/dist/respawn-and-supervise.js.map +1 -0
  49. package/package.json +45 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-client.js","sourceRoot":"","sources":["../src/cdp-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAgBH;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,YAAoB;IAC7C,OAAO,IAAI,OAAO,CAAC,CAAC,cAAc,EAAE,aAAa,EAAE,EAAE;QACnD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,YAAY,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;QAChD,MAAM,SAAS,GAAG,IAAI,GAAG,EAA0D,CAAC;QACpF,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,SAAS,cAAc,CAAC,KAAY;YAClC,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,OAAO,CAAC,MAAM,EAAE;gBAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YACzD,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC;QAED,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE;YACnC,OAAO,GAAG,IAAI,CAAC;YACf,cAAc,CAAC;gBACb,IAAI,CAAI,MAAc,EAAE,SAAkC,EAAE;oBAC1D,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;oBACpB,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;wBACxC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,OAAmC,EAAE,MAAM,EAAE,CAAC,CAAC;wBAC1E,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;oBACtD,CAAC,CAAC,CAAC;gBACL,CAAC;gBACD,EAAE,CAAC,MAAc,EAAE,OAAkD;oBACnE,IAAI,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;oBAChC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;wBACtB,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;wBAChB,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;oBAC7B,CAAC;oBACD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBACjB,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACnC,CAAC;gBACD,KAAK;oBACH,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,CAAC;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAmB,EAAE,EAAE;YACzD,MAAM,GAAG,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7E,IAAI,GAAsH,CAAC;YAC3H,IAAI,CAAC;gBACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;YACtC,CAAC;YAAC,MAAM,CAAC;gBACP,iEAAiE;gBACjE,kEAAkE;gBAClE,wDAAwD;gBACxD,2DAA2D;gBAC3D,OAAO;YACT,CAAC;YAED,IAAI,GAAG,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACpC,IAAI,OAAO,KAAK,SAAS;oBAAE,OAAO;gBAClC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACvB,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;oBAC5B,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,yBAAyB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBAC7F,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC9B,CAAC;gBACD,OAAO;YACT,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACtC,IAAI,GAAG,KAAK,SAAS;oBAAE,OAAO;gBAC9B,KAAK,MAAM,OAAO,IAAI,GAAG;oBAAE,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;YACpC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,qCAAqC,YAAY,EAAE,CAAC,CAAC;YAC7E,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;gBACf,aAAa,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;YACD,cAAc,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;YACpC,cAAc,CAAC,IAAI,KAAK,CAAC,oBAAoB,YAAY,SAAS,CAAC,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Partial going-forward resource restriction for an already-running,
3
+ * attached-to process, via cgroups v2 -- the second, narrower alternative
4
+ * next to respawn-and-supervise (`respawn-and-supervise.ts`).
5
+ *
6
+ * **What this genuinely gets you, stated exactly, because the honest
7
+ * boundary here is not the same shape as spawn-mode's.** Moving a live
8
+ * pid's cgroup membership (writing it into a delegated cgroup's own
9
+ * `cgroup.procs`, per the kernel's cgroups v2 unified hierarchy) applies
10
+ * real, kernel-enforced resource limits -- `memory.max`, `cpu.max`,
11
+ * `pids.max` -- to a process this runtime never spawned, without killing
12
+ * or restarting it. That is real and this module proves it for real (see
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.
24
+ *
25
+ * **How the delegated cgroup is found -- measured on this host, not
26
+ * assumed from cgroups v2 documentation.** A non-root user cannot write
27
+ * anywhere under `/sys/fs/cgroup` by default; what makes any of this
28
+ * possible at all is a systemd user session's own delegation
29
+ * (`user@<uid>.service`, `Delegate=yes`), which typically extends through
30
+ * `app.slice`/`session.slice`/`background.slice` beneath it. But
31
+ * delegation is not uniform all the way down: cgroups v2's "no internal
32
+ * processes" rule means a *leaf* cgroup that already directly holds a
33
+ * process (a systemd `.scope`, including the very one this Node process
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.
44
+ */
45
+ import type { CapabilityStatus } from "@descryy/runtime-contracts";
46
+ /**
47
+ * The literal, load-bearing scope string. Every `CgroupMoveResult` carries
48
+ * this exact value under `scope` -- never restated by hand elsewhere, so a
49
+ * caller (or a future capability-report string, or a decisions-inbox entry)
50
+ * that quotes `.scope` can never accidentally drift from this wording.
51
+ */
52
+ export declare const CGROUP_PARTIAL_RESTRICTION_SCOPE: "partial -- resource limits only, no filesystem or network isolation";
53
+ /**
54
+ * Walks up from this process's own cgroup toward `/sys/fs/cgroup`, returning
55
+ * the deepest (most specific) ancestor directory whose children genuinely
56
+ * get a real `memory.max` -- i.e. the actual delegation boundary, not
57
+ * merely a directory this uid happens to own. `null` when cgroups v2 is not
58
+ * mounted, this process's own cgroup path cannot be determined, or no
59
+ * ancestor all the way to the root passes the probe (no delegation at all).
60
+ */
61
+ export declare function findDelegatedCgroupAncestor(): string | null;
62
+ export declare function cgroupV2Capability(): CapabilityStatus;
63
+ export interface CgroupResourceLimits {
64
+ /**
65
+ * Maps to cgroups v2 `memory.max`, in bytes. **Also writes `memory.swap.max=0`
66
+ * for the same cgroup, deliberately, when this is set** -- measured directly
67
+ * while building this module: with swap available on the host (the common
68
+ * case), `memory.max` alone does not kill anything, it just makes the
69
+ * kernel push the cgroup's pages to swap under pressure, so a caller
70
+ * declaring a memory cap would silently get "slows down and swaps"
71
+ * instead of the real, hard boundary the field name promises. Disabling
72
+ * swap for the created cgroup is what makes `memory.max` an actual
73
+ * enforced limit (the kernel's own cgroup-aware OOM killer terminates a
74
+ * process here, SIGKILL, once reclaim genuinely cannot bring usage under
75
+ * the cap) rather than a soft one. Not independently toggleable in this
76
+ * pass -- if a future caller genuinely wants "cap it but let it swap,"
77
+ * that is a real, separate field to add later, not silently assumed here.
78
+ */
79
+ readonly memoryMaxBytes?: number;
80
+ /** Maps to cgroups v2 `pids.max` -- caps the number of tasks (processes/threads) this cgroup may hold at once. */
81
+ readonly maxProcesses?: number;
82
+ /**
83
+ * Raw cgroups v2 `cpu.max` value (`"$MAX $PERIOD"`, e.g. `"50000 100000"`
84
+ * for 50% of one CPU). Declaring this when the delegated ancestor does
85
+ * not actually expose a writable `cpu.max` to its children is a refusal
86
+ * (throws), not a silently-dropped limit -- matching `applySandbox`'s own
87
+ * "declared, refused rather than silently ignored if unenforceable"
88
+ * precedent. Measured on this repo's own dev host: `cpu` was NOT
89
+ * delegated at the same level `memory`/`pids` were, so this is a real,
90
+ * not hypothetical, gap a caller can hit.
91
+ */
92
+ readonly cpuMax?: string;
93
+ }
94
+ export interface CgroupMoveResult {
95
+ readonly applied: true;
96
+ readonly cgroupPath: string;
97
+ /** Always the literal `CGROUP_PARTIAL_RESTRICTION_SCOPE` string -- see its own doc for why this is never restated by hand. */
98
+ readonly scope: typeof CGROUP_PARTIAL_RESTRICTION_SCOPE;
99
+ readonly appliedLimits: CgroupResourceLimits;
100
+ /**
101
+ * Best-effort removal of the cgroup this call created. Succeeds only once
102
+ * `cgroup.procs` is empty -- the pid exited, or was itself moved
103
+ * elsewhere -- because cgroups v2 refuses to remove a non-empty cgroup,
104
+ * the same "no internal processes hiding a live one" rule
105
+ * `findDelegatedCgroupAncestor`'s own probe relies on. Returns whether
106
+ * removal actually happened; never throws.
107
+ */
108
+ readonly release: () => boolean;
109
+ }
110
+ /**
111
+ * Moves `options.pid` -- an already-running process this runtime did not
112
+ * spawn -- into a newly created, resource-limited cgroup. Throws (does not
113
+ * silently proceed) when the target is not alive, when `cgroupV2Capability()`
114
+ * reports unavailable, or when a declared limit's controller file does not
115
+ * actually exist under the delegated ancestor found for it.
116
+ *
117
+ * Limits are written to the new cgroup BEFORE the pid is moved into it, so
118
+ * a process is never observed inside a cgroup with a half-applied limit
119
+ * set; if any write fails, the created (still-empty) cgroup is removed and
120
+ * the error is rethrown rather than left as an orphaned, unlimited group.
121
+ */
122
+ export declare function moveProcessIntoRestrictedCgroup(options: {
123
+ readonly pid: number;
124
+ readonly limits: CgroupResourceLimits;
125
+ readonly nameHint?: string;
126
+ }): CgroupMoveResult;
127
+ //# sourceMappingURL=cgroup-partial-restriction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cgroup-partial-restriction.d.ts","sourceRoot":"","sources":["../src/cgroup-partial-restriction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAKnE;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,EAAG,qEAA8E,CAAC;AAkD/H;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,GAAG,IAAI,CAiB3D;AAED,wBAAgB,kBAAkB,IAAI,gBAAgB,CAkBrD;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,kHAAkH;IAClH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8HAA8H;IAC9H,QAAQ,CAAC,KAAK,EAAE,OAAO,gCAAgC,CAAC;IACxD,QAAQ,CAAC,aAAa,EAAE,oBAAoB,CAAC;IAC7C;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC;CACjC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE;IACvD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,gBAAgB,CA2EnB"}
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Partial going-forward resource restriction for an already-running,
3
+ * attached-to process, via cgroups v2 -- the second, narrower alternative
4
+ * next to respawn-and-supervise (`respawn-and-supervise.ts`).
5
+ *
6
+ * **What this genuinely gets you, stated exactly, because the honest
7
+ * boundary here is not the same shape as spawn-mode's.** Moving a live
8
+ * pid's cgroup membership (writing it into a delegated cgroup's own
9
+ * `cgroup.procs`, per the kernel's cgroups v2 unified hierarchy) applies
10
+ * real, kernel-enforced resource limits -- `memory.max`, `cpu.max`,
11
+ * `pids.max` -- to a process this runtime never spawned, without killing
12
+ * or restarting it. That is real and this module proves it for real (see
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.
24
+ *
25
+ * **How the delegated cgroup is found -- measured on this host, not
26
+ * assumed from cgroups v2 documentation.** A non-root user cannot write
27
+ * anywhere under `/sys/fs/cgroup` by default; what makes any of this
28
+ * possible at all is a systemd user session's own delegation
29
+ * (`user@<uid>.service`, `Delegate=yes`), which typically extends through
30
+ * `app.slice`/`session.slice`/`background.slice` beneath it. But
31
+ * delegation is not uniform all the way down: cgroups v2's "no internal
32
+ * processes" rule means a *leaf* cgroup that already directly holds a
33
+ * process (a systemd `.scope`, including the very one this Node process
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.
44
+ */
45
+ import { existsSync, mkdirSync, readFileSync, rmdirSync, writeFileSync } from "node:fs";
46
+ import { dirname, join } from "node:path";
47
+ import { isProcessAlive } from "./attach-to-running-process.js";
48
+ 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
+ */
55
+ export const CGROUP_PARTIAL_RESTRICTION_SCOPE = "partial -- resource limits only, no filesystem or network isolation";
56
+ function ownUnifiedCgroupPath() {
57
+ let content;
58
+ try {
59
+ content = readFileSync("/proc/self/cgroup", "utf8");
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ for (const line of content.trim().split("\n")) {
65
+ // cgroups v2 unified-hierarchy lines read "0::<path>" -- hierarchy id 0,
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.
69
+ const firstColon = line.indexOf(":");
70
+ const secondColon = line.indexOf(":", firstColon + 1);
71
+ if (firstColon === -1 || secondColon === -1)
72
+ continue;
73
+ if (line.slice(0, firstColon) === "0" && firstColon + 1 === secondColon) {
74
+ return line.slice(secondColon + 1);
75
+ }
76
+ }
77
+ return null;
78
+ }
79
+ /**
80
+ * Real, empirical probe -- not a permission-bit read. See this module's own
81
+ * doc for why: a directory can be `mkdir`-able yet still produce a child
82
+ * with no `memory.max` at all, when the controller was never actually
83
+ * enabled in that parent's own `cgroup.subtree_control` (this process's own
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.
87
+ */
88
+ function probeControllableChild(candidateDir) {
89
+ const probe = join(candidateDir, `.descry-cgroup-probe-${String(process.pid)}-${String(Date.now())}-${String(Math.random()).slice(2, 8)}`);
90
+ try {
91
+ mkdirSync(probe);
92
+ }
93
+ catch {
94
+ return false;
95
+ }
96
+ const controllable = existsSync(join(probe, "memory.max"));
97
+ try {
98
+ rmdirSync(probe);
99
+ }
100
+ catch {
101
+ // Best-effort cleanup of our own just-created probe directory -- not
102
+ // this module's problem if something else raced to touch it first.
103
+ }
104
+ return controllable;
105
+ }
106
+ /**
107
+ * Walks up from this process's own cgroup toward `/sys/fs/cgroup`, returning
108
+ * the deepest (most specific) ancestor directory whose children genuinely
109
+ * get a real `memory.max` -- i.e. the actual delegation boundary, not
110
+ * merely a directory this uid happens to own. `null` when cgroups v2 is not
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).
113
+ */
114
+ export function findDelegatedCgroupAncestor() {
115
+ if (process.platform !== "linux")
116
+ return null;
117
+ if (!existsSync(join(CGROUP_ROOT, "cgroup.controllers")))
118
+ return null;
119
+ const ownPath = ownUnifiedCgroupPath();
120
+ if (ownPath === null)
121
+ return null;
122
+ let dir = join(CGROUP_ROOT, ownPath.replace(/^\/+/, ""));
123
+ // Walk strictly upward, never testing CGROUP_ROOT itself -- writing
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.
127
+ while (dir !== CGROUP_ROOT && dirname(dir) !== dir) {
128
+ if (existsSync(dir) && probeControllableChild(dir))
129
+ return dir;
130
+ dir = dirname(dir);
131
+ }
132
+ return null;
133
+ }
134
+ export function cgroupV2Capability() {
135
+ if (process.platform !== "linux") {
136
+ return { availability: "unavailable", reason: `cgroups v2 resource-limit move is Linux-only; not available on "${process.platform}"` };
137
+ }
138
+ if (!existsSync(join(CGROUP_ROOT, "cgroup.controllers"))) {
139
+ return { availability: "unavailable", reason: `${CGROUP_ROOT} has no cgroup.controllers file -- this host is not using the cgroups v2 unified hierarchy` };
140
+ }
141
+ const ancestor = findDelegatedCgroupAncestor();
142
+ if (ancestor === null) {
143
+ return {
144
+ availability: "unavailable",
145
+ reason: "cgroups v2 is mounted, but no writable, controller-delegated cgroup ancestor was found for this process -- commonly means this " +
146
+ "process is not running under a systemd user session with delegation (Delegate=yes on user@<uid>.service), or is running as a " +
147
+ "user with no cgroup delegation anywhere in its own ancestry. Refusing rather than silently doing nothing.",
148
+ };
149
+ }
150
+ return { availability: "available", reason: null };
151
+ }
152
+ /**
153
+ * Moves `options.pid` -- an already-running process this runtime did not
154
+ * spawn -- into a newly created, resource-limited cgroup. Throws (does not
155
+ * silently proceed) when the target is not alive, when `cgroupV2Capability()`
156
+ * reports unavailable, or when a declared limit's controller file does not
157
+ * actually exist under the delegated ancestor found for it.
158
+ *
159
+ * Limits are written to the new cgroup BEFORE the pid is moved into it, so
160
+ * a process is never observed inside a cgroup with a half-applied limit
161
+ * set; if any write fails, the created (still-empty) cgroup is removed and
162
+ * the error is rethrown rather than left as an orphaned, unlimited group.
163
+ */
164
+ export function moveProcessIntoRestrictedCgroup(options) {
165
+ if (!isProcessAlive(options.pid)) {
166
+ throw new Error(`moveProcessIntoRestrictedCgroup: pid ${String(options.pid)} is not running -- cannot move a target that does not exist.`);
167
+ }
168
+ const capability = cgroupV2Capability();
169
+ if (capability.availability !== "available") {
170
+ throw new Error(`moveProcessIntoRestrictedCgroup: cannot apply -- ${capability.reason}`);
171
+ }
172
+ const ancestor = findDelegatedCgroupAncestor();
173
+ if (ancestor === null) {
174
+ // Unreachable given capability.availability === "available" above, but
175
+ // narrows the type honestly rather than a non-null assertion.
176
+ throw new Error("moveProcessIntoRestrictedCgroup: no delegated cgroup ancestor found despite capability reporting available -- internal inconsistency.");
177
+ }
178
+ const cgroupPath = join(ancestor, `descry-restrict-${options.nameHint ?? String(options.pid)}-${String(Date.now())}`);
179
+ mkdirSync(cgroupPath);
180
+ try {
181
+ if (options.limits.cpuMax !== undefined && !existsSync(join(cgroupPath, "cpu.max"))) {
182
+ throw new Error(`cpuMax was requested but the delegated cgroup ancestor (${ancestor}) does not expose a writable cpu.max to its children on this host -- ` +
183
+ "refusing rather than silently ignoring the limit.");
184
+ }
185
+ if (options.limits.memoryMaxBytes !== undefined) {
186
+ writeFileSync(join(cgroupPath, "memory.max"), String(options.limits.memoryMaxBytes));
187
+ // See CgroupResourceLimits.memoryMaxBytes's own comment: without this,
188
+ // a host with swap available reclaims into swap instead of killing,
189
+ // and memory.max stops being a real limit. Best-effort -- a host with
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.
193
+ try {
194
+ writeFileSync(join(cgroupPath, "memory.swap.max"), "0");
195
+ }
196
+ catch {
197
+ // Disclosed above, not silently pretended to have succeeded.
198
+ }
199
+ }
200
+ if (options.limits.maxProcesses !== undefined) {
201
+ writeFileSync(join(cgroupPath, "pids.max"), String(options.limits.maxProcesses));
202
+ }
203
+ if (options.limits.cpuMax !== undefined) {
204
+ writeFileSync(join(cgroupPath, "cpu.max"), options.limits.cpuMax);
205
+ }
206
+ // Moved last, only once every requested limit is confirmed written.
207
+ writeFileSync(join(cgroupPath, "cgroup.procs"), String(options.pid));
208
+ }
209
+ catch (error) {
210
+ try {
211
+ rmdirSync(cgroupPath);
212
+ }
213
+ catch {
214
+ // Best-effort -- if the pid was already moved in before the failure,
215
+ // the directory is non-empty and cannot be removed; nothing more this
216
+ // catch can honestly do about that.
217
+ }
218
+ throw new Error(`moveProcessIntoRestrictedCgroup: failed to apply limits or move pid ${String(options.pid)} into ${cgroupPath}: ${error instanceof Error ? error.message : String(error)}`);
219
+ }
220
+ return {
221
+ applied: true,
222
+ cgroupPath,
223
+ scope: CGROUP_PARTIAL_RESTRICTION_SCOPE,
224
+ appliedLimits: options.limits,
225
+ release: () => {
226
+ try {
227
+ rmdirSync(cgroupPath);
228
+ return true;
229
+ }
230
+ catch {
231
+ return false;
232
+ }
233
+ },
234
+ };
235
+ }
236
+ //# sourceMappingURL=cgroup-partial-restriction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cgroup-partial-restriction.js","sourceRoot":"","sources":["../src/cgroup-partial-restriction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `Evidence.collectorVersion`'s source for evidence the orchestrator itself
3
+ * emits directly (e.g. `COLLECTOR_ERROR` for a collector that never got
4
+ * past `start()`, so no collector-owned code ever ran to attribute the row
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.
10
+ */
11
+ export declare const COLLECTOR_VERSION: string;
12
+ //# sourceMappingURL=collector-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector-version.d.ts","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,eAAO,MAAM,iBAAiB,EAAE,MAA6E,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `Evidence.collectorVersion`'s source for evidence the orchestrator itself
3
+ * emits directly (e.g. `COLLECTOR_ERROR` for a collector that never got
4
+ * past `start()`, so no collector-owned code ever ran to attribute the row
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.
10
+ */
11
+ import { createRequire } from "node:module";
12
+ const require = createRequire(import.meta.url);
13
+ export const COLLECTOR_VERSION = require("../package.json").version;
14
+ //# sourceMappingURL=collector-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector-version.js","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;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"}
@@ -0,0 +1,19 @@
1
+ export type { PollingProcessOutputSourceOptions } from "./polling-output-source.ts";
2
+ export { createPollingProcessOutputSource } from "./polling-output-source.ts";
3
+ export type { AttachToRunningProcessOptions } from "./attach-to-running-process.ts";
4
+ export { attachToRunningProcess, isProcessAlive } from "./attach-to-running-process.ts";
5
+ export type { AttachToRunningNodeProcessOptions } from "./attach-to-running-node-process.ts";
6
+ export { attachToRunningNodeProcess, discoverNodeInspectorUrl } from "./attach-to-running-node-process.ts";
7
+ export type { CdpSession } from "./cdp-client.ts";
8
+ export { connectCdp } from "./cdp-client.ts";
9
+ export type { AttachToRunningJvmProcessOptions } from "./attach-to-running-jvm-process.ts";
10
+ export { attachToRunningJvmProcess } from "./attach-to-running-jvm-process.ts";
11
+ export type { RespawnCommand, RespawnCommandSource, RespawnAndSuperviseOptions, RespawnAndSuperviseResult, } from "./respawn-and-supervise.ts";
12
+ export { respawnAndSupervise, inferRespawnCommandFromProc } from "./respawn-and-supervise.ts";
13
+ export type { CgroupResourceLimits, CgroupMoveResult, } from "./cgroup-partial-restriction.ts";
14
+ export { CGROUP_PARTIAL_RESTRICTION_SCOPE, cgroupV2Capability, findDelegatedCgroupAncestor, moveProcessIntoRestrictedCgroup, } from "./cgroup-partial-restriction.ts";
15
+ export type { InstrumentedExecutionInput, InstrumentedExecutionResult } from "./instrumented-execution.ts";
16
+ export { runInstrumentedExecution, createSourceRootResolver } from "./instrumented-execution.ts";
17
+ export type { BackendObservationMechanism, RunRemoteLogObservationInput, RunRemoteLogObservationResult, } from "./profile-backend-observation.ts";
18
+ export { backendObservationMechanismFor, runRemoteLogObservation } from "./profile-backend-observation.ts";
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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;AACpF,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,MAAM,6BAA6B,CAAC;AAC3G,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACjG,YAAY,EACV,2BAA2B,EAC3B,4BAA4B,EAC5B,6BAA6B,GAC9B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export { createPollingProcessOutputSource } from "./polling-output-source.js";
2
+ export { attachToRunningProcess, isProcessAlive } from "./attach-to-running-process.js";
3
+ export { attachToRunningNodeProcess, discoverNodeInspectorUrl } from "./attach-to-running-node-process.js";
4
+ export { connectCdp } from "./cdp-client.js";
5
+ export { attachToRunningJvmProcess } from "./attach-to-running-jvm-process.js";
6
+ export { respawnAndSupervise, inferRespawnCommandFromProc } from "./respawn-and-supervise.js";
7
+ export { CGROUP_PARTIAL_RESTRICTION_SCOPE, cgroupV2Capability, findDelegatedCgroupAncestor, moveProcessIntoRestrictedCgroup, } from "./cgroup-partial-restriction.js";
8
+ export { runInstrumentedExecution, createSourceRootResolver } from "./instrumented-execution.js";
9
+ export { backendObservationMechanismFor, runRemoteLogObservation } from "./profile-backend-observation.js";
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gCAAgC,EAAE,MAAM,4BAA4B,CAAC;AAE9E,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAExF,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAE3G,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAO/E,OAAO,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAK9F,OAAO,EACL,gCAAgC,EAChC,kBAAkB,EAClB,2BAA2B,EAC3B,+BAA+B,GAChC,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAMjG,OAAO,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * **The assembly nothing else performed.**
3
+ *
4
+ * Every piece of the runtime existed and none of them were wired together
5
+ * in production code: the controller spawned services, `RuntimeAdapter`
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.
28
+ */
29
+ import type { Evidence, Execution, ExecutionConfiguration, ProcessHandle, ServiceRootLookup } from "@descryy/runtime-contracts";
30
+ import type { RuntimeAdapter } from "@descryy/runtime-backend-observation";
31
+ import { type CreateExecutionInput, type RunOptions } from "@descryy/runtime-controller";
32
+ import { EvidenceStore } from "@descryy/runtime-evidence-store";
33
+ export interface InstrumentedExecutionInput {
34
+ /** Carries the `ExecutionConfiguration` itself — the controller's own input shape, not a parallel copy that could drift from it. */
35
+ readonly execution: CreateExecutionInput;
36
+ readonly runOptions: RunOptions;
37
+ /** Supplies the collectors. One adapter per execution: a run observes one backend runtime. */
38
+ readonly adapter: RuntimeAdapter;
39
+ /** Where evidence is written. Provided rather than constructed so a caller owns the database's lifetime and its redaction policy. */
40
+ readonly store: EvidenceStore;
41
+ /**
42
+ * How long to keep draining collector output after the services are up.
43
+ * There is no "the application is done" signal at this layer — a server
44
+ * runs until stopped — so the caller states how long the observation
45
+ * window is rather than this module guessing.
46
+ */
47
+ readonly observeForMs: number;
48
+ /**
49
+ * **Opt-in, per service name** (a key into `execution.configuration.services`):
50
+ * front that service with `InboundProxy` (RT-192, DEC-271) once — and
51
+ * only once — its OWN readiness has already succeeded. This module never
52
+ * re-asks the question; it reads the answer `controller.run()` already
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.
56
+ *
57
+ * Absent, or a service not named here, is today's behaviour, unchanged:
58
+ * no proxy, traffic observed only through whatever collectors `adapter`
59
+ * itself supplies.
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.
69
+ */
70
+ readonly frontWithInboundProxy?: Readonly<Record<string, boolean>>;
71
+ /**
72
+ * Fires once every collector for this run — including any `InboundProxy`
73
+ * `frontWithInboundProxy` started — has started, and before the
74
+ * observation window's sleep begins. Exists so a caller (chiefly a test)
75
+ * can drive real traffic through a just-started proxy while it and the
76
+ * service it fronts are still alive; `InstrumentedExecutionResult` itself
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.
81
+ */
82
+ readonly onReady?: (info: {
83
+ readonly execution: Execution;
84
+ /** Service name → the proxy's own bound port, for every service that was both flagged AND successfully fronted. */
85
+ readonly proxyPorts: Readonly<Record<string, number>>;
86
+ }) => void | Promise<void>;
87
+ }
88
+ export interface InstrumentedExecutionResult {
89
+ readonly execution: Execution;
90
+ readonly evidence: readonly Evidence[];
91
+ /** Every collector's own report of what it could do, after the run — not what it claimed at construction. */
92
+ readonly collectorCapabilities: readonly {
93
+ readonly collectorId: string;
94
+ readonly capabilities: ReturnType<import("@descryy/runtime-contracts").Collector["capabilities"]>;
95
+ }[];
96
+ /** Set when `run()` refused before spawning anything. Evidence is empty and that is a refusal, not an absence of failures. */
97
+ readonly validationError: string | null;
98
+ }
99
+ /**
100
+ * Builds the origin → source-root lookup a collector reads through
101
+ * `CollectorContext.resolveSourceRoot`.
102
+ *
103
+ * This is the piece that had been missing at both ends: RT-029 wrote the
104
+ * lookup and nothing called it; RT-033 added the channel and nothing filled
105
+ * it. It closes over the execution's *own* processes, which is what makes
106
+ * the port→service join possible at all — under ephemeral ports the
107
+ * configuration cannot know the port, so only the running process can say.
108
+ */
109
+ export declare function createSourceRootResolver(processes: readonly ProcessHandle[], configuration: ExecutionConfiguration): (origin: string) => ServiceRootLookup;
110
+ export declare function runInstrumentedExecution(input: InstrumentedExecutionInput): Promise<InstrumentedExecutionResult>;
111
+ //# sourceMappingURL=instrumented-execution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumented-execution.d.ts","sourceRoot":"","sources":["../src/instrumented-execution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAEV,QAAQ,EACR,SAAS,EACT,sBAAsB,EACtB,aAAa,EACb,iBAAiB,EAClB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,sCAAsC,CAAC;AAEhG,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;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnE;;;;;;;;;;OAUG;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;CACzC;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,SAAS,aAAa,EAAE,EACnC,aAAa,EAAE,sBAAsB,GACpC,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAEvC;AAED,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAqVtH"}