@a11ign/screenreader-fleet 0.0.0-reserved.0 → 0.2.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/LICENSE +661 -0
- package/README.md +94 -2
- package/dist/capture-client.d.mts +49 -0
- package/dist/capture-client.d.mts.map +1 -0
- package/dist/capture-client.mjs +352 -0
- package/dist/capture-client.mjs.map +1 -0
- package/dist/check-worker-code.d.mts +34 -0
- package/dist/check-worker-code.d.mts.map +1 -0
- package/dist/check-worker-code.mjs +142 -0
- package/dist/check-worker-code.mjs.map +1 -0
- package/dist/cli-flags.d.mts +71 -0
- package/dist/cli-flags.d.mts.map +1 -0
- package/dist/cli-flags.mjs +207 -0
- package/dist/cli-flags.mjs.map +1 -0
- package/dist/code-drift.d.mts +140 -0
- package/dist/code-drift.d.mts.map +1 -0
- package/dist/code-drift.mjs +284 -0
- package/dist/code-drift.mjs.map +1 -0
- package/dist/command-line-census.d.mts +33 -0
- package/dist/command-line-census.d.mts.map +1 -0
- package/dist/command-line-census.mjs +96 -0
- package/dist/command-line-census.mjs.map +1 -0
- package/dist/compare-workers.d.mts +3 -0
- package/dist/compare-workers.d.mts.map +1 -0
- package/dist/compare-workers.mjs +332 -0
- package/dist/compare-workers.mjs.map +1 -0
- package/dist/control-plane-isolation.d.mts +45 -0
- package/dist/control-plane-isolation.d.mts.map +1 -0
- package/dist/control-plane-isolation.mjs +67 -0
- package/dist/control-plane-isolation.mjs.map +1 -0
- package/dist/deploy-worker.d.mts +3 -0
- package/dist/deploy-worker.d.mts.map +1 -0
- package/dist/deploy-worker.mjs +333 -0
- package/dist/deploy-worker.mjs.map +1 -0
- package/dist/doctor.d.mts +216 -0
- package/dist/doctor.d.mts.map +1 -0
- package/dist/doctor.mjs +962 -0
- package/dist/doctor.mjs.map +1 -0
- package/dist/fleet-consistency.d.mts +235 -0
- package/dist/fleet-consistency.d.mts.map +1 -0
- package/dist/fleet-consistency.mjs +436 -0
- package/dist/fleet-consistency.mjs.map +1 -0
- package/dist/fleet-env.d.mts +228 -0
- package/dist/fleet-env.d.mts.map +1 -0
- package/dist/fleet-env.mjs +509 -0
- package/dist/fleet-env.mjs.map +1 -0
- package/dist/fleet-scripts.d.mts +11 -0
- package/dist/fleet-scripts.d.mts.map +1 -0
- package/dist/fleet-scripts.mjs +41 -0
- package/dist/fleet-scripts.mjs.map +1 -0
- package/dist/git-safe-env.d.mts +10 -0
- package/dist/git-safe-env.d.mts.map +1 -0
- package/dist/git-safe-env.mjs +44 -0
- package/dist/git-safe-env.mjs.map +1 -0
- package/dist/guest-run.d.mts +26 -0
- package/dist/guest-run.d.mts.map +1 -0
- package/dist/guest-run.mjs +164 -0
- package/dist/guest-run.mjs.map +1 -0
- package/dist/host-address.d.mts +33 -0
- package/dist/host-address.d.mts.map +1 -0
- package/dist/host-address.mjs +105 -0
- package/dist/host-address.mjs.map +1 -0
- package/dist/host-capacity.d.mts +64 -0
- package/dist/host-capacity.d.mts.map +1 -0
- package/dist/host-capacity.mjs +152 -0
- package/dist/host-capacity.mjs.map +1 -0
- package/dist/host-metrics.d.mts +116 -0
- package/dist/host-metrics.d.mts.map +1 -0
- package/dist/host-metrics.mjs +201 -0
- package/dist/host-metrics.mjs.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/local-vm.d.ts +125 -0
- package/dist/local-vm.d.ts.map +1 -0
- package/dist/local-vm.js +360 -0
- package/dist/local-vm.js.map +1 -0
- package/dist/measure-guard.d.mts +34 -0
- package/dist/measure-guard.d.mts.map +1 -0
- package/dist/measure-guard.mjs +73 -0
- package/dist/measure-guard.mjs.map +1 -0
- package/dist/normalise-fleet.d.mts +2 -0
- package/dist/normalise-fleet.d.mts.map +1 -0
- package/dist/normalise-fleet.mjs +76 -0
- package/dist/normalise-fleet.mjs.map +1 -0
- package/dist/npm-cli-executable.d.mts +42 -0
- package/dist/npm-cli-executable.d.mts.map +1 -0
- package/dist/npm-cli-executable.mjs +159 -0
- package/dist/npm-cli-executable.mjs.map +1 -0
- package/dist/probe-outcome.d.mts +89 -0
- package/dist/probe-outcome.d.mts.map +1 -0
- package/dist/probe-outcome.mjs +104 -0
- package/dist/probe-outcome.mjs.map +1 -0
- package/dist/protocol-guard.d.mts +34 -0
- package/dist/protocol-guard.d.mts.map +1 -0
- package/dist/protocol-guard.mjs +121 -0
- package/dist/protocol-guard.mjs.map +1 -0
- package/dist/source-walk.d.mts +12 -0
- package/dist/source-walk.d.mts.map +1 -0
- package/dist/source-walk.mjs +56 -0
- package/dist/source-walk.mjs.map +1 -0
- package/dist/transient-fault.d.mts +6 -0
- package/dist/transient-fault.d.mts.map +1 -0
- package/dist/transient-fault.mjs +86 -0
- package/dist/transient-fault.mjs.map +1 -0
- package/dist/utm-deprecated.d.mts +6 -0
- package/dist/utm-deprecated.d.mts.map +1 -0
- package/dist/utm-deprecated.mjs +23 -0
- package/dist/utm-deprecated.mjs.map +1 -0
- package/dist/worker-code-check.d.mts +29 -0
- package/dist/worker-code-check.d.mts.map +1 -0
- package/dist/worker-code-check.mjs +85 -0
- package/dist/worker-code-check.mjs.map +1 -0
- package/dist/worker-health.d.mts +56 -0
- package/dist/worker-health.d.mts.map +1 -0
- package/dist/worker-health.mjs +73 -0
- package/dist/worker-health.mjs.map +1 -0
- package/dist/worker-http.d.mts +103 -0
- package/dist/worker-http.d.mts.map +1 -0
- package/dist/worker-http.mjs +277 -0
- package/dist/worker-http.mjs.map +1 -0
- package/dist/worker-stats.d.mts +66 -0
- package/dist/worker-stats.d.mts.map +1 -0
- package/dist/worker-stats.mjs +143 -0
- package/dist/worker-stats.mjs.map +1 -0
- package/package.json +96 -4
- package/src/local-worker/autounattend.xml +280 -0
- package/src/local-worker/build-vm.sh +218 -0
- package/src/local-worker/clone-worker.sh +141 -0
- package/src/local-worker/create-utm-vm.sh +202 -0
- package/src/local-worker/fetch-windows-iso.sh +238 -0
- package/src/local-worker/first-boot.cmd +58 -0
- package/src/local-worker/worker-ctl.sh +442 -0
- package/src/provisioning/README.md +28 -0
- package/src/provisioning/apply-foreground-lock-timeout.ps1 +71 -0
- package/src/provisioning/bare-metal/README.md +213 -0
- package/src/provisioning/bare-metal/a11y-bootstrap.service +58 -0
- package/src/provisioning/bare-metal/autounattend.xml +428 -0
- package/src/provisioning/bare-metal/serve-bootstrap.sh +86 -0
- package/src/provisioning/bootstrap-control-plane.sh +463 -0
- package/src/provisioning/bootstrap-windows-worker.ps1 +649 -0
- package/src/provisioning/build-lean-worker-image.ps1 +275 -0
- package/src/provisioning/diagnose-nvda-worker.ps1 +174 -0
- package/src/provisioning/provision-nvda-worker.ps1 +827 -0
- package/src/provisioning/set-display-mode.ps1 +411 -0
- package/src/provisioning/stamp-provision-revision.ps1 +184 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* `A11Y_WORKERS`, derived from the Ansible inventory — so a machine is added in ONE place.
|
|
4
|
+
*
|
|
5
|
+
* eval "$(npm run --silent fleet:env)"
|
|
6
|
+
* npm run fleet:env -- --list # just the URLs, one per line
|
|
7
|
+
*
|
|
8
|
+
* ## Why derive rather than maintain both
|
|
9
|
+
*
|
|
10
|
+
* The fleet was a comma-separated string in an environment variable, which was fine for two VMs and is the
|
|
11
|
+
* wrong shape for twelve boxes. Ansible needs an inventory regardless, so the choice is not "one format or
|
|
12
|
+
* two" but "one source of truth or two" — and two is how a box comes to be provisioned but never dispatched
|
|
13
|
+
* to, or dispatched to but never updated. Both of those are silent: the run simply never sends that box a
|
|
14
|
+
* case, and nothing reports a machine it does not know about.
|
|
15
|
+
*
|
|
16
|
+
* ## Why this reads the file rather than shelling out to `ansible-inventory`
|
|
17
|
+
*
|
|
18
|
+
* `ansible-inventory --list` is authoritative and would be the better answer if Ansible were always
|
|
19
|
+
* present. It is not: the control plane runs captures, and requiring an Ansible install before a capture
|
|
20
|
+
* run could start would make the fleet tooling a dependency of the thing it exists to serve.
|
|
21
|
+
*
|
|
22
|
+
* ## Strict on purpose
|
|
23
|
+
*
|
|
24
|
+
* A hand-rolled reader for a subset of YAML is exactly the sort of thing that quietly returns four hosts
|
|
25
|
+
* out of twelve after somebody reformats the file — and a SHORT fleet list is invisible, because a run with
|
|
26
|
+
* eight workers looks like a run with eight workers. So this refuses rather than guesses: anything that
|
|
27
|
+
* looks like a host entry but does not parse is an error naming the line, and finding no hosts at all is an
|
|
28
|
+
* error too.
|
|
29
|
+
*/
|
|
30
|
+
import { readFileSync } from "node:fs";
|
|
31
|
+
import { assertWorkerUrl } from "./worker-http.mjs";
|
|
32
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
33
|
+
import { refuseUnknownFlags } from "./cli-flags.mjs";
|
|
34
|
+
/**
|
|
35
|
+
* its output is `eval`-ed by a shell, so a wrong shape is executed rather than read.
|
|
36
|
+
*
|
|
37
|
+
* An unrecognised flag is otherwise IGNORED, so it runs the default and reports success.
|
|
38
|
+
*/
|
|
39
|
+
refuseUnknownFlags(["--list"], { entry: import.meta.url, command: "npm run fleet:env" });
|
|
40
|
+
export const DEFAULT_WORKER_PORT = 8765;
|
|
41
|
+
/**
|
|
42
|
+
* The fleet named by the environment — ONE parser, because there were three that did not agree.
|
|
43
|
+
*
|
|
44
|
+
* `doctor.mjs` preferred `A11Y_WORKERS`, `check-worker-code.mjs` preferred `A11Y_WORKER`, and the
|
|
45
|
+
* dataset runner read only `A11Y_WORKERS`. With both variables set, `doctor` and `worker:code` reported
|
|
46
|
+
* on **different machines** — so "doctor says the fleet is fine" and "worker:code says a worker is
|
|
47
|
+
* stale" could be statements about two disjoint sets, with nothing to say so.
|
|
48
|
+
*
|
|
49
|
+
* `A11Y_WORKERS` wins, matching the dataset runner: the plural names the pool a run dispatches across,
|
|
50
|
+
* and a diagnostic that describes a different set from the one that will do the work is worse than no
|
|
51
|
+
* diagnostic. Each entry is trimmed and de-slashed, because `A11Y_WORKERS=a, b` otherwise yields a URL
|
|
52
|
+
* with a leading space — a configuration typo wearing a dead-machine costume.
|
|
53
|
+
*
|
|
54
|
+
* Returns `[]` rather than null when neither is set: "no worker was named" is a normal state that means
|
|
55
|
+
* "find the local VMs", and every caller already branches on emptiness.
|
|
56
|
+
*
|
|
57
|
+
* @returns {Array<{ name: string, url: string }>}
|
|
58
|
+
*/
|
|
59
|
+
export function configuredWorkers() {
|
|
60
|
+
const raw = process.env.A11Y_WORKERS ?? process.env.A11Y_WORKER ?? "";
|
|
61
|
+
const named = process.env.A11Y_WORKERS !== undefined ? "A11Y_WORKERS" : "A11Y_WORKER";
|
|
62
|
+
return raw.split(",")
|
|
63
|
+
.map((w) => w.trim())
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
// Validated, because this is the ENV route into the same defect the `--worker=` clients now refuse.
|
|
66
|
+
// `A11Y_WORKERS=http://:8765` used to pass straight through to every consumer of this function --
|
|
67
|
+
// `doctor`, `worker:code`, `fleet:status`, the dataset runner -- each of which would then report a
|
|
68
|
+
// machine that cannot be addressed as one that is not answering. Note the empty case is untouched:
|
|
69
|
+
// "no worker was named" is a normal state meaning "find the local VMs", and every caller branches on it.
|
|
70
|
+
.map((url) => assertWorkerUrl(url, { source: named }))
|
|
71
|
+
.map((url) => ({ name: url.replace(/^https?:\/\//, ""), url }));
|
|
72
|
+
}
|
|
73
|
+
// THE INVENTORY LIVES IN `packages/control`, because it describes the machines the CONTROL PLANE drives
|
|
74
|
+
// and it is read by the ansible that runs there. `packages/worker-fleet` is PUBLISHED, and `control` is
|
|
75
|
+
// never published (ADR 0012), so this constant is a real cycle -- audit §3.2 -- and NOT the sanctioned
|
|
76
|
+
// direction: `control` reaching `worker-fleet` by relative import is fine (control has no
|
|
77
|
+
// `node_modules`); this file, reaching back into a package that will not exist in an installed
|
|
78
|
+
// `node_modules/@a11ign/screenreader-fleet`, is what the audit calls "ships code whose data file lives in
|
|
79
|
+
// a package that is never published".
|
|
80
|
+
//
|
|
81
|
+
// FIXED 2026-09-06 by injection, not by moving this module: `doctor.mjs` and `check-worker-code.mjs` are
|
|
82
|
+
// PUBLISHED bins that must keep resolving a bare-metal fleet correctly when run as `npm run doctor` from
|
|
83
|
+
// this checkout, so the functions below take the path as an optional PARAMETER, defaulting to this
|
|
84
|
+
// constant. The default is not a fix in itself -- an installed tarball still ships one that points at a
|
|
85
|
+
// package it will never find -- but `inventoryWorkerUrls`/`namedInventoryWorkers` already catch that and
|
|
86
|
+
// return `[]`, which is this project's own supported "no bare-metal fleet declared here" answer (see their
|
|
87
|
+
// own comments). What injection buys is that the assumption is now a NAMED, overridable default rather
|
|
88
|
+
// than a hidden module constant -- a caller outside this monorepo (or a test) can supply its own path
|
|
89
|
+
// instead of silently inheriting one that can only ever resolve here.
|
|
90
|
+
const INVENTORY = fileURLToPath(new URL("../../control/ansible/inventory.yml", import.meta.url));
|
|
91
|
+
const GROUP_VARS = fileURLToPath(new URL("../../control/ansible/group_vars/a11y_workers.yml", import.meta.url));
|
|
92
|
+
/** A line that declares a host address, ignoring anything commented out. */
|
|
93
|
+
const HOST_LINE = /^\s*ansible_host\s*:\s*(\S+)\s*$/;
|
|
94
|
+
/** Anything that mentions the key but does not parse — a reformat, a quoted value, a list. */
|
|
95
|
+
const SUSPECT = /ansible_host\s*:/;
|
|
96
|
+
/** A mapping key, with or without an inline value. Indentation is the only thing that says where it sits. */
|
|
97
|
+
const KEY_LINE = /^(\s*)([A-Za-z_][\w.-]*)\s*:/;
|
|
98
|
+
/**
|
|
99
|
+
* A host var declaring whether the host is in the CAPTURE SET. Absent means in: the default is unchanged, so
|
|
100
|
+
* a host that says nothing needs no inventory edit.
|
|
101
|
+
*/
|
|
102
|
+
const CAPTURE_KEY = "a11y_capture";
|
|
103
|
+
const CAPTURE_LINE = new RegExp(`^(\\s*)${CAPTURE_KEY}\\s*:\\s*(\\S+)\\s*$`);
|
|
104
|
+
const CAPTURE_SUSPECT = new RegExp(`${CAPTURE_KEY}\\s*:`);
|
|
105
|
+
/**
|
|
106
|
+
* The inventory group whose hosts are capture workers.
|
|
107
|
+
*
|
|
108
|
+
* This reader was GROUPLESS until 2026-08-21, and that was a live hazard rather than an untidiness: every
|
|
109
|
+
* `ansible_host:` in the file became `http://<addr>:8765`, so adding any non-worker host to `inventory.yml`
|
|
110
|
+
* -- the lab container, the control container, a switch -- would have silently added a phantom worker to
|
|
111
|
+
* `A11Y_WORKERS`, and a run would have dispatched capture cases to it. That is the exact failure this
|
|
112
|
+
* module's own header says it exists to prevent ("dispatched to but never updated", "both of those are
|
|
113
|
+
* silent"), arriving through the door nobody had shut.
|
|
114
|
+
*/
|
|
115
|
+
export const WORKER_GROUP = "a11y_workers";
|
|
116
|
+
/**
|
|
117
|
+
* One frame of the indentation stack: a key and the column it started at.
|
|
118
|
+
*
|
|
119
|
+
* @typedef {{indent: number, key: string}} Frame
|
|
120
|
+
*/
|
|
121
|
+
/**
|
|
122
|
+
* A host as the inventory declares it — the ADDRESS and the NAME together.
|
|
123
|
+
*
|
|
124
|
+
* They travel as one value because separating them is what sent `fleet:sleep` at the wrong machine: every
|
|
125
|
+
* tool that ACTS on a worker takes the name, every tool that REPORTS on one printed the address, and
|
|
126
|
+
* nothing mapped between them. `collectHost` explains the incident.
|
|
127
|
+
*
|
|
128
|
+
* `capture` is false for a host that declares `a11y_capture: false`: enrolled, served, deployed to, and not
|
|
129
|
+
* handed corpus cases.
|
|
130
|
+
*
|
|
131
|
+
* @typedef {{name: string|undefined, host: string, capture: boolean}} Host
|
|
132
|
+
*/
|
|
133
|
+
/**
|
|
134
|
+
* The group a host sits in: the key directly beneath `children`.
|
|
135
|
+
*
|
|
136
|
+
* Ansible nests as `all.children.<group>.hosts.<name>`, so the group is positional rather than something
|
|
137
|
+
* to pattern-match on a name. Reading it from the path means a group added later needs no change here.
|
|
138
|
+
*/
|
|
139
|
+
/** @param {Array<string|undefined>} path @returns {string|undefined} */
|
|
140
|
+
function groupOf(path) {
|
|
141
|
+
const children = path.indexOf("children");
|
|
142
|
+
return children === -1 ? undefined : path[children + 1];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Track the YAML path by indentation — a stack, not a parser.
|
|
146
|
+
*
|
|
147
|
+
* Deliberately not a YAML library, for the reason the header gives about `ansible-inventory`: the control
|
|
148
|
+
* plane must be able to read the fleet without installing anything. Indentation is sufficient because the
|
|
149
|
+
* only question asked of the path is which group a host is in.
|
|
150
|
+
*/
|
|
151
|
+
/** @param {Frame[]} stack @param {string} line */
|
|
152
|
+
function descend(stack, line) {
|
|
153
|
+
const match = line.match(KEY_LINE);
|
|
154
|
+
if (!match)
|
|
155
|
+
return;
|
|
156
|
+
const indent = match[1].length;
|
|
157
|
+
while (stack.length && stack[stack.length - 1].indent >= indent)
|
|
158
|
+
stack.pop();
|
|
159
|
+
stack.push({ indent, key: match[2] });
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Which group each line of the inventory sits in, index-aligned with `text.split(/\r?\n/)`.
|
|
163
|
+
*
|
|
164
|
+
* Exported so there is ONE group implementation. `fleet-discover.mjs` has its own host reader with its own
|
|
165
|
+
* regexes -- two readers of one file, which this repo's notes call its most expensive recurring shape -- and
|
|
166
|
+
* when this module became group-aware that one did not, so `fleet:discover` probed the lab container on
|
|
167
|
+
* :8765 and reported it "ASLEEP?". A phantom worker in the diagnostic instead of in the dispatch list is
|
|
168
|
+
* still a phantom worker. Rather than teach a second parser about groups, both now ask this.
|
|
169
|
+
*
|
|
170
|
+
* @param {string} text
|
|
171
|
+
* @returns {Array<string | undefined>}
|
|
172
|
+
*/
|
|
173
|
+
export function groupPerLine(text) {
|
|
174
|
+
/** @type {Frame[]} */
|
|
175
|
+
const stack = [];
|
|
176
|
+
return text.split(/\r?\n/).map((line) => {
|
|
177
|
+
if (!line.trim() || line.trimStart().startsWith("#"))
|
|
178
|
+
return groupOf(stack.map((f) => f.key));
|
|
179
|
+
if (!HOST_LINE.test(line))
|
|
180
|
+
descend(stack, line);
|
|
181
|
+
return groupOf(stack.map((f) => f.key));
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The hosts of one inventory group, each with whether it is in the capture set.
|
|
186
|
+
*
|
|
187
|
+
* ONE reader for both questions ("the whole fleet" and "the capture set"), because a second pass over the
|
|
188
|
+
* file is a second parser, and this repo has paid for that shape already (`fleet-discover.mjs`).
|
|
189
|
+
*
|
|
190
|
+
* @param {string} text
|
|
191
|
+
* @param {string} group
|
|
192
|
+
* @returns {Host[]}
|
|
193
|
+
*/
|
|
194
|
+
function hostsInGroup(text, group) {
|
|
195
|
+
/** @type {Host[]} */
|
|
196
|
+
const hosts = [];
|
|
197
|
+
/** @type {Frame[]} */
|
|
198
|
+
const stack = [];
|
|
199
|
+
/** @type {Map<string, {capture: boolean, line: number}>} */
|
|
200
|
+
const declared = new Map();
|
|
201
|
+
text.split(/\r?\n/).forEach((line, index) => {
|
|
202
|
+
if (!line.trim() || line.trimStart().startsWith("#"))
|
|
203
|
+
return;
|
|
204
|
+
const match = line.match(HOST_LINE);
|
|
205
|
+
if (match) {
|
|
206
|
+
collectHost(hosts, { match, index, stack, group });
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (CAPTURE_SUSPECT.test(line)) {
|
|
210
|
+
readCaptureDeclaration(declared, { line, index, stack });
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (SUSPECT.test(line)) {
|
|
214
|
+
throw new Error(`inventory.yml:${index + 1} looks like a host entry but does not parse: ${line.trim()}\n`
|
|
215
|
+
+ "This reader understands `ansible_host: <address>` and nothing else, deliberately — a fleet list "
|
|
216
|
+
+ "that silently comes up short is invisible, because a run with fewer workers looks normal.");
|
|
217
|
+
}
|
|
218
|
+
descend(stack, line);
|
|
219
|
+
});
|
|
220
|
+
if (!hosts.length) {
|
|
221
|
+
throw new Error(`no hosts found under ${group}.hosts in inventory.yml. Add one before running.`);
|
|
222
|
+
}
|
|
223
|
+
return applyCaptureDeclarations(hosts, declared, group);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Record `a11y_capture: true|false` against the host whose var it is — the nearest enclosing key.
|
|
227
|
+
*
|
|
228
|
+
* Strict for the reason the header gives: `a11y_capture: no` or `"false"` that this reader took for "not
|
|
229
|
+
* declared" would leave a cold host in the capture set, and a capture guard would then refuse the run
|
|
230
|
+
* naming the wrong cause. Only the two literals are read; anything else names its line.
|
|
231
|
+
*
|
|
232
|
+
* @param {Map<string, {capture: boolean, line: number}>} declared
|
|
233
|
+
* @param {{line: string, index: number, stack: Frame[]}} at
|
|
234
|
+
*/
|
|
235
|
+
function readCaptureDeclaration(declared, { line, index, stack }) {
|
|
236
|
+
const match = line.match(CAPTURE_LINE);
|
|
237
|
+
const value = match?.[2];
|
|
238
|
+
if (value !== "true" && value !== "false") {
|
|
239
|
+
throw new Error(`inventory.yml:${index + 1} declares ${CAPTURE_KEY} but not as \`true\` or \`false\`: ${line.trim()}\n`
|
|
240
|
+
+ "This reader takes those two literals and nothing else, so a host is never left in or out of the "
|
|
241
|
+
+ "capture set by a spelling it did not understand.");
|
|
242
|
+
}
|
|
243
|
+
const indent = /** @type {RegExpMatchArray} */ (match)[1].length;
|
|
244
|
+
const owner = [...stack].reverse().find((frame) => frame.indent < indent)?.key;
|
|
245
|
+
declared.set(owner ?? "", { capture: value === "true", line: index + 1 });
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Attach each declaration to its host, and refuse one that belongs to no host of the group.
|
|
249
|
+
*
|
|
250
|
+
* A declaration on a group or on a host in another group would otherwise be read and dropped, which is the
|
|
251
|
+
* silent shape this module exists to refuse: somebody believes a machine is out of the capture set and it is
|
|
252
|
+
* not.
|
|
253
|
+
*
|
|
254
|
+
* @param {Host[]} hosts
|
|
255
|
+
* @param {Map<string, {capture: boolean, line: number}>} declared
|
|
256
|
+
* @param {string} group
|
|
257
|
+
* @returns {Host[]}
|
|
258
|
+
*/
|
|
259
|
+
function applyCaptureDeclarations(hosts, declared, group) {
|
|
260
|
+
const names = new Set(hosts.map((host) => host.name));
|
|
261
|
+
for (const [owner, { line }] of declared) {
|
|
262
|
+
if (!names.has(owner)) {
|
|
263
|
+
throw new Error(`inventory.yml:${line} declares ${CAPTURE_KEY} on \`${owner}\`, which is not a host in ${group}.\n`
|
|
264
|
+
+ `Declare it directly under the host (\`${group}.hosts.<name>.${CAPTURE_KEY}\`); it is not read anywhere else.`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return hosts.map((host) => ({ ...host, capture: declared.get(host.name ?? "")?.capture ?? true }));
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Worker URLs from the text of an inventory file.
|
|
271
|
+
*
|
|
272
|
+
* `scope` says which question is asked. `"fleet"` (the default) is every enrolled worker -- what `doctor`,
|
|
273
|
+
* `worker:code`, `fleet:status` and the deploy tooling mean, and they must keep seeing a worker that is
|
|
274
|
+
* serving but not capturing. `"capture"` is the fleet minus every host that declares `a11y_capture: false`
|
|
275
|
+
* -- what `A11Y_WORKERS` means. It is a string and not a flag because a boolean argument does not say
|
|
276
|
+
* which of the two you got.
|
|
277
|
+
*
|
|
278
|
+
* Exported and pure so the strictness above is testable: a reader that has never been shown to reject
|
|
279
|
+
* anything is a reader nobody knows the limits of.
|
|
280
|
+
*
|
|
281
|
+
* @param {string} text
|
|
282
|
+
* @param {{ port?: number, group?: string, scope?: "fleet" | "capture" }} [options]
|
|
283
|
+
* @returns {string[]}
|
|
284
|
+
*/
|
|
285
|
+
export function workersFromInventory(text, { port = DEFAULT_WORKER_PORT, group = WORKER_GROUP, scope = "fleet" } = {}) {
|
|
286
|
+
const hosts = hostsInGroup(text, group);
|
|
287
|
+
const chosen = scope === "capture" ? hosts.filter((host) => host.capture) : hosts;
|
|
288
|
+
if (!chosen.length) {
|
|
289
|
+
// An empty A11Y_WORKERS means "find the local VMs", which do not exist on the control plane -- so an
|
|
290
|
+
// inventory with every host excluded must fail HERE, naming the cause, not as something unrelated.
|
|
291
|
+
throw new Error(`every host under ${group}.hosts declares ${CAPTURE_KEY}: false, so the capture set is empty.`);
|
|
292
|
+
}
|
|
293
|
+
return chosen.map(({ host }) => `http://${host}:${port}`);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* The hosts left OUT of the capture set, so the command that leaves them out can say so.
|
|
297
|
+
*
|
|
298
|
+
* @param {string} text
|
|
299
|
+
* @param {{ port?: number, group?: string }} [options]
|
|
300
|
+
* @returns {{name: string, url: string}[]}
|
|
301
|
+
*/
|
|
302
|
+
export function hostsOutOfCaptureSet(text, { port = DEFAULT_WORKER_PORT, group = WORKER_GROUP } = {}) {
|
|
303
|
+
return hostsInGroup(text, group)
|
|
304
|
+
.filter((host) => !host.capture)
|
|
305
|
+
.map(({ name, host }) => ({ name: name ?? host, url: `http://${host}:${port}` }));
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* The inventory as `{ url -> name }`, so a report can say which machine a command would act on.
|
|
309
|
+
*
|
|
310
|
+
* Same parser, same group rules — deliberately not a second reader of the same file, which this repo
|
|
311
|
+
* calls its most expensive recurring shape and has already paid for once in `fleet-discover.mjs`.
|
|
312
|
+
*
|
|
313
|
+
* @param {string} text
|
|
314
|
+
* @param {{ port?: number, group?: string }} [options]
|
|
315
|
+
* @returns {Record<string, string>}
|
|
316
|
+
*/
|
|
317
|
+
export function workerNamesFromInventory(text, { port = DEFAULT_WORKER_PORT, group = WORKER_GROUP } = {}) {
|
|
318
|
+
/** @type {Host[]} */
|
|
319
|
+
const hosts = [];
|
|
320
|
+
/** @type {Frame[]} */
|
|
321
|
+
const stack = [];
|
|
322
|
+
text.split(/\r?\n/).forEach((line, index) => {
|
|
323
|
+
if (!line.trim() || line.trimStart().startsWith("#"))
|
|
324
|
+
return;
|
|
325
|
+
const match = line.match(HOST_LINE);
|
|
326
|
+
if (match)
|
|
327
|
+
return collectHost(hosts, { match, index, stack, group });
|
|
328
|
+
descend(stack, line);
|
|
329
|
+
});
|
|
330
|
+
// Falls back to the ADDRESS when the inventory nests a host without a name. That cannot happen in a
|
|
331
|
+
// well-formed inventory — Ansible keys hosts by name — but `undefined` reaching a report would print as
|
|
332
|
+
// the string "undefined", which is this repo's worst failure shape: a value that looks like an answer.
|
|
333
|
+
// An address is a worse label than a name and still identifies the machine, which is what this map is for.
|
|
334
|
+
return Object.fromEntries(hosts.map(({ name, host }) => [`http://${host}:${port}`, name ?? host]));
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Keep this host if it is in the worker group; refuse it if it is in no group at all.
|
|
338
|
+
*
|
|
339
|
+
* A host outside every group is the ambiguous case, and it gets an error rather than a default. Including
|
|
340
|
+
* it recreates the phantom-worker bug; dropping it silently is how a fleet list comes up short — and this
|
|
341
|
+
* module exists because both of those are invisible. So it says which line, and which group it expected.
|
|
342
|
+
*
|
|
343
|
+
* @param {Host[]} hosts
|
|
344
|
+
* @param {{match: RegExpMatchArray, index: number, stack: Frame[], group: string}} found
|
|
345
|
+
*/
|
|
346
|
+
function collectHost(hosts, { match, index, stack, group }) {
|
|
347
|
+
const found = groupOf(stack.map((frame) => frame.key));
|
|
348
|
+
if (found === group) {
|
|
349
|
+
// The inventory NAME comes along with the address. Ansible nests the host as
|
|
350
|
+
// `...hosts.<name>.ansible_host`, so the name is the innermost frame the stack is still holding.
|
|
351
|
+
//
|
|
352
|
+
// Carried because every tool that ACTS on a worker takes the name (`-l a11y-worker-4`) while every
|
|
353
|
+
// tool that REPORTS on one printed only the address — and nothing mapped them. On 2026-08-24 that
|
|
354
|
+
// sent `fleet:sleep` at the wrong machine: `fleet:status` named .224 as the Edge-drifted box, and
|
|
355
|
+
// .224 is a11y-worker-FIVE. Two commands, one fleet, no shared vocabulary.
|
|
356
|
+
hosts.push({ name: stack[stack.length - 1]?.key, host: match[1].replace(/^["']|["']$/g, ""), capture: true });
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (found === undefined) {
|
|
360
|
+
throw new Error(`inventory.yml:${index + 1} declares a host outside any group: ${match[0].trim()}\n`
|
|
361
|
+
+ `This reader takes workers from \`${group}\` only, so it cannot tell whether an ungrouped host is a `
|
|
362
|
+
+ "capture worker or something else on the network. Nest it under `all.children.<group>.hosts`.");
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
/** The port the group vars declare, so it is stated once and not guessed here. */
|
|
366
|
+
/** @param {string} text @returns {number} */
|
|
367
|
+
export function portFromGroupVars(text) {
|
|
368
|
+
const match = text.match(/^\s*a11y_port\s*:\s*(\d+)\s*$/m);
|
|
369
|
+
return match ? Number(match[1]) : DEFAULT_WORKER_PORT;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* The workers declared in `inventory.yml` — i.e. the BARE-METAL fleet.
|
|
373
|
+
*
|
|
374
|
+
* Exists so a caller can tell a physical box from a local UTM VM, which decides how it is deployed to and
|
|
375
|
+
* therefore what remedy to print. `worker:code` used to tell every stale worker to run `utmctl` and
|
|
376
|
+
* `npm run worker:deploy`, which CANNOT reach a bare-metal box — it is a `utmctl file push` keyed on a VM
|
|
377
|
+
* UUID and fails immediately off macOS. Following that advice on this fleet wastes the time it takes to
|
|
378
|
+
* discover the tool was describing a different kind of machine.
|
|
379
|
+
*
|
|
380
|
+
* Reads the same file through the same parser as `main()`, rather than a second copy of the knowledge.
|
|
381
|
+
*
|
|
382
|
+
* `inventoryPath`/`groupVarsPath` are INJECTED, defaulting to this monorepo's own control-plane files —
|
|
383
|
+
* see the comment above `INVENTORY` for why the default exists and what it does not fix on its own.
|
|
384
|
+
*
|
|
385
|
+
* @param {{ inventoryPath?: string, groupVarsPath?: string }} [paths]
|
|
386
|
+
*/
|
|
387
|
+
export function inventoryWorkerUrls({ inventoryPath = INVENTORY, groupVarsPath = GROUP_VARS } = {}) {
|
|
388
|
+
try {
|
|
389
|
+
const port = portFromGroupVars(readFileSync(groupVarsPath, "utf8"));
|
|
390
|
+
return workersFromInventory(readFileSync(inventoryPath, "utf8"), { port });
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
// No inventory, or one that does not parse, means "no bare-metal fleet declared here" — a local-VM-only
|
|
394
|
+
// checkout is a supported setup. Rethrowing would make a hint fail the command it is only advising.
|
|
395
|
+
return [];
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* The bare-metal fleet as `{name, url}` — the shape a REPORT needs.
|
|
400
|
+
*
|
|
401
|
+
* `fleet-status.mjs` already pairs the two, and its comment records what the address alone cost: "this
|
|
402
|
+
* table named .224 as the box whose Edge had drifted, and .224 is a11y-worker-FIVE — so
|
|
403
|
+
* `fleet:sleep --limit=a11y-worker-4` put a healthy machine to sleep and left the drifted one serving. A
|
|
404
|
+
* report and a command that cannot be matched up is a report you have to translate, and translation is
|
|
405
|
+
* where the mistake goes."
|
|
406
|
+
*
|
|
407
|
+
* Here rather than in each reporter, because that is the same pairing and a second copy would drift.
|
|
408
|
+
*
|
|
409
|
+
* `inventoryPath`/`groupVarsPath` are INJECTED, same reason and same default as `inventoryWorkerUrls`.
|
|
410
|
+
*
|
|
411
|
+
* @param {{ inventoryPath?: string, groupVarsPath?: string }} [paths]
|
|
412
|
+
* @returns {{name: string, url: string}[]} empty when no inventory is declared, like `inventoryWorkerUrls`
|
|
413
|
+
*/
|
|
414
|
+
export function namedInventoryWorkers({ inventoryPath = INVENTORY, groupVarsPath = GROUP_VARS } = {}) {
|
|
415
|
+
try {
|
|
416
|
+
const port = portFromGroupVars(readFileSync(groupVarsPath, "utf8"));
|
|
417
|
+
const inventory = readFileSync(inventoryPath, "utf8");
|
|
418
|
+
const names = workerNamesFromInventory(inventory, { port });
|
|
419
|
+
return workersFromInventory(inventory, { port })
|
|
420
|
+
.map((url) => ({ name: names[url] ?? url.replace(/^https?:\/\//, ""), url }));
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
return [];
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* WHICH WORKERS TO USE, AND WHERE THAT LIST CAME FROM — the one precedence, in one place.
|
|
428
|
+
*
|
|
429
|
+
* THREE MODULES HELD THREE DIFFERENT ANSWERS, and one of them carried a comment saying they had been
|
|
430
|
+
* unified. Measured 2026-08-29:
|
|
431
|
+
*
|
|
432
|
+
* doctor.mjs named -> inventory
|
|
433
|
+
* check-worker-code.mjs named -> LOCAL UTM POOL -> inventory
|
|
434
|
+
* capture-screenreader-dataset.mjs named -> LOCAL UTM POOL -> single-VM lease (never reads inventory)
|
|
435
|
+
*
|
|
436
|
+
* The corpus capture path's own comment reads "One parser, in fleet-env.mjs. This copy and doctor's and
|
|
437
|
+
* check-worker-code's had drifted apart on precedence, which meant a diagnostic could describe a different
|
|
438
|
+
* fleet from the one about to run." That unification covered the NAMED half only; the fallback order below
|
|
439
|
+
* it stayed three separate answers, so the sentence describes a fix that was half applied.
|
|
440
|
+
*
|
|
441
|
+
* The consequence is the one the comment predicted: on a Mac with any registered UTM guest, `worker:code`
|
|
442
|
+
* reports the local VM while `doctor` reports the five bare-metal boxes, and a capture dispatches to
|
|
443
|
+
* whichever the entry point happened to prefer.
|
|
444
|
+
*
|
|
445
|
+
* ## THE LOCAL UTM POOL IS LAST, and that is a deprecation, not a preference
|
|
446
|
+
*
|
|
447
|
+
* The local guests were a testing arrangement and are deprecated; `inventory.yml` is the fleet, and ADR
|
|
448
|
+
* 0012 already calls it the single source of truth. So the pool is a fallback for a checkout with NO
|
|
449
|
+
* inventory — a supported setup for an outside contributor with one Mac and no hardware — and never a
|
|
450
|
+
* contender with one. Any other order reproduces the divergence above on every Mac that still has a bundle
|
|
451
|
+
* registered.
|
|
452
|
+
*
|
|
453
|
+
* `local` is injected rather than imported: reading the UTM pool means shelling out to `utmctl`, and this
|
|
454
|
+
* module is imported by everything that needs a worker list, including on Linux. A caller that has no
|
|
455
|
+
* local-pool reader simply omits it.
|
|
456
|
+
*
|
|
457
|
+
* @param {{ named?: () => {url: string}[], inventory?: () => string[], local?: () => string[] }} readers
|
|
458
|
+
* @returns {{ urls: string[], source: string }}
|
|
459
|
+
*/
|
|
460
|
+
export function resolveWorkerPool({ named = configuredWorkers, inventory = inventoryWorkerUrls, local = () => [], } = {}) {
|
|
461
|
+
const configured = named();
|
|
462
|
+
// Naming workers means you are managing them — nothing is started or stopped for you — so an explicit
|
|
463
|
+
// list always wins. That half was already consistent everywhere; it is the fallback below that was not.
|
|
464
|
+
if (configured.length)
|
|
465
|
+
return { urls: configured.map((w) => w.url), source: "A11Y_WORKER(S)" };
|
|
466
|
+
const fleet = inventory();
|
|
467
|
+
if (fleet.length)
|
|
468
|
+
return { urls: fleet, source: "inventory.yml" };
|
|
469
|
+
const pool = local();
|
|
470
|
+
if (pool.length)
|
|
471
|
+
return { urls: pool, source: "the local UTM pool (DEPRECATED — see inventory.yml)" };
|
|
472
|
+
// Names what it LOOKED IN, never a bare "nothing found". `lab:inventory`'s rule: "'none here' and 'none
|
|
473
|
+
// anywhere' are different answers, and it now refuses to turn the first into the second."
|
|
474
|
+
return { urls: [], source: "A11Y_WORKER(S), inventory.yml and the local UTM pool — all empty" };
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* What `fleet:env` prints, as data: `stdout` is what a shell reads, `stderr` is what the operator reads.
|
|
478
|
+
*
|
|
479
|
+
* `mode: "list"` is the WHOLE fleet, one URL per line, and leaves nobody out. The default is the capture
|
|
480
|
+
* set as an `export`, and it NAMES every host it left out on stderr -- an omission with no line is a
|
|
481
|
+
* machine nothing reports, which is the failure this module's header is about. stderr, because stdout is
|
|
482
|
+
* `eval`-ed.
|
|
483
|
+
*
|
|
484
|
+
* @param {string} text the inventory
|
|
485
|
+
* @param {{ port?: number, mode?: "env" | "list" }} [options]
|
|
486
|
+
* @returns {{stdout: string, stderr: string}}
|
|
487
|
+
*/
|
|
488
|
+
export function fleetEnvOutput(text, { port = DEFAULT_WORKER_PORT, mode = "env" } = {}) {
|
|
489
|
+
if (mode === "list") {
|
|
490
|
+
return { stdout: `${workersFromInventory(text, { port }).join("\n")}\n`, stderr: "" };
|
|
491
|
+
}
|
|
492
|
+
const workers = workersFromInventory(text, { port, scope: "capture" });
|
|
493
|
+
const stderr = hostsOutOfCaptureSet(text, { port })
|
|
494
|
+
.map(({ name, url }) => `fleet:env: ${name} (${url}) is NOT in A11Y_WORKERS -- its inventory entry declares ${CAPTURE_KEY}: false. `
|
|
495
|
+
+ "It is still enrolled: `--list`, `doctor` and `fleet:status` name it.\n")
|
|
496
|
+
.join("");
|
|
497
|
+
// Shell-quoted, so `eval "$(npm run --silent fleet:env)"` is safe even if a hostname ever contains
|
|
498
|
+
// something the shell would otherwise split on.
|
|
499
|
+
return { stdout: `export A11Y_WORKERS='${workers.join(",")}'\n`, stderr };
|
|
500
|
+
}
|
|
501
|
+
function main() {
|
|
502
|
+
const port = portFromGroupVars(readFileSync(GROUP_VARS, "utf8"));
|
|
503
|
+
const { stdout, stderr } = fleetEnvOutput(readFileSync(INVENTORY, "utf8"), { port, mode: process.argv.includes("--list") ? "list" : "env" });
|
|
504
|
+
process.stderr.write(stderr);
|
|
505
|
+
process.stdout.write(stdout);
|
|
506
|
+
}
|
|
507
|
+
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href)
|
|
508
|
+
main();
|
|
509
|
+
//# sourceMappingURL=fleet-env.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleet-env.mjs","sourceRoot":"","sources":["../src/fleet-env.mjs"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Absolute paths to the provisioning and lifecycle scripts. Absolute, because a consumer spawns them. */
|
|
2
|
+
export function fleetScriptPaths(): {
|
|
3
|
+
dir: string;
|
|
4
|
+
workerCtl: string;
|
|
5
|
+
buildVm: string;
|
|
6
|
+
cloneWorker: string;
|
|
7
|
+
createUtmVm: string;
|
|
8
|
+
fetchWindowsIso: string;
|
|
9
|
+
provisioning: string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=fleet-scripts.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleet-scripts.d.mts","sourceRoot":"","sources":["../src/fleet-scripts.mjs"],"names":[],"mappings":"AA6BA,0GAA0G;AAC1G;;;;;;;;EAWC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* Where the fleet's shell, PowerShell and XML assets live — ONE definition.
|
|
4
|
+
*
|
|
5
|
+
* `worker-ctl.sh` was resolved independently in four modules: `local-vm.ts`, `doctor.mjs`,
|
|
6
|
+
* `deploy-worker.mjs` and `check-worker-code.mjs`. Three of them agreed and the fourth did not —
|
|
7
|
+
* `local-vm.ts` said `../../scripts/local-worker/…`, which after M6 pointed at `packages/scripts/…`, a
|
|
8
|
+
* directory that does not exist. So `leaseWorker`, the primary export of this package, could not find the
|
|
9
|
+
* script it drives.
|
|
10
|
+
*
|
|
11
|
+
* It went unnoticed because every check that exercised a lease set `A11Y_WORKER`, which short-circuits VM
|
|
12
|
+
* discovery — so the DEFAULT path, the one CLAUDE.md documents as "with no A11Y_WORKER set the run finds the
|
|
13
|
+
* local VM", was the one path never run. Four copies of a fact is three chances to be wrong about it.
|
|
14
|
+
*
|
|
15
|
+
* `.mjs` rather than `.ts` on purpose: the fleet's own scripts are `.mjs` and are run directly with
|
|
16
|
+
* `node packages/worker-fleet/src/doctor.mjs`, with nothing compiled first, so they can only import `.mjs`
|
|
17
|
+
* siblings.
|
|
18
|
+
*/
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
/**
|
|
22
|
+
* `../src/local-worker/`, NOT `./local-worker/`. These assets are not JavaScript, so tsc does not copy them
|
|
23
|
+
* into `dist` — they ship from `src`. Resolving via the package root works from `src/…` under tsx and from
|
|
24
|
+
* `dist/…` when installed, because the two are siblings one level below it. Getting this wrong is not subtle:
|
|
25
|
+
* the isolation gate failed with `dist/local-worker/` missing.
|
|
26
|
+
*/
|
|
27
|
+
const assetDir = () => fileURLToPath(new URL("../src/local-worker/", import.meta.url));
|
|
28
|
+
/** Absolute paths to the provisioning and lifecycle scripts. Absolute, because a consumer spawns them. */
|
|
29
|
+
export function fleetScriptPaths() {
|
|
30
|
+
const dir = assetDir();
|
|
31
|
+
return {
|
|
32
|
+
dir,
|
|
33
|
+
workerCtl: join(dir, "worker-ctl.sh"),
|
|
34
|
+
buildVm: join(dir, "build-vm.sh"),
|
|
35
|
+
cloneWorker: join(dir, "clone-worker.sh"),
|
|
36
|
+
createUtmVm: join(dir, "create-utm-vm.sh"),
|
|
37
|
+
fetchWindowsIso: join(dir, "fetch-windows-iso.sh"),
|
|
38
|
+
provisioning: fileURLToPath(new URL("../src/provisioning/", import.meta.url)),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=fleet-scripts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleet-scripts.mjs","sourceRoot":"","sources":["../src/fleet-scripts.mjs"],"names":[],"mappings":"AAAA,YAAY;AACZ;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,sBAAsB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAEvF,0GAA0G;AAC1G,MAAM,UAAU,gBAAgB;IAC9B,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC;IACvB,OAAO;QACL,GAAG;QACH,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC;QACrC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC;QACjC,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC;QACzC,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC;QAC1C,eAAe,EAAE,IAAI,CAAC,GAAG,EAAE,sBAAsB,CAAC;QAClD,YAAY,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,sBAAsB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KAC9E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `process.env` with every `GIT_*` key removed, `extra` applied on top. A prefix strip, never a list
|
|
3
|
+
* lookup, so a new `GIT_*` variable is caught without this file needing to know its name.
|
|
4
|
+
* @param {Record<string, string>} [extra]
|
|
5
|
+
* @returns {Record<string, string | undefined>}
|
|
6
|
+
*/
|
|
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[];
|
|
10
|
+
//# sourceMappingURL=git-safe-env.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-safe-env.d.mts","sourceRoot":"","sources":["../src/git-safe-env.mjs"],"names":[],"mappings":"AA8BA;;;;;GAKG;AACH,sCAHW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAS9C;AAxBD,gCAAgC;AAChC,sCADW,SAAS,MAAM,EAAE,CAS1B"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// A DELIBERATE, DISCLOSED DUPLICATE of `packages/guards/src/git-env.mjs` at the repo root.
|
|
2
|
+
//
|
|
3
|
+
// git EXPORTS `GIT_DIR`/`GIT_WORK_TREE`/`GIT_INDEX_FILE` into every hook environment, and a process that
|
|
4
|
+
// spawns `git` with an inherited `env` operates on whatever `GIT_DIR` names, not on `cwd` -- see
|
|
5
|
+
// `packages/guards/src/git-env.mjs`'s header for the incident that proved it (a pre-push-hook test forged 15 commits
|
|
6
|
+
// across all refs of the real repo).
|
|
7
|
+
//
|
|
8
|
+
// This package publishes `check-worker-code.mjs` and `deploy-worker.mjs` as `bin` entries
|
|
9
|
+
// (`package.json`), so every file they import -- `code-drift.mjs` among them -- ships in the published
|
|
10
|
+
// tarball and can only import from INSIDE `@a11ign/screenreader-fleet`. The repo-root `scripts/` directory
|
|
11
|
+
// does not exist once this package is installed from npm, so importing it here would work in this
|
|
12
|
+
// monorepo and break for every real consumer. That is the entire reason this file exists rather than a
|
|
13
|
+
// relative import to the root: not stylistic preference, a publish-boundary constraint (see ADR 0004).
|
|
14
|
+
//
|
|
15
|
+
// Kept textually identical to `packages/guards/src/git-env.mjs` and pinned equal to it by
|
|
16
|
+
// `git-safe-env.test.ts`, which is this repo's own remedy #3 ("pin them equal with a test") for a fact
|
|
17
|
+
// that CANNOT be stated once because the two copies cross a package-publishing boundary neither can
|
|
18
|
+
// import through.
|
|
19
|
+
/** @type {readonly string[]} */
|
|
20
|
+
export const KNOWN_GIT_REDIRECT_VARS = [
|
|
21
|
+
"GIT_DIR",
|
|
22
|
+
"GIT_WORK_TREE",
|
|
23
|
+
"GIT_INDEX_FILE",
|
|
24
|
+
"GIT_CEILING_DIRECTORIES",
|
|
25
|
+
"GIT_OBJECT_DIRECTORY",
|
|
26
|
+
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
27
|
+
"GIT_COMMON_DIR",
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* `process.env` with every `GIT_*` key removed, `extra` applied on top. A prefix strip, never a list
|
|
31
|
+
* lookup, so a new `GIT_*` variable is caught without this file needing to know its name.
|
|
32
|
+
* @param {Record<string, string>} [extra]
|
|
33
|
+
* @returns {Record<string, string | undefined>}
|
|
34
|
+
*/
|
|
35
|
+
export function sandboxGitEnv(extra = {}) {
|
|
36
|
+
/** @type {Record<string, string | undefined>} */
|
|
37
|
+
const scrubbed = {};
|
|
38
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
39
|
+
if (!key.startsWith("GIT_"))
|
|
40
|
+
scrubbed[key] = value;
|
|
41
|
+
}
|
|
42
|
+
return { ...scrubbed, ...extra };
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=git-safe-env.mjs.map
|