@fieldwangai/agentflow 0.1.53 → 0.1.55

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.
@@ -274,7 +274,7 @@ export function listNodesJson(workspaceRoot, flowId, flowSource, opts = {}) {
274
274
  addFromDir(PACKAGE_BUILTIN_NODES_DIR, "project");
275
275
  addFromDir(path.join(root, LEGACY_NODES_DIR), "project");
276
276
  addFromDir(path.join(root, PROJECT_NODES_DIR), "project");
277
- for (const manifest of listMarketplaceNodes(root, marketplaceFlowData)) {
277
+ for (const manifest of listMarketplaceNodes(root, marketplaceFlowData, opts)) {
278
278
  let type = "agent";
279
279
  const runtimeType = String(manifest.baseDefinitionId || manifest.runtime?.type || manifest.type || "").toLowerCase();
280
280
  if (runtimeType.startsWith("control")) type = "control";
@@ -524,7 +524,7 @@ export function readNodeJson(workspaceRoot, nodeId, flowId, flowSource, opts = {
524
524
  } catch (_) {}
525
525
  }
526
526
  }
527
- const resolved = resolveMarketplaceNodePackage(root, flowDir, nodeId, opts.flowData || null);
527
+ const resolved = resolveMarketplaceNodePackage(root, flowDir, nodeId, opts.flowData || null, opts);
528
528
  if (!resolved) return { error: "Node not found: " + nodeId };
529
529
  const readmePath = path.join(resolved.packageDir, "README.md");
530
530
  let type = "agent";
@@ -751,7 +751,7 @@ function resolveNodeFileScope(workspaceRoot, nodeId, flowId, flowSource, opts =
751
751
  } catch (_) {}
752
752
  }
753
753
  }
754
- const resolved = resolveMarketplaceNodePackage(workspaceRoot, flowDir, nodeId, flowData);
754
+ const resolved = resolveMarketplaceNodePackage(workspaceRoot, flowDir, nodeId, flowData, opts);
755
755
  if (!resolved) return null;
756
756
  return { baseDir: resolved.packageDir, allowAllFiles: true, primaryFilePath: path.join(resolved.packageDir, "node.yaml"), manifest: resolved };
757
757
  }
@@ -109,6 +109,17 @@ function normalizeManifest(raw, packageDir, source = "workspace") {
109
109
  };
110
110
  }
111
111
 
112
+ function manifestOwnerUserId(manifest) {
113
+ return String(manifest?.ownerUserId || manifest?.createdBy || "").trim();
114
+ }
115
+
116
+ function canAccessMarketplaceNode(manifest, opts = {}) {
117
+ const requestedUserId = String(opts.userId || "").trim();
118
+ if (!requestedUserId) return true;
119
+ if ((manifest?.source || "marketplace") !== "marketplace") return true;
120
+ return manifestOwnerUserId(manifest) === requestedUserId;
121
+ }
122
+
112
123
  function sortVersionsDesc(versions) {
113
124
  return [...versions].sort((a, b) => b.localeCompare(a, undefined, { numeric: true, sensitivity: "base" }));
114
125
  }
@@ -297,12 +308,13 @@ function collectionDeps(flowData) {
297
308
  return deps && Array.isArray(deps.collections) ? deps.collections : [];
298
309
  }
299
310
 
300
- export function resolveMarketplaceNodePackage(workspaceRoot, flowDir, definitionId, flowData = null) {
311
+ export function resolveMarketplaceNodePackage(workspaceRoot, flowDir, definitionId, flowData = null, opts = {}) {
301
312
  const parsed = parseMarketplaceDefinitionId(definitionId);
302
313
  if (!parsed) return null;
303
314
  const id = parsed.id;
304
315
  const requestedVersion = parsed.version || dependencyVersion(flowData, id) || lockVersion(flowDir, id);
305
316
  let packageDir = findNodePackageDir(workspaceRoot, id, requestedVersion);
317
+ let packageSource = "marketplace";
306
318
 
307
319
  if (!packageDir) {
308
320
  for (const dir of iterCollectionNodeDirs(workspaceRoot, collectionDeps(flowData))) {
@@ -311,13 +323,15 @@ export function resolveMarketplaceNodePackage(workspaceRoot, flowDir, definition
311
323
  if (!manifest || manifest.id !== id) continue;
312
324
  if (requestedVersion && manifest.version !== requestedVersion) continue;
313
325
  packageDir = dir;
326
+ packageSource = "collection";
314
327
  break;
315
328
  }
316
329
  }
317
330
 
318
331
  if (!packageDir) return null;
319
- const manifest = normalizeManifest(readYamlObject(path.join(packageDir, NODE_MANIFEST)), packageDir);
332
+ const manifest = normalizeManifest(readYamlObject(path.join(packageDir, NODE_MANIFEST)), packageDir, packageSource);
320
333
  if (!manifest) return null;
334
+ if (!canAccessMarketplaceNode(manifest, opts)) return null;
321
335
  return {
322
336
  ...manifest,
323
337
  requestedDefinitionId: definitionId,
@@ -325,13 +339,14 @@ export function resolveMarketplaceNodePackage(workspaceRoot, flowDir, definition
325
339
  };
326
340
  }
327
341
 
328
- export function listMarketplaceNodes(workspaceRoot, flowData = null) {
342
+ export function listMarketplaceNodes(workspaceRoot, flowData = null, opts = {}) {
329
343
  const root = workspacePackageRoot(workspaceRoot);
330
344
  const out = [];
331
345
  const seen = new Set();
332
346
  const addManifest = (dir, source = "marketplace") => {
333
347
  const manifest = normalizeManifest(readYamlObject(path.join(dir, NODE_MANIFEST)), dir, source);
334
348
  if (!manifest) return;
349
+ if (!canAccessMarketplaceNode(manifest, opts)) return;
335
350
  const key = `${manifest.id}@${manifest.version}`;
336
351
  if (seen.has(key)) return;
337
352
  seen.add(key);
@@ -357,7 +372,7 @@ export function listMarketplaceNodes(workspaceRoot, flowData = null) {
357
372
 
358
373
  export function listMarketplacePackages(workspaceRoot, opts = {}) {
359
374
  const root = workspacePackageRoot(workspaceRoot);
360
- const nodes = listMarketplaceNodes(workspaceRoot).map((n) => ({
375
+ const nodes = listMarketplaceNodes(workspaceRoot, null, opts).map((n) => ({
361
376
  id: n.id,
362
377
  version: n.version,
363
378
  definitionId: n.definitionId,
@@ -392,10 +407,11 @@ export function listMarketplacePackages(workspaceRoot, opts = {}) {
392
407
  return { nodes, collections };
393
408
  }
394
409
 
395
- export function listMarketplaceFlowSnippets(workspaceRoot) {
410
+ export function listMarketplaceFlowSnippets(workspaceRoot, opts = {}) {
396
411
  const root = workspacePackageRoot(workspaceRoot);
397
412
  const snippetsRoot = path.join(root, "flow-snippets");
398
413
  const snippets = [];
414
+ const requestedUserId = String(opts.userId || "").trim();
399
415
  if (!fs.existsSync(snippetsRoot)) return { snippets };
400
416
  for (const entry of fs.readdirSync(snippetsRoot, { withFileTypes: true })) {
401
417
  if (!entry.isDirectory()) continue;
@@ -405,6 +421,8 @@ export function listMarketplaceFlowSnippets(workspaceRoot) {
405
421
  const manifest = readYamlObject(path.join(dir, FLOW_SNIPPET_MANIFEST));
406
422
  if (!manifest) continue;
407
423
  const snippet = manifest.snippet && typeof manifest.snippet === "object" ? manifest.snippet : {};
424
+ const ownerUserId = String(manifest.ownerUserId || manifest.createdBy || "").trim();
425
+ if (requestedUserId && ownerUserId !== requestedUserId) continue;
408
426
  snippets.push({
409
427
  id: manifest.id || entry.name,
410
428
  version: manifest.version || version,
@@ -415,6 +433,7 @@ export function listMarketplaceFlowSnippets(workspaceRoot) {
415
433
  edgeCount: Number(manifest.edgeCount) || (Array.isArray(snippet.edges) ? snippet.edges.length : 0),
416
434
  createdAt: manifest.createdAt || "",
417
435
  updatedAt: manifest.updatedAt || "",
436
+ ownerUserId,
418
437
  packageDir: dir,
419
438
  snippet,
420
439
  });
@@ -430,9 +449,15 @@ export function listMarketplaceFlowSnippets(workspaceRoot) {
430
449
  export function deleteMarketplaceNodePackage(workspaceRoot, id, version, opts = {}) {
431
450
  const packageDir = resolveWorkspaceNodePackageDir(workspaceRoot, id, version);
432
451
  if (!packageDir) return { ok: false, error: "Invalid marketplace node id or version" };
433
- if (!fs.existsSync(path.join(packageDir, NODE_MANIFEST))) {
452
+ const manifestPath = path.join(packageDir, NODE_MANIFEST);
453
+ if (!fs.existsSync(manifestPath)) {
434
454
  return { ok: false, error: `Marketplace node package not found: ${id}@${version}` };
435
455
  }
456
+ const manifest = normalizeManifest(readYamlObject(manifestPath), packageDir, "marketplace");
457
+ const requestedUserId = String(opts.userId || "").trim();
458
+ if (!requestedUserId || !manifest || manifestOwnerUserId(manifest) !== requestedUserId) {
459
+ return { ok: false, error: "Marketplace node permission denied" };
460
+ }
436
461
  const usage = listMarketplaceNodeUsages(workspaceRoot, id, version, opts);
437
462
  if (usage.length > 0) {
438
463
  return { ok: false, error: "Marketplace node is still used by flows", usage };
@@ -449,12 +474,19 @@ export function deleteMarketplaceNodePackage(workspaceRoot, id, version, opts =
449
474
  return { ok: true, id, version, packageDir };
450
475
  }
451
476
 
452
- export function deleteMarketplaceFlowSnippetPackage(workspaceRoot, id, version) {
477
+ export function deleteMarketplaceFlowSnippetPackage(workspaceRoot, id, version, opts = {}) {
453
478
  const packageDir = resolveWorkspaceFlowSnippetPackageDir(workspaceRoot, id, version);
454
479
  if (!packageDir) return { ok: false, error: "Invalid flow snippet id or version" };
455
- if (!fs.existsSync(path.join(packageDir, FLOW_SNIPPET_MANIFEST))) {
480
+ const manifestPath = path.join(packageDir, FLOW_SNIPPET_MANIFEST);
481
+ if (!fs.existsSync(manifestPath)) {
456
482
  return { ok: false, error: `Flow snippet package not found: ${id}@${version}` };
457
483
  }
484
+ const manifest = readYamlObject(manifestPath) || {};
485
+ const ownerUserId = String(manifest.ownerUserId || manifest.createdBy || "").trim();
486
+ const requestedUserId = String(opts.userId || "").trim();
487
+ if (!requestedUserId || ownerUserId !== requestedUserId) {
488
+ return { ok: false, error: "Flow snippet permission denied" };
489
+ }
458
490
  fs.rmSync(packageDir, { recursive: true, force: true });
459
491
  const versionRoot = path.dirname(packageDir);
460
492
  try {
@@ -467,12 +499,12 @@ export function deleteMarketplaceFlowSnippetPackage(workspaceRoot, id, version)
467
499
  return { ok: true, id, version, packageDir };
468
500
  }
469
501
 
470
- export function writeFlowMarketplaceLock(workspaceRoot, flowDir, flowData) {
502
+ export function writeFlowMarketplaceLock(workspaceRoot, flowDir, flowData, opts = {}) {
471
503
  if (!flowData || !flowData.instances || typeof flowData.instances !== "object") return null;
472
504
  const nodes = {};
473
505
  for (const inst of Object.values(flowData.instances)) {
474
506
  const defId = inst && (inst.marketplaceRef || inst.definitionId);
475
- const resolved = resolveMarketplaceNodePackage(workspaceRoot, flowDir, defId, flowData);
507
+ const resolved = resolveMarketplaceNodePackage(workspaceRoot, flowDir, defId, flowData, opts);
476
508
  if (!resolved) continue;
477
509
  nodes[resolved.id] = {
478
510
  version: resolved.version,
@@ -663,6 +695,8 @@ export function publishNodeFromInstance(workspaceRoot, payload = {}, options = {
663
695
  const version = normalizeVersion(payload.version || "1.0.0");
664
696
  const sourceDefinitionId = String(payload.definitionId || "").trim();
665
697
  if (!id) return { ok: false, error: "Invalid package id" };
698
+ const ownerUserId = String(options.userId || "").trim();
699
+ if (!ownerUserId) return { ok: false, error: "Authentication required" };
666
700
 
667
701
  const inputs = normalizeSlotList(payload.inputs || payload.input).map((slot) => ({
668
702
  type: slot.type,
@@ -682,6 +716,12 @@ export function publishNodeFromInstance(workspaceRoot, payload = {}, options = {
682
716
  const body = String(payload.body || "").trim();
683
717
  const description = String(payload.description || body || `Published from node ${label}`).trim();
684
718
  const dest = path.join(workspacePackageRoot(workspaceRoot), "nodes", id, version);
719
+ const existingManifest = readYamlObject(path.join(dest, NODE_MANIFEST));
720
+ if (existingManifest) {
721
+ const existingOwner = manifestOwnerUserId(existingManifest);
722
+ if (existingOwner !== ownerUserId) return { ok: false, error: "Marketplace node permission denied" };
723
+ }
724
+ const now = new Date().toISOString();
685
725
  fs.mkdirSync(path.dirname(dest), { recursive: true });
686
726
  fs.rmSync(dest, { recursive: true, force: true });
687
727
  fs.mkdirSync(dest, { recursive: true });
@@ -711,6 +751,10 @@ export function publishNodeFromInstance(workspaceRoot, payload = {}, options = {
711
751
  runtime,
712
752
  inputs,
713
753
  outputs,
754
+ ownerUserId,
755
+ createdBy: ownerUserId,
756
+ createdAt: existingManifest?.createdAt || now,
757
+ updatedAt: now,
714
758
  };
715
759
  if (packagedScript?.packagedFiles?.length) manifest.packagedFiles = packagedScript.packagedFiles;
716
760
  fs.writeFileSync(path.join(dest, NODE_MANIFEST), yaml.dump(manifest, { lineWidth: -1 }), "utf-8");
@@ -732,11 +776,13 @@ export function publishNodeFromInstance(workspaceRoot, payload = {}, options = {
732
776
  };
733
777
  }
734
778
 
735
- export function publishFlowSnippet(workspaceRoot, payload = {}) {
779
+ export function publishFlowSnippet(workspaceRoot, payload = {}, opts = {}) {
736
780
  const label = String(payload.displayName || payload.name || payload.id || "flow snippet").trim();
737
781
  const id = safePackageId(payload.id || payload.packageId || label);
738
782
  const version = normalizeVersion(payload.version || "1.0.0");
739
783
  if (!id) return { ok: false, error: "Invalid snippet id" };
784
+ const ownerUserId = String(opts.userId || "").trim();
785
+ if (!ownerUserId) return { ok: false, error: "Authentication required" };
740
786
 
741
787
  const rawSnippet = payload.snippet && typeof payload.snippet === "object" ? payload.snippet : {};
742
788
  const instances = rawSnippet.instances && typeof rawSnippet.instances === "object" ? rawSnippet.instances : {};
@@ -748,6 +794,11 @@ export function publishFlowSnippet(workspaceRoot, payload = {}) {
748
794
  const now = new Date().toISOString();
749
795
  const dest = resolveWorkspaceFlowSnippetPackageDir(workspaceRoot, id, version);
750
796
  if (!dest) return { ok: false, error: "Invalid snippet id or version" };
797
+ const existingManifest = readYamlObject(path.join(dest, FLOW_SNIPPET_MANIFEST));
798
+ if (existingManifest) {
799
+ const existingOwner = String(existingManifest.ownerUserId || existingManifest.createdBy || "").trim();
800
+ if (existingOwner !== ownerUserId) return { ok: false, error: "Flow snippet permission denied" };
801
+ }
751
802
  fs.mkdirSync(path.dirname(dest), { recursive: true });
752
803
  fs.rmSync(dest, { recursive: true, force: true });
753
804
  fs.mkdirSync(dest, { recursive: true });
@@ -759,6 +810,8 @@ export function publishFlowSnippet(workspaceRoot, payload = {}) {
759
810
  displayName: label,
760
811
  description: String(payload.description || "").trim(),
761
812
  tags: Array.isArray(payload.tags) ? payload.tags.map((x) => String(x).trim()).filter(Boolean) : [],
813
+ ownerUserId,
814
+ createdBy: ownerUserId,
762
815
  nodeCount,
763
816
  edgeCount: edges.length,
764
817
  createdAt: now,
@@ -783,10 +836,10 @@ export function publishFlowSnippet(workspaceRoot, payload = {}) {
783
836
  return { ok: true, id, version, packageDir: dest, snippet: manifest.snippet };
784
837
  }
785
838
 
786
- export function installFlowDependency(workspaceRoot, flowDir, spec) {
839
+ export function installFlowDependency(workspaceRoot, flowDir, spec, opts = {}) {
787
840
  const parsed = parseMarketplaceDefinitionId(spec.startsWith("marketplace:") ? spec : `marketplace:${spec}`);
788
841
  if (!parsed) return { ok: false, error: `Invalid marketplace node spec: ${spec}` };
789
- const resolved = resolveMarketplaceNodePackage(workspaceRoot, flowDir, `marketplace:${parsed.id}${parsed.version ? `@${parsed.version}` : ""}`, { dependencies: {} });
842
+ const resolved = resolveMarketplaceNodePackage(workspaceRoot, flowDir, `marketplace:${parsed.id}${parsed.version ? `@${parsed.version}` : ""}`, { dependencies: {} }, opts);
790
843
  if (!resolved) return { ok: false, error: `Marketplace node not found: ${spec}` };
791
844
 
792
845
  const flowYamlPath = path.join(flowDir, "flow.yaml");
@@ -798,6 +851,6 @@ export function installFlowDependency(workspaceRoot, flowDir, spec) {
798
851
  if (!exists) nodes.push({ id: resolved.id, version: resolved.version });
799
852
  data.dependencies = { ...deps, nodes };
800
853
  fs.writeFileSync(flowYamlPath, yaml.dump(data, { lineWidth: -1 }), "utf-8");
801
- writeFlowMarketplaceLock(workspaceRoot, flowDir, data);
854
+ writeFlowMarketplaceLock(workspaceRoot, flowDir, data, opts);
802
855
  return { ok: true, id: resolved.id, version: resolved.version, definitionId: `marketplace:${resolved.id}@${resolved.version}` };
803
856
  }
@@ -4842,7 +4842,7 @@ export function startUiServer({
4842
4842
 
4843
4843
  if (req.method === "GET" && url.pathname === "/api/marketplace/flow-snippets") {
4844
4844
  try {
4845
- json(res, 200, listMarketplaceFlowSnippets(root));
4845
+ json(res, 200, listMarketplaceFlowSnippets(root, userCtx));
4846
4846
  } catch (e) {
4847
4847
  json(res, 500, { error: (e && e.message) || String(e) });
4848
4848
  }
@@ -4873,7 +4873,7 @@ export function startUiServer({
4873
4873
  return;
4874
4874
  }
4875
4875
  try {
4876
- const result = deleteMarketplaceFlowSnippetPackage(root, id, version);
4876
+ const result = deleteMarketplaceFlowSnippetPackage(root, id, version, userCtx);
4877
4877
  json(res, result.ok ? 200 : 400, result);
4878
4878
  } catch (e) {
4879
4879
  json(res, 500, { ok: false, error: (e && e.message) || String(e) });
@@ -4911,7 +4911,7 @@ export function startUiServer({
4911
4911
  json(res, 400, { error: resolved.error || "Could not resolve flow directory" });
4912
4912
  return;
4913
4913
  }
4914
- const result = installFlowDependency(root, resolved.flowDir, nodeSpec);
4914
+ const result = installFlowDependency(root, resolved.flowDir, nodeSpec, userCtx);
4915
4915
  json(res, result.ok ? 200 : 400, result);
4916
4916
  } catch (e) {
4917
4917
  json(res, 500, { ok: false, error: (e && e.message) || String(e) });
@@ -4935,7 +4935,7 @@ export function startUiServer({
4935
4935
  const resolved = resolveFlowDirForWrite(root, flowId, flowSource, userCtx);
4936
4936
  if (!resolved.error && resolved.flowDir) flowDir = resolved.flowDir;
4937
4937
  }
4938
- const result = publishNodeFromInstance(root, payload || {}, { flowDir });
4938
+ const result = publishNodeFromInstance(root, payload || {}, { flowDir, ...userCtx });
4939
4939
  json(res, result.ok ? 200 : 400, result);
4940
4940
  } catch (e) {
4941
4941
  json(res, 500, { ok: false, error: (e && e.message) || String(e) });
@@ -4952,7 +4952,7 @@ export function startUiServer({
4952
4952
  return;
4953
4953
  }
4954
4954
  try {
4955
- const result = publishFlowSnippet(root, payload || {});
4955
+ const result = publishFlowSnippet(root, payload || {}, userCtx);
4956
4956
  json(res, result.ok ? 200 : 400, result);
4957
4957
  } catch (e) {
4958
4958
  json(res, 500, { ok: false, error: (e && e.message) || String(e) });