@schlessera/brain-ui-server 0.27.0 → 0.28.0

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 (58) hide show
  1. package/README.md +1 -0
  2. package/dist/app.d.ts.map +1 -1
  3. package/dist/app.js +13 -1
  4. package/dist/app.js.map +1 -1
  5. package/dist/brain/client.d.ts +2 -0
  6. package/dist/brain/client.d.ts.map +1 -1
  7. package/dist/brain/client.js +7 -0
  8. package/dist/brain/client.js.map +1 -1
  9. package/dist/config/env.d.ts +2 -0
  10. package/dist/config/env.d.ts.map +1 -1
  11. package/dist/config/env.js +11 -0
  12. package/dist/config/env.js.map +1 -1
  13. package/dist/db/settings.d.ts +8 -0
  14. package/dist/db/settings.d.ts.map +1 -1
  15. package/dist/db/settings.js +14 -0
  16. package/dist/db/settings.js.map +1 -1
  17. package/dist/routes/skills.d.ts +28 -0
  18. package/dist/routes/skills.d.ts.map +1 -0
  19. package/dist/routes/skills.js +173 -0
  20. package/dist/routes/skills.js.map +1 -0
  21. package/dist/routes/tool-permissions.d.ts +17 -0
  22. package/dist/routes/tool-permissions.d.ts.map +1 -0
  23. package/dist/routes/tool-permissions.js +26 -0
  24. package/dist/routes/tool-permissions.js.map +1 -0
  25. package/dist/skills/install.d.ts +77 -0
  26. package/dist/skills/install.d.ts.map +1 -0
  27. package/dist/skills/install.js +273 -0
  28. package/dist/skills/install.js.map +1 -0
  29. package/dist/skills/manager.d.ts +58 -0
  30. package/dist/skills/manager.d.ts.map +1 -0
  31. package/dist/skills/manager.js +208 -0
  32. package/dist/skills/manager.js.map +1 -0
  33. package/dist/ws/bridge.d.ts.map +1 -1
  34. package/dist/ws/bridge.js +9 -0
  35. package/dist/ws/bridge.js.map +1 -1
  36. package/dist/ws/connection.d.ts.map +1 -1
  37. package/dist/ws/connection.js +1 -0
  38. package/dist/ws/connection.js.map +1 -1
  39. package/dist/ws/dispatch.d.ts.map +1 -1
  40. package/dist/ws/dispatch.js +5 -0
  41. package/dist/ws/dispatch.js.map +1 -1
  42. package/dist/ws/host.d.ts +14 -0
  43. package/dist/ws/host.d.ts.map +1 -1
  44. package/dist/ws/host.js +2 -0
  45. package/dist/ws/host.js.map +1 -1
  46. package/package.json +5 -3
  47. package/src/app.ts +18 -0
  48. package/src/brain/client.ts +12 -0
  49. package/src/config/env.ts +13 -0
  50. package/src/db/settings.ts +17 -0
  51. package/src/routes/skills.ts +203 -0
  52. package/src/routes/tool-permissions.ts +37 -0
  53. package/src/skills/install.ts +332 -0
  54. package/src/skills/manager.ts +258 -0
  55. package/src/ws/bridge.ts +11 -0
  56. package/src/ws/connection.ts +1 -0
  57. package/src/ws/dispatch.ts +5 -0
  58. package/src/ws/host.ts +16 -0
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Management surface for the user's remembered "always allow" tool grants —
3
+ * the Settings screen's view of what the approval cards' "Always allow"
4
+ * button has accumulated, and the way to take a grant back.
5
+ *
6
+ * The grants themselves are consulted by the ws bridge (an auto-allowed tool
7
+ * never raises a card); this surface only lists and removes. Mount BEHIND
8
+ * the /api auth guard: the list shapes what the agent can do unprompted.
9
+ */
10
+ import { Hono } from "hono";
11
+ import { getAutoAllowedTools, setAutoAllowedTools } from "../db/settings.js";
12
+ export function createToolPermissionRoutes(deps) {
13
+ const { db, log } = deps;
14
+ return new Hono()
15
+ .get("/tool-permissions", (c) => c.json({ tools: getAutoAllowedTools(db, log) }))
16
+ .delete("/tool-permissions/:tool", (c) => {
17
+ const tool = decodeURIComponent(c.req.param("tool"));
18
+ const current = getAutoAllowedTools(db, log);
19
+ if (!current.includes(tool)) {
20
+ return c.json({ error: "Not an auto-allowed tool." }, 404);
21
+ }
22
+ setAutoAllowedTools(db, current.filter((t) => t !== tool));
23
+ return c.json({ tools: getAutoAllowedTools(db, log) });
24
+ });
25
+ }
26
+ //# sourceMappingURL=tool-permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-permissions.js","sourceRoot":"","sources":["../../src/routes/tool-permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7E,MAAM,UAAU,0BAA0B,CAAC,IAG1C;IACC,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACzB,OAAO,IAAI,IAAI,EAAE;SACd,GAAG,CAAC,mBAAmB,EAAE,CAAC,CAAC,EAAE,EAAE,CAC9B,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAChD;SACA,MAAM,CAAC,yBAAyB,EAAE,CAAC,CAAC,EAAE,EAAE;QACvC,MAAM,IAAI,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,mBAAmB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,2BAA2B,EAAE,EAAE,GAAG,CAAC,CAAC;QAC7D,CAAC;QACD,mBAAmB,CACjB,EAAE,EACF,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAClC,CAAC;QACF,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Skill installation from archives — the "install" half of the Settings →
3
+ * Skills surface: a user-uploaded ZIP, or a GitHub repository (public, or
4
+ * private via the deployment's GITHUB_TOKEN) fetched as a zipball. Both run
5
+ * through ONE pipeline so they share the same guarantees:
6
+ *
7
+ * - A skill is any directory in the archive containing a SKILL.md (the
8
+ * archive root included). One archive may carry several skills.
9
+ * - The INSTALLED name comes from the SKILL.md frontmatter (validated like a
10
+ * created skill), not from whatever the archive called the folder.
11
+ * - Zip-slip is structurally impossible: entry paths are normalized and any
12
+ * `..`, absolute path, backslash, or NUL rejects the archive; files are
13
+ * written only under `.agents/skills/<name>/`, staged then swapped so a
14
+ * half-written skill is never discoverable.
15
+ * - Caps: archive ≤ 20 MB compressed, ≤ 50 MB inflated, ≤ 400 files per
16
+ * skill, per-file ≤ 10 MB.
17
+ * - Conflicts are SKIPPED by default and reported; `overwrite: true` may
18
+ * replace an existing CUSTOM skill (enabled or disabled) but can never
19
+ * touch a package skill — those are symlinks and stay refused.
20
+ */
21
+ export declare const MAX_ARCHIVE_BYTES: number;
22
+ export declare const MAX_INFLATED_BYTES: number;
23
+ export declare const MAX_FILES_PER_SKILL = 400;
24
+ export declare const MAX_FILE_BYTES: number;
25
+ export declare class InstallError extends Error {
26
+ }
27
+ export interface InstallOutcome {
28
+ /** Installed-as name (frontmatter), or the archive folder for failures. */
29
+ name: string;
30
+ status: "installed" | "replaced" | "skipped";
31
+ reason?: string;
32
+ files?: number;
33
+ }
34
+ export interface InstallOptions {
35
+ overwrite?: boolean;
36
+ /** Only consider entries under this archive subpath (GitHub `tree` URLs). */
37
+ subpath?: string;
38
+ }
39
+ export interface InstallDeps {
40
+ brainPath: string;
41
+ }
42
+ /**
43
+ * The shared core: install every skill found among already-unzipped entries.
44
+ * Throws InstallError for archive-level problems; per-skill problems land as
45
+ * skipped outcomes.
46
+ */
47
+ export declare function installSkillsFromEntries(deps: InstallDeps, entries: Record<string, Uint8Array>, opts?: InstallOptions): InstallOutcome[];
48
+ /** Install every skill found in an uploaded ZIP archive. */
49
+ export declare function installSkillsFromZip(deps: InstallDeps, archive: Uint8Array, opts?: InstallOptions): InstallOutcome[];
50
+ export interface GitHubSource {
51
+ owner: string;
52
+ repo: string;
53
+ ref?: string;
54
+ /** Repo subpath to search for skills (from a /tree/<ref>/<path> URL). */
55
+ subpath?: string;
56
+ }
57
+ /**
58
+ * Parse "owner/repo", a github.com URL, or a /tree/<ref>/<subpath> URL.
59
+ * The ref/subpath split is heuristic for URLs (a ref containing "/" cannot
60
+ * be told apart from a path); pass an explicit ref for those.
61
+ */
62
+ export declare function parseGitHubSource(input: string): GitHubSource | null;
63
+ /** Injectable for tests; production passes globalThis.fetch. */
64
+ export type Fetcher = (url: string, init?: RequestInit) => Promise<Response>;
65
+ /**
66
+ * Download a repo zipball (the API endpoint — works for private repos with a
67
+ * token, follows the codeload redirect) and install through the shared
68
+ * pipeline. GitHub zipballs prefix every entry with `owner-repo-sha/`; that
69
+ * wrapper folder is stripped before candidate discovery so subpaths from
70
+ * /tree/ URLs match repo-relative paths.
71
+ */
72
+ export declare function installSkillsFromGitHub(deps: InstallDeps, source: GitHubSource, opts?: {
73
+ overwrite?: boolean;
74
+ token?: string;
75
+ fetcher?: Fetcher;
76
+ }): Promise<InstallOutcome[]>;
77
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/skills/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,eAAO,MAAM,iBAAiB,QAAmB,CAAC;AAClD,eAAO,MAAM,kBAAkB,QAAmB,CAAC;AACnD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,cAAc,QAAmB,CAAC;AAI/C,qBAAa,YAAa,SAAQ,KAAK;CAAG;AAE1C,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAmBD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,EACnC,IAAI,GAAE,cAAmB,GACxB,cAAc,EAAE,CAyDlB;AA2ED,4DAA4D;AAC5D,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,UAAU,EACnB,IAAI,GAAE,cAAmB,GACxB,cAAc,EAAE,CAalB;AAMD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAqBpE;AAED,gEAAgE;AAChE,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE7E;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GACpE,OAAO,CAAC,cAAc,EAAE,CAAC,CA8C3B"}
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Skill installation from archives — the "install" half of the Settings →
3
+ * Skills surface: a user-uploaded ZIP, or a GitHub repository (public, or
4
+ * private via the deployment's GITHUB_TOKEN) fetched as a zipball. Both run
5
+ * through ONE pipeline so they share the same guarantees:
6
+ *
7
+ * - A skill is any directory in the archive containing a SKILL.md (the
8
+ * archive root included). One archive may carry several skills.
9
+ * - The INSTALLED name comes from the SKILL.md frontmatter (validated like a
10
+ * created skill), not from whatever the archive called the folder.
11
+ * - Zip-slip is structurally impossible: entry paths are normalized and any
12
+ * `..`, absolute path, backslash, or NUL rejects the archive; files are
13
+ * written only under `.agents/skills/<name>/`, staged then swapped so a
14
+ * half-written skill is never discoverable.
15
+ * - Caps: archive ≤ 20 MB compressed, ≤ 50 MB inflated, ≤ 400 files per
16
+ * skill, per-file ≤ 10 MB.
17
+ * - Conflicts are SKIPPED by default and reported; `overwrite: true` may
18
+ * replace an existing CUSTOM skill (enabled or disabled) but can never
19
+ * touch a package skill — those are symlinks and stay refused.
20
+ */
21
+ import { unzipSync } from "fflate";
22
+ import { existsSync, lstatSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs";
23
+ import { dirname, join } from "path";
24
+ import matter from "gray-matter";
25
+ export const MAX_ARCHIVE_BYTES = 20 * 1024 * 1024;
26
+ export const MAX_INFLATED_BYTES = 50 * 1024 * 1024;
27
+ export const MAX_FILES_PER_SKILL = 400;
28
+ export const MAX_FILE_BYTES = 10 * 1024 * 1024;
29
+ const NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
30
+ export class InstallError extends Error {
31
+ }
32
+ /** A safe, normalized archive path or null. Rejects slip vectors outright. */
33
+ function safeEntryPath(raw) {
34
+ if (raw.includes("\\") || raw.includes("\0") || raw.startsWith("/"))
35
+ return null;
36
+ const parts = raw.split("/").filter((p) => p !== "" && p !== ".");
37
+ if (parts.some((p) => p === ".." || p.length > 128))
38
+ return null;
39
+ return parts.join("/");
40
+ }
41
+ function existsAs(p) {
42
+ try {
43
+ const st = lstatSync(p);
44
+ return st.isSymbolicLink() ? "symlink" : st.isDirectory() ? "dir" : null;
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
50
+ /**
51
+ * The shared core: install every skill found among already-unzipped entries.
52
+ * Throws InstallError for archive-level problems; per-skill problems land as
53
+ * skipped outcomes.
54
+ */
55
+ export function installSkillsFromEntries(deps, entries, opts = {}) {
56
+ // Normalize + guard every entry, enforcing the inflated cap.
57
+ const files = new Map();
58
+ let inflated = 0;
59
+ for (const [raw, bytes] of Object.entries(entries)) {
60
+ if (raw.endsWith("/"))
61
+ continue; // directory marker
62
+ const path = safeEntryPath(raw);
63
+ if (path === null) {
64
+ throw new InstallError(`Archive contains an unsafe path: ${JSON.stringify(raw)}`);
65
+ }
66
+ if (bytes.length > MAX_FILE_BYTES) {
67
+ throw new InstallError(`${path} exceeds ${MAX_FILE_BYTES / 1024 / 1024}MB.`);
68
+ }
69
+ inflated += bytes.length;
70
+ if (inflated > MAX_INFLATED_BYTES) {
71
+ throw new InstallError("Archive inflates past the 50MB cap.");
72
+ }
73
+ files.set(path, bytes);
74
+ }
75
+ // Candidate skill roots: every directory (or the root) holding a SKILL.md,
76
+ // optionally filtered to a subpath.
77
+ const prefix = opts.subpath ? opts.subpath.replace(/^\/+|\/+$/g, "") + "/" : "";
78
+ const roots = [...files.keys()]
79
+ .filter((p) => p === "SKILL.md" || p.endsWith("/SKILL.md"))
80
+ .map((p) => (p === "SKILL.md" ? "" : p.slice(0, -"/SKILL.md".length)))
81
+ .filter((root) => (prefix ? (root + "/").startsWith(prefix) : true))
82
+ .sort();
83
+ if (roots.length === 0) {
84
+ throw new InstallError("No skill found: no directory containing a SKILL.md" +
85
+ (opts.subpath ? ` under "${opts.subpath}"` : "") +
86
+ ".");
87
+ }
88
+ const enabledDir = join(deps.brainPath, ".agents", "skills");
89
+ const disabledDir = join(deps.brainPath, ".agents", "skills-disabled");
90
+ mkdirSync(enabledDir, { recursive: true });
91
+ const outcomes = [];
92
+ for (const root of roots) {
93
+ const rootPrefix = root === "" ? "" : root + "/";
94
+ const label = root || "(archive root)";
95
+ // Each file belongs to its DEEPEST skill root, so nested skills never
96
+ // double-install into their parent.
97
+ const deeper = roots.filter((r) => r !== root && r.startsWith(rootPrefix));
98
+ const skillFiles = new Map();
99
+ for (const [path, bytes] of files) {
100
+ if (!path.startsWith(rootPrefix))
101
+ continue;
102
+ if (deeper.some((d) => path.startsWith(d + "/")))
103
+ continue;
104
+ skillFiles.set(path.slice(rootPrefix.length), bytes);
105
+ }
106
+ outcomes.push(installOne(label, skillFiles, enabledDir, disabledDir, opts));
107
+ }
108
+ return outcomes;
109
+ }
110
+ function installOne(label, skillFiles, enabledDir, disabledDir, opts) {
111
+ const skillMd = skillFiles.get("SKILL.md");
112
+ if (!skillMd)
113
+ return { name: label, status: "skipped", reason: "SKILL.md unreadable" };
114
+ let data;
115
+ try {
116
+ data = matter(Buffer.from(skillMd).toString("utf-8")).data;
117
+ }
118
+ catch (e) {
119
+ return {
120
+ name: label,
121
+ status: "skipped",
122
+ reason: `frontmatter does not parse: ${e instanceof Error ? e.message : String(e)}`,
123
+ };
124
+ }
125
+ const name = typeof data.name === "string" ? data.name : "";
126
+ if (!NAME_PATTERN.test(name)) {
127
+ return {
128
+ name: label,
129
+ status: "skipped",
130
+ reason: "frontmatter `name` missing or not lowercase kebab-case",
131
+ };
132
+ }
133
+ if (typeof data.description !== "string" || !data.description.trim()) {
134
+ return { name, status: "skipped", reason: "frontmatter has no `description`" };
135
+ }
136
+ if (skillFiles.size > MAX_FILES_PER_SKILL) {
137
+ return { name, status: "skipped", reason: `more than ${MAX_FILES_PER_SKILL} files` };
138
+ }
139
+ const target = join(enabledDir, name);
140
+ const disabledTarget = join(disabledDir, name);
141
+ if (existsAs(target) === "symlink") {
142
+ return {
143
+ name,
144
+ status: "skipped",
145
+ reason: "a built-in (package) skill has this name; rename yours in the frontmatter",
146
+ };
147
+ }
148
+ const existing = existsAs(target) === "dir" || existsAs(disabledTarget) === "dir";
149
+ if (existing && !opts.overwrite) {
150
+ return { name, status: "skipped", reason: "already installed (enable overwrite to replace)" };
151
+ }
152
+ // Stage into a temp sibling, then swap — a half-written skill dir must
153
+ // never be what an agent discovers.
154
+ const staging = join(enabledDir, `.install-${name}-${process.pid}`);
155
+ rmSync(staging, { recursive: true, force: true });
156
+ try {
157
+ for (const [rel, bytes] of skillFiles) {
158
+ const dest = join(staging, rel);
159
+ mkdirSync(dirname(dest), { recursive: true });
160
+ writeFileSync(dest, bytes);
161
+ }
162
+ rmSync(target, { recursive: true, force: true });
163
+ if (existsSync(disabledTarget))
164
+ rmSync(disabledTarget, { recursive: true, force: true });
165
+ renameSync(staging, target);
166
+ return { name, status: existing ? "replaced" : "installed", files: skillFiles.size };
167
+ }
168
+ catch (e) {
169
+ rmSync(staging, { recursive: true, force: true });
170
+ return {
171
+ name,
172
+ status: "skipped",
173
+ reason: `write failed: ${e instanceof Error ? e.message : String(e)}`,
174
+ };
175
+ }
176
+ }
177
+ /** Install every skill found in an uploaded ZIP archive. */
178
+ export function installSkillsFromZip(deps, archive, opts = {}) {
179
+ if (archive.length > MAX_ARCHIVE_BYTES) {
180
+ throw new InstallError(`Archive exceeds ${MAX_ARCHIVE_BYTES / 1024 / 1024}MB.`);
181
+ }
182
+ let entries;
183
+ try {
184
+ entries = unzipSync(archive);
185
+ }
186
+ catch (e) {
187
+ throw new InstallError(`Not a readable ZIP archive: ${e instanceof Error ? e.message : String(e)}`);
188
+ }
189
+ return installSkillsFromEntries(deps, entries, opts);
190
+ }
191
+ /**
192
+ * Parse "owner/repo", a github.com URL, or a /tree/<ref>/<subpath> URL.
193
+ * The ref/subpath split is heuristic for URLs (a ref containing "/" cannot
194
+ * be told apart from a path); pass an explicit ref for those.
195
+ */
196
+ export function parseGitHubSource(input) {
197
+ const trimmed = input.trim();
198
+ const bare = /^([\w.-]+)\/([\w.-]+)$/.exec(trimmed);
199
+ if (bare)
200
+ return { owner: bare[1], repo: bare[2].replace(/\.git$/, "") };
201
+ let url;
202
+ try {
203
+ url = new URL(trimmed);
204
+ }
205
+ catch {
206
+ return null;
207
+ }
208
+ if (url.hostname !== "github.com" && url.hostname !== "www.github.com")
209
+ return null;
210
+ const parts = url.pathname.split("/").filter(Boolean);
211
+ if (parts.length < 2)
212
+ return null;
213
+ const owner = parts[0];
214
+ const repo = parts[1].replace(/\.git$/, "");
215
+ if (parts.length >= 4 && (parts[2] === "tree" || parts[2] === "blob")) {
216
+ const ref = parts[3];
217
+ const subpath = parts.slice(4).join("/") || undefined;
218
+ return { owner, repo, ref, ...(subpath ? { subpath } : {}) };
219
+ }
220
+ return { owner, repo };
221
+ }
222
+ /**
223
+ * Download a repo zipball (the API endpoint — works for private repos with a
224
+ * token, follows the codeload redirect) and install through the shared
225
+ * pipeline. GitHub zipballs prefix every entry with `owner-repo-sha/`; that
226
+ * wrapper folder is stripped before candidate discovery so subpaths from
227
+ * /tree/ URLs match repo-relative paths.
228
+ */
229
+ export async function installSkillsFromGitHub(deps, source, opts = {}) {
230
+ const fetcher = opts.fetcher ?? fetch;
231
+ const ref = source.ref ? `/${encodeURIComponent(source.ref)}` : "";
232
+ const url = `https://api.github.com/repos/${encodeURIComponent(source.owner)}/${encodeURIComponent(source.repo)}/zipball${ref}`;
233
+ const res = await fetcher(url, {
234
+ headers: {
235
+ Accept: "application/vnd.github+json",
236
+ "User-Agent": "brain-kit-ui",
237
+ ...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),
238
+ },
239
+ redirect: "follow",
240
+ });
241
+ if (res.status === 404) {
242
+ throw new InstallError(`GitHub says 404 for ${source.owner}/${source.repo}` +
243
+ (opts.token
244
+ ? "."
245
+ : " — if the repository is private, set GITHUB_TOKEN on the server."));
246
+ }
247
+ if (!res.ok) {
248
+ throw new InstallError(`GitHub zipball fetch failed: HTTP ${res.status}`);
249
+ }
250
+ const buf = new Uint8Array(await res.arrayBuffer());
251
+ if (buf.length > MAX_ARCHIVE_BYTES) {
252
+ throw new InstallError(`Repository archive exceeds ${MAX_ARCHIVE_BYTES / 1024 / 1024}MB.`);
253
+ }
254
+ let entries;
255
+ try {
256
+ entries = unzipSync(buf);
257
+ }
258
+ catch (e) {
259
+ throw new InstallError(`GitHub returned an unreadable archive: ${e instanceof Error ? e.message : String(e)}`);
260
+ }
261
+ const stripped = {};
262
+ for (const [key, bytes] of Object.entries(entries)) {
263
+ const slash = key.indexOf("/");
264
+ if (slash === -1)
265
+ continue; // nothing lives outside the wrapper dir
266
+ stripped[key.slice(slash + 1)] = bytes;
267
+ }
268
+ return installSkillsFromEntries(deps, stripped, {
269
+ ...(opts.overwrite !== undefined ? { overwrite: opts.overwrite } : {}),
270
+ ...(source.subpath ? { subpath: source.subpath } : {}),
271
+ });
272
+ }
273
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/skills/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Custom-skill management over the brain repo's canonical skill home.
3
+ *
4
+ * The store IS the filesystem convention `brain skills sync` already defines:
5
+ *
6
+ * - `.agents/skills/<name>` as a REAL directory = a user's custom skill —
7
+ * the local layer, highest discovery precedence, never touched by sync,
8
+ * inside the brain repo so it persists across deployments and rides the
9
+ * repo's own git backup.
10
+ * - `.agents/skills/<name>` as a SYMLINK = a package skill (core or module),
11
+ * materialized by sync. Read-only here: managed by the packages.
12
+ * - `.agents/skills-disabled/<name>` = a custom skill parked out of
13
+ * discovery. Disable is a directory move, so it applies to EVERY backend
14
+ * at once — after a sync, the claude/pi/codex/gemini emitters prune their
15
+ * links and the AGENTS.md index drops the row.
16
+ *
17
+ * After every mutation the caller runs `brain skills sync` so all agent
18
+ * integration dirs and the AGENTS.md index block follow the canonical home.
19
+ *
20
+ * Security posture: names are strictly validated (no traversal), mutations
21
+ * refuse to operate through symlinks (a package skill can never be edited or
22
+ * deleted from here), and content is size-capped. The routes sit behind the
23
+ * /api auth guard — this surface configures what the agent does.
24
+ */
25
+ /** A SKILL.md past this is not a skill, it's a document dump. */
26
+ export declare const MAX_SKILL_CONTENT_BYTES: number;
27
+ export interface SkillEntry {
28
+ /** Directory name (= the skill's identity for enable/disable/remove). */
29
+ name: string;
30
+ /** Frontmatter description, or empty when unparseable. */
31
+ description: string;
32
+ /** Where the skill comes from; only "custom" entries are editable. */
33
+ source: "builtin" | "custom";
34
+ enabled: boolean;
35
+ /** Set when the SKILL.md failed to parse — shown, not hidden. */
36
+ warning?: string;
37
+ }
38
+ export interface SkillDetail extends SkillEntry {
39
+ content: string;
40
+ /** Files in the skill directory besides SKILL.md (repo-relative names). */
41
+ extraFiles: string[];
42
+ }
43
+ export declare class SkillValidationError extends Error {
44
+ }
45
+ export declare class SkillNotFoundError extends Error {
46
+ }
47
+ export declare class SkillConflictError extends Error {
48
+ }
49
+ export interface SkillManager {
50
+ list(): SkillEntry[];
51
+ get(name: string): SkillDetail;
52
+ create(name: string, content: string): SkillEntry;
53
+ update(name: string, content: string): SkillEntry;
54
+ setEnabled(name: string, enabled: boolean): SkillEntry;
55
+ remove(name: string): void;
56
+ }
57
+ export declare function createSkillManager(brainPath: string): SkillManager;
58
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/skills/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAkBH,iEAAiE;AACjE,eAAO,MAAM,uBAAuB,QAAa,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;AAClD,qBAAa,kBAAmB,SAAQ,KAAK;CAAG;AAChD,qBAAa,kBAAmB,SAAQ,KAAK;CAAG;AAEhD,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,UAAU,EAAE,CAAC;IACrB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC/B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAClD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAClD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC;IACvD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CAsLlE"}