@fieldwangai/agentflow 0.1.84 → 0.1.86
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.
- package/README.md +21 -5
- package/README.zh-CN.md +21 -5
- package/bin/agentflow.mjs +1 -1
- package/bin/lib/agent-runners.mjs +589 -0
- package/bin/lib/auth.mjs +11 -1
- package/bin/lib/catalog-flows.mjs +2 -0
- package/bin/lib/composer-agent.mjs +163 -1
- package/bin/lib/composer-model-router.mjs +28 -3
- package/bin/lib/composer-planner.mjs +3 -1
- package/bin/lib/help.mjs +16 -14
- package/bin/lib/locales/en.json +5 -1
- package/bin/lib/locales/zh.json +5 -1
- package/bin/lib/main.mjs +5 -0
- package/bin/lib/mcp-server.mjs +328 -0
- package/bin/lib/model-config.mjs +32 -3
- package/bin/lib/model-lists.mjs +68 -3
- package/bin/lib/node-execute.mjs +22 -1
- package/bin/lib/ui-server.mjs +1420 -73
- package/bin/lib/workspace-run-logs.mjs +181 -0
- package/bin/pipeline/validate-flow.mjs +19 -11
- package/bin/pipeline/validate-for-ui.mjs +19 -11
- package/builtin/nodes/display_react_app.md +52 -0
- package/builtin/web-ui/dist/assets/index-CSr7NGO4.js +322 -0
- package/builtin/web-ui/dist/assets/index-DIO0cfyb.css +1 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +3 -2
- package/builtin/web-ui/dist/assets/index-CVdxKCZm.css +0 -1
- package/builtin/web-ui/dist/assets/index-w1bX4CFG.js +0 -297
|
@@ -10,7 +10,7 @@ import path from "path";
|
|
|
10
10
|
import { getAgentflowDataRoot, getAgentflowUserDataRoot, sanitizeAgentflowUserId } from "./paths.mjs";
|
|
11
11
|
import { readMergedEnvObject } from "./user-env.mjs";
|
|
12
12
|
import { resolveCliAndModel } from "./model-config.mjs";
|
|
13
|
-
import { runClaudeCodeAgentWithPrompt, runCursorAgentWithPrompt, runOpenCodeAgentWithPrompt } from "./agent-runners.mjs";
|
|
13
|
+
import { runCodexAgentWithPrompt, runClaudeCodeAgentWithPrompt, runCursorAgentWithPrompt, runOpenCodeAgentWithPrompt } from "./agent-runners.mjs";
|
|
14
14
|
import { planComposerTasks, hasPlannerApiAvailable, shouldUsePhased, classifyComplexity, classifyTaskComplexity, PHASED_DEFINITIONS } from "./composer-planner.mjs";
|
|
15
15
|
import { executeScriptOp, isSupportedScriptOp } from "./composer-script-ops.mjs";
|
|
16
16
|
import { routeModel } from "./composer-model-router.mjs";
|
|
@@ -152,12 +152,149 @@ function materializeWorkspaceCursorMcpPrivateConfig(workspaceRoot, userId) {
|
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
function tomlString(value) {
|
|
156
|
+
return JSON.stringify(String(value ?? ""));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function tomlArray(values) {
|
|
160
|
+
return `[${(Array.isArray(values) ? values : []).map((value) => tomlString(value)).join(", ")}]`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function tomlInlineTable(obj) {
|
|
164
|
+
const entries = Object.entries(obj && typeof obj === "object" && !Array.isArray(obj) ? obj : {})
|
|
165
|
+
.filter(([key]) => String(key || "").trim())
|
|
166
|
+
.map(([key, value]) => `${tomlString(String(key).trim())} = ${tomlString(value)}`);
|
|
167
|
+
return `{ ${entries.join(", ")} }`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function codexMcpName(name, used) {
|
|
171
|
+
const base = String(name || "mcp")
|
|
172
|
+
.trim()
|
|
173
|
+
.replace(/[^A-Za-z0-9_-]+/g, "_")
|
|
174
|
+
.replace(/^_+|_+$/g, "") || "mcp";
|
|
175
|
+
let out = base;
|
|
176
|
+
let i = 2;
|
|
177
|
+
while (used.has(out)) out = `${base}_${i++}`;
|
|
178
|
+
used.add(out);
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function bearerFromHeaders(headers) {
|
|
183
|
+
const obj = headers && typeof headers === "object" && !Array.isArray(headers) ? headers : {};
|
|
184
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
185
|
+
if (String(key).toLowerCase() !== "authorization") continue;
|
|
186
|
+
const match = String(value ?? "").match(/^Bearer\s+(.+)$/i);
|
|
187
|
+
if (match?.[1]) return match[1].trim();
|
|
188
|
+
}
|
|
189
|
+
return "";
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function mergedCursorMcpServersForCodex(workspaceRoot, userId) {
|
|
193
|
+
const safe = sanitizeAgentflowUserId(userId);
|
|
194
|
+
const workspace = path.resolve(workspaceRoot || process.cwd());
|
|
195
|
+
const localServers = cursorMcpServersFromFile(path.join(workspace, ".cursor", "mcp.json"));
|
|
196
|
+
const globalServers = cursorMcpServersFromFile(path.join(os.homedir(), ".cursor", "mcp.json"));
|
|
197
|
+
const privateServers = safe ? readUserMcpPrivateServers(safe) : {};
|
|
198
|
+
const merged = { ...globalServers, ...localServers };
|
|
199
|
+
|
|
200
|
+
for (const [name, privateServer] of Object.entries(privateServers)) {
|
|
201
|
+
const current = merged[name];
|
|
202
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) continue;
|
|
203
|
+
const privateEnv = privateServer?.env && typeof privateServer.env === "object" && !Array.isArray(privateServer.env) ? privateServer.env : {};
|
|
204
|
+
const privateHeaders = privateServer?.headers && typeof privateServer.headers === "object" && !Array.isArray(privateServer.headers) ? privateServer.headers : {};
|
|
205
|
+
const currentEnv = current.env && typeof current.env === "object" && !Array.isArray(current.env) ? current.env : {};
|
|
206
|
+
const currentHeaders = current.headers && typeof current.headers === "object" && !Array.isArray(current.headers) ? current.headers : {};
|
|
207
|
+
merged[name] = {
|
|
208
|
+
...current,
|
|
209
|
+
...(Object.keys(privateEnv).length ? { env: { ...currentEnv, ...privateEnv } } : {}),
|
|
210
|
+
...(Object.keys(privateHeaders).length ? { headers: { ...currentHeaders, ...privateHeaders } } : {}),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return merged;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function codexMcpOverridesFromCursorConfig(workspaceRoot, userId) {
|
|
218
|
+
const safe = sanitizeAgentflowUserId(userId);
|
|
219
|
+
const privateServers = safe ? readUserMcpPrivateServers(safe) : {};
|
|
220
|
+
const servers = mergedCursorMcpServersForCodex(workspaceRoot, userId);
|
|
221
|
+
const used = new Set();
|
|
222
|
+
const codexConfigArgs = [];
|
|
223
|
+
const env = {};
|
|
224
|
+
|
|
225
|
+
for (const [rawName, rawServer] of Object.entries(servers)) {
|
|
226
|
+
if (!rawServer || typeof rawServer !== "object" || Array.isArray(rawServer)) continue;
|
|
227
|
+
if (rawServer.disabled === true) continue;
|
|
228
|
+
const name = codexMcpName(rawName, used);
|
|
229
|
+
const prefix = `mcp_servers.${name}`;
|
|
230
|
+
const envObj = rawServer.env && typeof rawServer.env === "object" && !Array.isArray(rawServer.env) ? rawServer.env : {};
|
|
231
|
+
const headers = rawServer.headers && typeof rawServer.headers === "object" && !Array.isArray(rawServer.headers) ? rawServer.headers : {};
|
|
232
|
+
const privateServer = privateServers[rawName];
|
|
233
|
+
const privateEnv = privateServer?.env && typeof privateServer.env === "object" && !Array.isArray(privateServer.env) ? privateServer.env : {};
|
|
234
|
+
const url = String(rawServer.url || "").trim();
|
|
235
|
+
const command = String(rawServer.command || "").trim();
|
|
236
|
+
|
|
237
|
+
if (url) {
|
|
238
|
+
codexConfigArgs.push(`${prefix}.url=${tomlString(url)}`);
|
|
239
|
+
if (rawServer.bearer_token_env_var && String(rawServer.bearer_token_env_var).trim()) {
|
|
240
|
+
codexConfigArgs.push(`${prefix}.bearer_token_env_var=${tomlString(rawServer.bearer_token_env_var)}`);
|
|
241
|
+
}
|
|
242
|
+
if (rawServer.oauth_client_id && String(rawServer.oauth_client_id).trim()) {
|
|
243
|
+
codexConfigArgs.push(`${prefix}.oauth_client_id=${tomlString(rawServer.oauth_client_id)}`);
|
|
244
|
+
}
|
|
245
|
+
if (rawServer.oauth_resource && String(rawServer.oauth_resource).trim()) {
|
|
246
|
+
codexConfigArgs.push(`${prefix}.oauth_resource=${tomlString(rawServer.oauth_resource)}`);
|
|
247
|
+
}
|
|
248
|
+
const bearer = bearerFromHeaders(headers);
|
|
249
|
+
if (bearer) {
|
|
250
|
+
const envKey = `AGENTFLOW_CODEX_MCP_${name.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_BEARER`;
|
|
251
|
+
env[envKey] = bearer;
|
|
252
|
+
codexConfigArgs.push(`${prefix}.bearer_token_env_var=${tomlString(envKey)}`);
|
|
253
|
+
}
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!command) continue;
|
|
258
|
+
codexConfigArgs.push(`${prefix}.command=${tomlString(command)}`);
|
|
259
|
+
if (Array.isArray(rawServer.args) && rawServer.args.length) {
|
|
260
|
+
codexConfigArgs.push(`${prefix}.args=${tomlArray(rawServer.args)}`);
|
|
261
|
+
}
|
|
262
|
+
if (rawServer.cwd && String(rawServer.cwd).trim()) {
|
|
263
|
+
codexConfigArgs.push(`${prefix}.cwd=${tomlString(rawServer.cwd)}`);
|
|
264
|
+
}
|
|
265
|
+
const publicEnvForConfig = {};
|
|
266
|
+
for (const [key, value] of Object.entries(envObj)) {
|
|
267
|
+
const envKey = String(key || "").trim();
|
|
268
|
+
if (!envKey) continue;
|
|
269
|
+
if (Object.prototype.hasOwnProperty.call(privateEnv, envKey)) {
|
|
270
|
+
env[envKey] = String(privateEnv[envKey] ?? "");
|
|
271
|
+
} else {
|
|
272
|
+
publicEnvForConfig[envKey] = value;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (Object.keys(publicEnvForConfig).length) {
|
|
276
|
+
codexConfigArgs.push(`${prefix}.env=${tomlInlineTable(publicEnvForConfig)}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return { codexConfigArgs, env };
|
|
281
|
+
}
|
|
282
|
+
|
|
155
283
|
function agentflowUserEnv(userId) {
|
|
156
284
|
const safe = sanitizeAgentflowUserId(userId);
|
|
157
285
|
pruneCursorMcpPrivateEnvPlaceholders();
|
|
158
286
|
return { ...readMergedEnvObject(safe), ...(safe ? readUserMcpPrivateEnvObject(safe) : {}), AGENTFLOW_USER_ID: safe };
|
|
159
287
|
}
|
|
160
288
|
|
|
289
|
+
function runCodexAgentWithPrivateMcp(cliWorkspace, prompt, options, userId) {
|
|
290
|
+
const { codexConfigArgs, env } = codexMcpOverridesFromCursorConfig(cliWorkspace, userId);
|
|
291
|
+
return runCodexAgentWithPrompt(cliWorkspace, prompt, {
|
|
292
|
+
...options,
|
|
293
|
+
codexConfigArgs: [...(Array.isArray(options?.codexConfigArgs) ? options.codexConfigArgs : []), ...codexConfigArgs],
|
|
294
|
+
env: { ...(options?.env || {}), ...env },
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
161
298
|
function runCursorAgentWithPrivateMcp(cliWorkspace, prompt, options, userId) {
|
|
162
299
|
const restore = materializeWorkspaceCursorMcpPrivateConfig(cliWorkspace, userId);
|
|
163
300
|
let handle;
|
|
@@ -291,6 +428,13 @@ export function startComposerAgent(opts) {
|
|
|
291
428
|
});
|
|
292
429
|
}
|
|
293
430
|
|
|
431
|
+
if (cli === "codex") {
|
|
432
|
+
return runCodexAgentWithPrivateMcp(cliWs, prompt, {
|
|
433
|
+
...common,
|
|
434
|
+
model: model || undefined,
|
|
435
|
+
}, opts.agentflowUserId);
|
|
436
|
+
}
|
|
437
|
+
|
|
294
438
|
if (cli === "claude-code") {
|
|
295
439
|
return runClaudeCodeAgentWithPrompt(cliWs, prompt, {
|
|
296
440
|
...common,
|
|
@@ -581,6 +725,15 @@ export async function runComposerPostFlowValidationAndRepair(opts) {
|
|
|
581
725
|
});
|
|
582
726
|
setChild(handle.child);
|
|
583
727
|
await handle.finished;
|
|
728
|
+
} else if (cli === "codex") {
|
|
729
|
+
const handle = runCodexAgentWithPrivateMcp(cliWs, agentPrompt, {
|
|
730
|
+
onStreamEvent: stepEmit,
|
|
731
|
+
model: model || undefined,
|
|
732
|
+
force: Boolean(opts.force),
|
|
733
|
+
env,
|
|
734
|
+
}, opts.agentflowUserId || opts.flowContext?.userId);
|
|
735
|
+
setChild(handle.child);
|
|
736
|
+
await handle.finished;
|
|
584
737
|
} else if (cli === "claude-code") {
|
|
585
738
|
const handle = runClaudeCodeAgentWithPrompt(cliWs, agentPrompt, {
|
|
586
739
|
onStreamEvent: stepEmit,
|
|
@@ -854,6 +1007,15 @@ export function startComposerMultiStep(opts) {
|
|
|
854
1007
|
});
|
|
855
1008
|
currentChild = handle.child;
|
|
856
1009
|
await handle.finished;
|
|
1010
|
+
} else if (cli === "codex") {
|
|
1011
|
+
const handle = runCodexAgentWithPrivateMcp(cliWs, agentPrompt, {
|
|
1012
|
+
onStreamEvent: stepEmit,
|
|
1013
|
+
model: model || undefined,
|
|
1014
|
+
force: Boolean(opts.force),
|
|
1015
|
+
env,
|
|
1016
|
+
}, opts.agentflowUserId || opts.flowContext?.userId);
|
|
1017
|
+
currentChild = handle.child;
|
|
1018
|
+
await handle.finished;
|
|
857
1019
|
} else if (cli === "claude-code") {
|
|
858
1020
|
const handle = runClaudeCodeAgentWithPrompt(cliWs, agentPrompt, {
|
|
859
1021
|
onStreamEvent: stepEmit,
|
|
@@ -61,14 +61,16 @@ function classifyModel(modelId) {
|
|
|
61
61
|
function loadModelLists() {
|
|
62
62
|
try {
|
|
63
63
|
const p = getModelListsAbs();
|
|
64
|
-
if (!fs.existsSync(p)) return { cursor: [], opencode: [] };
|
|
64
|
+
if (!fs.existsSync(p)) return { cursor: [], opencode: [], claudeCode: [], codex: [] };
|
|
65
65
|
const data = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
66
66
|
return {
|
|
67
67
|
cursor: Array.isArray(data.cursor) ? data.cursor.map(String) : [],
|
|
68
68
|
opencode: Array.isArray(data.opencode) ? data.opencode.map(String) : [],
|
|
69
|
+
claudeCode: Array.isArray(data.claudeCode) ? data.claudeCode.map(String) : [],
|
|
70
|
+
codex: Array.isArray(data.codex) ? data.codex.map(String) : [],
|
|
69
71
|
};
|
|
70
72
|
} catch {
|
|
71
|
-
return { cursor: [], opencode: [] };
|
|
73
|
+
return { cursor: [], opencode: [], claudeCode: [], codex: [] };
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -87,6 +89,18 @@ function buildModelTiers(modelList) {
|
|
|
87
89
|
return tiers;
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
function modelEntryId(entry) {
|
|
93
|
+
const idx = String(entry || "").indexOf(" - ");
|
|
94
|
+
return idx >= 0 ? entry.slice(0, idx).trim() : String(entry || "").trim();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function prefixedModels(models, prefix) {
|
|
98
|
+
return (Array.isArray(models) ? models : [])
|
|
99
|
+
.map((m) => modelEntryId(m))
|
|
100
|
+
.filter(Boolean)
|
|
101
|
+
.map((m) => `${prefix}:${m}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
90
104
|
// ─── 公开接口 ──────────────────────────────────────────────────────────────
|
|
91
105
|
|
|
92
106
|
/**
|
|
@@ -105,7 +119,12 @@ export function routeModel(complexity, opts = {}) {
|
|
|
105
119
|
}
|
|
106
120
|
|
|
107
121
|
const lists = loadModelLists();
|
|
108
|
-
const allModels = [
|
|
122
|
+
const allModels = [
|
|
123
|
+
...lists.cursor,
|
|
124
|
+
...prefixedModels(lists.codex, "codex"),
|
|
125
|
+
...prefixedModels(lists.claudeCode, "claude-code"),
|
|
126
|
+
...prefixedModels(lists.opencode, "opencode"),
|
|
127
|
+
];
|
|
109
128
|
if (allModels.length === 0) {
|
|
110
129
|
return { model: null, tier: complexity === "complex" ? "capable" : complexity === "simple" ? "fast" : "balanced", source: "no-models-available" };
|
|
111
130
|
}
|
|
@@ -146,11 +165,17 @@ export function getModelTierInfo() {
|
|
|
146
165
|
const lists = loadModelLists();
|
|
147
166
|
const cursorTiers = buildModelTiers(lists.cursor);
|
|
148
167
|
const opencodeTiers = buildModelTiers(lists.opencode);
|
|
168
|
+
const claudeCodeTiers = buildModelTiers(lists.claudeCode);
|
|
169
|
+
const codexTiers = buildModelTiers(lists.codex);
|
|
149
170
|
return {
|
|
150
171
|
cursor: cursorTiers,
|
|
151
172
|
opencode: opencodeTiers,
|
|
173
|
+
claudeCode: claudeCodeTiers,
|
|
174
|
+
codex: codexTiers,
|
|
152
175
|
totalCursor: lists.cursor.length,
|
|
153
176
|
totalOpencode: lists.opencode.length,
|
|
177
|
+
totalClaudeCode: lists.claudeCode.length,
|
|
178
|
+
totalCodex: lists.codex.length,
|
|
154
179
|
};
|
|
155
180
|
}
|
|
156
181
|
|
|
@@ -12,7 +12,7 @@ import { buildPlannerSkillContext } from "./composer-skill-router.mjs";
|
|
|
12
12
|
import { buildNodeSchemaPromptSection } from "./composer-node-schema.mjs";
|
|
13
13
|
import { formatInstancePlannerHint } from "./composer-flow-instances.mjs";
|
|
14
14
|
import { resolveCliAndModel } from "./model-config.mjs";
|
|
15
|
-
import { runClaudeCodeAgentWithPrompt, runCursorAgentWithPrompt, runOpenCodeAgentWithPrompt } from "./agent-runners.mjs";
|
|
15
|
+
import { runCodexAgentWithPrompt, runClaudeCodeAgentWithPrompt, runCursorAgentWithPrompt, runOpenCodeAgentWithPrompt } from "./agent-runners.mjs";
|
|
16
16
|
import { t } from "./i18n.mjs";
|
|
17
17
|
|
|
18
18
|
const PLANNER_MAX_TOKENS = 2048;
|
|
@@ -428,6 +428,8 @@ async function classifyViaCli(userPrompt, cliWorkspace) {
|
|
|
428
428
|
|
|
429
429
|
const runner = cli === "opencode"
|
|
430
430
|
? runOpenCodeAgentWithPrompt(cliWorkspace, prompt, { onStreamEvent, model: model || undefined })
|
|
431
|
+
: cli === "codex"
|
|
432
|
+
? runCodexAgentWithPrompt(cliWorkspace, prompt, { onStreamEvent, model: model || undefined })
|
|
431
433
|
: cli === "claude-code"
|
|
432
434
|
? runClaudeCodeAgentWithPrompt(cliWorkspace, prompt, { onStreamEvent, model: model || undefined })
|
|
433
435
|
: runCursorAgentWithPrompt(cliWorkspace, prompt, { onStreamEvent, model: model || undefined, force: true });
|
package/bin/lib/help.mjs
CHANGED
|
@@ -8,7 +8,7 @@ export function printHelp() {
|
|
|
8
8
|
// 根据语言输出不同的帮助文本
|
|
9
9
|
if (isZh) {
|
|
10
10
|
log.info(`
|
|
11
|
-
AgentFlow CLI — 使用 Cursor / OpenCode / Claude Code CLI 流式输出驱动 apply/replay。
|
|
11
|
+
AgentFlow CLI — 使用 Cursor / OpenCode / Claude Code / Codex CLI 流式输出驱动 apply/replay。
|
|
12
12
|
|
|
13
13
|
用法:
|
|
14
14
|
agentflow login [--provider github|google] 登录 AgentFlow Hub(默认 GitHub)
|
|
@@ -18,6 +18,7 @@ AgentFlow CLI — 使用 Cursor / OpenCode / Claude Code CLI 流式输出驱动
|
|
|
18
18
|
agentflow download <slug|title> [--user|--workspace] [--as <id>] [--raw [--output <dir>]] 从 Hub 下载流程(默认 --user 安装到 ~/agentflow/pipelines/<id>;--workspace 安装到当前工程 .workspace/agentflow/pipelines/<id>;--raw 仅保留压缩包)
|
|
19
19
|
agentflow list 列出所有流水线
|
|
20
20
|
agentflow ui [--host <addr>] [--port <n>] [--scheduler] [--no-open] [--hide-community-links] 本地 HTTP:流水线列表 + React Flow 节点流程图编辑保存(默认 127.0.0.1:8765;可用 AGENTFLOW_UI_HOST)
|
|
21
|
+
agentflow mcp 启动 AgentFlow MCP stdio server,供 Cursor/Codex 运行流程并读取 display 输出
|
|
21
22
|
agentflow scheduler start [--poll-ms <ms>] 启动定时执行调度器(读取各流水线 schedule.json)
|
|
22
23
|
agentflow scheduler status [--json] 查看定时执行配置与状态
|
|
23
24
|
agentflow scheduler cancel <FlowName> <uuid> 取消某次等待中的 watch/run
|
|
@@ -31,19 +32,19 @@ AgentFlow CLI — 使用 Cursor / OpenCode / Claude Code CLI 流式输出驱动
|
|
|
31
32
|
agentflow run-status <flowName> <uuid> 输出该次运行的节点状态 JSON(供 UI 展示 success/pending 等角标)
|
|
32
33
|
agentflow extract-thinking <flowName> <uuid> 从该次 run 的 logs/log.txt 提取 thinking,写入 logs/thinking_by_session_and_nodes.md
|
|
33
34
|
agentflow extract-thinking -list 列出所有存在 logs/log.txt 的 run(可接 --json)
|
|
34
|
-
agentflow update-model-lists 拉取 Cursor / OpenCode 模型列表并写入 ~/agentflow/model-lists.json
|
|
35
|
+
agentflow update-model-lists 拉取 Cursor / OpenCode / Claude Code / Codex 模型列表并写入 ~/agentflow/model-lists.json
|
|
35
36
|
agentflow write-flow <flowId> --json --flow-source <user|workspace> 从 stdin 读入 YAML 写入用户目录或工作区(builtin 已弃用,将视为 workspace)
|
|
36
37
|
agentflow --help
|
|
37
38
|
|
|
38
39
|
选项:
|
|
39
40
|
--workspace-root <path> 工作区根目录(默认:当前目录)
|
|
40
|
-
--dry-run (仅 apply)打印就绪节点后退出,不执行
|
|
41
|
-
--model <name> 后端模型。默认走 Cursor;前缀 opencode:<model>、claude-code:<model>、api:<provider>/<model> 可切换后端。覆盖 CURSOR_AGENT_MODEL。
|
|
41
|
+
--dry-run (仅 apply)打印就绪节点后退出,不执行 agent 后端
|
|
42
|
+
--model <name> 后端模型。默认走 Cursor;前缀 opencode:<model>、claude-code:<model>、codex:<model>、api:<provider>/<model> 可切换后端。覆盖 CURSOR_AGENT_MODEL。
|
|
42
43
|
--input <name>=<value> (仅 apply)覆盖 flow 中 provide 节点的值。value 前缀 file: 表示文件路径。可多次使用。
|
|
43
44
|
--debug 显示调试日志(灰色,低优先级)
|
|
44
45
|
--force 传递 --force/--trust 给 Cursor;设置 OPENCODE_PERMISSION 允许 OpenCode 的 external_directory(默认开启)。使用 --no-force 禁用。
|
|
45
|
-
--parallel 并行运行同轮就绪节点(默认关闭)。多个
|
|
46
|
-
--machine-readable 向 stdout 每行输出一个 JSON 事件(apply-start/node-start/node-done/node-failed/apply-done/apply-paused)。供 UI 运行按钮使用:解析 stdout 显示当前节点;
|
|
46
|
+
--parallel 并行运行同轮就绪节点(默认关闭)。多个 CLI 进程可能竞争后端自身的本地配置。
|
|
47
|
+
--machine-readable 向 stdout 每行输出一个 JSON 事件(apply-start/node-start/node-done/node-failed/apply-done/apply-paused)。供 UI 运行按钮使用:解析 stdout 显示当前节点;agent 后端输出转到 stderr。
|
|
47
48
|
--lang <code> 设置语言:en、zh(默认:zh,或从 LANG 环境变量检测)
|
|
48
49
|
|
|
49
50
|
路径说明:
|
|
@@ -71,13 +72,13 @@ Apply:构建运行目录,解析流程,循环运行就绪节点。
|
|
|
71
72
|
Resume:将 pending 和 failed 节点标记为成功(例如 UserCheck 确认后或重试失败后),然后继续 apply。
|
|
72
73
|
Replay:运行单个节点(pre-process → execute → post-process)。
|
|
73
74
|
|
|
74
|
-
需要:Node >=18,以下任一 CLI 在 PATH 中用于节点执行:Cursor CLI('agent',默认)、OpenCode CLI('opencode',env 覆盖 OPENCODE_CMD)、Claude Code CLI('claude',env 覆盖 CLAUDE_CODE_CMD,需先 'claude /login')。
|
|
75
|
+
需要:Node >=18,以下任一 CLI 在 PATH 中用于节点执行:Cursor CLI('agent',默认)、OpenCode CLI('opencode',env 覆盖 OPENCODE_CMD)、Claude Code CLI('claude',env 覆盖 CLAUDE_CODE_CMD,需先 'claude /login')、Codex CLI('codex',env 覆盖 CODEX_CMD,需先 'codex login')。
|
|
75
76
|
Apply/replay 脚本已打包在 agentflow 包中(bin/pipeline/)。
|
|
76
77
|
`);
|
|
77
78
|
} else {
|
|
78
79
|
// 英文版本
|
|
79
80
|
log.info(`
|
|
80
|
-
AgentFlow CLI — drive apply/replay with Cursor / OpenCode / Claude Code CLI streaming.
|
|
81
|
+
AgentFlow CLI — drive apply/replay with Cursor / OpenCode / Claude Code / Codex CLI streaming.
|
|
81
82
|
|
|
82
83
|
Usage:
|
|
83
84
|
agentflow login [--provider github|google] Login to AgentFlow Hub (default: GitHub)
|
|
@@ -87,6 +88,7 @@ Usage:
|
|
|
87
88
|
agentflow download <slug|title> [--user|--workspace] [--as <id>] [--raw [--output <dir>]] Download flow (default --user → ~/agentflow/pipelines/<id>; --workspace → current project's .workspace/agentflow/pipelines/<id>; --raw keeps the archive)
|
|
88
89
|
agentflow list List all pipelines
|
|
89
90
|
agentflow ui [--host <addr>] [--port <n>] [--scheduler] [--no-open] [--hide-community-links] Local HTTP: pipeline list + React Flow node diagram editor (default 127.0.0.1:8765; AGENTFLOW_UI_HOST supported)
|
|
91
|
+
agentflow mcp Start the AgentFlow MCP stdio server for Cursor/Codex to run flows and read display outputs
|
|
90
92
|
agentflow scheduler start [--poll-ms <ms>] Start the scheduled-run scheduler (reads each pipeline schedule.json)
|
|
91
93
|
agentflow scheduler status [--json] Show scheduled-run configuration and state
|
|
92
94
|
agentflow scheduler cancel <FlowName> <uuid> Cancel a waiting watch/run
|
|
@@ -100,19 +102,19 @@ Usage:
|
|
|
100
102
|
agentflow run-status <flowName> <uuid> Output node status JSON for this run (for UI success/pending badges)
|
|
101
103
|
agentflow extract-thinking <flowName> <uuid> Extract thinking from run logs/log.txt, write to logs/thinking_by_session_and_nodes.md
|
|
102
104
|
agentflow extract-thinking -list List all runs with logs/log.txt (use --json)
|
|
103
|
-
agentflow update-model-lists Fetch Cursor / OpenCode model lists to ~/agentflow/model-lists.json
|
|
105
|
+
agentflow update-model-lists Fetch Cursor / OpenCode / Claude Code / Codex model lists to ~/agentflow/model-lists.json
|
|
104
106
|
agentflow write-flow <flowId> --json --flow-source <user|workspace> Read YAML from stdin and write to user dir or workspace (builtin deprecated, treated as workspace)
|
|
105
107
|
agentflow --help
|
|
106
108
|
|
|
107
109
|
Options:
|
|
108
110
|
--workspace-root <path> Workspace root (default: cwd)
|
|
109
|
-
--dry-run (apply only) Print ready nodes and exit without running
|
|
110
|
-
--model <name> Backend model. Default routes to Cursor; prefixes opencode:<model>, claude-code:<model>, api:<provider>/<model> switch backend. Overrides CURSOR_AGENT_MODEL.
|
|
111
|
+
--dry-run (apply only) Print ready nodes and exit without running an agent backend
|
|
112
|
+
--model <name> Backend model. Default routes to Cursor; prefixes opencode:<model>, claude-code:<model>, codex:<model>, api:<provider>/<model> switch backend. Overrides CURSOR_AGENT_MODEL.
|
|
111
113
|
--input <name>=<value> (apply only) Override provide node values in flow. Prefix value with file: for file paths. Can be used multiple times.
|
|
112
114
|
--debug Show debug logs (gray, low priority)
|
|
113
115
|
--force Pass --force/--trust to Cursor; set OPENCODE_PERMISSION to allow external_directory for OpenCode (default: on). Use --no-force to disable.
|
|
114
|
-
--parallel Run same-round ready nodes in parallel (default: off). Multiple
|
|
115
|
-
--machine-readable Emit one JSON event per line to stdout (apply-start/node-start/node-done/node-failed/apply-done/apply-paused). For UI run button: parse stdout to show current node;
|
|
116
|
+
--parallel Run same-round ready nodes in parallel (default: off). Multiple CLI processes may race on backend-local config.
|
|
117
|
+
--machine-readable Emit one JSON event per line to stdout (apply-start/node-start/node-done/node-failed/apply-done/apply-paused). For UI run button: parse stdout to show current node; agent backend output goes to stderr.
|
|
116
118
|
--lang <code> Set language: en, zh (default: en, or auto-detect from LANG env)
|
|
117
119
|
|
|
118
120
|
Path notes:
|
|
@@ -140,7 +142,7 @@ Apply: builds run dir, parses flow, runs ready nodes in a loop.
|
|
|
140
142
|
Resume: marks pending and failed node(s) as success (e.g. after UserCheck confirm or retry failed), then continues apply.
|
|
141
143
|
Replay: runs a single node (pre-process → execute → post-process).
|
|
142
144
|
|
|
143
|
-
Requires: Node >=18, any of Cursor CLI ('agent', default), OpenCode CLI ('opencode', env OPENCODE_CMD),
|
|
145
|
+
Requires: Node >=18, any of Cursor CLI ('agent', default), OpenCode CLI ('opencode', env OPENCODE_CMD), Claude Code CLI ('claude', env CLAUDE_CODE_CMD, run 'claude /login' first), or Codex CLI ('codex', env CODEX_CMD, run 'codex login' first) in PATH for node execution.
|
|
144
146
|
Apply/replay scripts are bundled in the agentflow package (bin/pipeline/).
|
|
145
147
|
`);
|
|
146
148
|
}
|
package/bin/lib/locales/en.json
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"default": "Default"
|
|
36
36
|
},
|
|
37
37
|
"cli": {
|
|
38
|
-
"description": "AgentFlow CLI — drive apply/replay with Cursor or
|
|
38
|
+
"description": "AgentFlow CLI — drive apply/replay with Cursor, OpenCode, Claude Code, or Codex CLI streaming",
|
|
39
39
|
"usage": "Usage",
|
|
40
40
|
"commands": "Commands",
|
|
41
41
|
"options": "Options",
|
|
@@ -341,6 +341,10 @@
|
|
|
341
341
|
"displayName": "HTML Display",
|
|
342
342
|
"description": "Render HTML in a sandboxed iframe on the Workspace canvas and pass the source downstream"
|
|
343
343
|
},
|
|
344
|
+
"display_react_app": {
|
|
345
|
+
"displayName": "React App",
|
|
346
|
+
"description": "Preview a React project in a sandboxed iframe on the Workspace canvas and pass the project JSON downstream"
|
|
347
|
+
},
|
|
344
348
|
"display_image": {
|
|
345
349
|
"displayName": "Image Display",
|
|
346
350
|
"description": "Preview an image URL, data URL, or image path on the Workspace canvas and pass the source downstream"
|
package/bin/lib/locales/zh.json
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"default": "默认"
|
|
36
36
|
},
|
|
37
37
|
"cli": {
|
|
38
|
-
"description": "AgentFlow CLI — 使用 Cursor 或
|
|
38
|
+
"description": "AgentFlow CLI — 使用 Cursor、OpenCode、Claude Code 或 Codex CLI 流式输出驱动 apply/replay",
|
|
39
39
|
"usage": "用法",
|
|
40
40
|
"commands": "命令",
|
|
41
41
|
"options": "选项",
|
|
@@ -341,6 +341,10 @@
|
|
|
341
341
|
"displayName": "HTML 展示",
|
|
342
342
|
"description": "在 Workspace 画布中用 sandbox iframe 渲染 HTML,并将源码继续传给下游"
|
|
343
343
|
},
|
|
344
|
+
"display_react_app": {
|
|
345
|
+
"displayName": "React 工程",
|
|
346
|
+
"description": "在 Workspace 画布中用 sandbox iframe 预览 React 工程,并将工程 JSON 继续传给下游"
|
|
347
|
+
},
|
|
344
348
|
"display_image": {
|
|
345
349
|
"displayName": "图片展示",
|
|
346
350
|
"description": "在 Workspace 画布中预览图片 URL、data URL 或图片路径,并将来源继续传给下游"
|
package/bin/lib/main.mjs
CHANGED
|
@@ -32,6 +32,7 @@ import { hubPublish } from "./hub-publish.mjs";
|
|
|
32
32
|
import { hubListRemote, hubDownload } from "./hub-remote.mjs";
|
|
33
33
|
import { cancelScheduledRun, listScheduleStatuses, startScheduler } from "./scheduler.mjs";
|
|
34
34
|
import { installFlowDependency, listMarketplacePackages, publishNodePackage } from "./marketplace.mjs";
|
|
35
|
+
import { startMcpServer } from "./mcp-server.mjs";
|
|
35
36
|
|
|
36
37
|
async function readStdin() {
|
|
37
38
|
const chunks = [];
|
|
@@ -116,6 +117,10 @@ export async function main() {
|
|
|
116
117
|
if (jsonMode) process.stdout.write(JSON.stringify(result) + "\n");
|
|
117
118
|
process.exit(0);
|
|
118
119
|
}
|
|
120
|
+
if (sub === "mcp") {
|
|
121
|
+
await startMcpServer();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
119
124
|
const jsonOnlySubs = [
|
|
120
125
|
"list-flows",
|
|
121
126
|
"list-nodes",
|