@fieldwangai/agentflow 0.1.52 → 0.1.54
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/auth.mjs +4 -3
- package/bin/lib/marketplace.mjs +24 -4
- package/bin/lib/ui-server.mjs +30 -4
- package/builtin/web-ui/dist/assets/index-DUDsW1dU.css +1 -0
- package/builtin/web-ui/dist/assets/index-DVw2IOGE.js +238 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/builtin/web-ui/dist/likee-context/usecase-ad-query.png +0 -0
- package/builtin/web-ui/dist/likee-context/usecase-data-query.png +0 -0
- package/builtin/web-ui/dist/likee-context/usecase-display.png +0 -0
- package/builtin/web-ui/dist/likee-context/usecase-h5-prototype.png +0 -0
- package/builtin/web-ui/dist/likee-context/usecase-prd.png +0 -0
- package/builtin/web-ui/dist/likee-context/usecase-prototype.png +0 -0
- package/builtin/web-ui/dist/likee-context/usecase-sync.png +0 -0
- package/package.json +1 -1
- package/builtin/web-ui/dist/assets/index-CGls6aTE.js +0 -238
- package/builtin/web-ui/dist/assets/index-DlZeY5p1.css +0 -1
package/bin/lib/auth.mjs
CHANGED
|
@@ -152,6 +152,7 @@ function userAllowlistMatchSet(users) {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
export function isAuthUserAllowed(user) {
|
|
155
|
+
if (user?.isAdmin) return true;
|
|
155
156
|
const allowlist = readUserAllowlist();
|
|
156
157
|
if (!allowlist.enabled) return true;
|
|
157
158
|
const allowed = userAllowlistMatchSet(allowlist.users);
|
|
@@ -266,15 +267,15 @@ export function loginOrCreateUser(username, password) {
|
|
|
266
267
|
if (!userId) {
|
|
267
268
|
return { ok: false, error: "用户名须以字母开头,仅可使用字母、数字、下划线与连字符,最多 64 字符" };
|
|
268
269
|
}
|
|
269
|
-
if (!isAuthUserAllowed({ userId, username: String(username || "").trim() })) {
|
|
270
|
-
return { ok: false, forbidden: true, error: "用户不在白名单中,请联系管理员开通访问权限" };
|
|
271
|
-
}
|
|
272
270
|
const pwd = String(password || "");
|
|
273
271
|
if (pwd.length < 4) return { ok: false, error: "密码至少 4 位" };
|
|
274
272
|
|
|
275
273
|
const users = readAuthUsers();
|
|
276
274
|
const firstUser = Object.keys(users).length === 0;
|
|
277
275
|
let user = users[userId];
|
|
276
|
+
if (!isAuthUserAllowed({ userId, username: String(username || "").trim(), isAdmin: Boolean(user?.isAdmin) })) {
|
|
277
|
+
return { ok: false, forbidden: true, error: "用户不在白名单中,请联系管理员开通访问权限" };
|
|
278
|
+
}
|
|
278
279
|
if (!user) {
|
|
279
280
|
const hashed = hashPassword(pwd);
|
|
280
281
|
user = {
|
package/bin/lib/marketplace.mjs
CHANGED
|
@@ -392,10 +392,11 @@ export function listMarketplacePackages(workspaceRoot, opts = {}) {
|
|
|
392
392
|
return { nodes, collections };
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
-
export function listMarketplaceFlowSnippets(workspaceRoot) {
|
|
395
|
+
export function listMarketplaceFlowSnippets(workspaceRoot, opts = {}) {
|
|
396
396
|
const root = workspacePackageRoot(workspaceRoot);
|
|
397
397
|
const snippetsRoot = path.join(root, "flow-snippets");
|
|
398
398
|
const snippets = [];
|
|
399
|
+
const requestedUserId = String(opts.userId || "").trim();
|
|
399
400
|
if (!fs.existsSync(snippetsRoot)) return { snippets };
|
|
400
401
|
for (const entry of fs.readdirSync(snippetsRoot, { withFileTypes: true })) {
|
|
401
402
|
if (!entry.isDirectory()) continue;
|
|
@@ -405,6 +406,8 @@ export function listMarketplaceFlowSnippets(workspaceRoot) {
|
|
|
405
406
|
const manifest = readYamlObject(path.join(dir, FLOW_SNIPPET_MANIFEST));
|
|
406
407
|
if (!manifest) continue;
|
|
407
408
|
const snippet = manifest.snippet && typeof manifest.snippet === "object" ? manifest.snippet : {};
|
|
409
|
+
const ownerUserId = String(manifest.ownerUserId || manifest.createdBy || "").trim();
|
|
410
|
+
if (requestedUserId && ownerUserId !== requestedUserId) continue;
|
|
408
411
|
snippets.push({
|
|
409
412
|
id: manifest.id || entry.name,
|
|
410
413
|
version: manifest.version || version,
|
|
@@ -415,6 +418,7 @@ export function listMarketplaceFlowSnippets(workspaceRoot) {
|
|
|
415
418
|
edgeCount: Number(manifest.edgeCount) || (Array.isArray(snippet.edges) ? snippet.edges.length : 0),
|
|
416
419
|
createdAt: manifest.createdAt || "",
|
|
417
420
|
updatedAt: manifest.updatedAt || "",
|
|
421
|
+
ownerUserId,
|
|
418
422
|
packageDir: dir,
|
|
419
423
|
snippet,
|
|
420
424
|
});
|
|
@@ -449,12 +453,19 @@ export function deleteMarketplaceNodePackage(workspaceRoot, id, version, opts =
|
|
|
449
453
|
return { ok: true, id, version, packageDir };
|
|
450
454
|
}
|
|
451
455
|
|
|
452
|
-
export function deleteMarketplaceFlowSnippetPackage(workspaceRoot, id, version) {
|
|
456
|
+
export function deleteMarketplaceFlowSnippetPackage(workspaceRoot, id, version, opts = {}) {
|
|
453
457
|
const packageDir = resolveWorkspaceFlowSnippetPackageDir(workspaceRoot, id, version);
|
|
454
458
|
if (!packageDir) return { ok: false, error: "Invalid flow snippet id or version" };
|
|
455
|
-
|
|
459
|
+
const manifestPath = path.join(packageDir, FLOW_SNIPPET_MANIFEST);
|
|
460
|
+
if (!fs.existsSync(manifestPath)) {
|
|
456
461
|
return { ok: false, error: `Flow snippet package not found: ${id}@${version}` };
|
|
457
462
|
}
|
|
463
|
+
const manifest = readYamlObject(manifestPath) || {};
|
|
464
|
+
const ownerUserId = String(manifest.ownerUserId || manifest.createdBy || "").trim();
|
|
465
|
+
const requestedUserId = String(opts.userId || "").trim();
|
|
466
|
+
if (!requestedUserId || ownerUserId !== requestedUserId) {
|
|
467
|
+
return { ok: false, error: "Flow snippet permission denied" };
|
|
468
|
+
}
|
|
458
469
|
fs.rmSync(packageDir, { recursive: true, force: true });
|
|
459
470
|
const versionRoot = path.dirname(packageDir);
|
|
460
471
|
try {
|
|
@@ -732,11 +743,13 @@ export function publishNodeFromInstance(workspaceRoot, payload = {}, options = {
|
|
|
732
743
|
};
|
|
733
744
|
}
|
|
734
745
|
|
|
735
|
-
export function publishFlowSnippet(workspaceRoot, payload = {}) {
|
|
746
|
+
export function publishFlowSnippet(workspaceRoot, payload = {}, opts = {}) {
|
|
736
747
|
const label = String(payload.displayName || payload.name || payload.id || "flow snippet").trim();
|
|
737
748
|
const id = safePackageId(payload.id || payload.packageId || label);
|
|
738
749
|
const version = normalizeVersion(payload.version || "1.0.0");
|
|
739
750
|
if (!id) return { ok: false, error: "Invalid snippet id" };
|
|
751
|
+
const ownerUserId = String(opts.userId || "").trim();
|
|
752
|
+
if (!ownerUserId) return { ok: false, error: "Authentication required" };
|
|
740
753
|
|
|
741
754
|
const rawSnippet = payload.snippet && typeof payload.snippet === "object" ? payload.snippet : {};
|
|
742
755
|
const instances = rawSnippet.instances && typeof rawSnippet.instances === "object" ? rawSnippet.instances : {};
|
|
@@ -748,6 +761,11 @@ export function publishFlowSnippet(workspaceRoot, payload = {}) {
|
|
|
748
761
|
const now = new Date().toISOString();
|
|
749
762
|
const dest = resolveWorkspaceFlowSnippetPackageDir(workspaceRoot, id, version);
|
|
750
763
|
if (!dest) return { ok: false, error: "Invalid snippet id or version" };
|
|
764
|
+
const existingManifest = readYamlObject(path.join(dest, FLOW_SNIPPET_MANIFEST));
|
|
765
|
+
if (existingManifest) {
|
|
766
|
+
const existingOwner = String(existingManifest.ownerUserId || existingManifest.createdBy || "").trim();
|
|
767
|
+
if (existingOwner !== ownerUserId) return { ok: false, error: "Flow snippet permission denied" };
|
|
768
|
+
}
|
|
751
769
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
752
770
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
753
771
|
fs.mkdirSync(dest, { recursive: true });
|
|
@@ -759,6 +777,8 @@ export function publishFlowSnippet(workspaceRoot, payload = {}) {
|
|
|
759
777
|
displayName: label,
|
|
760
778
|
description: String(payload.description || "").trim(),
|
|
761
779
|
tags: Array.isArray(payload.tags) ? payload.tags.map((x) => String(x).trim()).filter(Boolean) : [],
|
|
780
|
+
ownerUserId,
|
|
781
|
+
createdBy: ownerUserId,
|
|
762
782
|
nodeCount,
|
|
763
783
|
edgeCount: edges.length,
|
|
764
784
|
createdAt: now,
|
package/bin/lib/ui-server.mjs
CHANGED
|
@@ -1601,11 +1601,15 @@ function workspaceSourceSlotForEdge(graph, edge) {
|
|
|
1601
1601
|
function workspaceOutputSlotValueForEdge(graph, outputs, edge) {
|
|
1602
1602
|
const sourceId = String(edge?.source || "");
|
|
1603
1603
|
const slot = workspaceSourceSlotForEdge(graph, edge);
|
|
1604
|
+
const out = outputs.get(sourceId);
|
|
1605
|
+
const sourceIndex = workspaceHandleIndex(edge?.sourceHandle, "output");
|
|
1606
|
+
const slotName = String(slot?.name || "").trim();
|
|
1607
|
+
const isPrimaryOutput = !slot || slotName === "result" || slotName === "content" || sourceIndex === 0;
|
|
1608
|
+
if (isPrimaryOutput && out != null && String(out).trim()) return String(out);
|
|
1604
1609
|
if (slot && String(slot?.type || "") !== "node") {
|
|
1605
1610
|
const value = workspaceSlotValue(slot);
|
|
1606
1611
|
if (value.trim()) return value;
|
|
1607
1612
|
}
|
|
1608
|
-
const out = outputs.get(sourceId);
|
|
1609
1613
|
if (out != null && String(out).trim()) return String(out);
|
|
1610
1614
|
const instances = graph?.instances && typeof graph.instances === "object" ? graph.instances : {};
|
|
1611
1615
|
return workspaceInstanceText(instances[sourceId]);
|
|
@@ -2022,6 +2026,26 @@ function workspaceDownstreamDisplayRequirements(graph, nodeId) {
|
|
|
2022
2026
|
].join("\n");
|
|
2023
2027
|
}
|
|
2024
2028
|
|
|
2029
|
+
function workspaceNodeScopeGuardrails(graph, nodeId, inputValues = {}) {
|
|
2030
|
+
const edges = Array.isArray(graph?.edges) ? graph.edges : [];
|
|
2031
|
+
const directInputEdges = edges
|
|
2032
|
+
.filter((edge) => String(edge?.target || "") === String(nodeId))
|
|
2033
|
+
.filter((edge) => !isWorkspaceSemanticInputSlot(workspaceTargetSlotForEdge(graph, edge)));
|
|
2034
|
+
const upstreamNodeIds = Array.from(new Set(directInputEdges.map((edge) => String(edge?.source || "").trim()).filter(Boolean)));
|
|
2035
|
+
const inputNames = Object.keys(inputValues || {}).filter(Boolean);
|
|
2036
|
+
return [
|
|
2037
|
+
"## 当前节点上下文边界",
|
|
2038
|
+
"",
|
|
2039
|
+
"你只负责执行当前节点;`上游上下文`、已解析输入槽和节点任务就是本节点的业务上下文边界。",
|
|
2040
|
+
upstreamNodeIds.length ? `当前直接上游节点:${upstreamNodeIds.map((id) => `\`${id}\``).join("、")}。` : "当前没有直接业务上游节点。",
|
|
2041
|
+
inputNames.length ? `当前已解析输入槽:${inputNames.map((name) => `\`${name}\``).join("、")}。` : "当前没有已解析的具名业务输入槽。",
|
|
2042
|
+
"不要为了理解本节点而读取或搜索整张 workspace、`workspace.graph.json`、正式 `flow.yaml`、历史 run/log 或其它未连接节点。",
|
|
2043
|
+
"不要把下游展示节点已有内容、下游错误信息、其它分支节点内容当作本节点输入;下游输出要求只用于决定最终 JSON 的字段和格式。",
|
|
2044
|
+
"只有当当前节点任务文本或上游输入明确要求读取/修改 `workspace.graph.json`、`flow.yaml` 或某个具体文件路径时,才可以打开对应文件。",
|
|
2045
|
+
"如果完成任务所需信息不在当前节点任务、输入槽或上游上下文中,应明确说明缺少哪个上游输入,而不是扫描整张 workspace 猜测。",
|
|
2046
|
+
].join("\n");
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2025
2049
|
function workspaceOutputProtocolRequirements(graph, nodeId) {
|
|
2026
2050
|
const instance = graph?.instances?.[nodeId] || {};
|
|
2027
2051
|
const outputSlots = Array.isArray(instance.output) ? instance.output : [];
|
|
@@ -2355,11 +2379,13 @@ function workspaceNodePrompt(graph, nodeId, upstreamText, skillsBlock, mcpBlock
|
|
|
2355
2379
|
const instance = graph.instances[nodeId] || {};
|
|
2356
2380
|
const body = workspaceResolveBodyPlaceholders(instance.body || "", inputValues).trim();
|
|
2357
2381
|
const label = String(instance.label || nodeId).trim();
|
|
2382
|
+
const scopeGuardrails = workspaceNodeScopeGuardrails(graph, nodeId, inputValues);
|
|
2358
2383
|
const downstreamRequirements = workspaceDownstreamDisplayRequirements(graph, nodeId);
|
|
2359
2384
|
const outputProtocolRequirements = workspaceOutputProtocolRequirements(graph, nodeId);
|
|
2360
2385
|
return [
|
|
2361
2386
|
"你正在执行 AgentFlow Workspace 画布中的一个临时节点。",
|
|
2362
2387
|
"按 Workspace 输出协议返回该节点要传给下游展示/后续节点的数据。",
|
|
2388
|
+
scopeGuardrails,
|
|
2363
2389
|
workspaceSearchGuardrailsBlock(),
|
|
2364
2390
|
skillsBlock ? `\n## Available Skills\n\n${skillsBlock}` : "",
|
|
2365
2391
|
mcpBlock ? `\n## Available MCP\n\n${mcpBlock}` : "",
|
|
@@ -4816,7 +4842,7 @@ export function startUiServer({
|
|
|
4816
4842
|
|
|
4817
4843
|
if (req.method === "GET" && url.pathname === "/api/marketplace/flow-snippets") {
|
|
4818
4844
|
try {
|
|
4819
|
-
json(res, 200, listMarketplaceFlowSnippets(root));
|
|
4845
|
+
json(res, 200, listMarketplaceFlowSnippets(root, userCtx));
|
|
4820
4846
|
} catch (e) {
|
|
4821
4847
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
4822
4848
|
}
|
|
@@ -4847,7 +4873,7 @@ export function startUiServer({
|
|
|
4847
4873
|
return;
|
|
4848
4874
|
}
|
|
4849
4875
|
try {
|
|
4850
|
-
const result = deleteMarketplaceFlowSnippetPackage(root, id, version);
|
|
4876
|
+
const result = deleteMarketplaceFlowSnippetPackage(root, id, version, userCtx);
|
|
4851
4877
|
json(res, result.ok ? 200 : 400, result);
|
|
4852
4878
|
} catch (e) {
|
|
4853
4879
|
json(res, 500, { ok: false, error: (e && e.message) || String(e) });
|
|
@@ -4926,7 +4952,7 @@ export function startUiServer({
|
|
|
4926
4952
|
return;
|
|
4927
4953
|
}
|
|
4928
4954
|
try {
|
|
4929
|
-
const result = publishFlowSnippet(root, payload || {});
|
|
4955
|
+
const result = publishFlowSnippet(root, payload || {}, userCtx);
|
|
4930
4956
|
json(res, result.ok ? 200 : 400, result);
|
|
4931
4957
|
} catch (e) {
|
|
4932
4958
|
json(res, 500, { ok: false, error: (e && e.message) || String(e) });
|