cursedops 0.10.11 → 0.10.12

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.10.11",
3
+ "version": "0.10.12",
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": {
@@ -193,6 +193,61 @@ export function underCpuTail(wrapper: readonly string[], env: WorkerEnv, command
193
193
  return [...wrapper, "--env", env, "--", ...command];
194
194
  }
195
195
 
196
+ /** A zone name off a route: its `zone_name`, or the last two labels of a custom domain's host. */
197
+ const routeZones = (routes: unknown): { zones: Set<string>; hosts: Set<string> } => {
198
+ const zones = new Set<string>();
199
+ const hosts = new Set<string>();
200
+ for (const route of Array.isArray(routes) ? routes : []) {
201
+ const r = (typeof route === "string" ? { pattern: route } : route) as { pattern?: string; zone_name?: string };
202
+ const host = (r.pattern ?? "").replace(/^https?:\/\//, "").split("/")[0]?.replace(/^\*\.?/, "") ?? "";
203
+ if (host) hosts.add(host);
204
+ const zone = r.zone_name ?? host.split(".").slice(-2).join(".");
205
+ if (zone) zones.add(zone);
206
+ }
207
+ return { zones, hosts };
208
+ };
209
+
210
+ /**
211
+ * 🔴 Why a Worker must not `fetch` another hostname on its own zone — refused at deploy, for every
212
+ * Worker app and every relay, because this is the one seam all of them pass through.
213
+ *
214
+ * A subrequest from a Worker to a hostname on the SAME zone skips every other Worker's route and
215
+ * goes straight to the zone's ORIGIN. On `cursedalchemy.com` the origin of `auth.` and
216
+ * `binary-server.` is a tunnel into the owner's Mac, so the call works while the Mac is on and
217
+ * dies the moment it is off, with nothing in any suite able to tell. Met twice: auth's JWKS
218
+ * refresh (task 2150, fixed with an `AUTH` binding), then every server-side byte read — the owner
219
+ * turned the Mac off on 2026-09-25 and family's tree lost every face, although its bytes rest in
220
+ * R2. The cure is a service binding, which reaches the Worker itself.
221
+ *
222
+ * The rule, over the config wrangler deploys for `env` (a named env inherits none of `vars`,
223
+ * `routes` or `services`): every `vars` entry named `*_URL` whose https host is a DIFFERENT host on
224
+ * a zone this Worker is routed on needs a `services` binding named the var's stem or its last
225
+ * words — `BINARY_SERVER_URL` → `BINARY_SERVER`, `FAMILY_AUTH_URL` → `AUTH`. The Worker's own
226
+ * hostname is exempt; so is a Worker with no route on the zone (a `workers.dev`-only preview's
227
+ * subrequests DO run the target's route). Returns the refusal, or `null`.
228
+ */
229
+ export function sameZoneFetchRefusal(config: Record<string, unknown>, env: WorkerEnv): string | null {
230
+ const scope = (env === "stage" ? ((config.env as Record<string, Record<string, unknown>> | undefined)?.stage ?? {}) : config) as Record<string, unknown>;
231
+ const { zones, hosts } = routeZones(scope.routes);
232
+ if (zones.size === 0) return null;
233
+ const bindings = (Array.isArray(scope.services) ? scope.services : []).map((s) => String((s as { binding?: string }).binding ?? ""));
234
+ const missing: string[] = [];
235
+ for (const [name, value] of Object.entries((scope.vars as Record<string, unknown> | undefined) ?? {})) {
236
+ if (!name.endsWith("_URL") || typeof value !== "string") continue;
237
+ const host = /^https:\/\/([^/:]+)/.exec(value)?.[1];
238
+ if (!host || hosts.has(host)) continue;
239
+ if (![...zones].some((zone) => host === zone || host.endsWith(`.${zone}`))) continue;
240
+ const stem = name.slice(0, -"_URL".length);
241
+ if (bindings.some((b) => b && (stem === b || stem.endsWith(`_${b}`)))) continue;
242
+ missing.push(`${name} (${host}) → a \`services\` binding named ${stem}`);
243
+ }
244
+ if (missing.length === 0) return null;
245
+ return (
246
+ `${env} would fetch another hostname on its own zone without a service binding, and a same-zone subrequest skips that hostname's Worker and lands on the zone's origin — the Mac's tunnel, so it fails whenever the Mac is off: ${missing.join("; ")}. ` +
247
+ "Add the binding to wrangler.jsonc (for THIS env — named envs inherit none) and send the calls through it (`cursedbelt-server/binary-store`'s `fetch` option, or `env.<BINDING>.fetch`)."
248
+ );
249
+ }
250
+
196
251
  /** A refusal an app adds to the sequence: a sentence saying why not, or `null` to proceed. */
197
252
  export type Refusal = () => string | null;
198
253
 
@@ -408,6 +463,16 @@ export function runWorkerDeploy(spec: WorkerDeploySpec, deps: DeployDeps): Deplo
408
463
  const why = refusal();
409
464
  if (why) return stop("refusal", why);
410
465
  }
466
+ // Built in, never an app's to remember: see `sameZoneFetchRefusal`. A deploy with no readable
467
+ // config would fail at wrangler anyway, so an unreadable file is not a pass we invent here.
468
+ let wrangler: Record<string, unknown> | null = null;
469
+ try {
470
+ wrangler = readWranglerJsonc((deps.read ?? ((path: string) => readFileSync(path, "utf8")))("wrangler.jsonc"));
471
+ } catch {}
472
+ for (const env of spec.env === "production" ? (["stage", "production"] as const) : (["stage"] as const)) {
473
+ const why = wrangler ? sameZoneFetchRefusal(wrangler, env) : null;
474
+ if (why) return stop("same-zone", `${why} Nothing was deployed.`);
475
+ }
411
476
  if (spec.env === "production") {
412
477
  for (const command of spec.stageFirst ?? []) {
413
478
  step(`the stage first — production waits on it: ${command.join(" ")}`);