Files
clawhub/scripts/seed.ts
T
Patrick Erichsen 0fb07e5b99 feat: align skills.sh catalog presentation (#3370)
* feat: align skills.sh catalog presentation

* refactor: share skill detail shell with skills.sh

* test: seed skills.sh route fixtures locally

* refactor: share full skill detail page view

* feat: refine skills.sh detail presentation

* feat: refine skills.sh detail metadata

* fix: refine skills.sh detail spacing
2026-08-01 16:46:48 -07:00

159 lines
4.2 KiB
TypeScript

#!/usr/bin/env bun
import { spawnSync } from "node:child_process";
type SeedOptions = {
previewName: string | null;
};
type SeedStep = {
command: string;
args: string[];
};
const CONVEX_FUNCTIONS_READY_TIMEOUT_MS = 120_000;
const REACHABILITY_POLL_MS = 500;
export function parseSeedArgs(args: string[]): SeedOptions {
const options: SeedOptions = { previewName: null };
for (let index = 0; index < args.length; index += 1) {
const arg = args[index];
if (arg === "--preview-name") {
options.previewName = readValue(args, ++index, arg);
} else if (arg.startsWith("--preview-name=")) {
options.previewName = arg.slice("--preview-name=".length);
} else {
throw new Error(`Unknown argument: ${arg}`);
}
}
return options;
}
export function buildSeedSteps(options: SeedOptions): SeedStep[] {
const convexTargetArgs = options.previewName
? ["--preview-name", options.previewName]
: ["--no-push"];
const corpusTargetArgs = options.previewName ? ["--preview-name", options.previewName] : [];
return [
{
command: "bunx",
args: ["convex", "run", ...convexTargetArgs, "devSeed:seedLocalFixtures"],
},
{
command: "bunx",
args: ["convex", "run", ...convexTargetArgs, "devSeed:seedCanonicalSearchFixture"],
},
{
command: "bun",
args: ["scripts/public-corpus/seed-public-corpus.ts", ...corpusTargetArgs],
},
{
command: "bun",
args: ["scripts/public-corpus/seed-catalog-presentation.ts", ...corpusTargetArgs],
},
{
command: "bunx",
args: ["convex", "run", ...convexTargetArgs, "statsMaintenance:updateGlobalStatsAction"],
},
];
}
export function isConvexFunctionUnavailableOutput(output: string) {
return (
output.includes("Could not find function for") &&
output.includes("Did you forget to run `npx convex dev`")
);
}
export function assertSeedTargetAllowed(
options: SeedOptions,
env: NodeJS.ProcessEnv = process.env,
) {
if (options.previewName) {
assertPreviewSeedTargetAllowed(env);
return;
}
const deployment = env.CONVEX_DEPLOYMENT?.trim();
if (
deployment === "anonymous-agent" ||
deployment?.startsWith("anonymous:") ||
deployment?.startsWith("local:")
) {
return;
}
throw new Error("Shared seed without --deployment requires a local Convex deployment");
}
export function assertPreviewSeedTargetAllowed(env: NodeJS.ProcessEnv = process.env) {
if (!env.CONVEX_DEPLOY_KEY?.trim().startsWith("preview:")) {
throw new Error("Shared preview seed requires a Convex Preview deploy key");
}
}
async function runStep(step: SeedStep) {
if (step.command !== "bunx" || step.args[0] !== "convex" || step.args[1] !== "run") {
return (
spawnSync(step.command, step.args, {
cwd: process.cwd(),
env: process.env,
stdio: "inherit",
}).status ?? 1
);
}
const startedAt = Date.now();
while (true) {
const result = spawnSync(step.command, step.args, {
cwd: process.cwd(),
encoding: "utf8",
env: process.env,
});
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
if (result.status === 0) {
if (output) process.stdout.write(output);
return 0;
}
if (
!isConvexFunctionUnavailableOutput(output) ||
Date.now() - startedAt >= CONVEX_FUNCTIONS_READY_TIMEOUT_MS
) {
if (output) process.stdout.write(output);
return result.status ?? 1;
}
console.log("Convex functions are not queryable yet; retrying...");
await sleep(REACHABILITY_POLL_MS);
}
}
function sleep(ms: number) {
return new Promise<void>((resolve) => {
setTimeout(resolve, ms);
});
}
function readValue(args: string[], index: number, flag: string) {
const value = args[index]?.trim();
if (!value) throw new Error(`Missing value for ${flag}`);
return value;
}
async function main() {
const options = parseSeedArgs(process.argv.slice(2));
assertSeedTargetAllowed(options);
for (const step of buildSeedSteps(options)) {
const status = await runStep(step);
if (status !== 0) process.exit(status);
}
}
if (import.meta.main) {
main().catch((error: unknown) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
}