@coreplane/switchboard 0.0.0 → 1.18.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 +201 -0
- package/README.md +18 -1
- package/dist/assets/.dockerignore +27 -0
- package/dist/assets/.env.example +33 -0
- package/dist/assets/Dockerfile +111 -0
- package/dist/assets/config/config.example.yaml +359 -0
- package/dist/assets/deploy/bin/build-stamp.d.mts +15 -0
- package/dist/assets/deploy/bin/build-stamp.mjs +98 -0
- package/dist/assets/deploy/bin/cf-logs +32 -0
- package/dist/assets/deploy/cloudflare/package.json +29 -0
- package/dist/assets/deploy/cloudflare/preflight.mjs +243 -0
- package/dist/assets/deploy/cloudflare/tsconfig.json +18 -0
- package/dist/assets/deploy/cloudflare/worker.ts +382 -0
- package/dist/assets/deploy/cloudflare/wrangler.template.jsonc +67 -0
- package/dist/assets/deploy/cloudflare/write-build.d.mts +7 -0
- package/dist/assets/deploy/cloudflare/write-build.mjs +53 -0
- package/dist/assets/deploy/cloudflare-docs/package.json +18 -0
- package/dist/assets/deploy/cloudflare-docs/wrangler.template.jsonc +30 -0
- package/dist/assets/deploy/cloudflare-memory/package.json +25 -0
- package/dist/assets/deploy/cloudflare-memory/tsconfig.json +17 -0
- package/dist/assets/deploy/cloudflare-memory/worker.ts +2635 -0
- package/dist/assets/deploy/cloudflare-memory/wrangler.template.jsonc +50 -0
- package/dist/assets/deploy/cloudflare-resident/Dockerfile +91 -0
- package/dist/assets/deploy/cloudflare-resident/gc.ts +287 -0
- package/dist/assets/deploy/cloudflare-resident/node-async-hooks.d.ts +11 -0
- package/dist/assets/deploy/cloudflare-resident/package.json +29 -0
- package/dist/assets/deploy/cloudflare-resident/preflight.mjs +224 -0
- package/dist/assets/deploy/cloudflare-resident/tsconfig.json +19 -0
- package/dist/assets/deploy/cloudflare-resident/worker.ts +6637 -0
- package/dist/assets/deploy/cloudflare-resident/wrangler.template.jsonc +120 -0
- package/dist/assets/deploy/cloudflare-sandbox/Dockerfile +67 -0
- package/dist/assets/deploy/cloudflare-sandbox/docker-wrapper.sh +37 -0
- package/dist/assets/deploy/cloudflare-sandbox/package.json +26 -0
- package/dist/assets/deploy/cloudflare-sandbox/tsconfig.json +20 -0
- package/dist/assets/deploy/cloudflare-sandbox/worker.ts +410 -0
- package/dist/assets/deploy/cloudflare-sandbox/wrangler.template.jsonc +67 -0
- package/dist/assets/deploy/profile.example.json +13 -0
- package/dist/assets/deploy/secrets.manifest.json +108 -0
- package/dist/assets/docker-entrypoint.sh +15 -0
- package/dist/assets/package-lock.json +18407 -0
- package/dist/assets/package.json +104 -0
- package/dist/assets/project.json +219 -0
- package/dist/assets/source.json +5 -0
- package/dist/assets/src/core/authz/actor.ts +100 -0
- package/dist/assets/src/core/authz/authorize.ts +169 -0
- package/dist/assets/src/core/authz/grants.ts +347 -0
- package/dist/assets/src/core/authz/policy.ts +281 -0
- package/dist/assets/src/core/authz/resource.ts +147 -0
- package/dist/assets/src/core/authz/types.ts +164 -0
- package/dist/assets/src/core/drain.ts +54 -0
- package/dist/assets/src/core/ingressTokens.ts +64 -0
- package/dist/assets/src/core/memory/engine.ts +115 -0
- package/dist/assets/src/core/memory/scorer.ts +147 -0
- package/dist/assets/src/core/memory/types.ts +120 -0
- package/dist/assets/src/core/normalizeSpans.ts +299 -0
- package/dist/assets/src/core/prDescriptionTypes.ts +54 -0
- package/dist/assets/src/core/redact.ts +113 -0
- package/dist/assets/src/core/runEvents.ts +537 -0
- package/dist/assets/src/core/runFriction.ts +665 -0
- package/dist/assets/src/core/runLedger/decisions.ts +126 -0
- package/dist/assets/src/core/runLedger/types.ts +177 -0
- package/dist/assets/src/core/runRecord.ts +627 -0
- package/dist/assets/src/core/runShape.ts +61 -0
- package/dist/assets/src/core/schedules.ts +452 -0
- package/dist/assets/src/core/time/formatDuration.ts +61 -0
- package/dist/assets/src/core/trace/attrs.ts +203 -0
- package/dist/assets/src/core/trace/classify.ts +49 -0
- package/dist/assets/src/core/trace/clock.ts +6 -0
- package/dist/assets/src/core/trace/context.ts +9 -0
- package/dist/assets/src/core/trace/ids.ts +23 -0
- package/dist/assets/src/core/trace/partition.ts +235 -0
- package/dist/assets/src/core/trace/sinks.ts +68 -0
- package/dist/assets/src/core/trace/streamSpans.ts +163 -0
- package/dist/assets/src/core/trace/traceparent.ts +29 -0
- package/dist/assets/src/core/trace/tracer.ts +247 -0
- package/dist/assets/src/core/trace/types.ts +125 -0
- package/dist/assets/src/core/trace/workerTrace.ts +97 -0
- package/dist/assets/src/deploy/buildStamp.ts +93 -0
- package/dist/assets/src/deploy/liveGate.ts +203 -0
- package/dist/assets/src/deploy/profile.ts +162 -0
- package/dist/assets/src/deploy/restart.ts +393 -0
- package/dist/assets/src/effort.ts +17 -0
- package/dist/assets/src/execution/bashTimeout.ts +78 -0
- package/dist/assets/src/execution/bindingPurge.ts +43 -0
- package/dist/assets/src/execution/residentBackupTransfer.ts +50 -0
- package/dist/assets/src/execution/residentCleanliness.ts +95 -0
- package/dist/assets/src/execution/residentCredentials.ts +81 -0
- package/dist/assets/src/execution/residentDepCache.ts +321 -0
- package/dist/assets/src/execution/residentDepsStore.ts +326 -0
- package/dist/assets/src/execution/residentDetach.ts +48 -0
- package/dist/assets/src/execution/residentDisk.ts +107 -0
- package/dist/assets/src/execution/residentDiskBudget.ts +448 -0
- package/dist/assets/src/execution/residentExecWrap.ts +100 -0
- package/dist/assets/src/execution/residentHead.ts +85 -0
- package/dist/assets/src/execution/residentReadonly.ts +72 -0
- package/dist/assets/src/execution/residentRefresh.ts +429 -0
- package/dist/assets/src/execution/residentRestoreExtract.ts +130 -0
- package/dist/assets/src/execution/residentState.ts +47 -0
- package/dist/assets/src/execution/residentStepReport.ts +98 -0
- package/dist/assets/src/execution/residentStepTrace.ts +97 -0
- package/dist/assets/src/execution/residentSteps.ts +99 -0
- package/dist/assets/src/execution/residentText.ts +83 -0
- package/dist/assets/src/execution/residentTrace.ts +119 -0
- package/dist/assets/src/execution/sandboxEnv.ts +42 -0
- package/dist/assets/src/execution/sandboxErrors.ts +159 -0
- package/dist/assets/src/execution/sandboxKeepalive.ts +118 -0
- package/dist/assets/src/execution/shellQuote.ts +8 -0
- package/dist/assets/src/mcp/registry.ts +242 -0
- package/dist/assets/src/providers/types.ts +152 -0
- package/dist/assets/web/dist/.vite/manifest.json +176 -0
- package/dist/assets/web/dist/assets/AppShell-Bk2gbvet.js +1 -0
- package/dist/assets/web/dist/assets/CostsPage-CTZcMYYx.js +1 -0
- package/dist/assets/web/dist/assets/NotFoundPage-C-BuaSm8.js +1 -0
- package/dist/assets/web/dist/assets/ResidentDetailPage-D3shEnzl.js +1 -0
- package/dist/assets/web/dist/assets/ResidentsIndexPage-DWIubQ05.js +1 -0
- package/dist/assets/web/dist/assets/RunRoutePage-BMjuE-oX.js +126 -0
- package/dist/assets/web/dist/assets/RunRoutePage-XVFj0XDc.css +1 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-C3_jYIo0.js +1 -0
- package/dist/assets/web/dist/assets/RunsTabs-C4krAL9o.js +1 -0
- package/dist/assets/web/dist/assets/ScheduledPage-g1W58mtN.js +1 -0
- package/dist/assets/web/dist/assets/StatusDot-DcPRw3zu.js +1 -0
- package/dist/assets/web/dist/assets/Tooltip-DJUkMYjo.js +1 -0
- package/dist/assets/web/dist/assets/favicon-DL1rdWJt.js +1 -0
- package/dist/assets/web/dist/assets/localIso-L06jV29p.js +1 -0
- package/dist/assets/web/dist/assets/main-BsBGUyMH.css +2 -0
- package/dist/assets/web/dist/assets/main-CyM5f4JC.js +28 -0
- package/dist/assets/web/dist/assets/residentDiskBudget-BMBKlYRH.js +1 -0
- package/dist/assets/web/dist/assets/seed-BglCRKLA.js +6 -0
- package/dist/assets/web/dist/assets/wallClock-Ckv3sKoR.js +1 -0
- package/dist/cli.js +34494 -0
- package/package.json +43 -10
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Deploy preflight for the resident Worker (docs/reference/specs/resident-repos.md item 44).
|
|
3
|
+
//
|
|
4
|
+
// `wrangler deploy` replaces every ResidentDO isolate and invalidates the
|
|
5
|
+
// Sandbox SDK process handles held by in-flight runs ("Process handle refers
|
|
6
|
+
// to a previous runtime incarnation") — the container-restart deferral in
|
|
7
|
+
// `reconcileImage` cannot protect against a Worker deploy. So `npm run deploy`
|
|
8
|
+
// runs this first: it asks the live Worker's `GET /residents` for every
|
|
9
|
+
// resident's in-flight count and exits non-zero while anything is running.
|
|
10
|
+
//
|
|
11
|
+
// Fail closed: no bearer, unreachable Worker, or a resident whose live view
|
|
12
|
+
// errored all refuse — a deploy never proceeds blind. `RESIDENT_DEPLOY_FORCE=1`
|
|
13
|
+
// (or `--force` when run directly) is the explicit, warned bypass.
|
|
14
|
+
//
|
|
15
|
+
// Dependency-free Node (fetch is built in). `decide()` is pure and unit-tested
|
|
16
|
+
// (preflight.test.mjs); `main()` only does I/O around it.
|
|
17
|
+
|
|
18
|
+
import { pathToFileURL } from "node:url";
|
|
19
|
+
|
|
20
|
+
/** The env var naming this Worker's origin. `deploy all` sets it from the deployment profile; the
|
|
21
|
+
* preflight has no address of its own (deploy/profile.json is the only place the fleet's hostnames live). */
|
|
22
|
+
export const BASE_URL_ENV = "RESIDENT_BASE_URL";
|
|
23
|
+
export const TOKEN_ENV_VARS = ["RESIDENT_ADMIN_TOKEN", "RESIDENT_OPERATOR_TOKEN", "RESIDENT_READ_TOKEN"];
|
|
24
|
+
|
|
25
|
+
const HOW_TO_SET_TOKEN =
|
|
26
|
+
`set one of ${TOKEN_ENV_VARS.join(" / ")} in the environment (the same bearer ` +
|
|
27
|
+
"`npm run secrets` put on the Worker; any scope may read /residents), e.g. " +
|
|
28
|
+
"`RESIDENT_ADMIN_TOKEN=… npm run deploy`";
|
|
29
|
+
const HOW_TO_FORCE =
|
|
30
|
+
"to deploy anyway (this WILL kill in-flight runs): `RESIDENT_DEPLOY_FORCE=1 npm run deploy` (`node preflight.mjs --force` checks alone)";
|
|
31
|
+
/** Lifecycle states in which NOTHING is executing on the resident — the only
|
|
32
|
+
* states a deploy may land on. Every other state is either known mid-cycle
|
|
33
|
+
* (the engine is running a refresh's fetch/rebuild, a restore, or
|
|
34
|
+
* provisioning — an isolate swap kills that like a thread run) or a
|
|
35
|
+
* state this script does not know, which fails closed as `unknown`: this is
|
|
36
|
+
* plain JS outside the shared `ResidentLifecycleState` type, so an allow-list
|
|
37
|
+
* of settled states is what keeps vocabulary drift from silently allowing. */
|
|
38
|
+
const SETTLED_STATES = new Set(["warm", "degraded", "down"]);
|
|
39
|
+
/** The engine is executing and an isolate swap would FAIL it: provisioning
|
|
40
|
+
* (`onboarding`) has no checkpoint to resume from — a killed install or build
|
|
41
|
+
* is `provision-failed`, `down`, and only a rebuild recovers it. */
|
|
42
|
+
const PROVISIONING_STATES = new Set(["onboarding"]);
|
|
43
|
+
/** The engine is executing and an isolate swap merely INTERRUPTS it: a refresh
|
|
44
|
+
* cycle re-arms in 45 s and resumes from its disk checkpoints (resident-repos
|
|
45
|
+
* items 44 and 48); a restore is retried by the next hydrate, which first
|
|
46
|
+
* unmounts and removes whatever the interrupted one left (item 61). These
|
|
47
|
+
* used to refuse like `onboarding`, and a release deploy once spent its whole
|
|
48
|
+
* budget behind a resident stuck in `restoring` — a state an isolate swap
|
|
49
|
+
* would have HELPED. Allowed with a warning. */
|
|
50
|
+
const INTERRUPTIBLE_STATES = new Set(["refreshing", "restoring"]);
|
|
51
|
+
|
|
52
|
+
/** First present, non-blank bearer in preference order; null when none. */
|
|
53
|
+
export function readToken(env) {
|
|
54
|
+
for (const name of TOKEN_ENV_VARS) {
|
|
55
|
+
const v = env[name];
|
|
56
|
+
if (typeof v === "string" && v.trim() !== "") return v;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** GET /residents. Never throws: `{ok:true,payload}` or `{ok:false,error}`. */
|
|
62
|
+
export async function fetchResidents(baseUrl, token, { timeoutMs = 15_000 } = {}) {
|
|
63
|
+
if (!token) return { ok: false, error: "no bearer in the environment" };
|
|
64
|
+
const url = new URL("/residents", baseUrl).toString();
|
|
65
|
+
try {
|
|
66
|
+
const res = await fetch(url, {
|
|
67
|
+
headers: { authorization: `Bearer ${token}` },
|
|
68
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
69
|
+
});
|
|
70
|
+
const text = await res.text();
|
|
71
|
+
if (!res.ok) return { ok: false, error: `GET ${url} → HTTP ${res.status}: ${text.slice(0, 200)}` };
|
|
72
|
+
try {
|
|
73
|
+
return { ok: true, payload: JSON.parse(text) };
|
|
74
|
+
} catch {
|
|
75
|
+
return { ok: false, error: `GET ${url} → non-JSON body: ${text.slice(0, 200)}` };
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
return { ok: false, error: `GET ${url} failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The decision, pure. `fetched` is the result of `fetchResidents`.
|
|
84
|
+
* @returns {{ allow: boolean, forced: boolean, busy: {resource:string,inFlight:number}[], provisioning: {resource:string,state:string}[], interrupting: {resource:string,state:string}[], unknown: {resource:string,error:string}[], message: string }}
|
|
85
|
+
*/
|
|
86
|
+
export function decide(fetched, { force = false } = {}) {
|
|
87
|
+
const busy = [];
|
|
88
|
+
const provisioning = [];
|
|
89
|
+
const interrupting = [];
|
|
90
|
+
const unknown = [];
|
|
91
|
+
const problems = [];
|
|
92
|
+
|
|
93
|
+
if (!fetched || fetched.ok !== true) {
|
|
94
|
+
problems.push(`resident Worker not consulted: ${fetched?.error ?? "unknown error"}`);
|
|
95
|
+
} else {
|
|
96
|
+
const residents = fetched.payload?.residents;
|
|
97
|
+
if (!Array.isArray(residents)) {
|
|
98
|
+
problems.push("resident Worker answered without a `residents` array (malformed /residents payload)");
|
|
99
|
+
} else {
|
|
100
|
+
for (const r of residents) {
|
|
101
|
+
const resource = typeof r?.resource === "string" ? r.resource : "<unnamed resident>";
|
|
102
|
+
const live = r?.live;
|
|
103
|
+
if (!live || typeof live !== "object" || typeof live.error === "string") {
|
|
104
|
+
unknown.push({ resource, error: live?.error ?? "no live view" });
|
|
105
|
+
} else if (typeof live.inFlight !== "number") {
|
|
106
|
+
unknown.push({
|
|
107
|
+
resource,
|
|
108
|
+
error:
|
|
109
|
+
"live view carries no inFlight count (Worker predates the preflight — deploy once with RESIDENT_DEPLOY_FORCE=1)",
|
|
110
|
+
});
|
|
111
|
+
} else if (!Number.isInteger(live.inFlight) || live.inFlight < 0) {
|
|
112
|
+
// A count that is not a non-negative integer can only come from a
|
|
113
|
+
// counter bug (double release, unmatched decrement). We cannot tell
|
|
114
|
+
// idle from busy, so it is unknown — never an implicit "0 busy".
|
|
115
|
+
unknown.push({ resource, error: `live view carries an impossible inFlight=${live.inFlight} (counter bug)` });
|
|
116
|
+
} else if (
|
|
117
|
+
live.runsInFlight !== undefined &&
|
|
118
|
+
(typeof live.runsInFlight !== "number" || !Number.isInteger(live.runsInFlight) || live.runsInFlight < 0)
|
|
119
|
+
) {
|
|
120
|
+
unknown.push({
|
|
121
|
+
resource,
|
|
122
|
+
error: `live view carries an impossible runsInFlight=${live.runsInFlight} (counter bug)`,
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
// `inFlight` counts the refresh cycle itself along with the runs, so a
|
|
126
|
+
// resident that is merely refreshing reads ≥ 1. The busy decision is
|
|
127
|
+
// about what a swap kills for a USER — the runs — which a Worker since
|
|
128
|
+
// item 44's revision reports apart as `runsInFlight`; an older Worker
|
|
129
|
+
// without the field is judged on `inFlight` as before (over-refusing,
|
|
130
|
+
// never under). Runs and cycles are independent facts; report BOTH so
|
|
131
|
+
// an operator who waits for the runs is not surprised by a second refusal.
|
|
132
|
+
const runs = typeof live.runsInFlight === "number" ? live.runsInFlight : live.inFlight;
|
|
133
|
+
if (runs > 0) busy.push({ resource, inFlight: runs });
|
|
134
|
+
if (PROVISIONING_STATES.has(live.state)) {
|
|
135
|
+
// Provisioning is mid-flight. An isolate swap kills it just like a
|
|
136
|
+
// thread run (seen live: `build-failed: exit 143: Session
|
|
137
|
+
// terminated` right after a deploy that passed the inFlight
|
|
138
|
+
// check), and unlike a refresh it cannot resume.
|
|
139
|
+
provisioning.push({ resource, state: live.state });
|
|
140
|
+
} else if (INTERRUPTIBLE_STATES.has(live.state)) {
|
|
141
|
+
interrupting.push({ resource, state: live.state });
|
|
142
|
+
} else if (!SETTLED_STATES.has(live.state)) {
|
|
143
|
+
// A state this script does not know: fail closed rather than assume settled.
|
|
144
|
+
unknown.push({
|
|
145
|
+
resource,
|
|
146
|
+
error: `live view carries an unrecognized state ${JSON.stringify(live.state)} (vocabulary drift — update preflight.mjs)`,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (busy.length)
|
|
152
|
+
problems.push(`in flight: ${busy.map((b) => `${b.resource} (${b.inFlight} in flight)`).join(", ")}`);
|
|
153
|
+
if (provisioning.length)
|
|
154
|
+
problems.push(
|
|
155
|
+
`provisioning: ${provisioning.map((m) => `${m.resource} (${m.state})`).join(", ")} — the provision would be killed and only a rebuild recovers it`,
|
|
156
|
+
);
|
|
157
|
+
if (unknown.length)
|
|
158
|
+
problems.push(`unknown state: ${unknown.map((u) => `${u.resource} (${u.error})`).join(", ")}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Interruptible cycles never refuse; they are named so the log says what the
|
|
163
|
+
// swap interrupts and why that is fine.
|
|
164
|
+
const warningText = interrupting.length
|
|
165
|
+
? ` — WARNING: mid-cycle: ${interrupting.map((m) => `${m.resource} (${m.state})`).join(", ")} — the isolate swap interrupts it; a refresh re-arms in 45 s from its checkpoints, a restore is retried by the next hydrate (resident-repos items 44/61)`
|
|
166
|
+
: "";
|
|
167
|
+
|
|
168
|
+
if (problems.length === 0) {
|
|
169
|
+
const count = fetched.payload.residents.length;
|
|
170
|
+
return {
|
|
171
|
+
allow: true,
|
|
172
|
+
forced: false,
|
|
173
|
+
busy,
|
|
174
|
+
provisioning,
|
|
175
|
+
interrupting,
|
|
176
|
+
unknown,
|
|
177
|
+
message: `preflight ok: ${count} residents, no resident has work in flight or a provision running${warningText}`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const detail = problems.map((p) => ` - ${p}`).join("\n");
|
|
181
|
+
if (force) {
|
|
182
|
+
return {
|
|
183
|
+
allow: true,
|
|
184
|
+
forced: true,
|
|
185
|
+
busy,
|
|
186
|
+
provisioning,
|
|
187
|
+
interrupting,
|
|
188
|
+
unknown,
|
|
189
|
+
message: `preflight WARNING: deploying by force despite —\n${detail}\n in-flight runs and provisions on the residents above WILL be killed (process handles invalidated by the isolate swap)${warningText}`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const hints = [HOW_TO_FORCE];
|
|
193
|
+
if (fetched?.ok !== true) hints.unshift(HOW_TO_SET_TOKEN);
|
|
194
|
+
return {
|
|
195
|
+
allow: false,
|
|
196
|
+
forced: false,
|
|
197
|
+
busy,
|
|
198
|
+
provisioning,
|
|
199
|
+
interrupting,
|
|
200
|
+
unknown,
|
|
201
|
+
message: `preflight REFUSED: a Worker deploy swaps every ResidentDO isolate and kills in-flight runs and provisions —\n${detail}\n wait for them to finish and retry; ${hints.join("; ")}${warningText}`,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export async function main(argv = process.argv.slice(2), env = process.env) {
|
|
206
|
+
const force = argv.includes("--force") || env.RESIDENT_DEPLOY_FORCE === "1";
|
|
207
|
+
const baseUrl = env[BASE_URL_ENV];
|
|
208
|
+
if (!baseUrl) {
|
|
209
|
+
console.error(
|
|
210
|
+
`[resident-preflight] ${BASE_URL_ENV} is not set — the resident Worker's origin comes from the deployment profile; deploy through \`npm run cli -- deploy all\` (it sets it), or set it to https://<resident hostname> to run this alone`,
|
|
211
|
+
);
|
|
212
|
+
return 2;
|
|
213
|
+
}
|
|
214
|
+
const fetched = await fetchResidents(baseUrl, readToken(env));
|
|
215
|
+
const d = decide(fetched, { force });
|
|
216
|
+
(d.allow && !d.forced ? console.log : console.error)(`[resident-preflight] ${d.message}`);
|
|
217
|
+
return d.allow ? 0 : 1;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Run only when executed directly (`node preflight.mjs`), not when imported by tests.
|
|
221
|
+
// pathToFileURL, not `file://${argv[1]}`, so the guard also holds on Windows paths.
|
|
222
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
223
|
+
process.exitCode = await main();
|
|
224
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Scoped config so `npm run typecheck` checks worker.ts on its own terms.
|
|
3
|
+
// Without it tsc falls back to the repo-root tsconfig, which excludes this
|
|
4
|
+
// directory — and TypeScript 7 refuses `tsc <file>` outright when any
|
|
5
|
+
// tsconfig.json is discoverable (TS5112), so the options live here instead
|
|
6
|
+
// of on the command line. Types resolve through node resolution, so they
|
|
7
|
+
// are found wherever npm installed them.
|
|
8
|
+
"compilerOptions": {
|
|
9
|
+
"target": "es2022",
|
|
10
|
+
"module": "es2022",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"lib": ["es2022"],
|
|
13
|
+
"types": ["@cloudflare/workers-types"],
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"skipLibCheck": true
|
|
17
|
+
},
|
|
18
|
+
"include": ["worker.ts", "node-async-hooks.d.ts"]
|
|
19
|
+
}
|