@a11ign/screenreader-fleet 0.5.2 → 0.6.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/README.md +37 -0
- package/dist/{capture-client.d.mts → capture-client.d.ts} +2 -2
- package/dist/{check-worker-code.d.mts → check-worker-code.d.ts} +2 -3
- package/dist/check-worker-code.mjs +1 -1
- package/dist/{cli-flags.d.mts → cli-flags.d.ts} +5 -5
- package/dist/{code-drift.d.mts → code-drift.d.ts} +7 -7
- package/dist/{command-line-census.d.mts → command-line-census.d.ts} +3 -3
- package/dist/{compare-workers.d.mts → compare-workers.d.ts} +1 -1
- package/dist/{control-plane-isolation.d.mts → control-plane-isolation.d.ts} +1 -1
- package/dist/{doctor.d.mts → doctor.d.ts} +57 -31
- package/dist/doctor.mjs +4 -4
- package/dist/{fleet-consistency.d.mts → fleet-consistency.d.ts} +50 -85
- package/dist/fleet-consistency.mjs +4 -0
- package/dist/{fleet-env.d.mts → fleet-env.d.ts} +45 -45
- package/dist/{fleet-scripts.d.mts → fleet-scripts.d.ts} +1 -1
- package/dist/{git-safe-env.d.mts → git-safe-env.d.ts} +3 -3
- package/dist/{guest-run.d.mts → guest-run.d.ts} +5 -5
- package/dist/{host-address.d.mts → host-address.d.ts} +4 -4
- package/dist/{host-capacity.d.mts → host-capacity.d.ts} +5 -5
- package/dist/{host-metrics.d.mts → host-metrics.d.ts} +23 -15
- package/dist/index.mjs +1 -1
- package/dist/{measure-guard.d.mts → measure-guard.d.ts} +14 -14
- package/dist/{npm-cli-executable.d.mts → npm-cli-executable.d.ts} +4 -4
- package/dist/{probe-outcome.d.mts → probe-outcome.d.ts} +43 -44
- package/dist/{protocol-guard.d.mts → protocol-guard.d.ts} +3 -3
- package/dist/source-walk.d.ts +11 -0
- package/dist/{transient-fault.d.mts → transient-fault.d.ts} +1 -1
- package/dist/tsx-import.d.ts +6 -0
- package/dist/{utm-deprecated.d.mts → utm-deprecated.d.ts} +1 -1
- package/dist/worker-code-check.d.ts +95 -0
- package/dist/worker-code-check.mjs +1 -1
- package/dist/{worker-health.d.mts → worker-health.d.ts} +2 -2
- package/dist/{worker-http.d.mts → worker-http.d.ts} +51 -51
- package/dist/{worker-stats.d.mts → worker-stats.d.ts} +40 -20
- package/package.json +30 -19
- package/src/local-worker/build-vm.sh +2 -2
- package/src/local-worker/clone-worker.sh +1 -1
- package/src/local-worker/create-utm-vm.sh +1 -1
- package/src/local-worker/fetch-windows-iso.sh +5 -5
- package/src/local-worker/worker-ctl.sh +12 -12
- package/src/provisioning/apply-foreground-lock-timeout.ps1 +1 -1
- package/src/provisioning/bootstrap-windows-worker.ps1 +1 -1
- package/src/provisioning/diagnose-nvda-worker.ps1 +1 -1
- package/src/provisioning/stamp-provision-revision.ps1 +3 -3
- package/dist/source-walk.d.mts +0 -11
- package/dist/worker-code-check.d.mts +0 -54
- /package/dist/{normalise-fleet.d.mts → normalise-fleet.d.ts} +0 -0
- /package/dist/{src_fleet-scripts_mjs.mjs → src_fleet-scripts_ts.mjs} +0 -0
- /package/dist/{src_git-safe-env_mjs.mjs → src_git-safe-env_ts.mjs} +0 -0
|
@@ -52,6 +52,10 @@ const REPORTED_ONLY = [
|
|
|
52
52
|
{
|
|
53
53
|
path: "displayAdapter",
|
|
54
54
|
why: "the adapter is what decides whether a pinned display mode can be held at all -- on 2026-09-22 workers 7-11 were on Microsoft's Basic Display Adapter and captured at 640x480 under a `provisionRevision` identical to their peers' (#1955); the pinned Intel driver install resolved that, and all ten read an Intel adapter at CM_PROB_NONE on 2026-09-23. NOT a gate, and no longer because nobody reports it: 10 of 10 guests report it since 2026-09-23T18:02Z, reading `Intel(R) UHD Graphics 630` on nine and `Intel(R) HD Graphics 630` on one as of 2026-09-23. That single letter is a real hardware difference rather than a driver fallback, so no provisioning run can converge it and a gate would refuse that guest for ever (#2063, reading on #2170)"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
path: "windowsBuild",
|
|
58
|
+
why: "two boxes one cumulative update apart share a `windowsVersion` and are not the same Windows; the revision (`CurrentBuild.UBR`) is what tells them apart. NOT a gate yet: it graduates to MUST_MATCH once the fleet reads one value (#4433, #4405)"
|
|
55
59
|
}
|
|
56
60
|
];
|
|
57
61
|
const POLICY_MUST_MATCH = [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const DEFAULT_WORKER_PORT = 8765;
|
|
1
2
|
/**
|
|
2
3
|
* The fleet named by the environment — ONE parser, because there were three that did not agree.
|
|
3
4
|
*
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
*
|
|
17
18
|
* @returns {Array<{ name: string, url: string }>}
|
|
18
19
|
*/
|
|
19
|
-
export function configuredWorkers(): Array<{
|
|
20
|
+
export declare function configuredWorkers(): Array<{
|
|
20
21
|
name: string;
|
|
21
22
|
url: string;
|
|
22
23
|
}>;
|
|
@@ -34,13 +35,46 @@ export function configuredWorkers(): Array<{
|
|
|
34
35
|
* @param {{ argv?: readonly string[], baseDir?: string }} [options]
|
|
35
36
|
* @returns {{ inventoryPath: string, groupVarsPath: string }}
|
|
36
37
|
*/
|
|
37
|
-
export function inventoryPathsFor({ argv, baseDir }?: {
|
|
38
|
+
export declare function inventoryPathsFor({ argv, baseDir }?: {
|
|
38
39
|
argv?: readonly string[];
|
|
39
40
|
baseDir?: string;
|
|
40
41
|
}): {
|
|
41
42
|
inventoryPath: string;
|
|
42
43
|
groupVarsPath: string;
|
|
43
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* The inventory group whose hosts are capture workers.
|
|
47
|
+
*
|
|
48
|
+
* This reader was GROUPLESS until 2026-08-21, and that was a live hazard rather than an untidiness: every
|
|
49
|
+
* `ansible_host:` in the file became `http://<addr>:8765`, so adding any non-worker host to `inventory.yml`
|
|
50
|
+
* -- the lab container, the control container, a switch -- would have silently added a phantom worker to
|
|
51
|
+
* `A11Y_WORKERS`, and a run would have dispatched capture cases to it. That is the exact failure this
|
|
52
|
+
* module's own header says it exists to prevent ("dispatched to but never updated", "both of those are
|
|
53
|
+
* silent"), arriving through the door nobody had shut.
|
|
54
|
+
*/
|
|
55
|
+
export declare const WORKER_GROUP = "a11y_workers";
|
|
56
|
+
/**
|
|
57
|
+
* One frame of the indentation stack: a key and the column it started at.
|
|
58
|
+
*/
|
|
59
|
+
export type Frame = {
|
|
60
|
+
indent: number;
|
|
61
|
+
key: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* A host as the inventory declares it — the ADDRESS and the NAME together.
|
|
65
|
+
*
|
|
66
|
+
* They travel as one value because separating them is what sent `fleet:sleep` at the wrong machine: every
|
|
67
|
+
* tool that ACTS on a worker takes the name, every tool that REPORTS on one printed the address, and
|
|
68
|
+
* nothing mapped between them. `collectHost` explains the incident.
|
|
69
|
+
*
|
|
70
|
+
* `capture` is false for a host that declares `a11y_capture: false`: enrolled, served, deployed to, and not
|
|
71
|
+
* handed corpus cases.
|
|
72
|
+
*/
|
|
73
|
+
export type Host = {
|
|
74
|
+
name: string | undefined;
|
|
75
|
+
host: string;
|
|
76
|
+
capture: boolean;
|
|
77
|
+
};
|
|
44
78
|
/**
|
|
45
79
|
* Which group each line of the inventory sits in, index-aligned with `text.split(/\r?\n/)`.
|
|
46
80
|
*
|
|
@@ -53,7 +87,7 @@ export function inventoryPathsFor({ argv, baseDir }?: {
|
|
|
53
87
|
* @param {string} text
|
|
54
88
|
* @returns {Array<string | undefined>}
|
|
55
89
|
*/
|
|
56
|
-
export function groupPerLine(text: string): Array<string | undefined>;
|
|
90
|
+
export declare function groupPerLine(text: string): Array<string | undefined>;
|
|
57
91
|
/**
|
|
58
92
|
* Worker URLs from the text of an inventory file.
|
|
59
93
|
*
|
|
@@ -70,7 +104,7 @@ export function groupPerLine(text: string): Array<string | undefined>;
|
|
|
70
104
|
* @param {{ port?: number, group?: string, scope?: "fleet" | "capture" }} [options]
|
|
71
105
|
* @returns {string[]}
|
|
72
106
|
*/
|
|
73
|
-
export function workersFromInventory(text: string, { port, group, scope }?: {
|
|
107
|
+
export declare function workersFromInventory(text: string, { port, group, scope }?: {
|
|
74
108
|
port?: number;
|
|
75
109
|
group?: string;
|
|
76
110
|
scope?: "fleet" | "capture";
|
|
@@ -82,7 +116,7 @@ export function workersFromInventory(text: string, { port, group, scope }?: {
|
|
|
82
116
|
* @param {{ port?: number, group?: string }} [options]
|
|
83
117
|
* @returns {{name: string, url: string}[]}
|
|
84
118
|
*/
|
|
85
|
-
export function hostsOutOfCaptureSet(text: string, { port, group }?: {
|
|
119
|
+
export declare function hostsOutOfCaptureSet(text: string, { port, group }?: {
|
|
86
120
|
port?: number;
|
|
87
121
|
group?: string;
|
|
88
122
|
}): {
|
|
@@ -99,13 +133,13 @@ export function hostsOutOfCaptureSet(text: string, { port, group }?: {
|
|
|
99
133
|
* @param {{ port?: number, group?: string }} [options]
|
|
100
134
|
* @returns {Record<string, string>}
|
|
101
135
|
*/
|
|
102
|
-
export function workerNamesFromInventory(text: string, { port, group }?: {
|
|
136
|
+
export declare function workerNamesFromInventory(text: string, { port, group }?: {
|
|
103
137
|
port?: number;
|
|
104
138
|
group?: string;
|
|
105
139
|
}): Record<string, string>;
|
|
106
140
|
/** The port the group vars declare, so it is stated once and not guessed here. */
|
|
107
141
|
/** @param {string} text @returns {number} */
|
|
108
|
-
export function portFromGroupVars(text: string): number;
|
|
142
|
+
export declare function portFromGroupVars(text: string): number;
|
|
109
143
|
/**
|
|
110
144
|
* The workers declared in `inventory.yml` — i.e. the BARE-METAL fleet.
|
|
111
145
|
*
|
|
@@ -122,7 +156,7 @@ export function portFromGroupVars(text: string): number;
|
|
|
122
156
|
*
|
|
123
157
|
* @param {{ inventoryPath?: string, groupVarsPath?: string }} [paths]
|
|
124
158
|
*/
|
|
125
|
-
export function inventoryWorkerUrls({ inventoryPath, groupVarsPath }?: {
|
|
159
|
+
export declare function inventoryWorkerUrls({ inventoryPath, groupVarsPath }?: {
|
|
126
160
|
inventoryPath?: string;
|
|
127
161
|
groupVarsPath?: string;
|
|
128
162
|
}): string[];
|
|
@@ -142,7 +176,7 @@ export function inventoryWorkerUrls({ inventoryPath, groupVarsPath }?: {
|
|
|
142
176
|
* @param {{ inventoryPath?: string, groupVarsPath?: string }} [paths]
|
|
143
177
|
* @returns {{name: string, url: string}[]} empty when no inventory is declared, like `inventoryWorkerUrls`
|
|
144
178
|
*/
|
|
145
|
-
export function namedInventoryWorkers({ inventoryPath, groupVarsPath }?: {
|
|
179
|
+
export declare function namedInventoryWorkers({ inventoryPath, groupVarsPath }?: {
|
|
146
180
|
inventoryPath?: string;
|
|
147
181
|
groupVarsPath?: string;
|
|
148
182
|
}): {
|
|
@@ -183,7 +217,7 @@ export function namedInventoryWorkers({ inventoryPath, groupVarsPath }?: {
|
|
|
183
217
|
* @param {{ named?: () => {url: string}[], inventory?: () => string[], local?: () => string[] }} readers
|
|
184
218
|
* @returns {{ urls: string[], source: string }}
|
|
185
219
|
*/
|
|
186
|
-
export function resolveWorkerPool({ named, inventory, local, }?: {
|
|
220
|
+
export declare function resolveWorkerPool({ named, inventory, local, }?: {
|
|
187
221
|
named?: () => {
|
|
188
222
|
url: string;
|
|
189
223
|
}[];
|
|
@@ -205,44 +239,10 @@ export function resolveWorkerPool({ named, inventory, local, }?: {
|
|
|
205
239
|
* @param {{ port?: number, mode?: "env" | "list" }} [options]
|
|
206
240
|
* @returns {{stdout: string, stderr: string}}
|
|
207
241
|
*/
|
|
208
|
-
export function fleetEnvOutput(text: string, { port, mode }?: {
|
|
242
|
+
export declare function fleetEnvOutput(text: string, { port, mode }?: {
|
|
209
243
|
port?: number;
|
|
210
244
|
mode?: "env" | "list";
|
|
211
245
|
}): {
|
|
212
246
|
stdout: string;
|
|
213
247
|
stderr: string;
|
|
214
248
|
};
|
|
215
|
-
export const DEFAULT_WORKER_PORT: 8765;
|
|
216
|
-
/**
|
|
217
|
-
* The inventory group whose hosts are capture workers.
|
|
218
|
-
*
|
|
219
|
-
* This reader was GROUPLESS until 2026-08-21, and that was a live hazard rather than an untidiness: every
|
|
220
|
-
* `ansible_host:` in the file became `http://<addr>:8765`, so adding any non-worker host to `inventory.yml`
|
|
221
|
-
* -- the lab container, the control container, a switch -- would have silently added a phantom worker to
|
|
222
|
-
* `A11Y_WORKERS`, and a run would have dispatched capture cases to it. That is the exact failure this
|
|
223
|
-
* module's own header says it exists to prevent ("dispatched to but never updated", "both of those are
|
|
224
|
-
* silent"), arriving through the door nobody had shut.
|
|
225
|
-
*/
|
|
226
|
-
export const WORKER_GROUP: "a11y_workers";
|
|
227
|
-
/**
|
|
228
|
-
* One frame of the indentation stack: a key and the column it started at.
|
|
229
|
-
*/
|
|
230
|
-
export type Frame = {
|
|
231
|
-
indent: number;
|
|
232
|
-
key: string;
|
|
233
|
-
};
|
|
234
|
-
/**
|
|
235
|
-
* A host as the inventory declares it — the ADDRESS and the NAME together.
|
|
236
|
-
*
|
|
237
|
-
* They travel as one value because separating them is what sent `fleet:sleep` at the wrong machine: every
|
|
238
|
-
* tool that ACTS on a worker takes the name, every tool that REPORTS on one printed the address, and
|
|
239
|
-
* nothing mapped between them. `collectHost` explains the incident.
|
|
240
|
-
*
|
|
241
|
-
* `capture` is false for a host that declares `a11y_capture: false`: enrolled, served, deployed to, and not
|
|
242
|
-
* handed corpus cases.
|
|
243
|
-
*/
|
|
244
|
-
export type Host = {
|
|
245
|
-
name: string | undefined;
|
|
246
|
-
host: string;
|
|
247
|
-
capture: boolean;
|
|
248
|
-
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
/** @type {readonly string[]} */
|
|
2
|
+
export declare const KNOWN_GIT_REDIRECT_VARS: readonly string[];
|
|
1
3
|
/**
|
|
2
4
|
* `process.env` with every `GIT_*` key removed, `extra` applied on top. A prefix strip, never a list
|
|
3
5
|
* lookup, so a new `GIT_*` variable is caught without this file needing to know its name.
|
|
4
6
|
* @param {Record<string, string>} [extra]
|
|
5
7
|
* @returns {Record<string, string | undefined>}
|
|
6
8
|
*/
|
|
7
|
-
export function sandboxGitEnv(extra?: Record<string, string>): Record<string, string | undefined>;
|
|
8
|
-
/** @type {readonly string[]} */
|
|
9
|
-
export const KNOWN_GIT_REDIRECT_VARS: readonly string[];
|
|
9
|
+
export declare function sandboxGitEnv(extra?: Record<string, string>): Record<string, string | undefined>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Written by the wrapper as its last act. Absent means still running, or it died. */
|
|
2
|
+
export declare const DONE_SENTINEL = "---GUEST-RUN-DONE---";
|
|
1
3
|
/**
|
|
2
4
|
* The cmd line that registers and starts the elevated task.
|
|
3
5
|
*
|
|
@@ -5,7 +7,7 @@
|
|
|
5
7
|
* a guest, because it is the part that silently produces wrong answers rather than errors.
|
|
6
8
|
*/
|
|
7
9
|
/** @param {{ scriptPath: string, taskName?: string }} where */
|
|
8
|
-
export function scheduleCommand({ scriptPath, taskName }: {
|
|
10
|
+
export declare function scheduleCommand({ scriptPath, taskName }: {
|
|
9
11
|
scriptPath: string;
|
|
10
12
|
taskName?: string;
|
|
11
13
|
}): string;
|
|
@@ -16,10 +18,8 @@ export function scheduleCommand({ scriptPath, taskName }: {
|
|
|
16
18
|
* exactly how a trim that crashed immediately looked identical to a trim in progress, for three boots.
|
|
17
19
|
*/
|
|
18
20
|
/** @param {string} body @param {string} outputFile */
|
|
19
|
-
export function wrapScript(body: string, outputFile: string): string;
|
|
21
|
+
export declare function wrapScript(body: string, outputFile: string): string;
|
|
20
22
|
/** Did the run finish? Exported because "no sentinel" and "no file" are different failures. */
|
|
21
23
|
/** @param {string} output */
|
|
22
24
|
/** @param {string | null} output */
|
|
23
|
-
export function isComplete(output: string | null): boolean;
|
|
24
|
-
/** Written by the wrapper as its last act. Absent means still running, or it died. */
|
|
25
|
-
export const DONE_SENTINEL: "---GUEST-RUN-DONE---";
|
|
25
|
+
export declare function isComplete(output: string | null): boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Dotted quad to a comparable integer, or null if it is not one (a hostname, IPv6, nonsense). */
|
|
2
2
|
/** @param {string} ip */
|
|
3
|
-
export function ipv4ToInt(ip: string): number | null;
|
|
3
|
+
export declare function ipv4ToInt(ip: string): number | null;
|
|
4
4
|
/**
|
|
5
5
|
* This host's address on the same subnet as `guestIp`, or undefined if we have no interface onto it.
|
|
6
6
|
*
|
|
@@ -10,14 +10,14 @@ export function ipv4ToInt(ip: string): number | null;
|
|
|
10
10
|
* @param {string} guestIp
|
|
11
11
|
* @returns {string | undefined}
|
|
12
12
|
*/
|
|
13
|
-
export function hostAddressFor(guestIp: string): string | undefined;
|
|
13
|
+
export declare function hostAddressFor(guestIp: string): string | undefined;
|
|
14
14
|
/**
|
|
15
15
|
* This host's address as seen from a worker at `workerUrl`, when the two share a subnet.
|
|
16
16
|
*
|
|
17
17
|
* @param {string} workerUrl
|
|
18
18
|
* @returns {string | undefined}
|
|
19
19
|
*/
|
|
20
|
-
export function hostAddressForWorker(workerUrl: string): string | undefined;
|
|
20
|
+
export declare function hostAddressForWorker(workerUrl: string): string | undefined;
|
|
21
21
|
/**
|
|
22
22
|
* The base URL a worker should use to fetch host-served pages.
|
|
23
23
|
*
|
|
@@ -29,4 +29,4 @@ export function hostAddressForWorker(workerUrl: string): string | undefined;
|
|
|
29
29
|
* @param {number | string} [port]
|
|
30
30
|
* @returns {string}
|
|
31
31
|
*/
|
|
32
|
-
export function hostPagesBase(workerUrl: string, port?: number | string): string;
|
|
32
|
+
export declare function hostPagesBase(workerUrl: string, port?: number | string): string;
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* not stop a run. This codebase already applies that rule to foregroundLockTimeout(): a broken
|
|
10
10
|
* diagnostic taking the pool offline is worse than the fault it looks for.
|
|
11
11
|
*/
|
|
12
|
-
export function availableHostMemoryMb(): number | null;
|
|
12
|
+
export declare function availableHostMemoryMb(): number | null;
|
|
13
13
|
/** Physical RAM in MB. Unlike `os.freemem()` this cannot lie — it is a property of the machine. */
|
|
14
|
-
export function totalHostMemoryMb(): number;
|
|
14
|
+
export declare function totalHostMemoryMb(): number;
|
|
15
15
|
/**
|
|
16
16
|
* The most workers this machine can EVER hold, from physical RAM alone.
|
|
17
17
|
*
|
|
@@ -23,7 +23,7 @@ export function totalHostMemoryMb(): number;
|
|
|
23
23
|
*
|
|
24
24
|
* @returns {number} workers, from physical memory
|
|
25
25
|
*/
|
|
26
|
-
export function workerCeilingFromTotalRam(totalMb?: number): number;
|
|
26
|
+
export declare function workerCeilingFromTotalRam(totalMb?: number): number;
|
|
27
27
|
/**
|
|
28
28
|
* How many workers may be RUNNING at once, given what the host has spare.
|
|
29
29
|
*
|
|
@@ -45,7 +45,7 @@ export function workerCeilingFromTotalRam(totalMb?: number): number;
|
|
|
45
45
|
* @param {{ availableMb: number | null, alreadyRunning: number, totalMb?: number }} host
|
|
46
46
|
* @returns {number}
|
|
47
47
|
*/
|
|
48
|
-
export function workersHostCanRun({ availableMb, alreadyRunning, totalMb }: {
|
|
48
|
+
export declare function workersHostCanRun({ availableMb, alreadyRunning, totalMb }: {
|
|
49
49
|
availableMb: number | null;
|
|
50
50
|
alreadyRunning: number;
|
|
51
51
|
totalMb?: number;
|
|
@@ -56,7 +56,7 @@ export function workersHostCanRun({ availableMb, alreadyRunning, totalMb }: {
|
|
|
56
56
|
* @param {{ limit: number, wanted: number, availableMb: number | null }} cap
|
|
57
57
|
* @returns {string | null}
|
|
58
58
|
*/
|
|
59
|
-
export function capacityReason({ limit, wanted, availableMb }: {
|
|
59
|
+
export declare function capacityReason({ limit, wanted, availableMb }: {
|
|
60
60
|
limit: number;
|
|
61
61
|
wanted: number;
|
|
62
62
|
availableMb: number | null;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @param {string} output
|
|
8
8
|
*/
|
|
9
|
-
export function parseVmStat(output: string): {
|
|
9
|
+
export declare function parseVmStat(output: string): {
|
|
10
10
|
freeMb: number;
|
|
11
11
|
activeMb: number;
|
|
12
12
|
inactiveMb: number;
|
|
@@ -24,7 +24,7 @@ export function parseVmStat(output: string): {
|
|
|
24
24
|
* @param {string} output
|
|
25
25
|
* @returns {Array<{device: string, kbPerTransfer: number, transfersPerSecond: number, mbPerSecond: number}>}
|
|
26
26
|
*/
|
|
27
|
-
export function parseIostat(output: string): Array<{
|
|
27
|
+
export declare function parseIostat(output: string): Array<{
|
|
28
28
|
device: string;
|
|
29
29
|
kbPerTransfer: number;
|
|
30
30
|
transfersPerSecond: number;
|
|
@@ -34,7 +34,7 @@ export function parseIostat(output: string): Array<{
|
|
|
34
34
|
* Load averages, or null.
|
|
35
35
|
* @param {string} output
|
|
36
36
|
*/
|
|
37
|
-
export function parseLoadAverage(output: string): {
|
|
37
|
+
export declare function parseLoadAverage(output: string): {
|
|
38
38
|
one: number;
|
|
39
39
|
five: number;
|
|
40
40
|
fifteen: number;
|
|
@@ -46,7 +46,7 @@ export function parseLoadAverage(output: string): {
|
|
|
46
46
|
* @param {string} psOutput output of `ps -o pid=,rss=,comm=`
|
|
47
47
|
* @param {string} match substring of the command to keep
|
|
48
48
|
*/
|
|
49
|
-
export function parseProcessMemory(psOutput: string, match: string): {
|
|
49
|
+
export declare function parseProcessMemory(psOutput: string, match: string): {
|
|
50
50
|
pid: number;
|
|
51
51
|
residentMb: number;
|
|
52
52
|
command: string;
|
|
@@ -60,7 +60,7 @@ export function parseProcessMemory(psOutput: string, match: string): {
|
|
|
60
60
|
*
|
|
61
61
|
* @param {{ processMatch?: string }} options
|
|
62
62
|
*/
|
|
63
|
-
export function sampleHost({ processMatch }?: {
|
|
63
|
+
export declare function sampleHost({ processMatch }?: {
|
|
64
64
|
processMatch?: string;
|
|
65
65
|
}): {
|
|
66
66
|
at: string;
|
|
@@ -91,18 +91,14 @@ export function sampleHost({ processMatch }?: {
|
|
|
91
91
|
}[];
|
|
92
92
|
};
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
94
|
+
* What changed between two snapshots.
|
|
95
|
+
*
|
|
96
|
+
* Paging is reported as a DELTA because the absolute counters are since-boot and therefore enormous
|
|
97
|
+
* and useless: 6.6 GB of swap left over from an incident hours ago looks identical to a host swapping
|
|
98
|
+
* right now. Only the delta distinguishes them, and that distinction was worth an afternoon.
|
|
96
99
|
*/
|
|
97
|
-
export function diffHost(before: HostSnapshot, after: HostSnapshot): {
|
|
98
|
-
pageoutsDelta: number;
|
|
99
|
-
pageinsDelta: number;
|
|
100
|
-
swappingDuringRun: boolean;
|
|
101
|
-
compressorMb: number | null;
|
|
102
|
-
freeMb: number | null;
|
|
103
|
-
residentMbTotal: number;
|
|
104
|
-
};
|
|
105
100
|
/**
|
|
101
|
+
*
|
|
106
102
|
* Named because the first attempt at annotating this called it `Record<string, number>` -- flat numbers,
|
|
107
103
|
* which is what the CALL SITES read out of it and not what it is. A shape guessed from its uses is the
|
|
108
104
|
* same defect as a type inferred from its defaults, one file over.
|
|
@@ -113,3 +109,15 @@ export type HostSnapshot = {
|
|
|
113
109
|
residentMb: number;
|
|
114
110
|
}[];
|
|
115
111
|
};
|
|
112
|
+
/**
|
|
113
|
+
* @param {HostSnapshot} before
|
|
114
|
+
* @param {HostSnapshot} after
|
|
115
|
+
*/
|
|
116
|
+
export declare function diffHost(before: HostSnapshot, after: HostSnapshot): {
|
|
117
|
+
pageoutsDelta: number;
|
|
118
|
+
pageinsDelta: number;
|
|
119
|
+
swappingDuringRun: boolean;
|
|
120
|
+
compressorMb: number | null;
|
|
121
|
+
freeMb: number | null;
|
|
122
|
+
residentMbTotal: number;
|
|
123
|
+
};
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { execFile } from "node:child_process";
|
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
3
|
import { networkInterfaces } from "node:os";
|
|
4
4
|
import { capacityReason, availableHostMemoryMb, workersHostCanRun } from "./host-capacity.mjs";
|
|
5
|
-
import { fleetScriptPaths as fleet_scripts_fleetScriptPaths } from "./src_fleet-
|
|
5
|
+
import { fleetScriptPaths as fleet_scripts_fleetScriptPaths } from "./src_fleet-scripts_ts.mjs";
|
|
6
6
|
import { inventoryWorkerUrls } from "./fleet-env.mjs";
|
|
7
7
|
function warnUtmDeprecated(what) {
|
|
8
8
|
process.stderr.write(`DEPRECATED: ${what} manages a local UTM worker VM. UTM was a testing path and is not the fleet.\nCapture on the bare-metal fleet instead: npm run fleet:status, npm run fleet:deploy. See CLAUDE.md's\n"Working on a Mac" section.\n`);
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @param {string[]} workers
|
|
3
|
-
* @param {{ what: string, timeoutMs?: number, request?: import("./probe-outcome.
|
|
3
|
+
* @param {{ what: string, timeoutMs?: number, request?: import("./probe-outcome.ts").ProbeRequest }} about
|
|
4
4
|
* `what` names the measurement, so a refusal says what was NOT measured; `timeoutMs` and `request` are injectable
|
|
5
5
|
* @returns {Promise<void>} resolves when every worker is free; throws naming the busy ones
|
|
6
6
|
*/
|
|
7
|
-
export function refuseIfBusy(workers: string[], { what, timeoutMs, request }: {
|
|
7
|
+
export declare function refuseIfBusy(workers: string[], { what, timeoutMs, request }: {
|
|
8
8
|
what: string;
|
|
9
9
|
timeoutMs?: number;
|
|
10
|
-
request?: import("./probe-outcome.
|
|
10
|
+
request?: import("./probe-outcome.js").ProbeRequest;
|
|
11
11
|
}): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* THE VITALS PROBE'S TIMEOUT, WITH ITS READING (#2683). `worker:compare` reads `vitals` before the rounds and again
|
|
14
|
+
* straight AFTER the last capture, so the second read is the LOADED case: a box that has just finished one can take
|
|
15
|
+
* up to about 10 s to answer (#2671, the reading beside `WORKER_PROBE_TIMEOUT_MS`), against 3.09 s for the slowest
|
|
16
|
+
* healthy first-after-idle answer. 20 s is that ceiling twice over, kept from before #2683 because it already clears
|
|
17
|
+
* both. A vitals sample that times out costs the run one column, so there is no reason to give it less.
|
|
18
|
+
*/
|
|
19
|
+
export declare const VITALS_TIMEOUT_MS = 20000;
|
|
12
20
|
/**
|
|
13
21
|
* A worker's `/health` `vitals`, or `null` when there are none to be had, and SAYS WHY through `warn`: a box that did
|
|
14
22
|
* not answer, one that refused, and one that answered without vitals are three different reasons for the same empty
|
|
15
23
|
* column in the report, and the run used to print none of them.
|
|
16
24
|
*
|
|
17
25
|
* @param {string} worker
|
|
18
|
-
* @param {{ timeoutMs?: number, request?: import("./probe-outcome.
|
|
26
|
+
* @param {{ timeoutMs?: number, request?: import("./probe-outcome.ts").ProbeRequest, warn?: (line: string) => void }} [options]
|
|
19
27
|
* @returns {Promise<any>}
|
|
20
28
|
*/
|
|
21
|
-
export function sampleVitals(worker: string, { timeoutMs, request, warn }?: {
|
|
29
|
+
export declare function sampleVitals(worker: string, { timeoutMs, request, warn }?: {
|
|
22
30
|
timeoutMs?: number;
|
|
23
|
-
request?: import("./probe-outcome.
|
|
31
|
+
request?: import("./probe-outcome.js").ProbeRequest;
|
|
24
32
|
warn?: (line: string) => void;
|
|
25
33
|
}): Promise<any>;
|
|
26
|
-
/**
|
|
27
|
-
* THE VITALS PROBE'S TIMEOUT, WITH ITS READING (#2683). `worker:compare` reads `vitals` before the rounds and again
|
|
28
|
-
* straight AFTER the last capture, so the second read is the LOADED case: a box that has just finished one can take
|
|
29
|
-
* up to about 10 s to answer (#2671, the reading beside `WORKER_PROBE_TIMEOUT_MS`), against 3.09 s for the slowest
|
|
30
|
-
* healthy first-after-idle answer. 20 s is that ceiling twice over, kept from before #2683 because it already clears
|
|
31
|
-
* both. A vitals sample that times out costs the run one column, so there is no reason to give it less.
|
|
32
|
-
*/
|
|
33
|
-
export const VITALS_TIMEOUT_MS: 20000;
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
* @param {"npx" | "npm"} name
|
|
3
3
|
* @returns {string[]}
|
|
4
4
|
*/
|
|
5
|
-
export function npmCliScriptCandidates(name: "npx" | "npm"): string[];
|
|
5
|
+
export declare function npmCliScriptCandidates(name: "npx" | "npm"): string[];
|
|
6
6
|
/**
|
|
7
7
|
* @param {"npx" | "npm"} name
|
|
8
8
|
* @returns {string}
|
|
9
9
|
*/
|
|
10
|
-
export function resolveNpmCliScript(name: "npx" | "npm"): string;
|
|
10
|
+
export declare function resolveNpmCliScript(name: "npx" | "npm"): string;
|
|
11
11
|
/**
|
|
12
12
|
* @param {"npx" | "npm"} name
|
|
13
13
|
* @param {string[]} args
|
|
14
14
|
* @returns {{ command: string, args: string[] }}
|
|
15
15
|
*/
|
|
16
|
-
export function npmCliInvocation(name: "npx" | "npm", args: string[]): {
|
|
16
|
+
export declare function npmCliInvocation(name: "npx" | "npm", args: string[]): {
|
|
17
17
|
command: string;
|
|
18
18
|
args: string[];
|
|
19
19
|
};
|
|
@@ -35,7 +35,7 @@ export function npmCliInvocation(name: "npx" | "npm", args: string[]): {
|
|
|
35
35
|
* @param {string[]} args
|
|
36
36
|
* @returns {{ command: string, args: string[] }}
|
|
37
37
|
*/
|
|
38
|
-
export function pnpmCliInvocation(args: string[]): {
|
|
38
|
+
export declare function pnpmCliInvocation(args: string[]): {
|
|
39
39
|
command: string;
|
|
40
40
|
args: string[];
|
|
41
41
|
};
|
|
@@ -1,46 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @param {string} worker the worker's base URL
|
|
3
|
-
* @param {{ timeoutMs?: number, request?: ProbeRequest }} [options]
|
|
4
|
-
* @returns {Promise<Probe>}
|
|
5
|
-
*/
|
|
6
|
-
export function probeHealth(worker: string, { timeoutMs, request }?: {
|
|
7
|
-
timeoutMs?: number;
|
|
8
|
-
request?: ProbeRequest;
|
|
9
|
-
}): Promise<Probe>;
|
|
10
|
-
/**
|
|
11
|
-
* One sentence per outcome, in the words of what was OBSERVED. Only `refused`, `not-ready` and `busy` say the box is
|
|
12
|
-
* up, because only those are things the box itself said; a silent probe says "did not answer" and never "down".
|
|
13
|
-
*
|
|
14
|
-
* @param {Probe} probe
|
|
15
|
-
* @param {{ worker: string, timeoutMs?: number }} about
|
|
16
|
-
* @returns {string}
|
|
17
|
-
*/
|
|
18
|
-
export function describeProbe(probe: Probe, { worker, timeoutMs }: {
|
|
19
|
-
worker: string;
|
|
20
|
-
timeoutMs?: number;
|
|
21
|
-
}): string;
|
|
22
|
-
/** @typedef {typeof requestJson} ProbeRequest */
|
|
23
|
-
/**
|
|
24
|
-
* THE PER-PROBE TIMEOUT, WITH ITS READING. A probe that outlives it is UNKNOWN, never "down", so this number
|
|
25
|
-
* decides how much slowness a healthy box is allowed.
|
|
26
|
-
*
|
|
27
|
-
* All of it READ by others on the real fleet (`orchestrator`, #2671) and none measured by this row, whose
|
|
28
|
-
* engineer is barred from probing it:
|
|
29
|
-
* - the slowest HEALTHY box: 2.80 to 3.09 s on a11y-worker-13, -14 and -16, twelve others 0.53 to 0.76 s;
|
|
30
|
-
* - that is the FIRST answer after the box has been quiet more than 5 s, because the worker rebuilds its
|
|
31
|
-
* environment block with two synchronous `powershell.exe` calls when it is older than that, so a probe made
|
|
32
|
-
* by hand is nearly always the slow case;
|
|
33
|
-
* - a LOADED box (one that has just stopped a capture) can take up to about 10 s: each of the two calls is
|
|
34
|
-
* bounded at 5 s and they stop the worker's event loop for the whole time.
|
|
35
|
-
* 12 s is that loaded ceiling plus 2 s, the number `fleet-wake.mjs` `HEALTH_TIMEOUT_MS` states for the same
|
|
36
|
-
* reading. Before #2683 these entries used 5 s (`witness`, 1.91 s over the 3.09 s box and none over a loaded
|
|
37
|
-
* one), 8 s (`auth:leak-check`) and 10 s (`worker:compare`'s busy guard), read at `d119fb0f2`. Cost of the
|
|
38
|
-
* generosity: a box that really is off costs one 12 s wait before the message. A refusal costs nothing, it comes
|
|
39
|
-
* back at once.
|
|
40
|
-
*/
|
|
41
|
-
export const WORKER_PROBE_TIMEOUT_MS: 12000;
|
|
42
|
-
/** How to wake a box from a checkout of this repo. The published entries do not do it themselves. */
|
|
43
|
-
export const WAKE_HINT: string;
|
|
44
1
|
/**
|
|
45
2
|
* WHAT ONE `/health` PROBE CAN SAY, for the entries that reach a worker from the PUBLISHED side and by hand
|
|
46
3
|
* (`witness`, `worker:compare`, `auth:leak-check`, #2683 of #2655).
|
|
@@ -84,5 +41,47 @@ export type Probe = {
|
|
|
84
41
|
message: string;
|
|
85
42
|
timedOut: boolean;
|
|
86
43
|
};
|
|
44
|
+
import { requestJson } from "./worker-http.js";
|
|
87
45
|
export type ProbeRequest = typeof requestJson;
|
|
88
|
-
|
|
46
|
+
/**
|
|
47
|
+
* THE PER-PROBE TIMEOUT, WITH ITS READING. A probe that outlives it is UNKNOWN, never "down", so this number
|
|
48
|
+
* decides how much slowness a healthy box is allowed.
|
|
49
|
+
*
|
|
50
|
+
* All of it READ by others on the real fleet (`orchestrator`, #2671) and none measured by this row, whose
|
|
51
|
+
* engineer is barred from probing it:
|
|
52
|
+
* - the slowest HEALTHY box: 2.80 to 3.09 s on a11y-worker-13, -14 and -16, twelve others 0.53 to 0.76 s;
|
|
53
|
+
* - that is the FIRST answer after the box has been quiet more than 5 s, because the worker rebuilds its
|
|
54
|
+
* environment block with two synchronous `powershell.exe` calls when it is older than that, so a probe made
|
|
55
|
+
* by hand is nearly always the slow case;
|
|
56
|
+
* - a LOADED box (one that has just stopped a capture) can take up to about 10 s: each of the two calls is
|
|
57
|
+
* bounded at 5 s and they stop the worker's event loop for the whole time.
|
|
58
|
+
* 12 s is that loaded ceiling plus 2 s, the number `fleet-wake.mjs` `HEALTH_TIMEOUT_MS` states for the same
|
|
59
|
+
* reading. Before #2683 these entries used 5 s (`witness`, 1.91 s over the 3.09 s box and none over a loaded
|
|
60
|
+
* one), 8 s (`auth:leak-check`) and 10 s (`worker:compare`'s busy guard), read at `d119fb0f2`. Cost of the
|
|
61
|
+
* generosity: a box that really is off costs one 12 s wait before the message. A refusal costs nothing, it comes
|
|
62
|
+
* back at once.
|
|
63
|
+
*/
|
|
64
|
+
export declare const WORKER_PROBE_TIMEOUT_MS = 12000;
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} worker the worker's base URL
|
|
67
|
+
* @param {{ timeoutMs?: number, request?: ProbeRequest }} [options]
|
|
68
|
+
* @returns {Promise<Probe>}
|
|
69
|
+
*/
|
|
70
|
+
export declare function probeHealth(worker: string, { timeoutMs, request }?: {
|
|
71
|
+
timeoutMs?: number;
|
|
72
|
+
request?: ProbeRequest;
|
|
73
|
+
}): Promise<Probe>;
|
|
74
|
+
/** How to wake a box from a checkout of this repo. The published entries do not do it themselves. */
|
|
75
|
+
export declare const WAKE_HINT: string;
|
|
76
|
+
/**
|
|
77
|
+
* One sentence per outcome, in the words of what was OBSERVED. Only `refused`, `not-ready` and `busy` say the box is
|
|
78
|
+
* up, because only those are things the box itself said; a silent probe says "did not answer" and never "down".
|
|
79
|
+
*
|
|
80
|
+
* @param {Probe} probe
|
|
81
|
+
* @param {{ worker: string, timeoutMs?: number }} about
|
|
82
|
+
* @returns {string}
|
|
83
|
+
*/
|
|
84
|
+
export declare function describeProbe(probe: Probe, { worker, timeoutMs }: {
|
|
85
|
+
worker: string;
|
|
86
|
+
timeoutMs?: number;
|
|
87
|
+
}): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const RECAPTURE_COST: string;
|
|
1
2
|
/**
|
|
2
3
|
* Decide whether a deploy may proceed.
|
|
3
4
|
*
|
|
@@ -5,7 +6,7 @@
|
|
|
5
6
|
* allowed: boolean, source?: string }} input
|
|
6
7
|
* @returns {{ refuse: boolean, message: string }}
|
|
7
8
|
*/
|
|
8
|
-
export function protocolVerdict({ local, served, allowed, source }: {
|
|
9
|
+
export declare function protocolVerdict({ local, served, allowed, source }: {
|
|
9
10
|
local: number | string | null;
|
|
10
11
|
served: {
|
|
11
12
|
worker: string;
|
|
@@ -26,8 +27,7 @@ export function protocolVerdict({ local, served, allowed, source }: {
|
|
|
26
27
|
* @param {string[]} urls
|
|
27
28
|
* @returns {Promise<{worker: string, protocol: number|string|null}[]>}
|
|
28
29
|
*/
|
|
29
|
-
export function servedProtocols(urls: string[]): Promise<{
|
|
30
|
+
export declare function servedProtocols(urls: string[]): Promise<{
|
|
30
31
|
worker: string;
|
|
31
32
|
protocol: number | string | null;
|
|
32
33
|
}[]>;
|
|
33
|
-
export const RECAPTURE_COST: string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The repository root, resolved from this module rather than from the caller's cwd. */
|
|
2
|
+
export declare const REPOSITORY: string;
|
|
3
|
+
/**
|
|
4
|
+
* Every non-test source file under the repository root, as `[relativePath, source]`.
|
|
5
|
+
*
|
|
6
|
+
* @param {{ root?: string }} [options]
|
|
7
|
+
* @returns {Array<[string, string]>}
|
|
8
|
+
*/
|
|
9
|
+
export declare function sourceFiles({ root }?: {
|
|
10
|
+
root?: string;
|
|
11
|
+
}): Array<[string, string]>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The arguments that let a plain `node` run a `.ts` file (ADR 0043, Decision 8): the host's Node has no type stripping, so a test that
|
|
3
|
+
* starts a source file as a child process, as an operator would, starts it under `tsx`. Resolved to an absolute URL because those children
|
|
4
|
+
* run from a temporary directory, where a bare `tsx` would not resolve.
|
|
5
|
+
*/
|
|
6
|
+
export declare const TSX_ARGS: string[];
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* @param {string} what The command or module the caller is about to run — named so the message is
|
|
3
3
|
* specific to what actually fired, not a generic banner every UTM-adjacent file prints identically.
|
|
4
4
|
*/
|
|
5
|
-
export function warnUtmDeprecated(what: string): void;
|
|
5
|
+
export declare function warnUtmDeprecated(what: string): void;
|