neko-harness 0.2.2 → 0.2.4
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 +770 -52
- package/dist/neko.js +1148 -111
- package/dist/skills/skill-creator/LICENSE.txt +202 -0
- package/dist/skills/skill-creator/SKILL.md +270 -0
- package/dist/skills/skill-creator/assets/eval_review.html +146 -0
- package/dist/skills/skill-finder/SKILL.md +51 -0
- 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";
|
|
@@ -1182,14 +1192,25 @@ ${text}
|
|
|
1182
1192
|
};
|
|
1183
1193
|
|
|
1184
1194
|
// packages/core/src/prompt/resources.ts
|
|
1195
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1185
1196
|
import { readFile as readFile3 } from "fs/promises";
|
|
1186
1197
|
import { homedir as homedir3 } from "os";
|
|
1187
1198
|
import { join as join4 } from "path";
|
|
1199
|
+
import { fileURLToPath } from "url";
|
|
1188
1200
|
var userAgentsFile = (home) => join4(home, ".agents", "AGENTS.md");
|
|
1189
1201
|
var userSkillsDir = (home) => join4(home, ".agents", "skills");
|
|
1190
1202
|
var workspaceAgentsFile = (workspace) => join4(workspace, "AGENTS.md");
|
|
1191
1203
|
var workspaceSkillsDir = (workspace) => join4(workspace, ".agents", "skills");
|
|
1192
|
-
var
|
|
1204
|
+
var resolveBuiltinSkillsDir = () => {
|
|
1205
|
+
for (const url of [new URL("./skills/", import.meta.url), new URL("../../skills/", import.meta.url)]) {
|
|
1206
|
+
const path = fileURLToPath(url);
|
|
1207
|
+
if (existsSync2(join4(path, "skill-creator", "SKILL.md")))
|
|
1208
|
+
return path;
|
|
1209
|
+
}
|
|
1210
|
+
return null;
|
|
1211
|
+
};
|
|
1212
|
+
var BUILTIN_SKILLS_DIR = resolveBuiltinSkillsDir();
|
|
1213
|
+
var readResources = async (workspace, home, builtinDir, report, invocationName) => {
|
|
1193
1214
|
const contextFiles = [];
|
|
1194
1215
|
for (const path of [userAgentsFile(home), workspaceAgentsFile(workspace)]) {
|
|
1195
1216
|
try {
|
|
@@ -1210,11 +1231,13 @@ var readResources = async (workspace, home, report, invocationName) => {
|
|
|
1210
1231
|
if (skill.name === invocationName)
|
|
1211
1232
|
candidates.set(skill.filePath, content);
|
|
1212
1233
|
} : undefined;
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
|
|
1234
|
+
const scan = async (dir, level) => (await scanSkillsDir(dir, report, capture)).map((skill) => ({ ...skill, level }));
|
|
1235
|
+
const [builtin, user, project] = await Promise.all([
|
|
1236
|
+
builtinDir === null ? Promise.resolve([]) : scan(builtinDir, "builtin"),
|
|
1237
|
+
scan(userSkillsDir(home), "user"),
|
|
1238
|
+
scan(workspaceSkillsDir(workspace), "project")
|
|
1216
1239
|
]);
|
|
1217
|
-
const skills = mergeSkills([user, project]);
|
|
1240
|
+
const skills = mergeSkills([builtin, user, project]);
|
|
1218
1241
|
const winner = invocationName === undefined ? undefined : skills.find((skill) => skill.name === invocationName);
|
|
1219
1242
|
if (winner && candidates) {
|
|
1220
1243
|
const content = candidates.get(winner.filePath);
|
|
@@ -1223,11 +1246,12 @@ var readResources = async (workspace, home, report, invocationName) => {
|
|
|
1223
1246
|
}
|
|
1224
1247
|
return { contextFiles, skills };
|
|
1225
1248
|
};
|
|
1226
|
-
var createResourceLoader = (home = homedir3(), report = warnPromptDiagnostic) => {
|
|
1249
|
+
var createResourceLoader = (home = homedir3(), report = warnPromptDiagnostic, options) => {
|
|
1250
|
+
const builtinDir = options?.builtinDir === undefined ? BUILTIN_SKILLS_DIR : options.builtinDir;
|
|
1227
1251
|
const previous = new Map;
|
|
1228
1252
|
return async (workspace, invocationName) => {
|
|
1229
1253
|
const diagnostics = [];
|
|
1230
|
-
const resources = await readResources(workspace, home, (d) => diagnostics.push(d), invocationName);
|
|
1254
|
+
const resources = await readResources(workspace, home, builtinDir, (d) => diagnostics.push(d), invocationName);
|
|
1231
1255
|
const known = previous.get(workspace);
|
|
1232
1256
|
const current = new Set;
|
|
1233
1257
|
for (const diagnostic of diagnostics) {
|
|
@@ -1243,6 +1267,15 @@ var createResourceLoader = (home = homedir3(), report = warnPromptDiagnostic) =>
|
|
|
1243
1267
|
return resources;
|
|
1244
1268
|
};
|
|
1245
1269
|
};
|
|
1270
|
+
var scanSkillsByLevel = async (workspace, home = homedir3(), builtinDir = BUILTIN_SKILLS_DIR, report = warnPromptDiagnostic) => {
|
|
1271
|
+
const scan = async (dir, level) => (await scanSkillsDir(dir, report)).map((skill) => ({ ...skill, level }));
|
|
1272
|
+
const [builtin, user, project] = await Promise.all([
|
|
1273
|
+
builtinDir === null ? [] : scan(builtinDir, "builtin"),
|
|
1274
|
+
scan(userSkillsDir(home), "user"),
|
|
1275
|
+
scan(workspaceSkillsDir(workspace), "project")
|
|
1276
|
+
]);
|
|
1277
|
+
return [...project, ...user, ...builtin];
|
|
1278
|
+
};
|
|
1246
1279
|
|
|
1247
1280
|
// packages/core/src/prompt/snapshots.ts
|
|
1248
1281
|
var createResourceSnapshots = (load, submission) => {
|
|
@@ -2416,7 +2449,21 @@ async function createNekoRuntime(options = {}) {
|
|
|
2416
2449
|
const session = await sessionFor(id);
|
|
2417
2450
|
const agent = await session.conversation.agent(context);
|
|
2418
2451
|
const { skills } = await loadResources(savedWorkspace(agent.cwd));
|
|
2419
|
-
return skills.map((skill) => ({
|
|
2452
|
+
return skills.map((skill) => ({
|
|
2453
|
+
name: skill.name,
|
|
2454
|
+
description: skill.description,
|
|
2455
|
+
location: skill.filePath,
|
|
2456
|
+
level: skill.level
|
|
2457
|
+
}));
|
|
2458
|
+
},
|
|
2459
|
+
async listSkillsByLevel(cwd) {
|
|
2460
|
+
const skills = await scanSkillsByLevel(normalizeWorkspace(cwd ?? defaultCwd), options.promptHome ?? homedir4());
|
|
2461
|
+
return skills.map((skill) => ({
|
|
2462
|
+
name: skill.name,
|
|
2463
|
+
description: skill.description,
|
|
2464
|
+
location: skill.filePath,
|
|
2465
|
+
level: skill.level
|
|
2466
|
+
}));
|
|
2420
2467
|
},
|
|
2421
2468
|
installExtension(extension) {
|
|
2422
2469
|
registry.install(extension);
|
|
@@ -2585,6 +2632,7 @@ var NekoChat = defineService("neko.chat");
|
|
|
2585
2632
|
var NekoWorkspaces = defineService("neko.workspaces");
|
|
2586
2633
|
var NekoCredentials = defineService("neko.credentials");
|
|
2587
2634
|
var NekoWorkspaceFiles = defineService("neko.workspace-files");
|
|
2635
|
+
var NekoSkillMarket = defineService("neko.skill-market");
|
|
2588
2636
|
|
|
2589
2637
|
// packages/gateway/src/file-search-help.ts
|
|
2590
2638
|
function fileSearchInstallHelp(platform = process.platform) {
|
|
@@ -2606,10 +2654,644 @@ function fileSearchInstallHelp(platform = process.platform) {
|
|
|
2606
2654
|
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
2655
|
}
|
|
2608
2656
|
|
|
2657
|
+
// packages/gateway/src/skill-market.ts
|
|
2658
|
+
import { createHash as createHash2, randomUUID } from "crypto";
|
|
2659
|
+
import { mkdir as mkdir2, readdir as readdir2, rename as rename2, rm as rm2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
2660
|
+
import { homedir as homedir5 } from "os";
|
|
2661
|
+
import { dirname as dirname5, join as join8 } from "path";
|
|
2662
|
+
|
|
2663
|
+
// node_modules/fflate/esm/index.mjs
|
|
2664
|
+
import { createRequire } from "module";
|
|
2665
|
+
var require2 = createRequire("/");
|
|
2666
|
+
var _a;
|
|
2667
|
+
var Worker;
|
|
2668
|
+
var isMarkedAsUntransferable;
|
|
2669
|
+
try {
|
|
2670
|
+
_a = require2("worker_threads"), Worker = _a.Worker, isMarkedAsUntransferable = _a.isMarkedAsUntransferable;
|
|
2671
|
+
} catch (e) {}
|
|
2672
|
+
var u8 = Uint8Array;
|
|
2673
|
+
var u16 = Uint16Array;
|
|
2674
|
+
var i32 = Int32Array;
|
|
2675
|
+
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]);
|
|
2676
|
+
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]);
|
|
2677
|
+
var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
2678
|
+
var freb = function(eb, start) {
|
|
2679
|
+
var b = new u16(31);
|
|
2680
|
+
for (var i = 0;i < 31; ++i) {
|
|
2681
|
+
b[i] = start += 1 << eb[i - 1];
|
|
2682
|
+
}
|
|
2683
|
+
var r = new i32(b[30]);
|
|
2684
|
+
for (var i = 1;i < 30; ++i) {
|
|
2685
|
+
for (var j = b[i];j < b[i + 1]; ++j) {
|
|
2686
|
+
r[j] = j - b[i] << 5 | i;
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
return { b, r };
|
|
2690
|
+
};
|
|
2691
|
+
var _a = freb(fleb, 2);
|
|
2692
|
+
var fl = _a.b;
|
|
2693
|
+
var revfl = _a.r;
|
|
2694
|
+
fl[28] = 258, revfl[258] = 28;
|
|
2695
|
+
var _b = freb(fdeb, 0);
|
|
2696
|
+
var fd = _b.b;
|
|
2697
|
+
var revfd = _b.r;
|
|
2698
|
+
var rev = new u16(32768);
|
|
2699
|
+
for (i = 0;i < 32768; ++i) {
|
|
2700
|
+
x = (i & 43690) >> 1 | (i & 21845) << 1;
|
|
2701
|
+
x = (x & 52428) >> 2 | (x & 13107) << 2;
|
|
2702
|
+
x = (x & 61680) >> 4 | (x & 3855) << 4;
|
|
2703
|
+
rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
|
|
2704
|
+
}
|
|
2705
|
+
var x;
|
|
2706
|
+
var i;
|
|
2707
|
+
var hMap = function(cd, mb, r) {
|
|
2708
|
+
var s = cd.length;
|
|
2709
|
+
var i = 0;
|
|
2710
|
+
var l = new u16(mb);
|
|
2711
|
+
for (;i < s; ++i) {
|
|
2712
|
+
if (cd[i])
|
|
2713
|
+
++l[cd[i] - 1];
|
|
2714
|
+
}
|
|
2715
|
+
var le = new u16(mb);
|
|
2716
|
+
for (i = 1;i < mb; ++i) {
|
|
2717
|
+
le[i] = le[i - 1] + l[i - 1] << 1;
|
|
2718
|
+
}
|
|
2719
|
+
var co;
|
|
2720
|
+
if (r) {
|
|
2721
|
+
co = new u16(1 << mb);
|
|
2722
|
+
var rvb = 15 - mb;
|
|
2723
|
+
for (i = 0;i < s; ++i) {
|
|
2724
|
+
if (cd[i]) {
|
|
2725
|
+
var sv = i << 4 | cd[i];
|
|
2726
|
+
var r_1 = mb - cd[i];
|
|
2727
|
+
var v = le[cd[i] - 1]++ << r_1;
|
|
2728
|
+
for (var m = v | (1 << r_1) - 1;v <= m; ++v) {
|
|
2729
|
+
co[rev[v] >> rvb] = sv;
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
} else {
|
|
2734
|
+
co = new u16(s);
|
|
2735
|
+
for (i = 0;i < s; ++i) {
|
|
2736
|
+
if (cd[i]) {
|
|
2737
|
+
co[i] = rev[le[cd[i] - 1]++] >> 15 - cd[i];
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
return co;
|
|
2742
|
+
};
|
|
2743
|
+
var flt = new u8(288);
|
|
2744
|
+
for (i = 0;i < 144; ++i)
|
|
2745
|
+
flt[i] = 8;
|
|
2746
|
+
var i;
|
|
2747
|
+
for (i = 144;i < 256; ++i)
|
|
2748
|
+
flt[i] = 9;
|
|
2749
|
+
var i;
|
|
2750
|
+
for (i = 256;i < 280; ++i)
|
|
2751
|
+
flt[i] = 7;
|
|
2752
|
+
var i;
|
|
2753
|
+
for (i = 280;i < 288; ++i)
|
|
2754
|
+
flt[i] = 8;
|
|
2755
|
+
var i;
|
|
2756
|
+
var fdt = new u8(32);
|
|
2757
|
+
for (i = 0;i < 32; ++i)
|
|
2758
|
+
fdt[i] = 5;
|
|
2759
|
+
var i;
|
|
2760
|
+
var flrm = /* @__PURE__ */ hMap(flt, 9, 1);
|
|
2761
|
+
var fdrm = /* @__PURE__ */ hMap(fdt, 5, 1);
|
|
2762
|
+
var max = function(a) {
|
|
2763
|
+
var m = a[0];
|
|
2764
|
+
for (var i = 1;i < a.length; ++i) {
|
|
2765
|
+
if (a[i] > m)
|
|
2766
|
+
m = a[i];
|
|
2767
|
+
}
|
|
2768
|
+
return m;
|
|
2769
|
+
};
|
|
2770
|
+
var bits = function(d, p, m) {
|
|
2771
|
+
var o = p / 8 | 0;
|
|
2772
|
+
return (d[o] | d[o + 1] << 8) >> (p & 7) & m;
|
|
2773
|
+
};
|
|
2774
|
+
var bits16 = function(d, p) {
|
|
2775
|
+
var o = p / 8 | 0;
|
|
2776
|
+
return (d[o] | d[o + 1] << 8 | d[o + 2] << 16) >> (p & 7);
|
|
2777
|
+
};
|
|
2778
|
+
var shft = function(p) {
|
|
2779
|
+
return (p + 7) / 8 | 0;
|
|
2780
|
+
};
|
|
2781
|
+
var slc = function(v, s, e) {
|
|
2782
|
+
if (s == null || s < 0)
|
|
2783
|
+
s = 0;
|
|
2784
|
+
if (e == null || e > v.length)
|
|
2785
|
+
e = v.length;
|
|
2786
|
+
return new u8(v.subarray(s, e));
|
|
2787
|
+
};
|
|
2788
|
+
var ec = [
|
|
2789
|
+
"unexpected EOF",
|
|
2790
|
+
"invalid block type",
|
|
2791
|
+
"invalid length/literal",
|
|
2792
|
+
"invalid distance",
|
|
2793
|
+
"stream finished",
|
|
2794
|
+
"no stream handler",
|
|
2795
|
+
,
|
|
2796
|
+
"no callback",
|
|
2797
|
+
"invalid UTF-8 data",
|
|
2798
|
+
"extra field too long",
|
|
2799
|
+
"date not in range 1980-2099",
|
|
2800
|
+
"filename too long",
|
|
2801
|
+
"stream finishing",
|
|
2802
|
+
"invalid zip data"
|
|
2803
|
+
];
|
|
2804
|
+
var err = function(ind, msg, nt) {
|
|
2805
|
+
var e = new Error(msg || ec[ind]);
|
|
2806
|
+
e.code = ind;
|
|
2807
|
+
if (Error.captureStackTrace)
|
|
2808
|
+
Error.captureStackTrace(e, err);
|
|
2809
|
+
if (!nt)
|
|
2810
|
+
throw e;
|
|
2811
|
+
return e;
|
|
2812
|
+
};
|
|
2813
|
+
var inflt = function(dat, st, buf, dict) {
|
|
2814
|
+
var sl = dat.length, dl = dict ? dict.length : 0;
|
|
2815
|
+
if (!sl || st.f && !st.l)
|
|
2816
|
+
return buf || new u8(0);
|
|
2817
|
+
var noBuf = !buf;
|
|
2818
|
+
var resize = noBuf || st.i != 2;
|
|
2819
|
+
var noSt = st.i;
|
|
2820
|
+
if (noBuf)
|
|
2821
|
+
buf = new u8(sl * 3);
|
|
2822
|
+
var cbuf = function(l) {
|
|
2823
|
+
var bl = buf.length;
|
|
2824
|
+
if (l > bl) {
|
|
2825
|
+
var nbuf = new u8(Math.max(bl * 2, l));
|
|
2826
|
+
nbuf.set(buf);
|
|
2827
|
+
buf = nbuf;
|
|
2828
|
+
}
|
|
2829
|
+
};
|
|
2830
|
+
var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, { l: lm, d: dm, m: lbt, n: dbt } = st;
|
|
2831
|
+
var tbts = sl * 8;
|
|
2832
|
+
do {
|
|
2833
|
+
if (!lm) {
|
|
2834
|
+
final = bits(dat, pos, 1);
|
|
2835
|
+
var type = bits(dat, pos + 1, 3);
|
|
2836
|
+
pos += 3;
|
|
2837
|
+
if (!type) {
|
|
2838
|
+
var s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l;
|
|
2839
|
+
if (t > sl) {
|
|
2840
|
+
if (noSt)
|
|
2841
|
+
err(0);
|
|
2842
|
+
break;
|
|
2843
|
+
}
|
|
2844
|
+
if (resize)
|
|
2845
|
+
cbuf(bt + l);
|
|
2846
|
+
buf.set(dat.subarray(s, t), bt);
|
|
2847
|
+
st.b = bt += l, st.p = pos = t * 8, st.f = final;
|
|
2848
|
+
continue;
|
|
2849
|
+
} else if (type == 1)
|
|
2850
|
+
lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
|
|
2851
|
+
else if (type == 2) {
|
|
2852
|
+
var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
|
|
2853
|
+
var tl = hLit + bits(dat, pos + 5, 31) + 1;
|
|
2854
|
+
pos += 14;
|
|
2855
|
+
var ldt = new u8(tl);
|
|
2856
|
+
var clt = new u8(19);
|
|
2857
|
+
for (var i = 0;i < hcLen; ++i) {
|
|
2858
|
+
clt[clim[i]] = bits(dat, pos + i * 3, 7);
|
|
2859
|
+
}
|
|
2860
|
+
pos += hcLen * 3;
|
|
2861
|
+
var clb = max(clt), clbmsk = (1 << clb) - 1;
|
|
2862
|
+
var clm = hMap(clt, clb, 1);
|
|
2863
|
+
for (var i = 0;i < tl; ) {
|
|
2864
|
+
var r = clm[bits(dat, pos, clbmsk)];
|
|
2865
|
+
pos += r & 15;
|
|
2866
|
+
var s = r >> 4;
|
|
2867
|
+
if (s < 16) {
|
|
2868
|
+
ldt[i++] = s;
|
|
2869
|
+
} else {
|
|
2870
|
+
var c = 0, n = 0;
|
|
2871
|
+
if (s == 16)
|
|
2872
|
+
n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];
|
|
2873
|
+
else if (s == 17)
|
|
2874
|
+
n = 3 + bits(dat, pos, 7), pos += 3;
|
|
2875
|
+
else if (s == 18)
|
|
2876
|
+
n = 11 + bits(dat, pos, 127), pos += 7;
|
|
2877
|
+
while (n--)
|
|
2878
|
+
ldt[i++] = c;
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
|
|
2882
|
+
lbt = max(lt);
|
|
2883
|
+
dbt = max(dt);
|
|
2884
|
+
lm = hMap(lt, lbt, 1);
|
|
2885
|
+
dm = hMap(dt, dbt, 1);
|
|
2886
|
+
} else
|
|
2887
|
+
err(1);
|
|
2888
|
+
if (pos > tbts) {
|
|
2889
|
+
if (noSt)
|
|
2890
|
+
err(0);
|
|
2891
|
+
break;
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
if (resize)
|
|
2895
|
+
cbuf(bt + 131072);
|
|
2896
|
+
var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
|
|
2897
|
+
var lpos = pos;
|
|
2898
|
+
for (;; lpos = pos) {
|
|
2899
|
+
var c = lm[bits16(dat, pos) & lms], sym = c >> 4;
|
|
2900
|
+
pos += c & 15;
|
|
2901
|
+
if (pos > tbts) {
|
|
2902
|
+
if (noSt)
|
|
2903
|
+
err(0);
|
|
2904
|
+
break;
|
|
2905
|
+
}
|
|
2906
|
+
if (!c)
|
|
2907
|
+
err(2);
|
|
2908
|
+
if (sym < 256)
|
|
2909
|
+
buf[bt++] = sym;
|
|
2910
|
+
else if (sym == 256) {
|
|
2911
|
+
lpos = pos, lm = null;
|
|
2912
|
+
break;
|
|
2913
|
+
} else {
|
|
2914
|
+
var add = sym - 254;
|
|
2915
|
+
if (sym > 264) {
|
|
2916
|
+
var i = sym - 257, b = fleb[i];
|
|
2917
|
+
add = bits(dat, pos, (1 << b) - 1) + fl[i];
|
|
2918
|
+
pos += b;
|
|
2919
|
+
}
|
|
2920
|
+
var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
|
|
2921
|
+
if (!d)
|
|
2922
|
+
err(3);
|
|
2923
|
+
pos += d & 15;
|
|
2924
|
+
var dt = fd[dsym];
|
|
2925
|
+
if (dsym > 3) {
|
|
2926
|
+
var b = fdeb[dsym];
|
|
2927
|
+
dt += bits16(dat, pos) & (1 << b) - 1, pos += b;
|
|
2928
|
+
}
|
|
2929
|
+
if (pos > tbts) {
|
|
2930
|
+
if (noSt)
|
|
2931
|
+
err(0);
|
|
2932
|
+
break;
|
|
2933
|
+
}
|
|
2934
|
+
if (resize)
|
|
2935
|
+
cbuf(bt + 131072);
|
|
2936
|
+
var end = bt + add;
|
|
2937
|
+
if (bt < dt) {
|
|
2938
|
+
var shift = dl - dt, dend = Math.min(dt, end);
|
|
2939
|
+
if (shift + bt < 0)
|
|
2940
|
+
err(3);
|
|
2941
|
+
for (;bt < dend; ++bt)
|
|
2942
|
+
buf[bt] = dict[shift + bt];
|
|
2943
|
+
}
|
|
2944
|
+
for (;bt < end; ++bt)
|
|
2945
|
+
buf[bt] = buf[bt - dt];
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
st.l = lm, st.p = lpos, st.b = bt, st.f = final;
|
|
2949
|
+
if (lm)
|
|
2950
|
+
final = 1, st.m = lbt, st.d = dm, st.n = dbt;
|
|
2951
|
+
} while (!final);
|
|
2952
|
+
return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
|
|
2953
|
+
};
|
|
2954
|
+
var et = /* @__PURE__ */ new u8(0);
|
|
2955
|
+
var b2 = function(d, b) {
|
|
2956
|
+
return d[b] | d[b + 1] << 8;
|
|
2957
|
+
};
|
|
2958
|
+
var b4 = function(d, b) {
|
|
2959
|
+
return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0;
|
|
2960
|
+
};
|
|
2961
|
+
var b8 = function(d, b) {
|
|
2962
|
+
return b4(d, b) + b4(d, b + 4) * 4294967296;
|
|
2963
|
+
};
|
|
2964
|
+
function inflateSync(data, opts) {
|
|
2965
|
+
return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);
|
|
2966
|
+
}
|
|
2967
|
+
var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder;
|
|
2968
|
+
var tds = 0;
|
|
2969
|
+
try {
|
|
2970
|
+
td.decode(et, { stream: true });
|
|
2971
|
+
tds = 1;
|
|
2972
|
+
} catch (e) {}
|
|
2973
|
+
var dutf8 = function(d) {
|
|
2974
|
+
for (var r = "", i = 0;; ) {
|
|
2975
|
+
var c = d[i++];
|
|
2976
|
+
var eb = (c > 127) + (c > 223) + (c > 239);
|
|
2977
|
+
if (i + eb > d.length)
|
|
2978
|
+
return { s: r, r: slc(d, i - 1) };
|
|
2979
|
+
if (!eb)
|
|
2980
|
+
r += String.fromCharCode(c);
|
|
2981
|
+
else if (eb == 3) {
|
|
2982
|
+
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);
|
|
2983
|
+
} else if (eb & 1)
|
|
2984
|
+
r += String.fromCharCode((c & 31) << 6 | d[i++] & 63);
|
|
2985
|
+
else
|
|
2986
|
+
r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | d[i++] & 63);
|
|
2987
|
+
}
|
|
2988
|
+
};
|
|
2989
|
+
function strFromU8(dat, latin1) {
|
|
2990
|
+
if (latin1) {
|
|
2991
|
+
var r = "";
|
|
2992
|
+
for (var i = 0;i < dat.length; i += 16384)
|
|
2993
|
+
r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));
|
|
2994
|
+
return r;
|
|
2995
|
+
} else if (td) {
|
|
2996
|
+
return td.decode(dat);
|
|
2997
|
+
} else {
|
|
2998
|
+
var _a = dutf8(dat), { s, r } = _a;
|
|
2999
|
+
if (r.length)
|
|
3000
|
+
err(8);
|
|
3001
|
+
return s;
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
var slzh = function(d, b) {
|
|
3005
|
+
return b + 30 + b2(d, b + 26) + b2(d, b + 28);
|
|
3006
|
+
};
|
|
3007
|
+
var zh = function(d, b, z) {
|
|
3008
|
+
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;
|
|
3009
|
+
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];
|
|
3010
|
+
return [b2(d, b + 10), sc, su, fn, es + efl + b2(d, b + 32), off];
|
|
3011
|
+
};
|
|
3012
|
+
var z64hs = function(d, b, l, z, sc, su, off) {
|
|
3013
|
+
var nsc = sc == 4294967295, nsu = su == 4294967295, noff = off == 4294967295, e = b + l;
|
|
3014
|
+
var nf = nsc + nsu + noff;
|
|
3015
|
+
if (z && nf) {
|
|
3016
|
+
for (;b + 4 < e; b += 4 + b2(d, b + 2)) {
|
|
3017
|
+
if (b2(d, b) == 1) {
|
|
3018
|
+
return [
|
|
3019
|
+
nsc ? b8(d, b + 4 + 8 * nsu) : sc,
|
|
3020
|
+
nsu ? b8(d, b + 4) : su,
|
|
3021
|
+
noff ? b8(d, b + 4 + 8 * (nsu + nsc)) : off,
|
|
3022
|
+
1
|
|
3023
|
+
];
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
if (z < 2)
|
|
3027
|
+
err(13);
|
|
3028
|
+
}
|
|
3029
|
+
return [sc, su, off, 0];
|
|
3030
|
+
};
|
|
3031
|
+
function unzipSync(data, opts) {
|
|
3032
|
+
var files = {};
|
|
3033
|
+
var e = data.length - 22;
|
|
3034
|
+
for (;b4(data, e) != 101010256; --e) {
|
|
3035
|
+
if (!e || data.length - e > 65558)
|
|
3036
|
+
err(13);
|
|
3037
|
+
}
|
|
3038
|
+
var c = b2(data, e + 8);
|
|
3039
|
+
if (!c)
|
|
3040
|
+
return {};
|
|
3041
|
+
var o = b4(data, e + 16);
|
|
3042
|
+
var z = b4(data, e - 20) == 117853008;
|
|
3043
|
+
if (z) {
|
|
3044
|
+
var ze = b4(data, e - 12);
|
|
3045
|
+
z = b4(data, ze) == 101075792;
|
|
3046
|
+
if (z) {
|
|
3047
|
+
c = b4(data, ze + 32);
|
|
3048
|
+
o = b4(data, ze + 48);
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
var fltr = opts && opts.filter;
|
|
3052
|
+
for (var i = 0;i < c; ++i) {
|
|
3053
|
+
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);
|
|
3054
|
+
o = no;
|
|
3055
|
+
if (!fltr || fltr({
|
|
3056
|
+
name: fn,
|
|
3057
|
+
size: sc,
|
|
3058
|
+
originalSize: su,
|
|
3059
|
+
compression: c_2
|
|
3060
|
+
})) {
|
|
3061
|
+
if (!c_2)
|
|
3062
|
+
files[fn] = slc(data, b, b + sc);
|
|
3063
|
+
else if (c_2 == 8)
|
|
3064
|
+
files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });
|
|
3065
|
+
else
|
|
3066
|
+
err(14, "unknown compression type " + c_2);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
return files;
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
// packages/gateway/src/skill-market.ts
|
|
3073
|
+
class SkillMarketError extends Error {
|
|
3074
|
+
code;
|
|
3075
|
+
constructor(code, message) {
|
|
3076
|
+
super(message);
|
|
3077
|
+
this.name = "SkillMarketError";
|
|
3078
|
+
this.code = code;
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
var CATALOG_TTL_MS = 60000;
|
|
3082
|
+
var ZIP_CACHE_MAX = 16;
|
|
3083
|
+
var MAX_PACKAGE_BYTES = 32 * 1024 * 1024;
|
|
3084
|
+
var userSkillsDir2 = (home) => join8(home, ".agents", "skills");
|
|
3085
|
+
var projectSkillsDir = (workspaceRoot) => join8(workspaceRoot, ".agents", "skills");
|
|
3086
|
+
var activeLevelOf = (state) => state.project ? "project" : state.user ? "user" : state.builtin ? "builtin" : null;
|
|
3087
|
+
var normalizeEntry = (raw) => {
|
|
3088
|
+
const row = raw ?? {};
|
|
3089
|
+
const metadata = row.metadata;
|
|
3090
|
+
const source = metadata !== null && typeof metadata === "object" ? metadata.source : undefined;
|
|
3091
|
+
return {
|
|
3092
|
+
name: String(row.name ?? ""),
|
|
3093
|
+
description: String(row.description ?? ""),
|
|
3094
|
+
category: typeof row.category === "string" && row.category !== "" ? row.category : "uncategorized",
|
|
3095
|
+
source: typeof source === "string" && source !== "" ? source : "unknown",
|
|
3096
|
+
license: String(row.license ?? ""),
|
|
3097
|
+
version: String(row.version ?? ""),
|
|
3098
|
+
sha256: String(row.sha256 ?? ""),
|
|
3099
|
+
sizeBytes: Number(row.size_bytes ?? 0),
|
|
3100
|
+
uploadedAt: String(row.uploaded_at ?? "")
|
|
3101
|
+
};
|
|
3102
|
+
};
|
|
3103
|
+
function createSkillMarketClient(options) {
|
|
3104
|
+
const home = options.home ?? homedir5();
|
|
3105
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
3106
|
+
const now = options.now ?? Date.now;
|
|
3107
|
+
let base;
|
|
3108
|
+
try {
|
|
3109
|
+
base = new URL(options.marketUrl);
|
|
3110
|
+
if (base.protocol !== "https:" && base.protocol !== "http:")
|
|
3111
|
+
throw new Error("unsupported protocol");
|
|
3112
|
+
} catch {
|
|
3113
|
+
throw new SkillMarketError("market_invalid_url", `Invalid skill market URL: ${options.marketUrl}`);
|
|
3114
|
+
}
|
|
3115
|
+
base = new URL(base.toString().replace(/\/+$/, ""));
|
|
3116
|
+
let catalog;
|
|
3117
|
+
const zipCache = new Map;
|
|
3118
|
+
const request = async (path) => {
|
|
3119
|
+
let response;
|
|
3120
|
+
try {
|
|
3121
|
+
response = await fetchImpl(new URL(path, `${base}/`).toString());
|
|
3122
|
+
} catch (error) {
|
|
3123
|
+
throw new SkillMarketError("market_unavailable", `Skill market unreachable: ${error instanceof Error ? error.message : String(error)}`);
|
|
3124
|
+
}
|
|
3125
|
+
return response;
|
|
3126
|
+
};
|
|
3127
|
+
const refreshCatalog = async () => {
|
|
3128
|
+
const response = await request("api/skills");
|
|
3129
|
+
if (!response.ok)
|
|
3130
|
+
throw new SkillMarketError("market_unavailable", `Skill list failed with HTTP ${response.status}`);
|
|
3131
|
+
const data = await response.json();
|
|
3132
|
+
const skills = Array.isArray(data.skills) ? data.skills.map(normalizeEntry) : [];
|
|
3133
|
+
catalog = { at: now(), skills };
|
|
3134
|
+
return skills;
|
|
3135
|
+
};
|
|
3136
|
+
const listSkills = async () => catalog !== undefined && now() - catalog.at < CATALOG_TTL_MS ? catalog.skills : refreshCatalog();
|
|
3137
|
+
const detail = async (name) => {
|
|
3138
|
+
let hit = (await listSkills()).find((skill) => skill.name === name);
|
|
3139
|
+
if (hit === undefined)
|
|
3140
|
+
hit = (await refreshCatalog()).find((skill) => skill.name === name);
|
|
3141
|
+
if (hit === undefined)
|
|
3142
|
+
throw new SkillMarketError("skill_not_found", `Skill not found on market: ${name}`);
|
|
3143
|
+
return hit;
|
|
3144
|
+
};
|
|
3145
|
+
const download = async (skill) => {
|
|
3146
|
+
const cached = zipCache.get(skill.sha256);
|
|
3147
|
+
if (cached !== undefined) {
|
|
3148
|
+
zipCache.delete(skill.sha256);
|
|
3149
|
+
zipCache.set(skill.sha256, cached);
|
|
3150
|
+
return cached;
|
|
3151
|
+
}
|
|
3152
|
+
if (skill.sizeBytes > MAX_PACKAGE_BYTES)
|
|
3153
|
+
throw new SkillMarketError("skill_too_large", `Package ${skill.name} is ${skill.sizeBytes} bytes (limit ${MAX_PACKAGE_BYTES})`);
|
|
3154
|
+
const response = await request(`api/skills/${encodeURIComponent(skill.name)}/download`);
|
|
3155
|
+
if (!response.ok)
|
|
3156
|
+
throw new SkillMarketError(response.status === 404 ? "skill_not_found" : "market_unavailable", `Download of ${skill.name} failed with HTTP ${response.status}`);
|
|
3157
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
3158
|
+
if (bytes.byteLength > MAX_PACKAGE_BYTES)
|
|
3159
|
+
throw new SkillMarketError("skill_too_large", `Package ${skill.name} exceeds ${MAX_PACKAGE_BYTES} bytes`);
|
|
3160
|
+
const digest = createHash2("sha256").update(bytes).digest("hex");
|
|
3161
|
+
if (digest !== skill.sha256)
|
|
3162
|
+
throw new SkillMarketError("skill_hash_mismatch", `SHA-256 mismatch for ${skill.name}: catalog says ${skill.sha256}, got ${digest}`);
|
|
3163
|
+
zipCache.set(skill.sha256, bytes);
|
|
3164
|
+
while (zipCache.size > ZIP_CACHE_MAX) {
|
|
3165
|
+
const oldest = zipCache.keys().next().value;
|
|
3166
|
+
if (oldest === undefined)
|
|
3167
|
+
break;
|
|
3168
|
+
zipCache.delete(oldest);
|
|
3169
|
+
}
|
|
3170
|
+
return bytes;
|
|
3171
|
+
};
|
|
3172
|
+
const unpack = (skill, zip) => {
|
|
3173
|
+
let entries;
|
|
3174
|
+
try {
|
|
3175
|
+
entries = unzipSync(zip);
|
|
3176
|
+
} catch {
|
|
3177
|
+
throw new SkillMarketError("skill_invalid_package", `Package ${skill.name} is not a valid zip archive`);
|
|
3178
|
+
}
|
|
3179
|
+
const files = new Map;
|
|
3180
|
+
const skillMds = [];
|
|
3181
|
+
for (const name of Object.keys(entries)) {
|
|
3182
|
+
if (name.endsWith("/"))
|
|
3183
|
+
continue;
|
|
3184
|
+
if (name.includes("\\") || name.startsWith("/"))
|
|
3185
|
+
throw new SkillMarketError("skill_invalid_package", `Unsafe path in package: ${name}`);
|
|
3186
|
+
const segments = name.split("/");
|
|
3187
|
+
if (segments.some((s) => s === ".." || s === ""))
|
|
3188
|
+
throw new SkillMarketError("skill_invalid_package", `Unsafe path in package: ${name}`);
|
|
3189
|
+
if (segments[segments.length - 1] === "SKILL.md")
|
|
3190
|
+
skillMds.push(name);
|
|
3191
|
+
files.set(name, entries[name]);
|
|
3192
|
+
}
|
|
3193
|
+
if (skillMds.length !== 1)
|
|
3194
|
+
throw new SkillMarketError("skill_invalid_package", skillMds.length === 0 ? `Package ${skill.name} contains no SKILL.md` : `Package ${skill.name} contains multiple SKILL.md files`);
|
|
3195
|
+
const root = skillMds[0].slice(0, -"SKILL.md".length);
|
|
3196
|
+
const inner = new Map;
|
|
3197
|
+
for (const [name, bytes] of files) {
|
|
3198
|
+
if (!name.startsWith(root))
|
|
3199
|
+
continue;
|
|
3200
|
+
inner.set(name.slice(root.length), bytes);
|
|
3201
|
+
}
|
|
3202
|
+
return inner;
|
|
3203
|
+
};
|
|
3204
|
+
const skillsDirFor = (scope, workspaceRoot) => {
|
|
3205
|
+
if (scope === "user")
|
|
3206
|
+
return userSkillsDir2(home);
|
|
3207
|
+
if (workspaceRoot === undefined || workspaceRoot === "")
|
|
3208
|
+
throw new SkillMarketError("market_unavailable", "Project-scope install needs a workspaceRoot");
|
|
3209
|
+
return projectSkillsDir(workspaceRoot);
|
|
3210
|
+
};
|
|
3211
|
+
const sweepTempDirs = async (skillsDir) => {
|
|
3212
|
+
const parent = dirname5(skillsDir);
|
|
3213
|
+
let names = [];
|
|
3214
|
+
try {
|
|
3215
|
+
names = await readdir2(parent);
|
|
3216
|
+
} catch {
|
|
3217
|
+
return;
|
|
3218
|
+
}
|
|
3219
|
+
await Promise.all(names.filter((n) => n.startsWith(".tmp-")).map((n) => rm2(join8(parent, n), { recursive: true, force: true }).catch(() => {})));
|
|
3220
|
+
};
|
|
3221
|
+
const install = async ({ name, scope, workspaceRoot, overwrite }) => {
|
|
3222
|
+
const skillsDir = skillsDirFor(scope, workspaceRoot);
|
|
3223
|
+
const target = join8(skillsDir, name);
|
|
3224
|
+
const exists = await stat2(target).catch(() => {
|
|
3225
|
+
return;
|
|
3226
|
+
}) !== undefined;
|
|
3227
|
+
if (exists && overwrite !== true)
|
|
3228
|
+
throw new SkillMarketError("skill_exists", `Skill ${name} already exists at ${scope} level (${target})`);
|
|
3229
|
+
const skill = await detail(name);
|
|
3230
|
+
const files = unpack(skill, await download(skill));
|
|
3231
|
+
await sweepTempDirs(skillsDir);
|
|
3232
|
+
const temp = join8(dirname5(skillsDir), `.tmp-${randomUUID()}`);
|
|
3233
|
+
try {
|
|
3234
|
+
for (const [relative, bytes] of files) {
|
|
3235
|
+
const dest = join8(temp, ...relative.split("/"));
|
|
3236
|
+
await mkdir2(dirname5(dest), { recursive: true });
|
|
3237
|
+
await writeFile2(dest, bytes);
|
|
3238
|
+
}
|
|
3239
|
+
if (exists)
|
|
3240
|
+
await rm2(target, { recursive: true, force: true });
|
|
3241
|
+
await mkdir2(skillsDir, { recursive: true });
|
|
3242
|
+
await rename2(temp, target);
|
|
3243
|
+
} finally {
|
|
3244
|
+
await rm2(temp, { recursive: true, force: true }).catch(() => {});
|
|
3245
|
+
}
|
|
3246
|
+
let shadowedBy;
|
|
3247
|
+
if (scope === "user" && workspaceRoot !== undefined && workspaceRoot !== "") {
|
|
3248
|
+
const projectDir = join8(projectSkillsDir(workspaceRoot), name);
|
|
3249
|
+
if (await stat2(projectDir).catch(() => {
|
|
3250
|
+
return;
|
|
3251
|
+
}) !== undefined)
|
|
3252
|
+
shadowedBy = "project";
|
|
3253
|
+
}
|
|
3254
|
+
return { name, scope, path: target, ...shadowedBy !== undefined ? { shadowedBy } : {} };
|
|
3255
|
+
};
|
|
3256
|
+
const uninstall = async ({ name, scope, workspaceRoot }) => {
|
|
3257
|
+
const skillsDir = skillsDirFor(scope, workspaceRoot);
|
|
3258
|
+
const target = join8(skillsDir, name);
|
|
3259
|
+
if (await stat2(target).catch(() => {
|
|
3260
|
+
return;
|
|
3261
|
+
}) === undefined)
|
|
3262
|
+
throw new SkillMarketError("skill_not_installed", `Skill ${name} is not installed at ${scope} level`);
|
|
3263
|
+
await rm2(target, { recursive: true, force: true });
|
|
3264
|
+
};
|
|
3265
|
+
return {
|
|
3266
|
+
async list(query) {
|
|
3267
|
+
if (query !== undefined && query.trim() !== "") {
|
|
3268
|
+
const response = await request(`api/skills?q=${encodeURIComponent(query.trim())}`);
|
|
3269
|
+
if (!response.ok)
|
|
3270
|
+
throw new SkillMarketError("market_unavailable", `Skill search failed with HTTP ${response.status}`);
|
|
3271
|
+
const data = await response.json();
|
|
3272
|
+
return Array.isArray(data.skills) ? data.skills.map(normalizeEntry) : [];
|
|
3273
|
+
}
|
|
3274
|
+
return listSkills();
|
|
3275
|
+
},
|
|
3276
|
+
detail,
|
|
3277
|
+
async preview(name) {
|
|
3278
|
+
const skill = await detail(name);
|
|
3279
|
+
const inner = unpack(skill, await download(skill));
|
|
3280
|
+
const bodyBytes = inner.get("SKILL.md");
|
|
3281
|
+
if (bodyBytes === undefined)
|
|
3282
|
+
throw new SkillMarketError("skill_invalid_package", `Package ${name} lost its SKILL.md`);
|
|
3283
|
+
const body = stripFrontmatter(new TextDecoder().decode(bodyBytes));
|
|
3284
|
+
return { body, files: [...inner.keys()].sort() };
|
|
3285
|
+
},
|
|
3286
|
+
install,
|
|
3287
|
+
uninstall
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
|
|
2609
3291
|
// packages/gateway/src/workspaces.ts
|
|
2610
3292
|
import { constants as constants3 } from "fs";
|
|
2611
|
-
import { access, readdir as
|
|
2612
|
-
import { dirname as
|
|
3293
|
+
import { access, readdir as readdir3, stat as stat3 } from "fs/promises";
|
|
3294
|
+
import { dirname as dirname6, isAbsolute as isAbsolute3, join as join9 } from "path";
|
|
2613
3295
|
async function browseWorkspaces(options) {
|
|
2614
3296
|
const requested = options.path.replace(/^\/+([A-Za-z]:[\\/])/, "$1");
|
|
2615
3297
|
if (process.platform === "win32" && (requested === "/" || requested === "\\")) {
|
|
@@ -2631,16 +3313,16 @@ async function browseWorkspaces(options) {
|
|
|
2631
3313
|
throw new Error("\u76EE\u5F55\u5206\u9875\u5DF2\u5931\u6548\uFF0C\u8BF7\u5237\u65B0\u5217\u8868");
|
|
2632
3314
|
}
|
|
2633
3315
|
}
|
|
2634
|
-
const entries = (await
|
|
3316
|
+
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
3317
|
const directories = [];
|
|
2636
3318
|
let next = offset;
|
|
2637
3319
|
for (;next < entries.length && directories.length < 100; next++) {
|
|
2638
3320
|
const entry = entries[next];
|
|
2639
|
-
const target =
|
|
3321
|
+
const target = join9(path, entry.name);
|
|
2640
3322
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
2641
3323
|
continue;
|
|
2642
3324
|
try {
|
|
2643
|
-
if (!(await
|
|
3325
|
+
if (!(await stat3(target)).isDirectory())
|
|
2644
3326
|
continue;
|
|
2645
3327
|
await access(target, constants3.R_OK | constants3.X_OK);
|
|
2646
3328
|
directories.push({ name: entry.name, path: target });
|
|
@@ -2650,7 +3332,7 @@ async function browseWorkspaces(options) {
|
|
|
2650
3332
|
}
|
|
2651
3333
|
return {
|
|
2652
3334
|
path,
|
|
2653
|
-
parent:
|
|
3335
|
+
parent: dirname6(path) === path ? null : dirname6(path),
|
|
2654
3336
|
entries: directories,
|
|
2655
3337
|
next: next < entries.length ? JSON.stringify({ key, offset: next }) : null
|
|
2656
3338
|
};
|
|
@@ -2670,6 +3352,26 @@ async function listDrives() {
|
|
|
2670
3352
|
// packages/gateway/src/server-services.ts
|
|
2671
3353
|
async function createNekoServerServices(options) {
|
|
2672
3354
|
const { runtime, context } = options;
|
|
3355
|
+
const market = createSkillMarketClient({
|
|
3356
|
+
marketUrl: options.marketUrl,
|
|
3357
|
+
...options.promptHome !== undefined ? { home: options.promptHome } : {}
|
|
3358
|
+
});
|
|
3359
|
+
const toRemote = (error) => {
|
|
3360
|
+
if (error instanceof SkillMarketError) {
|
|
3361
|
+
const code = error.code === "skill_not_found" || error.code === "skill_not_installed" ? "service_not_found" : "service_invalid_value";
|
|
3362
|
+
throw new RemoteServiceError(code, `[${error.code}] ${error.message}`);
|
|
3363
|
+
}
|
|
3364
|
+
throw error;
|
|
3365
|
+
};
|
|
3366
|
+
const withInstallState = async (skill, workspaceRoot) => {
|
|
3367
|
+
const locals = await runtime.listSkillsByLevel(workspaceRoot);
|
|
3368
|
+
const installed = {
|
|
3369
|
+
project: locals.some((s) => s.name === skill.name && s.level === "project"),
|
|
3370
|
+
user: locals.some((s) => s.name === skill.name && s.level === "user"),
|
|
3371
|
+
builtin: locals.some((s) => s.name === skill.name && s.level === "builtin")
|
|
3372
|
+
};
|
|
3373
|
+
return { ...skill, installed, activeLevel: activeLevelOf(installed) };
|
|
3374
|
+
};
|
|
2673
3375
|
const list = async () => (await runtime.listConversations()).map((info) => ({
|
|
2674
3376
|
id: String(info.id),
|
|
2675
3377
|
workspaceRoot: info.workspaceRoot,
|
|
@@ -2725,7 +3427,8 @@ async function createNekoServerServices(options) {
|
|
|
2725
3427
|
{ service: NekoModels, mode: "singleton" },
|
|
2726
3428
|
{ service: NekoCredentials, mode: "singleton" },
|
|
2727
3429
|
{ service: NekoWorkspaces, mode: "singleton" },
|
|
2728
|
-
{ service: NekoWorkspaceFiles, mode: "singleton" }
|
|
3430
|
+
{ service: NekoWorkspaceFiles, mode: "singleton" },
|
|
3431
|
+
{ service: NekoSkillMarket, mode: "singleton" }
|
|
2729
3432
|
]);
|
|
2730
3433
|
provider.provide(NekoSessionDirectory, { state: directory });
|
|
2731
3434
|
const updateCredentials = (operation) => serialize(async () => {
|
|
@@ -2775,6 +3478,13 @@ async function createNekoServerServices(options) {
|
|
|
2775
3478
|
}
|
|
2776
3479
|
});
|
|
2777
3480
|
provider.provide(NekoModels, { list: async () => runtime.listModels() });
|
|
3481
|
+
provider.provide(NekoSkillMarket, {
|
|
3482
|
+
list: (input) => market.list(input.query).then((skills) => Promise.all(skills.map((skill) => withInstallState(skill, input.workspaceRoot)))).catch(toRemote),
|
|
3483
|
+
detail: (input) => market.detail(input.name).then((skill) => withInstallState(skill, input.workspaceRoot)).catch(toRemote),
|
|
3484
|
+
preview: (input) => market.preview(input.name).catch(toRemote),
|
|
3485
|
+
install: (input) => serialize(() => market.install(input).then((result) => ({ ...result })).catch(toRemote)),
|
|
3486
|
+
uninstall: (input) => serialize(() => market.uninstall(input).catch(toRemote))
|
|
3487
|
+
});
|
|
2778
3488
|
provider.provide(NekoSessionManagement, {
|
|
2779
3489
|
create: (createOptions) => serialize(async () => {
|
|
2780
3490
|
const created = await runtime.createConversation(createOptions.model !== undefined || createOptions.thinkingLevel !== undefined || createOptions.cwd !== undefined ? {
|
|
@@ -3293,9 +4003,10 @@ async function startGateway(options = {}) {
|
|
|
3293
4003
|
const storagePath = options.storagePath ?? fileConfig.storagePath ?? DEFAULT_STORAGE_PATH;
|
|
3294
4004
|
const host = options.host ?? fileConfig.gateway?.host ?? DEFAULT_HOST;
|
|
3295
4005
|
const port = options.port !== undefined ? options.port : fileConfig.gateway?.port ?? (process.platform === "win32" ? 0 : DEFAULT_PORT);
|
|
3296
|
-
const
|
|
4006
|
+
const marketUrl = options.marketUrl ?? fileConfig.skills?.marketUrl ?? DEFAULT_SKILL_MARKET_URL;
|
|
4007
|
+
const nekoDir = dirname7(storagePath);
|
|
3297
4008
|
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 :
|
|
4009
|
+
const socketPath = options.socketPath !== undefined ? options.socketPath : fileConfig.gateway?.socketPath !== undefined ? fileConfig.gateway.socketPath : process.platform === "win32" ? null : join10(nekoDir, `${serverId}.sock`);
|
|
3299
4010
|
const runtime = await createNekoRuntime({
|
|
3300
4011
|
context,
|
|
3301
4012
|
storage: options.storage ?? "sqlite",
|
|
@@ -3318,6 +4029,8 @@ async function startGateway(options = {}) {
|
|
|
3318
4029
|
serverId,
|
|
3319
4030
|
nekoDir,
|
|
3320
4031
|
model,
|
|
4032
|
+
marketUrl,
|
|
4033
|
+
promptHome: options.promptHome,
|
|
3321
4034
|
onError: options.onError
|
|
3322
4035
|
});
|
|
3323
4036
|
} catch (error) {
|
|
@@ -3328,7 +4041,12 @@ async function startGateway(options = {}) {
|
|
|
3328
4041
|
async function bindGateway(runtime, options) {
|
|
3329
4042
|
const { context, host, port, socketPath, serverId, nekoDir, model } = options;
|
|
3330
4043
|
const modelLabel = `${model.provider}/${model.modelId}`;
|
|
3331
|
-
const serverServices = await createNekoServerServices({
|
|
4044
|
+
const serverServices = await createNekoServerServices({
|
|
4045
|
+
runtime,
|
|
4046
|
+
context,
|
|
4047
|
+
marketUrl: options.marketUrl,
|
|
4048
|
+
...options.promptHome !== undefined ? { promptHome: options.promptHome } : {}
|
|
4049
|
+
});
|
|
3332
4050
|
let wsListener;
|
|
3333
4051
|
const listeners = [];
|
|
3334
4052
|
if (socketPath !== null) {
|
|
@@ -3406,15 +4124,15 @@ function serverIdFromSocketPath(socketPath) {
|
|
|
3406
4124
|
return isServerId(candidate) ? candidate : undefined;
|
|
3407
4125
|
}
|
|
3408
4126
|
async function loadOrCreateServerId(nekoDir) {
|
|
3409
|
-
const file =
|
|
4127
|
+
const file = join10(nekoDir, SERVER_ID_FILENAME);
|
|
3410
4128
|
try {
|
|
3411
4129
|
const existing = (await readFile5(file, "utf8")).trim();
|
|
3412
4130
|
if (existing !== "")
|
|
3413
4131
|
return existing;
|
|
3414
4132
|
} catch {}
|
|
3415
|
-
const created =
|
|
3416
|
-
await
|
|
3417
|
-
await
|
|
4133
|
+
const created = randomUUID2();
|
|
4134
|
+
await mkdir3(nekoDir, { recursive: true, mode: 448 });
|
|
4135
|
+
await writeFile3(file, `${created}
|
|
3418
4136
|
`, { mode: 384 });
|
|
3419
4137
|
return created;
|
|
3420
4138
|
}
|