@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/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 = ["webServer", "skills", "tools", "sessions"];
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 readBody(req, limit = 1 << 20) {
66
- return new Promise((resolve, reject) => {
67
- let size = 0;
68
- let settled = false;
69
- const chunks = [];
70
- const fail = (error) => {
71
- if (settled) return;
72
- settled = true;
73
- reject(error);
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 new URL(`http://${authority}`);
109
- } catch {
110
- return void 0;
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 canonicalAuthority(authority, parsed) {
114
- const port = parsed.port !== "" ? parsed.port : new URL(`https://${authority}`).port;
115
- return port === "" ? parsed.hostname : `${parsed.hostname}:${port}`;
116
- }
117
- function isCanonicalAuthority(authority, parsed) {
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
- if (new URL(origin).host !== hostUrl.host) {
149
- return { statusCode: 403, code: "error.proto.forbiddenHost", error: "forbidden host" };
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.forbiddenHost", error: "forbidden host" };
96
+ return { statusCode: 403, code: "error.proto.forbidden", error: "forbidden origin" };
153
97
  }
154
98
  }
155
- return null;
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
- ctx.effect(() => ctx.webServer.register({
363
- kind: "prefix",
364
- path: "/api/michengai/dsh-skills-manager",
365
- handler: async (req, res) => {
366
- const u = new URL(req.url, "http://localhost");
367
- const path = u.pathname.replace(/\/+$/, "");
368
- const sessionId = req.headers["x-dsh-skills-session"];
369
- const projectOptions = () => ({ projectCwds: activeSessionCwds(ctx, sessionId) });
370
- const workspaceTarget = async (body) => {
371
- if (body.workspace === void 0) return null;
372
- return resolveWorkspaceTarget(body.workspace, registeredWorkspacePaths(ctx));
373
- };
374
- const workspaceScope = async (body) => {
375
- const target = await workspaceTarget(body);
376
- if (!target) return null;
377
- if ("ok" in target && target.ok === false) return target;
378
- return { projectCwds: target.root.workspaceCwds ?? [] };
379
- };
380
- const scopedOptions = (scope) => scope ?? projectOptions();
381
- const requestRoot = async (key, cwds) => cwds ? (await projectRoots(cwds)).find((root) => root.key === key) : findUserRoot(key) || (await projectRoots(projectOptions().projectCwds)).find((root) => root.key === key);
382
- const withScope = async (body, action) => {
383
- const scope = await workspaceScope(body);
384
- if (scope && "ok" in scope && scope.ok === false) return scope;
385
- return action(scope);
386
- };
387
- const attachSources = async (snapshot) => {
388
- try {
389
- const sources = await repositories.sources();
390
- for (const root of snapshot.roots) {
391
- const realRoot = await realpath(root.path).catch(() => root.path);
392
- for (const skill of root.skills || []) skill.installSource = sources[installationSourceKey(realRoot, skill.name)] ?? null;
393
- }
394
- } catch (caught) {
395
- const error = caught;
396
- snapshot.warnings.push({ code: "error.repo.state", error: error.message });
397
- }
398
- return snapshot;
399
- };
400
- const stateWithSources = async () => attachSources(await state(projectOptions()));
401
- try {
402
- const hostError = validateRequestOrigin(req, getTrustedHosts());
403
- if (hostError) {
404
- json(res, hostError.statusCode, { ok: false, code: hostError.code, error: hostError.error });
405
- return;
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
- if (req.method === "GET" && path === "/api/michengai/dsh-skills-manager/state") {
408
- return run(res, stateWithSources);
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
- if (req.method === "GET" && path === "/api/michengai/dsh-skills-manager/sidebar-entry") {
411
- return run(res, async () => ({ enabled: await getSidebarEntry() }));
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
- if (req.method === "GET" && path === "/api/michengai/dsh-skills-manager/online-update") {
414
- return run(res, async () => ({ enabled: await isUpdateEnabled(), locked: hostDisabled }));
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
- if ((req.method === "GET" || req.method === "HEAD") && path === "/api/michengai/dsh-skills-manager/workspaces") {
417
- return run(res, async () => ({ items: await listWorkspaces(registeredWorkspacePaths(ctx) ?? []) }), void 0, req.method === "HEAD");
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
- if ((req.method === "GET" || req.method === "HEAD") && path === "/api/michengai/dsh-skills-manager/repositories") {
420
- return run(res, () => enqueueMutation(() => repositories.list()), void 0, req.method === "HEAD");
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
- if (req.method === "GET" && (path === "/api/michengai/dsh-skills-manager/clawhub/list" || path === "/api/michengai/dsh-skills-manager/clawhub/search")) {
423
- const searching = path.endsWith("/search");
424
- const allowed = searching ? ["q", "limit"] : ["limit", "cursor", "sort"];
425
- if ([...u.searchParams.keys()].some((key) => !allowed.includes(key) || u.searchParams.getAll(key).length !== 1) || searching && !u.searchParams.has("q")) {
426
- json(res, 400, { ok: false, code: "error.catalog.query", error: "\u76EE\u5F55\u67E5\u8BE2\u53C2\u6570\u65E0\u6548" });
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
- const parameter = (key) => u.searchParams.get(key) ?? void 0;
430
- return run(res, () => searching ? searchClawHub(parameter("q"), { limit: parameter("limit") }) : listClawHub({ limit: parameter("limit"), sort: parameter("sort"), cursor: parameter("cursor") }));
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
- return run(res, () => searchSkillsSh(queries[0]));
439
- }
440
- if (req.method === "HEAD") {
441
- if (path === "/api/michengai/dsh-skills-manager/state") return run(res, stateWithSources, void 0, true);
442
- if (path === "/api/michengai/dsh-skills-manager/sidebar-entry") return run(res, async () => ({ enabled: await getSidebarEntry() }), void 0, true);
443
- if (path === "/api/michengai/dsh-skills-manager/online-update") return run(res, async () => ({ enabled: await isUpdateEnabled(), locked: hostDisabled }), void 0, true);
444
- json(res, 405, { ok: false, code: "error.proto.method", error: `method not allowed: ${req.method}` }, true);
445
- return;
446
- }
447
- if (req.method !== "POST") {
448
- json(res, 405, { ok: false, code: "error.proto.method", error: `method not allowed: ${req.method}` });
449
- return;
450
- }
451
- const requestError = validateMutationRequest(req, getTrustedHosts());
452
- if (requestError) {
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
- case "/api/michengai/dsh-skills-manager/enable":
520
- return run(res, () => withScope(body, async (scope) => setSkillEnabled(await requestRoot(String(body.root || "dsh"), scope?.projectCwds), String(body.name || ""), true, log)), afterWrite);
521
- case "/api/michengai/dsh-skills-manager/disable":
522
- return run(res, () => withScope(body, async (scope) => setSkillEnabled(await requestRoot(String(body.root || "dsh"), scope?.projectCwds), String(body.name || ""), false, log)), afterWrite);
523
- case "/api/michengai/dsh-skills-manager/source-enable":
524
- return run(res, () => withScope(body, (scope) => setSourceEnabled(String(body.root || ""), true, log, scopedOptions(scope))), afterWrite);
525
- case "/api/michengai/dsh-skills-manager/source-disable":
526
- return run(res, () => withScope(body, (scope) => setSourceEnabled(String(body.root || ""), false, log, scopedOptions(scope))), afterWrite);
527
- case "/api/michengai/dsh-skills-manager/root-path":
528
- return run(res, () => setRootPath(String(body.key || ""), body.path === void 0 ? null : body.path), afterWrite);
529
- case "/api/michengai/dsh-skills-manager/custom-source-add":
530
- return run(res, () => addCustomSources(Array.isArray(body.sources) ? body.sources : body), afterWrite);
531
- case "/api/michengai/dsh-skills-manager/custom-source-update":
532
- return run(res, () => updateCustomSource(String(body.id || ""), body), afterWrite);
533
- case "/api/michengai/dsh-skills-manager/custom-source-remove":
534
- return run(res, () => removeCustomSource(String(body.id || "")), afterWrite);
535
- case "/api/michengai/dsh-skills-manager/custom-source-scan":
536
- return run(res, () => scanCustomSourceCandidates(body.path));
537
- case "/api/michengai/dsh-skills-manager/category-create":
538
- return run(res, () => withScope(body, (scope) => createCategory(String(body.root || ""), String(body.name || ""), log, scopedOptions(scope))));
539
- case "/api/michengai/dsh-skills-manager/category-rename":
540
- return run(res, () => withScope(body, (scope) => renameCategory(String(body.root || ""), String(body.id || ""), String(body.name || ""), log, scopedOptions(scope))));
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
- } catch (caught) {
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
- }), "skills-manager: API route");
751
+ return () => {
752
+ };
753
+ }, "skills-manager: API routes");
608
754
  }
609
755
  export {
610
756
  activeSessionCwds,