@fieldwangai/agentflow 0.1.165 → 0.1.167

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.
@@ -30,8 +30,8 @@
30
30
  @keyframes af-app-loading-slide { from { transform: translateX(-115%); } to { transform: translateX(250%); } }
31
31
  @media (prefers-reduced-motion: reduce) { .af-app-loading__track span { width: 100%; animation: none; } }
32
32
  </style>
33
- <script type="module" crossorigin src="/assets/index-Czutb6ai.js"></script>
34
- <link rel="stylesheet" crossorigin href="/assets/index-BQeq5tdj.css">
33
+ <script type="module" crossorigin src="/assets/index-BLTi7FF5.js"></script>
34
+ <link rel="stylesheet" crossorigin href="/assets/index-yplDmRpj.css">
35
35
  </head>
36
36
  <body>
37
37
  <div id="root">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fieldwangai/agentflow",
3
- "version": "0.1.165",
3
+ "version": "0.1.167",
4
4
  "description": "Orchestration system for long-running complex agent tasks using Cursor, OpenCode, Claude Code, or Codex as execution backends",
5
5
  "type": "module",
6
6
  "main": "bin/agentflow.mjs",
@@ -56,7 +56,7 @@
56
56
  "dev:web": "cd builtin/web-ui && npm run dev",
57
57
  "dev:website": "cd website && npm run dev",
58
58
  "preview:website": "cd website && npm run preview",
59
- "version": "npm --prefix builtin/web-ui run build && git add builtin/web-ui/dist",
59
+ "version": "npm run build:agentflow-cli-skill-runtime && npm --prefix builtin/web-ui run build && git add skills/agentflow-cli/runtime builtin/web-ui/dist",
60
60
  "prepack": "npm run build:web-ui"
61
61
  },
62
62
  "dependencies": {
@@ -0,0 +1,127 @@
1
+ ---
2
+ name: agentflow-ai-exploration
3
+ description: Plan, visualize, audit, and archive AI execution through AgentFlow without MCP. Use when Codex or another agent needs to create a read-only expected execution graph, stream planned or actual Trace/Span events into a Workspace, perform a side-effect-safe dry-run policy check, inspect an external agent run, or materialize an approved exploration as editable workspace.flow.js DSL without running or publishing it.
4
+ ---
5
+
6
+ # AgentFlow AI Exploration
7
+
8
+ Use the bundled browser-authorized CLI to turn an AI plan or live agent run into an auditable AgentFlow Trace. Resolve `<skill-dir>` as the directory containing this file:
9
+
10
+ ```bash
11
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs <command> [options]
12
+ ```
13
+
14
+ Do not configure MCP or install npm dependencies.
15
+
16
+ ## Authorize
17
+
18
+ Check configuration first:
19
+
20
+ ```bash
21
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs config
22
+ ```
23
+
24
+ If `hasToken` is false, start browser authorization and return the `verificationUrl` to the user. Never ask them to paste a Token:
25
+
26
+ ```bash
27
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs auth start
28
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs auth complete
29
+ ```
30
+
31
+ The CLI shares the owner-only `~/.agentflow/auth.json` profile with `agentflow-cli`. Never print credentials.
32
+
33
+ ## Choose the workflow
34
+
35
+ - For “先给我计划 / dry-run / 提前看看怎么执行”, generate a server Plan, inspect it, optionally run the policy check, and stop. Do not execute the task.
36
+ - For “把这次 Codex/Agent 执行可视化”, create an observed Session before work, append meaningful events as actions occur, and finish the Session at the end.
37
+ - For “把探索结果变成流程”, inspect the Trace, review side effects, then materialize it into the current Workspace adjustment state. Do not publish, run, or enable schedules.
38
+ - For read-only review, use `list` and `get`; do not create or append anything.
39
+
40
+ ## Generate an expected graph
41
+
42
+ Call the read-only Plan endpoint:
43
+
44
+ ```bash
45
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs plan \
46
+ --goal "分析失败任务并生成修复步骤" \
47
+ --flow-id <flow-id>
48
+ ```
49
+
50
+ Read every planned event before reporting. Distinguish `none/read` steps from `write/external` steps and call out `requiresApproval` entries. Plan means expected behavior, never completed behavior.
51
+
52
+ To check policy without running tools:
53
+
54
+ ```bash
55
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs dry-run \
56
+ --id <session-id> \
57
+ --flow-id <flow-id>
58
+ ```
59
+
60
+ This dry-run only classifies and blocks side effects. Always say `executedTools: false`; never describe it as a sandbox execution.
61
+
62
+ ## Trace an external agent run
63
+
64
+ Create one Session before acting:
65
+
66
+ ```bash
67
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs create \
68
+ --title "Codex 修复探索" \
69
+ --goal "定位失败并形成可复用流程" \
70
+ --flow-id <flow-id>
71
+ ```
72
+
73
+ Keep the returned `exploration.id`. Append one event per meaningful decision, tool call, file change, command, or artifact. Prefer `--file` for reliable JSON:
74
+
75
+ ```bash
76
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs append \
77
+ --id <session-id> \
78
+ --flow-id <flow-id> \
79
+ --phase observed \
80
+ --file /absolute/path/to/events.json
81
+ ```
82
+
83
+ Use stable `spanId` and `parentSpanId` values. Mark events `running` until their outcome is known, then append the completion/error event. Do not claim success at tool start. Classify side effects conservatively:
84
+
85
+ - `none`: reasoning or local decision only
86
+ - `read`: local search, inspection, or read-only query
87
+ - `write`: files, git state, configuration, or local mutation
88
+ - `external`: HTTP writes, messages, publishing, deployment, or remote mutation
89
+
90
+ Do not emit hidden reasoning, every streamed token, complete secret-bearing inputs, or noisy low-level status. The server redacts common secrets, but omit them before upload.
91
+
92
+ Always close the Session, including failure paths:
93
+
94
+ ```bash
95
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs finish \
96
+ --id <session-id> \
97
+ --flow-id <flow-id> \
98
+ --status completed \
99
+ --summary "完成定位并生成修复建议"
100
+ ```
101
+
102
+ Use `--status failed` with a concise error summary when execution fails.
103
+
104
+ ## Materialize reviewed Trace
105
+
106
+ First read the complete Session:
107
+
108
+ ```bash
109
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs get --id <session-id> --flow-id <flow-id>
110
+ ```
111
+
112
+ If materializable events include `write`, `external`, or `requiresApproval: true`, show their names and effects to the user. Pass `--approve-side-effects` only after the user explicitly approves those listed effects:
113
+
114
+ ```bash
115
+ node <skill-dir>/scripts/agentflow-ai-exploration.mjs materialize \
116
+ --id <session-id> \
117
+ --flow-id <flow-id> \
118
+ --approve-side-effects
119
+ ```
120
+
121
+ Materialization edits the Workspace DSL adjustment state and records provenance. It does not execute, publish, replace a stable release, or activate Scheduled Run. Use `agentflow-author-flow` afterward when the user asks to test and publish the generated graph.
122
+
123
+ ## Target the right Workspace
124
+
125
+ Omit `--flow-id` only when operating the server's current Workspace root. For stored flows, pass the exact `--flow-id` and `--flow-source`. Do not guess an owner or target. Admin read scope may use `--admin-owner-id`, but write endpoints still enforce Workspace permissions.
126
+
127
+ Read [references/protocol.md](references/protocol.md) when constructing custom events, integrating another Agent SDK, or diagnosing API/CLI errors.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "AgentFlow AI Exploration"
3
+ short_description: "Plan, trace, review, and materialize AI runs"
4
+ default_prompt: "Use $agentflow-ai-exploration to plan this task, stream its execution trace, and materialize the reviewed result as Workspace DSL."
@@ -0,0 +1,120 @@
1
+ # AI Exploration protocol
2
+
3
+ ## Commands
4
+
5
+ All commands return JSON:
6
+
7
+ ```text
8
+ config
9
+ auth start | complete | status | logout
10
+ list [workspace scope]
11
+ get --id <session-id> [workspace scope]
12
+ create [--title] [--goal] [--mode observed|planned] [workspace scope]
13
+ plan --goal <text> [--model <key>] [workspace scope]
14
+ append --id <session-id> (--file <json> | --event <json> | --stdin) [--phase <phase>] [workspace scope]
15
+ finish --id <session-id> [--status completed|failed] [--summary <text>] [workspace scope]
16
+ dry-run --id <session-id> [workspace scope]
17
+ materialize --id <session-id> [--model <key>] [--approve-side-effects] [workspace scope]
18
+ ```
19
+
20
+ Workspace scope options are `--flow-id`, `--flow-source`, `--admin-owner-id`, and `--archived`.
21
+
22
+ Configuration order:
23
+
24
+ 1. `--base-url`, `--token`
25
+ 2. `AGENTFLOW_BASE_URL`, `AGENTFLOW_TOKEN`, `AGENTFLOW_SESSION_TOKEN`
26
+ 3. `AGENTFLOW_ENV_FILE`, `.env`, `.agentflow.env`, `~/.agentflow.env`
27
+ 4. `~/.agentflow/auth.json`
28
+
29
+ Default server: `http://ai.mengma.bigo.inner/`.
30
+
31
+ ## Trace event
32
+
33
+ An append file may be one event, an event array, or `{ "events": [...] }`:
34
+
35
+ ```json
36
+ {
37
+ "id": "read_log_complete",
38
+ "traceId": "",
39
+ "spanId": "read_log",
40
+ "parentSpanId": "turn_1",
41
+ "type": "tool",
42
+ "name": "Read failure log",
43
+ "summary": "Found a timeout in the delivery request",
44
+ "status": "success",
45
+ "sideEffect": "read",
46
+ "requiresApproval": false,
47
+ "startedAt": "2026-08-25T10:00:00.000Z",
48
+ "endedAt": "2026-08-25T10:00:01.000Z",
49
+ "inputPreview": "run-id: workspace_123",
50
+ "outputPreview": "request timeout after 60s",
51
+ "artifacts": [
52
+ { "kind": "log", "path": "logs/failure.txt", "label": "Failure log" }
53
+ ]
54
+ }
55
+ ```
56
+
57
+ Allowed values:
58
+
59
+ - `phase`: `planned`, `simulated`, `observed`, `materialized`
60
+ - `type`: `run`, `turn`, `decision`, `agent`, `tool`, `command`, `file`, `artifact`, `status`
61
+ - `status`: `planned`, `running`, `success`, `error`, `blocked`, `skipped`
62
+ - `sideEffect`: `none`, `read`, `write`, `external`
63
+
64
+ The server assigns `traceId`, sequence, and timestamps when omitted. It limits a Session to 5000 events and truncates previews. `write` and `external` automatically imply approval.
65
+
66
+ For a long operation, reuse the same `spanId` across start and terminal events while giving each event a unique `id`:
67
+
68
+ ```json
69
+ [
70
+ {
71
+ "id": "command_1_start",
72
+ "spanId": "command_1",
73
+ "type": "command",
74
+ "name": "Run tests",
75
+ "status": "running",
76
+ "sideEffect": "read"
77
+ },
78
+ {
79
+ "id": "command_1_finish",
80
+ "spanId": "command_1",
81
+ "type": "command",
82
+ "name": "Run tests",
83
+ "summary": "256 tests passed",
84
+ "status": "success",
85
+ "sideEffect": "read"
86
+ }
87
+ ]
88
+ ```
89
+
90
+ ## HTTP mapping
91
+
92
+ The bundled CLI maps to these authenticated endpoints:
93
+
94
+ | Operation | Endpoint |
95
+ | --- | --- |
96
+ | list | `GET /api/workspace/explorations` |
97
+ | get | `GET /api/workspace/exploration` |
98
+ | create | `POST /api/workspace/exploration` |
99
+ | append / finish | `POST /api/workspace/exploration/events` |
100
+ | plan | `POST /api/workspace/exploration/plan` |
101
+ | dry-run | `POST /api/workspace/exploration/dry-run` |
102
+ | materialize | `POST /api/workspace/exploration/materialize` |
103
+
104
+ Use the CLI unless integrating a runtime that cannot launch Node. Direct HTTP clients must send the same Bearer Token and Workspace scope fields.
105
+
106
+ ## Semantics
107
+
108
+ - Plan uses a read-only agent configuration where the selected backend supports it and returns expected spans only.
109
+ - Dry-run is a deterministic side-effect policy check. It does not call tools.
110
+ - Observed Trace records what actually happened; it must not rewrite planned events.
111
+ - Materialization prefers planned spans when present, otherwise observed spans. Simulated events are never converted into DSL nodes.
112
+ - Materialization writes the editable adjustment state only. Stable release, execution, publication, and schedule activation remain separate operations.
113
+
114
+ ## Error handling
115
+
116
+ - `401/403`: reauthorize or verify Workspace ownership; never retry with a copied credential.
117
+ - `404`: verify Session ID and the exact Workspace scope used to create it.
118
+ - `409` from materialize: inspect the returned side effects, ask for explicit approval, then rerun with `--approve-side-effects` only if approved.
119
+ - Failed Plan: inspect the returned `explorationId`; the failed Session remains available for audit.
120
+ - Failed materialization: the Session is marked failed and retains the observed error event. Do not publish the partial DSL.
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import {
6
+ authFile,
7
+ clearPending,
8
+ clearProfile,
9
+ savePending,
10
+ saveProfile,
11
+ savedPending,
12
+ savedProfile,
13
+ } from "./auth-store.mjs";
14
+
15
+ const DEFAULT_BASE_URL = "http://ai.mengma.bigo.inner/";
16
+
17
+ function usage() {
18
+ return `AgentFlow AI Exploration CLI
19
+
20
+ Usage:
21
+ agentflow-ai-exploration <command> [options]
22
+
23
+ Commands:
24
+ config
25
+ auth start | complete | status | logout
26
+ list [--flow-id <id>] [--flow-source user]
27
+ get --id <session-id> [--flow-id <id>]
28
+ create [--title <text>] [--goal <text>] [--mode observed|planned]
29
+ plan --goal <text> [--model <key>]
30
+ append --id <session-id> (--file <events.json> | --event <json> | --stdin) [--phase observed|planned]
31
+ finish --id <session-id> [--status completed|failed] [--summary <text>]
32
+ dry-run --id <session-id>
33
+ materialize --id <session-id> [--model <key>] [--approve-side-effects]
34
+
35
+ Shared options:
36
+ --flow-id <id> Target a stored Flow Workspace instead of the current Workspace
37
+ --flow-source <source> user|workspace (default: user)
38
+ --admin-owner-id <id> Admin review scope
39
+ --archived Target an archived Flow
40
+ --base-url <url> Override AGENTFLOW_BASE_URL
41
+ --token <token> Override saved browser authorization
42
+ `;
43
+ }
44
+
45
+ function parseArgv(argv) {
46
+ const output = { _: [] };
47
+ for (let index = 0; index < argv.length; index += 1) {
48
+ const item = argv[index];
49
+ if (!item.startsWith("--")) {
50
+ output._.push(item);
51
+ continue;
52
+ }
53
+ const equal = item.indexOf("=");
54
+ let name = item.slice(2);
55
+ let value = true;
56
+ if (equal >= 0) {
57
+ name = item.slice(2, equal);
58
+ value = item.slice(equal + 1);
59
+ } else if (argv[index + 1] && !argv[index + 1].startsWith("--")) {
60
+ value = argv[index + 1];
61
+ index += 1;
62
+ }
63
+ output[name] = value;
64
+ }
65
+ return output;
66
+ }
67
+
68
+ function option(args, name, fallback = "") {
69
+ const value = args[name];
70
+ return value === undefined || value === null || value === true ? fallback : String(value);
71
+ }
72
+
73
+ function loadDotenvFile(file) {
74
+ if (!file || !fs.existsSync(file)) return;
75
+ for (const line of fs.readFileSync(file, "utf8").split(/\r?\n/)) {
76
+ const match = line.trim().match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
77
+ if (!match || process.env[match[1]] !== undefined) continue;
78
+ let value = match[2].trim();
79
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) value = value.slice(1, -1);
80
+ process.env[match[1]] = value.replace(/\\n/g, "\n");
81
+ }
82
+ }
83
+
84
+ function loadEnvironment() {
85
+ for (const candidate of [
86
+ process.env.AGENTFLOW_ENV_FILE,
87
+ path.join(process.cwd(), ".env"),
88
+ path.join(process.cwd(), ".agentflow.env"),
89
+ path.join(os.homedir(), ".agentflow.env"),
90
+ ].filter(Boolean)) loadDotenvFile(path.resolve(candidate));
91
+ }
92
+
93
+ function baseUrl(args) {
94
+ return String(option(args, "base-url") || process.env.AGENTFLOW_BASE_URL || DEFAULT_BASE_URL).replace(/\/+$/, "");
95
+ }
96
+
97
+ function resolvedAuth(args) {
98
+ const direct = option(args, "token");
99
+ if (direct) return { token: direct, source: "flag", profile: null };
100
+ if (String(process.env.AGENTFLOW_TOKEN || "").trim()) return { token: String(process.env.AGENTFLOW_TOKEN).trim(), source: "AGENTFLOW_TOKEN", profile: null };
101
+ if (String(process.env.AGENTFLOW_SESSION_TOKEN || "").trim()) return { token: String(process.env.AGENTFLOW_SESSION_TOKEN).trim(), source: "AGENTFLOW_SESSION_TOKEN", profile: null };
102
+ const profile = savedProfile(baseUrl(args));
103
+ return profile?.token ? { token: profile.token, source: "saved-auth", profile } : { token: "", source: "", profile: null };
104
+ }
105
+
106
+ function token(args, required = true) {
107
+ const value = String(resolvedAuth(args).token || "").trim();
108
+ if (required && !value) throw new Error("AgentFlow authorization is missing. Run `auth start`, approve the returned URL, then run `auth complete`.");
109
+ return value;
110
+ }
111
+
112
+ async function httpJson(args, pathname, { method = "GET", body, tokenRequired = true } = {}) {
113
+ const authToken = token(args, tokenRequired);
114
+ const url = new URL(pathname, `${baseUrl(args)}/`);
115
+ const headers = { Accept: "application/json" };
116
+ if (body !== undefined) headers["Content-Type"] = "application/json";
117
+ if (authToken) {
118
+ headers.Authorization = `Bearer ${authToken}`;
119
+ headers.Cookie = `af_session=${encodeURIComponent(authToken)}`;
120
+ }
121
+ const response = await fetch(url, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) });
122
+ const text = await response.text();
123
+ let data;
124
+ try { data = text ? JSON.parse(text) : null; } catch { data = { text }; }
125
+ if (!response.ok) {
126
+ const error = new Error(data?.error || data?.message || text || `HTTP ${response.status}`);
127
+ error.status = response.status;
128
+ error.response = data;
129
+ throw error;
130
+ }
131
+ return data;
132
+ }
133
+
134
+ function scope(args) {
135
+ return {
136
+ flowId: option(args, "flow-id"),
137
+ flowSource: option(args, "flow-source", "user") || "user",
138
+ ...(option(args, "admin-owner-id") ? { adminOwnerId: option(args, "admin-owner-id") } : {}),
139
+ ...(args.archived === true ? { archived: true } : {}),
140
+ };
141
+ }
142
+
143
+ function query(input) {
144
+ const params = new URLSearchParams();
145
+ for (const [name, value] of Object.entries(input)) {
146
+ if (value === "" || value === undefined || value === null || value === false) continue;
147
+ params.set(name, value === true ? "1" : String(value));
148
+ }
149
+ const output = params.toString();
150
+ return output ? `?${output}` : "";
151
+ }
152
+
153
+ function required(args, name) {
154
+ const value = option(args, name);
155
+ if (!value) throw new Error(`Missing --${name}.`);
156
+ return value;
157
+ }
158
+
159
+ function readJsonInput(args) {
160
+ let source = "";
161
+ if (option(args, "file")) source = fs.readFileSync(path.resolve(option(args, "file")), "utf8");
162
+ else if (option(args, "event")) source = option(args, "event");
163
+ else if (args.stdin === true) source = fs.readFileSync(0, "utf8");
164
+ else throw new Error("append requires --file <events.json>, --event <json>, or --stdin.");
165
+ let parsed;
166
+ try { parsed = JSON.parse(source); } catch (error) { throw new Error(`Invalid Trace JSON: ${error.message}`); }
167
+ if (Array.isArray(parsed)) return parsed;
168
+ if (Array.isArray(parsed?.events)) return parsed.events;
169
+ if (parsed && typeof parsed === "object") return [parsed];
170
+ throw new Error("Trace JSON must be an event object, an event array, or an object with events[].");
171
+ }
172
+
173
+ function print(value) {
174
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
175
+ }
176
+
177
+ async function runAuth(action, args) {
178
+ const target = baseUrl(args);
179
+ if (action === "start" || action === "login") {
180
+ const result = await httpJson(args, "/api/auth/cli/device", {
181
+ method: "POST",
182
+ tokenRequired: false,
183
+ body: { clientName: "AgentFlow AI Exploration" },
184
+ });
185
+ const pending = savePending(target, result);
186
+ print({ status: "authorization_required", baseUrl: target, requestId: pending.requestId, userCode: pending.userCode, verificationUrl: pending.verificationUrl, expiresAt: pending.expiresAt });
187
+ return;
188
+ }
189
+ if (action === "complete") {
190
+ const pending = savedPending(target);
191
+ if (!pending?.deviceCode) throw new Error("No pending authorization. Run `auth start` first.");
192
+ let result;
193
+ try {
194
+ result = await httpJson(args, "/api/auth/cli/token", { method: "POST", tokenRequired: false, body: { deviceCode: pending.deviceCode } });
195
+ } catch (error) {
196
+ if (["access_denied", "expired_token", "invalid_grant"].includes(String(error?.response?.code || ""))) clearPending(target);
197
+ throw error;
198
+ }
199
+ if (result?.code === "authorization_pending") {
200
+ print({ status: "authorization_pending", baseUrl: target, verificationUrl: pending.verificationUrl, expiresAt: pending.expiresAt });
201
+ process.exitCode = 2;
202
+ return;
203
+ }
204
+ if (!result?.token) throw new Error("Authorization exchange did not return a token.");
205
+ const saved = saveProfile(target, result);
206
+ print({ status: "authenticated", baseUrl: target, user: result.user || null, scopes: result.scopes || [], expiresAt: result.expiresAt || 0, credentialFile: saved.file });
207
+ return;
208
+ }
209
+ if (action === "status") {
210
+ const auth = resolvedAuth(args);
211
+ if (!auth.token) {
212
+ print({ authenticated: false, baseUrl: target, credentialFile: authFile() });
213
+ return;
214
+ }
215
+ const me = await httpJson(args, "/api/auth/me", { tokenRequired: false });
216
+ print({ authenticated: Boolean(me?.authenticated), baseUrl: target, tokenSource: auth.source, user: me?.user || null, credentialFile: auth.source === "saved-auth" ? authFile() : "" });
217
+ return;
218
+ }
219
+ if (action === "logout") {
220
+ const auth = resolvedAuth(args);
221
+ let revoked = false;
222
+ if (auth.token) {
223
+ try { revoked = Boolean((await httpJson(args, "/api/auth/cli/revoke", { method: "POST", body: {} }))?.ok); } catch {}
224
+ }
225
+ print({ authenticated: false, baseUrl: target, revoked, localCleared: clearProfile(target), tokenSource: auth.source });
226
+ return;
227
+ }
228
+ throw new Error("Unknown auth action. Use start, complete, status, or logout.");
229
+ }
230
+
231
+ async function main() {
232
+ loadEnvironment();
233
+ const args = parseArgv(process.argv.slice(2));
234
+ const command = String(args._[0] || "help").toLowerCase();
235
+ if (["help", "--help", "-h"].includes(command)) {
236
+ process.stdout.write(usage());
237
+ return;
238
+ }
239
+ if (command === "auth") {
240
+ await runAuth(String(args._[1] || "status").toLowerCase(), args);
241
+ return;
242
+ }
243
+ if (command === "config") {
244
+ const auth = resolvedAuth(args);
245
+ print({ baseUrl: baseUrl(args), hasToken: Boolean(auth.token), tokenSource: auth.source, credentialFile: auth.source === "saved-auth" ? authFile() : "" });
246
+ return;
247
+ }
248
+ if (command === "list") {
249
+ print(await httpJson(args, `/api/workspace/explorations${query(scope(args))}`));
250
+ return;
251
+ }
252
+ if (command === "get") {
253
+ print(await httpJson(args, `/api/workspace/exploration${query({ ...scope(args), id: required(args, "id") })}`));
254
+ return;
255
+ }
256
+ if (command === "create") {
257
+ print(await httpJson(args, "/api/workspace/exploration", {
258
+ method: "POST",
259
+ body: {
260
+ ...scope(args),
261
+ title: option(args, "title") || option(args, "goal") || "External Agent run",
262
+ goal: option(args, "goal"),
263
+ mode: option(args, "mode", "observed") || "observed",
264
+ status: option(args, "status", "running") || "running",
265
+ source: { provider: option(args, "provider", "external") || "external", agent: option(args, "agent", "Codex / Agent SDK") || "Codex / Agent SDK" },
266
+ },
267
+ }));
268
+ return;
269
+ }
270
+ if (command === "plan") {
271
+ print(await httpJson(args, "/api/workspace/exploration/plan", {
272
+ method: "POST",
273
+ body: { ...scope(args), goal: required(args, "goal"), model: option(args, "model") },
274
+ }));
275
+ return;
276
+ }
277
+ if (command === "append") {
278
+ print(await httpJson(args, "/api/workspace/exploration/events", {
279
+ method: "POST",
280
+ body: { ...scope(args), id: required(args, "id"), phase: option(args, "phase", "observed") || "observed", events: readJsonInput(args) },
281
+ }));
282
+ return;
283
+ }
284
+ if (command === "finish") {
285
+ print(await httpJson(args, "/api/workspace/exploration/events", {
286
+ method: "POST",
287
+ body: { ...scope(args), id: required(args, "id"), events: [], status: option(args, "status", "completed") || "completed", summary: option(args, "summary") },
288
+ }));
289
+ return;
290
+ }
291
+ if (command === "dry-run") {
292
+ print(await httpJson(args, "/api/workspace/exploration/dry-run", { method: "POST", body: { ...scope(args), id: required(args, "id") } }));
293
+ return;
294
+ }
295
+ if (command === "materialize") {
296
+ print(await httpJson(args, "/api/workspace/exploration/materialize", {
297
+ method: "POST",
298
+ body: { ...scope(args), id: required(args, "id"), model: option(args, "model"), approveSideEffects: args["approve-side-effects"] === true },
299
+ }));
300
+ return;
301
+ }
302
+ throw new Error(`Unknown command: ${command}`);
303
+ }
304
+
305
+ main().catch((error) => {
306
+ process.stderr.write(`${JSON.stringify({ ok: false, error: error?.message || String(error), status: error?.status || 0, response: error?.response || null }, null, 2)}\n`);
307
+ process.exitCode = 1;
308
+ });