@senpiper/sp-agent 0.1.9 → 0.1.10

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.
Files changed (69) hide show
  1. package/INSTALL.md +1 -2
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +1 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/distribution.d.ts +2 -2
  6. package/dist/distribution.d.ts.map +1 -1
  7. package/dist/distribution.js +1 -2
  8. package/dist/distribution.js.map +1 -1
  9. package/dist/extension-identity.generated.d.ts +3 -3
  10. package/dist/extension-identity.generated.d.ts.map +1 -1
  11. package/dist/extension-identity.generated.js +3 -3
  12. package/dist/extension-identity.generated.js.map +1 -1
  13. package/dist/runtime-cli.d.ts.map +1 -1
  14. package/dist/runtime-cli.js +1 -1
  15. package/dist/runtime-cli.js.map +1 -1
  16. package/dist/version.d.ts +1 -1
  17. package/dist/version.d.ts.map +1 -1
  18. package/dist/version.js +1 -1
  19. package/dist/version.js.map +1 -1
  20. package/node_modules/@senpiper/pi-coding-agent/dist/bundle/chunks/{chunk-IK73Y232.js → chunk-LUSUF4ZH.js} +1 -1
  21. package/node_modules/@senpiper/pi-coding-agent/dist/bundle/cli.js +1 -1
  22. package/node_modules/@senpiper/pi-coding-agent/dist/bundle/index.js +1 -1
  23. package/node_modules/@senpiper/pi-coding-agent/dist/bundle/rpc-entry.js +1 -1
  24. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  25. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  26. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
  27. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  28. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  29. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  30. package/node_modules/@senpiper/sp-agent-extensions/TERMINAL_WEB_BRIDGE.md +1 -1
  31. package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.d.ts +0 -6
  32. package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.d.ts.map +1 -1
  33. package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.js +2 -81
  34. package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.js.map +1 -1
  35. package/node_modules/@senpiper/sp-agent-extensions/dist/commands.d.ts +2 -2
  36. package/node_modules/@senpiper/sp-agent-extensions/dist/commands.d.ts.map +1 -1
  37. package/node_modules/@senpiper/sp-agent-extensions/dist/commands.js +2 -2
  38. package/node_modules/@senpiper/sp-agent-extensions/dist/commands.js.map +1 -1
  39. package/node_modules/@senpiper/sp-agent-extensions/dist/gateway.d.ts.map +1 -1
  40. package/node_modules/@senpiper/sp-agent-extensions/dist/gateway.js +14 -3
  41. package/node_modules/@senpiper/sp-agent-extensions/dist/gateway.js.map +1 -1
  42. package/node_modules/@senpiper/sp-agent-extensions/dist/index.d.ts +0 -2
  43. package/node_modules/@senpiper/sp-agent-extensions/dist/index.d.ts.map +1 -1
  44. package/node_modules/@senpiper/sp-agent-extensions/dist/index.js +0 -2
  45. package/node_modules/@senpiper/sp-agent-extensions/dist/index.js.map +1 -1
  46. package/node_modules/@senpiper/sp-agent-extensions/dist/interactions.d.ts +0 -2
  47. package/node_modules/@senpiper/sp-agent-extensions/dist/interactions.d.ts.map +1 -1
  48. package/node_modules/@senpiper/sp-agent-extensions/dist/interactions.js +0 -3
  49. package/node_modules/@senpiper/sp-agent-extensions/dist/interactions.js.map +1 -1
  50. package/node_modules/@senpiper/sp-agent-extensions/dist/login-command.d.ts.map +1 -1
  51. package/node_modules/@senpiper/sp-agent-extensions/dist/login-command.js +1 -11
  52. package/node_modules/@senpiper/sp-agent-extensions/dist/login-command.js.map +1 -1
  53. package/node_modules/@senpiper/sp-agent-extensions/dist/startup-header.d.ts.map +1 -1
  54. package/node_modules/@senpiper/sp-agent-extensions/dist/startup-header.js +1 -1
  55. package/node_modules/@senpiper/sp-agent-extensions/dist/startup-header.js.map +1 -1
  56. package/node_modules/@senpiper/sp-agent-extensions/package.json +1 -4
  57. package/package.json +3 -3
  58. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-command.d.ts +0 -4
  59. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-command.d.ts.map +0 -1
  60. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-command.js +0 -67
  61. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-command.js.map +0 -1
  62. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-installation.d.ts +0 -14
  63. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-installation.d.ts.map +0 -1
  64. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-installation.js +0 -32
  65. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-installation.js.map +0 -1
  66. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding.d.ts +0 -39
  67. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding.d.ts.map +0 -1
  68. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding.js +0 -311
  69. package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding.js.map +0 -1
@@ -1,311 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- import { createHash } from "node:crypto";
3
- import { chmodSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
4
- import { tmpdir } from "node:os";
5
- import { basename, join } from "node:path";
6
- import { parse as parseYaml } from "yaml";
7
- import { SP_CLIENT_NAME, SP_USER_AGENT } from "./gateway.js";
8
- const MAX_ARCHIVE_BYTES = 100 * 1024 * 1024;
9
- const SAFE_CATALOG_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
10
- export function getOnboardingStatePath(agentDir) {
11
- return join(agentDir, "onboarding.json");
12
- }
13
- export function clearOnboardingState(agentDir) {
14
- rmSync(getOnboardingStatePath(agentDir), { force: true });
15
- }
16
- export function hasCompletedOnboarding(agentDir) {
17
- const path = getOnboardingStatePath(agentDir);
18
- if (!existsSync(path))
19
- return false;
20
- try {
21
- const value = JSON.parse(readFileSync(path, "utf8"));
22
- return (isRecord(value) &&
23
- value.schemaVersion === 1 &&
24
- typeof value.completedAt === "string" &&
25
- Array.isArray(value.roles) &&
26
- value.roles.every((role) => typeof role === "string" && SAFE_CATALOG_ID.test(role)) &&
27
- Array.isArray(value.skillIds) &&
28
- value.skillIds.every((skillId) => typeof skillId === "string" && SAFE_CATALOG_ID.test(skillId)));
29
- }
30
- catch {
31
- return false;
32
- }
33
- }
34
- function isRecord(value) {
35
- return typeof value === "object" && value !== null && !Array.isArray(value);
36
- }
37
- function requiredString(value, field) {
38
- if (typeof value !== "string" || !value.trim()) {
39
- throw new Error(`sp-gateway skill catalog has an invalid ${field}.`);
40
- }
41
- return value.trim();
42
- }
43
- function catalogId(value, field) {
44
- const id = requiredString(value, field);
45
- if (!SAFE_CATALOG_ID.test(id))
46
- throw new Error(`sp-gateway skill catalog has unsafe ${field} ${id}.`);
47
- return id;
48
- }
49
- function parseRole(value) {
50
- if (!isRecord(value))
51
- throw new Error("sp-gateway skill catalog has an invalid role.");
52
- return {
53
- id: catalogId(value.id, "role id"),
54
- title: requiredString(value.title, "role title"),
55
- };
56
- }
57
- function parseSource(value) {
58
- if (!isRecord(value))
59
- throw new Error("sp-gateway skill catalog has an invalid skill source.");
60
- const source = {
61
- archiveUrl: requiredString(value.archiveUrl, "archive URL"),
62
- commit: requiredString(value.commit, "source commit"),
63
- sha256: requiredString(value.sha256, "source checksum"),
64
- path: requiredString(value.path, "source path"),
65
- };
66
- const url = new URL(source.archiveUrl);
67
- if (url.protocol !== "https:" || url.hostname !== "codeload.github.com") {
68
- throw new Error("sp-gateway skill catalog source must use https://codeload.github.com/.");
69
- }
70
- if (!/^[0-9a-f]{40}$/.test(source.commit) || !/^[0-9a-f]{64}$/.test(source.sha256)) {
71
- throw new Error("sp-gateway skill catalog source has an invalid commit or checksum.");
72
- }
73
- const pathParts = source.path.split("/");
74
- if (source.path.startsWith("/") ||
75
- source.path.includes("\\") ||
76
- pathParts.some((part) => !part || part === "." || part === "..")) {
77
- throw new Error("sp-gateway skill catalog source has an unsafe path.");
78
- }
79
- return source;
80
- }
81
- function parseSkill(value) {
82
- if (!isRecord(value))
83
- throw new Error("sp-gateway skill catalog has an invalid skill.");
84
- if (typeof value.default !== "boolean" || !Array.isArray(value.roles)) {
85
- throw new Error("sp-gateway skill catalog has invalid skill associations.");
86
- }
87
- const id = catalogId(value.id, "skill id");
88
- if (value.dependencies !== undefined && !Array.isArray(value.dependencies)) {
89
- throw new Error("sp-gateway skill catalog has invalid skill dependencies.");
90
- }
91
- return {
92
- id,
93
- title: requiredString(value.title, "skill title"),
94
- description: requiredString(value.description, "skill description"),
95
- default: value.default,
96
- roles: value.roles.map((role) => catalogId(role, "skill role")),
97
- license: requiredString(value.license, "skill license"),
98
- dependencies: Array.isArray(value.dependencies)
99
- ? value.dependencies.map((dependency) => catalogId(dependency, "skill dependency"))
100
- : undefined,
101
- source: parseSource(value.source),
102
- };
103
- }
104
- export function parseSkillCatalog(value) {
105
- if (!isRecord(value) || value.schemaVersion !== 1 || !Array.isArray(value.roles) || !Array.isArray(value.skills)) {
106
- throw new Error("sp-gateway returned an unsupported skill catalog.");
107
- }
108
- const roles = value.roles.map(parseRole);
109
- const skills = value.skills.map(parseSkill);
110
- const roleIds = new Set(roles.map((role) => role.id));
111
- const skillIds = new Set(skills.map((skill) => skill.id));
112
- if (roleIds.size !== roles.length)
113
- throw new Error("sp-gateway skill catalog contains duplicate role ids.");
114
- if (skillIds.size !== skills.length)
115
- throw new Error("sp-gateway skill catalog contains duplicate skill ids.");
116
- for (const skill of skills) {
117
- if (new Set(skill.roles).size !== skill.roles.length) {
118
- throw new Error(`Skill ${skill.id} contains duplicate role ids.`);
119
- }
120
- if (new Set(skill.dependencies ?? []).size !== (skill.dependencies?.length ?? 0)) {
121
- throw new Error(`Skill ${skill.id} contains duplicate dependency ids.`);
122
- }
123
- for (const role of skill.roles) {
124
- if (!roleIds.has(role))
125
- throw new Error(`Skill ${skill.id} references unknown role ${role}.`);
126
- }
127
- for (const dependency of skill.dependencies ?? []) {
128
- if (!skillIds.has(dependency))
129
- throw new Error(`Skill ${skill.id} has unknown dependency ${dependency}.`);
130
- }
131
- }
132
- return { schemaVersion: 1, roles, skills };
133
- }
134
- function skillCatalogEndpoint(baseUrl) {
135
- const url = new URL(baseUrl);
136
- url.pathname = "/api/v1/skills";
137
- url.search = "";
138
- url.hash = "";
139
- return url.toString();
140
- }
141
- export function selectCatalogSkills(catalog, selectedRoles, includeDefaults, includeRoleSkills) {
142
- const knownRoles = new Set(catalog.roles.map((role) => role.id));
143
- for (const role of selectedRoles) {
144
- if (!knownRoles.has(role))
145
- throw new Error(`Unknown selected role ${role}.`);
146
- }
147
- const roles = new Set(selectedRoles);
148
- const selected = new Set(catalog.skills
149
- .filter((skill) => (includeDefaults && skill.default) || (includeRoleSkills && skill.roles.some((role) => roles.has(role))))
150
- .map((skill) => skill.id));
151
- const byId = new Map(catalog.skills.map((skill) => [skill.id, skill]));
152
- const pending = [...selected];
153
- while (pending.length > 0) {
154
- const skill = byId.get(pending.pop());
155
- for (const dependency of skill?.dependencies ?? []) {
156
- if (selected.has(dependency))
157
- continue;
158
- selected.add(dependency);
159
- pending.push(dependency);
160
- }
161
- }
162
- return catalog.skills.filter((skill) => selected.has(skill.id));
163
- }
164
- function assertSafeArchive(archivePath) {
165
- const result = spawnSync("tar", ["-tzf", archivePath], { encoding: "utf-8", maxBuffer: 20 * 1024 * 1024 });
166
- if (result.status !== 0) {
167
- throw new Error(`Invalid skill archive: ${result.stderr.trim() || "tar could not read it"}`);
168
- }
169
- for (const entry of result.stdout.split("\n").filter(Boolean)) {
170
- const parts = entry.split("/");
171
- if (entry.startsWith("/") || entry.includes("\\") || entry.includes("\0") || parts.includes("..")) {
172
- throw new Error("Skill archive contains an unsafe path.");
173
- }
174
- }
175
- }
176
- function assertNoSymlinks(path) {
177
- for (const entry of readdirSync(path, { withFileTypes: true })) {
178
- const entryPath = join(path, entry.name);
179
- if (lstatSync(entryPath).isSymbolicLink())
180
- throw new Error("Skill directory contains a symbolic link.");
181
- if (entry.isDirectory())
182
- assertNoSymlinks(entryPath);
183
- }
184
- }
185
- function readSkillName(skillPath) {
186
- const skillFile = join(skillPath, "SKILL.md");
187
- if (!existsSync(skillFile))
188
- throw new Error("Skill is missing SKILL.md.");
189
- const content = readFileSync(skillFile, "utf-8");
190
- const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(content);
191
- if (!match)
192
- throw new Error("Skill has invalid SKILL.md frontmatter.");
193
- const frontmatter = parseYaml(match[1]);
194
- if (!isRecord(frontmatter))
195
- throw new Error("Skill has invalid SKILL.md frontmatter.");
196
- return requiredString(frontmatter.name, "SKILL.md name");
197
- }
198
- async function downloadAndExtractArchive(source, workspace, cache) {
199
- const cacheKey = `${source.archiveUrl}:${source.sha256}`;
200
- const cached = cache.get(cacheKey);
201
- if (cached)
202
- return cached;
203
- const response = await fetch(source.archiveUrl, {
204
- headers: {
205
- Accept: "application/gzip, application/octet-stream",
206
- "User-Agent": SP_USER_AGENT,
207
- "X-Senpiper-Client": SP_CLIENT_NAME,
208
- },
209
- });
210
- if (!response.ok)
211
- throw new Error(`Skill archive download failed (${response.status} ${response.statusText}).`);
212
- if (response.url) {
213
- const responseUrl = new URL(response.url);
214
- if (responseUrl.protocol !== "https:" || responseUrl.hostname !== "codeload.github.com") {
215
- throw new Error("Skill archive redirected to an unsupported host.");
216
- }
217
- }
218
- const contentLength = Number(response.headers.get("content-length"));
219
- if (Number.isFinite(contentLength) && contentLength > MAX_ARCHIVE_BYTES) {
220
- throw new Error("Skill archive exceeds the 100 MB download limit.");
221
- }
222
- const archive = Buffer.from(await response.arrayBuffer());
223
- if (archive.byteLength > MAX_ARCHIVE_BYTES)
224
- throw new Error("Skill archive exceeds the 100 MB download limit.");
225
- const actualSha256 = createHash("sha256").update(archive).digest("hex");
226
- if (actualSha256 !== source.sha256)
227
- throw new Error("Skill archive checksum verification failed.");
228
- const archivePath = join(workspace, `${cache.size}.tar.gz`);
229
- const extractPath = join(workspace, `${cache.size}-extracted`);
230
- writeFileSync(archivePath, archive);
231
- mkdirSync(extractPath);
232
- assertSafeArchive(archivePath);
233
- const extraction = spawnSync("tar", ["-xzf", archivePath, "--no-same-owner", "--no-same-permissions", "-C", extractPath], {
234
- encoding: "utf-8",
235
- maxBuffer: 20 * 1024 * 1024,
236
- });
237
- if (extraction.status !== 0) {
238
- throw new Error(`Skill archive extraction failed: ${extraction.stderr.trim() || "unknown tar error"}`);
239
- }
240
- const roots = readdirSync(extractPath, { withFileTypes: true }).filter((entry) => entry.isDirectory());
241
- if (roots.length !== 1)
242
- throw new Error("Skill archive must contain one repository root.");
243
- const repositoryRoot = join(extractPath, roots[0].name);
244
- cache.set(cacheKey, repositoryRoot);
245
- return repositoryRoot;
246
- }
247
- export function writeOnboardingState(agentDir, roles, skillIds) {
248
- mkdirSync(agentDir, { recursive: true });
249
- const statePath = getOnboardingStatePath(agentDir);
250
- writeFileSync(statePath, `${JSON.stringify({ schemaVersion: 1, completedAt: new Date().toISOString(), roles, skillIds }, null, "\t")}\n`, { encoding: "utf-8", mode: 0o600 });
251
- if (process.platform !== "win32")
252
- chmodSync(statePath, 0o600);
253
- }
254
- export async function installCatalogSkills(skills, agentDir) {
255
- const workspace = mkdtempSync(join(tmpdir(), "sp-skill-install-"));
256
- const archiveCache = new Map();
257
- const skillsDir = join(agentDir, "skills");
258
- mkdirSync(skillsDir, { recursive: true });
259
- const results = [];
260
- try {
261
- for (const skill of skills) {
262
- try {
263
- const repositoryRoot = await downloadAndExtractArchive(skill.source, workspace, archiveCache);
264
- const sourcePath = join(repositoryRoot, ...skill.source.path.split("/"));
265
- if (!existsSync(sourcePath) || !lstatSync(sourcePath).isDirectory()) {
266
- throw new Error(`Archive does not contain ${skill.source.path}.`);
267
- }
268
- assertNoSymlinks(sourcePath);
269
- const declaredName = readSkillName(sourcePath);
270
- if (declaredName !== skill.id || basename(sourcePath) !== skill.id) {
271
- throw new Error(`Skill identity mismatch: expected ${skill.id}, found ${declaredName}.`);
272
- }
273
- const stagingPath = join(skillsDir, `.${skill.id}.installing`);
274
- const destinationPath = join(skillsDir, skill.id);
275
- rmSync(stagingPath, { recursive: true, force: true });
276
- cpSync(sourcePath, stagingPath, { recursive: true });
277
- rmSync(destinationPath, { recursive: true, force: true });
278
- renameSync(stagingPath, destinationPath);
279
- results.push({ skillId: skill.id, installed: true });
280
- }
281
- catch (error) {
282
- results.push({
283
- skillId: skill.id,
284
- installed: false,
285
- error: error instanceof Error ? error.message : String(error),
286
- });
287
- }
288
- }
289
- }
290
- finally {
291
- rmSync(workspace, { recursive: true, force: true });
292
- }
293
- return results;
294
- }
295
- export async function fetchSkillCatalog(baseUrl, apiKey) {
296
- const response = await fetch(skillCatalogEndpoint(baseUrl), {
297
- headers: {
298
- Authorization: `Bearer ${apiKey}`,
299
- Accept: "application/json",
300
- "User-Agent": SP_USER_AGENT,
301
- "X-Senpiper-Client": SP_CLIENT_NAME,
302
- },
303
- });
304
- if (!response.ok) {
305
- const body = await response.text().catch(() => "");
306
- const suffix = body.trim() ? `: ${body.trim().slice(0, 300)}` : "";
307
- throw new Error(`sp-gateway rejected /api/v1/skills (${response.status} ${response.statusText})${suffix}`);
308
- }
309
- return parseSkillCatalog((await response.json()));
310
- }
311
- //# sourceMappingURL=onboarding.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport {\n\tchmodSync,\n\tcpSync,\n\texistsSync,\n\tlstatSync,\n\tmkdirSync,\n\tmkdtempSync,\n\treaddirSync,\n\treadFileSync,\n\trenameSync,\n\trmSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { basename, join } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { SP_CLIENT_NAME, SP_USER_AGENT } from \"./gateway.ts\";\n\nexport interface SkillCatalogRole {\n\tid: string;\n\ttitle: string;\n}\n\nexport interface SkillCatalogSource {\n\tarchiveUrl: string;\n\tcommit: string;\n\tsha256: string;\n\tpath: string;\n}\n\nexport interface SkillCatalogEntry {\n\tid: string;\n\ttitle: string;\n\tdescription: string;\n\tdefault: boolean;\n\troles: string[];\n\tlicense: string;\n\tdependencies?: string[];\n\tsource: SkillCatalogSource;\n}\n\nexport interface SkillCatalog {\n\tschemaVersion: 1;\n\troles: SkillCatalogRole[];\n\tskills: SkillCatalogEntry[];\n}\n\nexport interface SkillInstallResult {\n\tskillId: string;\n\tinstalled: boolean;\n\terror?: string;\n}\n\nconst MAX_ARCHIVE_BYTES = 100 * 1024 * 1024;\nconst SAFE_CATALOG_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;\n\nexport function getOnboardingStatePath(agentDir: string) {\n\treturn join(agentDir, \"onboarding.json\");\n}\n\nexport function clearOnboardingState(agentDir: string) {\n\trmSync(getOnboardingStatePath(agentDir), { force: true });\n}\n\nexport function hasCompletedOnboarding(agentDir: string) {\n\tconst path = getOnboardingStatePath(agentDir);\n\tif (!existsSync(path)) return false;\n\ttry {\n\t\tconst value = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n\t\treturn (\n\t\t\tisRecord(value) &&\n\t\t\tvalue.schemaVersion === 1 &&\n\t\t\ttypeof value.completedAt === \"string\" &&\n\t\t\tArray.isArray(value.roles) &&\n\t\t\tvalue.roles.every((role) => typeof role === \"string\" && SAFE_CATALOG_ID.test(role)) &&\n\t\t\tArray.isArray(value.skillIds) &&\n\t\t\tvalue.skillIds.every((skillId) => typeof skillId === \"string\" && SAFE_CATALOG_ID.test(skillId))\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction requiredString(value: unknown, field: string) {\n\tif (typeof value !== \"string\" || !value.trim()) {\n\t\tthrow new Error(`sp-gateway skill catalog has an invalid ${field}.`);\n\t}\n\treturn value.trim();\n}\n\nfunction catalogId(value: unknown, field: string) {\n\tconst id = requiredString(value, field);\n\tif (!SAFE_CATALOG_ID.test(id)) throw new Error(`sp-gateway skill catalog has unsafe ${field} ${id}.`);\n\treturn id;\n}\n\nfunction parseRole(value: unknown) {\n\tif (!isRecord(value)) throw new Error(\"sp-gateway skill catalog has an invalid role.\");\n\treturn {\n\t\tid: catalogId(value.id, \"role id\"),\n\t\ttitle: requiredString(value.title, \"role title\"),\n\t};\n}\n\nfunction parseSource(value: unknown) {\n\tif (!isRecord(value)) throw new Error(\"sp-gateway skill catalog has an invalid skill source.\");\n\tconst source = {\n\t\tarchiveUrl: requiredString(value.archiveUrl, \"archive URL\"),\n\t\tcommit: requiredString(value.commit, \"source commit\"),\n\t\tsha256: requiredString(value.sha256, \"source checksum\"),\n\t\tpath: requiredString(value.path, \"source path\"),\n\t};\n\tconst url = new URL(source.archiveUrl);\n\tif (url.protocol !== \"https:\" || url.hostname !== \"codeload.github.com\") {\n\t\tthrow new Error(\"sp-gateway skill catalog source must use https://codeload.github.com/.\");\n\t}\n\tif (!/^[0-9a-f]{40}$/.test(source.commit) || !/^[0-9a-f]{64}$/.test(source.sha256)) {\n\t\tthrow new Error(\"sp-gateway skill catalog source has an invalid commit or checksum.\");\n\t}\n\tconst pathParts = source.path.split(\"/\");\n\tif (\n\t\tsource.path.startsWith(\"/\") ||\n\t\tsource.path.includes(\"\\\\\") ||\n\t\tpathParts.some((part) => !part || part === \".\" || part === \"..\")\n\t) {\n\t\tthrow new Error(\"sp-gateway skill catalog source has an unsafe path.\");\n\t}\n\treturn source;\n}\n\nfunction parseSkill(value: unknown) {\n\tif (!isRecord(value)) throw new Error(\"sp-gateway skill catalog has an invalid skill.\");\n\tif (typeof value.default !== \"boolean\" || !Array.isArray(value.roles)) {\n\t\tthrow new Error(\"sp-gateway skill catalog has invalid skill associations.\");\n\t}\n\tconst id = catalogId(value.id, \"skill id\");\n\tif (value.dependencies !== undefined && !Array.isArray(value.dependencies)) {\n\t\tthrow new Error(\"sp-gateway skill catalog has invalid skill dependencies.\");\n\t}\n\treturn {\n\t\tid,\n\t\ttitle: requiredString(value.title, \"skill title\"),\n\t\tdescription: requiredString(value.description, \"skill description\"),\n\t\tdefault: value.default,\n\t\troles: value.roles.map((role) => catalogId(role, \"skill role\")),\n\t\tlicense: requiredString(value.license, \"skill license\"),\n\t\tdependencies: Array.isArray(value.dependencies)\n\t\t\t? value.dependencies.map((dependency) => catalogId(dependency, \"skill dependency\"))\n\t\t\t: undefined,\n\t\tsource: parseSource(value.source),\n\t};\n}\n\nexport function parseSkillCatalog(value: unknown): SkillCatalog {\n\tif (!isRecord(value) || value.schemaVersion !== 1 || !Array.isArray(value.roles) || !Array.isArray(value.skills)) {\n\t\tthrow new Error(\"sp-gateway returned an unsupported skill catalog.\");\n\t}\n\tconst roles = value.roles.map(parseRole);\n\tconst skills = value.skills.map(parseSkill);\n\tconst roleIds = new Set(roles.map((role) => role.id));\n\tconst skillIds = new Set(skills.map((skill) => skill.id));\n\tif (roleIds.size !== roles.length) throw new Error(\"sp-gateway skill catalog contains duplicate role ids.\");\n\tif (skillIds.size !== skills.length) throw new Error(\"sp-gateway skill catalog contains duplicate skill ids.\");\n\tfor (const skill of skills) {\n\t\tif (new Set(skill.roles).size !== skill.roles.length) {\n\t\t\tthrow new Error(`Skill ${skill.id} contains duplicate role ids.`);\n\t\t}\n\t\tif (new Set(skill.dependencies ?? []).size !== (skill.dependencies?.length ?? 0)) {\n\t\t\tthrow new Error(`Skill ${skill.id} contains duplicate dependency ids.`);\n\t\t}\n\t\tfor (const role of skill.roles) {\n\t\t\tif (!roleIds.has(role)) throw new Error(`Skill ${skill.id} references unknown role ${role}.`);\n\t\t}\n\t\tfor (const dependency of skill.dependencies ?? []) {\n\t\t\tif (!skillIds.has(dependency)) throw new Error(`Skill ${skill.id} has unknown dependency ${dependency}.`);\n\t\t}\n\t}\n\treturn { schemaVersion: 1, roles, skills };\n}\n\nfunction skillCatalogEndpoint(baseUrl: string) {\n\tconst url = new URL(baseUrl);\n\turl.pathname = \"/api/v1/skills\";\n\turl.search = \"\";\n\turl.hash = \"\";\n\treturn url.toString();\n}\n\nexport function selectCatalogSkills(\n\tcatalog: SkillCatalog,\n\tselectedRoles: readonly string[],\n\tincludeDefaults: boolean,\n\tincludeRoleSkills: boolean,\n): SkillCatalogEntry[] {\n\tconst knownRoles = new Set(catalog.roles.map((role) => role.id));\n\tfor (const role of selectedRoles) {\n\t\tif (!knownRoles.has(role)) throw new Error(`Unknown selected role ${role}.`);\n\t}\n\tconst roles = new Set(selectedRoles);\n\tconst selected = new Set(\n\t\tcatalog.skills\n\t\t\t.filter(\n\t\t\t\t(skill) =>\n\t\t\t\t\t(includeDefaults && skill.default) || (includeRoleSkills && skill.roles.some((role) => roles.has(role))),\n\t\t\t)\n\t\t\t.map((skill) => skill.id),\n\t);\n\tconst byId = new Map(catalog.skills.map((skill) => [skill.id, skill]));\n\tconst pending = [...selected];\n\twhile (pending.length > 0) {\n\t\tconst skill = byId.get(pending.pop()!);\n\t\tfor (const dependency of skill?.dependencies ?? []) {\n\t\t\tif (selected.has(dependency)) continue;\n\t\t\tselected.add(dependency);\n\t\t\tpending.push(dependency);\n\t\t}\n\t}\n\treturn catalog.skills.filter((skill) => selected.has(skill.id));\n}\n\nfunction assertSafeArchive(archivePath: string) {\n\tconst result = spawnSync(\"tar\", [\"-tzf\", archivePath], { encoding: \"utf-8\", maxBuffer: 20 * 1024 * 1024 });\n\tif (result.status !== 0) {\n\t\tthrow new Error(`Invalid skill archive: ${result.stderr.trim() || \"tar could not read it\"}`);\n\t}\n\tfor (const entry of result.stdout.split(\"\\n\").filter(Boolean)) {\n\t\tconst parts = entry.split(\"/\");\n\t\tif (entry.startsWith(\"/\") || entry.includes(\"\\\\\") || entry.includes(\"\\0\") || parts.includes(\"..\")) {\n\t\t\tthrow new Error(\"Skill archive contains an unsafe path.\");\n\t\t}\n\t}\n}\n\nfunction assertNoSymlinks(path: string): void {\n\tfor (const entry of readdirSync(path, { withFileTypes: true })) {\n\t\tconst entryPath = join(path, entry.name);\n\t\tif (lstatSync(entryPath).isSymbolicLink()) throw new Error(\"Skill directory contains a symbolic link.\");\n\t\tif (entry.isDirectory()) assertNoSymlinks(entryPath);\n\t}\n}\n\nfunction readSkillName(skillPath: string) {\n\tconst skillFile = join(skillPath, \"SKILL.md\");\n\tif (!existsSync(skillFile)) throw new Error(\"Skill is missing SKILL.md.\");\n\tconst content = readFileSync(skillFile, \"utf-8\");\n\tconst match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---(?:\\r?\\n|$)/.exec(content);\n\tif (!match) throw new Error(\"Skill has invalid SKILL.md frontmatter.\");\n\tconst frontmatter = parseYaml(match[1]) as unknown;\n\tif (!isRecord(frontmatter)) throw new Error(\"Skill has invalid SKILL.md frontmatter.\");\n\treturn requiredString(frontmatter.name, \"SKILL.md name\");\n}\n\nasync function downloadAndExtractArchive(source: SkillCatalogSource, workspace: string, cache: Map<string, string>) {\n\tconst cacheKey = `${source.archiveUrl}:${source.sha256}`;\n\tconst cached = cache.get(cacheKey);\n\tif (cached) return cached;\n\n\tconst response = await fetch(source.archiveUrl, {\n\t\theaders: {\n\t\t\tAccept: \"application/gzip, application/octet-stream\",\n\t\t\t\"User-Agent\": SP_USER_AGENT,\n\t\t\t\"X-Senpiper-Client\": SP_CLIENT_NAME,\n\t\t},\n\t});\n\tif (!response.ok) throw new Error(`Skill archive download failed (${response.status} ${response.statusText}).`);\n\tif (response.url) {\n\t\tconst responseUrl = new URL(response.url);\n\t\tif (responseUrl.protocol !== \"https:\" || responseUrl.hostname !== \"codeload.github.com\") {\n\t\t\tthrow new Error(\"Skill archive redirected to an unsupported host.\");\n\t\t}\n\t}\n\tconst contentLength = Number(response.headers.get(\"content-length\"));\n\tif (Number.isFinite(contentLength) && contentLength > MAX_ARCHIVE_BYTES) {\n\t\tthrow new Error(\"Skill archive exceeds the 100 MB download limit.\");\n\t}\n\tconst archive = Buffer.from(await response.arrayBuffer());\n\tif (archive.byteLength > MAX_ARCHIVE_BYTES) throw new Error(\"Skill archive exceeds the 100 MB download limit.\");\n\tconst actualSha256 = createHash(\"sha256\").update(archive).digest(\"hex\");\n\tif (actualSha256 !== source.sha256) throw new Error(\"Skill archive checksum verification failed.\");\n\n\tconst archivePath = join(workspace, `${cache.size}.tar.gz`);\n\tconst extractPath = join(workspace, `${cache.size}-extracted`);\n\twriteFileSync(archivePath, archive);\n\tmkdirSync(extractPath);\n\tassertSafeArchive(archivePath);\n\tconst extraction = spawnSync(\n\t\t\"tar\",\n\t\t[\"-xzf\", archivePath, \"--no-same-owner\", \"--no-same-permissions\", \"-C\", extractPath],\n\t\t{\n\t\t\tencoding: \"utf-8\",\n\t\t\tmaxBuffer: 20 * 1024 * 1024,\n\t\t},\n\t);\n\tif (extraction.status !== 0) {\n\t\tthrow new Error(`Skill archive extraction failed: ${extraction.stderr.trim() || \"unknown tar error\"}`);\n\t}\n\tconst roots = readdirSync(extractPath, { withFileTypes: true }).filter((entry) => entry.isDirectory());\n\tif (roots.length !== 1) throw new Error(\"Skill archive must contain one repository root.\");\n\tconst repositoryRoot = join(extractPath, roots[0]!.name);\n\tcache.set(cacheKey, repositoryRoot);\n\treturn repositoryRoot;\n}\n\nexport function writeOnboardingState(agentDir: string, roles: readonly string[], skillIds: readonly string[]): void {\n\tmkdirSync(agentDir, { recursive: true });\n\tconst statePath = getOnboardingStatePath(agentDir);\n\twriteFileSync(\n\t\tstatePath,\n\t\t`${JSON.stringify({ schemaVersion: 1, completedAt: new Date().toISOString(), roles, skillIds }, null, \"\\t\")}\\n`,\n\t\t{ encoding: \"utf-8\", mode: 0o600 },\n\t);\n\tif (process.platform !== \"win32\") chmodSync(statePath, 0o600);\n}\n\nexport async function installCatalogSkills(\n\tskills: readonly SkillCatalogEntry[],\n\tagentDir: string,\n): Promise<SkillInstallResult[]> {\n\tconst workspace = mkdtempSync(join(tmpdir(), \"sp-skill-install-\"));\n\tconst archiveCache = new Map<string, string>();\n\tconst skillsDir = join(agentDir, \"skills\");\n\tmkdirSync(skillsDir, { recursive: true });\n\tconst results: SkillInstallResult[] = [];\n\ttry {\n\t\tfor (const skill of skills) {\n\t\t\ttry {\n\t\t\t\tconst repositoryRoot = await downloadAndExtractArchive(skill.source, workspace, archiveCache);\n\t\t\t\tconst sourcePath = join(repositoryRoot, ...skill.source.path.split(\"/\"));\n\t\t\t\tif (!existsSync(sourcePath) || !lstatSync(sourcePath).isDirectory()) {\n\t\t\t\t\tthrow new Error(`Archive does not contain ${skill.source.path}.`);\n\t\t\t\t}\n\t\t\t\tassertNoSymlinks(sourcePath);\n\t\t\t\tconst declaredName = readSkillName(sourcePath);\n\t\t\t\tif (declaredName !== skill.id || basename(sourcePath) !== skill.id) {\n\t\t\t\t\tthrow new Error(`Skill identity mismatch: expected ${skill.id}, found ${declaredName}.`);\n\t\t\t\t}\n\t\t\t\tconst stagingPath = join(skillsDir, `.${skill.id}.installing`);\n\t\t\t\tconst destinationPath = join(skillsDir, skill.id);\n\t\t\t\trmSync(stagingPath, { recursive: true, force: true });\n\t\t\t\tcpSync(sourcePath, stagingPath, { recursive: true });\n\t\t\t\trmSync(destinationPath, { recursive: true, force: true });\n\t\t\t\trenameSync(stagingPath, destinationPath);\n\t\t\t\tresults.push({ skillId: skill.id, installed: true });\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tskillId: skill.id,\n\t\t\t\t\tinstalled: false,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t} finally {\n\t\trmSync(workspace, { recursive: true, force: true });\n\t}\n\treturn results;\n}\n\nexport async function fetchSkillCatalog(baseUrl: string, apiKey: string): Promise<SkillCatalog> {\n\tconst response = await fetch(skillCatalogEndpoint(baseUrl), {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"User-Agent\": SP_USER_AGENT,\n\t\t\t\"X-Senpiper-Client\": SP_CLIENT_NAME,\n\t\t},\n\t});\n\tif (!response.ok) {\n\t\tconst body = await response.text().catch(() => \"\");\n\t\tconst suffix = body.trim() ? `: ${body.trim().slice(0, 300)}` : \"\";\n\t\tthrow new Error(`sp-gateway rejected /api/v1/skills (${response.status} ${response.statusText})${suffix}`);\n\t}\n\treturn parseSkillCatalog((await response.json()) as unknown);\n}\n"]}