@fieldwangai/agentflow 0.1.85 → 0.1.87
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 +579 -17
- 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-BqOb-W89.css +1 -0
- package/builtin/web-ui/dist/assets/index-Cla8u37K.js +340 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +3 -2
- package/builtin/web-ui/dist/assets/index-Lb9rIMqs.js +0 -250
- package/builtin/web-ui/dist/assets/index-aqphFZlG.css +0 -1
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
const DEFAULT_BASE_URL = "http://127.0.0.1:8875";
|
|
2
|
+
|
|
3
|
+
function envBaseUrl() {
|
|
4
|
+
return String(process.env.AGENTFLOW_BASE_URL || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function envToken() {
|
|
8
|
+
return String(process.env.AGENTFLOW_TOKEN || process.env.AGENTFLOW_SESSION_TOKEN || "").trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function toolText(data) {
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: typeof data === "string" ? data : JSON.stringify(data, null, 2),
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function toolError(error) {
|
|
23
|
+
return {
|
|
24
|
+
isError: true,
|
|
25
|
+
content: [{ type: "text", text: String(error?.message || error || "Unknown error") }],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function httpJson(pathname, { method = "GET", body } = {}) {
|
|
30
|
+
const baseUrl = envBaseUrl();
|
|
31
|
+
const token = envToken();
|
|
32
|
+
const url = new URL(pathname, baseUrl);
|
|
33
|
+
const headers = {
|
|
34
|
+
Accept: "application/json",
|
|
35
|
+
};
|
|
36
|
+
if (body !== undefined) headers["Content-Type"] = "application/json";
|
|
37
|
+
if (token) {
|
|
38
|
+
headers.Authorization = `Bearer ${token}`;
|
|
39
|
+
headers.Cookie = `af_session=${encodeURIComponent(token)}`;
|
|
40
|
+
}
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
method,
|
|
43
|
+
headers,
|
|
44
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
45
|
+
});
|
|
46
|
+
const text = await response.text();
|
|
47
|
+
let data = null;
|
|
48
|
+
try {
|
|
49
|
+
data = text ? JSON.parse(text) : null;
|
|
50
|
+
} catch {
|
|
51
|
+
data = { text };
|
|
52
|
+
}
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const message = data?.error || data?.message || text || `HTTP ${response.status}`;
|
|
55
|
+
throw new Error(message);
|
|
56
|
+
}
|
|
57
|
+
return data;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function query(params = {}) {
|
|
61
|
+
const search = new URLSearchParams();
|
|
62
|
+
for (const [key, value] of Object.entries(params || {})) {
|
|
63
|
+
if (value === undefined || value === null || value === "") continue;
|
|
64
|
+
search.set(key, String(value));
|
|
65
|
+
}
|
|
66
|
+
const s = search.toString();
|
|
67
|
+
return s ? `?${s}` : "";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function displayKind(definitionId) {
|
|
71
|
+
const id = String(definitionId || "");
|
|
72
|
+
if (id === "display_markdown") return "markdown";
|
|
73
|
+
if (id === "display_mermaid") return "mermaid";
|
|
74
|
+
if (id === "display_ascii") return "ascii";
|
|
75
|
+
if (id === "display_html") return "html";
|
|
76
|
+
if (id === "display_react_app") return "react";
|
|
77
|
+
if (id === "display_image") return "image";
|
|
78
|
+
if (id === "display_chart") return "chart";
|
|
79
|
+
if (id === "display_table") return "table";
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function slotText(slots, names = []) {
|
|
84
|
+
const wanted = new Set(names.map(String));
|
|
85
|
+
for (const slot of Array.isArray(slots) ? slots : []) {
|
|
86
|
+
const name = String(slot?.name || "");
|
|
87
|
+
if (wanted.size && !wanted.has(name)) continue;
|
|
88
|
+
const value = slot?.value ?? slot?.default ?? "";
|
|
89
|
+
if (String(value || "").trim()) return String(value);
|
|
90
|
+
}
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function extractDisplayOutputs(graph) {
|
|
95
|
+
const instances = graph?.instances && typeof graph.instances === "object" ? graph.instances : {};
|
|
96
|
+
const outputs = [];
|
|
97
|
+
for (const [nodeId, instance] of Object.entries(instances)) {
|
|
98
|
+
const kind = displayKind(instance?.definitionId);
|
|
99
|
+
if (!kind) continue;
|
|
100
|
+
const primary = kind === "image" ? "src" : "content";
|
|
101
|
+
const content = String(instance?.body || "") ||
|
|
102
|
+
slotText(instance?.input, [primary, "content", "markdown", "html", "src"]) ||
|
|
103
|
+
slotText(instance?.output, [primary, "content", "markdown", "html", "src"]);
|
|
104
|
+
outputs.push({
|
|
105
|
+
nodeId,
|
|
106
|
+
label: String(instance?.label || instance?.displayName || nodeId),
|
|
107
|
+
definitionId: String(instance?.definitionId || ""),
|
|
108
|
+
kind,
|
|
109
|
+
content,
|
|
110
|
+
hasContent: Boolean(String(content || "").trim()),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return outputs;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const tools = [
|
|
117
|
+
{
|
|
118
|
+
name: "agentflow_list_flows",
|
|
119
|
+
description: "List AgentFlow flows visible to the configured user.",
|
|
120
|
+
inputSchema: {
|
|
121
|
+
type: "object",
|
|
122
|
+
properties: {},
|
|
123
|
+
additionalProperties: false,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "agentflow_get_workspace_graph",
|
|
128
|
+
description: "Read a flow as a workspace graph before running or inspecting display nodes.",
|
|
129
|
+
inputSchema: {
|
|
130
|
+
type: "object",
|
|
131
|
+
required: ["flowId"],
|
|
132
|
+
properties: {
|
|
133
|
+
flowId: { type: "string" },
|
|
134
|
+
flowSource: { type: "string", default: "user" },
|
|
135
|
+
},
|
|
136
|
+
additionalProperties: false,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "agentflow_run_flow",
|
|
141
|
+
description: "Run an AgentFlow workspace flow and return the merged graph plus display outputs.",
|
|
142
|
+
inputSchema: {
|
|
143
|
+
type: "object",
|
|
144
|
+
required: ["flowId"],
|
|
145
|
+
properties: {
|
|
146
|
+
flowId: { type: "string" },
|
|
147
|
+
flowSource: { type: "string", default: "user" },
|
|
148
|
+
runNodeId: { type: "string", description: "Optional run/schedule node id. Empty runs from the graph start." },
|
|
149
|
+
inputs: { type: "object", description: "Optional flow input values." },
|
|
150
|
+
},
|
|
151
|
+
additionalProperties: false,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "agentflow_get_run_status",
|
|
156
|
+
description: "Get active workspace run status for a flow.",
|
|
157
|
+
inputSchema: {
|
|
158
|
+
type: "object",
|
|
159
|
+
required: ["flowId"],
|
|
160
|
+
properties: {
|
|
161
|
+
flowId: { type: "string" },
|
|
162
|
+
flowSource: { type: "string", default: "user" },
|
|
163
|
+
},
|
|
164
|
+
additionalProperties: false,
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "agentflow_get_run_logs",
|
|
169
|
+
description: "Read workspace run logs. Provide runId for a specific run, or flowId to list recent runs.",
|
|
170
|
+
inputSchema: {
|
|
171
|
+
type: "object",
|
|
172
|
+
properties: {
|
|
173
|
+
flowId: { type: "string" },
|
|
174
|
+
flowSource: { type: "string" },
|
|
175
|
+
runId: { type: "string" },
|
|
176
|
+
limit: { type: "number", default: 20 },
|
|
177
|
+
},
|
|
178
|
+
additionalProperties: false,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "agentflow_get_display_outputs",
|
|
183
|
+
description: "Extract display node outputs from a workspace graph.",
|
|
184
|
+
inputSchema: {
|
|
185
|
+
type: "object",
|
|
186
|
+
required: ["flowId"],
|
|
187
|
+
properties: {
|
|
188
|
+
flowId: { type: "string" },
|
|
189
|
+
flowSource: { type: "string", default: "user" },
|
|
190
|
+
},
|
|
191
|
+
additionalProperties: false,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
async function callTool(name, args = {}) {
|
|
197
|
+
if (name === "agentflow_list_flows") {
|
|
198
|
+
return toolText(await httpJson("/api/flows"));
|
|
199
|
+
}
|
|
200
|
+
if (name === "agentflow_get_workspace_graph") {
|
|
201
|
+
const flowSource = args.flowSource || "user";
|
|
202
|
+
return toolText(await httpJson(`/api/workspace/graph${query({ flowId: args.flowId, flowSource })}`));
|
|
203
|
+
}
|
|
204
|
+
if (name === "agentflow_run_flow") {
|
|
205
|
+
const flowSource = args.flowSource || "user";
|
|
206
|
+
const graphPayload = await httpJson(`/api/workspace/graph${query({ flowId: args.flowId, flowSource })}`);
|
|
207
|
+
const runPayload = {
|
|
208
|
+
flowId: args.flowId,
|
|
209
|
+
flowSource,
|
|
210
|
+
runNodeId: args.runNodeId || "",
|
|
211
|
+
graph: graphPayload.graph,
|
|
212
|
+
inputs: args.inputs && typeof args.inputs === "object" ? args.inputs : {},
|
|
213
|
+
};
|
|
214
|
+
const result = await httpJson("/api/workspace/run", { method: "POST", body: runPayload });
|
|
215
|
+
return toolText({
|
|
216
|
+
ok: result?.ok === true,
|
|
217
|
+
flowId: args.flowId,
|
|
218
|
+
flowSource,
|
|
219
|
+
runNodeId: args.runNodeId || "",
|
|
220
|
+
order: result?.order || [],
|
|
221
|
+
pauseNodeIds: result?.pauseNodeIds || [],
|
|
222
|
+
touchedNodeIds: result?.touchedNodeIds || [],
|
|
223
|
+
displayOutputs: extractDisplayOutputs(result?.graph),
|
|
224
|
+
graph: result?.graph || null,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
if (name === "agentflow_get_run_status") {
|
|
228
|
+
return toolText(await httpJson(`/api/workspace/run/status${query({ flowId: args.flowId, flowSource: args.flowSource || "user" })}`));
|
|
229
|
+
}
|
|
230
|
+
if (name === "agentflow_get_run_logs") {
|
|
231
|
+
if (args.runId) {
|
|
232
|
+
return toolText(await httpJson(`/api/workspace/run-logs/${encodeURIComponent(String(args.runId))}`));
|
|
233
|
+
}
|
|
234
|
+
return toolText(await httpJson(`/api/workspace/run-logs${query({
|
|
235
|
+
flowId: args.flowId || "",
|
|
236
|
+
flowSource: args.flowSource || "",
|
|
237
|
+
limit: args.limit || 20,
|
|
238
|
+
})}`));
|
|
239
|
+
}
|
|
240
|
+
if (name === "agentflow_get_display_outputs") {
|
|
241
|
+
const flowSource = args.flowSource || "user";
|
|
242
|
+
const graphPayload = await httpJson(`/api/workspace/graph${query({ flowId: args.flowId, flowSource })}`);
|
|
243
|
+
return toolText({
|
|
244
|
+
flowId: args.flowId,
|
|
245
|
+
flowSource,
|
|
246
|
+
displayOutputs: extractDisplayOutputs(graphPayload.graph),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return toolError(`Unknown tool: ${name}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function response(id, result) {
|
|
253
|
+
return { jsonrpc: "2.0", id, result };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function errorResponse(id, error) {
|
|
257
|
+
return {
|
|
258
|
+
jsonrpc: "2.0",
|
|
259
|
+
id,
|
|
260
|
+
error: {
|
|
261
|
+
code: -32000,
|
|
262
|
+
message: String(error?.message || error || "Unknown error"),
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function writeMessage(message) {
|
|
268
|
+
process.stdout.write(JSON.stringify(message) + "\n");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function handleMessage(message) {
|
|
272
|
+
if (!message || typeof message !== "object") return;
|
|
273
|
+
const id = message.id;
|
|
274
|
+
const method = String(message.method || "");
|
|
275
|
+
try {
|
|
276
|
+
if (method === "initialize") {
|
|
277
|
+
writeMessage(response(id, {
|
|
278
|
+
protocolVersion: "2024-11-05",
|
|
279
|
+
capabilities: { tools: {} },
|
|
280
|
+
serverInfo: { name: "agentflow", version: "0.1.0" },
|
|
281
|
+
}));
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (method === "notifications/initialized") return;
|
|
285
|
+
if (method === "tools/list") {
|
|
286
|
+
writeMessage(response(id, { tools }));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (method === "tools/call") {
|
|
290
|
+
const params = message.params || {};
|
|
291
|
+
const result = await callTool(String(params.name || ""), params.arguments || {});
|
|
292
|
+
writeMessage(response(id, result));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (id !== undefined) writeMessage(errorResponse(id, `Unknown method: ${method}`));
|
|
296
|
+
} catch (e) {
|
|
297
|
+
if (id !== undefined) writeMessage(errorResponse(id, e));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function feedLine(line) {
|
|
302
|
+
const text = String(line || "").trim();
|
|
303
|
+
if (!text) return;
|
|
304
|
+
try {
|
|
305
|
+
void handleMessage(JSON.parse(text));
|
|
306
|
+
} catch (e) {
|
|
307
|
+
writeMessage(errorResponse(null, e));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export async function startMcpServer() {
|
|
312
|
+
process.stdin.setEncoding("utf8");
|
|
313
|
+
let buffer = "";
|
|
314
|
+
process.stdin.on("data", (chunk) => {
|
|
315
|
+
buffer += chunk;
|
|
316
|
+
let idx = buffer.indexOf("\n");
|
|
317
|
+
while (idx >= 0) {
|
|
318
|
+
const line = buffer.slice(0, idx);
|
|
319
|
+
buffer = buffer.slice(idx + 1);
|
|
320
|
+
feedLine(line);
|
|
321
|
+
idx = buffer.indexOf("\n");
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
process.stdin.on("end", () => {
|
|
325
|
+
if (buffer.trim()) feedLine(buffer);
|
|
326
|
+
});
|
|
327
|
+
await new Promise(() => {});
|
|
328
|
+
}
|
package/bin/lib/model-config.mjs
CHANGED
|
@@ -45,6 +45,14 @@ export function resolveCliAndModel(workspaceRoot, nodeModel, agentModelOverride)
|
|
|
45
45
|
if (raw.startsWith("api:")) {
|
|
46
46
|
return { cli: "api", model: raw, label: raw };
|
|
47
47
|
}
|
|
48
|
+
if (raw.startsWith("codex:")) {
|
|
49
|
+
const m = raw.slice("codex:".length).trim();
|
|
50
|
+
return {
|
|
51
|
+
cli: "codex",
|
|
52
|
+
model: m || null,
|
|
53
|
+
label: m ? `codex: ${m}` : "codex (default)",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
48
56
|
if (raw.startsWith("claude-code:")) {
|
|
49
57
|
const m = raw.slice("claude-code:".length).trim();
|
|
50
58
|
return {
|
|
@@ -75,9 +83,11 @@ export function resolveCliAndModel(workspaceRoot, nodeModel, agentModelOverride)
|
|
|
75
83
|
? "opencode"
|
|
76
84
|
: cfg.cli === "api"
|
|
77
85
|
? "api"
|
|
78
|
-
: cfg.cli === "
|
|
79
|
-
? "
|
|
80
|
-
: "
|
|
86
|
+
: cfg.cli === "codex"
|
|
87
|
+
? "codex"
|
|
88
|
+
: cfg.cli === "claude-code"
|
|
89
|
+
? "claude-code"
|
|
90
|
+
: "cursor";
|
|
81
91
|
const model = String(cfg.model).trim();
|
|
82
92
|
return { cli, model, label: `${cli}: ${model}` };
|
|
83
93
|
}
|
|
@@ -87,6 +97,15 @@ export function resolveCliAndModel(workspaceRoot, nodeModel, agentModelOverride)
|
|
|
87
97
|
return { cli: "api", model: key, label: key };
|
|
88
98
|
}
|
|
89
99
|
|
|
100
|
+
if (key && key.startsWith("codex:")) {
|
|
101
|
+
const model = key.slice("codex:".length) || "";
|
|
102
|
+
return {
|
|
103
|
+
cli: "codex",
|
|
104
|
+
model: model || null,
|
|
105
|
+
label: model ? `codex: ${model}` : "codex (default)",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
90
109
|
if (key && key.startsWith("claude-code:")) {
|
|
91
110
|
const model = key.slice("claude-code:".length) || "";
|
|
92
111
|
return {
|
|
@@ -109,6 +128,16 @@ export function resolveCliAndModel(workspaceRoot, nodeModel, agentModelOverride)
|
|
|
109
128
|
if (envModel && envModel.startsWith("api:")) {
|
|
110
129
|
return { cli: "api", model: envModel, label: envModel };
|
|
111
130
|
}
|
|
131
|
+
if (!key) {
|
|
132
|
+
const codexModel = process.env.CODEX_MODEL && String(process.env.CODEX_MODEL).trim();
|
|
133
|
+
if (codexModel) {
|
|
134
|
+
return {
|
|
135
|
+
cli: "codex",
|
|
136
|
+
model: codexModel,
|
|
137
|
+
label: `codex: ${codexModel}`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
112
141
|
const model = normalizeCursorModelForCli(key || envModel || "Auto");
|
|
113
142
|
return {
|
|
114
143
|
cli: "cursor",
|
package/bin/lib/model-lists.mjs
CHANGED
|
@@ -125,8 +125,68 @@ export function runOpencodeModels(workspaceRoot, provider) {
|
|
|
125
125
|
});
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
function parseCodexModelList(stdout) {
|
|
129
|
+
const cleaned = stripAnsiModelList(stdout);
|
|
130
|
+
const start = cleaned.indexOf("{");
|
|
131
|
+
const end = cleaned.lastIndexOf("}");
|
|
132
|
+
if (start < 0 || end <= start) return [];
|
|
133
|
+
try {
|
|
134
|
+
const parsed = JSON.parse(cleaned.slice(start, end + 1));
|
|
135
|
+
const rows = Array.isArray(parsed?.models) ? parsed.models : [];
|
|
136
|
+
const seen = new Set();
|
|
137
|
+
const models = [];
|
|
138
|
+
for (const row of rows) {
|
|
139
|
+
if (!row || typeof row !== "object") continue;
|
|
140
|
+
const slug = typeof row.slug === "string" ? row.slug.trim() : "";
|
|
141
|
+
if (!slug || seen.has(slug)) continue;
|
|
142
|
+
seen.add(slug);
|
|
143
|
+
const display = typeof row.display_name === "string" ? row.display_name.trim() : "";
|
|
144
|
+
models.push(display && display !== slug ? `${slug} - ${display}` : slug);
|
|
145
|
+
}
|
|
146
|
+
return models;
|
|
147
|
+
} catch (_) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function runCodexModels(workspaceRoot) {
|
|
153
|
+
return new Promise((resolve) => {
|
|
154
|
+
const codexCmd = process.env.CODEX_CMD || "codex";
|
|
155
|
+
const child = spawn(codexCmd, ["debug", "models", "--bundled"], {
|
|
156
|
+
cwd: workspaceRoot,
|
|
157
|
+
shell: false,
|
|
158
|
+
env: envWithCommonBinPaths(),
|
|
159
|
+
});
|
|
160
|
+
let stdout = "";
|
|
161
|
+
let stderr = "";
|
|
162
|
+
let settled = false;
|
|
163
|
+
const finish = (models) => {
|
|
164
|
+
if (settled) return;
|
|
165
|
+
settled = true;
|
|
166
|
+
resolve(models);
|
|
167
|
+
};
|
|
168
|
+
child.stdout?.on("data", (chunk) => {
|
|
169
|
+
stdout += chunk.toString("utf-8");
|
|
170
|
+
});
|
|
171
|
+
child.stderr?.on("data", (chunk) => {
|
|
172
|
+
stderr += chunk.toString("utf-8");
|
|
173
|
+
});
|
|
174
|
+
child.on("close", (code) => {
|
|
175
|
+
if (code === 0) finish(parseCodexModelList(stdout || stderr));
|
|
176
|
+
else finish([]);
|
|
177
|
+
});
|
|
178
|
+
child.on("error", () => finish([]));
|
|
179
|
+
setTimeout(() => {
|
|
180
|
+
try {
|
|
181
|
+
child.kill("SIGTERM");
|
|
182
|
+
} catch {}
|
|
183
|
+
finish([]);
|
|
184
|
+
}, 10_000);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
128
188
|
/**
|
|
129
|
-
* 拉取 Cursor / OpenCode 模型列表并写入 ~/agentflow/model-lists.json
|
|
189
|
+
* 拉取 Cursor / OpenCode / Claude Code / Codex 模型列表并写入 ~/agentflow/model-lists.json
|
|
130
190
|
* @param {string} workspaceRoot
|
|
131
191
|
* @param {{ opencodeProviderOverride?: string }} [opts]
|
|
132
192
|
*/
|
|
@@ -150,9 +210,11 @@ export async function updateModelLists(workspaceRoot, opts = {}) {
|
|
|
150
210
|
cursor: [],
|
|
151
211
|
opencode: [],
|
|
152
212
|
claudeCode: [],
|
|
213
|
+
codex: [],
|
|
153
214
|
cursorFetchedAt: null,
|
|
154
215
|
opencodeFetchedAt: null,
|
|
155
216
|
claudeCodeFetchedAt: null,
|
|
217
|
+
codexFetchedAt: null,
|
|
156
218
|
};
|
|
157
219
|
try {
|
|
158
220
|
if (fs.existsSync(cachePath)) {
|
|
@@ -161,10 +223,11 @@ export async function updateModelLists(workspaceRoot, opts = {}) {
|
|
|
161
223
|
}
|
|
162
224
|
} catch (_) {}
|
|
163
225
|
|
|
164
|
-
const [cursorRaw, opencode, claudeCodeAvailable] = await Promise.all([
|
|
226
|
+
const [cursorRaw, opencode, claudeCodeAvailable, codex] = await Promise.all([
|
|
165
227
|
runCursorModels(root),
|
|
166
228
|
runOpencodeModels(root, opencodeProvider),
|
|
167
229
|
probeClaudeCodeAvailable(),
|
|
230
|
+
runCodexModels(root),
|
|
168
231
|
]);
|
|
169
232
|
const cursor = cursorRaw.filter(isCursorModelLine);
|
|
170
233
|
const claudeCode = claudeCodeAvailable ? getBuiltinClaudeCodeModels() : [];
|
|
@@ -174,9 +237,11 @@ export async function updateModelLists(workspaceRoot, opts = {}) {
|
|
|
174
237
|
cursor: cursor.length > 0 ? cursor : prev.cursor ?? [],
|
|
175
238
|
opencode: opencode.length > 0 ? opencode : prev.opencode ?? [],
|
|
176
239
|
claudeCode: claudeCode.length > 0 ? claudeCode : prev.claudeCode ?? [],
|
|
240
|
+
codex: codex.length > 0 ? codex : prev.codex ?? [],
|
|
177
241
|
cursorFetchedAt: cursor.length > 0 ? now : prev.cursorFetchedAt ?? null,
|
|
178
242
|
opencodeFetchedAt: opencode.length > 0 ? now : prev.opencodeFetchedAt ?? null,
|
|
179
243
|
claudeCodeFetchedAt: claudeCode.length > 0 ? now : prev.claudeCodeFetchedAt ?? null,
|
|
244
|
+
codexFetchedAt: codex.length > 0 ? now : prev.codexFetchedAt ?? null,
|
|
180
245
|
};
|
|
181
246
|
|
|
182
247
|
try {
|
|
@@ -184,5 +249,5 @@ export async function updateModelLists(workspaceRoot, opts = {}) {
|
|
|
184
249
|
fs.writeFileSync(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
185
250
|
} catch (_) {}
|
|
186
251
|
|
|
187
|
-
return { cursor: data.cursor, opencode: data.opencode, claudeCode: data.claudeCode };
|
|
252
|
+
return { cursor: data.cursor, opencode: data.opencode, claudeCode: data.claudeCode, codex: data.codex };
|
|
188
253
|
}
|
package/bin/lib/node-execute.mjs
CHANGED
|
@@ -4,6 +4,8 @@ import path from "path";
|
|
|
4
4
|
import { outputNodeBasename, outputDirForNode } from "../pipeline/get-exec-id.mjs";
|
|
5
5
|
import { writeResult } from "../pipeline/write-result.mjs";
|
|
6
6
|
import {
|
|
7
|
+
runCodexAgentForNode,
|
|
8
|
+
runCodexAgentWithPrompt,
|
|
7
9
|
runClaudeCodeAgentForNode,
|
|
8
10
|
runClaudeCodeAgentWithPrompt,
|
|
9
11
|
runCursorAgentForNode,
|
|
@@ -100,7 +102,7 @@ async function healToolNodejsWithAI(workspaceRoot, flowName, uuid, instanceId, r
|
|
|
100
102
|
|
|
101
103
|
const prompt = buildHealPrompt(scriptPath, resolvedScript, errorInfo, scriptContent);
|
|
102
104
|
const healCli =
|
|
103
|
-
cli === "opencode" ? "opencode" : cli === "claude-code" ? "claude-code" : "cursor";
|
|
105
|
+
cli === "opencode" ? "opencode" : cli === "codex" ? "codex" : cli === "claude-code" ? "claude-code" : "cursor";
|
|
104
106
|
|
|
105
107
|
log.info(`[tool_nodejs AI 自愈] ${instanceId} 调用 ${healCli}${model ? ` (${model})` : ""} 修复 ${path.basename(scriptPath)}`);
|
|
106
108
|
emitEvent(workspaceRoot, flowName, uuid, {
|
|
@@ -115,6 +117,9 @@ async function healToolNodejsWithAI(workspaceRoot, flowName, uuid, instanceId, r
|
|
|
115
117
|
if (healCli === "opencode") {
|
|
116
118
|
const { finished } = runOpenCodeAgentWithPrompt(workspaceRoot, prompt, { model: model || undefined, force: true });
|
|
117
119
|
await finished;
|
|
120
|
+
} else if (healCli === "codex") {
|
|
121
|
+
const { finished } = runCodexAgentWithPrompt(workspaceRoot, prompt, { model: model || undefined, force: true });
|
|
122
|
+
await finished;
|
|
118
123
|
} else if (healCli === "claude-code") {
|
|
119
124
|
const { finished } = runClaudeCodeAgentWithPrompt(workspaceRoot, prompt, { model: model || undefined });
|
|
120
125
|
await finished;
|
|
@@ -429,6 +434,22 @@ export async function executeNode(workspaceRoot, flowName, uuid, instanceId, pre
|
|
|
429
434
|
execWorkspaceRoot,
|
|
430
435
|
},
|
|
431
436
|
);
|
|
437
|
+
} else if (cli === "codex") {
|
|
438
|
+
await runCodexAgentForNode(
|
|
439
|
+
workspaceRoot,
|
|
440
|
+
{ promptPath, nodeContext: nodeContext ?? "", taskBody: taskBody ?? "", intermediatePath, resultPathRel: resultPath, subagent, instanceId },
|
|
441
|
+
{
|
|
442
|
+
model,
|
|
443
|
+
stderrBuffer: options.stderrBuffer,
|
|
444
|
+
force: options.force,
|
|
445
|
+
outputPrefix: options.outputPrefix,
|
|
446
|
+
prefixColor: options.prefixColor,
|
|
447
|
+
onToolCall: options.onToolCall,
|
|
448
|
+
flowName,
|
|
449
|
+
uuid,
|
|
450
|
+
execWorkspaceRoot,
|
|
451
|
+
},
|
|
452
|
+
);
|
|
432
453
|
} else if (cli === "claude-code") {
|
|
433
454
|
await runClaudeCodeAgentForNode(
|
|
434
455
|
workspaceRoot,
|