@fieldwangai/agentflow 0.1.89 → 0.1.91
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/ui-server.mjs
CHANGED
|
@@ -1617,10 +1617,48 @@ const DISPLAY_SHARE_ALLOWED_EXPIRY_DAYS = new Set([1, 7, 30, 90, 365]);
|
|
|
1617
1617
|
const NODE_STUDIO_DRAFTS_DIRNAME = "node-studio/drafts";
|
|
1618
1618
|
const USER_WORKSPACES_FILENAME = "workspaces.json";
|
|
1619
1619
|
|
|
1620
|
-
function
|
|
1620
|
+
function workspacesPath() {
|
|
1621
|
+
return path.join(getAgentflowDataRoot(), USER_WORKSPACES_FILENAME);
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
function legacyUserWorkspacesPath(userCtx = {}) {
|
|
1621
1625
|
return path.join(getAgentflowUserDataRoot(userCtx.userId || ""), USER_WORKSPACES_FILENAME);
|
|
1622
1626
|
}
|
|
1623
1627
|
|
|
1628
|
+
function defaultWorkspaceGitPath(id) {
|
|
1629
|
+
return path.join(getAgentflowDataRoot(), "workspaces", "repos", id);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
function legacyDefaultWorkspaceGitPath(userCtx = {}, id = "") {
|
|
1633
|
+
return path.join(getAgentflowUserDataRoot(userCtx.userId || ""), "workspaces", "repos", id);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
function isLegacyDefaultWorkspaceGitPath(rawPath = "", id = "", userCtx = {}) {
|
|
1637
|
+
const raw = String(rawPath || "").trim();
|
|
1638
|
+
if (!raw || !id) return false;
|
|
1639
|
+
try {
|
|
1640
|
+
return path.resolve(raw.replace(/^~(?=$|\/|\\)/, os.homedir())) === path.resolve(legacyDefaultWorkspaceGitPath(userCtx, id));
|
|
1641
|
+
} catch {
|
|
1642
|
+
return false;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
function workspaceRepoNameFromUrl(repoUrl = "") {
|
|
1647
|
+
const raw = String(repoUrl || "").trim();
|
|
1648
|
+
if (!raw) return "";
|
|
1649
|
+
let pathname = raw;
|
|
1650
|
+
try {
|
|
1651
|
+
pathname = new URL(raw).pathname;
|
|
1652
|
+
} catch {
|
|
1653
|
+
pathname = raw.split("?")[0].split("#")[0];
|
|
1654
|
+
}
|
|
1655
|
+
const name = pathname.replace(/\/+$/, "").split("/").filter(Boolean).pop() || "";
|
|
1656
|
+
return name
|
|
1657
|
+
.replace(/\.git$/i, "")
|
|
1658
|
+
.replace(/[^A-Za-z0-9._-]+/g, "_")
|
|
1659
|
+
.replace(/^_+|_+$/g, "");
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1624
1662
|
function normalizeWorkspaceEntry(entry = {}, index = 0, userCtx = {}) {
|
|
1625
1663
|
const label = String(entry?.label || entry?.name || "").trim();
|
|
1626
1664
|
const kindRaw = String(entry?.kind || entry?.source || "").trim().toLowerCase();
|
|
@@ -1632,9 +1670,11 @@ function normalizeWorkspaceEntry(entry = {}, index = 0, userCtx = {}) {
|
|
|
1632
1670
|
if (!rawPath && kind !== "git") return null;
|
|
1633
1671
|
const idRaw = String(entry?.id || label || mountPathRaw || repoUrl || rawPath || `workspace_${index + 1}`).trim().toLowerCase();
|
|
1634
1672
|
const id = idRaw.replace(/[^a-z0-9_-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 64) || `workspace_${index + 1}`;
|
|
1635
|
-
const
|
|
1636
|
-
const
|
|
1637
|
-
const
|
|
1673
|
+
const suggestedMountPath = kind === "git" ? workspaceRepoNameFromUrl(repoUrl) : "";
|
|
1674
|
+
const mountPath = (mountPathRaw || suggestedMountPath || id).replace(/^\/+/, "").replace(/\.\.(\/|\\|$)/g, "").trim() || id;
|
|
1675
|
+
const defaultGitPath = defaultWorkspaceGitPath(id);
|
|
1676
|
+
const explicitPath = kind === "git" && isLegacyDefaultWorkspaceGitPath(rawPath, id, userCtx) ? "" : rawPath;
|
|
1677
|
+
const absPath = path.resolve((explicitPath || defaultGitPath).replace(/^~(?=$|\/|\\)/, os.homedir()));
|
|
1638
1678
|
const exists = fs.existsSync(absPath) && fs.statSync(absPath).isDirectory();
|
|
1639
1679
|
return {
|
|
1640
1680
|
id,
|
|
@@ -1653,8 +1693,7 @@ function normalizeWorkspaceEntry(entry = {}, index = 0, userCtx = {}) {
|
|
|
1653
1693
|
};
|
|
1654
1694
|
}
|
|
1655
1695
|
|
|
1656
|
-
function
|
|
1657
|
-
const p = userWorkspacesPath(userCtx);
|
|
1696
|
+
function readWorkspacesFromPath(p, userCtx = {}) {
|
|
1658
1697
|
if (!fs.existsSync(p)) return [];
|
|
1659
1698
|
try {
|
|
1660
1699
|
const data = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
@@ -1665,6 +1704,49 @@ function readUserWorkspaces(userCtx = {}) {
|
|
|
1665
1704
|
}
|
|
1666
1705
|
}
|
|
1667
1706
|
|
|
1707
|
+
function readLegacyAdminWorkspaces(userCtx = {}) {
|
|
1708
|
+
const users = readAuthUsers();
|
|
1709
|
+
const candidates = [];
|
|
1710
|
+
for (const [userId, user] of Object.entries(users || {})) {
|
|
1711
|
+
if (user?.isAdmin) candidates.push(String(userId || ""));
|
|
1712
|
+
}
|
|
1713
|
+
if (userCtx?.isAdmin && userCtx.userId) candidates.unshift(String(userCtx.userId));
|
|
1714
|
+
const seenPaths = new Set();
|
|
1715
|
+
const seenEntries = new Set();
|
|
1716
|
+
const out = [];
|
|
1717
|
+
for (const userId of candidates) {
|
|
1718
|
+
const p = legacyUserWorkspacesPath({ userId });
|
|
1719
|
+
const resolved = path.resolve(p);
|
|
1720
|
+
if (seenPaths.has(resolved) || resolved === path.resolve(workspacesPath())) continue;
|
|
1721
|
+
seenPaths.add(resolved);
|
|
1722
|
+
for (const entry of readWorkspacesFromPath(p, { userId })) {
|
|
1723
|
+
const key = entry.id || entry.path || entry.repoUrl;
|
|
1724
|
+
if (seenEntries.has(key)) continue;
|
|
1725
|
+
seenEntries.add(key);
|
|
1726
|
+
out.push(entry);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
return out;
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
function readUserWorkspaces(userCtx = {}) {
|
|
1733
|
+
const globalPath = workspacesPath();
|
|
1734
|
+
const globalWorkspaces = fs.existsSync(globalPath) ? readWorkspacesFromPath(globalPath, userCtx) : [];
|
|
1735
|
+
const adminLegacy = readLegacyAdminWorkspaces(userCtx);
|
|
1736
|
+
if (globalWorkspaces.length || adminLegacy.length) {
|
|
1737
|
+
const seen = new Set();
|
|
1738
|
+
const out = [];
|
|
1739
|
+
for (const entry of [...globalWorkspaces, ...adminLegacy]) {
|
|
1740
|
+
const key = entry.id || entry.path || entry.repoUrl;
|
|
1741
|
+
if (seen.has(key)) continue;
|
|
1742
|
+
seen.add(key);
|
|
1743
|
+
out.push(entry);
|
|
1744
|
+
}
|
|
1745
|
+
return out;
|
|
1746
|
+
}
|
|
1747
|
+
return readWorkspacesFromPath(legacyUserWorkspacesPath(userCtx), userCtx);
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1668
1750
|
function writeUserWorkspaces(userCtx = {}, entries = []) {
|
|
1669
1751
|
const seen = new Set();
|
|
1670
1752
|
const workspaces = (Array.isArray(entries) ? entries : [])
|
|
@@ -1676,7 +1758,7 @@ function writeUserWorkspaces(userCtx = {}, entries = []) {
|
|
|
1676
1758
|
seen.add(key);
|
|
1677
1759
|
return true;
|
|
1678
1760
|
});
|
|
1679
|
-
const p =
|
|
1761
|
+
const p = workspacesPath();
|
|
1680
1762
|
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
1681
1763
|
fs.writeFileSync(p, JSON.stringify({ version: 1, workspaces }, null, 2) + "\n", "utf-8");
|
|
1682
1764
|
return workspaces;
|
|
@@ -2898,6 +2980,114 @@ function resolveWorkspaceScopeRoot(workspaceRoot, params = {}, opts = {}) {
|
|
|
2898
2980
|
return { root: path.resolve(result.path), flowId, flowSource, archived };
|
|
2899
2981
|
}
|
|
2900
2982
|
|
|
2983
|
+
function workspaceConversationsPath(scopedRoot) {
|
|
2984
|
+
return path.join(path.resolve(scopedRoot), ".workspace", "agentflow", "conversations.json");
|
|
2985
|
+
}
|
|
2986
|
+
|
|
2987
|
+
function workspaceConversationText(value, max = 4000) {
|
|
2988
|
+
const text = String(value ?? "").trim();
|
|
2989
|
+
if (!text) return "";
|
|
2990
|
+
return text.length > max ? `${text.slice(0, max)}\n...[truncated ${text.length - max} chars]` : text;
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
function normalizeWorkspaceConversationMessage(message = {}) {
|
|
2994
|
+
const text = workspaceConversationText(message?.text, 4000);
|
|
2995
|
+
if (!text) return null;
|
|
2996
|
+
const role = String(message?.role || "assistant").trim() === "user" ? "user" : "assistant";
|
|
2997
|
+
const kind = String(message?.kind || "").trim();
|
|
2998
|
+
return {
|
|
2999
|
+
role,
|
|
3000
|
+
...(kind ? { kind } : {}),
|
|
3001
|
+
text,
|
|
3002
|
+
...(message?.error ? { error: true } : {}),
|
|
3003
|
+
at: Number.isFinite(Number(message?.at)) ? Number(message.at) : Date.now(),
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
function normalizeWorkspaceConversationMessages(messages, limit = 80) {
|
|
3008
|
+
return (Array.isArray(messages) ? messages : [])
|
|
3009
|
+
.map(normalizeWorkspaceConversationMessage)
|
|
3010
|
+
.filter(Boolean)
|
|
3011
|
+
.slice(-limit);
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3014
|
+
function normalizeWorkspaceNodeChatSessions(nodeChats = {}) {
|
|
3015
|
+
const source = nodeChats && typeof nodeChats === "object" && !Array.isArray(nodeChats) ? nodeChats : {};
|
|
3016
|
+
const entries = Object.entries(source).slice(-80);
|
|
3017
|
+
const next = {};
|
|
3018
|
+
for (const [nodeId, session] of entries) {
|
|
3019
|
+
if (!session || typeof session !== "object" || Array.isArray(session)) continue;
|
|
3020
|
+
const id = String(nodeId || "").trim();
|
|
3021
|
+
if (!id) continue;
|
|
3022
|
+
const messages = normalizeWorkspaceConversationMessages(session.messages, 40);
|
|
3023
|
+
const draft = workspaceConversationText(session.draft || "", 2000);
|
|
3024
|
+
if (!messages.length && !draft) continue;
|
|
3025
|
+
next[id] = {
|
|
3026
|
+
sessionId: String(session.sessionId || `nodechat_${id}`).trim(),
|
|
3027
|
+
messages,
|
|
3028
|
+
...(draft ? { draft } : {}),
|
|
3029
|
+
candidateContent: "",
|
|
3030
|
+
running: false,
|
|
3031
|
+
error: "",
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
return next;
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3037
|
+
function normalizeWorkspaceComposerRunSessions(sessions = []) {
|
|
3038
|
+
return (Array.isArray(sessions) ? sessions : [])
|
|
3039
|
+
.map((session) => {
|
|
3040
|
+
if (!session || typeof session !== "object" || Array.isArray(session)) return null;
|
|
3041
|
+
const id = String(session.id || "").trim();
|
|
3042
|
+
if (!id) return null;
|
|
3043
|
+
const messages = normalizeWorkspaceConversationMessages(session.messages, 80);
|
|
3044
|
+
if (!messages.length) return null;
|
|
3045
|
+
const status = String(session.status || "done").trim();
|
|
3046
|
+
return {
|
|
3047
|
+
id,
|
|
3048
|
+
label: workspaceConversationText(session.label || id, 120),
|
|
3049
|
+
status: status === "failed" ? "failed" : "done",
|
|
3050
|
+
messages,
|
|
3051
|
+
};
|
|
3052
|
+
})
|
|
3053
|
+
.filter(Boolean)
|
|
3054
|
+
.slice(-20);
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
function normalizeWorkspaceConversations(raw = {}) {
|
|
3058
|
+
const data = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
3059
|
+
const composer = data.composer && typeof data.composer === "object" && !Array.isArray(data.composer) ? data.composer : {};
|
|
3060
|
+
const activeSessionId = String(composer.activeSessionId || "workspace").trim() || "workspace";
|
|
3061
|
+
return {
|
|
3062
|
+
version: 1,
|
|
3063
|
+
updatedAt: new Date().toISOString(),
|
|
3064
|
+
composer: {
|
|
3065
|
+
activeSessionId,
|
|
3066
|
+
messages: normalizeWorkspaceConversationMessages(composer.messages, 100),
|
|
3067
|
+
runSessions: normalizeWorkspaceComposerRunSessions(composer.runSessions),
|
|
3068
|
+
},
|
|
3069
|
+
nodeChats: normalizeWorkspaceNodeChatSessions(data.nodeChats),
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3072
|
+
|
|
3073
|
+
function readWorkspaceConversations(scopedRoot) {
|
|
3074
|
+
const filePath = workspaceConversationsPath(scopedRoot);
|
|
3075
|
+
if (!fs.existsSync(filePath)) return normalizeWorkspaceConversations({});
|
|
3076
|
+
try {
|
|
3077
|
+
return normalizeWorkspaceConversations(JSON.parse(fs.readFileSync(filePath, "utf-8")));
|
|
3078
|
+
} catch {
|
|
3079
|
+
return normalizeWorkspaceConversations({});
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
function writeWorkspaceConversations(scopedRoot, raw) {
|
|
3084
|
+
const filePath = workspaceConversationsPath(scopedRoot);
|
|
3085
|
+
const conversations = normalizeWorkspaceConversations(raw);
|
|
3086
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
3087
|
+
fs.writeFileSync(filePath, JSON.stringify(conversations, null, 2) + "\n", "utf-8");
|
|
3088
|
+
return conversations;
|
|
3089
|
+
}
|
|
3090
|
+
|
|
2901
3091
|
function workspaceSearchGuardrailsBlock() {
|
|
2902
3092
|
return [
|
|
2903
3093
|
"## 检索约束",
|
|
@@ -4071,7 +4261,7 @@ function workspaceTargetSlotForEdge(graph, edge) {
|
|
|
4071
4261
|
function isWorkspaceSemanticInputSlot(slot) {
|
|
4072
4262
|
const name = String(slot?.name || "");
|
|
4073
4263
|
const type = String(slot?.type || "");
|
|
4074
|
-
return type === "node" || name === "prev" || name === "next" || name === "skillsContext" || name === "mcpContext" || name === "workspaceContext" || name === "gitContext";
|
|
4264
|
+
return type === "node" || name === "prev" || name === "next" || name === "skillsContext" || name === "mcpContext" || name === "knowledgeContext" || name === "workspaceContext" || name === "gitContext";
|
|
4075
4265
|
}
|
|
4076
4266
|
|
|
4077
4267
|
function workspaceAgentInputBlock(inputValues = {}, inputMounts = {}) {
|
|
@@ -4904,11 +5094,86 @@ function workspaceContextObjectFromText(text, baseCwd, scopedRoot) {
|
|
|
4904
5094
|
};
|
|
4905
5095
|
}
|
|
4906
5096
|
|
|
5097
|
+
function workspaceKnowledgeSourceFromObject(source = {}, baseCwd = "", scopedRoot = "") {
|
|
5098
|
+
if (!source || typeof source !== "object" || Array.isArray(source)) return null;
|
|
5099
|
+
const rawPath = String(source.path || source.repoPath || source.cwd || source.workspaceRoot || "").trim();
|
|
5100
|
+
if (!rawPath) return null;
|
|
5101
|
+
const resolvedPath = workspaceResolvePath(baseCwd || scopedRoot, rawPath) || rawPath;
|
|
5102
|
+
return {
|
|
5103
|
+
id: String(source.id || source.mountPath || source.label || path.basename(resolvedPath) || "").trim(),
|
|
5104
|
+
label: String(source.label || source.id || source.mountPath || path.basename(resolvedPath) || "知识库").trim(),
|
|
5105
|
+
kind: String(source.kind || (source.repoUrl ? "git" : "local")).trim() || "local",
|
|
5106
|
+
type: String(source.type || "").trim(),
|
|
5107
|
+
path: resolvedPath,
|
|
5108
|
+
repoPath: resolvedPath,
|
|
5109
|
+
mountPath: String(source.mountPath || "").trim(),
|
|
5110
|
+
repoUrl: String(source.repoUrl || "").trim(),
|
|
5111
|
+
branch: String(source.branch || "").trim(),
|
|
5112
|
+
readonly: source.readonly !== false,
|
|
5113
|
+
};
|
|
5114
|
+
}
|
|
5115
|
+
|
|
5116
|
+
function workspaceKnowledgeSourcesFromText(text, baseCwd = "", scopedRoot = "") {
|
|
5117
|
+
const raw = String(text || "").trim();
|
|
5118
|
+
if (!raw) return [];
|
|
5119
|
+
const parsed = parseJsonText(raw, null);
|
|
5120
|
+
const candidates = Array.isArray(parsed)
|
|
5121
|
+
? parsed
|
|
5122
|
+
: (parsed && typeof parsed === "object" && Array.isArray(parsed.sources)
|
|
5123
|
+
? parsed.sources
|
|
5124
|
+
: (parsed && typeof parsed === "object" && (parsed.path || parsed.repoPath || parsed.cwd || parsed.workspaceRoot) ? [parsed] : []));
|
|
5125
|
+
if (candidates.length) {
|
|
5126
|
+
return candidates
|
|
5127
|
+
.map((source) => workspaceKnowledgeSourceFromObject(source, baseCwd, scopedRoot))
|
|
5128
|
+
.filter(Boolean);
|
|
5129
|
+
}
|
|
5130
|
+
const resolved = workspaceResolvePath(baseCwd || scopedRoot, raw) || raw;
|
|
5131
|
+
return [{
|
|
5132
|
+
id: path.basename(resolved) || "knowledge",
|
|
5133
|
+
label: path.basename(resolved) || "知识库",
|
|
5134
|
+
kind: "local",
|
|
5135
|
+
type: "",
|
|
5136
|
+
path: resolved,
|
|
5137
|
+
repoPath: resolved,
|
|
5138
|
+
mountPath: "",
|
|
5139
|
+
repoUrl: "",
|
|
5140
|
+
branch: "",
|
|
5141
|
+
readonly: true,
|
|
5142
|
+
}];
|
|
5143
|
+
}
|
|
5144
|
+
|
|
5145
|
+
function workspaceKnowledgeContextBlockFromSources(sources = []) {
|
|
5146
|
+
const valid = Array.isArray(sources) ? sources.filter((source) => source?.path || source?.repoPath) : [];
|
|
5147
|
+
if (!valid.length) return "";
|
|
5148
|
+
const lines = [
|
|
5149
|
+
"## 知识库上下文",
|
|
5150
|
+
"",
|
|
5151
|
+
"这些路径是只读知识库/上下文源,用于检索、阅读和分析;它们不代表当前执行 cwd。需要修改代码时,请先创建或使用可写工作区。",
|
|
5152
|
+
"",
|
|
5153
|
+
];
|
|
5154
|
+
valid.forEach((source, index) => {
|
|
5155
|
+
const label = String(source.label || source.id || source.mountPath || `知识库 ${index + 1}`).trim();
|
|
5156
|
+
const sourcePath = String(source.path || source.repoPath || "").trim();
|
|
5157
|
+
lines.push(`${index + 1}. ${label}`);
|
|
5158
|
+
if (source.kind) lines.push(` - 类型:${source.kind}${source.type ? `/${source.type}` : ""}`);
|
|
5159
|
+
if (sourcePath) lines.push(` - 路径:\`${sourcePath}\``);
|
|
5160
|
+
if (source.mountPath) lines.push(` - 挂载目录:${source.mountPath}`);
|
|
5161
|
+
if (source.repoUrl) lines.push(` - Git URL:${source.repoUrl}`);
|
|
5162
|
+
if (source.branch) lines.push(` - 分支:${source.branch}`);
|
|
5163
|
+
});
|
|
5164
|
+
return lines.join("\n");
|
|
5165
|
+
}
|
|
5166
|
+
|
|
4907
5167
|
function workspaceNodeWorkspaceContextBlock(graph, nodeId, outputs, scopedRoot = "", logicalCwd = "") {
|
|
4908
5168
|
const root = scopedRoot ? path.resolve(scopedRoot) : "";
|
|
4909
5169
|
const cwd = logicalCwd ? path.resolve(logicalCwd) : root;
|
|
5170
|
+
const knowledgeText = workspaceSemanticInputText(graph, nodeId, outputs, "knowledgeContext", scopedRoot);
|
|
5171
|
+
let knowledgeSources = workspaceKnowledgeSourcesFromText(knowledgeText, cwd || root, scopedRoot);
|
|
4910
5172
|
const workspaceText = workspaceSemanticInputText(graph, nodeId, outputs, "workspaceContext", scopedRoot);
|
|
4911
5173
|
let workspaceContext = workspaceContextObjectFromText(workspaceText, cwd || root, scopedRoot);
|
|
5174
|
+
if (!knowledgeSources.length && workspaceContext?.cwd) {
|
|
5175
|
+
knowledgeSources = workspaceKnowledgeSourcesFromText(JSON.stringify([workspaceContext]), cwd || root, scopedRoot);
|
|
5176
|
+
}
|
|
4912
5177
|
if (!workspaceContext && cwd && root && cwd !== root) {
|
|
4913
5178
|
workspaceContext = {
|
|
4914
5179
|
version: 1,
|
|
@@ -4920,7 +5185,8 @@ function workspaceNodeWorkspaceContextBlock(graph, nodeId, outputs, scopedRoot =
|
|
|
4920
5185
|
};
|
|
4921
5186
|
}
|
|
4922
5187
|
const gitContext = normalizeGitContext(workspaceSemanticInputText(graph, nodeId, outputs, "gitContext", scopedRoot));
|
|
4923
|
-
|
|
5188
|
+
const knowledgeBlock = workspaceKnowledgeContextBlockFromSources(knowledgeSources);
|
|
5189
|
+
if (!workspaceContext && !gitContext) return knowledgeBlock;
|
|
4924
5190
|
|
|
4925
5191
|
const contextCwd = workspaceContext?.cwd ? path.resolve(String(workspaceContext.cwd)) : "";
|
|
4926
5192
|
const workspaceRoot = workspaceContext?.workspaceRoot ? path.resolve(String(workspaceContext.workspaceRoot)) : contextCwd;
|
|
@@ -4943,7 +5209,7 @@ function workspaceNodeWorkspaceContextBlock(graph, nodeId, outputs, scopedRoot =
|
|
|
4943
5209
|
"- 读取、搜索、分析当前项目或资料时,优先从“当前工作目录上下文”开始;不要把节点的“当前执行目录”误认为项目根目录。",
|
|
4944
5210
|
"- 临时文件和正式产物仍必须按“文件边界”写入本节点的 `tmp/` 与 `outputs/`。",
|
|
4945
5211
|
].filter((line) => line !== "");
|
|
4946
|
-
return lines.join("\n");
|
|
5212
|
+
return [knowledgeBlock, lines.join("\n")].filter(Boolean).join("\n\n");
|
|
4947
5213
|
}
|
|
4948
5214
|
|
|
4949
5215
|
function workspaceDefaultWorkspaceContextBlock(scopedRoot = "", logicalCwd = "") {
|
|
@@ -5731,28 +5997,51 @@ async function runWorkspaceGraph(root, scopedRoot, payload, userCtx = {}, opts =
|
|
|
5731
5997
|
const pathSlot = inputSlots.find((slot) => String(slot?.name || "") === "path") ||
|
|
5732
5998
|
inputSlots.find((slot) => String(slot?.name || "") === "target");
|
|
5733
5999
|
const labelSlot = inputSlots.find((slot) => String(slot?.name || "") === "label");
|
|
6000
|
+
const knowledgeSlot = inputSlots.find((slot) => String(slot?.name || "") === "knowledgeContext");
|
|
5734
6001
|
const contextSlot = inputSlots.find((slot) => String(slot?.name || "") === "workspaceContext");
|
|
6002
|
+
const knowledgeText = workspaceSlotValue(knowledgeSlot);
|
|
6003
|
+
let knowledgeSources = workspaceKnowledgeSourcesFromText(knowledgeText, cwd || scopedRoot, scopedRoot);
|
|
5735
6004
|
const candidate = workspaceSlotValue(pathSlot) || workspaceInstanceText(instance) || inputText;
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
6005
|
+
if (!knowledgeSources.length && candidate) {
|
|
6006
|
+
const abs = path.resolve(scopedRoot, candidate);
|
|
6007
|
+
if (!fs.existsSync(abs) || !fs.statSync(abs).isDirectory()) {
|
|
6008
|
+
throw new Error(`Load Knowledge path does not exist or is not a directory: ${abs}`);
|
|
6009
|
+
}
|
|
6010
|
+
knowledgeSources = [{
|
|
6011
|
+
id: path.basename(abs) || "knowledge",
|
|
6012
|
+
label: workspaceSlotValue(labelSlot) || path.basename(abs) || "知识库",
|
|
6013
|
+
kind: "local",
|
|
6014
|
+
type: "",
|
|
6015
|
+
path: abs,
|
|
6016
|
+
repoPath: abs,
|
|
6017
|
+
mountPath: "",
|
|
6018
|
+
repoUrl: "",
|
|
6019
|
+
branch: "",
|
|
6020
|
+
readonly: true,
|
|
6021
|
+
}];
|
|
5739
6022
|
}
|
|
5740
|
-
cwd = abs;
|
|
5741
6023
|
const parsedContext = parseJsonText(workspaceSlotValue(contextSlot), {});
|
|
5742
6024
|
const configuredContext = parsedContext && typeof parsedContext === "object" && !Array.isArray(parsedContext) ? parsedContext : {};
|
|
5743
|
-
const
|
|
6025
|
+
const primarySource = knowledgeSources[0] || null;
|
|
6026
|
+
const primaryPath = primarySource?.path ? path.resolve(String(primarySource.path)) : "";
|
|
6027
|
+
const knowledgeContext = {
|
|
6028
|
+
version: 1,
|
|
6029
|
+
sources: knowledgeSources,
|
|
6030
|
+
};
|
|
6031
|
+
const workspaceContext = primarySource ? {
|
|
5744
6032
|
...configuredContext,
|
|
5745
6033
|
version: 1,
|
|
5746
|
-
label: workspaceSlotValue(labelSlot) || path.basename(
|
|
5747
|
-
cwd,
|
|
5748
|
-
workspaceRoot:
|
|
6034
|
+
label: primarySource.label || workspaceSlotValue(labelSlot) || path.basename(primaryPath) || "知识库",
|
|
6035
|
+
cwd: primaryPath,
|
|
6036
|
+
workspaceRoot: primaryPath,
|
|
5749
6037
|
pipelineWorkspace: path.resolve(scopedRoot),
|
|
5750
6038
|
previous: null,
|
|
5751
|
-
};
|
|
5752
|
-
let nextInstance = workspaceSetOutputSlot(instance, "
|
|
5753
|
-
nextInstance = workspaceSetOutputSlot(nextInstance, "
|
|
6039
|
+
} : null;
|
|
6040
|
+
let nextInstance = workspaceSetOutputSlot(instance, "knowledgeContext", JSON.stringify(knowledgeContext));
|
|
6041
|
+
nextInstance = workspaceSetOutputSlot(nextInstance, "workspaceContext", workspaceContext ? JSON.stringify(workspaceContext) : "");
|
|
6042
|
+
nextInstance = workspaceSetOutputSlot(nextInstance, "cwd", primaryPath);
|
|
5754
6043
|
graph.instances[nodeId] = nextInstance;
|
|
5755
|
-
publishNodeOutput(nodeId, JSON.stringify(
|
|
6044
|
+
publishNodeOutput(nodeId, JSON.stringify(knowledgeContext), { emitGraph: true });
|
|
5756
6045
|
emit({ type: "graph", nodeId, graph });
|
|
5757
6046
|
emit({ type: "node-done", nodeId, definitionId: defId });
|
|
5758
6047
|
continue;
|
|
@@ -6125,7 +6414,7 @@ async function runWorkspaceGraph(root, scopedRoot, payload, userCtx = {}, opts =
|
|
|
6125
6414
|
}
|
|
6126
6415
|
const historyBlock = workspaceNodeHistoryBlock(nodeId, scopedRoot, runPackage);
|
|
6127
6416
|
let workspaceContextBlock = workspaceNodeWorkspaceContextBlock(graph, nodeId, outputs, scopedRoot, cwd);
|
|
6128
|
-
if (isContextRunNode && workspaceBoolSlot(instance, "includeWorkspaceContext", true) && !workspaceContextBlock) {
|
|
6417
|
+
if (!isContextRunNode && workspaceBoolSlot(instance, "includeWorkspaceContext", true) && !workspaceContextBlock) {
|
|
6129
6418
|
workspaceContextBlock = workspaceDefaultWorkspaceContextBlock(scopedRoot, cwd);
|
|
6130
6419
|
}
|
|
6131
6420
|
const prompt = workspaceNodePrompt(graph, nodeId, promptUpstreamText, promptSkillsBlock, promptMcpBlock, runtimeInputValues, runPackage, historyBlock, workspaceContextBlock);
|
|
@@ -7752,7 +8041,7 @@ export function startUiServer({
|
|
|
7752
8041
|
}, userCtx);
|
|
7753
8042
|
const scopedRoot = scoped.error ? root : scoped.root;
|
|
7754
8043
|
json(res, 200, {
|
|
7755
|
-
path:
|
|
8044
|
+
path: workspacesPath(),
|
|
7756
8045
|
workspaces: listConfiguredWorkspaces(root, scopedRoot, userCtx),
|
|
7757
8046
|
customWorkspaces: readUserWorkspaces(userCtx),
|
|
7758
8047
|
});
|
|
@@ -7767,7 +8056,7 @@ export function startUiServer({
|
|
|
7767
8056
|
const payload = JSON.parse(await readBody(req));
|
|
7768
8057
|
const customWorkspaces = writeUserWorkspaces(userCtx, payload?.workspaces || payload?.customWorkspaces || []);
|
|
7769
8058
|
json(res, 200, {
|
|
7770
|
-
path:
|
|
8059
|
+
path: workspacesPath(),
|
|
7771
8060
|
workspaces: listConfiguredWorkspaces(root, root, userCtx),
|
|
7772
8061
|
customWorkspaces,
|
|
7773
8062
|
});
|
|
@@ -8628,6 +8917,47 @@ export function startUiServer({
|
|
|
8628
8917
|
return;
|
|
8629
8918
|
}
|
|
8630
8919
|
|
|
8920
|
+
if (url.pathname === "/api/workspace/conversations") {
|
|
8921
|
+
let payload = {};
|
|
8922
|
+
if (req.method === "POST") {
|
|
8923
|
+
try {
|
|
8924
|
+
payload = JSON.parse(await readBody(req));
|
|
8925
|
+
} catch {
|
|
8926
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
8927
|
+
return;
|
|
8928
|
+
}
|
|
8929
|
+
} else if (req.method !== "GET") {
|
|
8930
|
+
json(res, 405, { error: "Method not allowed" });
|
|
8931
|
+
return;
|
|
8932
|
+
}
|
|
8933
|
+
try {
|
|
8934
|
+
const scoped = resolveWorkspaceScopeRoot(root, {
|
|
8935
|
+
flowId: req.method === "POST" ? (payload.flowId || "") : (url.searchParams.get("flowId") || ""),
|
|
8936
|
+
flowSource: req.method === "POST" ? (payload.flowSource || "user") : (url.searchParams.get("flowSource") || "user"),
|
|
8937
|
+
archived: req.method === "POST"
|
|
8938
|
+
? (payload.archived === true || payload.flowArchived === true)
|
|
8939
|
+
: (url.searchParams.get("archived") === "1" || url.searchParams.get("flowArchived") === "1"),
|
|
8940
|
+
}, userCtx);
|
|
8941
|
+
if (scoped.error) {
|
|
8942
|
+
json(res, 400, { error: scoped.error });
|
|
8943
|
+
return;
|
|
8944
|
+
}
|
|
8945
|
+
if (req.method === "GET") {
|
|
8946
|
+
json(res, 200, { ok: true, conversations: readWorkspaceConversations(scoped.root) });
|
|
8947
|
+
return;
|
|
8948
|
+
}
|
|
8949
|
+
if (scoped.archived || isReadonlyBuiltinFlowSource(scoped.flowSource)) {
|
|
8950
|
+
json(res, 400, { error: "Cannot write conversations for builtin or archived pipeline workspace" });
|
|
8951
|
+
return;
|
|
8952
|
+
}
|
|
8953
|
+
const conversations = writeWorkspaceConversations(scoped.root, payload.conversations || payload);
|
|
8954
|
+
json(res, 200, { ok: true, conversations });
|
|
8955
|
+
} catch (e) {
|
|
8956
|
+
json(res, 500, { error: (e && e.message) || String(e) });
|
|
8957
|
+
}
|
|
8958
|
+
return;
|
|
8959
|
+
}
|
|
8960
|
+
|
|
8631
8961
|
if (req.method === "POST" && url.pathname === "/api/workspace/generate") {
|
|
8632
8962
|
let payload;
|
|
8633
8963
|
try {
|
|
@@ -1,47 +1,56 @@
|
|
|
1
1
|
---
|
|
2
|
-
#
|
|
2
|
+
# 内置节点:加载知识库
|
|
3
3
|
description: |
|
|
4
|
-
|
|
4
|
+
Load one or more read-only knowledge sources for downstream Agent nodes.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
`path` supports `${workspaceRoot}`, `${pipelineWorkspace}`, `${flowDir}`, absolute paths, and paths relative to the input workspace context.
|
|
12
|
-
displayName: CD Workspace
|
|
6
|
+
This node does not change the runtime cwd. It publishes `knowledgeContext`
|
|
7
|
+
for reading/searching referenced repos or folders. `workspaceContext` and
|
|
8
|
+
`cwd` are retained as legacy compatibility outputs for the first source.
|
|
9
|
+
displayName: 加载知识库
|
|
13
10
|
input:
|
|
14
11
|
- type: node
|
|
15
12
|
name: prev
|
|
16
13
|
default: ""
|
|
14
|
+
- type: text
|
|
15
|
+
name: knowledgeContext
|
|
16
|
+
default: ""
|
|
17
|
+
showOnNode: false
|
|
17
18
|
- type: text
|
|
18
19
|
name: path
|
|
19
20
|
default: ""
|
|
20
|
-
|
|
21
|
-
showOnNode: true
|
|
21
|
+
showOnNode: false
|
|
22
22
|
- type: text
|
|
23
23
|
name: mode
|
|
24
24
|
default: "set"
|
|
25
|
+
showOnNode: false
|
|
25
26
|
- type: text
|
|
26
27
|
name: label
|
|
27
28
|
default: ""
|
|
29
|
+
showOnNode: false
|
|
28
30
|
- type: text
|
|
29
31
|
name: workspaceContext
|
|
30
32
|
default: ""
|
|
33
|
+
showOnNode: false
|
|
31
34
|
output:
|
|
32
35
|
- type: node
|
|
33
36
|
name: next
|
|
34
37
|
default: ""
|
|
35
38
|
- type: text
|
|
36
|
-
name:
|
|
39
|
+
name: knowledgeContext
|
|
37
40
|
default: ""
|
|
38
41
|
required: true
|
|
39
42
|
showOnNode: true
|
|
43
|
+
- type: text
|
|
44
|
+
name: workspaceContext
|
|
45
|
+
default: ""
|
|
46
|
+
showOnNode: false
|
|
40
47
|
- type: file
|
|
41
48
|
name: cwd
|
|
42
49
|
default: ""
|
|
50
|
+
showOnNode: false
|
|
43
51
|
- type: text
|
|
44
52
|
name: previous
|
|
45
53
|
default: ""
|
|
54
|
+
showOnNode: false
|
|
46
55
|
---
|
|
47
|
-
|
|
56
|
+
Load selected knowledge sources for downstream agents.
|