@cavuno/board 1.44.0 → 2.0.1
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/CHANGELOG.md +22 -0
- package/CONTRIBUTING.md +25 -0
- package/LICENSE +21 -0
- package/README.md +12 -7
- package/SECURITY.md +28 -0
- package/TRADEMARKS.md +16 -0
- package/dist/_spec-sW85fuvT.d.mts +2731 -0
- package/dist/_spec-sW85fuvT.d.ts +2731 -0
- package/dist/bin.mjs +38 -349
- package/dist/{board-DZCw7A6P.d.ts → board-DKXyxwNN.d.ts} +2 -2
- package/dist/{board-BwFW1aU2.d.mts → board-QFeHT4UN.d.mts} +2 -2
- package/dist/doctor.d.mts +6 -19
- package/dist/doctor.d.ts +6 -19
- package/dist/doctor.js +38 -345
- package/dist/doctor.mjs +37 -344
- package/dist/filters.d.mts +5 -5
- package/dist/filters.d.ts +5 -5
- package/dist/format.d.mts +17 -19
- package/dist/format.d.ts +17 -19
- package/dist/go.d.mts +2 -2
- package/dist/go.d.ts +2 -2
- package/dist/go.js +1 -1
- package/dist/go.mjs +1 -1
- package/dist/index.d.mts +16 -18
- package/dist/index.d.ts +16 -18
- package/dist/index.js +39 -28
- package/dist/index.mjs +39 -28
- package/dist/{jobs-BVazo4mA.d.ts → jobs-CuRzTDb1.d.mts} +15 -13
- package/dist/{jobs-CSMshXGV.d.mts → jobs-DTnCZGtb.d.ts} +15 -13
- package/dist/paths.d.mts +6 -6
- package/dist/paths.d.ts +6 -6
- package/dist/paths.js +1 -1
- package/dist/paths.mjs +1 -1
- package/dist/roles-vY0AXhoc.d.mts +3 -3
- package/dist/roles-vY0AXhoc.d.ts +3 -3
- package/dist/route-contract.d.mts +16 -16
- package/dist/route-contract.d.ts +16 -16
- package/dist/route-contract.js +1 -1
- package/dist/route-contract.mjs +1 -1
- package/dist/{salaries-DMQsjLrV.d.ts → salaries-BOpR51Ny.d.mts} +3 -3
- package/dist/{salaries-IJlchnWk.d.mts → salaries-QoNpzRCT.d.ts} +3 -3
- package/dist/{search-B7gr_wOd.d.ts → search-B5ROY6PK.d.ts} +2 -2
- package/dist/{search-B6FJifRE.d.mts → search-f93nrSg3.d.mts} +2 -2
- package/dist/seo.d.mts +17 -18
- package/dist/seo.d.ts +17 -18
- package/dist/server.d.mts +14 -32
- package/dist/server.d.ts +14 -32
- package/dist/sitemap.d.mts +7 -7
- package/dist/sitemap.d.ts +7 -7
- package/dist/sitemap.js +4 -2
- package/dist/sitemap.mjs +4 -2
- package/dist/skills.d.mts +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +1 -1
- package/dist/suggest.d.mts +2 -2
- package/dist/suggest.d.ts +2 -2
- package/dist/theme.d.mts +10 -13
- package/dist/theme.d.ts +10 -13
- package/dist/theme.js +1 -1
- package/dist/theme.mjs +1 -1
- package/dist/{ui-copy-CKfFTtLk.d.mts → ui-copy-CL-VN1xb.d.mts} +2 -3
- package/dist/{ui-copy-CKfFTtLk.d.ts → ui-copy-CL-VN1xb.d.ts} +2 -3
- package/dist/well-known.d.mts +3 -3
- package/dist/well-known.d.ts +3 -3
- package/dist/well-known.js +1 -1
- package/dist/well-known.mjs +1 -1
- package/package.json +46 -38
- package/skills/cavuno-board-client/SKILL.md +1 -2
- package/skills/cavuno-board-filters/SKILL.md +2 -2
- package/skills/cavuno-board-format/SKILL.md +1 -1
- package/skills/cavuno-board-i18n/SKILL.md +3 -3
- package/skills/cavuno-board-job-posting/SKILL.md +1 -1
- package/skills/cavuno-board-messaging/SKILL.md +2 -3
- package/skills/cavuno-board-paywall/SKILL.md +1 -1
- package/skills/cavuno-board-salaries/SKILL.md +1 -1
- package/skills/cavuno-board-seo/SKILL.md +4 -6
- package/skills/cavuno-board-server/SKILL.md +1 -1
- package/skills/cavuno-board-setup/SKILL.md +2 -1
- package/skills/cavuno-board-sitemap/SKILL.md +1 -1
- package/skills/cavuno-board-smoke-test/SKILL.md +4 -13
- package/skills/flavors/tanstack-start/SKILL.md +1 -2
- package/skills/manifest.json +1 -1
- package/dist/_spec-CoL3oYpG.d.mts +0 -5296
- package/dist/_spec-CoL3oYpG.d.ts +0 -5296
package/dist/doctor.js
CHANGED
|
@@ -24,7 +24,7 @@ __export(doctor_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(doctor_exports);
|
|
26
26
|
|
|
27
|
-
// ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.14_tsx@4.21.0_typescript@5.9.3_yaml@2.
|
|
27
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.14_tsx@4.21.0_typescript@5.9.3_yaml@2.9.0/node_modules/tsup/assets/cjs_shims.js
|
|
28
28
|
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
29
29
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
30
30
|
|
|
@@ -217,6 +217,25 @@ function isOffendingSink(lines, index) {
|
|
|
217
217
|
if (!hasCookieWriteSink(stripped)) return false;
|
|
218
218
|
return DOMAIN_SCOPE.test(statementWindow(lines, index));
|
|
219
219
|
}
|
|
220
|
+
var GENERATED_DIRS = /* @__PURE__ */ new Set([
|
|
221
|
+
"paraglide",
|
|
222
|
+
"generated",
|
|
223
|
+
"_generated",
|
|
224
|
+
"__generated__"
|
|
225
|
+
]);
|
|
226
|
+
var GENERATED_FILE = /\.gen\.(?:ts|tsx|js|jsx|mjs|cjs)$/;
|
|
227
|
+
var GENERATED_BANNER = /@generated|generated by|generated from|auto-?generated|do not edit|don't edit/i;
|
|
228
|
+
var BANNER_SCAN_LINES = 5;
|
|
229
|
+
function hasGeneratedBanner(text) {
|
|
230
|
+
const lines = text.split(/\r?\n/);
|
|
231
|
+
const limit = Math.min(lines.length, BANNER_SCAN_LINES);
|
|
232
|
+
for (let i = 0; i < limit; i += 1) {
|
|
233
|
+
const line = lines[i];
|
|
234
|
+
if (!isCommentLine(line)) return false;
|
|
235
|
+
if (GENERATED_BANNER.test(line)) return true;
|
|
236
|
+
}
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
220
239
|
function walkSourceFiles(dir, out) {
|
|
221
240
|
let entries;
|
|
222
241
|
try {
|
|
@@ -226,6 +245,7 @@ function walkSourceFiles(dir, out) {
|
|
|
226
245
|
}
|
|
227
246
|
for (const name of entries) {
|
|
228
247
|
if (name === "node_modules" || name.startsWith(".")) continue;
|
|
248
|
+
if (GENERATED_DIRS.has(name) || GENERATED_FILE.test(name)) continue;
|
|
229
249
|
const full = (0, import_node_path2.join)(dir, name);
|
|
230
250
|
let st;
|
|
231
251
|
try {
|
|
@@ -265,6 +285,7 @@ function checkCookieCodecConformance(projectRoot) {
|
|
|
265
285
|
} catch {
|
|
266
286
|
continue;
|
|
267
287
|
}
|
|
288
|
+
if (hasGeneratedBanner(text)) continue;
|
|
268
289
|
const lines = text.split(/\r?\n/);
|
|
269
290
|
for (let i = 0; i < lines.length; i += 1) {
|
|
270
291
|
if (!isOffendingSink(lines, i)) continue;
|
|
@@ -275,7 +296,7 @@ function checkCookieCodecConformance(projectRoot) {
|
|
|
275
296
|
return [
|
|
276
297
|
COOKIE(
|
|
277
298
|
"pass",
|
|
278
|
-
"no domain-scoped Set-Cookie or document.cookie writes in src/"
|
|
299
|
+
"no domain-scoped Set-Cookie or document.cookie writes in app-authored src/ (generated output skipped)"
|
|
279
300
|
)
|
|
280
301
|
];
|
|
281
302
|
}
|
|
@@ -401,86 +422,18 @@ async function runReadProbes(fetchImpl, frontendUrl) {
|
|
|
401
422
|
];
|
|
402
423
|
}
|
|
403
424
|
|
|
404
|
-
// src/doctor/sandbox.ts
|
|
405
|
-
var SANDBOX_GATE = record("sandbox.persona-gate", 1);
|
|
406
|
-
var EXPECTED_PERSONA_COUNT = 8;
|
|
407
|
-
function skipSandboxPersonaGate(reason) {
|
|
408
|
-
return SANDBOX_GATE("skip", reason);
|
|
409
|
-
}
|
|
410
|
-
async function checkSandboxPersonaGate(fetchImpl, env, boardContext) {
|
|
411
|
-
if (boardContext === null) {
|
|
412
|
-
return SANDBOX_GATE(
|
|
413
|
-
"skip",
|
|
414
|
-
"board did not resolve \u2014 fix static.board first"
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
const url = `${apiBase(env.apiUrl)}/v1/boards/${encodeURIComponent(env.boardKey)}/sandbox/personas`;
|
|
418
|
-
const result = await probe(fetchImpl, url);
|
|
419
|
-
if (boardContext.sandbox !== true) {
|
|
420
|
-
return result.status === 404 ? SANDBOX_GATE(
|
|
421
|
-
"pass",
|
|
422
|
-
"sandbox persona endpoint refuses (404) on a non-sandbox board"
|
|
423
|
-
) : SANDBOX_GATE(
|
|
424
|
-
"fail",
|
|
425
|
-
`sandbox persona endpoint is reachable on a NON-sandbox board (HTTP ${result.status}) \u2014 the capability gate is not holding`
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
if (!result.ok) {
|
|
429
|
-
return SANDBOX_GATE(
|
|
430
|
-
"fail",
|
|
431
|
-
`sandbox persona roster did not resolve (HTTP ${result.status}) \u2014 the persona seed may have failed`
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
let personas;
|
|
435
|
-
let password;
|
|
436
|
-
try {
|
|
437
|
-
const parsed = JSON.parse(result.body);
|
|
438
|
-
personas = parsed.personas;
|
|
439
|
-
password = parsed.password;
|
|
440
|
-
} catch {
|
|
441
|
-
return SANDBOX_GATE(
|
|
442
|
-
"fail",
|
|
443
|
-
"sandbox persona endpoint returned 200 but not JSON \u2014 proxy or captive portal in the way?"
|
|
444
|
-
);
|
|
445
|
-
}
|
|
446
|
-
if (!Array.isArray(personas) || personas.length !== EXPECTED_PERSONA_COUNT) {
|
|
447
|
-
return SANDBOX_GATE(
|
|
448
|
-
"fail",
|
|
449
|
-
`expected ${EXPECTED_PERSONA_COUNT} personas, got ${Array.isArray(personas) ? personas.length : "a non-array"} \u2014 the persona seed is incomplete`
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
const unseeded = personas.filter((persona) => persona.seeded !== true);
|
|
453
|
-
if (unseeded.length > 0) {
|
|
454
|
-
const ids = unseeded.map((persona) => typeof persona.id === "string" ? persona.id : "?").join(", ");
|
|
455
|
-
return SANDBOX_GATE(
|
|
456
|
-
"fail",
|
|
457
|
-
`${unseeded.length} of ${EXPECTED_PERSONA_COUNT} personas are not seeded on the board (${ids}) \u2014 the persona seed failed or was purged; run POST /v1/boards/{key}/sandbox/reseed to restore the roster`
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
if (typeof password !== "string" || password.length === 0) {
|
|
461
|
-
return SANDBOX_GATE(
|
|
462
|
-
"fail",
|
|
463
|
-
"roster present but the published password is missing"
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
return SANDBOX_GATE(
|
|
467
|
-
"pass",
|
|
468
|
-
`sandbox roster complete and seeded (${EXPECTED_PERSONA_COUNT} personas)`
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
425
|
// src/doctor/theme.ts
|
|
473
426
|
var import_node_crypto = require("crypto");
|
|
474
427
|
var import_node_fs3 = require("fs");
|
|
475
428
|
var import_node_path3 = require("path");
|
|
476
429
|
var THEME = record("static.theme", 1);
|
|
477
|
-
function checkThemeFreshness(projectRoot
|
|
430
|
+
function checkThemeFreshness(projectRoot) {
|
|
478
431
|
const tokensPath = (0, import_node_path3.join)(projectRoot, "src/tokens.css");
|
|
479
432
|
if (!(0, import_node_fs3.existsSync)(tokensPath)) {
|
|
480
433
|
return [
|
|
481
434
|
THEME(
|
|
482
435
|
"skip",
|
|
483
|
-
"
|
|
436
|
+
"no src/tokens.css \u2014 local theme freshness check not applicable"
|
|
484
437
|
)
|
|
485
438
|
];
|
|
486
439
|
}
|
|
@@ -497,232 +450,9 @@ function checkThemeFreshness(projectRoot, context) {
|
|
|
497
450
|
)
|
|
498
451
|
];
|
|
499
452
|
}
|
|
500
|
-
if (!context) {
|
|
501
|
-
return [
|
|
502
|
-
THEME(
|
|
503
|
-
"skip",
|
|
504
|
-
"local derivations fresh; platform snapshot unverified \u2014 board context unavailable"
|
|
505
|
-
)
|
|
506
|
-
];
|
|
507
|
-
}
|
|
508
|
-
if (context.themeSnapshotHash !== tokensHash) {
|
|
509
|
-
return [
|
|
510
|
-
THEME(
|
|
511
|
-
"fail",
|
|
512
|
-
`platform theme snapshot is ${context.themeSnapshotHash ? "stale" : "missing"} \u2014 emails render ${context.themeSnapshotHash ? "an old" : "the legacy"} theme; sync it: \`npm run gen:theme -- --payload | npx convex run boards/themeSnapshot:sync\``
|
|
513
|
-
)
|
|
514
|
-
];
|
|
515
|
-
}
|
|
516
453
|
return [
|
|
517
|
-
THEME(
|
|
518
|
-
"pass",
|
|
519
|
-
`tokens.css \u21C4 resolved module \u21C4 platform snapshot (${tokensHash.slice(0, 12)}\u2026)`
|
|
520
|
-
)
|
|
521
|
-
];
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// src/doctor/writes.ts
|
|
525
|
-
var WRITE = {
|
|
526
|
-
board: record("write.board", 3),
|
|
527
|
-
register: record("write.register", 3),
|
|
528
|
-
login: record("write.login", 3),
|
|
529
|
-
email: record("write.email", 3),
|
|
530
|
-
apply: record("write.apply", 3),
|
|
531
|
-
alert: record("write.alert", 3)
|
|
532
|
-
};
|
|
533
|
-
var DEPENDENT_CHECKS = [
|
|
534
|
-
WRITE.register,
|
|
535
|
-
WRITE.login,
|
|
536
|
-
WRITE.email,
|
|
537
|
-
WRITE.apply,
|
|
538
|
-
WRITE.alert
|
|
539
|
-
];
|
|
540
|
-
function skipWriteProbes(reason) {
|
|
541
|
-
return Object.values(WRITE).map((make) => make("skip", reason));
|
|
542
|
-
}
|
|
543
|
-
var FETCH_TIMEOUT_MS2 = 1e4;
|
|
544
|
-
var RESEND_API_BASE = "https://api.resend.com";
|
|
545
|
-
async function request(fetchImpl, url, init) {
|
|
546
|
-
try {
|
|
547
|
-
const response = await fetchImpl(url, {
|
|
548
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2),
|
|
549
|
-
...init
|
|
550
|
-
});
|
|
551
|
-
let json = null;
|
|
552
|
-
try {
|
|
553
|
-
json = await response.json();
|
|
554
|
-
} catch {
|
|
555
|
-
json = null;
|
|
556
|
-
}
|
|
557
|
-
return { ok: response.ok, status: response.status, json };
|
|
558
|
-
} catch (error) {
|
|
559
|
-
return {
|
|
560
|
-
ok: false,
|
|
561
|
-
status: 0,
|
|
562
|
-
json: { error: error instanceof Error ? error.message : String(error) }
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
function post(fetchImpl, url, body, bearer) {
|
|
567
|
-
return request(fetchImpl, url, {
|
|
568
|
-
method: "POST",
|
|
569
|
-
headers: {
|
|
570
|
-
"content-type": "application/json",
|
|
571
|
-
accept: "application/json",
|
|
572
|
-
...bearer ? { authorization: `Bearer ${bearer}` } : {}
|
|
573
|
-
},
|
|
574
|
-
body: JSON.stringify(body)
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
578
|
-
async function observeVerificationEmail(fetchImpl, apiKey, recipient, pollMs, timeoutMs) {
|
|
579
|
-
const deadline = Date.now() + timeoutMs;
|
|
580
|
-
const subjectMarker = `[To: ${recipient}]`;
|
|
581
|
-
do {
|
|
582
|
-
const list = await request(
|
|
583
|
-
fetchImpl,
|
|
584
|
-
`${RESEND_API_BASE}/emails?limit=20`,
|
|
585
|
-
{
|
|
586
|
-
headers: { authorization: `Bearer ${apiKey}` }
|
|
587
|
-
}
|
|
588
|
-
);
|
|
589
|
-
if (!list.ok) {
|
|
590
|
-
return WRITE.email(
|
|
591
|
-
"fail",
|
|
592
|
-
`Resend list returned HTTP ${list.status} \u2014 wrong RESEND_API_KEY?`
|
|
593
|
-
);
|
|
594
|
-
}
|
|
595
|
-
const items = list.json?.data ?? [];
|
|
596
|
-
const match = items.find(
|
|
597
|
-
(item) => item.subject?.includes(subjectMarker) || item.to?.includes(recipient)
|
|
598
|
-
);
|
|
599
|
-
if (match) {
|
|
600
|
-
return WRITE.email(
|
|
601
|
-
"pass",
|
|
602
|
-
`verification send observed in Resend for ${recipient}`
|
|
603
|
-
);
|
|
604
|
-
}
|
|
605
|
-
await sleep(pollMs);
|
|
606
|
-
} while (Date.now() < deadline);
|
|
607
|
-
return WRITE.email(
|
|
608
|
-
"fail",
|
|
609
|
-
`no verification send for ${recipient} appeared in Resend within ${timeoutMs}ms`
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
async function runWriteProbes(options) {
|
|
613
|
-
const { fetchImpl } = options;
|
|
614
|
-
const base = `${apiBase(options.env.apiUrl)}/v1/boards/${encodeURIComponent(options.env.boardKey)}`;
|
|
615
|
-
const nonce = options.nonce ?? `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
616
|
-
const email = `doctor+${nonce}@cavuno.com`;
|
|
617
|
-
const password = `doctor-probe-${nonce}-A1!`;
|
|
618
|
-
let context = options.boardContext ?? null;
|
|
619
|
-
if (context === null) {
|
|
620
|
-
const r = await request(fetchImpl, base);
|
|
621
|
-
if (!r.ok) {
|
|
622
|
-
return [
|
|
623
|
-
WRITE.board("fail", `board context did not resolve (HTTP ${r.status})`),
|
|
624
|
-
...DEPENDENT_CHECKS.map(
|
|
625
|
-
(make) => make("skip", "not run \u2014 the write gate failed (see write.board)")
|
|
626
|
-
)
|
|
627
|
-
];
|
|
628
|
-
}
|
|
629
|
-
context = r.json ?? {};
|
|
630
|
-
}
|
|
631
|
-
if (context.sandbox !== true) {
|
|
632
|
-
return [
|
|
633
|
-
WRITE.board(
|
|
634
|
-
"fail",
|
|
635
|
-
"refusing write probes: this board is not the platform sandbox (`sandbox` is not true) \u2014 point PUBLIC_CAVUNO_BOARD at the sandbox pk_"
|
|
636
|
-
),
|
|
637
|
-
...DEPENDENT_CHECKS.map(
|
|
638
|
-
(make) => make("skip", "not run \u2014 the write gate failed (see write.board)")
|
|
639
|
-
)
|
|
640
|
-
];
|
|
641
|
-
}
|
|
642
|
-
const results = [
|
|
643
|
-
WRITE.board("pass", "board is the platform sandbox")
|
|
454
|
+
THEME("pass", `tokens.css \u21C4 resolved module (${tokensHash.slice(0, 12)}\u2026)`)
|
|
644
455
|
];
|
|
645
|
-
const registered = await post(fetchImpl, `${base}/auth/register`, {
|
|
646
|
-
role: "candidate",
|
|
647
|
-
method: "emailpass",
|
|
648
|
-
email,
|
|
649
|
-
password,
|
|
650
|
-
displayName: "Doctor Probe"
|
|
651
|
-
});
|
|
652
|
-
const sessionOk = (r) => r.ok && typeof r.json?.accessToken === "string";
|
|
653
|
-
results.push(
|
|
654
|
-
sessionOk(registered) ? WRITE.register("pass", `candidate registered (${email})`) : WRITE.register(
|
|
655
|
-
"fail",
|
|
656
|
-
`register returned HTTP ${registered.status}` + (registered.status === 429 ? " \u2014 rate-limited: register allows 5 signups / 15 min per IP; wait before rerunning" : "")
|
|
657
|
-
)
|
|
658
|
-
);
|
|
659
|
-
let accessToken = null;
|
|
660
|
-
if (sessionOk(registered)) {
|
|
661
|
-
const login = await post(fetchImpl, `${base}/auth/login`, {
|
|
662
|
-
email,
|
|
663
|
-
password
|
|
664
|
-
});
|
|
665
|
-
if (sessionOk(login)) {
|
|
666
|
-
accessToken = login.json.accessToken;
|
|
667
|
-
results.push(WRITE.login("pass", "emailpass login returns a session"));
|
|
668
|
-
} else {
|
|
669
|
-
results.push(WRITE.login("fail", `login returned HTTP ${login.status}`));
|
|
670
|
-
}
|
|
671
|
-
} else {
|
|
672
|
-
results.push(WRITE.login("skip", "not run \u2014 register failed"));
|
|
673
|
-
}
|
|
674
|
-
if (!sessionOk(registered)) {
|
|
675
|
-
results.push(WRITE.email("skip", "not run \u2014 register failed"));
|
|
676
|
-
} else if (!options.resendApiKey) {
|
|
677
|
-
results.push(
|
|
678
|
-
WRITE.email(
|
|
679
|
-
"skip",
|
|
680
|
-
"RESEND_API_KEY not set \u2014 the verification send was NOT verified (platform-operator check)"
|
|
681
|
-
)
|
|
682
|
-
);
|
|
683
|
-
} else {
|
|
684
|
-
results.push(
|
|
685
|
-
await observeVerificationEmail(
|
|
686
|
-
fetchImpl,
|
|
687
|
-
options.resendApiKey,
|
|
688
|
-
email,
|
|
689
|
-
options.emailPollMs ?? 2e3,
|
|
690
|
-
options.emailTimeoutMs ?? 3e4
|
|
691
|
-
)
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
if (!accessToken) {
|
|
695
|
-
results.push(WRITE.apply("skip", "not run \u2014 no session (see above)"));
|
|
696
|
-
} else {
|
|
697
|
-
const listing = await request(fetchImpl, `${base}/jobs?limit=1`);
|
|
698
|
-
const card = listing.json?.data?.[0];
|
|
699
|
-
if (!listing.ok || !card?.slug) {
|
|
700
|
-
results.push(
|
|
701
|
-
WRITE.apply(
|
|
702
|
-
"fail",
|
|
703
|
-
listing.ok ? "the board has no jobs to apply to \u2014 is the sandbox seeded?" : `jobs listing returned HTTP ${listing.status}`
|
|
704
|
-
)
|
|
705
|
-
);
|
|
706
|
-
} else {
|
|
707
|
-
const applied = await post(
|
|
708
|
-
fetchImpl,
|
|
709
|
-
`${base}/jobs/${encodeURIComponent(card.slug)}/apply`,
|
|
710
|
-
{},
|
|
711
|
-
accessToken
|
|
712
|
-
);
|
|
713
|
-
results.push(
|
|
714
|
-
applied.ok && typeof applied.json?.id === "string" ? WRITE.apply("pass", `applied to ${card.slug}`) : WRITE.apply("fail", `apply returned HTTP ${applied.status}`)
|
|
715
|
-
);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
const alert = await post(fetchImpl, `${base}/job-alerts`, {
|
|
719
|
-
email,
|
|
720
|
-
consent: true
|
|
721
|
-
});
|
|
722
|
-
results.push(
|
|
723
|
-
alert.ok ? WRITE.alert("pass", "job-alert subscription accepted") : WRITE.alert("fail", `job-alerts returned HTTP ${alert.status}`)
|
|
724
|
-
);
|
|
725
|
-
return results;
|
|
726
456
|
}
|
|
727
457
|
|
|
728
458
|
// src/doctor/run.ts
|
|
@@ -761,31 +491,21 @@ async function checkBoardResolves(fetchImpl, env) {
|
|
|
761
491
|
`${apiBase(env.apiUrl)}/v1/boards/${encodeURIComponent(env.boardKey)}`
|
|
762
492
|
);
|
|
763
493
|
if (!result.ok) {
|
|
764
|
-
return
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
),
|
|
769
|
-
context: null
|
|
770
|
-
};
|
|
494
|
+
return STATIC_BOARD(
|
|
495
|
+
"fail",
|
|
496
|
+
`board did not resolve for this key (HTTP ${result.status}) \u2014 revoked or wrong key?`
|
|
497
|
+
);
|
|
771
498
|
}
|
|
772
|
-
let parsed = null;
|
|
773
499
|
try {
|
|
774
|
-
parsed = JSON.parse(result.body);
|
|
500
|
+
const parsed = JSON.parse(result.body);
|
|
775
501
|
if (typeof parsed !== "object" || parsed === null) throw new Error("shape");
|
|
776
502
|
} catch {
|
|
777
|
-
return
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
),
|
|
782
|
-
context: null
|
|
783
|
-
};
|
|
503
|
+
return STATIC_BOARD(
|
|
504
|
+
"fail",
|
|
505
|
+
"board endpoint returned 200 but not JSON \u2014 proxy or captive portal in the way?"
|
|
506
|
+
);
|
|
784
507
|
}
|
|
785
|
-
return
|
|
786
|
-
result: STATIC_BOARD("pass", "publishable key resolves the board"),
|
|
787
|
-
context: parsed
|
|
788
|
-
};
|
|
508
|
+
return STATIC_BOARD("pass", "publishable key resolves the board");
|
|
789
509
|
}
|
|
790
510
|
var SKILL_ROOTS = [
|
|
791
511
|
".claude/skills",
|
|
@@ -848,46 +568,19 @@ async function runDoctor(options) {
|
|
|
848
568
|
} else {
|
|
849
569
|
results.push(STATIC_API("skip", "PUBLIC_CAVUNO_API_URL not set"));
|
|
850
570
|
}
|
|
851
|
-
let boardContext = null;
|
|
852
571
|
if (envOk) {
|
|
853
|
-
|
|
854
|
-
boardContext = resolved.context;
|
|
855
|
-
results.push(resolved.result);
|
|
572
|
+
results.push(await checkBoardResolves(fetchImpl, env));
|
|
856
573
|
} else {
|
|
857
574
|
results.push(STATIC_BOARD("skip", "env checks failed \u2014 fix them first"));
|
|
858
575
|
}
|
|
859
576
|
results.push(checkSkillsFreshness(options.projectRoot ?? process.cwd()));
|
|
860
|
-
results.push(
|
|
861
|
-
...checkThemeFreshness(options.projectRoot ?? process.cwd(), boardContext)
|
|
862
|
-
);
|
|
577
|
+
results.push(...checkThemeFreshness(options.projectRoot ?? process.cwd()));
|
|
863
578
|
results.push(
|
|
864
579
|
...checkCookieCodecConformance(options.projectRoot ?? process.cwd())
|
|
865
580
|
);
|
|
866
|
-
results.push(
|
|
867
|
-
envOk ? await checkSandboxPersonaGate(fetchImpl, env, boardContext) : skipSandboxPersonaGate("env checks failed \u2014 fix them first")
|
|
868
|
-
);
|
|
869
581
|
results.push(
|
|
870
582
|
...options.frontendUrl ? await runReadProbes(fetchImpl, options.frontendUrl) : skipReadProbes("no --frontend <url> given")
|
|
871
583
|
);
|
|
872
|
-
if (!options.sandbox) {
|
|
873
|
-
results.push(
|
|
874
|
-
...skipWriteProbes(
|
|
875
|
-
"write probes not enabled \u2014 run with --sandbox against the platform sandbox"
|
|
876
|
-
)
|
|
877
|
-
);
|
|
878
|
-
} else if (!envOk) {
|
|
879
|
-
results.push(...skipWriteProbes("env checks failed \u2014 fix them first"));
|
|
880
|
-
} else {
|
|
881
|
-
results.push(
|
|
882
|
-
...await runWriteProbes({
|
|
883
|
-
env,
|
|
884
|
-
fetchImpl,
|
|
885
|
-
resendApiKey: options.resendApiKey,
|
|
886
|
-
nonce: options.writeProbeNonce,
|
|
887
|
-
boardContext
|
|
888
|
-
})
|
|
889
|
-
);
|
|
890
|
-
}
|
|
891
584
|
return { results, summary: summarize(results) };
|
|
892
585
|
}
|
|
893
586
|
// Annotate the CommonJS export names for ESM import in node:
|