cursedops 0.22.1 → 0.23.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 +1 -1
- package/src/stageLook.ts +55 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedops",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.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 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": {
|
package/src/stageLook.ts
CHANGED
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
* to binary-server too they turn every media fetch into a CORS preflight it refuses.
|
|
14
14
|
* - **Sign-in is the stage's own door**: `grantStageOwner` (auth-stage's emailed second factor) and
|
|
15
15
|
* the app's `/api/auth/sso/callback?grant=…`. Never production's accounts service.
|
|
16
|
+
* - **An app with no sign-in is looked at signed out** (`signedIn: false`, `--signed-out`), and a stage
|
|
17
|
+
* with no custom domain at its own `origin` (`--origin https://<app>-stage.<account>.workers.dev`).
|
|
18
|
+
* patterns has neither (forge task 3918): asking auth-stage for its grant answered 400, so the one
|
|
19
|
+
* command could not look at it at all. `tools/stage-look.ts` decides both from auth's registry and
|
|
20
|
+
* the app's `wrangler.jsonc`, so nobody has to know.
|
|
16
21
|
* - **A reaped binary-server sandbox is SAID, not screenshotted.** Its images answer 404, or 503 with
|
|
17
22
|
* `x-bs-edge-cache: STAGE-ORIGIN-DOWN`; {@link brokenMediaOf} names them, and the CLI exits 1
|
|
18
23
|
* with the recovery (`apps/binary-server` → `bun run sandbox:up`, then upload again).
|
|
@@ -22,7 +27,7 @@
|
|
|
22
27
|
*
|
|
23
28
|
* CLI, from an app's directory (or `bun $FORGE/tools/stage-look.ts <app> …` from anywhere):
|
|
24
29
|
*
|
|
25
|
-
* stage-look <app> [path] [--out <dir>]
|
|
30
|
+
* stage-look <app> [path] [--out <dir>] [--signed-out] [--origin <url>] → <dir>/stage-<app>-390.png, …-1440.png
|
|
26
31
|
*/
|
|
27
32
|
import { spawnSync } from "node:child_process";
|
|
28
33
|
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
@@ -181,6 +186,10 @@ export interface StageLookSpec {
|
|
|
181
186
|
state?: string;
|
|
182
187
|
/** playwright's `chromium`; default: `playwright` resolved from the working directory. */
|
|
183
188
|
chromium?: LookChromium;
|
|
189
|
+
/** False for an app with no sign-in: no grant is asked for and no callback is opened. Default true. */
|
|
190
|
+
signedIn?: boolean;
|
|
191
|
+
/** The stage's origin when it is not {@link stageOrigin} — a stage on workers.dev alone. */
|
|
192
|
+
origin?: string;
|
|
184
193
|
/** Extra Chromium flags — collections' audio daemon, say. */
|
|
185
194
|
args?: string[];
|
|
186
195
|
/** Seams for the suite. */
|
|
@@ -204,14 +213,14 @@ async function defaultChromium(): Promise<LookChromium> {
|
|
|
204
213
|
}
|
|
205
214
|
|
|
206
215
|
/**
|
|
207
|
-
* A page on `<app>-stage`, signed in as its owner, with Access headers on the stage's requests only.
|
|
216
|
+
* A page on `<app>-stage`, signed in as its owner (unless `signedIn: false`), with Access headers on the stage's requests only.
|
|
208
217
|
* `Page` is the caller's playwright `Page` — the look builds it, so the cast is the caller's word.
|
|
209
218
|
*/
|
|
210
219
|
export async function openStagePage<Page = LookPage>(spec: StageLookSpec): Promise<StageLook<Page>> {
|
|
211
220
|
const cwd = process.cwd();
|
|
212
221
|
const forge = spec.forge ?? forgeCodeRoot(cwd);
|
|
213
222
|
if (!forge) throw new Error(`no forge.env above ${cwd} — run the look from inside the generation`);
|
|
214
|
-
const origin = stageOrigin(spec.app);
|
|
223
|
+
const origin = spec.origin ? new URL(spec.origin).origin : stageOrigin(spec.app);
|
|
215
224
|
const access = spec.headers ?? accessHeaders(spec.state ?? requireForgeState(cwd));
|
|
216
225
|
const chromium = spec.chromium ?? (await defaultChromium());
|
|
217
226
|
const browser = await chromium.launch({ args: [...(spec.args ?? []), ...pinnedResolverArgs(new URL(origin).hostname, spec.dig)] });
|
|
@@ -228,10 +237,12 @@ export async function openStagePage<Page = LookPage>(spec: StageLookSpec): Promi
|
|
|
228
237
|
const b = brokenMediaOf(r);
|
|
229
238
|
if (b) broken.push(b);
|
|
230
239
|
});
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
240
|
+
if (spec.signedIn !== false) {
|
|
241
|
+
const granted = (spec.grant ?? (() => grantStageOwner({ forge, app: spec.app })))();
|
|
242
|
+
if (!granted.ok) throw new Error(`auth-stage gave no grant for ${spec.app} — its output above says why (an app with no sign-in is looked at with --signed-out)`);
|
|
243
|
+
const back = await page.goto(`${origin}/api/auth/sso/callback?grant=${encodeURIComponent(granted.grant)}`, { waitUntil: "load" });
|
|
244
|
+
if (back && back.status() >= 400) throw new Error(`${origin}'s sign-in callback answered ${back.status()}`);
|
|
245
|
+
}
|
|
235
246
|
return { page: page as Page, origin, brokenMedia: () => [...broken], close: () => browser.close() };
|
|
236
247
|
} catch (e) {
|
|
237
248
|
await browser.close();
|
|
@@ -239,21 +250,52 @@ export async function openStagePage<Page = LookPage>(spec: StageLookSpec): Promi
|
|
|
239
250
|
}
|
|
240
251
|
}
|
|
241
252
|
|
|
253
|
+
export interface LookArgs {
|
|
254
|
+
app: string;
|
|
255
|
+
path: string;
|
|
256
|
+
out?: string;
|
|
257
|
+
signedIn: boolean;
|
|
258
|
+
origin?: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** The CLI's words, or what is wrong with them. A flag that takes a value and has none is a usage fault, never a path. */
|
|
262
|
+
export function parseLookArgs(argv: readonly string[]): LookArgs | { usage: string } {
|
|
263
|
+
const plain: string[] = [];
|
|
264
|
+
let out: string | undefined;
|
|
265
|
+
let origin: string | undefined;
|
|
266
|
+
let signedIn = true;
|
|
267
|
+
for (let i = 0; i < argv.length; i++) {
|
|
268
|
+
const a = argv[i] as string;
|
|
269
|
+
if (a === "--signed-out") signedIn = false;
|
|
270
|
+
else if (a === "--out" || a === "--origin") {
|
|
271
|
+
const v = argv[++i];
|
|
272
|
+
if (!v || v.startsWith("--")) return { usage: `${a} needs a value` };
|
|
273
|
+
if (a === "--out") out = v;
|
|
274
|
+
else if (!URL.canParse(v) || !/^https?:$/.test(new URL(v).protocol)) return { usage: `--origin ${v} is not an http(s) URL` };
|
|
275
|
+
else origin = new URL(v).origin;
|
|
276
|
+
} else if (a.startsWith("--")) return { usage: `unknown flag ${a}` };
|
|
277
|
+
else plain.push(a);
|
|
278
|
+
}
|
|
279
|
+
const [app, path = "/", extra] = plain;
|
|
280
|
+
if (!app) return { usage: "which app?" };
|
|
281
|
+
if (extra) return { usage: `one path at a time (got ${path} and ${extra})` };
|
|
282
|
+
return { app, path, signedIn, ...(out ? { out } : {}), ...(origin ? { origin } : {}) };
|
|
283
|
+
}
|
|
284
|
+
|
|
242
285
|
/** Shoot `path` at 390 and 1440. Exit 1 when the page or its media is broken, so a look never passes on broken tiles. */
|
|
243
286
|
async function main(argv: string[]): Promise<number> {
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (!app || (outAt >= 0 && !out)) {
|
|
248
|
-
console.error("usage: stage-look <app> [path] [--out <dir>] (run in the app's directory, or `bun $FORGE/tools/stage-look.ts <app> …`)");
|
|
287
|
+
const parsed = parseLookArgs(argv);
|
|
288
|
+
if ("usage" in parsed) {
|
|
289
|
+
console.error(`${parsed.usage}\nusage: stage-look <app> [path] [--out <dir>] [--signed-out] [--origin <url>] (run in the app's directory, or \`bun $FORGE/tools/stage-look.ts <app> …\`)`);
|
|
249
290
|
return 2;
|
|
250
291
|
}
|
|
292
|
+
const { app, path, out, signedIn, origin } = parsed;
|
|
251
293
|
const dir = out ?? join(process.env.FORGE_BATCH_TMP ?? requireForgeState(process.cwd()), "stage-look");
|
|
252
294
|
mkdirSync(dir, { recursive: true });
|
|
253
295
|
let bad = false;
|
|
254
296
|
for (const width of [390, 1440]) {
|
|
255
297
|
type ShotPage = LookPage & SettlePage & { screenshot(o: { path: string; fullPage?: boolean }): Promise<unknown>; url(): string };
|
|
256
|
-
const look = await openStagePage<ShotPage>({ app, width });
|
|
298
|
+
const look = await openStagePage<ShotPage>({ app, width, signedIn, ...(origin ? { origin } : {}) });
|
|
257
299
|
try {
|
|
258
300
|
const failed: string[] = [];
|
|
259
301
|
look.page.on("response", (r) => {
|