neko-harness 0.2.2 → 0.2.3
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/dist/client/assets/index-DXuZXmFD.js +90 -0
- package/dist/client/assets/index-wX6mP4oW.css +2 -0
- package/dist/client/index.html +2 -2
- package/dist/gateway.js +761 -52
- package/dist/neko.js +1137 -108
- package/package.json +1 -1
- package/dist/client/assets/index-6edxlI44.css +0 -2
- package/dist/client/assets/index-CZ-wxzM4.js +0 -88
package/dist/gateway.js
CHANGED
|
@@ -105,9 +105,9 @@ function parseGatewayArgs(argv) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// packages/gateway/src/gateway.ts
|
|
108
|
-
import { randomUUID } from "crypto";
|
|
109
|
-
import { mkdir as
|
|
110
|
-
import { basename as basename4, dirname as
|
|
108
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
109
|
+
import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
110
|
+
import { basename as basename4, dirname as dirname7, join as join10 } from "path";
|
|
111
111
|
import { BACKGROUND_CONTEXT as BACKGROUND_CONTEXT2 } from "@earendil-works/chord/context";
|
|
112
112
|
import { isServerId } from "@earendil-works/pi-protocol";
|
|
113
113
|
import { Server, SessionNotFoundError } from "@earendil-works/pi-server";
|
|
@@ -226,6 +226,7 @@ class CompactionManager {
|
|
|
226
226
|
import { existsSync, readFileSync } from "fs";
|
|
227
227
|
import { homedir } from "os";
|
|
228
228
|
import { dirname, join, resolve } from "path";
|
|
229
|
+
var DEFAULT_SKILL_MARKET_URL = "https://skill.kirineko.tech";
|
|
229
230
|
var NEKO_CONFIG_BASENAME = "neko.config.json";
|
|
230
231
|
function findNekoConfigFile(startDir = process.cwd()) {
|
|
231
232
|
let dir = resolve(startDir);
|
|
@@ -328,6 +329,16 @@ function validate(config, source) {
|
|
|
328
329
|
if (theme !== undefined && theme !== "light" && theme !== "dark" && theme !== "auto") {
|
|
329
330
|
throw new Error(`neko config: ${source}: "theme" must be "light", "dark", or "auto".`);
|
|
330
331
|
}
|
|
332
|
+
const skills = config.skills;
|
|
333
|
+
if (skills !== undefined) {
|
|
334
|
+
if (typeof skills !== "object" || skills === null) {
|
|
335
|
+
throw new Error(`neko config: ${source}: "skills" must be an object.`);
|
|
336
|
+
}
|
|
337
|
+
const { marketUrl } = skills;
|
|
338
|
+
if (marketUrl !== undefined && typeof marketUrl !== "string") {
|
|
339
|
+
throw new Error(`neko config: ${source}: "skills.marketUrl" must be a string.`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
331
342
|
}
|
|
332
343
|
// packages/core/src/credentials.ts
|
|
333
344
|
import { chmod, mkdir, open, readFile, rename, unlink } from "fs/promises";
|
|
@@ -936,6 +947,32 @@ function contextTokens(entries) {
|
|
|
936
947
|
}
|
|
937
948
|
return null;
|
|
938
949
|
}
|
|
950
|
+
// packages/core/src/prompt/frontmatter.ts
|
|
951
|
+
import { parse } from "yaml";
|
|
952
|
+
var normalizeNewlines = (value) => value.replace(/\r\n/g, `
|
|
953
|
+
`).replace(/\r/g, `
|
|
954
|
+
`);
|
|
955
|
+
var extractFrontmatter = (content) => {
|
|
956
|
+
const normalized = normalizeNewlines(content.replace(/^\uFEFF/, ""));
|
|
957
|
+
const opening = /^---[ \t]*\n/.exec(normalized);
|
|
958
|
+
if (!opening)
|
|
959
|
+
return { yamlString: null, body: normalized };
|
|
960
|
+
const rest = normalized.slice(opening[0].length);
|
|
961
|
+
const closing = /^---[ \t]*(?:\n|$)/m.exec(rest);
|
|
962
|
+
if (!closing)
|
|
963
|
+
return { yamlString: null, body: normalized };
|
|
964
|
+
return { yamlString: rest.slice(0, closing.index), body: rest.slice(closing.index + closing[0].length).trim() };
|
|
965
|
+
};
|
|
966
|
+
var parseFrontmatter = (content) => {
|
|
967
|
+
const { yamlString, body } = extractFrontmatter(content);
|
|
968
|
+
if (!yamlString)
|
|
969
|
+
return { frontmatter: {}, body };
|
|
970
|
+
const parsed = parse(yamlString);
|
|
971
|
+
if (parsed !== null && (typeof parsed !== "object" || Array.isArray(parsed)))
|
|
972
|
+
throw new Error("Frontmatter must be a YAML mapping.");
|
|
973
|
+
return { frontmatter: parsed ?? {}, body };
|
|
974
|
+
};
|
|
975
|
+
var stripFrontmatter = (content) => extractFrontmatter(content).body;
|
|
939
976
|
// packages/core/src/runtime.ts
|
|
940
977
|
import { homedir as homedir4 } from "os";
|
|
941
978
|
import { join as join5 } from "path";
|
|
@@ -965,33 +1002,6 @@ var warnPromptDiagnostic = ({ path, message }) => {
|
|
|
965
1002
|
};
|
|
966
1003
|
var isMissing = (error) => typeof error === "object" && error !== null && ("code" in error) && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
967
1004
|
|
|
968
|
-
// packages/core/src/prompt/frontmatter.ts
|
|
969
|
-
import { parse } from "yaml";
|
|
970
|
-
var normalizeNewlines = (value) => value.replace(/\r\n/g, `
|
|
971
|
-
`).replace(/\r/g, `
|
|
972
|
-
`);
|
|
973
|
-
var extractFrontmatter = (content) => {
|
|
974
|
-
const normalized = normalizeNewlines(content.replace(/^\uFEFF/, ""));
|
|
975
|
-
const opening = /^---[ \t]*\n/.exec(normalized);
|
|
976
|
-
if (!opening)
|
|
977
|
-
return { yamlString: null, body: normalized };
|
|
978
|
-
const rest = normalized.slice(opening[0].length);
|
|
979
|
-
const closing = /^---[ \t]*(?:\n|$)/m.exec(rest);
|
|
980
|
-
if (!closing)
|
|
981
|
-
return { yamlString: null, body: normalized };
|
|
982
|
-
return { yamlString: rest.slice(0, closing.index), body: rest.slice(closing.index + closing[0].length).trim() };
|
|
983
|
-
};
|
|
984
|
-
var parseFrontmatter = (content) => {
|
|
985
|
-
const { yamlString, body } = extractFrontmatter(content);
|
|
986
|
-
if (!yamlString)
|
|
987
|
-
return { frontmatter: {}, body };
|
|
988
|
-
const parsed = parse(yamlString);
|
|
989
|
-
if (parsed !== null && (typeof parsed !== "object" || Array.isArray(parsed)))
|
|
990
|
-
throw new Error("Frontmatter must be a YAML mapping.");
|
|
991
|
-
return { frontmatter: parsed ?? {}, body };
|
|
992
|
-
};
|
|
993
|
-
var stripFrontmatter = (content) => extractFrontmatter(content).body;
|
|
994
|
-
|
|
995
1005
|
// packages/core/src/prompt/skills.ts
|
|
996
1006
|
import { readdir, readFile as readFile2 } from "fs/promises";
|
|
997
1007
|
import { basename, dirname as dirname3, join as join3 } from "path";
|
|
@@ -1185,11 +1195,13 @@ ${text}
|
|
|
1185
1195
|
import { readFile as readFile3 } from "fs/promises";
|
|
1186
1196
|
import { homedir as homedir3 } from "os";
|
|
1187
1197
|
import { join as join4 } from "path";
|
|
1198
|
+
import { fileURLToPath } from "url";
|
|
1188
1199
|
var userAgentsFile = (home) => join4(home, ".agents", "AGENTS.md");
|
|
1189
1200
|
var userSkillsDir = (home) => join4(home, ".agents", "skills");
|
|
1190
1201
|
var workspaceAgentsFile = (workspace) => join4(workspace, "AGENTS.md");
|
|
1191
1202
|
var workspaceSkillsDir = (workspace) => join4(workspace, ".agents", "skills");
|
|
1192
|
-
var
|
|
1203
|
+
var BUILTIN_SKILLS_DIR = fileURLToPath(new URL("../../skills/", import.meta.url));
|
|
1204
|
+
var readResources = async (workspace, home, builtinDir, report, invocationName) => {
|
|
1193
1205
|
const contextFiles = [];
|
|
1194
1206
|
for (const path of [userAgentsFile(home), workspaceAgentsFile(workspace)]) {
|
|
1195
1207
|
try {
|
|
@@ -1210,11 +1222,13 @@ var readResources = async (workspace, home, report, invocationName) => {
|
|
|
1210
1222
|
if (skill.name === invocationName)
|
|
1211
1223
|
candidates.set(skill.filePath, content);
|
|
1212
1224
|
} : undefined;
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
|
|
1225
|
+
const scan = async (dir, level) => (await scanSkillsDir(dir, report, capture)).map((skill) => ({ ...skill, level }));
|
|
1226
|
+
const [builtin, user, project] = await Promise.all([
|
|
1227
|
+
builtinDir === null ? Promise.resolve([]) : scan(builtinDir, "builtin"),
|
|
1228
|
+
scan(userSkillsDir(home), "user"),
|
|
1229
|
+
scan(workspaceSkillsDir(workspace), "project")
|
|
1216
1230
|
]);
|
|
1217
|
-
const skills = mergeSkills([user, project]);
|
|
1231
|
+
const skills = mergeSkills([builtin, user, project]);
|
|
1218
1232
|
const winner = invocationName === undefined ? undefined : skills.find((skill) => skill.name === invocationName);
|
|
1219
1233
|
if (winner && candidates) {
|
|
1220
1234
|
const content = candidates.get(winner.filePath);
|
|
@@ -1223,11 +1237,12 @@ var readResources = async (workspace, home, report, invocationName) => {
|
|
|
1223
1237
|
}
|
|
1224
1238
|
return { contextFiles, skills };
|
|
1225
1239
|
};
|
|
1226
|
-
var createResourceLoader = (home = homedir3(), report = warnPromptDiagnostic) => {
|
|
1240
|
+
var createResourceLoader = (home = homedir3(), report = warnPromptDiagnostic, options) => {
|
|
1241
|
+
const builtinDir = options?.builtinDir === undefined ? BUILTIN_SKILLS_DIR : options.builtinDir;
|
|
1227
1242
|
const previous = new Map;
|
|
1228
1243
|
return async (workspace, invocationName) => {
|
|
1229
1244
|
const diagnostics = [];
|
|
1230
|
-
const resources = await readResources(workspace, home, (d) => diagnostics.push(d), invocationName);
|
|
1245
|
+
const resources = await readResources(workspace, home, builtinDir, (d) => diagnostics.push(d), invocationName);
|
|
1231
1246
|
const known = previous.get(workspace);
|
|
1232
1247
|
const current = new Set;
|
|
1233
1248
|
for (const diagnostic of diagnostics) {
|
|
@@ -1243,6 +1258,15 @@ var createResourceLoader = (home = homedir3(), report = warnPromptDiagnostic) =>
|
|
|
1243
1258
|
return resources;
|
|
1244
1259
|
};
|
|
1245
1260
|
};
|
|
1261
|
+
var scanSkillsByLevel = async (workspace, home = homedir3(), builtinDir = BUILTIN_SKILLS_DIR, report = warnPromptDiagnostic) => {
|
|
1262
|
+
const scan = async (dir, level) => (await scanSkillsDir(dir, report)).map((skill) => ({ ...skill, level }));
|
|
1263
|
+
const [builtin, user, project] = await Promise.all([
|
|
1264
|
+
builtinDir === null ? [] : scan(builtinDir, "builtin"),
|
|
1265
|
+
scan(userSkillsDir(home), "user"),
|
|
1266
|
+
scan(workspaceSkillsDir(workspace), "project")
|
|
1267
|
+
]);
|
|
1268
|
+
return [...project, ...user, ...builtin];
|
|
1269
|
+
};
|
|
1246
1270
|
|
|
1247
1271
|
// packages/core/src/prompt/snapshots.ts
|
|
1248
1272
|
var createResourceSnapshots = (load, submission) => {
|
|
@@ -2416,7 +2440,21 @@ async function createNekoRuntime(options = {}) {
|
|
|
2416
2440
|
const session = await sessionFor(id);
|
|
2417
2441
|
const agent = await session.conversation.agent(context);
|
|
2418
2442
|
const { skills } = await loadResources(savedWorkspace(agent.cwd));
|
|
2419
|
-
return skills.map((skill) => ({
|
|
2443
|
+
return skills.map((skill) => ({
|
|
2444
|
+
name: skill.name,
|
|
2445
|
+
description: skill.description,
|
|
2446
|
+
location: skill.filePath,
|
|
2447
|
+
level: skill.level
|
|
2448
|
+
}));
|
|
2449
|
+
},
|
|
2450
|
+
async listSkillsByLevel(cwd) {
|
|
2451
|
+
const skills = await scanSkillsByLevel(normalizeWorkspace(cwd ?? defaultCwd), options.promptHome ?? homedir4());
|
|
2452
|
+
return skills.map((skill) => ({
|
|
2453
|
+
name: skill.name,
|
|
2454
|
+
description: skill.description,
|
|
2455
|
+
location: skill.filePath,
|
|
2456
|
+
level: skill.level
|
|
2457
|
+
}));
|
|
2420
2458
|
},
|
|
2421
2459
|
installExtension(extension) {
|
|
2422
2460
|
registry.install(extension);
|
|
@@ -2585,6 +2623,7 @@ var NekoChat = defineService("neko.chat");
|
|
|
2585
2623
|
var NekoWorkspaces = defineService("neko.workspaces");
|
|
2586
2624
|
var NekoCredentials = defineService("neko.credentials");
|
|
2587
2625
|
var NekoWorkspaceFiles = defineService("neko.workspace-files");
|
|
2626
|
+
var NekoSkillMarket = defineService("neko.skill-market");
|
|
2588
2627
|
|
|
2589
2628
|
// packages/gateway/src/file-search-help.ts
|
|
2590
2629
|
function fileSearchInstallHelp(platform = process.platform) {
|
|
@@ -2606,10 +2645,644 @@ function fileSearchInstallHelp(platform = process.platform) {
|
|
|
2606
2645
|
return `\u672A\u627E\u5230 fd \u6216 fdfind\uFF0C\u6682\u65F6\u65E0\u6CD5\u4F7F\u7528 @ \u6587\u4EF6\u8865\u5168\u3002\u8BF7\u5728\u8FD0\u884C neko \u7684\u7535\u8111\u4E0A\u5B89\u88C5\u3002${install}\u5B89\u88C5\u540E\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u5E76\u91CD\u542F neko\u3002`;
|
|
2607
2646
|
}
|
|
2608
2647
|
|
|
2648
|
+
// packages/gateway/src/skill-market.ts
|
|
2649
|
+
import { createHash as createHash2, randomUUID } from "crypto";
|
|
2650
|
+
import { mkdir as mkdir2, readdir as readdir2, rename as rename2, rm as rm2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
2651
|
+
import { homedir as homedir5 } from "os";
|
|
2652
|
+
import { dirname as dirname5, join as join8 } from "path";
|
|
2653
|
+
|
|
2654
|
+
// node_modules/fflate/esm/index.mjs
|
|
2655
|
+
import { createRequire } from "module";
|
|
2656
|
+
var require2 = createRequire("/");
|
|
2657
|
+
var _a;
|
|
2658
|
+
var Worker;
|
|
2659
|
+
var isMarkedAsUntransferable;
|
|
2660
|
+
try {
|
|
2661
|
+
_a = require2("worker_threads"), Worker = _a.Worker, isMarkedAsUntransferable = _a.isMarkedAsUntransferable;
|
|
2662
|
+
} catch (e) {}
|
|
2663
|
+
var u8 = Uint8Array;
|
|
2664
|
+
var u16 = Uint16Array;
|
|
2665
|
+
var i32 = Int32Array;
|
|
2666
|
+
var fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0, 0]);
|
|
2667
|
+
var fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0]);
|
|
2668
|
+
var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
2669
|
+
var freb = function(eb, start) {
|
|
2670
|
+
var b = new u16(31);
|
|
2671
|
+
for (var i = 0;i < 31; ++i) {
|
|
2672
|
+
b[i] = start += 1 << eb[i - 1];
|
|
2673
|
+
}
|
|
2674
|
+
var r = new i32(b[30]);
|
|
2675
|
+
for (var i = 1;i < 30; ++i) {
|
|
2676
|
+
for (var j = b[i];j < b[i + 1]; ++j) {
|
|
2677
|
+
r[j] = j - b[i] << 5 | i;
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
return { b, r };
|
|
2681
|
+
};
|
|
2682
|
+
var _a = freb(fleb, 2);
|
|
2683
|
+
var fl = _a.b;
|
|
2684
|
+
var revfl = _a.r;
|
|
2685
|
+
fl[28] = 258, revfl[258] = 28;
|
|
2686
|
+
var _b = freb(fdeb, 0);
|
|
2687
|
+
var fd = _b.b;
|
|
2688
|
+
var revfd = _b.r;
|
|
2689
|
+
var rev = new u16(32768);
|
|
2690
|
+
for (i = 0;i < 32768; ++i) {
|
|
2691
|
+
x = (i & 43690) >> 1 | (i & 21845) << 1;
|
|
2692
|
+
x = (x & 52428) >> 2 | (x & 13107) << 2;
|
|
2693
|
+
x = (x & 61680) >> 4 | (x & 3855) << 4;
|
|
2694
|
+
rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
|
|
2695
|
+
}
|
|
2696
|
+
var x;
|
|
2697
|
+
var i;
|
|
2698
|
+
var hMap = function(cd, mb, r) {
|
|
2699
|
+
var s = cd.length;
|
|
2700
|
+
var i = 0;
|
|
2701
|
+
var l = new u16(mb);
|
|
2702
|
+
for (;i < s; ++i) {
|
|
2703
|
+
if (cd[i])
|
|
2704
|
+
++l[cd[i] - 1];
|
|
2705
|
+
}
|
|
2706
|
+
var le = new u16(mb);
|
|
2707
|
+
for (i = 1;i < mb; ++i) {
|
|
2708
|
+
le[i] = le[i - 1] + l[i - 1] << 1;
|
|
2709
|
+
}
|
|
2710
|
+
var co;
|
|
2711
|
+
if (r) {
|
|
2712
|
+
co = new u16(1 << mb);
|
|
2713
|
+
var rvb = 15 - mb;
|
|
2714
|
+
for (i = 0;i < s; ++i) {
|
|
2715
|
+
if (cd[i]) {
|
|
2716
|
+
var sv = i << 4 | cd[i];
|
|
2717
|
+
var r_1 = mb - cd[i];
|
|
2718
|
+
var v = le[cd[i] - 1]++ << r_1;
|
|
2719
|
+
for (var m = v | (1 << r_1) - 1;v <= m; ++v) {
|
|
2720
|
+
co[rev[v] >> rvb] = sv;
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
} else {
|
|
2725
|
+
co = new u16(s);
|
|
2726
|
+
for (i = 0;i < s; ++i) {
|
|
2727
|
+
if (cd[i]) {
|
|
2728
|
+
co[i] = rev[le[cd[i] - 1]++] >> 15 - cd[i];
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
return co;
|
|
2733
|
+
};
|
|
2734
|
+
var flt = new u8(288);
|
|
2735
|
+
for (i = 0;i < 144; ++i)
|
|
2736
|
+
flt[i] = 8;
|
|
2737
|
+
var i;
|
|
2738
|
+
for (i = 144;i < 256; ++i)
|
|
2739
|
+
flt[i] = 9;
|
|
2740
|
+
var i;
|
|
2741
|
+
for (i = 256;i < 280; ++i)
|
|
2742
|
+
flt[i] = 7;
|
|
2743
|
+
var i;
|
|
2744
|
+
for (i = 280;i < 288; ++i)
|
|
2745
|
+
flt[i] = 8;
|
|
2746
|
+
var i;
|
|
2747
|
+
var fdt = new u8(32);
|
|
2748
|
+
for (i = 0;i < 32; ++i)
|
|
2749
|
+
fdt[i] = 5;
|
|
2750
|
+
var i;
|
|
2751
|
+
var flrm = /* @__PURE__ */ hMap(flt, 9, 1);
|
|
2752
|
+
var fdrm = /* @__PURE__ */ hMap(fdt, 5, 1);
|
|
2753
|
+
var max = function(a) {
|
|
2754
|
+
var m = a[0];
|
|
2755
|
+
for (var i = 1;i < a.length; ++i) {
|
|
2756
|
+
if (a[i] > m)
|
|
2757
|
+
m = a[i];
|
|
2758
|
+
}
|
|
2759
|
+
return m;
|
|
2760
|
+
};
|
|
2761
|
+
var bits = function(d, p, m) {
|
|
2762
|
+
var o = p / 8 | 0;
|
|
2763
|
+
return (d[o] | d[o + 1] << 8) >> (p & 7) & m;
|
|
2764
|
+
};
|
|
2765
|
+
var bits16 = function(d, p) {
|
|
2766
|
+
var o = p / 8 | 0;
|
|
2767
|
+
return (d[o] | d[o + 1] << 8 | d[o + 2] << 16) >> (p & 7);
|
|
2768
|
+
};
|
|
2769
|
+
var shft = function(p) {
|
|
2770
|
+
return (p + 7) / 8 | 0;
|
|
2771
|
+
};
|
|
2772
|
+
var slc = function(v, s, e) {
|
|
2773
|
+
if (s == null || s < 0)
|
|
2774
|
+
s = 0;
|
|
2775
|
+
if (e == null || e > v.length)
|
|
2776
|
+
e = v.length;
|
|
2777
|
+
return new u8(v.subarray(s, e));
|
|
2778
|
+
};
|
|
2779
|
+
var ec = [
|
|
2780
|
+
"unexpected EOF",
|
|
2781
|
+
"invalid block type",
|
|
2782
|
+
"invalid length/literal",
|
|
2783
|
+
"invalid distance",
|
|
2784
|
+
"stream finished",
|
|
2785
|
+
"no stream handler",
|
|
2786
|
+
,
|
|
2787
|
+
"no callback",
|
|
2788
|
+
"invalid UTF-8 data",
|
|
2789
|
+
"extra field too long",
|
|
2790
|
+
"date not in range 1980-2099",
|
|
2791
|
+
"filename too long",
|
|
2792
|
+
"stream finishing",
|
|
2793
|
+
"invalid zip data"
|
|
2794
|
+
];
|
|
2795
|
+
var err = function(ind, msg, nt) {
|
|
2796
|
+
var e = new Error(msg || ec[ind]);
|
|
2797
|
+
e.code = ind;
|
|
2798
|
+
if (Error.captureStackTrace)
|
|
2799
|
+
Error.captureStackTrace(e, err);
|
|
2800
|
+
if (!nt)
|
|
2801
|
+
throw e;
|
|
2802
|
+
return e;
|
|
2803
|
+
};
|
|
2804
|
+
var inflt = function(dat, st, buf, dict) {
|
|
2805
|
+
var sl = dat.length, dl = dict ? dict.length : 0;
|
|
2806
|
+
if (!sl || st.f && !st.l)
|
|
2807
|
+
return buf || new u8(0);
|
|
2808
|
+
var noBuf = !buf;
|
|
2809
|
+
var resize = noBuf || st.i != 2;
|
|
2810
|
+
var noSt = st.i;
|
|
2811
|
+
if (noBuf)
|
|
2812
|
+
buf = new u8(sl * 3);
|
|
2813
|
+
var cbuf = function(l) {
|
|
2814
|
+
var bl = buf.length;
|
|
2815
|
+
if (l > bl) {
|
|
2816
|
+
var nbuf = new u8(Math.max(bl * 2, l));
|
|
2817
|
+
nbuf.set(buf);
|
|
2818
|
+
buf = nbuf;
|
|
2819
|
+
}
|
|
2820
|
+
};
|
|
2821
|
+
var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, { l: lm, d: dm, m: lbt, n: dbt } = st;
|
|
2822
|
+
var tbts = sl * 8;
|
|
2823
|
+
do {
|
|
2824
|
+
if (!lm) {
|
|
2825
|
+
final = bits(dat, pos, 1);
|
|
2826
|
+
var type = bits(dat, pos + 1, 3);
|
|
2827
|
+
pos += 3;
|
|
2828
|
+
if (!type) {
|
|
2829
|
+
var s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l;
|
|
2830
|
+
if (t > sl) {
|
|
2831
|
+
if (noSt)
|
|
2832
|
+
err(0);
|
|
2833
|
+
break;
|
|
2834
|
+
}
|
|
2835
|
+
if (resize)
|
|
2836
|
+
cbuf(bt + l);
|
|
2837
|
+
buf.set(dat.subarray(s, t), bt);
|
|
2838
|
+
st.b = bt += l, st.p = pos = t * 8, st.f = final;
|
|
2839
|
+
continue;
|
|
2840
|
+
} else if (type == 1)
|
|
2841
|
+
lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
|
|
2842
|
+
else if (type == 2) {
|
|
2843
|
+
var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
|
|
2844
|
+
var tl = hLit + bits(dat, pos + 5, 31) + 1;
|
|
2845
|
+
pos += 14;
|
|
2846
|
+
var ldt = new u8(tl);
|
|
2847
|
+
var clt = new u8(19);
|
|
2848
|
+
for (var i = 0;i < hcLen; ++i) {
|
|
2849
|
+
clt[clim[i]] = bits(dat, pos + i * 3, 7);
|
|
2850
|
+
}
|
|
2851
|
+
pos += hcLen * 3;
|
|
2852
|
+
var clb = max(clt), clbmsk = (1 << clb) - 1;
|
|
2853
|
+
var clm = hMap(clt, clb, 1);
|
|
2854
|
+
for (var i = 0;i < tl; ) {
|
|
2855
|
+
var r = clm[bits(dat, pos, clbmsk)];
|
|
2856
|
+
pos += r & 15;
|
|
2857
|
+
var s = r >> 4;
|
|
2858
|
+
if (s < 16) {
|
|
2859
|
+
ldt[i++] = s;
|
|
2860
|
+
} else {
|
|
2861
|
+
var c = 0, n = 0;
|
|
2862
|
+
if (s == 16)
|
|
2863
|
+
n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];
|
|
2864
|
+
else if (s == 17)
|
|
2865
|
+
n = 3 + bits(dat, pos, 7), pos += 3;
|
|
2866
|
+
else if (s == 18)
|
|
2867
|
+
n = 11 + bits(dat, pos, 127), pos += 7;
|
|
2868
|
+
while (n--)
|
|
2869
|
+
ldt[i++] = c;
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
|
|
2873
|
+
lbt = max(lt);
|
|
2874
|
+
dbt = max(dt);
|
|
2875
|
+
lm = hMap(lt, lbt, 1);
|
|
2876
|
+
dm = hMap(dt, dbt, 1);
|
|
2877
|
+
} else
|
|
2878
|
+
err(1);
|
|
2879
|
+
if (pos > tbts) {
|
|
2880
|
+
if (noSt)
|
|
2881
|
+
err(0);
|
|
2882
|
+
break;
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
if (resize)
|
|
2886
|
+
cbuf(bt + 131072);
|
|
2887
|
+
var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
|
|
2888
|
+
var lpos = pos;
|
|
2889
|
+
for (;; lpos = pos) {
|
|
2890
|
+
var c = lm[bits16(dat, pos) & lms], sym = c >> 4;
|
|
2891
|
+
pos += c & 15;
|
|
2892
|
+
if (pos > tbts) {
|
|
2893
|
+
if (noSt)
|
|
2894
|
+
err(0);
|
|
2895
|
+
break;
|
|
2896
|
+
}
|
|
2897
|
+
if (!c)
|
|
2898
|
+
err(2);
|
|
2899
|
+
if (sym < 256)
|
|
2900
|
+
buf[bt++] = sym;
|
|
2901
|
+
else if (sym == 256) {
|
|
2902
|
+
lpos = pos, lm = null;
|
|
2903
|
+
break;
|
|
2904
|
+
} else {
|
|
2905
|
+
var add = sym - 254;
|
|
2906
|
+
if (sym > 264) {
|
|
2907
|
+
var i = sym - 257, b = fleb[i];
|
|
2908
|
+
add = bits(dat, pos, (1 << b) - 1) + fl[i];
|
|
2909
|
+
pos += b;
|
|
2910
|
+
}
|
|
2911
|
+
var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
|
|
2912
|
+
if (!d)
|
|
2913
|
+
err(3);
|
|
2914
|
+
pos += d & 15;
|
|
2915
|
+
var dt = fd[dsym];
|
|
2916
|
+
if (dsym > 3) {
|
|
2917
|
+
var b = fdeb[dsym];
|
|
2918
|
+
dt += bits16(dat, pos) & (1 << b) - 1, pos += b;
|
|
2919
|
+
}
|
|
2920
|
+
if (pos > tbts) {
|
|
2921
|
+
if (noSt)
|
|
2922
|
+
err(0);
|
|
2923
|
+
break;
|
|
2924
|
+
}
|
|
2925
|
+
if (resize)
|
|
2926
|
+
cbuf(bt + 131072);
|
|
2927
|
+
var end = bt + add;
|
|
2928
|
+
if (bt < dt) {
|
|
2929
|
+
var shift = dl - dt, dend = Math.min(dt, end);
|
|
2930
|
+
if (shift + bt < 0)
|
|
2931
|
+
err(3);
|
|
2932
|
+
for (;bt < dend; ++bt)
|
|
2933
|
+
buf[bt] = dict[shift + bt];
|
|
2934
|
+
}
|
|
2935
|
+
for (;bt < end; ++bt)
|
|
2936
|
+
buf[bt] = buf[bt - dt];
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
st.l = lm, st.p = lpos, st.b = bt, st.f = final;
|
|
2940
|
+
if (lm)
|
|
2941
|
+
final = 1, st.m = lbt, st.d = dm, st.n = dbt;
|
|
2942
|
+
} while (!final);
|
|
2943
|
+
return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
|
|
2944
|
+
};
|
|
2945
|
+
var et = /* @__PURE__ */ new u8(0);
|
|
2946
|
+
var b2 = function(d, b) {
|
|
2947
|
+
return d[b] | d[b + 1] << 8;
|
|
2948
|
+
};
|
|
2949
|
+
var b4 = function(d, b) {
|
|
2950
|
+
return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0;
|
|
2951
|
+
};
|
|
2952
|
+
var b8 = function(d, b) {
|
|
2953
|
+
return b4(d, b) + b4(d, b + 4) * 4294967296;
|
|
2954
|
+
};
|
|
2955
|
+
function inflateSync(data, opts) {
|
|
2956
|
+
return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);
|
|
2957
|
+
}
|
|
2958
|
+
var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder;
|
|
2959
|
+
var tds = 0;
|
|
2960
|
+
try {
|
|
2961
|
+
td.decode(et, { stream: true });
|
|
2962
|
+
tds = 1;
|
|
2963
|
+
} catch (e) {}
|
|
2964
|
+
var dutf8 = function(d) {
|
|
2965
|
+
for (var r = "", i = 0;; ) {
|
|
2966
|
+
var c = d[i++];
|
|
2967
|
+
var eb = (c > 127) + (c > 223) + (c > 239);
|
|
2968
|
+
if (i + eb > d.length)
|
|
2969
|
+
return { s: r, r: slc(d, i - 1) };
|
|
2970
|
+
if (!eb)
|
|
2971
|
+
r += String.fromCharCode(c);
|
|
2972
|
+
else if (eb == 3) {
|
|
2973
|
+
c = ((c & 15) << 18 | (d[i++] & 63) << 12 | (d[i++] & 63) << 6 | d[i++] & 63) - 65536, r += String.fromCharCode(55296 | c >> 10, 56320 | c & 1023);
|
|
2974
|
+
} else if (eb & 1)
|
|
2975
|
+
r += String.fromCharCode((c & 31) << 6 | d[i++] & 63);
|
|
2976
|
+
else
|
|
2977
|
+
r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | d[i++] & 63);
|
|
2978
|
+
}
|
|
2979
|
+
};
|
|
2980
|
+
function strFromU8(dat, latin1) {
|
|
2981
|
+
if (latin1) {
|
|
2982
|
+
var r = "";
|
|
2983
|
+
for (var i = 0;i < dat.length; i += 16384)
|
|
2984
|
+
r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));
|
|
2985
|
+
return r;
|
|
2986
|
+
} else if (td) {
|
|
2987
|
+
return td.decode(dat);
|
|
2988
|
+
} else {
|
|
2989
|
+
var _a = dutf8(dat), { s, r } = _a;
|
|
2990
|
+
if (r.length)
|
|
2991
|
+
err(8);
|
|
2992
|
+
return s;
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
var slzh = function(d, b) {
|
|
2996
|
+
return b + 30 + b2(d, b + 26) + b2(d, b + 28);
|
|
2997
|
+
};
|
|
2998
|
+
var zh = function(d, b, z) {
|
|
2999
|
+
var fnl = b2(d, b + 28), efl = b2(d, b + 30), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl;
|
|
3000
|
+
var _a = z64hs(d, es, efl, z, b4(d, b + 20), b4(d, b + 24), b4(d, b + 42)), sc = _a[0], su = _a[1], off = _a[2];
|
|
3001
|
+
return [b2(d, b + 10), sc, su, fn, es + efl + b2(d, b + 32), off];
|
|
3002
|
+
};
|
|
3003
|
+
var z64hs = function(d, b, l, z, sc, su, off) {
|
|
3004
|
+
var nsc = sc == 4294967295, nsu = su == 4294967295, noff = off == 4294967295, e = b + l;
|
|
3005
|
+
var nf = nsc + nsu + noff;
|
|
3006
|
+
if (z && nf) {
|
|
3007
|
+
for (;b + 4 < e; b += 4 + b2(d, b + 2)) {
|
|
3008
|
+
if (b2(d, b) == 1) {
|
|
3009
|
+
return [
|
|
3010
|
+
nsc ? b8(d, b + 4 + 8 * nsu) : sc,
|
|
3011
|
+
nsu ? b8(d, b + 4) : su,
|
|
3012
|
+
noff ? b8(d, b + 4 + 8 * (nsu + nsc)) : off,
|
|
3013
|
+
1
|
|
3014
|
+
];
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
if (z < 2)
|
|
3018
|
+
err(13);
|
|
3019
|
+
}
|
|
3020
|
+
return [sc, su, off, 0];
|
|
3021
|
+
};
|
|
3022
|
+
function unzipSync(data, opts) {
|
|
3023
|
+
var files = {};
|
|
3024
|
+
var e = data.length - 22;
|
|
3025
|
+
for (;b4(data, e) != 101010256; --e) {
|
|
3026
|
+
if (!e || data.length - e > 65558)
|
|
3027
|
+
err(13);
|
|
3028
|
+
}
|
|
3029
|
+
var c = b2(data, e + 8);
|
|
3030
|
+
if (!c)
|
|
3031
|
+
return {};
|
|
3032
|
+
var o = b4(data, e + 16);
|
|
3033
|
+
var z = b4(data, e - 20) == 117853008;
|
|
3034
|
+
if (z) {
|
|
3035
|
+
var ze = b4(data, e - 12);
|
|
3036
|
+
z = b4(data, ze) == 101075792;
|
|
3037
|
+
if (z) {
|
|
3038
|
+
c = b4(data, ze + 32);
|
|
3039
|
+
o = b4(data, ze + 48);
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
var fltr = opts && opts.filter;
|
|
3043
|
+
for (var i = 0;i < c; ++i) {
|
|
3044
|
+
var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);
|
|
3045
|
+
o = no;
|
|
3046
|
+
if (!fltr || fltr({
|
|
3047
|
+
name: fn,
|
|
3048
|
+
size: sc,
|
|
3049
|
+
originalSize: su,
|
|
3050
|
+
compression: c_2
|
|
3051
|
+
})) {
|
|
3052
|
+
if (!c_2)
|
|
3053
|
+
files[fn] = slc(data, b, b + sc);
|
|
3054
|
+
else if (c_2 == 8)
|
|
3055
|
+
files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });
|
|
3056
|
+
else
|
|
3057
|
+
err(14, "unknown compression type " + c_2);
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
return files;
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
// packages/gateway/src/skill-market.ts
|
|
3064
|
+
class SkillMarketError extends Error {
|
|
3065
|
+
code;
|
|
3066
|
+
constructor(code, message) {
|
|
3067
|
+
super(message);
|
|
3068
|
+
this.name = "SkillMarketError";
|
|
3069
|
+
this.code = code;
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
var CATALOG_TTL_MS = 60000;
|
|
3073
|
+
var ZIP_CACHE_MAX = 16;
|
|
3074
|
+
var MAX_PACKAGE_BYTES = 32 * 1024 * 1024;
|
|
3075
|
+
var userSkillsDir2 = (home) => join8(home, ".agents", "skills");
|
|
3076
|
+
var projectSkillsDir = (workspaceRoot) => join8(workspaceRoot, ".agents", "skills");
|
|
3077
|
+
var activeLevelOf = (state) => state.project ? "project" : state.user ? "user" : state.builtin ? "builtin" : null;
|
|
3078
|
+
var normalizeEntry = (raw) => {
|
|
3079
|
+
const row = raw ?? {};
|
|
3080
|
+
const metadata = row.metadata;
|
|
3081
|
+
const source = metadata !== null && typeof metadata === "object" ? metadata.source : undefined;
|
|
3082
|
+
return {
|
|
3083
|
+
name: String(row.name ?? ""),
|
|
3084
|
+
description: String(row.description ?? ""),
|
|
3085
|
+
category: typeof row.category === "string" && row.category !== "" ? row.category : "uncategorized",
|
|
3086
|
+
source: typeof source === "string" && source !== "" ? source : "unknown",
|
|
3087
|
+
license: String(row.license ?? ""),
|
|
3088
|
+
version: String(row.version ?? ""),
|
|
3089
|
+
sha256: String(row.sha256 ?? ""),
|
|
3090
|
+
sizeBytes: Number(row.size_bytes ?? 0),
|
|
3091
|
+
uploadedAt: String(row.uploaded_at ?? "")
|
|
3092
|
+
};
|
|
3093
|
+
};
|
|
3094
|
+
function createSkillMarketClient(options) {
|
|
3095
|
+
const home = options.home ?? homedir5();
|
|
3096
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
3097
|
+
const now = options.now ?? Date.now;
|
|
3098
|
+
let base;
|
|
3099
|
+
try {
|
|
3100
|
+
base = new URL(options.marketUrl);
|
|
3101
|
+
if (base.protocol !== "https:" && base.protocol !== "http:")
|
|
3102
|
+
throw new Error("unsupported protocol");
|
|
3103
|
+
} catch {
|
|
3104
|
+
throw new SkillMarketError("market_invalid_url", `Invalid skill market URL: ${options.marketUrl}`);
|
|
3105
|
+
}
|
|
3106
|
+
base = new URL(base.toString().replace(/\/+$/, ""));
|
|
3107
|
+
let catalog;
|
|
3108
|
+
const zipCache = new Map;
|
|
3109
|
+
const request = async (path) => {
|
|
3110
|
+
let response;
|
|
3111
|
+
try {
|
|
3112
|
+
response = await fetchImpl(new URL(path, `${base}/`).toString());
|
|
3113
|
+
} catch (error) {
|
|
3114
|
+
throw new SkillMarketError("market_unavailable", `Skill market unreachable: ${error instanceof Error ? error.message : String(error)}`);
|
|
3115
|
+
}
|
|
3116
|
+
return response;
|
|
3117
|
+
};
|
|
3118
|
+
const refreshCatalog = async () => {
|
|
3119
|
+
const response = await request("api/skills");
|
|
3120
|
+
if (!response.ok)
|
|
3121
|
+
throw new SkillMarketError("market_unavailable", `Skill list failed with HTTP ${response.status}`);
|
|
3122
|
+
const data = await response.json();
|
|
3123
|
+
const skills = Array.isArray(data.skills) ? data.skills.map(normalizeEntry) : [];
|
|
3124
|
+
catalog = { at: now(), skills };
|
|
3125
|
+
return skills;
|
|
3126
|
+
};
|
|
3127
|
+
const listSkills = async () => catalog !== undefined && now() - catalog.at < CATALOG_TTL_MS ? catalog.skills : refreshCatalog();
|
|
3128
|
+
const detail = async (name) => {
|
|
3129
|
+
let hit = (await listSkills()).find((skill) => skill.name === name);
|
|
3130
|
+
if (hit === undefined)
|
|
3131
|
+
hit = (await refreshCatalog()).find((skill) => skill.name === name);
|
|
3132
|
+
if (hit === undefined)
|
|
3133
|
+
throw new SkillMarketError("skill_not_found", `Skill not found on market: ${name}`);
|
|
3134
|
+
return hit;
|
|
3135
|
+
};
|
|
3136
|
+
const download = async (skill) => {
|
|
3137
|
+
const cached = zipCache.get(skill.sha256);
|
|
3138
|
+
if (cached !== undefined) {
|
|
3139
|
+
zipCache.delete(skill.sha256);
|
|
3140
|
+
zipCache.set(skill.sha256, cached);
|
|
3141
|
+
return cached;
|
|
3142
|
+
}
|
|
3143
|
+
if (skill.sizeBytes > MAX_PACKAGE_BYTES)
|
|
3144
|
+
throw new SkillMarketError("skill_too_large", `Package ${skill.name} is ${skill.sizeBytes} bytes (limit ${MAX_PACKAGE_BYTES})`);
|
|
3145
|
+
const response = await request(`api/skills/${encodeURIComponent(skill.name)}/download`);
|
|
3146
|
+
if (!response.ok)
|
|
3147
|
+
throw new SkillMarketError(response.status === 404 ? "skill_not_found" : "market_unavailable", `Download of ${skill.name} failed with HTTP ${response.status}`);
|
|
3148
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
3149
|
+
if (bytes.byteLength > MAX_PACKAGE_BYTES)
|
|
3150
|
+
throw new SkillMarketError("skill_too_large", `Package ${skill.name} exceeds ${MAX_PACKAGE_BYTES} bytes`);
|
|
3151
|
+
const digest = createHash2("sha256").update(bytes).digest("hex");
|
|
3152
|
+
if (digest !== skill.sha256)
|
|
3153
|
+
throw new SkillMarketError("skill_hash_mismatch", `SHA-256 mismatch for ${skill.name}: catalog says ${skill.sha256}, got ${digest}`);
|
|
3154
|
+
zipCache.set(skill.sha256, bytes);
|
|
3155
|
+
while (zipCache.size > ZIP_CACHE_MAX) {
|
|
3156
|
+
const oldest = zipCache.keys().next().value;
|
|
3157
|
+
if (oldest === undefined)
|
|
3158
|
+
break;
|
|
3159
|
+
zipCache.delete(oldest);
|
|
3160
|
+
}
|
|
3161
|
+
return bytes;
|
|
3162
|
+
};
|
|
3163
|
+
const unpack = (skill, zip) => {
|
|
3164
|
+
let entries;
|
|
3165
|
+
try {
|
|
3166
|
+
entries = unzipSync(zip);
|
|
3167
|
+
} catch {
|
|
3168
|
+
throw new SkillMarketError("skill_invalid_package", `Package ${skill.name} is not a valid zip archive`);
|
|
3169
|
+
}
|
|
3170
|
+
const files = new Map;
|
|
3171
|
+
const skillMds = [];
|
|
3172
|
+
for (const name of Object.keys(entries)) {
|
|
3173
|
+
if (name.endsWith("/"))
|
|
3174
|
+
continue;
|
|
3175
|
+
if (name.includes("\\") || name.startsWith("/"))
|
|
3176
|
+
throw new SkillMarketError("skill_invalid_package", `Unsafe path in package: ${name}`);
|
|
3177
|
+
const segments = name.split("/");
|
|
3178
|
+
if (segments.some((s) => s === ".." || s === ""))
|
|
3179
|
+
throw new SkillMarketError("skill_invalid_package", `Unsafe path in package: ${name}`);
|
|
3180
|
+
if (segments[segments.length - 1] === "SKILL.md")
|
|
3181
|
+
skillMds.push(name);
|
|
3182
|
+
files.set(name, entries[name]);
|
|
3183
|
+
}
|
|
3184
|
+
if (skillMds.length !== 1)
|
|
3185
|
+
throw new SkillMarketError("skill_invalid_package", skillMds.length === 0 ? `Package ${skill.name} contains no SKILL.md` : `Package ${skill.name} contains multiple SKILL.md files`);
|
|
3186
|
+
const root = skillMds[0].slice(0, -"SKILL.md".length);
|
|
3187
|
+
const inner = new Map;
|
|
3188
|
+
for (const [name, bytes] of files) {
|
|
3189
|
+
if (!name.startsWith(root))
|
|
3190
|
+
continue;
|
|
3191
|
+
inner.set(name.slice(root.length), bytes);
|
|
3192
|
+
}
|
|
3193
|
+
return inner;
|
|
3194
|
+
};
|
|
3195
|
+
const skillsDirFor = (scope, workspaceRoot) => {
|
|
3196
|
+
if (scope === "user")
|
|
3197
|
+
return userSkillsDir2(home);
|
|
3198
|
+
if (workspaceRoot === undefined || workspaceRoot === "")
|
|
3199
|
+
throw new SkillMarketError("market_unavailable", "Project-scope install needs a workspaceRoot");
|
|
3200
|
+
return projectSkillsDir(workspaceRoot);
|
|
3201
|
+
};
|
|
3202
|
+
const sweepTempDirs = async (skillsDir) => {
|
|
3203
|
+
const parent = dirname5(skillsDir);
|
|
3204
|
+
let names = [];
|
|
3205
|
+
try {
|
|
3206
|
+
names = await readdir2(parent);
|
|
3207
|
+
} catch {
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
await Promise.all(names.filter((n) => n.startsWith(".tmp-")).map((n) => rm2(join8(parent, n), { recursive: true, force: true }).catch(() => {})));
|
|
3211
|
+
};
|
|
3212
|
+
const install = async ({ name, scope, workspaceRoot, overwrite }) => {
|
|
3213
|
+
const skillsDir = skillsDirFor(scope, workspaceRoot);
|
|
3214
|
+
const target = join8(skillsDir, name);
|
|
3215
|
+
const exists = await stat2(target).catch(() => {
|
|
3216
|
+
return;
|
|
3217
|
+
}) !== undefined;
|
|
3218
|
+
if (exists && overwrite !== true)
|
|
3219
|
+
throw new SkillMarketError("skill_exists", `Skill ${name} already exists at ${scope} level (${target})`);
|
|
3220
|
+
const skill = await detail(name);
|
|
3221
|
+
const files = unpack(skill, await download(skill));
|
|
3222
|
+
await sweepTempDirs(skillsDir);
|
|
3223
|
+
const temp = join8(dirname5(skillsDir), `.tmp-${randomUUID()}`);
|
|
3224
|
+
try {
|
|
3225
|
+
for (const [relative, bytes] of files) {
|
|
3226
|
+
const dest = join8(temp, ...relative.split("/"));
|
|
3227
|
+
await mkdir2(dirname5(dest), { recursive: true });
|
|
3228
|
+
await writeFile2(dest, bytes);
|
|
3229
|
+
}
|
|
3230
|
+
if (exists)
|
|
3231
|
+
await rm2(target, { recursive: true, force: true });
|
|
3232
|
+
await mkdir2(skillsDir, { recursive: true });
|
|
3233
|
+
await rename2(temp, target);
|
|
3234
|
+
} finally {
|
|
3235
|
+
await rm2(temp, { recursive: true, force: true }).catch(() => {});
|
|
3236
|
+
}
|
|
3237
|
+
let shadowedBy;
|
|
3238
|
+
if (scope === "user" && workspaceRoot !== undefined && workspaceRoot !== "") {
|
|
3239
|
+
const projectDir = join8(projectSkillsDir(workspaceRoot), name);
|
|
3240
|
+
if (await stat2(projectDir).catch(() => {
|
|
3241
|
+
return;
|
|
3242
|
+
}) !== undefined)
|
|
3243
|
+
shadowedBy = "project";
|
|
3244
|
+
}
|
|
3245
|
+
return { name, scope, path: target, ...shadowedBy !== undefined ? { shadowedBy } : {} };
|
|
3246
|
+
};
|
|
3247
|
+
const uninstall = async ({ name, scope, workspaceRoot }) => {
|
|
3248
|
+
const skillsDir = skillsDirFor(scope, workspaceRoot);
|
|
3249
|
+
const target = join8(skillsDir, name);
|
|
3250
|
+
if (await stat2(target).catch(() => {
|
|
3251
|
+
return;
|
|
3252
|
+
}) === undefined)
|
|
3253
|
+
throw new SkillMarketError("skill_not_installed", `Skill ${name} is not installed at ${scope} level`);
|
|
3254
|
+
await rm2(target, { recursive: true, force: true });
|
|
3255
|
+
};
|
|
3256
|
+
return {
|
|
3257
|
+
async list(query) {
|
|
3258
|
+
if (query !== undefined && query.trim() !== "") {
|
|
3259
|
+
const response = await request(`api/skills?q=${encodeURIComponent(query.trim())}`);
|
|
3260
|
+
if (!response.ok)
|
|
3261
|
+
throw new SkillMarketError("market_unavailable", `Skill search failed with HTTP ${response.status}`);
|
|
3262
|
+
const data = await response.json();
|
|
3263
|
+
return Array.isArray(data.skills) ? data.skills.map(normalizeEntry) : [];
|
|
3264
|
+
}
|
|
3265
|
+
return listSkills();
|
|
3266
|
+
},
|
|
3267
|
+
detail,
|
|
3268
|
+
async preview(name) {
|
|
3269
|
+
const skill = await detail(name);
|
|
3270
|
+
const inner = unpack(skill, await download(skill));
|
|
3271
|
+
const bodyBytes = inner.get("SKILL.md");
|
|
3272
|
+
if (bodyBytes === undefined)
|
|
3273
|
+
throw new SkillMarketError("skill_invalid_package", `Package ${name} lost its SKILL.md`);
|
|
3274
|
+
const body = stripFrontmatter(new TextDecoder().decode(bodyBytes));
|
|
3275
|
+
return { body, files: [...inner.keys()].sort() };
|
|
3276
|
+
},
|
|
3277
|
+
install,
|
|
3278
|
+
uninstall
|
|
3279
|
+
};
|
|
3280
|
+
}
|
|
3281
|
+
|
|
2609
3282
|
// packages/gateway/src/workspaces.ts
|
|
2610
3283
|
import { constants as constants3 } from "fs";
|
|
2611
|
-
import { access, readdir as
|
|
2612
|
-
import { dirname as
|
|
3284
|
+
import { access, readdir as readdir3, stat as stat3 } from "fs/promises";
|
|
3285
|
+
import { dirname as dirname6, isAbsolute as isAbsolute3, join as join9 } from "path";
|
|
2613
3286
|
async function browseWorkspaces(options) {
|
|
2614
3287
|
const requested = options.path.replace(/^\/+([A-Za-z]:[\\/])/, "$1");
|
|
2615
3288
|
if (process.platform === "win32" && (requested === "/" || requested === "\\")) {
|
|
@@ -2631,16 +3304,16 @@ async function browseWorkspaces(options) {
|
|
|
2631
3304
|
throw new Error("\u76EE\u5F55\u5206\u9875\u5DF2\u5931\u6548\uFF0C\u8BF7\u5237\u65B0\u5217\u8868");
|
|
2632
3305
|
}
|
|
2633
3306
|
}
|
|
2634
|
-
const entries = (await
|
|
3307
|
+
const entries = (await readdir3(path, { withFileTypes: true })).filter((entry) => (options.showHidden || !entry.name.startsWith(".")) && entry.name.toLowerCase().includes(query)).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
2635
3308
|
const directories = [];
|
|
2636
3309
|
let next = offset;
|
|
2637
3310
|
for (;next < entries.length && directories.length < 100; next++) {
|
|
2638
3311
|
const entry = entries[next];
|
|
2639
|
-
const target =
|
|
3312
|
+
const target = join9(path, entry.name);
|
|
2640
3313
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
2641
3314
|
continue;
|
|
2642
3315
|
try {
|
|
2643
|
-
if (!(await
|
|
3316
|
+
if (!(await stat3(target)).isDirectory())
|
|
2644
3317
|
continue;
|
|
2645
3318
|
await access(target, constants3.R_OK | constants3.X_OK);
|
|
2646
3319
|
directories.push({ name: entry.name, path: target });
|
|
@@ -2650,7 +3323,7 @@ async function browseWorkspaces(options) {
|
|
|
2650
3323
|
}
|
|
2651
3324
|
return {
|
|
2652
3325
|
path,
|
|
2653
|
-
parent:
|
|
3326
|
+
parent: dirname6(path) === path ? null : dirname6(path),
|
|
2654
3327
|
entries: directories,
|
|
2655
3328
|
next: next < entries.length ? JSON.stringify({ key, offset: next }) : null
|
|
2656
3329
|
};
|
|
@@ -2670,6 +3343,26 @@ async function listDrives() {
|
|
|
2670
3343
|
// packages/gateway/src/server-services.ts
|
|
2671
3344
|
async function createNekoServerServices(options) {
|
|
2672
3345
|
const { runtime, context } = options;
|
|
3346
|
+
const market = createSkillMarketClient({
|
|
3347
|
+
marketUrl: options.marketUrl,
|
|
3348
|
+
...options.promptHome !== undefined ? { home: options.promptHome } : {}
|
|
3349
|
+
});
|
|
3350
|
+
const toRemote = (error) => {
|
|
3351
|
+
if (error instanceof SkillMarketError) {
|
|
3352
|
+
const code = error.code === "skill_not_found" || error.code === "skill_not_installed" ? "service_not_found" : "service_invalid_value";
|
|
3353
|
+
throw new RemoteServiceError(code, `[${error.code}] ${error.message}`);
|
|
3354
|
+
}
|
|
3355
|
+
throw error;
|
|
3356
|
+
};
|
|
3357
|
+
const withInstallState = async (skill, workspaceRoot) => {
|
|
3358
|
+
const locals = await runtime.listSkillsByLevel(workspaceRoot);
|
|
3359
|
+
const installed = {
|
|
3360
|
+
project: locals.some((s) => s.name === skill.name && s.level === "project"),
|
|
3361
|
+
user: locals.some((s) => s.name === skill.name && s.level === "user"),
|
|
3362
|
+
builtin: locals.some((s) => s.name === skill.name && s.level === "builtin")
|
|
3363
|
+
};
|
|
3364
|
+
return { ...skill, installed, activeLevel: activeLevelOf(installed) };
|
|
3365
|
+
};
|
|
2673
3366
|
const list = async () => (await runtime.listConversations()).map((info) => ({
|
|
2674
3367
|
id: String(info.id),
|
|
2675
3368
|
workspaceRoot: info.workspaceRoot,
|
|
@@ -2725,7 +3418,8 @@ async function createNekoServerServices(options) {
|
|
|
2725
3418
|
{ service: NekoModels, mode: "singleton" },
|
|
2726
3419
|
{ service: NekoCredentials, mode: "singleton" },
|
|
2727
3420
|
{ service: NekoWorkspaces, mode: "singleton" },
|
|
2728
|
-
{ service: NekoWorkspaceFiles, mode: "singleton" }
|
|
3421
|
+
{ service: NekoWorkspaceFiles, mode: "singleton" },
|
|
3422
|
+
{ service: NekoSkillMarket, mode: "singleton" }
|
|
2729
3423
|
]);
|
|
2730
3424
|
provider.provide(NekoSessionDirectory, { state: directory });
|
|
2731
3425
|
const updateCredentials = (operation) => serialize(async () => {
|
|
@@ -2775,6 +3469,13 @@ async function createNekoServerServices(options) {
|
|
|
2775
3469
|
}
|
|
2776
3470
|
});
|
|
2777
3471
|
provider.provide(NekoModels, { list: async () => runtime.listModels() });
|
|
3472
|
+
provider.provide(NekoSkillMarket, {
|
|
3473
|
+
list: (input) => market.list(input.query).then((skills) => Promise.all(skills.map((skill) => withInstallState(skill, input.workspaceRoot)))).catch(toRemote),
|
|
3474
|
+
detail: (input) => market.detail(input.name).then((skill) => withInstallState(skill, input.workspaceRoot)).catch(toRemote),
|
|
3475
|
+
preview: (input) => market.preview(input.name).catch(toRemote),
|
|
3476
|
+
install: (input) => serialize(() => market.install(input).then((result) => ({ ...result })).catch(toRemote)),
|
|
3477
|
+
uninstall: (input) => serialize(() => market.uninstall(input).catch(toRemote))
|
|
3478
|
+
});
|
|
2778
3479
|
provider.provide(NekoSessionManagement, {
|
|
2779
3480
|
create: (createOptions) => serialize(async () => {
|
|
2780
3481
|
const created = await runtime.createConversation(createOptions.model !== undefined || createOptions.thinkingLevel !== undefined || createOptions.cwd !== undefined ? {
|
|
@@ -3293,9 +3994,10 @@ async function startGateway(options = {}) {
|
|
|
3293
3994
|
const storagePath = options.storagePath ?? fileConfig.storagePath ?? DEFAULT_STORAGE_PATH;
|
|
3294
3995
|
const host = options.host ?? fileConfig.gateway?.host ?? DEFAULT_HOST;
|
|
3295
3996
|
const port = options.port !== undefined ? options.port : fileConfig.gateway?.port ?? (process.platform === "win32" ? 0 : DEFAULT_PORT);
|
|
3296
|
-
const
|
|
3997
|
+
const marketUrl = options.marketUrl ?? fileConfig.skills?.marketUrl ?? DEFAULT_SKILL_MARKET_URL;
|
|
3998
|
+
const nekoDir = dirname7(storagePath);
|
|
3297
3999
|
const serverId = options.serverId ?? await loadOrCreateServerId(nekoDir);
|
|
3298
|
-
const socketPath = options.socketPath !== undefined ? options.socketPath : fileConfig.gateway?.socketPath !== undefined ? fileConfig.gateway.socketPath : process.platform === "win32" ? null :
|
|
4000
|
+
const socketPath = options.socketPath !== undefined ? options.socketPath : fileConfig.gateway?.socketPath !== undefined ? fileConfig.gateway.socketPath : process.platform === "win32" ? null : join10(nekoDir, `${serverId}.sock`);
|
|
3299
4001
|
const runtime = await createNekoRuntime({
|
|
3300
4002
|
context,
|
|
3301
4003
|
storage: options.storage ?? "sqlite",
|
|
@@ -3318,6 +4020,8 @@ async function startGateway(options = {}) {
|
|
|
3318
4020
|
serverId,
|
|
3319
4021
|
nekoDir,
|
|
3320
4022
|
model,
|
|
4023
|
+
marketUrl,
|
|
4024
|
+
promptHome: options.promptHome,
|
|
3321
4025
|
onError: options.onError
|
|
3322
4026
|
});
|
|
3323
4027
|
} catch (error) {
|
|
@@ -3328,7 +4032,12 @@ async function startGateway(options = {}) {
|
|
|
3328
4032
|
async function bindGateway(runtime, options) {
|
|
3329
4033
|
const { context, host, port, socketPath, serverId, nekoDir, model } = options;
|
|
3330
4034
|
const modelLabel = `${model.provider}/${model.modelId}`;
|
|
3331
|
-
const serverServices = await createNekoServerServices({
|
|
4035
|
+
const serverServices = await createNekoServerServices({
|
|
4036
|
+
runtime,
|
|
4037
|
+
context,
|
|
4038
|
+
marketUrl: options.marketUrl,
|
|
4039
|
+
...options.promptHome !== undefined ? { promptHome: options.promptHome } : {}
|
|
4040
|
+
});
|
|
3332
4041
|
let wsListener;
|
|
3333
4042
|
const listeners = [];
|
|
3334
4043
|
if (socketPath !== null) {
|
|
@@ -3406,15 +4115,15 @@ function serverIdFromSocketPath(socketPath) {
|
|
|
3406
4115
|
return isServerId(candidate) ? candidate : undefined;
|
|
3407
4116
|
}
|
|
3408
4117
|
async function loadOrCreateServerId(nekoDir) {
|
|
3409
|
-
const file =
|
|
4118
|
+
const file = join10(nekoDir, SERVER_ID_FILENAME);
|
|
3410
4119
|
try {
|
|
3411
4120
|
const existing = (await readFile5(file, "utf8")).trim();
|
|
3412
4121
|
if (existing !== "")
|
|
3413
4122
|
return existing;
|
|
3414
4123
|
} catch {}
|
|
3415
|
-
const created =
|
|
3416
|
-
await
|
|
3417
|
-
await
|
|
4124
|
+
const created = randomUUID2();
|
|
4125
|
+
await mkdir3(nekoDir, { recursive: true, mode: 448 });
|
|
4126
|
+
await writeFile3(file, `${created}
|
|
3418
4127
|
`, { mode: 384 });
|
|
3419
4128
|
return created;
|
|
3420
4129
|
}
|