@fieldwangai/agentflow 0.1.91 → 0.1.93

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.
@@ -15,8 +15,8 @@
15
15
  href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0"
16
16
  rel="stylesheet"
17
17
  />
18
- <script type="module" crossorigin src="/assets/index-B8ebrA1Z.js"></script>
19
- <link rel="stylesheet" crossorigin href="/assets/index-CWJ7Mp9B.css">
18
+ <script type="module" crossorigin src="/assets/index-Dtmpup-L.js"></script>
19
+ <link rel="stylesheet" crossorigin href="/assets/index-DV-QkHHi.css">
20
20
  </head>
21
21
  <body>
22
22
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fieldwangai/agentflow",
3
- "version": "0.1.91",
3
+ "version": "0.1.93",
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",
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: agentflow-cli
3
+ description: Direct AgentFlow platform operation through a bundled token-backed CLI, without MCP. Use when Codex needs to list AgentFlow workspaces or flows, start a flow run, inspect run status/logs, or fetch display outputs through AgentFlow HTTP APIs using AGENTFLOW_TOKEN from env or .env. Default AgentFlow base URL is http://ai.mengma.bigo.inner/.
4
+ ---
5
+
6
+ # AgentFlow CLI
7
+
8
+ Use this skill when the task is to operate AgentFlow itself from an AI agent. Do not configure or call MCP for this skill. Use the bundled CLI script instead:
9
+
10
+ ```bash
11
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs <command> [options]
12
+ ```
13
+
14
+ If the skill is installed outside this repository, resolve the script path relative to this `SKILL.md`.
15
+
16
+ ## Installation
17
+
18
+ Install from SkillHub before using this skill in another agent environment:
19
+
20
+ ```bash
21
+ skillhub install agentflow-cli --global --agent codex
22
+ ```
23
+
24
+ For a project-local install:
25
+
26
+ ```bash
27
+ skillhub install agentflow-cli --dir .agents/skills
28
+ ```
29
+
30
+ For other SkillHub-supported agents, change the agent name:
31
+
32
+ ```bash
33
+ skillhub install agentflow-cli --global --agent claude-code
34
+ ```
35
+
36
+ After installation, configure only the direct API token. Do not add an MCP server for this skill.
37
+
38
+ ## Configuration
39
+
40
+ The CLI reads configuration in this order:
41
+
42
+ 1. CLI flags: `--base-url`, `--token`
43
+ 2. Environment variables: `AGENTFLOW_BASE_URL`, `AGENTFLOW_TOKEN`, `AGENTFLOW_SESSION_TOKEN`
44
+ 3. Env files: `AGENTFLOW_ENV_FILE`, then `.env`, `.agentflow.env`, then `~/.agentflow.env`
45
+
46
+ Default base URL: `http://ai.mengma.bigo.inner/`.
47
+
48
+ Required token: `AGENTFLOW_TOKEN` or `AGENTFLOW_SESSION_TOKEN`. Never print the token in the final answer or logs.
49
+
50
+ Example `.env`:
51
+
52
+ ```dotenv
53
+ AGENTFLOW_TOKEN=replace-with-token
54
+ # Optional for local debug only:
55
+ # AGENTFLOW_BASE_URL=http://127.0.0.1:8875
56
+ ```
57
+
58
+ ## Commands
59
+
60
+ Prefer JSON output and let the CLI handle auth headers.
61
+
62
+ List knowledge/workspace entries:
63
+
64
+ ```bash
65
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs list-workspace
66
+ ```
67
+
68
+ List flows:
69
+
70
+ ```bash
71
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs list-flows
72
+ ```
73
+
74
+ Read one flow graph:
75
+
76
+ ```bash
77
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs get-graph --flow-id TestNodes --flow-source user
78
+ ```
79
+
80
+ Run a flow:
81
+
82
+ ```bash
83
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs run --flow-id TestNodes --flow-source user
84
+ ```
85
+
86
+ Run a specific run node with inputs:
87
+
88
+ ```bash
89
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs run \
90
+ --flow-id TestNodes \
91
+ --flow-source user \
92
+ --run-node-id workspace_run_1 \
93
+ --input topic=hello \
94
+ --input date=today
95
+ ```
96
+
97
+ List runs for a flow or workspace alias:
98
+
99
+ ```bash
100
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs list-run-by-workspace --workspace TestNodes --limit 20
101
+ ```
102
+
103
+ Read run logs:
104
+
105
+ ```bash
106
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs logs --run-id workspace-123
107
+ ```
108
+
109
+ Get active run status:
110
+
111
+ ```bash
112
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs status --flow-id TestNodes --flow-source user
113
+ ```
114
+
115
+ Extract display outputs from a flow:
116
+
117
+ ```bash
118
+ node skills/agentflow-cli/scripts/agentflow-cli.mjs display-outputs --flow-id TestNodes --flow-source user
119
+ ```
120
+
121
+ ## Workflow
122
+
123
+ 1. Check token availability with `config`.
124
+ 2. Use `list-workspace` or `list-flows` to discover targets.
125
+ 3. Use `run` to start the flow. If the task needs the generated page/text, inspect returned `displayOutputs` or call `display-outputs`.
126
+ 4. Use `status`, `list-run-by-workspace`, and `logs` when a run is active, failed, or needs debugging.
127
+
128
+ ## Failure Handling
129
+
130
+ - If the CLI says the token is missing, ask the user to set `AGENTFLOW_TOKEN` in env or `.env`.
131
+ - If the API returns 401/403, do not retry with a printed token. Ask the user to refresh the token.
132
+ - If `run` fails because a flow is already running, call `status` and `list-run-by-workspace` before retrying.
133
+ - If local debugging is needed, override `AGENTFLOW_BASE_URL`; otherwise keep the default internal URL.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "AgentFlow CLI"
3
+ short_description: "Directly call AgentFlow APIs with a token-backed CLI."
4
+ default_prompt: "Use the bundled AgentFlow CLI to list workspaces or flows, run a flow, inspect run logs, and fetch display outputs. Use AGENTFLOW_TOKEN from env or .env and default the base URL to the internal AgentFlow service."
@@ -0,0 +1,341 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+
6
+ const DEFAULT_BASE_URL = "http://ai.mengma.bigo.inner/";
7
+ const DISPLAY_DEFINITION_KINDS = new Map([
8
+ ["display_markdown", "markdown"],
9
+ ["display_mermaid", "mermaid"],
10
+ ["display_ascii", "ascii"],
11
+ ["display_html", "html"],
12
+ ["display_react_app", "react"],
13
+ ["display_image", "image"],
14
+ ["display_chart", "chart"],
15
+ ["display_table", "table"],
16
+ ]);
17
+
18
+ function usage() {
19
+ return `AgentFlow direct API CLI
20
+
21
+ Usage:
22
+ agentflow-cli <command> [options]
23
+
24
+ Config:
25
+ --base-url <url> Override AGENTFLOW_BASE_URL
26
+ --token <token> Override AGENTFLOW_TOKEN
27
+ AGENTFLOW_BASE_URL Defaults to ${DEFAULT_BASE_URL}
28
+ AGENTFLOW_TOKEN Required unless AGENTFLOW_SESSION_TOKEN is set
29
+ AGENTFLOW_ENV_FILE Optional dotenv file path
30
+
31
+ Commands:
32
+ config
33
+ list-workspace | list-workspaces
34
+ list-flows
35
+ get-graph --flow-id <id> [--flow-source user]
36
+ run --flow-id <id> [--flow-source user] [--run-node-id <id>] [--input k=v]
37
+ status --flow-id <id> [--flow-source user]
38
+ list-run-by-workspace | list-runs-by-workspace --workspace <flowId> [--limit 20]
39
+ list-runs [--flow-id <id>] [--flow-source user] [--limit 20]
40
+ logs --run-id <id>
41
+ display-outputs --flow-id <id> [--flow-source user]
42
+ sync-workspace --workspace <id>
43
+ `;
44
+ }
45
+
46
+ function parseDotenvValue(raw) {
47
+ let value = String(raw ?? "").trim();
48
+ if (!value) return "";
49
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
50
+ value = value.slice(1, -1);
51
+ }
52
+ return value.replace(/\\n/g, "\n");
53
+ }
54
+
55
+ function loadEnvFile(filePath) {
56
+ if (!filePath || !fs.existsSync(filePath)) return;
57
+ const content = fs.readFileSync(filePath, "utf8");
58
+ for (const line of content.split(/\r?\n/)) {
59
+ const trimmed = line.trim();
60
+ if (!trimmed || trimmed.startsWith("#")) continue;
61
+ const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
62
+ if (!match) continue;
63
+ const key = match[1];
64
+ if (process.env[key] !== undefined) continue;
65
+ process.env[key] = parseDotenvValue(match[2]);
66
+ }
67
+ }
68
+
69
+ function loadEnvFiles() {
70
+ const cwd = process.cwd();
71
+ const candidates = [
72
+ process.env.AGENTFLOW_ENV_FILE,
73
+ path.join(cwd, ".env"),
74
+ path.join(cwd, ".agentflow.env"),
75
+ path.join(os.homedir(), ".agentflow.env"),
76
+ ].filter(Boolean);
77
+ for (const candidate of candidates) {
78
+ loadEnvFile(path.resolve(candidate));
79
+ }
80
+ }
81
+
82
+ function parseArgv(argv) {
83
+ const out = { _: [] };
84
+ for (let i = 0; i < argv.length; i += 1) {
85
+ const item = argv[i];
86
+ if (!item.startsWith("--")) {
87
+ out._.push(item);
88
+ continue;
89
+ }
90
+ const eq = item.indexOf("=");
91
+ let key = item.slice(2);
92
+ let value = true;
93
+ if (eq >= 0) {
94
+ key = item.slice(2, eq);
95
+ value = item.slice(eq + 1);
96
+ } else if (argv[i + 1] && !argv[i + 1].startsWith("--")) {
97
+ value = argv[i + 1];
98
+ i += 1;
99
+ }
100
+ if (out[key] !== undefined) {
101
+ if (!Array.isArray(out[key])) out[key] = [out[key]];
102
+ out[key].push(value);
103
+ } else {
104
+ out[key] = value;
105
+ }
106
+ }
107
+ return out;
108
+ }
109
+
110
+ function option(args, name, fallback = "") {
111
+ const value = args[name];
112
+ if (Array.isArray(value)) return value[value.length - 1] ?? fallback;
113
+ if (value === true || value === undefined || value === null || value === "") return fallback;
114
+ return String(value);
115
+ }
116
+
117
+ function normalizedBaseUrl(args) {
118
+ const raw = option(args, "base-url") || process.env.AGENTFLOW_BASE_URL || DEFAULT_BASE_URL;
119
+ return String(raw).replace(/\/+$/, "");
120
+ }
121
+
122
+ function authToken(args, required = true) {
123
+ const token = option(args, "token") || process.env.AGENTFLOW_TOKEN || process.env.AGENTFLOW_SESSION_TOKEN || "";
124
+ const trimmed = String(token).trim();
125
+ if (required && !trimmed) {
126
+ throw new Error("Missing AGENTFLOW_TOKEN. Set it in env, .env, .agentflow.env, or pass --token.");
127
+ }
128
+ return trimmed;
129
+ }
130
+
131
+ function query(params = {}) {
132
+ const search = new URLSearchParams();
133
+ for (const [key, value] of Object.entries(params)) {
134
+ if (value === undefined || value === null || value === "") continue;
135
+ search.set(key, String(value));
136
+ }
137
+ const text = search.toString();
138
+ return text ? `?${text}` : "";
139
+ }
140
+
141
+ async function httpJson(args, pathname, { method = "GET", body, tokenRequired = true } = {}) {
142
+ const token = authToken(args, tokenRequired);
143
+ const url = new URL(pathname, normalizedBaseUrl(args));
144
+ const headers = { Accept: "application/json" };
145
+ if (body !== undefined) headers["Content-Type"] = "application/json";
146
+ if (token) {
147
+ headers.Authorization = `Bearer ${token}`;
148
+ headers.Cookie = `af_session=${encodeURIComponent(token)}`;
149
+ }
150
+ const response = await fetch(url, {
151
+ method,
152
+ headers,
153
+ body: body === undefined ? undefined : JSON.stringify(body),
154
+ });
155
+ const text = await response.text();
156
+ let data = null;
157
+ try {
158
+ data = text ? JSON.parse(text) : null;
159
+ } catch {
160
+ data = { text };
161
+ }
162
+ if (!response.ok) {
163
+ const message = data?.error || data?.message || text || `HTTP ${response.status}`;
164
+ throw new Error(`${method} ${url.pathname} failed: ${message}`);
165
+ }
166
+ return data;
167
+ }
168
+
169
+ function asArray(value) {
170
+ if (value === undefined) return [];
171
+ return Array.isArray(value) ? value : [value];
172
+ }
173
+
174
+ function parseInputs(args) {
175
+ const inputs = {};
176
+ for (const item of asArray(args.input)) {
177
+ const text = String(item);
178
+ const index = text.indexOf("=");
179
+ if (index <= 0) {
180
+ throw new Error(`Invalid --input "${text}". Expected key=value.`);
181
+ }
182
+ inputs[text.slice(0, index)] = text.slice(index + 1);
183
+ }
184
+ return inputs;
185
+ }
186
+
187
+ function slotText(slots, names = []) {
188
+ const wanted = new Set(names.map(String));
189
+ for (const slot of Array.isArray(slots) ? slots : []) {
190
+ const name = String(slot?.name || "");
191
+ if (wanted.size && !wanted.has(name)) continue;
192
+ const value = slot?.value ?? slot?.default ?? "";
193
+ if (String(value || "").trim()) return String(value);
194
+ }
195
+ return "";
196
+ }
197
+
198
+ function extractDisplayOutputs(graph) {
199
+ const instances = graph?.instances && typeof graph.instances === "object" ? graph.instances : {};
200
+ const outputs = [];
201
+ for (const [nodeId, instance] of Object.entries(instances)) {
202
+ const kind = DISPLAY_DEFINITION_KINDS.get(String(instance?.definitionId || ""));
203
+ if (!kind) continue;
204
+ const primary = kind === "image" ? "src" : "content";
205
+ const content = String(instance?.body || "") ||
206
+ slotText(instance?.input, [primary, "content", "markdown", "html", "src"]) ||
207
+ slotText(instance?.output, [primary, "content", "markdown", "html", "src"]);
208
+ outputs.push({
209
+ nodeId,
210
+ label: String(instance?.label || instance?.displayName || nodeId),
211
+ definitionId: String(instance?.definitionId || ""),
212
+ kind,
213
+ content,
214
+ hasContent: Boolean(String(content || "").trim()),
215
+ });
216
+ }
217
+ return outputs;
218
+ }
219
+
220
+ function printJson(data) {
221
+ process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
222
+ }
223
+
224
+ function requireFlowId(args) {
225
+ const flowId = option(args, "flow-id") || option(args, "flow");
226
+ if (!flowId) throw new Error("Missing --flow-id.");
227
+ return flowId;
228
+ }
229
+
230
+ async function main() {
231
+ loadEnvFiles();
232
+ const args = parseArgv(process.argv.slice(2));
233
+ const command = args._[0] || "help";
234
+ if (command === "help" || command === "--help" || command === "-h") {
235
+ process.stdout.write(usage());
236
+ return;
237
+ }
238
+
239
+ if (command === "config") {
240
+ printJson({
241
+ baseUrl: normalizedBaseUrl(args),
242
+ hasToken: Boolean(authToken(args, false)),
243
+ tokenSource: option(args, "token") ? "flag" : process.env.AGENTFLOW_TOKEN ? "AGENTFLOW_TOKEN" : process.env.AGENTFLOW_SESSION_TOKEN ? "AGENTFLOW_SESSION_TOKEN" : "",
244
+ });
245
+ return;
246
+ }
247
+
248
+ if (command === "list-workspace" || command === "list-workspaces") {
249
+ printJson(await httpJson(args, "/api/workspaces"));
250
+ return;
251
+ }
252
+
253
+ if (command === "sync-workspace") {
254
+ const id = option(args, "workspace") || option(args, "id");
255
+ if (!id) throw new Error("Missing --workspace.");
256
+ printJson(await httpJson(args, "/api/workspaces/sync", { method: "POST", body: { id } }));
257
+ return;
258
+ }
259
+
260
+ if (command === "list-flows" || command === "list-flow") {
261
+ printJson(await httpJson(args, "/api/flows"));
262
+ return;
263
+ }
264
+
265
+ if (command === "get-graph") {
266
+ const flowId = requireFlowId(args);
267
+ const flowSource = option(args, "flow-source") || "user";
268
+ printJson(await httpJson(args, `/api/workspace/graph${query({ flowId, flowSource })}`));
269
+ return;
270
+ }
271
+
272
+ if (command === "run") {
273
+ const flowId = requireFlowId(args);
274
+ const flowSource = option(args, "flow-source") || "user";
275
+ const runNodeId = option(args, "run-node-id") || "";
276
+ const graphPayload = await httpJson(args, `/api/workspace/graph${query({ flowId, flowSource })}`);
277
+ const result = await httpJson(args, "/api/workspace/run", {
278
+ method: "POST",
279
+ body: {
280
+ flowId,
281
+ flowSource,
282
+ runNodeId,
283
+ runAlias: option(args, "run-alias") || "",
284
+ graph: graphPayload.graph,
285
+ inputs: parseInputs(args),
286
+ },
287
+ });
288
+ printJson({
289
+ ...result,
290
+ displayOutputs: extractDisplayOutputs(result?.graph),
291
+ });
292
+ return;
293
+ }
294
+
295
+ if (command === "status") {
296
+ const flowId = requireFlowId(args);
297
+ const flowSource = option(args, "flow-source") || "user";
298
+ printJson(await httpJson(args, `/api/workspace/run/status${query({ flowId, flowSource })}`));
299
+ return;
300
+ }
301
+
302
+ if (command === "list-run-by-workspace" || command === "list-runs-by-workspace") {
303
+ const workspace = option(args, "workspace") || option(args, "flow-id") || option(args, "flow");
304
+ const flowSource = option(args, "flow-source") || "user";
305
+ const limit = Number(option(args, "limit") || 20);
306
+ printJson(await httpJson(args, `/api/workspace/run-logs${query({ flowId: workspace, flowSource, limit })}`));
307
+ return;
308
+ }
309
+
310
+ if (command === "list-runs") {
311
+ const flowId = option(args, "flow-id") || "";
312
+ const flowSource = option(args, "flow-source") || "";
313
+ const scheduleNodeId = option(args, "schedule-node-id") || "";
314
+ const runNodeId = option(args, "run-node-id") || "";
315
+ const limit = Number(option(args, "limit") || 20);
316
+ printJson(await httpJson(args, `/api/workspace/run-logs${query({ flowId, flowSource, scheduleNodeId, runNodeId, limit })}`));
317
+ return;
318
+ }
319
+
320
+ if (command === "logs") {
321
+ const runId = option(args, "run-id");
322
+ if (!runId) throw new Error("Missing --run-id.");
323
+ printJson(await httpJson(args, `/api/workspace/run-logs/${encodeURIComponent(runId)}`));
324
+ return;
325
+ }
326
+
327
+ if (command === "display-outputs") {
328
+ const flowId = requireFlowId(args);
329
+ const flowSource = option(args, "flow-source") || "user";
330
+ const graphPayload = await httpJson(args, `/api/workspace/graph${query({ flowId, flowSource })}`);
331
+ printJson({ flowId, flowSource, displayOutputs: extractDisplayOutputs(graphPayload.graph) });
332
+ return;
333
+ }
334
+
335
+ throw new Error(`Unknown command: ${command}\n\n${usage()}`);
336
+ }
337
+
338
+ main().catch((error) => {
339
+ process.stderr.write(`${error?.message || String(error)}\n`);
340
+ process.exit(1);
341
+ });