@fieldwangai/agentflow 0.1.31 → 0.1.32
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 +26 -4
- package/bin/lib/marketplace.mjs +124 -2
- package/bin/lib/ui-server.mjs +18 -2
- package/builtin/web-ui/dist/assets/index-D0Tkhqr6.css +1 -0
- package/builtin/web-ui/dist/assets/index-DyhW5chp.js +197 -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
|
@@ -656,16 +656,38 @@ function resolveMarkdownNodeFile(workspaceRoot, nodeId, flowId, flowSource, opts
|
|
|
656
656
|
|
|
657
657
|
function readNodeUsage(workspaceRoot, nodeId, opts = {}) {
|
|
658
658
|
const usage = [];
|
|
659
|
+
const marketSpec = parseMarketplaceDefinitionId(nodeId);
|
|
659
660
|
for (const flow of listFlowsJson(workspaceRoot, opts)) {
|
|
660
661
|
const flowPath = getFlowYamlAbs(workspaceRoot, flow.id, flow.source || "user", { archived: Boolean(flow.archived), userId: opts.userId });
|
|
661
662
|
if (!flowPath.path) continue;
|
|
662
663
|
try {
|
|
663
664
|
const data = yaml.load(fs.readFileSync(flowPath.path, "utf-8"));
|
|
664
665
|
const instances = data && typeof data === "object" ? data.instances : null;
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
666
|
+
const hits = [];
|
|
667
|
+
if (marketSpec) {
|
|
668
|
+
const deps = data && typeof data === "object" && data.dependencies && typeof data.dependencies === "object" ? data.dependencies : {};
|
|
669
|
+
const nodeDeps = Array.isArray(deps.nodes) ? deps.nodes : [];
|
|
670
|
+
if (nodeDeps.some((dep) => {
|
|
671
|
+
const parsed = typeof dep === "string"
|
|
672
|
+
? parseMarketplaceDefinitionId(dep.startsWith("marketplace:") ? dep : `marketplace:${dep}`)
|
|
673
|
+
: dep && typeof dep === "object"
|
|
674
|
+
? { id: dep.id, version: dep.version != null ? String(dep.version) : null }
|
|
675
|
+
: null;
|
|
676
|
+
return parsed && parsed.id === marketSpec.id && (!parsed.version || !marketSpec.version || parsed.version === marketSpec.version);
|
|
677
|
+
})) {
|
|
678
|
+
hits.push({ instanceId: "dependencies.nodes", label: "dependency" });
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (instances && typeof instances === "object") {
|
|
682
|
+
hits.push(...Object.entries(instances)
|
|
683
|
+
.filter(([, inst]) => {
|
|
684
|
+
if (!inst) return false;
|
|
685
|
+
if (!marketSpec) return inst.definitionId === nodeId;
|
|
686
|
+
const parsed = parseMarketplaceDefinitionId(inst.definitionId);
|
|
687
|
+
return parsed && parsed.id === marketSpec.id && (!parsed.version || !marketSpec.version || parsed.version === marketSpec.version);
|
|
688
|
+
})
|
|
689
|
+
.map(([instanceId, inst]) => ({ instanceId, label: inst.label || instanceId })));
|
|
690
|
+
}
|
|
669
691
|
if (hits.length > 0) {
|
|
670
692
|
usage.push({ flowId: flow.id, flowSource: flow.source || "user", archived: Boolean(flow.archived), instances: hits });
|
|
671
693
|
}
|
package/bin/lib/marketplace.mjs
CHANGED
|
@@ -2,7 +2,13 @@ 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";
|
|
@@ -100,6 +106,99 @@ function listVersionDirs(baseDir) {
|
|
|
100
106
|
.filter(Boolean);
|
|
101
107
|
}
|
|
102
108
|
|
|
109
|
+
function isSafePathSegment(value) {
|
|
110
|
+
const text = String(value || "").trim();
|
|
111
|
+
return Boolean(text) && !text.includes("\0") && !path.isAbsolute(text) && !text.split(/[\\/]+/).includes("..");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function resolveWorkspaceNodePackageDir(workspaceRoot, id, version) {
|
|
115
|
+
if (!isSafePathSegment(id) || !isSafePathSegment(version)) return null;
|
|
116
|
+
const base = path.resolve(workspacePackageRoot(workspaceRoot), "nodes");
|
|
117
|
+
const target = path.resolve(base, id, version);
|
|
118
|
+
if (target !== base && !target.startsWith(base + path.sep)) return null;
|
|
119
|
+
return target;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function collectFlowDirs(rootDir, source, archived = false) {
|
|
123
|
+
const out = [];
|
|
124
|
+
if (!fs.existsSync(rootDir)) return out;
|
|
125
|
+
let entries = [];
|
|
126
|
+
try {
|
|
127
|
+
entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
128
|
+
} catch {
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
for (const entry of entries) {
|
|
132
|
+
if (!entry.isDirectory() || entry.name === ARCHIVED_PIPELINES_DIR_NAME) continue;
|
|
133
|
+
const dir = path.join(rootDir, entry.name);
|
|
134
|
+
if (!fs.existsSync(path.join(dir, "flow.yaml"))) continue;
|
|
135
|
+
out.push({ flowId: entry.name, flowSource: source, archived, flowDir: dir });
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function listWritableFlowDirs(workspaceRoot, opts = {}) {
|
|
141
|
+
const root = path.resolve(workspaceRoot);
|
|
142
|
+
const userRoot = getUserPipelinesRoot(opts.userId);
|
|
143
|
+
const wsRoot = path.join(root, PIPELINES_DIR);
|
|
144
|
+
const legacyRoot = path.join(root, LEGACY_PIPELINES_DIR);
|
|
145
|
+
return [
|
|
146
|
+
...collectFlowDirs(userRoot, "user", false),
|
|
147
|
+
...collectFlowDirs(path.join(userRoot, ARCHIVED_PIPELINES_DIR_NAME), "user", true),
|
|
148
|
+
...collectFlowDirs(wsRoot, "workspace", false),
|
|
149
|
+
...collectFlowDirs(path.join(wsRoot, ARCHIVED_PIPELINES_DIR_NAME), "workspace", true),
|
|
150
|
+
...collectFlowDirs(legacyRoot, "workspace", false),
|
|
151
|
+
...collectFlowDirs(path.join(legacyRoot, ARCHIVED_PIPELINES_DIR_NAME), "workspace", true),
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function depMatchesNode(dep, id, version) {
|
|
156
|
+
if (typeof dep === "string") {
|
|
157
|
+
const parsed = parseMarketplaceDefinitionId(dep.startsWith("marketplace:") ? dep : `marketplace:${dep}`);
|
|
158
|
+
return Boolean(parsed && parsed.id === id && (!parsed.version || parsed.version === version));
|
|
159
|
+
}
|
|
160
|
+
if (!dep || typeof dep !== "object") return false;
|
|
161
|
+
return dep.id === id && (dep.version == null || String(dep.version) === version);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function instanceMatchesNode(inst, id, version) {
|
|
165
|
+
const parsed = parseMarketplaceDefinitionId(inst?.definitionId);
|
|
166
|
+
return Boolean(parsed && parsed.id === id && (!parsed.version || parsed.version === version));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function listMarketplaceNodeUsages(workspaceRoot, id, version, opts = {}) {
|
|
170
|
+
const usages = [];
|
|
171
|
+
if (!id || !version) return usages;
|
|
172
|
+
for (const flow of listWritableFlowDirs(workspaceRoot, opts)) {
|
|
173
|
+
const flowYamlPath = path.join(flow.flowDir, "flow.yaml");
|
|
174
|
+
const data = readYamlObject(flowYamlPath);
|
|
175
|
+
if (!data) continue;
|
|
176
|
+
const hits = [];
|
|
177
|
+
const deps = data.dependencies && typeof data.dependencies === "object" ? data.dependencies : {};
|
|
178
|
+
const nodeDeps = Array.isArray(deps.nodes) ? deps.nodes : [];
|
|
179
|
+
for (const dep of nodeDeps) {
|
|
180
|
+
if (depMatchesNode(dep, id, version)) {
|
|
181
|
+
hits.push({ instanceId: "dependencies.nodes", label: "dependency" });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const instances = data.instances && typeof data.instances === "object" ? data.instances : {};
|
|
185
|
+
for (const [instanceId, inst] of Object.entries(instances)) {
|
|
186
|
+
if (instanceMatchesNode(inst, id, version)) {
|
|
187
|
+
hits.push({ instanceId, label: inst?.label || instanceId });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (hits.length > 0) {
|
|
191
|
+
usages.push({
|
|
192
|
+
flowId: flow.flowId,
|
|
193
|
+
flowSource: flow.flowSource,
|
|
194
|
+
archived: flow.archived,
|
|
195
|
+
instances: hits,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return usages;
|
|
200
|
+
}
|
|
201
|
+
|
|
103
202
|
function findNodePackageDir(workspaceRoot, id, version) {
|
|
104
203
|
const root = workspacePackageRoot(workspaceRoot);
|
|
105
204
|
const nodeBase = path.join(root, "nodes", id);
|
|
@@ -230,7 +329,7 @@ export function listMarketplaceNodes(workspaceRoot, flowData = null) {
|
|
|
230
329
|
return out.sort((a, b) => a.id.localeCompare(b.id) || a.version.localeCompare(b.version));
|
|
231
330
|
}
|
|
232
331
|
|
|
233
|
-
export function listMarketplacePackages(workspaceRoot) {
|
|
332
|
+
export function listMarketplacePackages(workspaceRoot, opts = {}) {
|
|
234
333
|
const root = workspacePackageRoot(workspaceRoot);
|
|
235
334
|
const nodes = listMarketplaceNodes(workspaceRoot).map((n) => ({
|
|
236
335
|
id: n.id,
|
|
@@ -242,6 +341,7 @@ export function listMarketplacePackages(workspaceRoot) {
|
|
|
242
341
|
outputs: n.output,
|
|
243
342
|
packagedFiles: Array.isArray(n.packagedFiles) ? n.packagedFiles : [],
|
|
244
343
|
packageDir: n.packageDir,
|
|
344
|
+
usage: listMarketplaceNodeUsages(workspaceRoot, n.id, n.version, opts),
|
|
245
345
|
}));
|
|
246
346
|
const collections = [];
|
|
247
347
|
const collectionsRoot = path.join(root, "collections");
|
|
@@ -265,6 +365,28 @@ export function listMarketplacePackages(workspaceRoot) {
|
|
|
265
365
|
return { nodes, collections };
|
|
266
366
|
}
|
|
267
367
|
|
|
368
|
+
export function deleteMarketplaceNodePackage(workspaceRoot, id, version, opts = {}) {
|
|
369
|
+
const packageDir = resolveWorkspaceNodePackageDir(workspaceRoot, id, version);
|
|
370
|
+
if (!packageDir) return { ok: false, error: "Invalid marketplace node id or version" };
|
|
371
|
+
if (!fs.existsSync(path.join(packageDir, NODE_MANIFEST))) {
|
|
372
|
+
return { ok: false, error: `Marketplace node package not found: ${id}@${version}` };
|
|
373
|
+
}
|
|
374
|
+
const usage = listMarketplaceNodeUsages(workspaceRoot, id, version, opts);
|
|
375
|
+
if (usage.length > 0) {
|
|
376
|
+
return { ok: false, error: "Marketplace node is still used by flows", usage };
|
|
377
|
+
}
|
|
378
|
+
fs.rmSync(packageDir, { recursive: true, force: true });
|
|
379
|
+
const versionRoot = path.dirname(packageDir);
|
|
380
|
+
try {
|
|
381
|
+
if (fs.existsSync(versionRoot) && fs.readdirSync(versionRoot).length === 0) {
|
|
382
|
+
fs.rmdirSync(versionRoot);
|
|
383
|
+
}
|
|
384
|
+
} catch {
|
|
385
|
+
/* keep non-empty or unreadable parent */
|
|
386
|
+
}
|
|
387
|
+
return { ok: true, id, version, packageDir };
|
|
388
|
+
}
|
|
389
|
+
|
|
268
390
|
export function writeFlowMarketplaceLock(workspaceRoot, flowDir, flowData) {
|
|
269
391
|
if (!flowData || !flowData.instances || typeof flowData.instances !== "object") return null;
|
|
270
392
|
const nodes = {};
|
package/bin/lib/ui-server.mjs
CHANGED
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
import { runNodeScript } from "./pipeline-scripts.mjs";
|
|
75
75
|
import { readFlowSchedule, writeFlowSchedule } from "./schedule-config.mjs";
|
|
76
76
|
import { listScheduleStatuses } from "./scheduler.mjs";
|
|
77
|
-
import { installFlowDependency, listMarketplacePackages, publishNodeFromInstance } from "./marketplace.mjs";
|
|
77
|
+
import { deleteMarketplaceNodePackage, installFlowDependency, listMarketplacePackages, publishNodeFromInstance } from "./marketplace.mjs";
|
|
78
78
|
import {
|
|
79
79
|
authSetupRequired,
|
|
80
80
|
buildClearSessionCookie,
|
|
@@ -2280,13 +2280,29 @@ export function startUiServer({
|
|
|
2280
2280
|
|
|
2281
2281
|
if (req.method === "GET" && url.pathname === "/api/marketplace/nodes") {
|
|
2282
2282
|
try {
|
|
2283
|
-
json(res, 200, listMarketplacePackages(root));
|
|
2283
|
+
json(res, 200, listMarketplacePackages(root, userCtx));
|
|
2284
2284
|
} catch (e) {
|
|
2285
2285
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
2286
2286
|
}
|
|
2287
2287
|
return;
|
|
2288
2288
|
}
|
|
2289
2289
|
|
|
2290
|
+
if (req.method === "DELETE" && url.pathname === "/api/marketplace/node") {
|
|
2291
|
+
const id = url.searchParams.get("id") || "";
|
|
2292
|
+
const version = url.searchParams.get("version") || "";
|
|
2293
|
+
if (!id || !version) {
|
|
2294
|
+
json(res, 400, { ok: false, error: "Missing marketplace node id or version" });
|
|
2295
|
+
return;
|
|
2296
|
+
}
|
|
2297
|
+
try {
|
|
2298
|
+
const result = deleteMarketplaceNodePackage(root, id, version, userCtx);
|
|
2299
|
+
json(res, result.ok ? 200 : 400, result);
|
|
2300
|
+
} catch (e) {
|
|
2301
|
+
json(res, 500, { ok: false, error: (e && e.message) || String(e) });
|
|
2302
|
+
}
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2290
2306
|
if (req.method === "POST" && url.pathname === "/api/marketplace/install-node") {
|
|
2291
2307
|
let payload;
|
|
2292
2308
|
try {
|