cursedops 0.10.21 → 0.10.23
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 +1 -1
- package/src/workerDeploy.ts +68 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedops",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.23",
|
|
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/workerDeploy.ts
CHANGED
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
* 1. **a readable HEAD** — nothing to stamp is nothing that could prove the deploy;
|
|
34
34
|
* 2. **a clean tree for production** — a dirty one would report one commit and run another;
|
|
35
35
|
* 3. **the app's own refusals** (data) — e.g. its Mac host still loaded while the config routes
|
|
36
|
-
* the hostname to the Worker: two writers, two databases;
|
|
36
|
+
* the hostname to the Worker: two writers, two databases; then **`node_modules` made to
|
|
37
|
+
* match `bun.lock`** ({@link INSTALL}) — a bundle built from stale modules runs code the
|
|
38
|
+
* stamped commit does not name;
|
|
37
39
|
* 4. **the STAGE first, for production** — `collections` went live 2026-09-22 with every anonymous
|
|
38
40
|
* check green and its owner unable to sign in; the stage walk is what found why, and a step
|
|
39
41
|
* in the sequence is one a person in a hurry cannot skip;
|
|
@@ -490,6 +492,9 @@ export function judgeSchemaApply(code: number, stdout: string): { applied: boole
|
|
|
490
492
|
* Run the sequence in the header's order, stopping at the first red with a sentence. Never
|
|
491
493
|
* exits — the caller does, with {@link DeployResult.code}.
|
|
492
494
|
*/
|
|
495
|
+
/** What {@link runWorkerDeploy} runs first: `node_modules` exactly as `bun.lock` says. */
|
|
496
|
+
export const INSTALL: readonly string[] = ["bun", "install", "--frozen-lockfile"];
|
|
497
|
+
|
|
493
498
|
export function runWorkerDeploy(spec: WorkerDeploySpec, deps: DeployDeps): DeployResult {
|
|
494
499
|
const log = deps.log ?? ((line: string) => console.log(line));
|
|
495
500
|
const error = deps.error ?? ((line: string) => console.error(line));
|
|
@@ -523,6 +528,19 @@ export function runWorkerDeploy(spec: WorkerDeploySpec, deps: DeployDeps): Deplo
|
|
|
523
528
|
const why = wrangler ? sameZoneFetchRefusal(wrangler, env) : null;
|
|
524
529
|
if (why) return stop("same-zone", `${why} Nothing was deployed.`);
|
|
525
530
|
}
|
|
531
|
+
/*
|
|
532
|
+
* 🔴 Built in, never an app's to remember: `node_modules` is made to match `bun.lock` before
|
|
533
|
+
* anything builds or bundles (wrangler bundles `worker/` from it too, so `--no-build` needs it
|
|
534
|
+
* as much as a build does). A fast-forward that moves `bun.lock` does not touch
|
|
535
|
+
* `node_modules`, and on 2026-09-28 vault's deploy of a commit locking cursedbelt 5.15.2 built
|
|
536
|
+
* and shipped 5.14.0 from disk — the `/healthz` commit said one thing and the bundle ran
|
|
537
|
+
* another, the exact lie the clean-tree refusal exists to prevent. `--frozen-lockfile` is a
|
|
538
|
+
* no-op when they already agree, and refuses, rather than rewriting the lock, when they cannot.
|
|
539
|
+
*/
|
|
540
|
+
step("make node_modules match bun.lock");
|
|
541
|
+
if (deps.run(INSTALL, {}) !== 0) {
|
|
542
|
+
return stop("install", `\`${INSTALL.join(" ")}\` failed — node_modules cannot be made to match bun.lock, so nothing was built or deployed.`);
|
|
543
|
+
}
|
|
526
544
|
if (spec.env === "production") {
|
|
527
545
|
for (const command of spec.stageFirst ?? []) {
|
|
528
546
|
step(`the stage first — production waits on it: ${command.join(" ")}`);
|
|
@@ -905,6 +923,46 @@ export function judgeTailCapture(text: string, opts: JudgeOpts): TailVerdict {
|
|
|
905
923
|
return { ok: failures.length === 0, failures, report, recorded, traces: traces.length };
|
|
906
924
|
}
|
|
907
925
|
|
|
926
|
+
/** What `wrangler tail --format json` says a request did besides spend CPU. */
|
|
927
|
+
interface TailTraceLike {
|
|
928
|
+
scriptName?: string;
|
|
929
|
+
outcome?: string;
|
|
930
|
+
event?: { request?: { url?: string; method?: string } };
|
|
931
|
+
logs?: Array<{ level?: string; message?: unknown }>;
|
|
932
|
+
exceptions?: Array<{ name?: string; message?: string }>;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Every `console.*` line, exception and non-`ok` outcome this Worker produced in a capture, one line
|
|
937
|
+
* each, prefixed with the request that produced it. Pure.
|
|
938
|
+
*
|
|
939
|
+
* 🔴 Why it exists (task 2224): vault's stage walk went red with `callback → 302, NO cookie` while
|
|
940
|
+
* this tail was attached, and the consumer's own `console.warn` naming the cause was IN the capture —
|
|
941
|
+
* then thrown away, because only CPU was read out of it. Neither suspected cause reproduced after the
|
|
942
|
+
* fact. A failed command now prints what the Worker said while it failed.
|
|
943
|
+
*/
|
|
944
|
+
export function workerTailEvents(traces: readonly unknown[], scriptName: string): string[] {
|
|
945
|
+
const out: string[] = [];
|
|
946
|
+
for (const raw of traces as TailTraceLike[]) {
|
|
947
|
+
if (!raw || raw.scriptName !== scriptName) continue;
|
|
948
|
+
const req = raw.event?.request;
|
|
949
|
+
let where = "(no request)";
|
|
950
|
+
if (req?.url) {
|
|
951
|
+
const url = new URL(req.url);
|
|
952
|
+
// A path, never a query: a grant or a token rides there, and this goes to a log file.
|
|
953
|
+
where = `${req.method ?? "GET"} ${url.pathname}`;
|
|
954
|
+
}
|
|
955
|
+
for (const log of raw.logs ?? []) {
|
|
956
|
+
const parts = Array.isArray(log.message) ? log.message : [log.message];
|
|
957
|
+
const text = parts.map((p) => (typeof p === "string" ? p : JSON.stringify(p))).join(" ");
|
|
958
|
+
out.push(`${where} — ${log.level ?? "log"}: ${text}`);
|
|
959
|
+
}
|
|
960
|
+
for (const ex of raw.exceptions ?? []) out.push(`${where} — exception ${ex.name ?? "Error"}: ${ex.message ?? ""}`);
|
|
961
|
+
if (raw.outcome && raw.outcome !== "ok") out.push(`${where} — outcome ${raw.outcome}`);
|
|
962
|
+
}
|
|
963
|
+
return out;
|
|
964
|
+
}
|
|
965
|
+
|
|
908
966
|
/** The verdict in words, for the deploy log. */
|
|
909
967
|
export function formatTailVerdict(verdict: TailVerdict, bench: Pick<TailBench, "formatCpuBudgetReport">, scriptName: string): string {
|
|
910
968
|
const r = verdict.recorded;
|
|
@@ -1116,7 +1174,15 @@ export async function runWorkerCpuTail(spec: WorkerCpuTailSpec, deps: WorkerCpuD
|
|
|
1116
1174
|
deps.log(`\n${formatTailVerdict(verdict, spec.bench, spec.workerName)}`);
|
|
1117
1175
|
if (!drained) deps.error(`🔴 ${tag} the end marker never came back — the capture may be missing the command's last requests.`);
|
|
1118
1176
|
|
|
1119
|
-
if (ran !== 0)
|
|
1177
|
+
if (ran !== 0) {
|
|
1178
|
+
const said = workerTailEvents(spec.bench.parseTailLines(tail.captured()), spec.workerName);
|
|
1179
|
+
deps.error(
|
|
1180
|
+
said.length > 0
|
|
1181
|
+
? `\n${tag} what ${spec.workerName} logged while \`${spec.command.join(" ")}\` failed:\n${said.map((s) => ` ${s}`).join("\n")}`
|
|
1182
|
+
: `\n${tag} ${spec.workerName} logged nothing while \`${spec.command.join(" ")}\` failed — no console line, exception or failed outcome.`,
|
|
1183
|
+
);
|
|
1184
|
+
return { code: ran, step: "command", verdict };
|
|
1185
|
+
}
|
|
1120
1186
|
if (!drained) return { code: 1, step: "drain", verdict };
|
|
1121
1187
|
return verdict.ok ? { code: 0, step: "done", verdict } : { code: 1, step: "budget", verdict };
|
|
1122
1188
|
}
|