@descryy/runtime-controller 0.3.6 → 0.3.8

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 (69) hide show
  1. package/package.json +5 -5
  2. package/dist/attach-fencing.d.ts +0 -109
  3. package/dist/attach-fencing.d.ts.map +0 -1
  4. package/dist/attach-fencing.js +0 -215
  5. package/dist/attach-fencing.js.map +0 -1
  6. package/dist/capability-registry.d.ts +0 -9
  7. package/dist/capability-registry.d.ts.map +0 -1
  8. package/dist/capability-registry.js +0 -32
  9. package/dist/capability-registry.js.map +0 -1
  10. package/dist/collector-version.d.ts +0 -3
  11. package/dist/collector-version.d.ts.map +0 -1
  12. package/dist/collector-version.js +0 -5
  13. package/dist/collector-version.js.map +0 -1
  14. package/dist/container-sandbox.d.ts +0 -104
  15. package/dist/container-sandbox.d.ts.map +0 -1
  16. package/dist/container-sandbox.js +0 -152
  17. package/dist/container-sandbox.js.map +0 -1
  18. package/dist/controller.d.ts +0 -138
  19. package/dist/controller.d.ts.map +0 -1
  20. package/dist/controller.js +0 -449
  21. package/dist/controller.js.map +0 -1
  22. package/dist/dependency-version-check.d.ts +0 -36
  23. package/dist/dependency-version-check.d.ts.map +0 -1
  24. package/dist/dependency-version-check.js +0 -72
  25. package/dist/dependency-version-check.js.map +0 -1
  26. package/dist/env.d.ts +0 -10
  27. package/dist/env.d.ts.map +0 -1
  28. package/dist/env.js +0 -12
  29. package/dist/env.js.map +0 -1
  30. package/dist/environment-metadata.d.ts +0 -15
  31. package/dist/environment-metadata.d.ts.map +0 -1
  32. package/dist/environment-metadata.js +0 -63
  33. package/dist/environment-metadata.js.map +0 -1
  34. package/dist/environment-version-check.d.ts +0 -40
  35. package/dist/environment-version-check.d.ts.map +0 -1
  36. package/dist/environment-version-check.js +0 -176
  37. package/dist/environment-version-check.js.map +0 -1
  38. package/dist/execution-safety.d.ts +0 -96
  39. package/dist/execution-safety.d.ts.map +0 -1
  40. package/dist/execution-safety.js +0 -140
  41. package/dist/execution-safety.js.map +0 -1
  42. package/dist/index.d.ts +0 -29
  43. package/dist/index.d.ts.map +0 -1
  44. package/dist/index.js +0 -16
  45. package/dist/index.js.map +0 -1
  46. package/dist/orchestration.d.ts +0 -55
  47. package/dist/orchestration.d.ts.map +0 -1
  48. package/dist/orchestration.js +0 -219
  49. package/dist/orchestration.js.map +0 -1
  50. package/dist/process-collector.d.ts +0 -20
  51. package/dist/process-collector.d.ts.map +0 -1
  52. package/dist/process-collector.js +0 -116
  53. package/dist/process-collector.js.map +0 -1
  54. package/dist/process-identity.d.ts +0 -46
  55. package/dist/process-identity.d.ts.map +0 -1
  56. package/dist/process-identity.js +0 -186
  57. package/dist/process-identity.js.map +0 -1
  58. package/dist/process-manager.d.ts +0 -118
  59. package/dist/process-manager.d.ts.map +0 -1
  60. package/dist/process-manager.js +0 -310
  61. package/dist/process-manager.js.map +0 -1
  62. package/dist/readiness.d.ts +0 -122
  63. package/dist/readiness.d.ts.map +0 -1
  64. package/dist/readiness.js +0 -214
  65. package/dist/readiness.js.map +0 -1
  66. package/dist/sandbox.d.ts +0 -94
  67. package/dist/sandbox.d.ts.map +0 -1
  68. package/dist/sandbox.js +0 -228
  69. package/dist/sandbox.js.map +0 -1
@@ -1,186 +0,0 @@
1
- /**
2
- * RT-024's named hole, closed for the two mechanisms that can carry it:
3
- * "none of readiness's five mechanisms confirm the listener is THIS run's
4
- * process -- a stale listener from a dead run satisfies http/tcp-port
5
- * identically." Ephemeral-by-default (`allocateEphemeralPort`) closes the
6
- * common case by construction; this closes the fixed-port case by
7
- * detection, for real, via `/proc` -- not a heuristic guess.
8
- *
9
- * **Mechanism.** A TCP listening socket on Linux is a row in `/proc/net/tcp`
10
- * (or `/proc/net/tcp6`) carrying an inode; a process's open file descriptors
11
- * are symlinks under `/proc/<pid>/fd/*` named `socket:[<inode>]` when that fd
12
- * is a socket. If the expected pid's fd table holds a socket whose inode
13
- * matches a LISTEN row for the target port, that pid really does own the
14
- * listener -- not a name-based guess, a kernel-backed fact.
15
- *
16
- * **Scope, disclosed rather than silently narrowed.** Linux only (`/proc`
17
- * doesn't exist elsewhere) and only for a process in the same pid namespace
18
- * as this one -- true for every spawn this repo produces today: a direct
19
- * spawn, and a bwrap-wrapped one (bwrap execs into the target without
20
- * `--unshare-pid`, so `child.pid` names the real process, not a wrapper).
21
- * **Not true for `sandboxBackend: "container"`** -- a published Docker port
22
- * is commonly owned by a host-side `docker-proxy` process, not the pid this
23
- * module would be asked to verify, so a mismatch there would be a false
24
- * positive. Callers must not invoke this for the container backend; See
25
- * `controller.ts`'s call site, which guards on exactly that condition.
26
- * Anything this function cannot determine returns `"unsupported"`, never a
27
- * fabricated "verified" or a false "mismatch".
28
- *
29
- * **The spawned pid, or any of its descendants.** A launcher that forks the
30
- * real server is the ordinary case: `sh -c "..."`, `npm run dev`, `dotnet run`
31
- * (which builds and then runs the app as its child). The pid this run holds is
32
- * the launcher's, and the listener belongs to a process under it — still this
33
- * run's own process tree, which is exactly the property RT-024 asked for. The
34
- * tree is walked from `/proc/<pid>/stat`'s parent-pid field, a kernel fact like
35
- * the fd table, and a decoy outside that tree is refused exactly as before.
36
- * Checking the spawned pid alone made every forking launcher time out at
37
- * readiness (every .NET managed-lifecycle test, found at release integration).
38
- */
39
- import { readFileSync, readdirSync, readlinkSync } from "node:fs";
40
- function hexPort(port) {
41
- return port.toString(16).toUpperCase().padStart(4, "0");
42
- }
43
- /** Every inode `/proc/net/tcp{,6}` records as LISTENing on `port`, host-wide. Usually one; more than one only under `SO_REUSEPORT`. */
44
- function listenInodesForPort(port) {
45
- const inodes = new Set();
46
- const targetPort = hexPort(port);
47
- const TCP_LISTEN_STATE = "0A";
48
- for (const path of ["/proc/net/tcp", "/proc/net/tcp6"]) {
49
- let content;
50
- try {
51
- content = readFileSync(path, "utf8");
52
- }
53
- catch {
54
- // Absent entirely (non-Linux) or unreadable -- the caller sees an empty
55
- // set from both paths and reports "unsupported", never a false mismatch.
56
- continue;
57
- }
58
- for (const line of content.split("\n").slice(1)) {
59
- const fields = line.trim().split(/\s+/);
60
- const localAddress = fields[1];
61
- const state = fields[3];
62
- const inode = fields[9];
63
- if (localAddress === undefined || state === undefined || inode === undefined)
64
- continue;
65
- if (state !== TCP_LISTEN_STATE)
66
- continue;
67
- const portHex = localAddress.split(":")[1];
68
- if (portHex === targetPort)
69
- inodes.add(inode);
70
- }
71
- }
72
- return inodes;
73
- }
74
- /**
75
- * Does `expectedPid` own the socket listening on `port`, on this host, right
76
- * now? A real kernel-state check, not a guess -- see module header for the
77
- * mechanism and its disclosed scope.
78
- */
79
- /** The parent pid recorded in `/proc/<pid>/stat`, or null if it cannot be read. The command name
80
- * field is parenthesised and may itself contain spaces or parentheses, so the fields are read
81
- * from after its LAST closing parenthesis. */
82
- function parentPidOf(pid) {
83
- let stat;
84
- try {
85
- stat = readFileSync(`/proc/${pid}/stat`, "utf8");
86
- }
87
- catch {
88
- return null;
89
- }
90
- const afterName = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
91
- // afterName[0] is the state, afterName[1] the parent pid.
92
- return afterName[1] ?? null;
93
- }
94
- /** `root` and every live process below it, from one pass over `/proc`'s parent-pid fields. */
95
- function processTree(root) {
96
- const childrenOf = new Map();
97
- let entries;
98
- try {
99
- entries = readdirSync("/proc");
100
- }
101
- catch {
102
- return [String(root)];
103
- }
104
- for (const entry of entries) {
105
- if (!/^\d+$/.test(entry))
106
- continue;
107
- const parent = parentPidOf(entry);
108
- if (parent === null)
109
- continue;
110
- const siblings = childrenOf.get(parent);
111
- if (siblings === undefined)
112
- childrenOf.set(parent, [entry]);
113
- else
114
- siblings.push(entry);
115
- }
116
- const tree = [];
117
- const pending = [String(root)];
118
- const seen = new Set();
119
- while (pending.length > 0) {
120
- const pid = pending.pop();
121
- if (seen.has(pid))
122
- continue;
123
- seen.add(pid);
124
- tree.push(pid);
125
- for (const child of childrenOf.get(pid) ?? [])
126
- pending.push(child);
127
- }
128
- return tree;
129
- }
130
- /** Whether `pid`'s fd table holds one of `inodes`; `null` when that table cannot be read at all. */
131
- function ownsOneOf(pid, inodes) {
132
- let fds;
133
- try {
134
- fds = readdirSync(`/proc/${pid}/fd`);
135
- }
136
- catch {
137
- // Permission denied, or the pid is already gone -- cannot look inside its
138
- // fd table, so no claim either way.
139
- return null;
140
- }
141
- const SOCKET_FD_PATTERN = /^socket:\[(\d+)\]$/;
142
- for (const fd of fds) {
143
- let link;
144
- try {
145
- link = readlinkSync(`/proc/${pid}/fd/${fd}`);
146
- }
147
- catch {
148
- // A fd can close between readdir and readlink -- not this pid's fault,
149
- // just skip it and keep looking at the rest.
150
- continue;
151
- }
152
- const match = SOCKET_FD_PATTERN.exec(link);
153
- if (match !== null && inodes.has(match[1]))
154
- return true;
155
- }
156
- return false;
157
- }
158
- /**
159
- * Does `expectedPid` -- or a process descended from it -- own the socket
160
- * listening on `port`, on this host, right now? A real kernel-state check, not
161
- * a guess -- see module header for the mechanism and its disclosed scope.
162
- */
163
- export function verifyListeningSocketOwner(port, expectedPid) {
164
- const inodes = listenInodesForPort(port);
165
- if (inodes.size === 0) {
166
- // Either `/proc/net/tcp*` doesn't exist (non-Linux) or nothing is
167
- // actually listening there yet -- either way this function cannot make
168
- // a claim, so it declines rather than guessing.
169
- return "unsupported";
170
- }
171
- // The spawned pid itself first: its fd table being unreadable is the one
172
- // case that says nothing about ownership at all.
173
- const own = ownsOneOf(String(expectedPid), inodes);
174
- if (own === null)
175
- return "unsupported";
176
- if (own)
177
- return "verified";
178
- // Then its descendants. One that vanished or cannot be read is skipped --
179
- // it cannot turn a real mismatch into a claim either way.
180
- for (const pid of processTree(expectedPid).slice(1)) {
181
- if (ownsOneOf(pid, inodes) === true)
182
- return "verified";
183
- }
184
- return "mismatch";
185
- }
186
- //# sourceMappingURL=process-identity.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"process-identity.js","sourceRoot":"","sources":["../src/process-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAIlE,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,uIAAuI;AACvI,SAAS,mBAAmB,CAAC,IAAY;IACvC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,gBAAgB,GAAG,IAAI,CAAC;IAE9B,KAAK,MAAM,IAAI,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAAC,EAAE,CAAC;QACvD,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;YACxE,yEAAyE;YACzE,SAAS;QACX,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAChD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACxC,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,YAAY,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS;gBAAE,SAAS;YACvF,IAAI,KAAK,KAAK,gBAAgB;gBAAE,SAAS;YACzC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3C,IAAI,OAAO,KAAK,UAAU;gBAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH;;+CAE+C;AAC/C,SAAS,WAAW,CAAC,GAAW;IAC9B,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,SAAS,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnE,0DAA0D;IAC1D,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC9B,CAAC;AAED,8FAA8F;AAC9F,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACxB,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,SAAS;QACnC,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,MAAM,KAAK,IAAI;YAAE,SAAS;QAC9B,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,QAAQ,KAAK,SAAS;YAAE,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;;YACvD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IACD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAG,CAAC;QAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oGAAoG;AACpG,SAAS,SAAS,CAAC,GAAW,EAAE,MAA2B;IACzD,IAAI,GAAa,CAAC;IAClB,IAAI,CAAC;QACH,GAAG,GAAG,WAAW,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;QAC1E,oCAAoC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;IAC/C,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,YAAY,CAAC,SAAS,GAAG,OAAO,EAAE,EAAE,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,6CAA6C;YAC7C,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;YAAE,OAAO,IAAI,CAAC;IAC3D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAAY,EAAE,WAAmB;IAC1E,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACtB,kEAAkE;QAClE,uEAAuE;QACvE,gDAAgD;QAChD,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,yEAAyE;IACzE,iDAAiD;IACjD,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC;IACnD,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,aAAa,CAAC;IACvC,IAAI,GAAG;QAAE,OAAO,UAAU,CAAC;IAE3B,0EAA0E;IAC1E,0DAA0D;IAC1D,KAAK,MAAM,GAAG,IAAI,WAAW,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,IAAI,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,IAAI;YAAE,OAAO,UAAU,CAAC;IACzD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -1,118 +0,0 @@
1
- /**
2
- * Process manager (§5): command execution, cwd, env, process-group tracking,
3
- * stdout/stderr capture, exit-code capture, graceful shutdown, orphan prevention.
4
- *
5
- * Orphan prevention and group-kill use POSIX process groups (`detached: true` +
6
- * `process.kill(-pid, signal)`) -- not portable to Windows, out of scope for the V1
7
- * fixture-app slice (§34).
8
- */
9
- import type { FilesystemPolicy, NetworkPolicy, ProcessHandle, ResourceLimits, ServiceAttachConfiguration } from "@descryy/runtime-contracts";
10
- import { type AttachFence } from "./attach-fencing.ts";
11
- /**
12
- * Binds port 0, reads it, releases immediately. A real gap remains between "free
13
- * when checked" and "still free when the child binds it" (RT-024's gap, one layer
14
- * down) -- unavoidable without binding the real service directly to the probe
15
- * socket, which the child-process model doesn't support.
16
- *
17
- * **Examined and deliberately not "fixed".** Holding the probe socket longer widens
18
- * the window rather than narrowing it (the child can't bind while we hold it).
19
- * Instead, `awaitReadiness` names the endpoint it probed, so a collision reads as
20
- * "nothing answered on this port" rather than an unexplained failure. Window is a
21
- * handful of ms in practice; a genuine collision still fails loudly, just correctly
22
- * attributed.
23
- */
24
- export declare function allocateEphemeralPort(): Promise<number>;
25
- export interface SpawnProcessOptions {
26
- readonly processId: string;
27
- readonly command: string;
28
- readonly args?: readonly string[];
29
- readonly cwd?: string;
30
- readonly env?: Readonly<Record<string, string | undefined>>;
31
- /** Joins the resulting ProcessHandle back to ExecutionConfiguration.services (RT-023). Omit for no corresponding named service. */
32
- readonly serviceName?: string;
33
- /** Real port this process is bound to, when the caller already knows it. Recorded on the handle only. */
34
- readonly port?: number;
35
- /** §21 execution boundary (RT-070), via `applyResourceLimits`. Re-applied on every (re)spawn including `restart()`. */
36
- readonly resourceLimits?: ResourceLimits;
37
- /** §21 execution boundary, filesystem half, via `applySandbox`. Re-applied on every (re)spawn. */
38
- readonly filesystemPolicy?: FilesystemPolicy;
39
- /** §21 execution boundary, network half, via `applySandbox`. Re-applied on every (re)spawn. */
40
- readonly networkPolicy?: NetworkPolicy;
41
- /**
42
- * Selects the mechanism enforcing `filesystemPolicy`/`networkPolicy`. Absent means
43
- * `"bwrap"` (unchanged default); `"container"` routes through `applyContainerSandbox`.
44
- *
45
- * **Does not compose with `resourceLimits` in this pass** -- the container backend
46
- * skips `applyResourceLimits`'s `prlimit` wrap entirely (a minimal image isn't
47
- * guaranteed to carry `prlimit`, Docker's own limit flags aren't wired up).
48
- * Disclosed in `container-sandbox.ts`, not silently dropped.
49
- */
50
- readonly sandboxBackend?: "bwrap" | "container";
51
- }
52
- export interface ProcessExitInfo {
53
- readonly exitCode: number | null;
54
- readonly signal: NodeJS.Signals | null;
55
- }
56
- export interface ManagedProcess {
57
- handle(): ProcessHandle;
58
- /** Combined stdout+stderr captured so far, in arrival order -- backing store for log-pattern readiness checks. */
59
- readOutput(): string;
60
- waitForExit(): Promise<ProcessExitInfo>;
61
- /** SIGTERM the process group, wait up to `gracePeriodMs`, then SIGKILL. Idempotent. */
62
- kill(gracePeriodMs?: number): Promise<void>;
63
- /**
64
- * Kills the current process (no-op wait if already exited) and spawns a fresh one
65
- * with the same command/args/cwd/env. `handle()`/`readOutput()`/`waitForExit()`
66
- * observe the new process on the same object -- no re-fetch needed. Resolves once
67
- * the new process exists, not once it's ready (readiness is the caller's job).
68
- */
69
- restart(gracePeriodMs?: number): Promise<void>;
70
- /**
71
- * The attach-time fence (RT-N3), or `null` for a process this runtime spawned
72
- * itself -- fencing only exists to mark content that predates OBSERVATION, and
73
- * a spawned process has no output before Descry started it. See
74
- * `computeAttachFence` (`attach-fencing.ts`) for what it records and why.
75
- */
76
- attachFence(): AttachFence | null;
77
- }
78
- export declare function spawnProcess(options: SpawnProcessOptions): ManagedProcess;
79
- /**
80
- * `process.kill(pid, 0)` sends no signal, only asks the kernel if delivery is
81
- * possible. `ESRCH` = no such pid; `EPERM` = a pid we don't own but that exists.
82
- *
83
- * Deliberate second copy of `orchestrator/src/attach-to-running-process.ts`'s
84
- * same-named function, not an import -- `orchestrator` depends on `controller`, so
85
- * the reverse import would be circular. Tiny and pure enough to duplicate safely.
86
- */
87
- export declare function isProcessAlive(pid: number): boolean;
88
- export interface AttachProcessOptions {
89
- readonly processId: string;
90
- readonly attach: ServiceAttachConfiguration;
91
- readonly serviceName?: string;
92
- readonly port?: number;
93
- }
94
- /**
95
- * `ManagedProcess` for a service attached to rather than spawned. Reads
96
- * `attach.logFilePath` fresh on every `readOutput()` -- same growing-whole-buffer
97
- * contract as a spawned process's in-memory pipe buffer.
98
- *
99
- * **`kill()` is a deliberate no-op, not a stub.** Descry did not spawn this process,
100
- * and `ATTACH_MODE_SCOPE_DISCLOSURE` is unconditional: attach mode never executes
101
- * code in or applies changes to what it observes. Ending observation is the honest
102
- * equivalent of "stop" -- the target keeps running. `restart()` throws for the same
103
- * reason: no command exists to relaunch it with.
104
- *
105
- * **Attach fencing (RT-N3).** `computeAttachFence` snapshots, at construction time
106
- * (i.e. the moment of attach), how much of `logFilePath` already existed and
107
- * whether some other process also holds it open -- see `attach-fencing.ts`. Exposed
108
- * via `attachFence()` for a caller building a `ProcessOutputSource` over
109
- * `readOutput()` to mark pre-existing content instead of reporting it as freshly
110
- * observed. `readOutput()` itself additionally refuses (rather than silently
111
- * mis-reading) if the file is ever found shorter than the largest size already
112
- * seen -- the same "cannot reconstruct a rotated/truncated buffer" rule
113
- * `orchestrator/src/polling-output-source.ts` enforces one layer up, duplicated
114
- * here for the same reason `isProcessAlive` is: this module cannot depend on
115
- * that one without a cycle, and the check is small and pure enough to repeat.
116
- */
117
- export declare function attachManagedProcess(options: AttachProcessOptions): ManagedProcess;
118
- //# sourceMappingURL=process-manager.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../src/process-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAI7I,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAevD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,mIAAmI;IACnI,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,yGAAyG;IACzG,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,uHAAuH;IACvH,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC,kGAAkG;IAClG,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAC7C,+FAA+F;IAC/F,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;CACjD;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,IAAI,aAAa,CAAC;IACxB,kHAAkH;IAClH,UAAU,IAAI,MAAM,CAAC;IACrB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;IACxC,uFAAuF;IACvF,IAAI,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C;;;;;OAKG;IACH,OAAO,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C;;;;;OAKG;IACH,WAAW,IAAI,WAAW,GAAG,IAAI,CAAC;CACnC;AAID,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,cAAc,CAiJzE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAID;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,GAAG,cAAc,CAwFlF"}
@@ -1,310 +0,0 @@
1
- /**
2
- * Process manager (§5): command execution, cwd, env, process-group tracking,
3
- * stdout/stderr capture, exit-code capture, graceful shutdown, orphan prevention.
4
- *
5
- * Orphan prevention and group-kill use POSIX process groups (`detached: true` +
6
- * `process.kill(-pid, signal)`) -- not portable to Windows, out of scope for the V1
7
- * fixture-app slice (§34).
8
- */
9
- import { execFile, spawn } from "node:child_process";
10
- import { readFileSync } from "node:fs";
11
- import { createServer } from "node:net";
12
- import { promisify } from "node:util";
13
- import { applyResourceLimits } from "./execution-safety.js";
14
- import { applySandbox } from "./sandbox.js";
15
- import { applyContainerSandbox } from "./container-sandbox.js";
16
- import { computeAttachFence } from "./attach-fencing.js";
17
- /**
18
- * Binds port 0, reads it, releases immediately. A real gap remains between "free
19
- * when checked" and "still free when the child binds it" (RT-024's gap, one layer
20
- * down) -- unavoidable without binding the real service directly to the probe
21
- * socket, which the child-process model doesn't support.
22
- *
23
- * **Examined and deliberately not "fixed".** Holding the probe socket longer widens
24
- * the window rather than narrowing it (the child can't bind while we hold it).
25
- * Instead, `awaitReadiness` names the endpoint it probed, so a collision reads as
26
- * "nothing answered on this port" rather than an unexplained failure. Window is a
27
- * handful of ms in practice; a genuine collision still fails loudly, just correctly
28
- * attributed.
29
- */
30
- export function allocateEphemeralPort() {
31
- return new Promise((resolve, reject) => {
32
- const probe = createServer();
33
- probe.once("error", reject);
34
- probe.listen(0, () => {
35
- const address = probe.address();
36
- if (address === null || typeof address === "string") {
37
- probe.close();
38
- reject(new Error("could not determine an ephemeral port"));
39
- return;
40
- }
41
- const { port } = address;
42
- probe.close(() => resolve(port));
43
- });
44
- });
45
- }
46
- const DEFAULT_GRACE_PERIOD_MS = 5000;
47
- export function spawnProcess(options) {
48
- let startedAt = "";
49
- let exitedAt = null;
50
- let exitCode = null;
51
- let signal = null;
52
- let outputBuffer = "";
53
- let killStarted = false;
54
- let child;
55
- let settled = false;
56
- // Set only for the container backend -- see kill() for why it needs a separate
57
- // stop mechanism from bwrap's group-kill.
58
- let containerName = null;
59
- let resolveExit;
60
- let exitPromise;
61
- // Everything a (re)spawn needs to reset, in one place -- construction and
62
- // restart() must not drift into resetting different subsets of this state.
63
- function spawnChild() {
64
- startedAt = new Date().toISOString();
65
- exitedAt = null;
66
- exitCode = null;
67
- signal = null;
68
- outputBuffer = "";
69
- killStarted = false;
70
- settled = false;
71
- exitPromise = new Promise((resolve) => {
72
- resolveExit = resolve;
73
- });
74
- // "container" skips applyResourceLimits entirely (see SpawnProcessOptions.
75
- // sandboxBackend) -- "bwrap" (default) is unchanged.
76
- const wrapped = options.sandboxBackend === "container"
77
- ? applyContainerSandbox({
78
- interpreterCommand: options.command,
79
- command: options.command,
80
- args: options.args ?? [],
81
- cwd: options.cwd ?? process.cwd(),
82
- processEnv: { ...process.env, ...options.env },
83
- ...(options.filesystemPolicy === undefined ? {} : { filesystemPolicy: options.filesystemPolicy }),
84
- ...(options.networkPolicy === undefined ? {} : { networkPolicy: options.networkPolicy }),
85
- })
86
- : applySandbox({
87
- interpreterCommand: options.command,
88
- ...applyResourceLimits(options.command, options.args ?? [], options.resourceLimits),
89
- cwd: options.cwd ?? process.cwd(),
90
- ...(options.filesystemPolicy === undefined ? {} : { filesystemPolicy: options.filesystemPolicy }),
91
- ...(options.networkPolicy === undefined ? {} : { networkPolicy: options.networkPolicy }),
92
- });
93
- containerName = wrapped.containerName ?? null;
94
- child = spawn(wrapped.command, wrapped.args, {
95
- cwd: options.cwd,
96
- env: { ...process.env, ...options.env },
97
- detached: true,
98
- stdio: ["ignore", "pipe", "pipe"],
99
- });
100
- child.stdout?.on("data", (chunk) => {
101
- outputBuffer += chunk.toString("utf8");
102
- });
103
- child.stderr?.on("data", (chunk) => {
104
- outputBuffer += chunk.toString("utf8");
105
- });
106
- // A bad command (ENOENT) surfaces as an `'error'` event, not a thrown exception,
107
- // and per Node's docs `'exit'` may never fire after `'error'`. Both events settle
108
- // the same promise exactly once, so a failed spawn reads as an ordinary
109
- // never-became-ready process rather than crashing the caller or hanging cleanup.
110
- function settle(info) {
111
- if (settled)
112
- return;
113
- settled = true;
114
- exitedAt = new Date().toISOString();
115
- exitCode = info.exitCode;
116
- signal = info.signal;
117
- resolveExit(info);
118
- }
119
- child.once("exit", (code, exitSignal) => settle({ exitCode: code, signal: exitSignal }));
120
- child.once("error", () => settle({ exitCode: null, signal: null }));
121
- }
122
- spawnChild();
123
- function handle() {
124
- return {
125
- processId: options.processId,
126
- command: options.command,
127
- pid: child.pid ?? null,
128
- startedAt,
129
- exitedAt,
130
- exitCode,
131
- signal,
132
- serviceName: options.serviceName ?? null,
133
- port: options.port ?? null,
134
- };
135
- }
136
- async function kill(gracePeriodMs = DEFAULT_GRACE_PERIOD_MS) {
137
- if (killStarted || exitedAt !== null) {
138
- await exitPromise;
139
- return;
140
- }
141
- killStarted = true;
142
- const pid = child.pid;
143
- if (pid === undefined) {
144
- return;
145
- }
146
- if (containerName !== null) {
147
- // Found via escape testing: the local `docker` CLI process is not the
148
- // container -- it runs under `dockerd`, a separate process tree.
149
- // Signaling the CLI's process group left a real orphaned container running.
150
- // `docker stop` (by name) is the real stop mechanism here; `--rm` then
151
- // removes it. Still falls through to group-kill below as a CLI backstop.
152
- try {
153
- await execFileAsync("docker", ["stop", "--time", String(Math.max(1, Math.round(gracePeriodMs / 1000))), containerName]);
154
- }
155
- catch {
156
- // Already stopped/removed -- not worth surfacing.
157
- }
158
- }
159
- signalGroup(pid, "SIGTERM");
160
- const exited = await Promise.race([exitPromise.then(() => true), sleep(gracePeriodMs).then(() => false)]);
161
- if (!exited) {
162
- signalGroup(pid, "SIGKILL");
163
- await exitPromise;
164
- }
165
- }
166
- async function restart(gracePeriodMs = DEFAULT_GRACE_PERIOD_MS) {
167
- // Safe regardless of the old process's state -- kill() is idempotent, and this
168
- // waits for it to be fully gone before spawnChild() reassigns state, so no
169
- // outgoing event can land on the incoming process.
170
- await kill(gracePeriodMs);
171
- spawnChild();
172
- }
173
- return {
174
- handle,
175
- readOutput: () => outputBuffer,
176
- waitForExit: () => exitPromise,
177
- kill,
178
- restart,
179
- // A spawned process has no output that predates this runtime observing it.
180
- attachFence: () => null,
181
- };
182
- }
183
- /**
184
- * `process.kill(pid, 0)` sends no signal, only asks the kernel if delivery is
185
- * possible. `ESRCH` = no such pid; `EPERM` = a pid we don't own but that exists.
186
- *
187
- * Deliberate second copy of `orchestrator/src/attach-to-running-process.ts`'s
188
- * same-named function, not an import -- `orchestrator` depends on `controller`, so
189
- * the reverse import would be circular. Tiny and pure enough to duplicate safely.
190
- */
191
- export function isProcessAlive(pid) {
192
- try {
193
- process.kill(pid, 0);
194
- return true;
195
- }
196
- catch (error) {
197
- return error.code === "EPERM";
198
- }
199
- }
200
- const DEFAULT_ATTACH_POLL_INTERVAL_MS = 50;
201
- /**
202
- * `ManagedProcess` for a service attached to rather than spawned. Reads
203
- * `attach.logFilePath` fresh on every `readOutput()` -- same growing-whole-buffer
204
- * contract as a spawned process's in-memory pipe buffer.
205
- *
206
- * **`kill()` is a deliberate no-op, not a stub.** Descry did not spawn this process,
207
- * and `ATTACH_MODE_SCOPE_DISCLOSURE` is unconditional: attach mode never executes
208
- * code in or applies changes to what it observes. Ending observation is the honest
209
- * equivalent of "stop" -- the target keeps running. `restart()` throws for the same
210
- * reason: no command exists to relaunch it with.
211
- *
212
- * **Attach fencing (RT-N3).** `computeAttachFence` snapshots, at construction time
213
- * (i.e. the moment of attach), how much of `logFilePath` already existed and
214
- * whether some other process also holds it open -- see `attach-fencing.ts`. Exposed
215
- * via `attachFence()` for a caller building a `ProcessOutputSource` over
216
- * `readOutput()` to mark pre-existing content instead of reporting it as freshly
217
- * observed. `readOutput()` itself additionally refuses (rather than silently
218
- * mis-reading) if the file is ever found shorter than the largest size already
219
- * seen -- the same "cannot reconstruct a rotated/truncated buffer" rule
220
- * `orchestrator/src/polling-output-source.ts` enforces one layer up, duplicated
221
- * here for the same reason `isProcessAlive` is: this module cannot depend on
222
- * that one without a cycle, and the check is small and pure enough to repeat.
223
- */
224
- export function attachManagedProcess(options) {
225
- const { pid, logFilePath } = options.attach;
226
- if (!isProcessAlive(pid)) {
227
- throw new Error(`attachManagedProcess: pid ${String(pid)} for "${options.processId}" is not running — attach requires ` +
228
- `a target that already exists.`);
229
- }
230
- const fence = computeAttachFence(pid, logFilePath);
231
- let maxObservedByteLength = fence.attachOffsetBytes;
232
- const startedAt = new Date().toISOString();
233
- let exitedAt = null;
234
- let exitPromise = null;
235
- function handle() {
236
- if (exitedAt === null && !isProcessAlive(pid)) {
237
- exitedAt = new Date().toISOString();
238
- }
239
- return {
240
- processId: options.processId,
241
- // No real command to report -- disclosed rather than guessed.
242
- command: `<attached: pid ${String(pid)}, ${logFilePath}>`,
243
- pid,
244
- startedAt,
245
- exitedAt,
246
- // Not observable for a process we don't spawn or reap.
247
- exitCode: null,
248
- signal: null,
249
- serviceName: options.serviceName ?? null,
250
- port: options.port ?? null,
251
- attached: true,
252
- };
253
- }
254
- function readOutput() {
255
- let content;
256
- try {
257
- content = readFileSync(logFilePath, "utf8");
258
- }
259
- catch (error) {
260
- const detail = error instanceof Error ? error.message : String(error);
261
- throw new Error(`attachManagedProcess: could not read log file "${logFilePath}" for pid ${String(pid)}: ${detail}`);
262
- }
263
- const byteLength = Buffer.byteLength(content, "utf8");
264
- if (byteLength < maxObservedByteLength) {
265
- throw new Error(`attachManagedProcess: log file "${logFilePath}" for pid ${String(pid)} shrank from ${String(maxObservedByteLength)} ` +
266
- `to ${String(byteLength)} bytes between reads -- this looks like log rotation or truncation, not new output. ` +
267
- "The file-tail contract can't reconstruct a rotated/truncated buffer without either duplicating or dropping " +
268
- `lines. Reported rather than guessed. (This run's own attach offset was ${String(fence.attachOffsetBytes)} bytes.)`);
269
- }
270
- maxObservedByteLength = byteLength;
271
- return content;
272
- }
273
- function waitForExit() {
274
- exitPromise ??= new Promise((resolve) => {
275
- const poll = () => {
276
- if (!isProcessAlive(pid)) {
277
- exitedAt ??= new Date().toISOString();
278
- resolve({ exitCode: null, signal: null });
279
- return;
280
- }
281
- setTimeout(poll, DEFAULT_ATTACH_POLL_INTERVAL_MS);
282
- };
283
- poll();
284
- });
285
- return exitPromise;
286
- }
287
- function kill() {
288
- // Deliberate no-op -- see module doc above.
289
- return Promise.resolve();
290
- }
291
- function restart() {
292
- return Promise.reject(new Error(`attachManagedProcess: cannot restart an attached process (pid ${String(pid)}) — Descry did not spawn ` +
293
- `it and has no command to relaunch it with.`));
294
- }
295
- return { handle, readOutput, waitForExit, kill, restart, attachFence: () => fence };
296
- }
297
- const execFileAsync = promisify(execFile);
298
- function signalGroup(pid, signal) {
299
- try {
300
- // Negative pid targets the whole process group (orphan-prevention mechanism).
301
- process.kill(-pid, signal);
302
- }
303
- catch {
304
- // Group already gone -- not an error.
305
- }
306
- }
307
- function sleep(ms) {
308
- return new Promise((resolve) => setTimeout(resolve, ms));
309
- }
310
- //# sourceMappingURL=process-manager.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../src/process-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAoB,MAAM,qBAAqB,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC5B,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE;YACnB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACpD,KAAK,CAAC,KAAK,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC,CAAC;gBAC3D,OAAO;YACT,CAAC;YACD,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;YACzB,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AA0DD,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAErC,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,MAAM,GAA0B,IAAI,CAAC;IACzC,IAAI,YAAY,GAAG,EAAE,CAAC;IACtB,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,KAAmB,CAAC;IACxB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,+EAA+E;IAC/E,0CAA0C;IAC1C,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,WAA6C,CAAC;IAClD,IAAI,WAAqC,CAAC;IAE1C,0EAA0E;IAC1E,2EAA2E;IAC3E,SAAS,UAAU;QACjB,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,QAAQ,GAAG,IAAI,CAAC;QAChB,QAAQ,GAAG,IAAI,CAAC;QAChB,MAAM,GAAG,IAAI,CAAC;QACd,YAAY,GAAG,EAAE,CAAC;QAClB,WAAW,GAAG,KAAK,CAAC;QACpB,OAAO,GAAG,KAAK,CAAC;QAChB,WAAW,GAAG,IAAI,OAAO,CAAkB,CAAC,OAAO,EAAE,EAAE;YACrD,WAAW,GAAG,OAAO,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,2EAA2E;QAC3E,qDAAqD;QACrD,MAAM,OAAO,GACX,OAAO,CAAC,cAAc,KAAK,WAAW;YACpC,CAAC,CAAC,qBAAqB,CAAC;gBACpB,kBAAkB,EAAE,OAAO,CAAC,OAAO;gBACnC,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;gBACxB,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;gBACjC,UAAU,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;gBAC9C,GAAG,CAAC,OAAO,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC;gBACjG,GAAG,CAAC,OAAO,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;aACzF,CAAC;YACJ,CAAC,CAAC,YAAY,CAAC;gBACX,kBAAkB,EAAE,OAAO,CAAC,OAAO;gBACnC,GAAG,mBAAmB,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,OAAO,CAAC,cAAc,CAAC;gBACnF,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;gBACjC,GAAG,CAAC,OAAO,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC;gBACjG,GAAG,CAAC,OAAO,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;aACzF,CAAC,CAAC;QACT,aAAa,GAAI,OAA+C,CAAC,aAAa,IAAI,IAAI,CAAC;QACvF,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE;YAC3C,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;YACvC,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QAEH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,YAAY,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,YAAY,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,iFAAiF;QACjF,kFAAkF;QAClF,wEAAwE;QACxE,iFAAiF;QACjF,SAAS,MAAM,CAAC,IAAqB;YACnC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACpC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YACzB,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YACrB,WAAW,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QACzF,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,UAAU,EAAE,CAAC;IAEb,SAAS,MAAM;QACb,OAAO;YACL,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,IAAI;YACtB,SAAS;YACT,QAAQ;YACR,QAAQ;YACR,MAAM;YACN,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;YACxC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI;SAC3B,CAAC;IACJ,CAAC;IAED,KAAK,UAAU,IAAI,CAAC,aAAa,GAAG,uBAAuB;QACzD,IAAI,WAAW,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACrC,MAAM,WAAW,CAAC;YAClB,OAAO;QACT,CAAC;QACD,WAAW,GAAG,IAAI,CAAC;QACnB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;QACtB,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC3B,sEAAsE;YACtE,iEAAiE;YACjE,4EAA4E;YAC5E,uEAAuE;YACvE,yEAAyE;YACzE,IAAI,CAAC;gBACH,MAAM,aAAa,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC;YAC1H,CAAC;YAAC,MAAM,CAAC;gBACP,kDAAkD;YACpD,CAAC;QACH,CAAC;QAED,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1G,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAC5B,MAAM,WAAW,CAAC;QACpB,CAAC;IACH,CAAC;IAED,KAAK,UAAU,OAAO,CAAC,aAAa,GAAG,uBAAuB;QAC5D,+EAA+E;QAC/E,2EAA2E;QAC3E,mDAAmD;QACnD,MAAM,IAAI,CAAC,aAAa,CAAC,CAAC;QAC1B,UAAU,EAAE,CAAC;IACf,CAAC;IAED,OAAO;QACL,MAAM;QACN,UAAU,EAAE,GAAG,EAAE,CAAC,YAAY;QAC9B,WAAW,EAAE,GAAG,EAAE,CAAC,WAAW;QAC9B,IAAI;QACJ,OAAO;QACP,2EAA2E;QAC3E,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;KACxB,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;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;AASD,MAAM,+BAA+B,GAAG,EAAE,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAA6B;IAChE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAC5C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,6BAA6B,MAAM,CAAC,GAAG,CAAC,SAAS,OAAO,CAAC,SAAS,qCAAqC;YACrG,+BAA+B,CAClC,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACnD,IAAI,qBAAqB,GAAG,KAAK,CAAC,iBAAiB,CAAC;IAEpD,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,WAAW,GAAoC,IAAI,CAAC;IAExD,SAAS,MAAM;QACb,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9C,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACtC,CAAC;QACD,OAAO;YACL,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,8DAA8D;YAC9D,OAAO,EAAE,kBAAkB,MAAM,CAAC,GAAG,CAAC,KAAK,WAAW,GAAG;YACzD,GAAG;YACH,SAAS;YACT,QAAQ;YACR,uDAAuD;YACvD,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,IAAI;YACZ,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;YACxC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI;YAC1B,QAAQ,EAAE,IAAI;SACf,CAAC;IACJ,CAAC;IAED,SAAS,UAAU;QACjB,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,kDAAkD,WAAW,aAAa,MAAM,CAAC,GAAG,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;QACtH,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACtD,IAAI,UAAU,GAAG,qBAAqB,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CACb,mCAAmC,WAAW,aAAa,MAAM,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,qBAAqB,CAAC,GAAG;gBACpH,MAAM,MAAM,CAAC,UAAU,CAAC,sFAAsF;gBAC9G,6GAA6G;gBAC7G,0EAA0E,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,UAAU,CACtH,CAAC;QACJ,CAAC;QACD,qBAAqB,GAAG,UAAU,CAAC;QACnC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,SAAS,WAAW;QAClB,WAAW,KAAK,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACtC,MAAM,IAAI,GAAG,GAAS,EAAE;gBACtB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;oBACzB,QAAQ,KAAK,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;oBACtC,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC1C,OAAO;gBACT,CAAC;gBACD,UAAU,CAAC,IAAI,EAAE,+BAA+B,CAAC,CAAC;YACpD,CAAC,CAAC;YACF,IAAI,EAAE,CAAC;QACT,CAAC,CAAC,CAAC;QACH,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,SAAS,IAAI;QACX,4CAA4C;QAC5C,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,SAAS,OAAO;QACd,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,KAAK,CACP,iEAAiE,MAAM,CAAC,GAAG,CAAC,2BAA2B;YACrG,4CAA4C,CAC/C,CACF,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;AACtF,CAAC;AAED,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,SAAS,WAAW,CAAC,GAAW,EAAE,MAAsB;IACtD,IAAI,CAAC;QACH,8EAA8E;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;IACxC,CAAC;AACH,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}