



























@@ -23,7 +23,7 @@ type CrabboxInspect = {
2323};
24242525type Options = {
26-command: "finish" | "probe" | "run" | "screenshot" | "send" | "start" | "status";
26+command: "finish" | "probe" | "publish" | "run" | "screenshot" | "send" | "start" | "status";
2727 crabboxBin: string;
2828 desktopChatTitle: string;
2929 dryRun: boolean;
@@ -36,6 +36,10 @@ type Options = {
3636 mockPort: number;
3737 outputDir: string;
3838 provider: string;
39+ publishFullArtifacts: boolean;
40+publishPr?: number;
41+ publishRepo: string;
42+publishSummary?: string;
3943 recordSeconds: number;
4044 remoteCommand: string[];
4145sessionFile?: string;
@@ -120,14 +124,19 @@ function usageText() {
120124" node --import tsx scripts/e2e/telegram-user-crabbox-proof.ts screenshot --session <session.json>",
121125" node --import tsx scripts/e2e/telegram-user-crabbox-proof.ts status --session <session.json>",
122126" node --import tsx scripts/e2e/telegram-user-crabbox-proof.ts finish --session <session.json>",
127+" node --import tsx scripts/e2e/telegram-user-crabbox-proof.ts publish --session <session.json> --pr <number>",
123128"",
124129"Useful options:",
125130" --desktop-chat-title <name> Telegram Desktop chat to select before recording.",
126131" --id <cbx_id> Reuse an existing Crabbox desktop lease.",
127132" --keep-box Leave the Crabbox lease running for VNC debugging.",
128133" --output-dir <path> Artifact directory under the repo.",
134+" --pr <number> Pull request number for publish.",
129135" --record-seconds <seconds> Desktop video duration. Default: 35.",
136+" --repo <owner/name> GitHub repo for publish. Default: openclaw/openclaw.",
130137" --session <path> Session file from start. Default: <output-dir>/session.json.",
138+" --summary <text> Artifact publish summary.",
139+" --full-artifacts Publish all session artifacts. Default publishes only the motion GIF.",
131140" --tdlib-sha256 <hex> Expected SHA-256 for --tdlib-url. Defaults to <url>.sha256.",
132141" --tdlib-url <url> Linux tdlib archive containing libtdjson.so.",
133142" --dry-run Validate local inputs and print the plan.",
@@ -163,7 +172,16 @@ function parsePositiveInteger(value: string, label: string) {
163172164173function parseArgs(argv: string[]): Options {
165174argv = argv[0] === "--" ? argv.slice(1) : argv;
166-const commands = new Set(["finish", "probe", "run", "screenshot", "send", "start", "status"]);
175+const commands = new Set([
176+"finish",
177+"probe",
178+"publish",
179+"run",
180+"screenshot",
181+"send",
182+"start",
183+"status",
184+]);
167185const command = commands.has(argv[0] ?? "") ? (argv.shift() as Options["command"]) : "probe";
168186const stamp = new Date().toISOString().replace(/[:.]/gu, "-");
169187const opts: Options = {
@@ -179,6 +197,8 @@ function parseArgs(argv: string[]): Options {
179197mockPort: 19_882,
180198outputDir: path.join(DEFAULT_OUTPUT_ROOT, stamp),
181199provider: process.env.OPENCLAW_TELEGRAM_USER_CRABBOX_PROVIDER?.trim() || "aws",
200+publishFullArtifacts: false,
201+publishRepo: "openclaw/openclaw",
182202recordSeconds: 35,
183203remoteCommand: [],
184204target: "linux",
@@ -231,10 +251,18 @@ function parseArgs(argv: string[]): Options {
231251opts.outputDir = readValue();
232252} else if (arg === "--provider") {
233253opts.provider = readValue();
254+} else if (arg === "--pr") {
255+opts.publishPr = parsePositiveInteger(readValue(), "--pr");
256+} else if (arg === "--repo") {
257+opts.publishRepo = readValue();
234258} else if (arg === "--record-seconds") {
235259opts.recordSeconds = parsePositiveInteger(readValue(), "--record-seconds");
236260} else if (arg === "--session") {
237261opts.sessionFile = readValue();
262+} else if (arg === "--summary") {
263+opts.publishSummary = readValue();
264+} else if (arg === "--full-artifacts") {
265+opts.publishFullArtifacts = true;
238266} else if (arg === "--sut-username") {
239267opts.sutUsername = readValue().replace(/^@/u, "");
240268} else if (arg === "--target") {
@@ -261,9 +289,15 @@ function parseArgs(argv: string[]): Options {
261289if (command === "run" && opts.remoteCommand.length === 0) {
262290throw new Error("run requires a remote command after --.");
263291}
264-if (["finish", "run", "screenshot", "send", "status"].includes(command) && !opts.sessionFile) {
292+if (
293+["finish", "publish", "run", "screenshot", "send", "status"].includes(command) &&
294+!opts.sessionFile
295+) {
265296throw new Error(`${command} requires --session.`);
266297}
298+if (command === "publish" && !opts.publishPr) {
299+throw new Error("publish requires --pr.");
300+}
267301return opts;
268302}
269303@@ -1550,6 +1584,55 @@ async function finishSession(root: string, opts: Options, outputDir: string) {
15501584 }
15511585}
155215861587+async function publishSessionArtifacts(root: string, opts: Options, outputDir: string) {
1588+ const { session } = readSession(root, opts, outputDir);
1589+ const motionGifPath = path.join(session.outputDir, "telegram-user-crabbox-session-motion.gif");
1590+ const publishDir = opts.publishFullArtifacts
1591+ ? session.outputDir
1592+ : path.join(session.outputDir, "publish-gif-only");
1593+ if (!opts.publishFullArtifacts) {
1594+ if (!fs.existsSync(motionGifPath)) {
1595+ throw new Error(
1596+ `Missing motion GIF. Run finish first: ${path.relative(root, motionGifPath)}`,
1597+ );
1598+ }
1599+ fs.rmSync(publishDir, { force: true, recursive: true });
1600+ fs.mkdirSync(publishDir, { recursive: true });
1601+ fs.copyFileSync(
1602+ motionGifPath,
1603+ path.join(publishDir, "telegram-user-crabbox-session-motion.gif"),
1604+ );
1605+ }
1606+ await runCommand({
1607+ command: opts.crabboxBin,
1608+ args: [
1609+ "artifacts",
1610+ "publish",
1611+ "--pr",
1612+ String(opts.publishPr),
1613+ "--repo",
1614+ opts.publishRepo,
1615+ "--dir",
1616+ publishDir,
1617+ "--summary",
1618+ opts.publishSummary ??
1619+ (opts.publishFullArtifacts
1620+ ? "Telegram real-user Crabbox session artifacts"
1621+ : "Telegram real-user Crabbox session motion GIF"),
1622+ "--template",
1623+ "openclaw",
1624+ ...(opts.dryRun ? ["--dry-run"] : []),
1625+ ],
1626+ cwd: root,
1627+ stdio: "inherit",
1628+ });
1629+ return {
1630+ artifactMode: opts.publishFullArtifacts ? "full" : "gif-only",
1631+ publishDir: path.relative(root, publishDir),
1632+ status: "pass",
1633+ };
1634+}
1635+15531636async function main() {
15541637 const opts = parseArgs(process.argv.slice(2));
15551638 const root = repoRoot();
@@ -1581,6 +1664,10 @@ async function main() {
15811664 await finishSession(root, opts, outputDir);
15821665 return;
15831666 }
1667+ if (opts.command === "publish") {
1668+ console.log(JSON.stringify(await publishSessionArtifacts(root, opts, outputDir), null, 2));
1669+ return;
1670+ }
1584167115851672 const localRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-telegram-crabbox-"));
15861673 const summary: JsonObject = {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。