@fieldwangai/agentflow 0.1.31 → 0.1.33
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/bin/lib/catalog-flows.mjs +33 -6
- package/bin/lib/composer-node-schema.mjs +7 -7
- package/bin/lib/composer-planner.mjs +5 -5
- package/bin/lib/git-worktree.mjs +248 -0
- package/bin/lib/gitlab-mr.mjs +174 -0
- package/bin/lib/locales/en.json +24 -0
- package/bin/lib/locales/zh.json +24 -0
- package/bin/lib/marketplace.mjs +230 -4
- package/bin/lib/paths.mjs +5 -0
- package/bin/lib/ui-server.mjs +298 -14
- package/bin/pipeline/pre-process-node.mjs +152 -11
- package/bin/pipeline/validate-flow.mjs +7 -17
- package/builtin/nodes/display_html.md +31 -0
- package/builtin/nodes/display_image.md +35 -0
- package/builtin/nodes/provide_bool.md +11 -0
- package/builtin/nodes/tool_git_checkout.md +8 -1
- package/builtin/nodes/tool_git_worktree_load.md +54 -0
- package/builtin/nodes/tool_git_worktree_unload.md +51 -0
- package/builtin/nodes/tool_gitlab_create_mr.md +113 -0
- package/builtin/web-ui/dist/assets/index-BWAb27N0.js +198 -0
- package/builtin/web-ui/dist/assets/index-DgfSfcjH.css +1 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/builtin/web-ui/dist/assets/index-BVWwQpvg.css +0 -1
- package/builtin/web-ui/dist/assets/index-CvNy1n3f.js +0 -197
package/bin/lib/locales/zh.json
CHANGED
|
@@ -281,6 +281,18 @@
|
|
|
281
281
|
"displayName": "Git 拉取",
|
|
282
282
|
"description": "克隆或更新 Git 仓库,并输出可供下游使用的工作区上下文"
|
|
283
283
|
},
|
|
284
|
+
"tool_git_worktree_load": {
|
|
285
|
+
"displayName": "加载 Worktree",
|
|
286
|
+
"description": "创建或复用 Git worktree,并输出 workspaceContext 与 gitContext"
|
|
287
|
+
},
|
|
288
|
+
"tool_git_worktree_unload": {
|
|
289
|
+
"displayName": "卸载 Worktree",
|
|
290
|
+
"description": "移除 Git worktree,并恢复下游工作区上下文"
|
|
291
|
+
},
|
|
292
|
+
"tool_gitlab_create_mr": {
|
|
293
|
+
"displayName": "提交 GitLab MR",
|
|
294
|
+
"description": "为当前分支创建或复用 GitLab Merge Request,并输出 MR 链接"
|
|
295
|
+
},
|
|
284
296
|
"tool_nodejs": {
|
|
285
297
|
"displayName": "Node.js 脚本",
|
|
286
298
|
"description": "执行 Node.js 脚本,以 exit code 判断成败,stdout 作为结果"
|
|
@@ -313,6 +325,14 @@
|
|
|
313
325
|
"displayName": "ASCII 图展示",
|
|
314
326
|
"description": "在 Workspace 画布中渲染等宽 ASCII 图,并将文本继续传给下游"
|
|
315
327
|
},
|
|
328
|
+
"display_html": {
|
|
329
|
+
"displayName": "HTML 展示",
|
|
330
|
+
"description": "在 Workspace 画布中用 sandbox iframe 渲染 HTML,并将源码继续传给下游"
|
|
331
|
+
},
|
|
332
|
+
"display_image": {
|
|
333
|
+
"displayName": "图片展示",
|
|
334
|
+
"description": "在 Workspace 画布中预览图片 URL、data URL 或图片路径,并将来源继续传给下游"
|
|
335
|
+
},
|
|
316
336
|
"provide_str": {
|
|
317
337
|
"displayName": "文本",
|
|
318
338
|
"description": "直接提供一段文本,value 会原样供下游引用"
|
|
@@ -320,6 +340,10 @@
|
|
|
320
340
|
"provide_file": {
|
|
321
341
|
"displayName": "文件",
|
|
322
342
|
"description": "直接提供文件路径或内容,value 会原样供下游引用"
|
|
343
|
+
},
|
|
344
|
+
"provide_bool": {
|
|
345
|
+
"displayName": "布尔",
|
|
346
|
+
"description": "直接提供 true/false 布尔值,value 会供下游 bool 引脚引用"
|
|
323
347
|
}
|
|
324
348
|
},
|
|
325
349
|
"pipeline": {
|
package/bin/lib/marketplace.mjs
CHANGED
|
@@ -2,10 +2,17 @@ import fs from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import yaml from "js-yaml";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ARCHIVED_PIPELINES_DIR_NAME,
|
|
7
|
+
LEGACY_PIPELINES_DIR,
|
|
8
|
+
MARKETPLACE_PACKAGES_DIR,
|
|
9
|
+
PIPELINES_DIR,
|
|
10
|
+
getUserPipelinesRoot,
|
|
11
|
+
} from "./paths.mjs";
|
|
6
12
|
|
|
7
13
|
const NODE_MANIFEST = "node.yaml";
|
|
8
14
|
const COLLECTION_MANIFEST = "collection.yaml";
|
|
15
|
+
const FLOW_SNIPPET_MANIFEST = "flow-snippet.yaml";
|
|
9
16
|
const LOCK_FILENAME = "agentflow.lock.json";
|
|
10
17
|
|
|
11
18
|
function workspacePackageRoot(workspaceRoot) {
|
|
@@ -55,15 +62,20 @@ function readJsonObject(filePath) {
|
|
|
55
62
|
function normalizeSlotList(value) {
|
|
56
63
|
if (!Array.isArray(value)) return [];
|
|
57
64
|
return value.map((slot) => {
|
|
58
|
-
if (!slot || typeof slot !== "object") return { type: "text", name: "", default: "" };
|
|
65
|
+
if (!slot || typeof slot !== "object") return { type: "text", name: "", default: "", showOnNode: false };
|
|
59
66
|
const type = slot.type != null ? String(slot.type).trim() : "text";
|
|
60
67
|
const name = slot.name != null ? String(slot.name).trim() : "";
|
|
61
68
|
const def = slot.default !== undefined ? slot.default : slot.value;
|
|
62
|
-
|
|
69
|
+
const normalized = {
|
|
63
70
|
type,
|
|
64
71
|
name,
|
|
65
72
|
default: def == null ? "" : String(def),
|
|
66
73
|
};
|
|
74
|
+
if (slot.required != null) normalized.required = Boolean(slot.required);
|
|
75
|
+
normalized.showOnNode = slot.showOnNode != null
|
|
76
|
+
? Boolean(slot.showOnNode)
|
|
77
|
+
: Boolean(normalized.required) || type.toLowerCase() === "node";
|
|
78
|
+
return normalized;
|
|
67
79
|
});
|
|
68
80
|
}
|
|
69
81
|
|
|
@@ -100,6 +112,107 @@ function listVersionDirs(baseDir) {
|
|
|
100
112
|
.filter(Boolean);
|
|
101
113
|
}
|
|
102
114
|
|
|
115
|
+
function isSafePathSegment(value) {
|
|
116
|
+
const text = String(value || "").trim();
|
|
117
|
+
return Boolean(text) && !text.includes("\0") && !path.isAbsolute(text) && !text.split(/[\\/]+/).includes("..");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function resolveWorkspaceNodePackageDir(workspaceRoot, id, version) {
|
|
121
|
+
if (!isSafePathSegment(id) || !isSafePathSegment(version)) return null;
|
|
122
|
+
const base = path.resolve(workspacePackageRoot(workspaceRoot), "nodes");
|
|
123
|
+
const target = path.resolve(base, id, version);
|
|
124
|
+
if (target !== base && !target.startsWith(base + path.sep)) return null;
|
|
125
|
+
return target;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function resolveWorkspaceFlowSnippetPackageDir(workspaceRoot, id, version) {
|
|
129
|
+
if (!isSafePathSegment(id) || !isSafePathSegment(version)) return null;
|
|
130
|
+
const base = path.resolve(workspacePackageRoot(workspaceRoot), "flow-snippets");
|
|
131
|
+
const target = path.resolve(base, id, version);
|
|
132
|
+
if (target !== base && !target.startsWith(base + path.sep)) return null;
|
|
133
|
+
return target;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function collectFlowDirs(rootDir, source, archived = false) {
|
|
137
|
+
const out = [];
|
|
138
|
+
if (!fs.existsSync(rootDir)) return out;
|
|
139
|
+
let entries = [];
|
|
140
|
+
try {
|
|
141
|
+
entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
142
|
+
} catch {
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
for (const entry of entries) {
|
|
146
|
+
if (!entry.isDirectory() || entry.name === ARCHIVED_PIPELINES_DIR_NAME) continue;
|
|
147
|
+
const dir = path.join(rootDir, entry.name);
|
|
148
|
+
if (!fs.existsSync(path.join(dir, "flow.yaml"))) continue;
|
|
149
|
+
out.push({ flowId: entry.name, flowSource: source, archived, flowDir: dir });
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function listWritableFlowDirs(workspaceRoot, opts = {}) {
|
|
155
|
+
const root = path.resolve(workspaceRoot);
|
|
156
|
+
const userRoot = getUserPipelinesRoot(opts.userId);
|
|
157
|
+
const wsRoot = path.join(root, PIPELINES_DIR);
|
|
158
|
+
const legacyRoot = path.join(root, LEGACY_PIPELINES_DIR);
|
|
159
|
+
return [
|
|
160
|
+
...collectFlowDirs(userRoot, "user", false),
|
|
161
|
+
...collectFlowDirs(path.join(userRoot, ARCHIVED_PIPELINES_DIR_NAME), "user", true),
|
|
162
|
+
...collectFlowDirs(wsRoot, "workspace", false),
|
|
163
|
+
...collectFlowDirs(path.join(wsRoot, ARCHIVED_PIPELINES_DIR_NAME), "workspace", true),
|
|
164
|
+
...collectFlowDirs(legacyRoot, "workspace", false),
|
|
165
|
+
...collectFlowDirs(path.join(legacyRoot, ARCHIVED_PIPELINES_DIR_NAME), "workspace", true),
|
|
166
|
+
];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function depMatchesNode(dep, id, version) {
|
|
170
|
+
if (typeof dep === "string") {
|
|
171
|
+
const parsed = parseMarketplaceDefinitionId(dep.startsWith("marketplace:") ? dep : `marketplace:${dep}`);
|
|
172
|
+
return Boolean(parsed && parsed.id === id && (!parsed.version || parsed.version === version));
|
|
173
|
+
}
|
|
174
|
+
if (!dep || typeof dep !== "object") return false;
|
|
175
|
+
return dep.id === id && (dep.version == null || String(dep.version) === version);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function instanceMatchesNode(inst, id, version) {
|
|
179
|
+
const parsed = parseMarketplaceDefinitionId(inst?.definitionId);
|
|
180
|
+
return Boolean(parsed && parsed.id === id && (!parsed.version || parsed.version === version));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function listMarketplaceNodeUsages(workspaceRoot, id, version, opts = {}) {
|
|
184
|
+
const usages = [];
|
|
185
|
+
if (!id || !version) return usages;
|
|
186
|
+
for (const flow of listWritableFlowDirs(workspaceRoot, opts)) {
|
|
187
|
+
const flowYamlPath = path.join(flow.flowDir, "flow.yaml");
|
|
188
|
+
const data = readYamlObject(flowYamlPath);
|
|
189
|
+
if (!data) continue;
|
|
190
|
+
const hits = [];
|
|
191
|
+
const deps = data.dependencies && typeof data.dependencies === "object" ? data.dependencies : {};
|
|
192
|
+
const nodeDeps = Array.isArray(deps.nodes) ? deps.nodes : [];
|
|
193
|
+
for (const dep of nodeDeps) {
|
|
194
|
+
if (depMatchesNode(dep, id, version)) {
|
|
195
|
+
hits.push({ instanceId: "dependencies.nodes", label: "dependency" });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const instances = data.instances && typeof data.instances === "object" ? data.instances : {};
|
|
199
|
+
for (const [instanceId, inst] of Object.entries(instances)) {
|
|
200
|
+
if (instanceMatchesNode(inst, id, version)) {
|
|
201
|
+
hits.push({ instanceId, label: inst?.label || instanceId });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (hits.length > 0) {
|
|
205
|
+
usages.push({
|
|
206
|
+
flowId: flow.flowId,
|
|
207
|
+
flowSource: flow.flowSource,
|
|
208
|
+
archived: flow.archived,
|
|
209
|
+
instances: hits,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return usages;
|
|
214
|
+
}
|
|
215
|
+
|
|
103
216
|
function findNodePackageDir(workspaceRoot, id, version) {
|
|
104
217
|
const root = workspacePackageRoot(workspaceRoot);
|
|
105
218
|
const nodeBase = path.join(root, "nodes", id);
|
|
@@ -230,7 +343,7 @@ export function listMarketplaceNodes(workspaceRoot, flowData = null) {
|
|
|
230
343
|
return out.sort((a, b) => a.id.localeCompare(b.id) || a.version.localeCompare(b.version));
|
|
231
344
|
}
|
|
232
345
|
|
|
233
|
-
export function listMarketplacePackages(workspaceRoot) {
|
|
346
|
+
export function listMarketplacePackages(workspaceRoot, opts = {}) {
|
|
234
347
|
const root = workspacePackageRoot(workspaceRoot);
|
|
235
348
|
const nodes = listMarketplaceNodes(workspaceRoot).map((n) => ({
|
|
236
349
|
id: n.id,
|
|
@@ -242,6 +355,7 @@ export function listMarketplacePackages(workspaceRoot) {
|
|
|
242
355
|
outputs: n.output,
|
|
243
356
|
packagedFiles: Array.isArray(n.packagedFiles) ? n.packagedFiles : [],
|
|
244
357
|
packageDir: n.packageDir,
|
|
358
|
+
usage: listMarketplaceNodeUsages(workspaceRoot, n.id, n.version, opts),
|
|
245
359
|
}));
|
|
246
360
|
const collections = [];
|
|
247
361
|
const collectionsRoot = path.join(root, "collections");
|
|
@@ -265,6 +379,63 @@ export function listMarketplacePackages(workspaceRoot) {
|
|
|
265
379
|
return { nodes, collections };
|
|
266
380
|
}
|
|
267
381
|
|
|
382
|
+
export function listMarketplaceFlowSnippets(workspaceRoot) {
|
|
383
|
+
const root = workspacePackageRoot(workspaceRoot);
|
|
384
|
+
const snippetsRoot = path.join(root, "flow-snippets");
|
|
385
|
+
const snippets = [];
|
|
386
|
+
if (!fs.existsSync(snippetsRoot)) return { snippets };
|
|
387
|
+
for (const entry of fs.readdirSync(snippetsRoot, { withFileTypes: true })) {
|
|
388
|
+
if (!entry.isDirectory()) continue;
|
|
389
|
+
const base = path.join(snippetsRoot, entry.name);
|
|
390
|
+
for (const version of listVersionDirs(base)) {
|
|
391
|
+
const dir = path.join(base, version);
|
|
392
|
+
const manifest = readYamlObject(path.join(dir, FLOW_SNIPPET_MANIFEST));
|
|
393
|
+
if (!manifest) continue;
|
|
394
|
+
const snippet = manifest.snippet && typeof manifest.snippet === "object" ? manifest.snippet : {};
|
|
395
|
+
snippets.push({
|
|
396
|
+
id: manifest.id || entry.name,
|
|
397
|
+
version: manifest.version || version,
|
|
398
|
+
displayName: manifest.displayName || manifest.name || entry.name,
|
|
399
|
+
description: manifest.description || "",
|
|
400
|
+
tags: Array.isArray(manifest.tags) ? manifest.tags.map((x) => String(x)) : [],
|
|
401
|
+
nodeCount: Number(manifest.nodeCount) || Object.keys(snippet.instances || {}).length || 0,
|
|
402
|
+
edgeCount: Number(manifest.edgeCount) || (Array.isArray(snippet.edges) ? snippet.edges.length : 0),
|
|
403
|
+
createdAt: manifest.createdAt || "",
|
|
404
|
+
updatedAt: manifest.updatedAt || "",
|
|
405
|
+
packageDir: dir,
|
|
406
|
+
snippet,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
snippets.sort((a, b) => {
|
|
411
|
+
const byTime = String(b.updatedAt || b.createdAt || "").localeCompare(String(a.updatedAt || a.createdAt || ""));
|
|
412
|
+
return byTime || a.id.localeCompare(b.id) || a.version.localeCompare(b.version);
|
|
413
|
+
});
|
|
414
|
+
return { snippets };
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function deleteMarketplaceNodePackage(workspaceRoot, id, version, opts = {}) {
|
|
418
|
+
const packageDir = resolveWorkspaceNodePackageDir(workspaceRoot, id, version);
|
|
419
|
+
if (!packageDir) return { ok: false, error: "Invalid marketplace node id or version" };
|
|
420
|
+
if (!fs.existsSync(path.join(packageDir, NODE_MANIFEST))) {
|
|
421
|
+
return { ok: false, error: `Marketplace node package not found: ${id}@${version}` };
|
|
422
|
+
}
|
|
423
|
+
const usage = listMarketplaceNodeUsages(workspaceRoot, id, version, opts);
|
|
424
|
+
if (usage.length > 0) {
|
|
425
|
+
return { ok: false, error: "Marketplace node is still used by flows", usage };
|
|
426
|
+
}
|
|
427
|
+
fs.rmSync(packageDir, { recursive: true, force: true });
|
|
428
|
+
const versionRoot = path.dirname(packageDir);
|
|
429
|
+
try {
|
|
430
|
+
if (fs.existsSync(versionRoot) && fs.readdirSync(versionRoot).length === 0) {
|
|
431
|
+
fs.rmdirSync(versionRoot);
|
|
432
|
+
}
|
|
433
|
+
} catch {
|
|
434
|
+
/* keep non-empty or unreadable parent */
|
|
435
|
+
}
|
|
436
|
+
return { ok: true, id, version, packageDir };
|
|
437
|
+
}
|
|
438
|
+
|
|
268
439
|
export function writeFlowMarketplaceLock(workspaceRoot, flowDir, flowData) {
|
|
269
440
|
if (!flowData || !flowData.instances || typeof flowData.instances !== "object") return null;
|
|
270
441
|
const nodes = {};
|
|
@@ -465,11 +636,15 @@ export function publishNodeFromInstance(workspaceRoot, payload = {}, options = {
|
|
|
465
636
|
type: slot.type,
|
|
466
637
|
name: slot.name,
|
|
467
638
|
default: slot.default,
|
|
639
|
+
...(slot.required != null ? { required: Boolean(slot.required) } : {}),
|
|
640
|
+
...(slot.showOnNode != null ? { showOnNode: Boolean(slot.showOnNode) } : {}),
|
|
468
641
|
}));
|
|
469
642
|
const outputs = normalizeSlotList(payload.outputs || payload.output).map((slot) => ({
|
|
470
643
|
type: slot.type,
|
|
471
644
|
name: slot.name,
|
|
472
645
|
default: slot.default,
|
|
646
|
+
...(slot.required != null ? { required: Boolean(slot.required) } : {}),
|
|
647
|
+
...(slot.showOnNode != null ? { showOnNode: Boolean(slot.showOnNode) } : {}),
|
|
473
648
|
}));
|
|
474
649
|
const script = String(payload.script || "").trim();
|
|
475
650
|
const body = String(payload.body || "").trim();
|
|
@@ -522,6 +697,57 @@ export function publishNodeFromInstance(workspaceRoot, payload = {}, options = {
|
|
|
522
697
|
};
|
|
523
698
|
}
|
|
524
699
|
|
|
700
|
+
export function publishFlowSnippet(workspaceRoot, payload = {}) {
|
|
701
|
+
const label = String(payload.displayName || payload.name || payload.id || "flow snippet").trim();
|
|
702
|
+
const id = safePackageId(payload.id || payload.packageId || label);
|
|
703
|
+
const version = normalizeVersion(payload.version || "1.0.0");
|
|
704
|
+
if (!id) return { ok: false, error: "Invalid snippet id" };
|
|
705
|
+
|
|
706
|
+
const rawSnippet = payload.snippet && typeof payload.snippet === "object" ? payload.snippet : {};
|
|
707
|
+
const instances = rawSnippet.instances && typeof rawSnippet.instances === "object" ? rawSnippet.instances : {};
|
|
708
|
+
const edges = Array.isArray(rawSnippet.edges) ? rawSnippet.edges : [];
|
|
709
|
+
const ui = rawSnippet.ui && typeof rawSnippet.ui === "object" ? rawSnippet.ui : {};
|
|
710
|
+
const nodeCount = Object.keys(instances).length;
|
|
711
|
+
if (nodeCount < 2) return { ok: false, error: "A flow snippet needs at least two nodes" };
|
|
712
|
+
|
|
713
|
+
const now = new Date().toISOString();
|
|
714
|
+
const dest = resolveWorkspaceFlowSnippetPackageDir(workspaceRoot, id, version);
|
|
715
|
+
if (!dest) return { ok: false, error: "Invalid snippet id or version" };
|
|
716
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
717
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
718
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
719
|
+
|
|
720
|
+
const manifest = {
|
|
721
|
+
id,
|
|
722
|
+
version,
|
|
723
|
+
name: label,
|
|
724
|
+
displayName: label,
|
|
725
|
+
description: String(payload.description || "").trim(),
|
|
726
|
+
tags: Array.isArray(payload.tags) ? payload.tags.map((x) => String(x).trim()).filter(Boolean) : [],
|
|
727
|
+
nodeCount,
|
|
728
|
+
edgeCount: edges.length,
|
|
729
|
+
createdAt: now,
|
|
730
|
+
updatedAt: now,
|
|
731
|
+
snippet: {
|
|
732
|
+
instances,
|
|
733
|
+
edges: edges.map((edge) => ({
|
|
734
|
+
source: String(edge.source || ""),
|
|
735
|
+
target: String(edge.target || ""),
|
|
736
|
+
sourceHandle: edge.sourceHandle ?? null,
|
|
737
|
+
targetHandle: edge.targetHandle ?? null,
|
|
738
|
+
})).filter((edge) => edge.source && edge.target),
|
|
739
|
+
ui,
|
|
740
|
+
},
|
|
741
|
+
};
|
|
742
|
+
fs.writeFileSync(path.join(dest, FLOW_SNIPPET_MANIFEST), yaml.dump(manifest, { lineWidth: -1 }), "utf-8");
|
|
743
|
+
fs.writeFileSync(
|
|
744
|
+
path.join(dest, "README.md"),
|
|
745
|
+
`# ${label}\n\n${manifest.description || "Published from an AgentFlow canvas selection."}\n`,
|
|
746
|
+
"utf-8",
|
|
747
|
+
);
|
|
748
|
+
return { ok: true, id, version, packageDir: dest, snippet: manifest.snippet };
|
|
749
|
+
}
|
|
750
|
+
|
|
525
751
|
export function installFlowDependency(workspaceRoot, flowDir, spec) {
|
|
526
752
|
const parsed = parseMarketplaceDefinitionId(spec.startsWith("marketplace:") ? spec : `marketplace:${spec}`);
|
|
527
753
|
if (!parsed) return { ok: false, error: `Invalid marketplace node spec: ${spec}` };
|
package/bin/lib/paths.mjs
CHANGED
|
@@ -262,11 +262,15 @@ export const LOCAL_ONLY_DEFINITION_IDS = new Set([
|
|
|
262
262
|
"control_start",
|
|
263
263
|
"control_end",
|
|
264
264
|
"tool_git_checkout",
|
|
265
|
+
"tool_git_worktree_load",
|
|
266
|
+
"tool_git_worktree_unload",
|
|
267
|
+
"tool_gitlab_create_mr",
|
|
265
268
|
"tool_print",
|
|
266
269
|
"tool_user_check",
|
|
267
270
|
"tool_user_ask",
|
|
268
271
|
"provide_str",
|
|
269
272
|
"provide_file",
|
|
273
|
+
"provide_bool",
|
|
270
274
|
]);
|
|
271
275
|
|
|
272
276
|
/** 仅 pre+post 且由 CLI 负责写终态的节点 */
|
|
@@ -276,4 +280,5 @@ export const LOCAL_ONLY_TERMINAL_SUCCESS_IDS = new Set([
|
|
|
276
280
|
"tool_print",
|
|
277
281
|
"provide_str",
|
|
278
282
|
"provide_file",
|
|
283
|
+
"provide_bool",
|
|
279
284
|
]);
|