cursedops 0.19.5 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,7 @@ import shape, worked example, and the incident each one is pinned to.
15
15
  | `cursedops/roots` | finding a generation's roots and a checkout's package root without knowing a path, and `repoCd`/`repoCommand`, the `cd` prefix of a printed command that RUNS when pasted |
16
16
  | `cursedops/paths` | the generation's whole-tree laws (`check-paths`, `check-doc-citations`, plus any `--also`) run over ONE repo, from a checkout or a worktree — and its `forge-paths` bin |
17
17
  | `cursedops/launchd` | installing, replacing and removing a macOS launchd user agent, and the port a LIVE job serves on (`livePort`) |
18
+ | `cursedops/launchd-imports` | the check that every launchd job — run straight out of an app's main checkout — imports only what the LOCKED versions of its packages export, so a lib-preview overlay's extra subpath or name cannot kill it at the next `bun install --force` (`launchdImportFaults`) |
18
19
  | `cursedops/smoke` | the scaffolding of a deployed smoke (ledger, fetch, DNS hint, exit code) — the one check no app owns — plus its VERDICTS: the origin asked on loopback, the smoke's own environment, a network that lies about DNS, and a version that has settled |
19
20
  | `cursedops/worker-deploy` | the sequence that ships a Cloudflare Worker — clean tree, stage first, schema, `--var` stamp, secrets, smoke — plus a billed-CPU tail wrapped around the walk and the smoke, and a ledger line per run |
20
21
  | `cursedops/worker-secrets` | a Worker holding EXACTLY its deployment's secrets, uploaded over a pipe, read back up to five times before an inexact set is believed |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cursedops",
3
- "version": "0.19.5",
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.",
3
+ "version": "0.20.0",
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 check that a launchd job imports only what the LOCKED versions of its packages export (a lib-preview overlay's extra subpath or name is a job that dies at the next forced reinstall), 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": {
7
7
  "typecheck": "tsc -p tsconfig.json --noEmit",
@@ -25,6 +25,12 @@
25
25
  "source": "./src/launchd.ts",
26
26
  "import": "./src/launchd.ts"
27
27
  },
28
+ "./launchd-imports": {
29
+ "types": "./src/launchdImports.ts",
30
+ "bun": "./src/launchdImports.ts",
31
+ "source": "./src/launchdImports.ts",
32
+ "import": "./src/launchdImports.ts"
33
+ },
28
34
  "./api-floor": {
29
35
  "types": "./src/apiFloor.ts",
30
36
  "bun": "./src/apiFloor.ts",
@@ -0,0 +1,243 @@
1
+ /**
2
+ * 🔴 A launchd job imports, AT RUNTIME, only what the LOCKED versions of its packages provide.
3
+ *
4
+ * Found building forge task 3579 (2026-10-22) in collections, lifted here by forge task 3756 so
5
+ * every app with a launchd job runs it. A job — a scheduled script, a backup, a server — runs
6
+ * `bun run <entry>` straight out of an app's MAIN checkout. An unpublished library is tried with
7
+ * the generation's `tools/lib-preview.ts`, which overlays its build into `node_modules` under the
8
+ * locked version's name; `bun install --force` (the generation's standing advice before hunting a
9
+ * phantom bug) puts the locked bytes back. A job that imports a subpath only the overlay has then
10
+ * dies on its first import, every run, until the publish — and the gate, the typecheck and every
11
+ * test stay green, because they all read the overlay.
12
+ *
13
+ * So {@link launchdImportFaults} walks each entry's import graph (type-only imports are erased and
14
+ * skipped, as Bun skips them) and, for every package carrying a `lib-preview` mark, requires the
15
+ * subpath to be an export of the version `bun.lock` names, read from bun's own cache — and every
16
+ * NAME imported from it to be one that version's module exports. A subpath alone was not enough
17
+ * (forge task 3752): a new function added to `cursedbelt-server/dropzone` imports from a subpath the
18
+ * locked version already has, and the job dies on that one missing binding just the same.
19
+ *
20
+ * The fix when it reds is to import that thing by TYPE only, move what the job needs into a leaf
21
+ * with no such import, or wait for the publish.
22
+ */
23
+ import { existsSync, readFileSync } from "node:fs";
24
+ import { homedir } from "node:os";
25
+ import { dirname, join, resolve } from "node:path";
26
+
27
+ /** A runtime import of a package: who imports it, the `pkg` / `./subpath` it names, and the bindings it takes. */
28
+ export interface PackageImport {
29
+ from: string;
30
+ pkg: string;
31
+ subpath: string;
32
+ /** Each named binding (`default` for a default import). Empty for `* as`, which cannot miss one. */
33
+ names: string[];
34
+ }
35
+
36
+ /** An import a forced reinstall would break, and why. */
37
+ export interface PreviewOnlyImport {
38
+ import: PackageImport;
39
+ locked: string;
40
+ why: string;
41
+ }
42
+
43
+ const transpilers = {
44
+ ts: new Bun.Transpiler({ loader: "ts" }),
45
+ tsx: new Bun.Transpiler({ loader: "tsx" }),
46
+ };
47
+
48
+ /** The transpiler a file is read with — a `.ts` file's `<T>x` cast is not JSX. */
49
+ function transpilerFor(file: string): Bun.Transpiler {
50
+ return file.endsWith(".tsx") ? transpilers.tsx : transpilers.ts;
51
+ }
52
+
53
+ /**
54
+ * The bindings each runtime `import … from` and `export { … } from` statement in `source` takes,
55
+ * by specifier — after types are erased, as Bun erases them.
56
+ */
57
+ export function importedNames(source: string, transpiler: Bun.Transpiler = transpilers.tsx): Map<string, string[]> {
58
+ const out = new Map<string, string[]>();
59
+ const code = transpiler.transformSync(source);
60
+ const add = (specifier: string, name: string): void => {
61
+ const names = out.get(specifier) ?? [];
62
+ if (name) names.push(name);
63
+ out.set(specifier, names);
64
+ };
65
+ for (const [, clause = "", specifier = ""] of code.matchAll(/^import\s+([^"';]+?)\s+from\s*["']([^"']+)["']/gm)) {
66
+ add(specifier, "");
67
+ const braces = clause.match(/\{([^}]*)\}/)?.[1];
68
+ const head = clause.replace(/\{[^}]*\}/, "").replace(/,/g, " ").trim();
69
+ if (head && !head.startsWith("*")) add(specifier, "default");
70
+ for (const part of braces?.split(",") ?? []) add(specifier, part.trim().split(/\s+as\s+/)[0]?.trim() ?? "");
71
+ }
72
+ for (const [, braces = "", specifier = ""] of code.matchAll(/^export\s*\{([^}]*)\}\s*from\s*["']([^"']+)["']/gm))
73
+ for (const part of braces.split(",")) add(specifier, part.trim().split(/\s+as\s+/)[0]?.trim() ?? "");
74
+ return out;
75
+ }
76
+
77
+ const EXTENSIONS = ["", ".ts", ".tsx", "/index.ts", "/index.tsx"];
78
+
79
+ /** The repo file a relative specifier lands on, `.js` spelling included — null outside TypeScript. */
80
+ function resolveLocal(file: string, specifier: string): string | null {
81
+ const base = resolve(dirname(file), specifier);
82
+ const bases = /\.[cm]?js$/.test(base) ? [base, base.replace(/\.([cm]?)js$/, ".$1ts")] : [base];
83
+ for (const candidate of bases.flatMap((stem) => EXTENSIONS.map((ext) => `${stem}${ext}`)))
84
+ if (existsSync(candidate) && /\.tsx?$/.test(candidate)) return candidate;
85
+ return null;
86
+ }
87
+
88
+ /** Every runtime package import reachable from `entry` through the repo's own files. */
89
+ export function runtimeImports(entry: string): PackageImport[] {
90
+ const seen = new Set<string>();
91
+ const out: PackageImport[] = [];
92
+ const visit = (file: string): void => {
93
+ if (seen.has(file)) return;
94
+ seen.add(file);
95
+ // A shebang is not TypeScript; the transpiler refuses the whole file over one.
96
+ const source = readFileSync(file, "utf8").replace(/^#!.*/, "");
97
+ const transpiler = transpilerFor(file);
98
+ const names = importedNames(source, transpiler);
99
+ for (const { path } of transpiler.scanImports(source)) {
100
+ if (path.startsWith(".") || path.startsWith("/")) {
101
+ const hit = resolveLocal(file, path);
102
+ if (hit) visit(hit);
103
+ continue;
104
+ }
105
+ if (path.startsWith("node:") || path.startsWith("bun:") || path === "bun") continue;
106
+ const parts = path.split("/");
107
+ const pkg = path.startsWith("@") ? parts.slice(0, 2).join("/") : (parts[0] as string);
108
+ const rest = path.slice(pkg.length);
109
+ out.push({ from: file, pkg, subpath: rest ? `.${rest}` : ".", names: names.get(path) ?? [] });
110
+ }
111
+ };
112
+ visit(entry);
113
+ return out;
114
+ }
115
+
116
+ /** Does this `exports` map name the subpath, directly or through a `*` pattern? */
117
+ export function exported(exportsMap: unknown, subpath: string): boolean {
118
+ if (exportsMap === undefined || typeof exportsMap === "string") return subpath === ".";
119
+ const keys = Object.keys(exportsMap as object);
120
+ if (!keys.some((key) => key.startsWith("."))) return subpath === ".";
121
+ return keys.some((key) => {
122
+ if (key === subpath) return true;
123
+ const star = key.indexOf("*");
124
+ return star >= 0 && subpath.startsWith(key.slice(0, star)) && subpath.endsWith(key.slice(star + 1));
125
+ });
126
+ }
127
+
128
+ /** The file an `exports` entry sends Bun to — its `bun` condition first, as Bun resolves it, and a `*` pattern filled in. */
129
+ export function exportTarget(exportsMap: unknown, subpath: string): string | null {
130
+ let entry: unknown;
131
+ let fill = "";
132
+ if (exportsMap === undefined || typeof exportsMap === "string") entry = subpath === "." ? exportsMap : undefined;
133
+ else {
134
+ const map = exportsMap as Record<string, unknown>;
135
+ entry = map[subpath];
136
+ if (entry === undefined)
137
+ for (const key of Object.keys(map)) {
138
+ const star = key.indexOf("*");
139
+ const [head, tail] = [key.slice(0, star), key.slice(star + 1)];
140
+ if (star >= 0 && subpath.startsWith(head) && subpath.endsWith(tail) && subpath.length >= head.length + tail.length) {
141
+ entry = map[key];
142
+ fill = subpath.slice(head.length, subpath.length - tail.length);
143
+ break;
144
+ }
145
+ }
146
+ }
147
+ const pick = (target: string): string => target.replace("*", fill);
148
+ if (typeof entry === "string") return pick(entry);
149
+ if (!entry || typeof entry !== "object") return null;
150
+ const conditions = entry as Record<string, unknown>;
151
+ for (const condition of ["bun", "import", "default"]) if (typeof conditions[condition] === "string") return pick(conditions[condition] as string);
152
+ return null;
153
+ }
154
+
155
+ /** Every runtime name a module exports, through its `export * from "./…"` re-exports; `null` when one cannot be read. */
156
+ export function moduleExports(file: string, seen = new Set<string>()): Set<string> | null {
157
+ if (seen.has(file)) return new Set();
158
+ seen.add(file);
159
+ if (!existsSync(file)) return null;
160
+ const source = readFileSync(file, "utf8");
161
+ const names = new Set(new Bun.Transpiler({ loader: file.endsWith(".tsx") ? "tsx" : "ts" }).scan(source).exports);
162
+ for (const [, from = ""] of source.matchAll(/^export\s+\*\s+from\s*["'](\.[^"']+)["']/gm)) {
163
+ const base = resolve(dirname(file), from.replace(/\.js$/, ""));
164
+ const hit = EXTENSIONS.map((ext) => `${base}${ext}`).find((candidate) => existsSync(candidate) && /\.[jt]sx?$/.test(candidate));
165
+ const inner = hit ? moduleExports(hit, seen) : null;
166
+ if (!inner) return null;
167
+ for (const name of inner) names.add(name);
168
+ }
169
+ return names;
170
+ }
171
+
172
+ /**
173
+ * The imports a forced reinstall would break: each `pkg/subpath` whose installed copy is a
174
+ * lib-preview overlay and whose LOCKED version does not export it, or not every name taken from it.
175
+ * A locked version missing from the cache cannot be proven either way, so it is reported too —
176
+ * fail closed for a live job.
177
+ */
178
+ export function previewOnlyImports(imports: readonly PackageImport[], nodeModules: string, cacheDir: string): PreviewOnlyImport[] {
179
+ const out: PreviewOnlyImport[] = [];
180
+ for (const imported of imports) {
181
+ const markPath = join(nodeModules, imported.pkg, ".lib-preview.json");
182
+ if (!existsSync(markPath)) continue;
183
+ const { locked } = JSON.parse(readFileSync(markPath, "utf8")) as { locked: string };
184
+ const lockedPkg = join(cacheDir, `${imported.pkg}@${locked}@@@1`, "package.json");
185
+ if (!existsSync(lockedPkg)) {
186
+ out.push({ import: imported, locked, why: `the locked ${imported.pkg}@${locked} is not in bun's cache to check against` });
187
+ continue;
188
+ }
189
+ const { exports } = JSON.parse(readFileSync(lockedPkg, "utf8")) as { exports?: unknown };
190
+ if (!exported(exports, imported.subpath)) {
191
+ out.push({ import: imported, locked, why: `${imported.pkg}@${locked} (what bun.lock installs) has no export "${imported.subpath}"` });
192
+ continue;
193
+ }
194
+ if (imported.names.length === 0) continue;
195
+ const target = exportTarget(exports, imported.subpath);
196
+ const names = target ? moduleExports(join(dirname(lockedPkg), target)) : null;
197
+ const missing = names ? imported.names.filter((name) => !names.has(name)) : imported.names;
198
+ if (missing.length > 0)
199
+ out.push({
200
+ import: imported,
201
+ locked,
202
+ why: names
203
+ ? `${imported.pkg}@${locked} (what bun.lock installs) exports "${imported.subpath}" but not ${missing.join(", ")}`
204
+ : `${imported.pkg}@${locked}'s "${imported.subpath}" module could not be read to check ${missing.join(", ")} against`,
205
+ });
206
+ }
207
+ return out;
208
+ }
209
+
210
+ /** Bun's install cache, where every locked version sits unpacked — `BUN_INSTALL_CACHE_DIR` first, as bun reads it. */
211
+ export function bunCacheDir(env: Record<string, string | undefined> = process.env): string {
212
+ return env.BUN_INSTALL_CACHE_DIR ?? join(env.BUN_INSTALL ?? join(homedir(), ".bun"), "install", "cache");
213
+ }
214
+
215
+ /** Where {@link launchdImportFaults} looks, when an app's own test does not say. */
216
+ export interface LaunchdImportOptions {
217
+ /** The `node_modules` the jobs resolve from. Default `<root>/node_modules`. */
218
+ nodeModules?: string;
219
+ /** Bun's install cache. Default {@link bunCacheDir}. */
220
+ cacheDir?: string;
221
+ }
222
+
223
+ /**
224
+ * The one call an app's test makes: every entry a launchd job runs (relative to `root`, such as
225
+ * `scripts/<job>.ts`), walked and checked — one line per import a forced reinstall would break,
226
+ * so `expect(launchdImportFaults(ROOT, entries)).toEqual([])` names each in its failure. An entry
227
+ * that is not there is a fault too: a renamed script would otherwise pass by checking nothing.
228
+ */
229
+ export function launchdImportFaults(root: string, entries: readonly string[], options: LaunchdImportOptions = {}): string[] {
230
+ const nodeModules = options.nodeModules ?? join(root, "node_modules");
231
+ const cacheDir = options.cacheDir ?? bunCacheDir();
232
+ const faults: string[] = [];
233
+ for (const entry of entries) {
234
+ const file = join(root, entry);
235
+ if (!existsSync(file)) {
236
+ faults.push(`${entry} — a launchd job runs it, and it is not there`);
237
+ continue;
238
+ }
239
+ for (const { import: imported, why } of previewOnlyImports(runtimeImports(file), nodeModules, cacheDir))
240
+ faults.push(`${imported.from.slice(root.length + 1)} imports ${imported.pkg}${imported.subpath.slice(1)} — ${why}`);
241
+ }
242
+ return faults;
243
+ }
@@ -97,7 +97,7 @@ export const SECRET_PUT_ATTEMPTS = 3;
97
97
  * Reads of the account after the puts before a set that is not exact is believed. A read changes
98
98
  * nothing, so asking again can only replace a stale answer with a current one — see {@link syncSecrets}.
99
99
  */
100
- export const SECRET_READBACK_ATTEMPTS = 5;
100
+ export const SECRET_READBACK_ATTEMPTS = 8;
101
101
 
102
102
  /**
103
103
  * Apply `plan`: put every value, delete every extra, then READ THE ACCOUNT BACK and fail unless it
@@ -113,6 +113,9 @@ export const SECRET_READBACK_ATTEMPTS = 5;
113
113
  * listed both secrets, both puts said ok, and the list read straight after answered NOTHING — a
114
114
  * green deploy stopped as "secrets NOT attached". The same script run again a minute later read
115
115
  * both. The LAST read is the verdict, so a set that is really wrong is still a red.
116
+ * Eight reads, pausing 2 s more each time, wait 56 s: on 2026-10-24 (ship run 20-24-05) photos'
117
+ * FIRST put of FAMILY_PEOPLE_TOKEN said ok and five reads over 20 s never listed it, so the deploy
118
+ * stopped as "secrets NOT attached"; `--check` a few minutes later read it held.
116
119
  */
117
120
  export async function syncSecrets(
118
121
  plan: SecretPlan,