@descryy/runtime-controller 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.
- package/dist/capability-registry.d.ts +24 -0
- package/dist/capability-registry.d.ts.map +1 -0
- package/dist/capability-registry.js +42 -0
- package/dist/capability-registry.js.map +1 -0
- package/dist/collector-version.d.ts +10 -0
- package/dist/collector-version.d.ts.map +1 -0
- package/dist/collector-version.js +12 -0
- package/dist/collector-version.js.map +1 -0
- package/dist/container-sandbox.d.ts +188 -0
- package/dist/container-sandbox.d.ts.map +1 -0
- package/dist/container-sandbox.js +233 -0
- package/dist/container-sandbox.js.map +1 -0
- package/dist/controller.d.ts +162 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +433 -0
- package/dist/controller.js.map +1 -0
- package/dist/dependency-version-check.d.ts +90 -0
- package/dist/dependency-version-check.d.ts.map +1 -0
- package/dist/dependency-version-check.js +121 -0
- package/dist/dependency-version-check.js.map +1 -0
- package/dist/env.d.ts +16 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +18 -0
- package/dist/env.js.map +1 -0
- package/dist/environment-metadata.d.ts +23 -0
- package/dist/environment-metadata.d.ts.map +1 -0
- package/dist/environment-metadata.js +80 -0
- package/dist/environment-metadata.js.map +1 -0
- package/dist/environment-version-check.d.ts +83 -0
- package/dist/environment-version-check.d.ts.map +1 -0
- package/dist/environment-version-check.js +218 -0
- package/dist/environment-version-check.js.map +1 -0
- package/dist/execution-safety.d.ts +154 -0
- package/dist/execution-safety.d.ts.map +1 -0
- package/dist/execution-safety.js +194 -0
- package/dist/execution-safety.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestration.d.ts +70 -0
- package/dist/orchestration.d.ts.map +1 -0
- package/dist/orchestration.js +255 -0
- package/dist/orchestration.js.map +1 -0
- package/dist/process-collector.d.ts +34 -0
- package/dist/process-collector.d.ts.map +1 -0
- package/dist/process-collector.js +148 -0
- package/dist/process-collector.js.map +1 -0
- package/dist/process-manager.d.ts +133 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +333 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/readiness.d.ts +120 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +179 -0
- package/dist/readiness.js.map +1 -0
- package/dist/sandbox.d.ts +139 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +271 -0
- package/dist/sandbox.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAGjD,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAa1F,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAG5F,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,2BAA2B,EAC3B,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,8BAA8B,EAC9B,0BAA0B,EAC1B,YAAY,GACb,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,0BAA0B,EAC1B,sCAAsC,EACtC,mCAAmC,EACnC,uCAAuC,EACvC,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAGvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAajI,OAAO,EAAE,eAAe,EAAE,6BAA6B,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns a declarative `services` map (plan §6, RT-028) into a concrete
|
|
3
|
+
* spawn order for `ExecutionController.run()` (RT-032). Everything here is
|
|
4
|
+
* pure and side-effect-free — validated and ordered before anything is
|
|
5
|
+
* spawned, so a bad reference is refused atomically rather than discovered
|
|
6
|
+
* mid-sequence with some services already running.
|
|
7
|
+
*
|
|
8
|
+
* Start order and port injection are **declared in configuration, never
|
|
9
|
+
* inferred** — the same "ask, don't guess" discipline RT-023 already
|
|
10
|
+
* applied to browser script-URL roots. A `${name.port}` reference alone
|
|
11
|
+
* does not create an ordering guarantee; `name` must also appear in the
|
|
12
|
+
* referencing service's own `dependsOn`, or validation refuses.
|
|
13
|
+
*/
|
|
14
|
+
import type { ServiceConfiguration } from "@descryy/runtime-contracts";
|
|
15
|
+
export type ServicesValidation = {
|
|
16
|
+
readonly valid: true;
|
|
17
|
+
readonly order: readonly string[];
|
|
18
|
+
} | {
|
|
19
|
+
readonly valid: false;
|
|
20
|
+
readonly reason: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Validates a `services` map and returns a valid topological spawn order.
|
|
24
|
+
* Three checks, all before anything is spawned:
|
|
25
|
+
* 1. every `dependsOn` entry names a real key in `services`;
|
|
26
|
+
* 2. the `dependsOn` graph has no cycle (a start order must exist);
|
|
27
|
+
* 3. every `${name}.port` placeholder referenced in a service's own `env`
|
|
28
|
+
* has `name` in that same service's `dependsOn` — the placeholder does
|
|
29
|
+
* not itself create the ordering guarantee it depends on.
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateServicesConfiguration(services: Readonly<Record<string, ServiceConfiguration>>): ServicesValidation;
|
|
32
|
+
/**
|
|
33
|
+
* Replaces every `${name}.port` in `env`'s values with the resolved port
|
|
34
|
+
* for `name`. Assumes `validateServicesConfiguration` already confirmed
|
|
35
|
+
* every referenced name is a declared dependency that the caller resolves
|
|
36
|
+
* earlier in the same spawn order — a reference this function can't
|
|
37
|
+
* resolve is left untouched rather than silently dropped, so a caller that
|
|
38
|
+
* skipped validation sees an obviously-wrong literal `${...}` instead of a
|
|
39
|
+
* silently empty substitution.
|
|
40
|
+
*/
|
|
41
|
+
export declare function substitutePortPlaceholders(env: Readonly<Record<string, string>> | undefined, resolvedPorts: ReadonlyMap<string, number>): Record<string, string>;
|
|
42
|
+
export type CommandTokenization = {
|
|
43
|
+
readonly ok: true;
|
|
44
|
+
readonly command: string;
|
|
45
|
+
readonly args: readonly string[];
|
|
46
|
+
} | {
|
|
47
|
+
readonly ok: false;
|
|
48
|
+
readonly reason: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* POSIX-style tokenizer for `ServiceConfiguration.command`: single quotes
|
|
52
|
+
* (literal throughout), double quotes (with `\` escaping `"` and `\`), and
|
|
53
|
+
* backslash escapes outside quotes. `spawnProcess` takes `command`/`args`
|
|
54
|
+
* separately while configuration carries one string (plan §6's YAML shape),
|
|
55
|
+
* so something has to split it, and doing that by whitespace alone breaks
|
|
56
|
+
* every path with a space in it — `"C:\Program Files\node.exe"` and
|
|
57
|
+
* `python3 -c "import app; app.main()"` both tokenized wrong before.
|
|
58
|
+
*
|
|
59
|
+
* **Refuses rather than guesses** on an unterminated quote, a trailing
|
|
60
|
+
* backslash, or an unquoted shell operator. Each of those has no correct
|
|
61
|
+
* interpretation without a shell, and returning a plausible tokenization
|
|
62
|
+
* for input that cannot mean what it says is how a configuration error
|
|
63
|
+
* arrives disguised as an application failure.
|
|
64
|
+
*
|
|
65
|
+
* Globs and `~` are *not* refused: they pass through literally, which is
|
|
66
|
+
* exactly what happens with no shell, and unlike the operators above they
|
|
67
|
+
* are frequently intended literally.
|
|
68
|
+
*/
|
|
69
|
+
export declare function tokenizeCommand(raw: string): CommandTokenization;
|
|
70
|
+
//# sourceMappingURL=orchestration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestration.d.ts","sourceRoot":"","sources":["../src/orchestration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAiBvE,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC3D;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,GACvD,kBAAkB,CA+DpB;AAkCD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,EACjD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GACzC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASxB;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACjF;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAkBpD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CA8EhE"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns a declarative `services` map (plan §6, RT-028) into a concrete
|
|
3
|
+
* spawn order for `ExecutionController.run()` (RT-032). Everything here is
|
|
4
|
+
* pure and side-effect-free — validated and ordered before anything is
|
|
5
|
+
* spawned, so a bad reference is refused atomically rather than discovered
|
|
6
|
+
* mid-sequence with some services already running.
|
|
7
|
+
*
|
|
8
|
+
* Start order and port injection are **declared in configuration, never
|
|
9
|
+
* inferred** — the same "ask, don't guess" discipline RT-023 already
|
|
10
|
+
* applied to browser script-URL roots. A `${name.port}` reference alone
|
|
11
|
+
* does not create an ordering guarantee; `name` must also appear in the
|
|
12
|
+
* referencing service's own `dependsOn`, or validation refuses.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Bounded at `{1,256}`, not `+`, since RT-070's own regex audit (prompted by
|
|
16
|
+
* RT-069's ReDoS finding in `evidence-store/redaction.ts`) named this exact
|
|
17
|
+
* shape: an unbounded character class immediately followed by a required
|
|
18
|
+
* literal (`.port}`) that can be absent from the input. On a long string
|
|
19
|
+
* with `${` but no closing `.port}`, an unbounded class here would
|
|
20
|
+
* backtrack once per starting position -- O(n^2). `service.env` values are
|
|
21
|
+
* developer-authored configuration, not runtime/adversarial data, which is
|
|
22
|
+
* why this was flagged low-risk rather than urgent -- but the shape is
|
|
23
|
+
* identical to the one that DID become a real denial-of-service on a
|
|
24
|
+
* different input source the same day, and 256 characters is far more than
|
|
25
|
+
* any real service or dependency name needs.
|
|
26
|
+
*/
|
|
27
|
+
const PORT_PLACEHOLDER = /\$\{([^}]{1,256})\.port\}/g;
|
|
28
|
+
/**
|
|
29
|
+
* Validates a `services` map and returns a valid topological spawn order.
|
|
30
|
+
* Three checks, all before anything is spawned:
|
|
31
|
+
* 1. every `dependsOn` entry names a real key in `services`;
|
|
32
|
+
* 2. the `dependsOn` graph has no cycle (a start order must exist);
|
|
33
|
+
* 3. every `${name}.port` placeholder referenced in a service's own `env`
|
|
34
|
+
* has `name` in that same service's `dependsOn` — the placeholder does
|
|
35
|
+
* not itself create the ordering guarantee it depends on.
|
|
36
|
+
*/
|
|
37
|
+
export function validateServicesConfiguration(services) {
|
|
38
|
+
const names = Object.keys(services);
|
|
39
|
+
for (const name of names) {
|
|
40
|
+
for (const dep of services[name]?.dependsOn ?? []) {
|
|
41
|
+
if (!(dep in services)) {
|
|
42
|
+
return { valid: false, reason: `service "${name}" depends on "${dep}", which is not in services` };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const orderResult = topologicalOrder(services, names);
|
|
47
|
+
if (!orderResult.valid) {
|
|
48
|
+
return orderResult;
|
|
49
|
+
}
|
|
50
|
+
for (const name of names) {
|
|
51
|
+
const declared = new Set(services[name]?.dependsOn ?? []);
|
|
52
|
+
for (const value of Object.values(services[name]?.env ?? {})) {
|
|
53
|
+
for (const match of value.matchAll(PORT_PLACEHOLDER)) {
|
|
54
|
+
const referenced = match[1];
|
|
55
|
+
if (referenced !== undefined && !declared.has(referenced)) {
|
|
56
|
+
return {
|
|
57
|
+
valid: false,
|
|
58
|
+
reason: `service "${name}" references "\${${referenced}.port}" in env but does not declare "${referenced}" in dependsOn`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Fourth check, and like the other three it runs before anything is
|
|
65
|
+
// spawned: exactly one of `command`/`attach` per service (RT-attach-mode-
|
|
66
|
+
// service-configuration). Neither means nothing tells `run()` how to
|
|
67
|
+
// start this service; both means an ambiguous configuration nothing here
|
|
68
|
+
// should silently pick a side on.
|
|
69
|
+
for (const name of names) {
|
|
70
|
+
const service = services[name];
|
|
71
|
+
const hasCommand = service?.command !== undefined;
|
|
72
|
+
const hasAttach = service?.attach !== undefined;
|
|
73
|
+
if (hasCommand === hasAttach) {
|
|
74
|
+
return {
|
|
75
|
+
valid: false,
|
|
76
|
+
reason: `service "${name}" must declare exactly one of "command" (spawn) or "attach" (attach to an already-running process), not ${hasCommand ? "both" : "neither"}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Fifth check, same atomic-refusal placement. A command that cannot be
|
|
81
|
+
// tokenized is a configuration error, and discovering it mid-sequence
|
|
82
|
+
// would mean refusing service 3 after services 1 and 2 are already
|
|
83
|
+
// running — recoverable via rollback, but needlessly: nothing about this
|
|
84
|
+
// check needs a process to exist.
|
|
85
|
+
for (const name of names) {
|
|
86
|
+
const command = services[name]?.command;
|
|
87
|
+
if (command === undefined)
|
|
88
|
+
continue;
|
|
89
|
+
const tokenized = tokenizeCommand(command);
|
|
90
|
+
if (!tokenized.ok) {
|
|
91
|
+
return { valid: false, reason: `service "${name}": ${tokenized.reason}` };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return orderResult;
|
|
95
|
+
}
|
|
96
|
+
function topologicalOrder(services, names) {
|
|
97
|
+
const order = [];
|
|
98
|
+
const visited = new Set();
|
|
99
|
+
const visiting = new Set();
|
|
100
|
+
function visit(name) {
|
|
101
|
+
if (visited.has(name))
|
|
102
|
+
return null;
|
|
103
|
+
if (visiting.has(name))
|
|
104
|
+
return `cycle detected in dependsOn involving "${name}"`;
|
|
105
|
+
visiting.add(name);
|
|
106
|
+
for (const dep of services[name]?.dependsOn ?? []) {
|
|
107
|
+
const err = visit(dep);
|
|
108
|
+
if (err !== null)
|
|
109
|
+
return err;
|
|
110
|
+
}
|
|
111
|
+
visiting.delete(name);
|
|
112
|
+
visited.add(name);
|
|
113
|
+
order.push(name);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
for (const name of names) {
|
|
117
|
+
const err = visit(name);
|
|
118
|
+
if (err !== null) {
|
|
119
|
+
return { valid: false, reason: err };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { valid: true, order };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Replaces every `${name}.port` in `env`'s values with the resolved port
|
|
126
|
+
* for `name`. Assumes `validateServicesConfiguration` already confirmed
|
|
127
|
+
* every referenced name is a declared dependency that the caller resolves
|
|
128
|
+
* earlier in the same spawn order — a reference this function can't
|
|
129
|
+
* resolve is left untouched rather than silently dropped, so a caller that
|
|
130
|
+
* skipped validation sees an obviously-wrong literal `${...}` instead of a
|
|
131
|
+
* silently empty substitution.
|
|
132
|
+
*/
|
|
133
|
+
export function substitutePortPlaceholders(env, resolvedPorts) {
|
|
134
|
+
const result = {};
|
|
135
|
+
for (const [key, value] of Object.entries(env ?? {})) {
|
|
136
|
+
result[key] = value.replace(PORT_PLACEHOLDER, (literal, name) => {
|
|
137
|
+
const port = resolvedPorts.get(name);
|
|
138
|
+
return port === undefined ? literal : String(port);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Shell operators that only mean anything to a shell. `spawnProcess` uses
|
|
145
|
+
* `child_process.spawn` **without** one, deliberately — no implicit shell
|
|
146
|
+
* means no injection surface and no expansion nobody asked for. So a
|
|
147
|
+
* command containing these was never going to behave as written: it would
|
|
148
|
+
* be handed to the program as a literal argument.
|
|
149
|
+
*
|
|
150
|
+
* Refusing is the honest answer. The previous whitespace split passed
|
|
151
|
+
* `node server.mjs > out.log` through as three arguments to `node`, which
|
|
152
|
+
* starts a process that fails in a way pointing at the application rather
|
|
153
|
+
* than at its own configuration. Someone who genuinely wants a shell can
|
|
154
|
+
* say so — `sh -c "..."` tokenizes fine, since the operators are then
|
|
155
|
+
* inside quotes where they belong.
|
|
156
|
+
*/
|
|
157
|
+
const SHELL_OPERATORS = new Set(["|", "&", ";", "<", ">", "`", "$"]);
|
|
158
|
+
/**
|
|
159
|
+
* POSIX-style tokenizer for `ServiceConfiguration.command`: single quotes
|
|
160
|
+
* (literal throughout), double quotes (with `\` escaping `"` and `\`), and
|
|
161
|
+
* backslash escapes outside quotes. `spawnProcess` takes `command`/`args`
|
|
162
|
+
* separately while configuration carries one string (plan §6's YAML shape),
|
|
163
|
+
* so something has to split it, and doing that by whitespace alone breaks
|
|
164
|
+
* every path with a space in it — `"C:\Program Files\node.exe"` and
|
|
165
|
+
* `python3 -c "import app; app.main()"` both tokenized wrong before.
|
|
166
|
+
*
|
|
167
|
+
* **Refuses rather than guesses** on an unterminated quote, a trailing
|
|
168
|
+
* backslash, or an unquoted shell operator. Each of those has no correct
|
|
169
|
+
* interpretation without a shell, and returning a plausible tokenization
|
|
170
|
+
* for input that cannot mean what it says is how a configuration error
|
|
171
|
+
* arrives disguised as an application failure.
|
|
172
|
+
*
|
|
173
|
+
* Globs and `~` are *not* refused: they pass through literally, which is
|
|
174
|
+
* exactly what happens with no shell, and unlike the operators above they
|
|
175
|
+
* are frequently intended literally.
|
|
176
|
+
*/
|
|
177
|
+
export function tokenizeCommand(raw) {
|
|
178
|
+
const tokens = [];
|
|
179
|
+
let current = "";
|
|
180
|
+
let started = false; // distinguishes an empty quoted token ('') from no token
|
|
181
|
+
let quote = null;
|
|
182
|
+
for (let i = 0; i < raw.length; i++) {
|
|
183
|
+
const char = raw[i];
|
|
184
|
+
if (quote === "'") {
|
|
185
|
+
if (char === "'")
|
|
186
|
+
quote = null;
|
|
187
|
+
else
|
|
188
|
+
current += char;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (quote === '"') {
|
|
192
|
+
if (char === "\\") {
|
|
193
|
+
const next = raw[i + 1];
|
|
194
|
+
if (next === undefined)
|
|
195
|
+
return { ok: false, reason: 'command ends with a trailing backslash inside a double-quoted string, which escapes nothing' };
|
|
196
|
+
// Only `"` and `\` are escapable inside double quotes; anything
|
|
197
|
+
// else keeps the backslash, as a shell would.
|
|
198
|
+
if (next === '"' || next === "\\") {
|
|
199
|
+
current += next;
|
|
200
|
+
i++;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
current += char;
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (char === '"')
|
|
208
|
+
quote = null;
|
|
209
|
+
else
|
|
210
|
+
current += char;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (char === "'" || char === '"') {
|
|
214
|
+
quote = char;
|
|
215
|
+
started = true;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (char === "\\") {
|
|
219
|
+
const next = raw[i + 1];
|
|
220
|
+
if (next === undefined)
|
|
221
|
+
return { ok: false, reason: "command ends with a trailing backslash, which escapes nothing" };
|
|
222
|
+
current += next;
|
|
223
|
+
started = true;
|
|
224
|
+
i++;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (SHELL_OPERATORS.has(char)) {
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
reason: `command contains the shell operator "${char}" outside quotes, but processes are spawned without a shell — ` +
|
|
231
|
+
`it would be passed to the program as a literal argument rather than interpreted. ` +
|
|
232
|
+
`Use an explicit shell (sh -c "...") if that is what you meant.`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (/\s/.test(char)) {
|
|
236
|
+
if (started) {
|
|
237
|
+
tokens.push(current);
|
|
238
|
+
current = "";
|
|
239
|
+
started = false;
|
|
240
|
+
}
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
current += char;
|
|
244
|
+
started = true;
|
|
245
|
+
}
|
|
246
|
+
if (quote !== null)
|
|
247
|
+
return { ok: false, reason: `command has an unterminated ${quote === '"' ? "double" : "single"} quote` };
|
|
248
|
+
if (started)
|
|
249
|
+
tokens.push(current);
|
|
250
|
+
const [head, ...rest] = tokens;
|
|
251
|
+
if (head === undefined || head === "")
|
|
252
|
+
return { ok: false, reason: "command is empty" };
|
|
253
|
+
return { ok: true, command: head, args: rest };
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=orchestration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestration.js","sourceRoot":"","sources":["../src/orchestration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ProcessCollector`: turns `ProcessLifecycleEvent` (`controller.ts`) into
|
|
3
|
+
* `PROCESS_STARTED` / `PROCESS_READY` / `PROCESS_EXITED` Evidence.
|
|
4
|
+
*
|
|
5
|
+
* `backend-observation/collector.ts` named this a non-goal of `LogCollector`
|
|
6
|
+
* — "whether it becomes Evidence at all is an orchestration-layer question"
|
|
7
|
+
* — and it stayed unbuilt long enough that all three event types were
|
|
8
|
+
* declared in `contracts/src/runtime-event.ts` and emitted by nothing
|
|
9
|
+
* (RUNTIME-CHECKLIST.md §3). This is that orchestration-layer answer: a
|
|
10
|
+
* plain translator with no lifecycle logic of its own. `ExecutionController`
|
|
11
|
+
* decides *when* each transition happens (spawn, readiness settling either
|
|
12
|
+
* way, reap) and calls `RunOptions.onProcessLifecycleEvent` at exactly
|
|
13
|
+
* those moments; this collector's only job is turning one of those calls
|
|
14
|
+
* into one `context.emit()` call. It does not spawn anything, does not
|
|
15
|
+
* decide readiness, and does not know why a process exited — only that it
|
|
16
|
+
* did, and what `ProcessHandle` says about it.
|
|
17
|
+
*
|
|
18
|
+
* **Wiring order is load-bearing.** `start(context)` must be called before
|
|
19
|
+
* `RunOptions.onProcessLifecycleEvent` can fire — i.e. before
|
|
20
|
+
* `ExecutionController.run()` is called with this collector's
|
|
21
|
+
* `handleLifecycleEvent` wired in. `handleLifecycleEvent` throws rather
|
|
22
|
+
* than silently dropping an event if that order is violated: a lost
|
|
23
|
+
* PROCESS_STARTED reads to everything downstream as "this process was
|
|
24
|
+
* never observed," which is a worse and quieter failure than a thrown
|
|
25
|
+
* error at the exact call site that got the order wrong.
|
|
26
|
+
*/
|
|
27
|
+
import type { Collector } from "@descryy/runtime-contracts";
|
|
28
|
+
import type { ProcessLifecycleEvent } from "./controller.ts";
|
|
29
|
+
export interface ProcessCollector extends Collector {
|
|
30
|
+
/** Wire as `RunOptions.onProcessLifecycleEvent` — see the module doc for the ordering this depends on. */
|
|
31
|
+
handleLifecycleEvent(event: ProcessLifecycleEvent): void;
|
|
32
|
+
}
|
|
33
|
+
export declare function createProcessCollector(): ProcessCollector;
|
|
34
|
+
//# sourceMappingURL=process-collector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-collector.d.ts","sourceRoot":"","sources":["../src/process-collector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAmF,MAAM,4BAA4B,CAAC;AAC7I,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG7D,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,0GAA0G;IAC1G,oBAAoB,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,CAAC;CAC1D;AA4ED,wBAAgB,sBAAsB,IAAI,gBAAgB,CAuDzD"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ProcessCollector`: turns `ProcessLifecycleEvent` (`controller.ts`) into
|
|
3
|
+
* `PROCESS_STARTED` / `PROCESS_READY` / `PROCESS_EXITED` Evidence.
|
|
4
|
+
*
|
|
5
|
+
* `backend-observation/collector.ts` named this a non-goal of `LogCollector`
|
|
6
|
+
* — "whether it becomes Evidence at all is an orchestration-layer question"
|
|
7
|
+
* — and it stayed unbuilt long enough that all three event types were
|
|
8
|
+
* declared in `contracts/src/runtime-event.ts` and emitted by nothing
|
|
9
|
+
* (RUNTIME-CHECKLIST.md §3). This is that orchestration-layer answer: a
|
|
10
|
+
* plain translator with no lifecycle logic of its own. `ExecutionController`
|
|
11
|
+
* decides *when* each transition happens (spawn, readiness settling either
|
|
12
|
+
* way, reap) and calls `RunOptions.onProcessLifecycleEvent` at exactly
|
|
13
|
+
* those moments; this collector's only job is turning one of those calls
|
|
14
|
+
* into one `context.emit()` call. It does not spawn anything, does not
|
|
15
|
+
* decide readiness, and does not know why a process exited — only that it
|
|
16
|
+
* did, and what `ProcessHandle` says about it.
|
|
17
|
+
*
|
|
18
|
+
* **Wiring order is load-bearing.** `start(context)` must be called before
|
|
19
|
+
* `RunOptions.onProcessLifecycleEvent` can fire — i.e. before
|
|
20
|
+
* `ExecutionController.run()` is called with this collector's
|
|
21
|
+
* `handleLifecycleEvent` wired in. `handleLifecycleEvent` throws rather
|
|
22
|
+
* than silently dropping an event if that order is violated: a lost
|
|
23
|
+
* PROCESS_STARTED reads to everything downstream as "this process was
|
|
24
|
+
* never observed," which is a worse and quieter failure than a thrown
|
|
25
|
+
* error at the exact call site that got the order wrong.
|
|
26
|
+
*/
|
|
27
|
+
import { COLLECTOR_VERSION } from "./collector-version.js";
|
|
28
|
+
/**
|
|
29
|
+
* All seven `CollectorCapabilities` fields describe DOM/console/network/
|
|
30
|
+
* backend-log/trace/source-mapping/stack-capture observation — none of them
|
|
31
|
+
* is "process lifecycle observation," which is the one thing this collector
|
|
32
|
+
* actually does. There is no eighth field to report it as available in.
|
|
33
|
+
* Reported `unavailable` on all seven, each with its own honest reason,
|
|
34
|
+
* rather than inventing a field this package doesn't own (`contracts` is
|
|
35
|
+
* outside this lane's boundary this round — flagged to the peer rather than
|
|
36
|
+
* changed unilaterally). A collector that is doing its one real job while
|
|
37
|
+
* `capabilities()` shows nothing but `unavailable` is itself a disclosed
|
|
38
|
+
* gap in the capability model, not a bug in this file.
|
|
39
|
+
*/
|
|
40
|
+
function computeCapabilities() {
|
|
41
|
+
const reason = "ProcessCollector observes process lifecycle (started/ready/exited) only — the capability model has no field for that yet.";
|
|
42
|
+
const status = { availability: "unavailable", reason };
|
|
43
|
+
return {
|
|
44
|
+
domObservation: status,
|
|
45
|
+
consoleObservation: status,
|
|
46
|
+
networkObservation: status,
|
|
47
|
+
backendLogAccess: status,
|
|
48
|
+
distributedTrace: status,
|
|
49
|
+
sourceMapping: status,
|
|
50
|
+
stackCapture: status,
|
|
51
|
+
processLifecycle: { availability: "available", reason: null },
|
|
52
|
+
databaseObservation: status,
|
|
53
|
+
externalServiceObservation: status,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function eventTypeOf(kind) {
|
|
57
|
+
switch (kind) {
|
|
58
|
+
case "process-started":
|
|
59
|
+
return "PROCESS_STARTED";
|
|
60
|
+
case "process-ready":
|
|
61
|
+
case "process-ready-failed":
|
|
62
|
+
return "PROCESS_READY";
|
|
63
|
+
case "process-exited":
|
|
64
|
+
return "PROCESS_EXITED";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* `process-ready-failed` still becomes a `PROCESS_READY`-typed Evidence
|
|
69
|
+
* item — the vocabulary has no separate "readiness failed" type, and
|
|
70
|
+
* inventing one here would be a runtime-event vocabulary change made
|
|
71
|
+
* unilaterally in a collector, the wrong layer for it. The failure is not
|
|
72
|
+
* lost: `payload.ready` is `false` and `payload.readiness` carries the full
|
|
73
|
+
* `ReadinessResult` (mechanism attempted, elapsed time, reason), so a
|
|
74
|
+
* reader distinguishes "became ready" from "readiness failed" from the
|
|
75
|
+
* payload, not the event type — the same shape `ServiceStartResult`
|
|
76
|
+
* already uses (`succeeded: boolean` alongside a `stage`), not a new one.
|
|
77
|
+
*/
|
|
78
|
+
function payloadOf(event) {
|
|
79
|
+
switch (event.kind) {
|
|
80
|
+
case "process-started":
|
|
81
|
+
return { processId: event.handle.processId, serviceName: event.serviceName, pid: event.handle.pid, command: event.handle.command, startedAt: event.handle.startedAt };
|
|
82
|
+
case "process-ready":
|
|
83
|
+
case "process-ready-failed":
|
|
84
|
+
return { processId: event.handle.processId, serviceName: event.serviceName, ready: event.readiness.ready, readiness: event.readiness };
|
|
85
|
+
case "process-exited":
|
|
86
|
+
// `phase` is what lets a reader tell "crashed on boot" from
|
|
87
|
+
// "finished/was stopped after running successfully" — see
|
|
88
|
+
// `ProcessLifecycleEvent`'s own doc comment in controller.ts.
|
|
89
|
+
return {
|
|
90
|
+
processId: event.handle.processId,
|
|
91
|
+
serviceName: event.serviceName,
|
|
92
|
+
exitCode: event.handle.exitCode,
|
|
93
|
+
signal: event.handle.signal,
|
|
94
|
+
exitedAt: event.handle.exitedAt,
|
|
95
|
+
phase: event.phase,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export function createProcessCollector() {
|
|
100
|
+
const capabilities = computeCapabilities();
|
|
101
|
+
let context = null;
|
|
102
|
+
return {
|
|
103
|
+
collectorId: "process-collector",
|
|
104
|
+
start(ctx) {
|
|
105
|
+
context = ctx;
|
|
106
|
+
return Promise.resolve({ available: true });
|
|
107
|
+
},
|
|
108
|
+
stop() {
|
|
109
|
+
// Idempotent per the Collector contract; nothing held to release —
|
|
110
|
+
// this collector owns no process, socket, or file handle of its own,
|
|
111
|
+
// only a reference to the context it emits through.
|
|
112
|
+
context = null;
|
|
113
|
+
return Promise.resolve();
|
|
114
|
+
},
|
|
115
|
+
capabilities() {
|
|
116
|
+
return capabilities;
|
|
117
|
+
},
|
|
118
|
+
handleLifecycleEvent(event) {
|
|
119
|
+
if (context === null) {
|
|
120
|
+
throw new Error(`ProcessCollector.handleLifecycleEvent("${event.kind}") called before start() — ` +
|
|
121
|
+
`wire handleLifecycleEvent as RunOptions.onProcessLifecycleEvent only after start() has resolved, ` +
|
|
122
|
+
`or this event (and everything downstream of it) is silently unobserved rather than loudly wrong.`);
|
|
123
|
+
}
|
|
124
|
+
context.emit({
|
|
125
|
+
timestamp: new Date().toISOString(),
|
|
126
|
+
source: event.kind === "process-exited" ? "process-exit" : "backend-process",
|
|
127
|
+
service: event.serviceName,
|
|
128
|
+
process: event.handle.processId,
|
|
129
|
+
eventType: eventTypeOf(event.kind),
|
|
130
|
+
payload: payloadOf(event),
|
|
131
|
+
traceId: null,
|
|
132
|
+
requestId: null,
|
|
133
|
+
correlationId: null,
|
|
134
|
+
graphNodeId: null,
|
|
135
|
+
sourceLocation: null,
|
|
136
|
+
stackTrace: null,
|
|
137
|
+
// A direct observation of our own orchestration state (we spawned
|
|
138
|
+
// it, we polled its readiness, we reaped it) — never inferred or
|
|
139
|
+
// guessed, same basis LogCollector already uses for its own
|
|
140
|
+
// verbatim-capture confidence of 1.
|
|
141
|
+
confidence: 1,
|
|
142
|
+
redactionStatus: "pending-redaction",
|
|
143
|
+
collectorVersion: COLLECTOR_VERSION,
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=process-collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-collector.js","sourceRoot":"","sources":["../src/process-collector.ts"],"names":[],"mappings":"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"}
|