cursedops 0.16.1 → 0.17.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursedops",
3
- "version": "0.16.1",
3
+ "version": "0.17.0",
4
4
  "description": "The build-and-ops answers this generation's apps wrote independently and identically: finding a generation's roots — and printing a command that runs when pasted — without knowing a path, the generation's whole-tree laws run over one repo from a checkout or a worktree, macOS launchd agent install/replace/remove and the live port a job serves, the scaffolding and verdicts of a deployed smoke (origin probe, the smoke's own environment, a network that lies about DNS, a settled version), the static-serving helpers eight apps copied — the path-traversal guard among them — the API floor that keeps an unmatched /api/... from ever being answered with the app shell, the commit and dirty flag a checkout-served process reports, the Cloudflare Worker deploy toolkit four apps copied (the deploy sequence, exact-set secrets over a pipe, origin-first rollback, the curl edge fetch, the row-for-row D1 import proof, the billed-CPU tail check around a deploy's walk and smoke, and each app's worker:secrets and worker:smoke main as one function of its data), the relay a Worker fronts a Mac-bound app with (the Durable Object, the frames, the Mac's dialer and key rotation — lifted from station for roms — and the signed-in stage walk's skeleton and the relay app's whole worker:deploy), the Worker import-graph and await-port checks every Worker app's suite runs over its own source, and the public-surface ratchet three published libraries each carried a forked copy of. Mechanism only — no app knows its name from here. Bun, zero runtime dependencies (typescript is an optional peer, for public-surface only), ships source.",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/edgeFetch.ts CHANGED
@@ -59,10 +59,18 @@ export interface EdgeFetchOptions {
59
59
  }
60
60
 
61
61
  /**
62
- * A `RequestInit` this fetch reads one more field from: `maxTimeSec`, curl's `--max-time` for
63
- * THIS request, over the instance's {@link EdgeFetchOptions.maxTimeSec}. A plain `fetch` ignores it.
62
+ * A `RequestInit` this fetch reads two more fields from. A plain `fetch` ignores both.
63
+ *
64
+ * · `maxTimeSec` — curl's `--max-time` for THIS request, over the instance's
65
+ * {@link EdgeFetchOptions.maxTimeSec};
66
+ * · `replaySafe` — the caller's word that sending this request twice does nothing the first did
67
+ * not, WHATEVER its method: a smoke's anonymous census probe, which the origin gate refuses
68
+ * before any handler runs. Such a request is replayed after a timeout or a dropped reply like a
69
+ * GET. Measured 2026-10-13: family's stage smoke died on census route 55 of 101, an anonymous
70
+ * `POST /api/media` that never reached the Worker (no trace of it), because a POST's timeout
71
+ * was never replayed — a green Worker, a red ship lane (forge task 3115).
64
72
  */
65
- export type EdgeRequestInit = RequestInit & { maxTimeSec?: number };
73
+ export type EdgeRequestInit = RequestInit & { maxTimeSec?: number; replaySafe?: boolean };
66
74
 
67
75
  /**
68
76
  * 🔴 How many MORE times a request is sent after a connection-level curl failure that carried
@@ -85,12 +93,13 @@ const EDGE_RETRIES = 2;
85
93
  * three of them. Measured 2026-09-26: flix's prod smoke went red on one `curl exit 28` for
86
94
  * `GET /healthz` while the same URL answered 200 in 0.2 s seconds later (task 2190).
87
95
  *
96
+ * A request its caller marked `replaySafe` ({@link EdgeRequestInit}) counts as safe to run twice.
88
97
  * Every other exit, and every HTTP status, whatever it is, is returned exactly as before.
89
98
  */
90
- function retryableCurlFailure(exit: number | null, stdout: Buffer, method = "GET", retry = 1): boolean {
99
+ function retryableCurlFailure(exit: number | null, stdout: Buffer, method = "GET", retry = 1, replaySafe = false): boolean {
91
100
  if (stdout.length > 0 || exit === null) return false;
92
101
  if (exit === 6 || exit === 7 || exit === 35) return true;
93
- const replayable = ["GET", "HEAD", "OPTIONS", "PUT", "DELETE"].includes(method.toUpperCase());
102
+ const replayable = replaySafe || ["GET", "HEAD", "OPTIONS", "PUT", "DELETE"].includes(method.toUpperCase());
94
103
  if (exit === 28) return replayable && retry === 1;
95
104
  return (exit === 52 || exit === 56) && replayable;
96
105
  }
@@ -181,7 +190,7 @@ export function createEdgeFetch(options: EdgeFetchOptions = {}): (url: string, i
181
190
  }
182
191
  try {
183
192
  let out = curl([...head(), ...args, target.toString()]);
184
- for (let retry = 1; retry <= EDGE_RETRIES && retryableCurlFailure(out.status, out.stdout, init.method, retry); retry++) {
193
+ for (let retry = 1; retry <= EDGE_RETRIES && retryableCurlFailure(out.status, out.stdout, init.method, retry, init.replaySafe); retry++) {
185
194
  await new Promise((r) => setTimeout(r, options.retryDelayMs?.(retry) ?? 500 * retry));
186
195
  // `head()` again: a host with no pin yet is asked again, so a retry can find the address.
187
196
  out = curl([...head(), ...args, target.toString()]);
package/src/relayStage.ts CHANGED
@@ -20,6 +20,7 @@ import { lookup as dnsLookup } from "node:dns/promises";
20
20
  import { readdirSync, readFileSync, statSync } from "node:fs";
21
21
  import { join, relative } from "node:path";
22
22
  import { sha256Hex } from "cursedops/relay";
23
+ import { awaitConverged, type ConvergenceOptions, type ConvergenceProbe } from "cursedops/smoke";
23
24
 
24
25
  /** The accounts door every stage signs in at — `apps/auth` deployed a second time. */
25
26
  export const STAGE_AUTH_URL = "https://auth-stage.cursedalchemy.com";
@@ -159,6 +160,30 @@ export const realRun: Run = (argv, options) => {
159
160
  return { status: done.status, stdout: done.stdout ?? "", stderr: done.stderr ?? "" };
160
161
  };
161
162
 
163
+ /**
164
+ * 🔴 How many rounds in a row every `/healthz` must name a just-put link digest before a walk uses it.
165
+ * A digest put is a new Worker version, and the edge takes it up instance by instance: the link
166
+ * staying up for 15 s proves only that the instance the link dialled has it. The Worker signs the
167
+ * Mac's identity with that digest, so a request answered by an instance still on the old one is the
168
+ * Mac's own 401 (and station's kept read a 503). Measured 2026-10-15 (forge task 3235): station's
169
+ * fast-lane walk at baf8495, the link steady for 15 s, then four writes 401 at the Mac; on the real
170
+ * stage the put needed 8 rounds to agree 5 in a row.
171
+ */
172
+ export const LINK_KEY_ROUNDS = 5;
173
+
174
+ /** The question {@link putStageLinkDigest} asks after a put: does this `/healthz` answer's `worker.linkKeyId` equal `expected`? */
175
+ export function linkKeyProbe(health: () => Promise<Response>, expected: string): ConvergenceProbe {
176
+ return {
177
+ name: "/healthz linkKeyId",
178
+ check: async () => {
179
+ const res = await health();
180
+ if (!res.ok) return `status ${res.status}`;
181
+ const id = ((await res.json()) as { worker?: { linkKeyId?: string } }).worker?.linkKeyId ?? "";
182
+ return id === expected ? null : `answers ${id || "no key"}, not ${expected}`;
183
+ },
184
+ };
185
+ }
186
+
162
187
  export interface DigestPut {
163
188
  /** The app's checkout, where `wrangler.jsonc` is. */
164
189
  cwd: string;
@@ -167,22 +192,32 @@ export interface DigestPut {
167
192
  key: string;
168
193
  /** `cloudflareCredential(...)`. */
169
194
  credential: Record<string, string>;
195
+ /** One `/healthz` read of the stage Worker — asked until every answer names the new digest. */
196
+ health: () => Promise<Response>;
197
+ /** The fingerprint `/healthz` publishes for a digest — the app's `cursedauth/relay-door` `linkKeyId(app, digest)`. */
198
+ keyId: (digest: string) => Promise<string>;
170
199
  run?: Run;
200
+ /** Injected for tests; `rounds` defaults to {@link LINK_KEY_ROUNDS}. */
201
+ convergence?: ConvergenceOptions;
171
202
  }
172
203
 
173
204
  /**
174
205
  * Put `key`'s digest on the STAGE Worker (`--env stage`, always — this module has no production
175
- * path). A per-run key for the walk, and again for the rotation leg. `ok: false` carries wrangler's
176
- * stderr. A secret put is a new version that reaches the edge in its own time: the caller waits for
177
- * a link that stays up ({@link linkedSteadily}), never a fixed sleep.
206
+ * path), then return only once every `/healthz` answer has named it {@link LINK_KEY_ROUNDS} rounds in
207
+ * a row — so no walk can put a digest and use it before the edge has (forge tasks 3235, 3237). A
208
+ * per-run key for the walk, and again for the rotation leg. `ok: false` is a refused put (wrangler's
209
+ * stderr) or an edge that never agreed; `detail` says which, for the walk's row.
178
210
  */
179
- export async function putStageLinkDigest(put: DigestPut): Promise<{ ok: boolean; stderr: string }> {
211
+ export async function putStageLinkDigest(put: DigestPut): Promise<{ ok: boolean; stderr: string; detail: string }> {
212
+ const digest = await sha256Hex(put.key);
180
213
  const done = (put.run ?? realRun)(["bunx", "wrangler", "secret", "put", put.secretName, "--env", "stage"], {
181
214
  cwd: put.cwd,
182
- input: await sha256Hex(put.key),
215
+ input: digest,
183
216
  env: put.credential,
184
217
  });
185
- return { ok: done.status === 0, stderr: done.stderr };
218
+ if (done.status !== 0) return { ok: false, stderr: done.stderr, detail: `wrangler refused the put (exit ${done.status})` };
219
+ const held = await awaitConverged([linkKeyProbe(put.health, await put.keyId(digest))], { rounds: LINK_KEY_ROUNDS, ...put.convergence });
220
+ return { ok: held.converged, stderr: done.stderr, detail: `every answer holds it — ${held.detail}` };
186
221
  }
187
222
 
188
223
  /**
package/src/smoke.ts CHANGED
@@ -1127,9 +1127,15 @@ export async function runWorkerSmoke(spec: WorkerSmokeSpec): Promise<number> {
1127
1127
  `credentials go back to ${JSON.stringify(health.publicUrl)} — this deployment's ${deployment.publicUrl}`,
1128
1128
  );
1129
1129
 
1130
+ // Each probe is anonymous and refused at the gate, so it is replay-safe whatever its method,
1131
+ // and one that gets no answer even then is this check's red — never a crash of the whole smoke.
1130
1132
  const open: string[] = [];
1131
1133
  for (const route of spec.census) {
1132
- const answered = (await ask(route.path, { method: route.method })).status;
1134
+ const probe: EdgeRequestInit = { method: route.method, replaySafe: true };
1135
+ const answered = await ask(route.path, probe).then(
1136
+ (res) => res.status,
1137
+ (error: Error) => `no answer (${error.message})`,
1138
+ );
1133
1139
  if (answered !== 401) open.push(`${route.method} ${route.path} → ${answered}`);
1134
1140
  }
1135
1141
  smoke.record(
@@ -1214,7 +1220,12 @@ async function accessDoorSmoke(
1214
1220
  } else if (address === "preview") {
1215
1221
  const open: string[] = [];
1216
1222
  for (const route of spec.census) {
1217
- const answered = (await ask(route.path, { method: route.method })).status;
1223
+ // Refused by Access before the Worker sees it: replay-safe, as the origin-gate census.
1224
+ const probe: EdgeRequestInit = { method: route.method, replaySafe: true };
1225
+ const answered = await ask(route.path, probe).then(
1226
+ (res) => res.status,
1227
+ (error: Error) => `no answer (${error.message})`,
1228
+ );
1218
1229
  if (answered !== 403) open.push(`${route.method} ${route.path} → ${answered}`);
1219
1230
  }
1220
1231
  for (const path of spec.shellPaths) {
@@ -989,7 +989,7 @@ export function runWorkerDeploy(spec: WorkerDeploySpec, deps: DeployDeps): Deplo
989
989
  const detail = `${spec.workerName} deployed${spec.secrets ? ", attached" : ""}${spec.smoke ? " and smoked" : ""} @ ${commit.slice(0, 8)}.`;
990
990
  if (unmeasured.length > 0) {
991
991
  const why =
992
- `${detail} Worker CPU UNMEASURED — the tail never attached around ${unmeasured.join(" and ")}, which PASSED. ` +
992
+ `${detail} Worker CPU UNMEASURED — the tail heard nothing of ${unmeasured.join(" and ")}, which PASSED. ` +
993
993
  "Nothing is wrong with the deployment and nothing needs rolling back; rerun the smoke under the app's CPU wrapper to measure it.";
994
994
  error(`\n🟡 ${tag} ${why}`);
995
995
  return finish({ code: TAIL_UNATTACHED_EXIT, step: "cpu-unmeasured", detail: why, commit, dirty });
@@ -1338,6 +1338,29 @@ export function workerTailEvents(traces: readonly unknown[], scriptName: string)
1338
1338
  return out;
1339
1339
  }
1340
1340
 
1341
+ /**
1342
+ * How many of this Worker's invocations a capture holds AFTER the trace carrying `cpu-tail=<marker>`,
1343
+ * not counting any other marker. Pure. 0 when the start marker is not in the capture at all.
1344
+ *
1345
+ * 🔴 Why it exists (task 3110): music's stage walk PASSED under a tail that carried the start marker
1346
+ * and then nothing — ~20 requests and the end marker, none heard in 45 s. A tail that went deaf after
1347
+ * attaching measured nothing, exactly like one that never attached (decision 6), so it is unmeasured,
1348
+ * not red. A PARTIAL capture (any of the command's requests heard) stays red: that one may really be
1349
+ * missing the last requests.
1350
+ */
1351
+ export function heardAfterMarker(traces: readonly unknown[], scriptName: string, marker: string): number {
1352
+ let started = false;
1353
+ let heard = 0;
1354
+ for (const raw of traces as TailTraceLike[]) {
1355
+ if (!raw || raw.scriptName !== scriptName) continue;
1356
+ const url = raw.event?.request?.url;
1357
+ const sent = url ? URL.parse(url)?.searchParams.get("cpu-tail") : null;
1358
+ if (!started) started = sent === marker;
1359
+ else if (sent == null) heard++;
1360
+ }
1361
+ return heard;
1362
+ }
1363
+
1341
1364
  /** The verdict in words, for the deploy log. */
1342
1365
  export function formatTailVerdict(verdict: TailVerdict, bench: Pick<TailBench, "formatCpuBudgetReport">, scriptName: string): string {
1343
1366
  const r = verdict.recorded;
@@ -1475,7 +1498,8 @@ export interface WorkerCpuTailSpec extends Omit<JudgeOpts, "scriptName"> {
1475
1498
  }
1476
1499
 
1477
1500
  /**
1478
- * The exit code of a CPU tail that never attached, twice, around a command that PASSED (`EX_UNAVAILABLE`):
1501
+ * The exit code of a CPU tail that never attached, twice — or went deaf once it had (task 3110) — around a
1502
+ * command that PASSED (`EX_UNAVAILABLE`):
1479
1503
  * nothing was measured, and nothing was wrong with the deployment. Distinct from the command's own
1480
1504
  * codes so {@link runWorkerDeploy} can say which of the two happened — decision 6.
1481
1505
  */
@@ -1558,6 +1582,13 @@ export async function runWorkerCpuTail(spec: WorkerCpuTailSpec, deps: WorkerCpuD
1558
1582
  );
1559
1583
  return { code: ran, step: "command", verdict };
1560
1584
  }
1561
- if (!drained) return { code: 1, step: "drain", verdict };
1585
+ if (!drained) {
1586
+ // Decision 6, again: a tail that heard none of a PASSED command went deaf — nothing was measured.
1587
+ if (heardAfterMarker(spec.bench.parseTailLines(tail.captured()), spec.workerName, marker) === 0) {
1588
+ deps.error(`🟡 ${tag} wrangler tail went deaf after it attached — it heard none of \`${spec.command.join(" ")}\`, which PASSED. CPU UNMEASURED.`);
1589
+ return { code: TAIL_UNATTACHED_EXIT, step: "unmeasured", verdict };
1590
+ }
1591
+ return { code: 1, step: "drain", verdict };
1592
+ }
1562
1593
  return verdict.ok ? { code: 0, step: "done", verdict } : { code: 1, step: "budget", verdict };
1563
1594
  }