@michengai/dsh-skills-manager 1.2.21 → 1.2.23
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/CHANGELOG.md +20 -0
- package/CHANGELOG.zh-CN.md +20 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/lib/client.js +6 -6
- package/lib/core.js +14 -11
- package/lib/index.js +503 -357
- package/lib/plugin-updater.js +60 -0
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -39,7 +39,7 @@ import { listClawHub, searchClawHub } from "./clawhub-catalog.js";
|
|
|
39
39
|
import { searchSkillsSh } from "./skills-sh-catalog.js";
|
|
40
40
|
import { loadCatalogPackage, readCatalogSkill } from "./catalog-skill.js";
|
|
41
41
|
const name = "skills-manager";
|
|
42
|
-
const inject = ["
|
|
42
|
+
const inject = ["connection", "skills", "tools", "sessions"];
|
|
43
43
|
const CLIENT_MARKER_HEADER = "x-dsh-skills-manager";
|
|
44
44
|
const MAX_LOG_BYTES = 1 << 20;
|
|
45
45
|
const MAX_UPLOAD_BODY_BYTES = 88 << 20;
|
|
@@ -62,106 +62,79 @@ function makeLog() {
|
|
|
62
62
|
await queue;
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
75
|
-
req.on("data", (c) => {
|
|
76
|
-
if (settled) return;
|
|
77
|
-
size += c.length;
|
|
78
|
-
if (size > limit) {
|
|
79
|
-
const error = new Error("body too large");
|
|
80
|
-
error.statusCode = 413;
|
|
81
|
-
error.code = "error.proto.bodyTooLarge";
|
|
82
|
-
fail(error);
|
|
83
|
-
req.resume();
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
chunks.push(c);
|
|
87
|
-
});
|
|
88
|
-
req.on("end", () => {
|
|
89
|
-
if (settled) return;
|
|
90
|
-
try {
|
|
91
|
-
const raw = Buffer.concat(chunks).toString("utf8");
|
|
92
|
-
const body = raw ? JSON.parse(raw) : {};
|
|
93
|
-
settled = true;
|
|
94
|
-
resolve(body);
|
|
95
|
-
} catch (caught) {
|
|
96
|
-
const e = caught;
|
|
97
|
-
const error = new Error(`invalid JSON body: ${e.message}`);
|
|
98
|
-
error.statusCode = 400;
|
|
99
|
-
error.code = "error.proto.invalidJson";
|
|
100
|
-
fail(error);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
req.on("error", fail);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
function parseAuthority(authority) {
|
|
65
|
+
async function readJsonBody(request, limit = 1 << 20) {
|
|
66
|
+
const text = await request.text();
|
|
67
|
+
if (Buffer.byteLength(text) > limit) {
|
|
68
|
+
const error = new Error("body too large");
|
|
69
|
+
error.statusCode = 413;
|
|
70
|
+
error.code = "error.proto.bodyTooLarge";
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
if (!text.trim()) return {};
|
|
107
74
|
try {
|
|
108
|
-
return
|
|
109
|
-
} catch {
|
|
110
|
-
|
|
75
|
+
return JSON.parse(text);
|
|
76
|
+
} catch (caught) {
|
|
77
|
+
const e = caught;
|
|
78
|
+
const error = new Error(`invalid JSON body: ${e.message}`);
|
|
79
|
+
error.statusCode = 400;
|
|
80
|
+
error.code = "error.proto.invalidJson";
|
|
81
|
+
throw error;
|
|
111
82
|
}
|
|
112
83
|
}
|
|
113
|
-
function
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return canonicalAuthority(authority, parsed) === authority.toLowerCase();
|
|
119
|
-
}
|
|
120
|
-
function isLoopbackHostname(hostname) {
|
|
121
|
-
if (hostname === "localhost" || hostname === "[::1]") return true;
|
|
122
|
-
const parts = hostname.split(".");
|
|
123
|
-
return parts.length === 4 && parts[0] === "127" && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255);
|
|
124
|
-
}
|
|
125
|
-
function isTrustedAuthority(hostUrl, trustedHosts) {
|
|
126
|
-
return trustedHosts.some((entry) => {
|
|
127
|
-
if (typeof entry !== "string") return false;
|
|
128
|
-
const entryUrl = parseAuthority(entry);
|
|
129
|
-
if (!entryUrl || !isCanonicalAuthority(entry, entryUrl)) return false;
|
|
130
|
-
return canonicalAuthority(entry, entryUrl) === entryUrl.hostname ? entryUrl.hostname === hostUrl.hostname : entryUrl.host === hostUrl.host;
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
function validateRequestOrigin(req, trustedHosts = []) {
|
|
134
|
-
const host = typeof req.headers.host === "string" ? req.headers.host : "";
|
|
135
|
-
const hostUrl = parseAuthority(host);
|
|
136
|
-
if (!hostUrl || !isCanonicalAuthority(host, hostUrl)) {
|
|
137
|
-
return { statusCode: 403, code: "error.proto.forbiddenHost", error: "forbidden host" };
|
|
138
|
-
}
|
|
139
|
-
if (!isLoopbackHostname(hostUrl.hostname) && !isTrustedAuthority(hostUrl, trustedHosts)) {
|
|
140
|
-
return { statusCode: 403, code: "error.proto.forbiddenHost", error: "forbidden host" };
|
|
141
|
-
}
|
|
142
|
-
if (req.headers["sec-fetch-site"] === "cross-site") {
|
|
143
|
-
return { statusCode: 403, code: "error.proto.forbiddenHost", error: "forbidden host" };
|
|
144
|
-
}
|
|
145
|
-
const origin = req.headers.origin;
|
|
146
|
-
if (typeof origin === "string") {
|
|
84
|
+
function validateMutationRequest(req) {
|
|
85
|
+
const marker = req.headers.get(CLIENT_MARKER_HEADER) || req.headers.get("x-michengai-client");
|
|
86
|
+
if (marker !== "1") return { statusCode: 403, code: "error.proto.forbidden", error: "forbidden mutation request" };
|
|
87
|
+
const origin = req.headers.get("origin");
|
|
88
|
+
if (origin) {
|
|
147
89
|
try {
|
|
148
|
-
|
|
149
|
-
|
|
90
|
+
const originHost = new URL(origin).host;
|
|
91
|
+
const reqHost = req.headers.get("host") || new URL(req.url).host;
|
|
92
|
+
if (originHost !== reqHost) {
|
|
93
|
+
return { statusCode: 403, code: "error.proto.forbidden", error: "forbidden cross-origin mutation request" };
|
|
150
94
|
}
|
|
151
95
|
} catch {
|
|
152
|
-
return { statusCode: 403, code: "error.proto.
|
|
96
|
+
return { statusCode: 403, code: "error.proto.forbidden", error: "forbidden origin" };
|
|
153
97
|
}
|
|
154
98
|
}
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
function validateMutationRequest(req, trustedHosts) {
|
|
158
|
-
const hostError = validateRequestOrigin(req, trustedHosts);
|
|
159
|
-
if (hostError) return hostError;
|
|
160
|
-
if (req.headers[CLIENT_MARKER_HEADER] !== "1") return { statusCode: 403, code: "error.proto.forbidden", error: "forbidden mutation request" };
|
|
161
|
-
const contentType = String(req.headers["content-type"] || "").split(";", 1)[0].trim().toLowerCase();
|
|
99
|
+
const contentType = String(req.headers.get("content-type") || "").split(";", 1)[0].trim().toLowerCase();
|
|
162
100
|
if (contentType !== "application/json") return { statusCode: 415, code: "error.proto.contentType", error: "content-type must be application/json" };
|
|
163
101
|
return null;
|
|
164
102
|
}
|
|
103
|
+
function jsonResponse(code, payload, headOnly = false) {
|
|
104
|
+
const body = JSON.stringify(payload);
|
|
105
|
+
return new Response(headOnly ? null : body, {
|
|
106
|
+
status: code,
|
|
107
|
+
headers: {
|
|
108
|
+
"content-type": "application/json; charset=utf-8",
|
|
109
|
+
"cache-control": "no-store",
|
|
110
|
+
"content-length": String(Buffer.byteLength(body))
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async function runFetch(task, afterSuccess, headOnly = false) {
|
|
115
|
+
try {
|
|
116
|
+
const r = await task();
|
|
117
|
+
if (r && typeof r === "object" && "ok" in r && r.ok === false) {
|
|
118
|
+
return jsonResponse(400, r, headOnly);
|
|
119
|
+
}
|
|
120
|
+
if (afterSuccess) {
|
|
121
|
+
try {
|
|
122
|
+
afterSuccess();
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return jsonResponse(200, { ok: true, data: r }, headOnly);
|
|
127
|
+
} catch (caught) {
|
|
128
|
+
const e = caught;
|
|
129
|
+
const status = Number.isInteger(e && e.statusCode) ? e.statusCode : 500;
|
|
130
|
+
return jsonResponse(status, {
|
|
131
|
+
ok: false,
|
|
132
|
+
...e && typeof e.code === "string" ? { code: e.code } : {},
|
|
133
|
+
...e && e.params && typeof e.params === "object" && !Array.isArray(e.params) ? { params: e.params } : {},
|
|
134
|
+
error: String(e && e.message ? e.message : e)
|
|
135
|
+
}, headOnly);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
165
138
|
function json(res, code, payload, headOnly = false) {
|
|
166
139
|
if (!res || res.writableEnded || res.destroyed) return;
|
|
167
140
|
res.once("error", () => {
|
|
@@ -176,28 +149,6 @@ function json(res, code, payload, headOnly = false) {
|
|
|
176
149
|
} catch {
|
|
177
150
|
}
|
|
178
151
|
}
|
|
179
|
-
function run(res, task, afterSuccess, headOnly = false) {
|
|
180
|
-
return Promise.resolve().then(task).then((r) => {
|
|
181
|
-
if (r && typeof r === "object" && "ok" in r && r.ok === false) json(res, 400, r, headOnly);
|
|
182
|
-
else {
|
|
183
|
-
try {
|
|
184
|
-
if (afterSuccess) afterSuccess();
|
|
185
|
-
} catch {
|
|
186
|
-
}
|
|
187
|
-
json(res, 200, { ok: true, data: r }, headOnly);
|
|
188
|
-
}
|
|
189
|
-
}).catch((e) => {
|
|
190
|
-
try {
|
|
191
|
-
json(res, Number.isInteger(e && e.statusCode) ? e.statusCode : 500, {
|
|
192
|
-
ok: false,
|
|
193
|
-
...e && typeof e.code === "string" ? { code: e.code } : {},
|
|
194
|
-
...e && e.params && typeof e.params === "object" && !Array.isArray(e.params) ? { params: e.params } : {},
|
|
195
|
-
error: String(e && e.message ? e.message : e)
|
|
196
|
-
}, headOnly);
|
|
197
|
-
} catch {
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
152
|
function notifyChatCatalog(ctx, invalidateSkills) {
|
|
202
153
|
try {
|
|
203
154
|
if (typeof invalidateSkills === "function") invalidateSkills();
|
|
@@ -299,18 +250,6 @@ function apply(ctx, config) {
|
|
|
299
250
|
}), "skills-manager: plugin updater");
|
|
300
251
|
const log = makeLog();
|
|
301
252
|
const repositories = createRepositoryManager({ log, registeredWorkspaces: () => registeredWorkspacePaths(ctx) });
|
|
302
|
-
const getTrustedHosts = () => {
|
|
303
|
-
try {
|
|
304
|
-
const rawCtx = ctx;
|
|
305
|
-
const root = rawCtx.root || rawCtx;
|
|
306
|
-
const candidate = root.webRuntime || root.webStartup;
|
|
307
|
-
if (candidate && typeof candidate === "object" && Array.isArray(candidate.trustedHosts)) {
|
|
308
|
-
return candidate.trustedHosts;
|
|
309
|
-
}
|
|
310
|
-
} catch {
|
|
311
|
-
}
|
|
312
|
-
return [];
|
|
313
|
-
};
|
|
314
253
|
const findUserRoot = (key) => userRoots().find((root) => root.key === key);
|
|
315
254
|
const providerInvalidators = /* @__PURE__ */ new Set();
|
|
316
255
|
const invalidateSkills = () => {
|
|
@@ -359,252 +298,459 @@ function apply(ctx, config) {
|
|
|
359
298
|
return next();
|
|
360
299
|
});
|
|
361
300
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
301
|
+
const API_PREFIX = "/api/michengai/dsh-skills-manager";
|
|
302
|
+
const projectOptions = (sessionId) => ({ projectCwds: activeSessionCwds(ctx, sessionId ?? void 0) });
|
|
303
|
+
const workspaceTarget = async (body) => {
|
|
304
|
+
if (body.workspace === void 0) return null;
|
|
305
|
+
return resolveWorkspaceTarget(body.workspace, registeredWorkspacePaths(ctx));
|
|
306
|
+
};
|
|
307
|
+
const workspaceScope = async (body) => {
|
|
308
|
+
const target = await workspaceTarget(body);
|
|
309
|
+
if (!target) return null;
|
|
310
|
+
if ("ok" in target && target.ok === false) return target;
|
|
311
|
+
return { projectCwds: target.root.workspaceCwds ?? [] };
|
|
312
|
+
};
|
|
313
|
+
const scopedOptions = (scope, sessionId) => scope ?? projectOptions(sessionId);
|
|
314
|
+
const requestRoot = async (key, cwds, sessionId) => cwds ? (await projectRoots(cwds)).find((root) => root.key === key) : findUserRoot(key) || (await projectRoots(projectOptions(sessionId).projectCwds)).find((root) => root.key === key);
|
|
315
|
+
const attachSources = async (snapshot) => {
|
|
316
|
+
try {
|
|
317
|
+
const sources = await repositories.sources();
|
|
318
|
+
for (const root of snapshot.roots) {
|
|
319
|
+
const realRoot = await realpath(root.path).catch(() => root.path);
|
|
320
|
+
for (const skill of root.skills || []) skill.installSource = sources[installationSourceKey(realRoot, skill.name)] ?? null;
|
|
321
|
+
}
|
|
322
|
+
} catch (caught) {
|
|
323
|
+
const error = caught;
|
|
324
|
+
snapshot.warnings.push({ code: "error.repo.state", error: error.message });
|
|
325
|
+
}
|
|
326
|
+
return snapshot;
|
|
327
|
+
};
|
|
328
|
+
const stateWithSources = async (req) => attachSources(await state(projectOptions(req.headers.get("x-dsh-skills-session"))));
|
|
329
|
+
const handlePost = async (req, action, options) => {
|
|
330
|
+
const mutationError = validateMutationRequest(req);
|
|
331
|
+
if (mutationError) {
|
|
332
|
+
return jsonResponse(mutationError.statusCode, { ok: false, code: mutationError.code, error: mutationError.error });
|
|
333
|
+
}
|
|
334
|
+
let body;
|
|
335
|
+
try {
|
|
336
|
+
body = await readJsonBody(req, options?.maxBytes);
|
|
337
|
+
} catch (caught) {
|
|
338
|
+
const e = caught;
|
|
339
|
+
return jsonResponse(Number.isInteger(e && e.statusCode) ? e.statusCode : 400, {
|
|
340
|
+
ok: false,
|
|
341
|
+
code: e && typeof e.code === "string" ? e.code : "error.proto.invalidJson",
|
|
342
|
+
error: String(e && e.message ? e.message : e)
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
if (options?.isRepo && (!body || typeof body !== "object" || Array.isArray(body))) {
|
|
346
|
+
return jsonResponse(400, { ok: false, code: "error.repo.invalid", error: "\u4ED3\u5E93\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61" });
|
|
347
|
+
}
|
|
348
|
+
const execute = async () => {
|
|
349
|
+
const scope = await workspaceScope(body);
|
|
350
|
+
if (scope && "ok" in scope && scope.ok === false) return scope;
|
|
351
|
+
return action(body, scope);
|
|
352
|
+
};
|
|
353
|
+
const afterSuccess = options?.afterSuccess ? () => options.afterSuccess(body) : void 0;
|
|
354
|
+
return runFetch(() => options?.queued !== false ? enqueueMutation(execute) : execute(), afterSuccess);
|
|
355
|
+
};
|
|
356
|
+
const routes = [
|
|
357
|
+
{
|
|
358
|
+
path: `${API_PREFIX}/state`,
|
|
359
|
+
methods: ["GET", "HEAD"],
|
|
360
|
+
fetch: (req) => runFetch(() => stateWithSources(req), void 0, req.method === "HEAD")
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
path: `${API_PREFIX}/sidebar-entry`,
|
|
364
|
+
methods: ["GET", "HEAD", "POST"],
|
|
365
|
+
fetch: (req) => {
|
|
366
|
+
if (req.method === "GET" || req.method === "HEAD") {
|
|
367
|
+
return runFetch(async () => ({ enabled: await getSidebarEntry() }), void 0, req.method === "HEAD");
|
|
406
368
|
}
|
|
407
|
-
|
|
408
|
-
|
|
369
|
+
return handlePost(req, async (b) => {
|
|
370
|
+
if (!b || typeof b.enabled !== "boolean") {
|
|
371
|
+
throw Object.assign(new Error("enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C"), { statusCode: 400, code: "error.proto.invalidParams" });
|
|
372
|
+
}
|
|
373
|
+
const enabled = await setSidebarEntry(b.enabled);
|
|
374
|
+
return { enabled };
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
path: `${API_PREFIX}/online-update`,
|
|
380
|
+
methods: ["GET", "HEAD", "POST"],
|
|
381
|
+
fetch: (req) => {
|
|
382
|
+
if (req.method === "GET" || req.method === "HEAD") {
|
|
383
|
+
return runFetch(async () => ({ enabled: await isUpdateEnabled(), locked: hostDisabled }), void 0, req.method === "HEAD");
|
|
409
384
|
}
|
|
410
|
-
|
|
411
|
-
|
|
385
|
+
return handlePost(req, async (b) => {
|
|
386
|
+
if (!b || typeof b.enabled !== "boolean") {
|
|
387
|
+
throw Object.assign(new Error("enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C"), { statusCode: 400, code: "error.proto.invalidParams" });
|
|
388
|
+
}
|
|
389
|
+
if (hostDisabled) {
|
|
390
|
+
throw Object.assign(new Error("\u5728\u7EBF\u66F4\u65B0\u5DF2\u88AB\u5BBF\u4E3B\u914D\u7F6E\u9501\u5B9A\u7981\u7528"), { statusCode: 403, code: "error.update.disabled" });
|
|
391
|
+
}
|
|
392
|
+
const enabled = await setOnlineUpdate(b.enabled);
|
|
393
|
+
return { enabled };
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
path: `${API_PREFIX}/workspaces`,
|
|
399
|
+
methods: ["GET", "HEAD"],
|
|
400
|
+
fetch: (req) => runFetch(async () => ({ items: await listWorkspaces(registeredWorkspacePaths(ctx) ?? []) }), void 0, req.method === "HEAD")
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
path: `${API_PREFIX}/repositories`,
|
|
404
|
+
methods: ["GET", "HEAD"],
|
|
405
|
+
fetch: (req) => runFetch(() => enqueueMutation(() => repositories.list()), void 0, req.method === "HEAD")
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
path: `${API_PREFIX}/clawhub/list`,
|
|
409
|
+
methods: ["GET"],
|
|
410
|
+
fetch: (req) => {
|
|
411
|
+
const u = new URL(req.url);
|
|
412
|
+
const allowed = ["limit", "cursor", "sort"];
|
|
413
|
+
if ([...u.searchParams.keys()].some((key) => !allowed.includes(key) || u.searchParams.getAll(key).length !== 1)) {
|
|
414
|
+
return Promise.resolve(jsonResponse(400, { ok: false, code: "error.catalog.query", error: "\u76EE\u5F55\u67E5\u8BE2\u53C2\u6570\u65E0\u6548" }));
|
|
412
415
|
}
|
|
413
|
-
|
|
414
|
-
|
|
416
|
+
const parameter = (key) => u.searchParams.get(key) ?? void 0;
|
|
417
|
+
return runFetch(() => listClawHub({ limit: parameter("limit"), sort: parameter("sort"), cursor: parameter("cursor") }));
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
path: `${API_PREFIX}/clawhub/search`,
|
|
422
|
+
methods: ["GET"],
|
|
423
|
+
fetch: (req) => {
|
|
424
|
+
const u = new URL(req.url);
|
|
425
|
+
const allowed = ["q", "limit"];
|
|
426
|
+
if ([...u.searchParams.keys()].some((key) => !allowed.includes(key) || u.searchParams.getAll(key).length !== 1) || !u.searchParams.has("q")) {
|
|
427
|
+
return Promise.resolve(jsonResponse(400, { ok: false, code: "error.catalog.query", error: "\u76EE\u5F55\u67E5\u8BE2\u53C2\u6570\u65E0\u6548" }));
|
|
415
428
|
}
|
|
416
|
-
|
|
417
|
-
|
|
429
|
+
const parameter = (key) => u.searchParams.get(key) ?? void 0;
|
|
430
|
+
return runFetch(() => searchClawHub(parameter("q"), { limit: parameter("limit") }));
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
path: `${API_PREFIX}/skills-sh/search`,
|
|
435
|
+
methods: ["GET"],
|
|
436
|
+
fetch: (req) => {
|
|
437
|
+
const u = new URL(req.url);
|
|
438
|
+
const queries = u.searchParams.getAll("q");
|
|
439
|
+
if (queries.length !== 1) {
|
|
440
|
+
return Promise.resolve(jsonResponse(400, { ok: false, code: "error.catalog.query", error: "\u641C\u7D22\u8BCD\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u8D85\u8FC7 100 \u4E2A\u5B57\u7B26" }));
|
|
418
441
|
}
|
|
419
|
-
|
|
420
|
-
|
|
442
|
+
return runFetch(() => searchSkillsSh(queries[0]));
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
path: `${API_PREFIX}/browse`,
|
|
447
|
+
methods: ["POST"],
|
|
448
|
+
fetch: (req) => handlePost(req, (b) => browseDirectories(b.path), { queued: false })
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
path: `${API_PREFIX}/repositories/refresh`,
|
|
452
|
+
methods: ["POST"],
|
|
453
|
+
fetch: (req) => handlePost(req, (b) => repositories.refresh(b), { queued: false, isRepo: true })
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
path: `${API_PREFIX}/catalog/detail`,
|
|
457
|
+
methods: ["POST"],
|
|
458
|
+
fetch: (req) => handlePost(req, (b) => readCatalogSkill(b), { queued: false })
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
path: `${API_PREFIX}/catalog/install`,
|
|
462
|
+
methods: ["POST"],
|
|
463
|
+
fetch: (req) => handlePost(req, async (b) => {
|
|
464
|
+
if (b.root !== void 0 && b.root !== "dsh" && b.root !== "agents") throw Object.assign(new Error("\u6280\u80FD\u5B89\u88C5\u4F4D\u7F6E\u65E0\u6548"), { statusCode: 400, code: "error.repo.invalid" });
|
|
465
|
+
if (b.workspace !== void 0 && b.root !== void 0 && b.root !== "dsh") throw Object.assign(new Error("\u5DE5\u4F5C\u533A\u53EA\u80FD\u5B89\u88C5\u5230\u9879\u76EE DSH skills"), { statusCode: 400, code: "error.repo.invalid" });
|
|
466
|
+
const target = await workspaceTarget(b);
|
|
467
|
+
if (target && "ok" in target && target.ok === false) return target;
|
|
468
|
+
const pack = await loadCatalogPackage(b);
|
|
469
|
+
const catalogId = b.catalog === "skillsh" ? `skillsh:${b.source}/${b.skillId}` : `clawhub:${b.owner}/${b.slug}`;
|
|
470
|
+
return enqueueMutation(() => repositories.installCatalog(catalogId, pack, { root: b.root, ...b.workspace === void 0 ? {} : { workspace: b.workspace } }));
|
|
471
|
+
}, { queued: false, afterSuccess: afterWrite })
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
path: `${API_PREFIX}/repositories/add`,
|
|
475
|
+
methods: ["POST"],
|
|
476
|
+
fetch: (req) => handlePost(req, (b) => repositories.add(b), { isRepo: true })
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
path: `${API_PREFIX}/repositories/remove`,
|
|
480
|
+
methods: ["POST"],
|
|
481
|
+
fetch: (req) => handlePost(req, (b) => repositories.remove(b), { isRepo: true })
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
path: `${API_PREFIX}/repositories/detail`,
|
|
485
|
+
methods: ["POST"],
|
|
486
|
+
fetch: (req) => handlePost(req, (b) => repositories.detail(b), { isRepo: true })
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
path: `${API_PREFIX}/repositories/install`,
|
|
490
|
+
methods: ["POST"],
|
|
491
|
+
fetch: (req) => handlePost(req, (b) => repositories.install(b, registeredWorkspacePaths(ctx)), { isRepo: true, afterSuccess: afterWrite })
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
path: `${API_PREFIX}/repositories/uninstall`,
|
|
495
|
+
methods: ["POST"],
|
|
496
|
+
fetch: (req) => handlePost(req, (b) => repositories.uninstall(b), { isRepo: true, afterSuccess: afterWrite })
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
path: `${API_PREFIX}/repositories/preview`,
|
|
500
|
+
methods: ["POST"],
|
|
501
|
+
fetch: (req) => handlePost(req, (b) => repositories.preview(b), { isRepo: true })
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
path: `${API_PREFIX}/repositories/update`,
|
|
505
|
+
methods: ["POST"],
|
|
506
|
+
fetch: (req) => handlePost(req, (b) => repositories.update(b), { isRepo: true, afterSuccess: afterWrite })
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
path: `${API_PREFIX}/repositories/rollback`,
|
|
510
|
+
methods: ["POST"],
|
|
511
|
+
fetch: (req) => handlePost(req, (b) => repositories.rollback(b), { isRepo: true, afterSuccess: afterWrite })
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
path: `${API_PREFIX}/workspace-skills`,
|
|
515
|
+
methods: ["POST"],
|
|
516
|
+
fetch: (req) => handlePost(req, async (b) => {
|
|
517
|
+
const snapshot = await workspaceSkills(b.workspace, registeredWorkspacePaths(ctx));
|
|
518
|
+
return "roots" in snapshot ? attachSources(snapshot) : snapshot;
|
|
519
|
+
})
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
path: `${API_PREFIX}/enable`,
|
|
523
|
+
methods: ["POST"],
|
|
524
|
+
fetch: (req) => handlePost(req, async (b, scope) => setSkillEnabled(await requestRoot(String(b.root || "dsh"), scope?.projectCwds, req.headers.get("x-dsh-skills-session")), String(b.name || ""), true, log), { afterSuccess: afterWrite })
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
path: `${API_PREFIX}/disable`,
|
|
528
|
+
methods: ["POST"],
|
|
529
|
+
fetch: (req) => handlePost(req, async (b, scope) => setSkillEnabled(await requestRoot(String(b.root || "dsh"), scope?.projectCwds, req.headers.get("x-dsh-skills-session")), String(b.name || ""), false, log), { afterSuccess: afterWrite })
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
path: `${API_PREFIX}/source-enable`,
|
|
533
|
+
methods: ["POST"],
|
|
534
|
+
fetch: (req) => handlePost(req, (b, scope) => setSourceEnabled(String(b.root || ""), true, log, scopedOptions(scope, req.headers.get("x-dsh-skills-session"))), { afterSuccess: afterWrite })
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
path: `${API_PREFIX}/source-disable`,
|
|
538
|
+
methods: ["POST"],
|
|
539
|
+
fetch: (req) => handlePost(req, (b, scope) => setSourceEnabled(String(b.root || ""), false, log, scopedOptions(scope, req.headers.get("x-dsh-skills-session"))), { afterSuccess: afterWrite })
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
path: `${API_PREFIX}/root-path`,
|
|
543
|
+
methods: ["POST"],
|
|
544
|
+
fetch: (req) => handlePost(req, (b) => setRootPath(String(b.key || ""), b.path === void 0 ? null : b.path), { afterSuccess: afterWrite })
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
path: `${API_PREFIX}/custom-source-add`,
|
|
548
|
+
methods: ["POST"],
|
|
549
|
+
fetch: (req) => handlePost(req, (b) => addCustomSources(Array.isArray(b.sources) ? b.sources : b), { afterSuccess: afterWrite })
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
path: `${API_PREFIX}/custom-source-update`,
|
|
553
|
+
methods: ["POST"],
|
|
554
|
+
fetch: (req) => handlePost(req, (b) => updateCustomSource(String(b.id || ""), b), { afterSuccess: afterWrite })
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
path: `${API_PREFIX}/custom-source-remove`,
|
|
558
|
+
methods: ["POST"],
|
|
559
|
+
fetch: (req) => handlePost(req, (b) => removeCustomSource(String(b.id || "")), { afterSuccess: afterWrite })
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
path: `${API_PREFIX}/custom-source-scan`,
|
|
563
|
+
methods: ["POST"],
|
|
564
|
+
fetch: (req) => handlePost(req, (b) => scanCustomSourceCandidates(b.path))
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
path: `${API_PREFIX}/category-create`,
|
|
568
|
+
methods: ["POST"],
|
|
569
|
+
fetch: (req) => handlePost(req, (b, scope) => createCategory(String(b.root || ""), String(b.name || ""), log, scopedOptions(scope, req.headers.get("x-dsh-skills-session"))))
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
path: `${API_PREFIX}/category-rename`,
|
|
573
|
+
methods: ["POST"],
|
|
574
|
+
fetch: (req) => handlePost(req, (b, scope) => renameCategory(String(b.root || ""), String(b.id || ""), String(b.name || ""), log, scopedOptions(scope, req.headers.get("x-dsh-skills-session"))))
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
path: `${API_PREFIX}/category-delete`,
|
|
578
|
+
methods: ["POST"],
|
|
579
|
+
fetch: (req) => handlePost(req, (b, scope) => deleteCategory(String(b.root || ""), String(b.id || ""), log, scopedOptions(scope, req.headers.get("x-dsh-skills-session"))))
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
path: `${API_PREFIX}/category-assign`,
|
|
583
|
+
methods: ["POST"],
|
|
584
|
+
fetch: (req) => handlePost(req, (b, scope) => assignSkillCategory(String(b.root || ""), String(b.name || ""), String(b.category || ""), log, scopedOptions(scope, req.headers.get("x-dsh-skills-session"))))
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
path: `${API_PREFIX}/category-move`,
|
|
588
|
+
methods: ["POST"],
|
|
589
|
+
fetch: (req) => handlePost(req, (b, scope) => moveSkillInCategory(String(b.root || ""), String(b.name || ""), String(b.category || ""), b.targetName ? String(b.targetName) : void 0, b.position === void 0 ? "before" : String(b.position), log, scopedOptions(scope, req.headers.get("x-dsh-skills-session"))))
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
path: `${API_PREFIX}/category-enable`,
|
|
593
|
+
methods: ["POST"],
|
|
594
|
+
fetch: (req) => handlePost(req, (b, scope) => setCategoryEnabled(String(b.root || ""), String(b.category || ""), true, log, scopedOptions(scope, req.headers.get("x-dsh-skills-session"))), { afterSuccess: afterWrite })
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
path: `${API_PREFIX}/category-disable`,
|
|
598
|
+
methods: ["POST"],
|
|
599
|
+
fetch: (req) => handlePost(req, (b, scope) => setCategoryEnabled(String(b.root || ""), String(b.category || ""), false, log, scopedOptions(scope, req.headers.get("x-dsh-skills-session"))), { afterSuccess: afterWrite })
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
path: `${API_PREFIX}/delete`,
|
|
603
|
+
methods: ["POST"],
|
|
604
|
+
fetch: (req) => handlePost(req, async (b, scope) => {
|
|
605
|
+
const target = await workspaceTarget(b);
|
|
606
|
+
if (target && "ok" in target && target.ok === false) return target;
|
|
607
|
+
const root = target ? target.root : await requestRoot(String(b.root || "dsh"), scope?.projectCwds, req.headers.get("x-dsh-skills-session"));
|
|
608
|
+
return repositories.trashSkill(root, String(b.name || ""));
|
|
609
|
+
}, { afterSuccess: afterWrite })
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
path: `${API_PREFIX}/trash-restore`,
|
|
613
|
+
methods: ["POST"],
|
|
614
|
+
fetch: (req) => handlePost(req, (b, scope) => repositories.restoreTrash(String(b.id || ""), { ...scope ?? {}, registeredWorkspaces: registeredWorkspacePaths(ctx) }), { afterSuccess: afterWrite })
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
path: `${API_PREFIX}/trash-delete`,
|
|
618
|
+
methods: ["POST"],
|
|
619
|
+
fetch: (req) => handlePost(req, (b) => repositories.permanentlyDeleteTrash(String(b.id || "")))
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
path: `${API_PREFIX}/detail`,
|
|
623
|
+
methods: ["POST"],
|
|
624
|
+
fetch: (req) => handlePost(req, (b, scope) => skillDetail(String(b.root || "dsh"), String(b.name || ""), scope ? { ...scope, workspaceBound: true } : projectOptions(req.headers.get("x-dsh-skills-session"))))
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
path: `${API_PREFIX}/create`,
|
|
628
|
+
methods: ["POST"],
|
|
629
|
+
fetch: (req) => handlePost(req, async (b) => {
|
|
630
|
+
const target = await workspaceTarget(b);
|
|
631
|
+
if (target && "ok" in target && target.ok === false) return target;
|
|
632
|
+
return createSkill(
|
|
633
|
+
{ name: b.name, description: b.description, body: b.body },
|
|
634
|
+
log,
|
|
635
|
+
{ root: target ? target.root : await requestRoot(String(b.root || "dsh"), void 0, req.headers.get("x-dsh-skills-session")) }
|
|
636
|
+
);
|
|
637
|
+
}, { afterSuccess: afterWrite })
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
path: `${API_PREFIX}/import`,
|
|
641
|
+
methods: ["POST"],
|
|
642
|
+
fetch: (req) => handlePost(req, async (b) => {
|
|
643
|
+
const target = await workspaceTarget(b);
|
|
644
|
+
if (target && "ok" in target && target.ok === false) return target;
|
|
645
|
+
return importSkill(String(b.source || ""), log, {
|
|
646
|
+
conflict: b.conflict === "overwrite" ? "overwrite" : "skip",
|
|
647
|
+
dryRun: b.dryRun === true,
|
|
648
|
+
...target ? { root: target.root } : b.root === "agents" ? { root: await requestRoot("agents", void 0, req.headers.get("x-dsh-skills-session")) } : {}
|
|
649
|
+
});
|
|
650
|
+
}, { afterSuccess: (b) => {
|
|
651
|
+
if (b.dryRun !== true) afterWrite();
|
|
652
|
+
} })
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
path: `${API_PREFIX}/upload`,
|
|
656
|
+
methods: ["POST"],
|
|
657
|
+
fetch: (req) => handlePost(req, async (b) => {
|
|
658
|
+
const target = await workspaceTarget(b);
|
|
659
|
+
if (target && "ok" in target && target.ok === false) return target;
|
|
660
|
+
return importUploadedSkill({ name: b.name, entries: b.entries, zip: b.zip }, log, {
|
|
661
|
+
conflict: b.conflict === "overwrite" ? "overwrite" : "skip",
|
|
662
|
+
...target ? { root: target.root } : b.root === "agents" ? { root: await requestRoot("agents", void 0, req.headers.get("x-dsh-skills-session")) } : {}
|
|
663
|
+
});
|
|
664
|
+
}, { afterSuccess: afterWrite, maxBytes: MAX_UPLOAD_BODY_BYTES })
|
|
665
|
+
}
|
|
666
|
+
];
|
|
667
|
+
ctx.effect(() => {
|
|
668
|
+
if (ctx.connection?.fetch?.register) {
|
|
669
|
+
const disposers = routes.map(
|
|
670
|
+
(route) => ctx.connection.fetch.register({
|
|
671
|
+
path: route.path,
|
|
672
|
+
methods: route.methods,
|
|
673
|
+
requestBody: route.requestBody ?? "buffered",
|
|
674
|
+
fetch: route.fetch
|
|
675
|
+
})
|
|
676
|
+
);
|
|
677
|
+
return () => {
|
|
678
|
+
for (const disposer of disposers) {
|
|
679
|
+
try {
|
|
680
|
+
disposer();
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
421
683
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
let webServer;
|
|
687
|
+
try {
|
|
688
|
+
webServer = ctx.webServer;
|
|
689
|
+
} catch {
|
|
690
|
+
}
|
|
691
|
+
if (webServer?.register) {
|
|
692
|
+
const routeMap = /* @__PURE__ */ new Map();
|
|
693
|
+
for (const r of routes) routeMap.set(r.path, r);
|
|
694
|
+
return webServer.register({
|
|
695
|
+
kind: "prefix",
|
|
696
|
+
path: API_PREFIX,
|
|
697
|
+
handler: async (req, res) => {
|
|
698
|
+
const u = new URL(req.url, "http://localhost");
|
|
699
|
+
const path = u.pathname.replace(/\/+$/, "");
|
|
700
|
+
const route = routeMap.get(path);
|
|
701
|
+
if (!route) {
|
|
702
|
+
json(res, 404, { ok: false, code: "error.proto.unknownAction", error: `unknown action: ${path}` });
|
|
427
703
|
return;
|
|
428
704
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
if (req.method === "GET" && path === "/api/michengai/dsh-skills-manager/skills-sh/search") {
|
|
433
|
-
const queries = u.searchParams.getAll("q");
|
|
434
|
-
if (queries.length !== 1) {
|
|
435
|
-
json(res, 400, { ok: false, code: "error.catalog.query", error: "\u641C\u7D22\u8BCD\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u8D85\u8FC7 100 \u4E2A\u5B57\u7B26" });
|
|
705
|
+
if (!route.methods.includes(req.method)) {
|
|
706
|
+
json(res, 405, { ok: false, code: "error.proto.method", error: `method not allowed: ${req.method}` });
|
|
436
707
|
return;
|
|
437
708
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
json(res, requestError.statusCode, { ok: false, code: requestError.code, error: requestError.error });
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
const body = await readBody(req, path === "/api/michengai/dsh-skills-manager/upload" ? MAX_UPLOAD_BODY_BYTES : void 0);
|
|
457
|
-
if (path.startsWith("/api/michengai/dsh-skills-manager/repositories/") && (!body || typeof body !== "object" || Array.isArray(body))) {
|
|
458
|
-
json(res, 400, { ok: false, code: "error.repo.invalid", error: "\u4ED3\u5E93\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61" });
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
if (path === "/api/michengai/dsh-skills-manager/browse") {
|
|
462
|
-
return run(res, () => browseDirectories(body.path));
|
|
463
|
-
}
|
|
464
|
-
if (path === "/api/michengai/dsh-skills-manager/repositories/refresh") return run(res, () => repositories.refresh(body));
|
|
465
|
-
if (path === "/api/michengai/dsh-skills-manager/catalog/detail") return run(res, () => readCatalogSkill(body));
|
|
466
|
-
if (path === "/api/michengai/dsh-skills-manager/catalog/install") {
|
|
467
|
-
return run(res, async () => {
|
|
468
|
-
if (body.root !== void 0 && body.root !== "dsh" && body.root !== "agents") throw Object.assign(new Error("\u6280\u80FD\u5B89\u88C5\u4F4D\u7F6E\u65E0\u6548"), { statusCode: 400, code: "error.repo.invalid" });
|
|
469
|
-
if (body.workspace !== void 0 && body.root !== void 0 && body.root !== "dsh") throw Object.assign(new Error("\u5DE5\u4F5C\u533A\u53EA\u80FD\u5B89\u88C5\u5230\u9879\u76EE DSH skills"), { statusCode: 400, code: "error.repo.invalid" });
|
|
470
|
-
const target = await workspaceTarget(body);
|
|
471
|
-
if (target && "ok" in target && target.ok === false) return target;
|
|
472
|
-
const pack = await loadCatalogPackage(body);
|
|
473
|
-
const catalogId = body.catalog === "skillsh" ? `skillsh:${body.source}/${body.skillId}` : `clawhub:${body.owner}/${body.slug}`;
|
|
474
|
-
return enqueueMutation(() => repositories.installCatalog(catalogId, pack, { root: body.root, ...body.workspace === void 0 ? {} : { workspace: body.workspace } }));
|
|
475
|
-
}, afterWrite);
|
|
476
|
-
}
|
|
477
|
-
return enqueueMutation(() => {
|
|
478
|
-
switch (path) {
|
|
479
|
-
case "/api/michengai/dsh-skills-manager/sidebar-entry":
|
|
480
|
-
return run(res, async () => {
|
|
481
|
-
if (!body || typeof body.enabled !== "boolean") {
|
|
482
|
-
throw Object.assign(new Error("enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C"), { statusCode: 400, code: "error.proto.invalidParams" });
|
|
483
|
-
}
|
|
484
|
-
const enabled = await setSidebarEntry(body.enabled);
|
|
485
|
-
return { enabled };
|
|
486
|
-
});
|
|
487
|
-
case "/api/michengai/dsh-skills-manager/online-update":
|
|
488
|
-
return run(res, async () => {
|
|
489
|
-
if (!body || typeof body.enabled !== "boolean") {
|
|
490
|
-
throw Object.assign(new Error("enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C"), { statusCode: 400, code: "error.proto.invalidParams" });
|
|
491
|
-
}
|
|
492
|
-
if (hostDisabled) {
|
|
493
|
-
throw Object.assign(new Error("\u5728\u7EBF\u66F4\u65B0\u5DF2\u88AB\u5BBF\u4E3B\u914D\u7F6E\u9501\u5B9A\u7981\u7528"), { statusCode: 403, code: "error.update.disabled" });
|
|
494
|
-
}
|
|
495
|
-
const enabled = await setOnlineUpdate(body.enabled);
|
|
496
|
-
return { enabled };
|
|
497
|
-
});
|
|
498
|
-
case "/api/michengai/dsh-skills-manager/repositories/add":
|
|
499
|
-
return run(res, () => repositories.add(body));
|
|
500
|
-
case "/api/michengai/dsh-skills-manager/repositories/remove":
|
|
501
|
-
return run(res, () => repositories.remove(body));
|
|
502
|
-
case "/api/michengai/dsh-skills-manager/repositories/detail":
|
|
503
|
-
return run(res, () => repositories.detail(body));
|
|
504
|
-
case "/api/michengai/dsh-skills-manager/repositories/install":
|
|
505
|
-
return run(res, () => repositories.install(body, registeredWorkspacePaths(ctx)), afterWrite);
|
|
506
|
-
case "/api/michengai/dsh-skills-manager/repositories/uninstall":
|
|
507
|
-
return run(res, () => repositories.uninstall(body), afterWrite);
|
|
508
|
-
case "/api/michengai/dsh-skills-manager/repositories/preview":
|
|
509
|
-
return run(res, () => repositories.preview(body));
|
|
510
|
-
case "/api/michengai/dsh-skills-manager/repositories/update":
|
|
511
|
-
return run(res, () => repositories.update(body), afterWrite);
|
|
512
|
-
case "/api/michengai/dsh-skills-manager/repositories/rollback":
|
|
513
|
-
return run(res, () => repositories.rollback(body), afterWrite);
|
|
514
|
-
case "/api/michengai/dsh-skills-manager/workspace-skills":
|
|
515
|
-
return run(res, async () => {
|
|
516
|
-
const snapshot = await workspaceSkills(body.workspace, registeredWorkspacePaths(ctx));
|
|
517
|
-
return "roots" in snapshot ? attachSources(snapshot) : snapshot;
|
|
709
|
+
const headers = new Headers();
|
|
710
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
711
|
+
if (v !== void 0) {
|
|
712
|
+
if (Array.isArray(v)) for (const item of v) headers.append(k, item);
|
|
713
|
+
else headers.set(k, v);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
try {
|
|
717
|
+
let bodyBuffer = null;
|
|
718
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
719
|
+
bodyBuffer = await new Promise((resolve, reject) => {
|
|
720
|
+
const chunks = [];
|
|
721
|
+
req.on("data", (c) => chunks.push(c));
|
|
722
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
723
|
+
req.on("error", reject);
|
|
518
724
|
});
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
case "/api/michengai/dsh-skills-manager/category-delete":
|
|
542
|
-
return run(res, () => withScope(body, (scope) => deleteCategory(String(body.root || ""), String(body.id || ""), log, scopedOptions(scope))));
|
|
543
|
-
case "/api/michengai/dsh-skills-manager/category-assign":
|
|
544
|
-
return run(res, () => withScope(body, (scope) => assignSkillCategory(String(body.root || ""), String(body.name || ""), String(body.category || ""), log, scopedOptions(scope))));
|
|
545
|
-
case "/api/michengai/dsh-skills-manager/category-move":
|
|
546
|
-
return run(res, () => withScope(body, (scope) => moveSkillInCategory(String(body.root || ""), String(body.name || ""), String(body.category || ""), body.targetName ? String(body.targetName) : void 0, body.position === void 0 ? "before" : String(body.position), log, scopedOptions(scope))));
|
|
547
|
-
case "/api/michengai/dsh-skills-manager/category-enable":
|
|
548
|
-
return run(res, () => withScope(body, (scope) => setCategoryEnabled(String(body.root || ""), String(body.category || ""), true, log, scopedOptions(scope))), afterWrite);
|
|
549
|
-
case "/api/michengai/dsh-skills-manager/category-disable":
|
|
550
|
-
return run(res, () => withScope(body, (scope) => setCategoryEnabled(String(body.root || ""), String(body.category || ""), false, log, scopedOptions(scope))), afterWrite);
|
|
551
|
-
case "/api/michengai/dsh-skills-manager/delete":
|
|
552
|
-
return run(res, () => withScope(body, async (scope) => {
|
|
553
|
-
const target = await workspaceTarget(body);
|
|
554
|
-
if (target && "ok" in target && target.ok === false) return target;
|
|
555
|
-
const root = target ? target.root : await requestRoot(String(body.root || "dsh"), scope?.projectCwds);
|
|
556
|
-
return repositories.trashSkill(root, String(body.name || ""));
|
|
557
|
-
}), afterWrite);
|
|
558
|
-
case "/api/michengai/dsh-skills-manager/trash-restore":
|
|
559
|
-
return run(res, () => withScope(body, (scope) => repositories.restoreTrash(String(body.id || ""), { ...scope ?? {}, registeredWorkspaces: registeredWorkspacePaths(ctx) })), afterWrite);
|
|
560
|
-
case "/api/michengai/dsh-skills-manager/trash-delete":
|
|
561
|
-
return run(res, () => repositories.permanentlyDeleteTrash(String(body.id || "")));
|
|
562
|
-
case "/api/michengai/dsh-skills-manager/detail":
|
|
563
|
-
return run(res, () => withScope(body, (scope) => skillDetail(String(body.root || "dsh"), String(body.name || ""), scope ? { ...scope, workspaceBound: true } : projectOptions())));
|
|
564
|
-
case "/api/michengai/dsh-skills-manager/create":
|
|
565
|
-
return run(res, async () => {
|
|
566
|
-
const target = await workspaceTarget(body);
|
|
567
|
-
if (target && "ok" in target && target.ok === false) return target;
|
|
568
|
-
return createSkill(
|
|
569
|
-
{ name: body.name, description: body.description, body: body.body },
|
|
570
|
-
log,
|
|
571
|
-
{ root: target ? target.root : await requestRoot(String(body.root || "dsh")) }
|
|
572
|
-
);
|
|
573
|
-
}, afterWrite);
|
|
574
|
-
case "/api/michengai/dsh-skills-manager/import":
|
|
575
|
-
return run(res, async () => {
|
|
576
|
-
const target = await workspaceTarget(body);
|
|
577
|
-
if (target && "ok" in target && target.ok === false) return target;
|
|
578
|
-
return importSkill(String(body.source || ""), log, {
|
|
579
|
-
conflict: body.conflict === "overwrite" ? "overwrite" : "skip",
|
|
580
|
-
dryRun: body.dryRun === true,
|
|
581
|
-
...target ? { root: target.root } : body.root === "agents" ? { root: await requestRoot("agents") } : {}
|
|
582
|
-
});
|
|
583
|
-
}, body.dryRun === true ? void 0 : afterWrite);
|
|
584
|
-
case "/api/michengai/dsh-skills-manager/upload":
|
|
585
|
-
return run(res, async () => {
|
|
586
|
-
const target = await workspaceTarget(body);
|
|
587
|
-
if (target && "ok" in target && target.ok === false) return target;
|
|
588
|
-
return importUploadedSkill({ name: body.name, entries: body.entries, zip: body.zip }, log, {
|
|
589
|
-
conflict: body.conflict === "overwrite" ? "overwrite" : "skip",
|
|
590
|
-
...target ? { root: target.root } : body.root === "agents" ? { root: await requestRoot("agents") } : {}
|
|
591
|
-
});
|
|
592
|
-
}, afterWrite);
|
|
593
|
-
default:
|
|
594
|
-
json(res, 404, { ok: false, code: "error.proto.unknownAction", error: `unknown action: ${path}` });
|
|
725
|
+
}
|
|
726
|
+
const fetchReq = new Request(u.toString(), {
|
|
727
|
+
method: req.method,
|
|
728
|
+
headers,
|
|
729
|
+
body: bodyBuffer && bodyBuffer.length > 0 ? new Uint8Array(bodyBuffer) : void 0
|
|
730
|
+
});
|
|
731
|
+
const response = await route.fetch(fetchReq);
|
|
732
|
+
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
733
|
+
if (req.method === "HEAD") {
|
|
734
|
+
res.end();
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const responseBuf = Buffer.from(await response.arrayBuffer());
|
|
738
|
+
res.end(responseBuf);
|
|
739
|
+
} catch (caught) {
|
|
740
|
+
const e = caught;
|
|
741
|
+
json(res, Number.isInteger(e && e.statusCode) ? e.statusCode : 500, {
|
|
742
|
+
ok: false,
|
|
743
|
+
...e && typeof e.code === "string" ? { code: e.code } : {},
|
|
744
|
+
...e && e.params && typeof e.params === "object" && !Array.isArray(e.params) ? { params: e.params } : {},
|
|
745
|
+
error: String(e && e.message ? e.message : e)
|
|
746
|
+
});
|
|
595
747
|
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
const e = caught;
|
|
599
|
-
json(res, Number.isInteger(e && e.statusCode) ? e.statusCode : 500, {
|
|
600
|
-
ok: false,
|
|
601
|
-
...e && typeof e.code === "string" ? { code: e.code } : {},
|
|
602
|
-
...e && e.params && typeof e.params === "object" && !Array.isArray(e.params) ? { params: e.params } : {},
|
|
603
|
-
error: String(e && e.message ? e.message : e)
|
|
604
|
-
});
|
|
605
|
-
}
|
|
748
|
+
}
|
|
749
|
+
});
|
|
606
750
|
}
|
|
607
|
-
|
|
751
|
+
return () => {
|
|
752
|
+
};
|
|
753
|
+
}, "skills-manager: API routes");
|
|
608
754
|
}
|
|
609
755
|
export {
|
|
610
756
|
activeSessionCwds,
|