@fieldwangai/agentflow 0.1.71 → 0.1.72
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/admin-storage-config.mjs +90 -5
- package/bin/lib/composer-skill-router.mjs +9 -1
- package/bin/lib/paths.mjs +28 -0
- package/bin/lib/skill-registry.mjs +20 -7
- package/bin/lib/ui-server.mjs +44 -15
- package/builtin/web-ui/dist/assets/index-NH_rVAzF.css +1 -0
- package/builtin/web-ui/dist/assets/{index-C8JMDUZb.js → index-t_zkIiXD.js} +89 -89
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/builtin/web-ui/dist/assets/index-B3Wr-I3p.css +0 -1
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
expandAgentflowHomePath,
|
|
8
8
|
getAgentflowDataRoot,
|
|
9
9
|
getAgentflowDataRootOverride,
|
|
10
|
+
getAgentflowSkillsRoot,
|
|
11
|
+
getAgentflowSkillsRootOverride,
|
|
10
12
|
writeAgentflowDataRootOverride,
|
|
13
|
+
writeAgentflowSkillsRootOverride,
|
|
11
14
|
} from "./paths.mjs";
|
|
12
15
|
|
|
13
16
|
function defaultAgentflowDataRoot() {
|
|
@@ -28,6 +31,14 @@ function normalizeDataRoot(raw) {
|
|
|
28
31
|
return dataRoot;
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
function normalizeSkillsRoot(raw) {
|
|
35
|
+
const skillsRoot = expandAgentflowHomePath(raw);
|
|
36
|
+
if (skillsRoot && !path.isAbsolute(skillsRoot)) {
|
|
37
|
+
throw new Error("skillsRoot must be an absolute path");
|
|
38
|
+
}
|
|
39
|
+
return skillsRoot;
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
function copyEntry(src, dest) {
|
|
32
43
|
const stat = fs.lstatSync(src);
|
|
33
44
|
if (stat.isSymbolicLink()) {
|
|
@@ -45,6 +56,12 @@ function copyEntry(src, dest) {
|
|
|
45
56
|
fs.copyFileSync(src, dest);
|
|
46
57
|
}
|
|
47
58
|
|
|
59
|
+
function copyEntryIfMissing(src, dest) {
|
|
60
|
+
if (fs.existsSync(dest)) return false;
|
|
61
|
+
copyEntry(src, dest);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
48
65
|
function migrateDataRoot(oldRoot, newRoot) {
|
|
49
66
|
const from = path.resolve(oldRoot);
|
|
50
67
|
const to = path.resolve(newRoot);
|
|
@@ -63,9 +80,49 @@ function migrateDataRoot(oldRoot, newRoot) {
|
|
|
63
80
|
return { migrated: true, copied: true };
|
|
64
81
|
}
|
|
65
82
|
|
|
83
|
+
function legacyCodexSkillsRoot() {
|
|
84
|
+
return path.join(os.homedir(), ".codex", "skills");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function migrateSkillsRoot(oldRoot, newRoot) {
|
|
88
|
+
const from = path.resolve(oldRoot);
|
|
89
|
+
const to = path.resolve(newRoot);
|
|
90
|
+
if (from === to) return { migrated: false, copied: false };
|
|
91
|
+
if (pathInside(from, to)) {
|
|
92
|
+
throw new Error("skillsRoot target cannot be inside the current skills root");
|
|
93
|
+
}
|
|
94
|
+
if (pathInside(to, from)) {
|
|
95
|
+
throw new Error("skillsRoot target cannot be a parent of the current skills root");
|
|
96
|
+
}
|
|
97
|
+
fs.mkdirSync(to, { recursive: true });
|
|
98
|
+
if (!fs.existsSync(from)) return { migrated: true, copied: false };
|
|
99
|
+
for (const name of fs.readdirSync(from)) {
|
|
100
|
+
copyEntry(path.join(from, name), path.join(to, name));
|
|
101
|
+
}
|
|
102
|
+
return { migrated: true, copied: true };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function migrateLegacyCodexSkills(targetRoot) {
|
|
106
|
+
const from = legacyCodexSkillsRoot();
|
|
107
|
+
const to = path.resolve(targetRoot);
|
|
108
|
+
const source = path.resolve(from);
|
|
109
|
+
if (source === to || !fs.existsSync(source)) return { migrated: false, copied: 0, source };
|
|
110
|
+
if (pathInside(source, to) || pathInside(to, source)) return { migrated: false, copied: 0, source };
|
|
111
|
+
fs.mkdirSync(to, { recursive: true });
|
|
112
|
+
let copied = 0;
|
|
113
|
+
for (const name of fs.readdirSync(source)) {
|
|
114
|
+
if (copyEntryIfMissing(path.join(source, name), path.join(to, name))) copied += 1;
|
|
115
|
+
}
|
|
116
|
+
return { migrated: copied > 0, copied, source };
|
|
117
|
+
}
|
|
118
|
+
|
|
66
119
|
export function readAdminStorageConfig() {
|
|
67
120
|
const envDataRoot = normalizeDataRoot(process.env.AGENTFLOW_HOME || "");
|
|
121
|
+
const envSkillsRoot = normalizeSkillsRoot(process.env.AGENTFLOW_SKILLS_ROOT || "");
|
|
68
122
|
const configuredDataRoot = getAgentflowDataRootOverride();
|
|
123
|
+
const configuredSkillsRoot = getAgentflowSkillsRootOverride();
|
|
124
|
+
const skillsRoot = getAgentflowSkillsRoot();
|
|
125
|
+
const legacySkillsRoot = legacyCodexSkillsRoot();
|
|
69
126
|
return {
|
|
70
127
|
version: 1,
|
|
71
128
|
dataRoot: getAgentflowDataRoot(),
|
|
@@ -73,21 +130,49 @@ export function readAdminStorageConfig() {
|
|
|
73
130
|
defaultDataRoot: defaultAgentflowDataRoot(),
|
|
74
131
|
envDataRoot,
|
|
75
132
|
envLocked: Boolean(envDataRoot),
|
|
133
|
+
skillsRoot,
|
|
134
|
+
configuredSkillsRoot,
|
|
135
|
+
defaultSkillsRoot: path.join(getAgentflowDataRoot(), "skills"),
|
|
136
|
+
envSkillsRoot,
|
|
137
|
+
skillsEnvLocked: Boolean(envSkillsRoot),
|
|
138
|
+
legacySkillsRoot,
|
|
139
|
+
legacySkillsRootExists: fs.existsSync(legacySkillsRoot),
|
|
76
140
|
configPath: AGENTFLOW_HOME_CONFIG_PATH,
|
|
77
141
|
};
|
|
78
142
|
}
|
|
79
143
|
|
|
80
144
|
export function writeAdminStorageConfig(config = {}) {
|
|
81
145
|
const current = readAdminStorageConfig();
|
|
82
|
-
if (current.envLocked) {
|
|
83
|
-
throw new Error("AGENTFLOW_HOME is set; update the environment variable instead of Admin Settings");
|
|
84
|
-
}
|
|
85
146
|
const nextRoot = normalizeDataRoot(config.dataRoot ?? "");
|
|
86
147
|
if (!nextRoot) throw new Error("dataRoot is required");
|
|
87
|
-
|
|
88
|
-
|
|
148
|
+
let migration = { migrated: false, copied: false };
|
|
149
|
+
if (current.envLocked) {
|
|
150
|
+
if (path.resolve(nextRoot) !== path.resolve(current.dataRoot)) {
|
|
151
|
+
throw new Error("AGENTFLOW_HOME is set; update the environment variable instead of Admin Settings");
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
migration = migrateDataRoot(current.dataRoot, nextRoot);
|
|
155
|
+
writeAgentflowDataRootOverride(nextRoot);
|
|
156
|
+
}
|
|
157
|
+
const afterDataRoot = readAdminStorageConfig();
|
|
158
|
+
if (afterDataRoot.skillsEnvLocked) {
|
|
159
|
+
return {
|
|
160
|
+
...afterDataRoot,
|
|
161
|
+
migration,
|
|
162
|
+
skillsMigration: { skipped: true, reason: "AGENTFLOW_SKILLS_ROOT is set" },
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const nextSkillsRoot = normalizeSkillsRoot(config.skillsRoot ?? afterDataRoot.defaultSkillsRoot);
|
|
166
|
+
if (!nextSkillsRoot) throw new Error("skillsRoot is required");
|
|
167
|
+
const skillsMigration = migrateSkillsRoot(current.skillsRoot, nextSkillsRoot);
|
|
168
|
+
writeAgentflowSkillsRootOverride(nextSkillsRoot);
|
|
169
|
+
const legacySkillsMigration = config.migrateLegacySkills === false
|
|
170
|
+
? { migrated: false, copied: 0, source: legacyCodexSkillsRoot(), skipped: true }
|
|
171
|
+
: migrateLegacyCodexSkills(nextSkillsRoot);
|
|
89
172
|
return {
|
|
90
173
|
...readAdminStorageConfig(),
|
|
91
174
|
migration,
|
|
175
|
+
skillsMigration,
|
|
176
|
+
legacySkillsMigration,
|
|
92
177
|
};
|
|
93
178
|
}
|
|
@@ -137,18 +137,26 @@ export function readComposerSkillDetail(packageRoot, workspaceRoot, keyOrName) {
|
|
|
137
137
|
return registryReadSkillDetail(packageRoot, workspaceRoot, keyOrName);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
function skillNameFromKey(keyOrName) {
|
|
141
|
+
const value = String(keyOrName || "").trim();
|
|
142
|
+
if (!value) return "";
|
|
143
|
+
const idx = value.indexOf(":");
|
|
144
|
+
return idx >= 0 ? value.slice(idx + 1).trim() : value;
|
|
145
|
+
}
|
|
146
|
+
|
|
140
147
|
export function loadResourcesForSkillKeys(skillKeys, packageRoot, workspaceRoot) {
|
|
141
148
|
if (!Array.isArray(skillKeys) || skillKeys.length === 0) {
|
|
142
149
|
return { skills: [], references: [], skillsHint: "", hasContext: false };
|
|
143
150
|
}
|
|
144
151
|
const wanted = new Set(skillKeys.map((x) => String(x || "").trim()).filter(Boolean));
|
|
145
152
|
if (wanted.size === 0) return { skills: [], references: [], skillsHint: "", hasContext: false };
|
|
153
|
+
const wantedNames = new Set(Array.from(wanted).map(skillNameFromKey).filter(Boolean));
|
|
146
154
|
|
|
147
155
|
const exactByKey = new Map(registryListSkills(packageRoot, workspaceRoot).map((item) => [item.key, item]));
|
|
148
156
|
const candidateItems = [];
|
|
149
157
|
const seenKeys = new Set();
|
|
150
158
|
for (const item of registryListUniqueSkills(packageRoot, workspaceRoot)) {
|
|
151
|
-
if (!wanted.has(item.key) && !wanted.has(item.name)) continue;
|
|
159
|
+
if (!wanted.has(item.key) && !wanted.has(item.name) && !wantedNames.has(item.name)) continue;
|
|
152
160
|
candidateItems.push(item);
|
|
153
161
|
seenKeys.add(item.key);
|
|
154
162
|
}
|
package/bin/lib/paths.mjs
CHANGED
|
@@ -49,6 +49,11 @@ export function getAgentflowDataRootOverride() {
|
|
|
49
49
|
return expandAgentflowHomePath(config.dataRoot || "");
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
export function getAgentflowSkillsRootOverride() {
|
|
53
|
+
const config = readAgentflowHomeConfig();
|
|
54
|
+
return expandAgentflowHomePath(config.skillsRoot || "");
|
|
55
|
+
}
|
|
56
|
+
|
|
52
57
|
export function writeAgentflowDataRootOverride(dataRoot) {
|
|
53
58
|
const nextRoot = expandAgentflowHomePath(dataRoot);
|
|
54
59
|
if (nextRoot && !path.isAbsolute(nextRoot)) {
|
|
@@ -62,6 +67,19 @@ export function writeAgentflowDataRootOverride(dataRoot) {
|
|
|
62
67
|
return nextRoot;
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
export function writeAgentflowSkillsRootOverride(skillsRoot) {
|
|
71
|
+
const nextRoot = expandAgentflowHomePath(skillsRoot);
|
|
72
|
+
if (nextRoot && !path.isAbsolute(nextRoot)) {
|
|
73
|
+
throw new Error("skillsRoot must be an absolute path");
|
|
74
|
+
}
|
|
75
|
+
const current = readAgentflowHomeConfig();
|
|
76
|
+
const next = { ...current, skillsRoot: nextRoot };
|
|
77
|
+
if (!nextRoot) delete next.skillsRoot;
|
|
78
|
+
fs.mkdirSync(path.dirname(AGENTFLOW_HOME_CONFIG_PATH), { recursive: true });
|
|
79
|
+
fs.writeFileSync(AGENTFLOW_HOME_CONFIG_PATH, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
80
|
+
return nextRoot;
|
|
81
|
+
}
|
|
82
|
+
|
|
65
83
|
export function getAgentflowDataRoot() {
|
|
66
84
|
const env = process.env.AGENTFLOW_HOME;
|
|
67
85
|
if (env != null && String(env).trim() !== "") {
|
|
@@ -72,6 +90,16 @@ export function getAgentflowDataRoot() {
|
|
|
72
90
|
return path.join(os.homedir(), "agentflow");
|
|
73
91
|
}
|
|
74
92
|
|
|
93
|
+
export function getAgentflowSkillsRoot() {
|
|
94
|
+
const env = process.env.AGENTFLOW_SKILLS_ROOT;
|
|
95
|
+
if (env != null && String(env).trim() !== "") {
|
|
96
|
+
return expandAgentflowHomePath(env);
|
|
97
|
+
}
|
|
98
|
+
const configured = getAgentflowSkillsRootOverride();
|
|
99
|
+
if (configured) return configured;
|
|
100
|
+
return path.join(getAgentflowDataRoot(), "skills");
|
|
101
|
+
}
|
|
102
|
+
|
|
75
103
|
export const AGENTFLOW_DEFAULT_USER_ID = "";
|
|
76
104
|
|
|
77
105
|
export function sanitizeAgentflowUserId(userId) {
|
|
@@ -2,6 +2,7 @@ import fs from "fs";
|
|
|
2
2
|
import os from "os";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import yaml from "js-yaml";
|
|
5
|
+
import { getAgentflowSkillsRoot } from "./paths.mjs";
|
|
5
6
|
|
|
6
7
|
const fileCache = new Map();
|
|
7
8
|
const CACHE_TTL_MS = 60_000;
|
|
@@ -9,10 +10,11 @@ const SOURCE_PRIORITY = new Map([
|
|
|
9
10
|
["workspace-agents", 100],
|
|
10
11
|
["workspace-codex", 95],
|
|
11
12
|
["workspace-cursor", 90],
|
|
13
|
+
["agentflow-root", 85],
|
|
12
14
|
["builtin", 80],
|
|
13
|
-
["global-agents",
|
|
14
|
-
["global-codex",
|
|
15
|
-
["global-cursor",
|
|
15
|
+
["global-agents", 55],
|
|
16
|
+
["global-codex", 50],
|
|
17
|
+
["global-cursor", 45],
|
|
16
18
|
]);
|
|
17
19
|
|
|
18
20
|
function readFileCached(absPath) {
|
|
@@ -28,6 +30,10 @@ function readFileCached(absPath) {
|
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
export function clearSkillRegistryCache() {
|
|
34
|
+
fileCache.clear();
|
|
35
|
+
}
|
|
36
|
+
|
|
31
37
|
export function stripSkillFrontmatter(content) {
|
|
32
38
|
const raw = String(content || "");
|
|
33
39
|
const match = raw.match(/^---\s*\r?\n[\s\S]*?\r?\n---\s*\r?\n?/);
|
|
@@ -109,11 +115,17 @@ export function defaultSkillSources(packageRoot, workspaceRoot) {
|
|
|
109
115
|
},
|
|
110
116
|
];
|
|
111
117
|
if (workspaceRoot) sources.push(...workspaceSkillSources(workspaceRoot, "workspace", "工作区"));
|
|
118
|
+
sources.push({
|
|
119
|
+
source: "agentflow-root",
|
|
120
|
+
sourceLabel: "AgentFlow Skills",
|
|
121
|
+
dir: getAgentflowSkillsRoot(),
|
|
122
|
+
installedBy: "agentflow-admin",
|
|
123
|
+
});
|
|
112
124
|
const home = os.homedir();
|
|
113
125
|
sources.push(
|
|
114
|
-
{ source: "global-agents", sourceLabel: "
|
|
115
|
-
{ source: "global-cursor", sourceLabel: "
|
|
116
|
-
{ source: "global-codex", sourceLabel: "
|
|
126
|
+
{ source: "global-agents", sourceLabel: "Legacy ~/.agents", dir: path.join(home, ".agents", "skills"), installedBy: "global" },
|
|
127
|
+
{ source: "global-cursor", sourceLabel: "Legacy ~/.cursor", dir: path.join(home, ".cursor", "skills"), installedBy: "global" },
|
|
128
|
+
{ source: "global-codex", sourceLabel: "Legacy ~/.codex", dir: path.join(home, ".codex", "skills"), installedBy: "skillhub-legacy" },
|
|
117
129
|
);
|
|
118
130
|
return sources;
|
|
119
131
|
}
|
|
@@ -183,9 +195,10 @@ export function listUniqueSkills(packageRoot, workspaceRoot, opts = {}) {
|
|
|
183
195
|
export function readSkillDetail(packageRoot, workspaceRoot, keyOrName) {
|
|
184
196
|
const wanted = String(keyOrName || "").trim();
|
|
185
197
|
if (!wanted) return null;
|
|
198
|
+
const wantedName = wanted.includes(":") ? wanted.slice(wanted.indexOf(":") + 1).trim() : wanted;
|
|
186
199
|
const all = listSkills(packageRoot, workspaceRoot);
|
|
187
200
|
const item = all.find((skill) => skill.key === wanted)
|
|
188
|
-
|| dedupeSkillsByName(all).find((skill) => skill.name === wanted);
|
|
201
|
+
|| dedupeSkillsByName(all).find((skill) => skill.name === wanted || skill.name === wantedName);
|
|
189
202
|
if (!item) return null;
|
|
190
203
|
return item;
|
|
191
204
|
}
|
package/bin/lib/ui-server.mjs
CHANGED
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
PACKAGE_ROOT,
|
|
46
46
|
ARCHIVED_PIPELINES_DIR_NAME,
|
|
47
47
|
getAgentflowDataRoot,
|
|
48
|
+
getAgentflowSkillsRoot,
|
|
48
49
|
getAgentflowUserConfigAbs,
|
|
49
50
|
getAgentflowUserDataRoot,
|
|
50
51
|
getUserPipelinesRoot,
|
|
@@ -62,6 +63,7 @@ import {
|
|
|
62
63
|
buildSkillInjectionBlock,
|
|
63
64
|
buildSkillCompactInjectionBlock,
|
|
64
65
|
} from "./composer-skill-router.mjs";
|
|
66
|
+
import { clearSkillRegistryCache } from "./skill-registry.mjs";
|
|
65
67
|
import { COMPOSER_NODE_SPEC_FILENAME } from "./composer-planner.mjs";
|
|
66
68
|
import { listRecentRunsFromDisk } from "./recent-runs.mjs";
|
|
67
69
|
import {
|
|
@@ -1134,6 +1136,22 @@ function normalizeSkillhubListPayload(raw) {
|
|
|
1134
1136
|
}).filter((x) => x.name);
|
|
1135
1137
|
}
|
|
1136
1138
|
|
|
1139
|
+
function skillhubManagedSkillsDir() {
|
|
1140
|
+
const dir = getAgentflowSkillsRoot();
|
|
1141
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1142
|
+
return dir;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
function skillhubListArgs(target, agent) {
|
|
1146
|
+
const normalizedTarget = String(target || "agentflow").trim();
|
|
1147
|
+
const normalizedAgent = String(agent || "codex").trim();
|
|
1148
|
+
const args = ["list", "--json"];
|
|
1149
|
+
if (normalizedTarget === "all") args.push("--all");
|
|
1150
|
+
else if (normalizedTarget === "legacy-global") args.push("--global", "--agent", normalizedAgent);
|
|
1151
|
+
else args.push("--dir", skillhubManagedSkillsDir());
|
|
1152
|
+
return args;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1137
1155
|
function skillhubInstallArgs(payload, { uninstall = false } = {}) {
|
|
1138
1156
|
const slug = String(payload?.slug || payload?.name || "").trim();
|
|
1139
1157
|
if (!slug && !payload?.collection) return null;
|
|
@@ -1144,10 +1162,12 @@ function skillhubInstallArgs(payload, { uninstall = false } = {}) {
|
|
|
1144
1162
|
args.push(slug);
|
|
1145
1163
|
}
|
|
1146
1164
|
if (payload?.skillId) args.push("--skill-id", String(payload.skillId).trim());
|
|
1147
|
-
const target = String(payload?.target || "
|
|
1165
|
+
const target = String(payload?.target || "agentflow").trim();
|
|
1148
1166
|
const agent = String(payload?.agent || "codex").trim();
|
|
1149
|
-
if (target === "global") {
|
|
1167
|
+
if (target === "global" || target === "legacy-global") {
|
|
1150
1168
|
args.push("--global", "--agent", agent);
|
|
1169
|
+
} else if (target === "agentflow") {
|
|
1170
|
+
args.push("--dir", skillhubManagedSkillsDir());
|
|
1151
1171
|
} else if (payload?.dir) {
|
|
1152
1172
|
args.push("--dir", String(payload.dir).trim());
|
|
1153
1173
|
}
|
|
@@ -5620,6 +5640,7 @@ export function startUiServer({
|
|
|
5620
5640
|
}
|
|
5621
5641
|
try {
|
|
5622
5642
|
const config = writeAdminStorageConfig(payload?.config || payload || {});
|
|
5643
|
+
clearSkillRegistryCache();
|
|
5623
5644
|
json(res, 200, { ok: true, config });
|
|
5624
5645
|
} catch (e) {
|
|
5625
5646
|
json(res, 400, { error: (e && e.message) || String(e) });
|
|
@@ -7058,17 +7079,19 @@ export function startUiServer({
|
|
|
7058
7079
|
}
|
|
7059
7080
|
|
|
7060
7081
|
if (req.method === "GET" && url.pathname === "/api/skillhub/list") {
|
|
7061
|
-
const target = url.searchParams.get("target") || "
|
|
7082
|
+
const target = url.searchParams.get("target") || "agentflow";
|
|
7062
7083
|
const agent = url.searchParams.get("agent") || "codex";
|
|
7063
|
-
const args =
|
|
7064
|
-
if (target === "all") args.push("--all");
|
|
7065
|
-
else if (target === "global") args.push("--global", "--agent", agent);
|
|
7084
|
+
const args = skillhubListArgs(target, agent);
|
|
7066
7085
|
const result = await runSkillhub(args, { cwd: root });
|
|
7067
7086
|
if (!result.ok) {
|
|
7068
7087
|
json(res, 500, { error: result.error, stdout: result.stdout });
|
|
7069
7088
|
return;
|
|
7070
7089
|
}
|
|
7071
|
-
json(res, 200, {
|
|
7090
|
+
json(res, 200, {
|
|
7091
|
+
skills: normalizeSkillhubListPayload(parseJsonText(result.stdout, [])),
|
|
7092
|
+
target,
|
|
7093
|
+
skillsRoot: target === "agentflow" ? getAgentflowSkillsRoot() : "",
|
|
7094
|
+
});
|
|
7072
7095
|
return;
|
|
7073
7096
|
}
|
|
7074
7097
|
|
|
@@ -7113,6 +7136,10 @@ export function startUiServer({
|
|
|
7113
7136
|
}
|
|
7114
7137
|
|
|
7115
7138
|
if (req.method === "POST" && url.pathname === "/api/skillhub/install") {
|
|
7139
|
+
if (!authUser?.isAdmin) {
|
|
7140
|
+
json(res, 403, { error: "Admin required" });
|
|
7141
|
+
return;
|
|
7142
|
+
}
|
|
7116
7143
|
let payload;
|
|
7117
7144
|
try {
|
|
7118
7145
|
payload = JSON.parse(await readBody(req));
|
|
@@ -7125,16 +7152,13 @@ export function startUiServer({
|
|
|
7125
7152
|
json(res, 400, { error: "Missing skill slug or collection" });
|
|
7126
7153
|
return;
|
|
7127
7154
|
}
|
|
7128
|
-
if (payload?.collection && !authUser?.isAdmin) {
|
|
7129
|
-
json(res, 403, { error: "Admin required" });
|
|
7130
|
-
return;
|
|
7131
|
-
}
|
|
7132
7155
|
const beforeSkills = payload?.collection ? listComposerSkills(PACKAGE_ROOT, root) : [];
|
|
7133
7156
|
const result = await runSkillhub(args, { cwd: root, timeoutMs: 180_000, maxBuffer: 4 * 1024 * 1024 });
|
|
7134
7157
|
if (!result.ok) {
|
|
7135
7158
|
json(res, 500, { error: result.error, stdout: result.stdout });
|
|
7136
7159
|
return;
|
|
7137
7160
|
}
|
|
7161
|
+
clearSkillRegistryCache();
|
|
7138
7162
|
let skillCollections = null;
|
|
7139
7163
|
if (payload?.collection) {
|
|
7140
7164
|
const afterSkills = listComposerSkills(PACKAGE_ROOT, root);
|
|
@@ -7146,6 +7170,10 @@ export function startUiServer({
|
|
|
7146
7170
|
}
|
|
7147
7171
|
|
|
7148
7172
|
if (req.method === "POST" && url.pathname === "/api/skillhub/uninstall") {
|
|
7173
|
+
if (!authUser?.isAdmin) {
|
|
7174
|
+
json(res, 403, { error: "Admin required" });
|
|
7175
|
+
return;
|
|
7176
|
+
}
|
|
7149
7177
|
let payload;
|
|
7150
7178
|
try {
|
|
7151
7179
|
payload = JSON.parse(await readBody(req));
|
|
@@ -7158,21 +7186,22 @@ export function startUiServer({
|
|
|
7158
7186
|
json(res, 400, { error: "Missing skill slug or collection" });
|
|
7159
7187
|
return;
|
|
7160
7188
|
}
|
|
7161
|
-
if (payload?.collection && !authUser?.isAdmin) {
|
|
7162
|
-
json(res, 403, { error: "Admin required" });
|
|
7163
|
-
return;
|
|
7164
|
-
}
|
|
7165
7189
|
const result = await runSkillhub(args, { cwd: root, timeoutMs: 120_000, maxBuffer: 4 * 1024 * 1024 });
|
|
7166
7190
|
if (!result.ok) {
|
|
7167
7191
|
json(res, 500, { error: result.error, stdout: result.stdout });
|
|
7168
7192
|
return;
|
|
7169
7193
|
}
|
|
7194
|
+
clearSkillRegistryCache();
|
|
7170
7195
|
const skillCollections = payload?.collection ? removeSkillhubCollectionGroup(userCtx, payload.collection, root) : null;
|
|
7171
7196
|
json(res, 200, { ok: true, stdout: result.stdout, skillCollections });
|
|
7172
7197
|
return;
|
|
7173
7198
|
}
|
|
7174
7199
|
|
|
7175
7200
|
if (req.method === "POST" && url.pathname === "/api/skillhub/update") {
|
|
7201
|
+
if (!authUser?.isAdmin) {
|
|
7202
|
+
json(res, 403, { error: "Admin required" });
|
|
7203
|
+
return;
|
|
7204
|
+
}
|
|
7176
7205
|
const result = await runSkillhub(["update"], { cwd: root, timeoutMs: 180_000, maxBuffer: 4 * 1024 * 1024 });
|
|
7177
7206
|
if (!result.ok) {
|
|
7178
7207
|
json(res, 500, { error: result.error, stdout: result.stdout });
|