项目文件夹

文件
wehub-resource-sync b3a7f98e5a
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:23:53 +08:00

137 行
4.6 KiB
TypeScript

// Local prototype runner.
//
// Wraps `flue run investigate` for convenience. Reads an issue fixture
// (or pulls one live with `gh issue view`), constructs the payload, and
// prints the structured InvestigateResult. No GitHub writes -- the
// orchestrator that does writes lives in .github/workflows/investigate.yml,
// not here.
//
// The investigate workflow expects AGENT_GH_TOKEN to be set; we forward
// whichever of GITHUB_TOKEN / GH_TOKEN the user has, treating it as the
// "agent" token even though locally there's no orchestrator/agent split.
// The agent's read-only token only affects what `gh issue view` etc.
// inside its sandbox can do; on a maintainer's laptop the host user
// already has those reads, so the sandbox token mostly stops the agent
// from doing accidental writes from inside its bash.
//
// Required env:
// CLOUDFLARE_ACCOUNT_ID
// CLOUDFLARE_GATEWAY_ID
// CLOUDFLARE_API_KEY
//
// Usage:
// pnpm prototype 1021 # one fixture
// pnpm prototype 1021 1049 1080 # several
// pnpm prototype --live 1083 # fetch live with gh
// FLUE_INVESTIGATE_MODEL=cloudflare-ai-gateway/claude-sonnet-4-6 pnpm prototype 1021
import { execSync, spawnSync } from "node:child_process";
import { readFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
interface Fixture {
number: number;
title: string;
body: string;
labels?: Array<{ name: string }>;
}
const HERE = dirname(fileURLToPath(import.meta.url));
const FIXTURES_DIR = resolve(HERE, "..", "fixtures");
const FLUE_DIR = resolve(HERE, "..");
const ISSUE_NUMBER_RE = /^\d+$/;
async function loadFixture(arg: string, live: boolean): Promise<Fixture> {
// `arg` is interpolated into a shell command (`gh issue view`) and a
// file path (`fixtures/issue-<arg>.json`). Restrict to plain integers
// so a `'1 && rm -rf /'` style input cannot smuggle metachars through
// execSync or path traversal through the fixture lookup.
if (!ISSUE_NUMBER_RE.test(arg)) {
throw new Error(`issueNumber must be a positive integer, got: ${JSON.stringify(arg)}`);
}
if (live) {
const raw = execSync(
`gh issue view ${arg} --repo emdash-cms/emdash --json number,title,body,labels`,
{ encoding: "utf8" },
);
const parsed: Fixture = JSON.parse(raw);
return parsed;
}
const path = join(FIXTURES_DIR, `issue-${arg}.json`);
const parsed: Fixture = JSON.parse(await readFile(path, "utf8"));
return parsed;
}
async function runOne(fixture: Fixture): Promise<void> {
const payload = JSON.stringify({
issueNumber: fixture.number,
issueTitle: fixture.title,
issueBody: fixture.body,
owner: "emdash-cms",
repo: "emdash",
});
console.error(`\n=== issue #${fixture.number}: ${fixture.title}`);
const start = Date.now();
// `pnpm exec` (not `npx`) so we invoke the lockfile-pinned Flue.
// `flue run` in 0.8 generates the workflow run id itself; no --id flag.
const result = spawnSync(
"pnpm",
["exec", "flue", "run", "investigate", "--target", "node", "--payload", payload],
{
cwd: FLUE_DIR,
env: process.env,
encoding: "utf8",
},
);
const elapsed = Date.now() - start;
console.error(`[${elapsed}ms] exit=${result.status}`);
if (result.stderr) console.error(result.stderr);
if (result.stdout) console.log(result.stdout);
}
async function main() {
const args = process.argv.slice(2);
const live = args.includes("--live");
const issueArgs = args.filter((a) => !a.startsWith("--"));
if (issueArgs.length === 0) {
console.error("usage: tsx scripts/run-local.ts [--live] <issueNumber> [<issueNumber>...]");
process.exit(2);
}
const missingGateway = [
"CLOUDFLARE_ACCOUNT_ID",
"CLOUDFLARE_GATEWAY_ID",
"CLOUDFLARE_API_KEY",
].filter((k) => !process.env[k]);
if (missingGateway.length > 0) {
console.error(`missing required env: ${missingGateway.join(", ")}`);
process.exit(2);
}
// Normalise GITHUB_TOKEN / GH_TOKEN into AGENT_GH_TOKEN, which is
// what investigate.ts reads. The workflow itself sets this explicitly
// from `secrets.GITHUB_TOKEN`; locally we accept the user's gh CLI
// token, treating it the same way.
const agentToken = process.env.AGENT_GH_TOKEN ?? process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
if (!agentToken) {
console.error("AGENT_GH_TOKEN (or GITHUB_TOKEN / GH_TOKEN) required for the agent's sandbox");
process.exit(2);
}
process.env.AGENT_GH_TOKEN = agentToken;
for (const arg of issueArgs) {
const fixture = await loadFixture(arg, live);
await runOne(fixture);
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});