|
1 | 1 | #!/usr/bin/env -S node --import tsx |
2 | 2 | // Code Mode Namespace Live script supports OpenClaw repository automation. |
3 | 3 | import { performance } from "node:perf_hooks"; |
| 4 | +import { pathToFileURL } from "node:url"; |
4 | 5 | import { Type } from "typebox"; |
5 | 6 | import type { Model } from "../../packages/agent-core/src/llm.js"; |
6 | 7 | import type { AgentEvent, AgentTool } from "../../packages/agent-core/src/types.js"; |
@@ -80,6 +81,7 @@ type RunMetrics = {
|
80 | 81 | }; |
81 | 82 | |
82 | 83 | const PLUGIN_ID = "fictions-live"; |
| 84 | +const POSITIVE_INTEGER_PATTERN = /^[1-9]\d*$/u; |
83 | 85 | |
84 | 86 | function cloneState(): FictionServiceState { |
85 | 87 | return { |
@@ -598,19 +600,32 @@ function readArg(name: string): string | undefined {
|
598 | 600 | return match?.slice(prefix.length); |
599 | 601 | } |
600 | 602 | |
601 | | -async function main() { |
602 | | -const apiKey = process.env.OPENAI_API_KEY?.trim(); |
603 | | -if (!apiKey) { |
604 | | -throw new Error("OPENAI_API_KEY is required"); |
| 603 | +export function parseTaskLimit(raw: string | undefined, label: string): number { |
| 604 | + const text = raw?.trim() ?? "3"; |
| 605 | + if (!POSITIVE_INTEGER_PATTERN.test(text)) { |
| 606 | + throw new Error(`${label} must be a positive integer`); |
| 607 | + } |
| 608 | + const parsed = Number(text); |
| 609 | + if (!Number.isSafeInteger(parsed)) { |
| 610 | + throw new Error(`${label} must be a safe positive integer`); |
605 | 611 | } |
| 612 | +return parsed; |
| 613 | +} |
| 614 | + |
| 615 | +export async function main() { |
606 | 616 | const model = readArg("model") ?? process.env.OPENCLAW_CODE_MODE_LIVE_MODEL ?? "gpt-5.4-mini"; |
607 | 617 | const modeArg = readArg("modes"); |
608 | 618 | const modes = (modeArg ? modeArg.split(",") : ["regular", "code-namespace"]) as Mode[]; |
609 | | -const taskLimit = Number(readArg("tasks") ?? process.env.OPENCLAW_CODE_MODE_LIVE_TASKS ?? "3"); |
610 | | -const selectedTasks = tasks.slice( |
611 | | -0, |
612 | | -Number.isFinite(taskLimit) && taskLimit > 0 ? taskLimit : tasks.length, |
| 619 | +const taskArg = readArg("tasks"); |
| 620 | +const taskLimit = parseTaskLimit( |
| 621 | +taskArg ?? process.env.OPENCLAW_CODE_MODE_LIVE_TASKS, |
| 622 | +taskArg === undefined ? "OPENCLAW_CODE_MODE_LIVE_TASKS" : "--tasks", |
613 | 623 | ); |
| 624 | +const apiKey = process.env.OPENAI_API_KEY?.trim(); |
| 625 | +if (!apiKey) { |
| 626 | +throw new Error("OPENAI_API_KEY is required"); |
| 627 | +} |
| 628 | +const selectedTasks = tasks.slice(0, taskLimit); |
614 | 629 | const results: RunMetrics[] = []; |
615 | 630 | for (const task of selectedTasks) { |
616 | 631 | for (const mode of modes) { |
@@ -640,6 +655,8 @@ async function main() {
|
640 | 655 | } |
641 | 656 | } |
642 | 657 | |
643 | | -await main().finally(() => { |
644 | | -clearCodeModeNamespacesForPlugin(PLUGIN_ID); |
645 | | -}); |
| 658 | +if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { |
| 659 | +await main().finally(() => { |
| 660 | +clearCodeModeNamespacesForPlugin(PLUGIN_ID); |
| 661 | +}); |
| 662 | +} |