@musher-dev/musher-sdk 0.1.1
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/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/index.cjs +2135 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1141 -0
- package/dist/index.d.ts +1141 -0
- package/dist/index.js +2056 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2056 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/frontmatter.ts
|
|
12
|
+
function parseFrontmatter(text) {
|
|
13
|
+
const match = FRONTMATTER_RE.exec(text);
|
|
14
|
+
if (!match) {
|
|
15
|
+
return { body: text };
|
|
16
|
+
}
|
|
17
|
+
const raw = match[1];
|
|
18
|
+
const body = match[2];
|
|
19
|
+
let name;
|
|
20
|
+
let description;
|
|
21
|
+
for (const line of raw.split("\n")) {
|
|
22
|
+
const fm = FIELD_RE.exec(line.trim());
|
|
23
|
+
if (!fm) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const value = stripQuotes(fm[2].trim());
|
|
27
|
+
if (fm[1] === "name") {
|
|
28
|
+
name = value;
|
|
29
|
+
} else if (fm[1] === "description") {
|
|
30
|
+
description = value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { name, description, body };
|
|
34
|
+
}
|
|
35
|
+
function stripQuotes(s) {
|
|
36
|
+
if (s.length >= 2) {
|
|
37
|
+
const first = s[0];
|
|
38
|
+
const last = s[s.length - 1];
|
|
39
|
+
if (first === '"' && last === '"' || first === "'" && last === "'") {
|
|
40
|
+
return s.slice(1, -1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return s;
|
|
44
|
+
}
|
|
45
|
+
function extractDescription(skill) {
|
|
46
|
+
const def = skill.definition();
|
|
47
|
+
if (!def) {
|
|
48
|
+
return `Skill: ${skill.name}`;
|
|
49
|
+
}
|
|
50
|
+
const text = def.text();
|
|
51
|
+
const fm = parseFrontmatter(text);
|
|
52
|
+
if (fm.description) {
|
|
53
|
+
return fm.description;
|
|
54
|
+
}
|
|
55
|
+
return text.slice(0, 200).replace(/\n/g, " ").trim() || `Skill: ${skill.name}`;
|
|
56
|
+
}
|
|
57
|
+
var FRONTMATTER_RE, FIELD_RE;
|
|
58
|
+
var init_frontmatter = __esm({
|
|
59
|
+
"src/frontmatter.ts"() {
|
|
60
|
+
"use strict";
|
|
61
|
+
FRONTMATTER_RE = /^---[ \t]*\n([\s\S]*?)---[ \t]*\n?([\s\S]*)$/;
|
|
62
|
+
FIELD_RE = /^(name|description)\s*:\s*(.+)$/;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// src/adapters/openai.ts
|
|
67
|
+
var openai_exports = {};
|
|
68
|
+
__export(openai_exports, {
|
|
69
|
+
exportOpenAIInlineSkill: () => exportOpenAIInlineSkill,
|
|
70
|
+
exportOpenAILocalSkill: () => exportOpenAILocalSkill
|
|
71
|
+
});
|
|
72
|
+
async function exportOpenAILocalSkill(skill, targetDir) {
|
|
73
|
+
const { mkdir: mkdir6, writeFile: writeFile6 } = await import("fs/promises");
|
|
74
|
+
const { join: join7, resolve: resolve5, dirname: dirname5 } = await import("path");
|
|
75
|
+
const skillDir = resolve5(targetDir, skill.name);
|
|
76
|
+
await mkdir6(skillDir, { recursive: true });
|
|
77
|
+
for (const fh of skill.files()) {
|
|
78
|
+
const relativePath = fh.logicalPath.replace(SKILL_PREFIX_RE, "");
|
|
79
|
+
const filePath = join7(skillDir, relativePath);
|
|
80
|
+
await mkdir6(dirname5(filePath), { recursive: true });
|
|
81
|
+
await writeFile6(filePath, fh.bytes());
|
|
82
|
+
}
|
|
83
|
+
const description = extractDescription(skill);
|
|
84
|
+
return { name: skill.name, description, path: skillDir };
|
|
85
|
+
}
|
|
86
|
+
function exportOpenAIInlineSkill(skill) {
|
|
87
|
+
const description = extractDescription(skill);
|
|
88
|
+
const zipBuffer = buildStoreZip(skill);
|
|
89
|
+
const data = zipBuffer.toString("base64");
|
|
90
|
+
return {
|
|
91
|
+
type: "inline",
|
|
92
|
+
name: skill.name,
|
|
93
|
+
description,
|
|
94
|
+
source: { type: "base64", mediaType: "application/zip", data }
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function buildStoreZip(skill) {
|
|
98
|
+
const files = skill.files().map((fh) => {
|
|
99
|
+
const relativePath = fh.logicalPath.replace(SKILL_PREFIX_RE, "");
|
|
100
|
+
const name = `${skill.name}/${relativePath}`;
|
|
101
|
+
return { name, data: Buffer.from(fh.bytes()) };
|
|
102
|
+
});
|
|
103
|
+
const parts = [];
|
|
104
|
+
const centralParts = [];
|
|
105
|
+
let offset = 0;
|
|
106
|
+
for (const file of files) {
|
|
107
|
+
const nameBuffer = Buffer.from(file.name, "utf-8");
|
|
108
|
+
const local = Buffer.alloc(30 + nameBuffer.length);
|
|
109
|
+
local.writeUInt32LE(67324752, 0);
|
|
110
|
+
local.writeUInt16LE(20, 4);
|
|
111
|
+
local.writeUInt16LE(0, 6);
|
|
112
|
+
local.writeUInt16LE(0, 8);
|
|
113
|
+
local.writeUInt16LE(0, 10);
|
|
114
|
+
local.writeUInt16LE(0, 12);
|
|
115
|
+
local.writeUInt32LE(crc32(file.data), 14);
|
|
116
|
+
local.writeUInt32LE(file.data.length, 18);
|
|
117
|
+
local.writeUInt32LE(file.data.length, 22);
|
|
118
|
+
local.writeUInt16LE(nameBuffer.length, 26);
|
|
119
|
+
local.writeUInt16LE(0, 28);
|
|
120
|
+
nameBuffer.copy(local, 30);
|
|
121
|
+
const central = Buffer.alloc(46 + nameBuffer.length);
|
|
122
|
+
central.writeUInt32LE(33639248, 0);
|
|
123
|
+
central.writeUInt16LE(20, 4);
|
|
124
|
+
central.writeUInt16LE(20, 6);
|
|
125
|
+
central.writeUInt16LE(0, 8);
|
|
126
|
+
central.writeUInt16LE(0, 10);
|
|
127
|
+
central.writeUInt16LE(0, 12);
|
|
128
|
+
central.writeUInt16LE(0, 14);
|
|
129
|
+
central.writeUInt32LE(crc32(file.data), 16);
|
|
130
|
+
central.writeUInt32LE(file.data.length, 20);
|
|
131
|
+
central.writeUInt32LE(file.data.length, 24);
|
|
132
|
+
central.writeUInt16LE(nameBuffer.length, 28);
|
|
133
|
+
central.writeUInt16LE(0, 30);
|
|
134
|
+
central.writeUInt16LE(0, 32);
|
|
135
|
+
central.writeUInt16LE(0, 34);
|
|
136
|
+
central.writeUInt16LE(0, 36);
|
|
137
|
+
central.writeUInt32LE(0, 38);
|
|
138
|
+
central.writeUInt32LE(offset, 42);
|
|
139
|
+
nameBuffer.copy(central, 46);
|
|
140
|
+
parts.push(local, file.data);
|
|
141
|
+
centralParts.push(central);
|
|
142
|
+
offset += local.length + file.data.length;
|
|
143
|
+
}
|
|
144
|
+
const centralDirOffset = offset;
|
|
145
|
+
let centralDirSize = 0;
|
|
146
|
+
for (const c of centralParts) {
|
|
147
|
+
centralDirSize += c.length;
|
|
148
|
+
}
|
|
149
|
+
const eocd = Buffer.alloc(22);
|
|
150
|
+
eocd.writeUInt32LE(101010256, 0);
|
|
151
|
+
eocd.writeUInt16LE(0, 4);
|
|
152
|
+
eocd.writeUInt16LE(0, 6);
|
|
153
|
+
eocd.writeUInt16LE(files.length, 8);
|
|
154
|
+
eocd.writeUInt16LE(files.length, 10);
|
|
155
|
+
eocd.writeUInt32LE(centralDirSize, 12);
|
|
156
|
+
eocd.writeUInt32LE(centralDirOffset, 16);
|
|
157
|
+
eocd.writeUInt16LE(0, 20);
|
|
158
|
+
return Buffer.concat([...parts, ...centralParts, eocd]);
|
|
159
|
+
}
|
|
160
|
+
function crc32(data) {
|
|
161
|
+
let crc = 4294967295;
|
|
162
|
+
for (let i = 0; i < data.length; i++) {
|
|
163
|
+
crc ^= data[i];
|
|
164
|
+
for (let j = 0; j < 8; j++) {
|
|
165
|
+
crc = crc & 1 ? crc >>> 1 ^ 3988292384 : crc >>> 1;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return (crc ^ 4294967295) >>> 0;
|
|
169
|
+
}
|
|
170
|
+
var SKILL_PREFIX_RE;
|
|
171
|
+
var init_openai = __esm({
|
|
172
|
+
"src/adapters/openai.ts"() {
|
|
173
|
+
"use strict";
|
|
174
|
+
init_frontmatter();
|
|
175
|
+
SKILL_PREFIX_RE = /^skills\/[^/]+\//;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// src/adapters/claude.ts
|
|
180
|
+
var claude_exports = {};
|
|
181
|
+
__export(claude_exports, {
|
|
182
|
+
exportClaudePlugin: () => exportClaudePlugin,
|
|
183
|
+
installClaudeSkills: () => installClaudeSkills
|
|
184
|
+
});
|
|
185
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
186
|
+
import { join, resolve } from "path";
|
|
187
|
+
async function exportClaudePlugin(source, opts) {
|
|
188
|
+
const bundle = "bundle" in source ? source.bundle : source;
|
|
189
|
+
const name = opts.name ?? bundle.ref.slug;
|
|
190
|
+
const pluginDir = resolve(opts.targetDir, name);
|
|
191
|
+
await mkdir(pluginDir, { recursive: true });
|
|
192
|
+
const files = source.files();
|
|
193
|
+
for (const fh of files) {
|
|
194
|
+
const filePath = join(pluginDir, fh.logicalPath);
|
|
195
|
+
await mkdir(join(filePath, ".."), { recursive: true });
|
|
196
|
+
await writeFile(filePath, fh.bytes());
|
|
197
|
+
}
|
|
198
|
+
const metaDir = join(pluginDir, ".claude-plugin");
|
|
199
|
+
await mkdir(metaDir, { recursive: true });
|
|
200
|
+
const manifest = {
|
|
201
|
+
name,
|
|
202
|
+
description: opts.description ?? "",
|
|
203
|
+
version: bundle.version,
|
|
204
|
+
files: files.map((f) => f.logicalPath)
|
|
205
|
+
};
|
|
206
|
+
await writeFile(join(metaDir, "plugin.json"), JSON.stringify(manifest, null, 2));
|
|
207
|
+
return pluginDir;
|
|
208
|
+
}
|
|
209
|
+
async function installClaudeSkills(bundle, dir, opts) {
|
|
210
|
+
const source = "bundle" in bundle ? bundle.bundle : bundle;
|
|
211
|
+
const skillsDir = resolve(dir, ".claude", "skills");
|
|
212
|
+
const prefix = opts?.prefix ?? "";
|
|
213
|
+
const written = [];
|
|
214
|
+
const skills = "skills" in bundle && typeof bundle.skills === "function" ? bundle.skills() : source.skills();
|
|
215
|
+
for (const skill of skills) {
|
|
216
|
+
const skillDir = join(skillsDir, prefix, skill.name);
|
|
217
|
+
await mkdir(skillDir, { recursive: true });
|
|
218
|
+
for (const fh of skill.files()) {
|
|
219
|
+
const relativePath = fh.logicalPath.replace(SKILL_PREFIX_RE2, "");
|
|
220
|
+
const filePath = join(skillDir, relativePath);
|
|
221
|
+
await mkdir(join(filePath, ".."), { recursive: true });
|
|
222
|
+
await writeFile(filePath, fh.bytes());
|
|
223
|
+
written.push(filePath);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return written;
|
|
227
|
+
}
|
|
228
|
+
var SKILL_PREFIX_RE2;
|
|
229
|
+
var init_claude = __esm({
|
|
230
|
+
"src/adapters/claude.ts"() {
|
|
231
|
+
"use strict";
|
|
232
|
+
SKILL_PREFIX_RE2 = /^skills\/[^/]+\//;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// src/adapters/vscode.ts
|
|
237
|
+
var vscode_exports = {};
|
|
238
|
+
__export(vscode_exports, {
|
|
239
|
+
installVSCodeSkills: () => installVSCodeSkills
|
|
240
|
+
});
|
|
241
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
242
|
+
import { dirname, join as join2, resolve as resolve2 } from "path";
|
|
243
|
+
async function installVSCodeSkills(source, dir, opts) {
|
|
244
|
+
const bundle = "bundle" in source ? source.bundle : source;
|
|
245
|
+
const subdir = opts?.subdir ?? ".agents/skills";
|
|
246
|
+
const skillsDir = resolve2(dir, subdir);
|
|
247
|
+
const written = [];
|
|
248
|
+
const skills = "skills" in source && typeof source.skills === "function" ? source.skills() : bundle.skills();
|
|
249
|
+
for (const skill of skills) {
|
|
250
|
+
const skillDir = join2(skillsDir, skill.name);
|
|
251
|
+
await mkdir2(skillDir, { recursive: true });
|
|
252
|
+
for (const fh of skill.files()) {
|
|
253
|
+
const relativePath = fh.logicalPath.replace(SKILL_PREFIX_RE3, "");
|
|
254
|
+
const filePath = join2(skillDir, relativePath);
|
|
255
|
+
await mkdir2(dirname(filePath), { recursive: true });
|
|
256
|
+
await writeFile2(filePath, fh.bytes());
|
|
257
|
+
written.push(filePath);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return written;
|
|
261
|
+
}
|
|
262
|
+
var SKILL_PREFIX_RE3;
|
|
263
|
+
var init_vscode = __esm({
|
|
264
|
+
"src/adapters/vscode.ts"() {
|
|
265
|
+
"use strict";
|
|
266
|
+
SKILL_PREFIX_RE3 = /^skills\/[^/]+\//;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// src/client.ts
|
|
271
|
+
import { createHash as createHash3 } from "crypto";
|
|
272
|
+
|
|
273
|
+
// src/bundle.ts
|
|
274
|
+
import { createHash } from "crypto";
|
|
275
|
+
import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
|
|
276
|
+
import { dirname as dirname3 } from "path";
|
|
277
|
+
|
|
278
|
+
// src/handles/agent-spec-handle.ts
|
|
279
|
+
var AgentSpecHandle = class {
|
|
280
|
+
name;
|
|
281
|
+
_file;
|
|
282
|
+
constructor(name, file) {
|
|
283
|
+
this.name = name;
|
|
284
|
+
this._file = file;
|
|
285
|
+
}
|
|
286
|
+
/** All files belonging to this agent spec (single file). */
|
|
287
|
+
files() {
|
|
288
|
+
return [this._file];
|
|
289
|
+
}
|
|
290
|
+
/** The agent spec content as text. */
|
|
291
|
+
content() {
|
|
292
|
+
return this._file.text();
|
|
293
|
+
}
|
|
294
|
+
/** The underlying file handle. */
|
|
295
|
+
file() {
|
|
296
|
+
return this._file;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// src/handles/file-handle.ts
|
|
301
|
+
var FileHandle = class {
|
|
302
|
+
logicalPath;
|
|
303
|
+
assetType;
|
|
304
|
+
sha256;
|
|
305
|
+
mediaType;
|
|
306
|
+
sizeBytes;
|
|
307
|
+
_content;
|
|
308
|
+
constructor(logicalPath, assetType, sha256, sizeBytes, content, mediaType) {
|
|
309
|
+
this.logicalPath = logicalPath;
|
|
310
|
+
this.assetType = assetType;
|
|
311
|
+
this.sha256 = sha256;
|
|
312
|
+
this.sizeBytes = sizeBytes;
|
|
313
|
+
this._content = content;
|
|
314
|
+
this.mediaType = mediaType;
|
|
315
|
+
}
|
|
316
|
+
/** Return content as a UTF-8 string. */
|
|
317
|
+
text() {
|
|
318
|
+
return this._content.toString("utf-8");
|
|
319
|
+
}
|
|
320
|
+
/** Return content as a Uint8Array. */
|
|
321
|
+
bytes() {
|
|
322
|
+
return new Uint8Array(this._content.buffer, this._content.byteOffset, this._content.byteLength);
|
|
323
|
+
}
|
|
324
|
+
/** Return content as a ReadableStream. */
|
|
325
|
+
stream() {
|
|
326
|
+
const bytes = this.bytes();
|
|
327
|
+
return new ReadableStream({
|
|
328
|
+
start(controller) {
|
|
329
|
+
controller.enqueue(bytes);
|
|
330
|
+
controller.close();
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// src/handles/prompt-handle.ts
|
|
337
|
+
var PromptHandle = class {
|
|
338
|
+
name;
|
|
339
|
+
_file;
|
|
340
|
+
constructor(name, file) {
|
|
341
|
+
this.name = name;
|
|
342
|
+
this._file = file;
|
|
343
|
+
}
|
|
344
|
+
/** All files belonging to this prompt (single file). */
|
|
345
|
+
files() {
|
|
346
|
+
return [this._file];
|
|
347
|
+
}
|
|
348
|
+
/** The prompt content as text. */
|
|
349
|
+
content() {
|
|
350
|
+
return this._file.text();
|
|
351
|
+
}
|
|
352
|
+
/** The underlying file handle. */
|
|
353
|
+
file() {
|
|
354
|
+
return this._file;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// src/handles/skill-handle.ts
|
|
359
|
+
init_frontmatter();
|
|
360
|
+
var SkillHandle = class {
|
|
361
|
+
name;
|
|
362
|
+
_files;
|
|
363
|
+
constructor(name, files) {
|
|
364
|
+
this.name = name;
|
|
365
|
+
this._files = files;
|
|
366
|
+
}
|
|
367
|
+
/** All files belonging to this skill. */
|
|
368
|
+
files() {
|
|
369
|
+
return [...this._files];
|
|
370
|
+
}
|
|
371
|
+
/** The SKILL.md definition file, if present. */
|
|
372
|
+
definition() {
|
|
373
|
+
return this._files.find(
|
|
374
|
+
(f) => f.logicalPath.toLowerCase().endsWith("/skill.md") || f.logicalPath.toLowerCase() === "skill.md"
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
/** Parsed frontmatter from SKILL.md, if present. */
|
|
378
|
+
metadata() {
|
|
379
|
+
const def = this.definition();
|
|
380
|
+
if (!def) {
|
|
381
|
+
return void 0;
|
|
382
|
+
}
|
|
383
|
+
return parseFrontmatter(def.text());
|
|
384
|
+
}
|
|
385
|
+
/** Export as an OpenAI local skill directory. */
|
|
386
|
+
async exportOpenAILocal(targetDir) {
|
|
387
|
+
const { exportOpenAILocalSkill: exportOpenAILocalSkill2 } = await Promise.resolve().then(() => (init_openai(), openai_exports));
|
|
388
|
+
return exportOpenAILocalSkill2(this, targetDir);
|
|
389
|
+
}
|
|
390
|
+
/** Export as an OpenAI inline base64 ZIP skill. */
|
|
391
|
+
async exportOpenAIInline() {
|
|
392
|
+
const { exportOpenAIInlineSkill: exportOpenAIInlineSkill2 } = await Promise.resolve().then(() => (init_openai(), openai_exports));
|
|
393
|
+
return exportOpenAIInlineSkill2(this);
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// src/handles/toolset-handle.ts
|
|
398
|
+
var ToolsetHandle = class {
|
|
399
|
+
name;
|
|
400
|
+
_file;
|
|
401
|
+
constructor(name, file) {
|
|
402
|
+
this.name = name;
|
|
403
|
+
this._file = file;
|
|
404
|
+
}
|
|
405
|
+
/** All files belonging to this toolset (single file). */
|
|
406
|
+
files() {
|
|
407
|
+
return [this._file];
|
|
408
|
+
}
|
|
409
|
+
/** The toolset content as text. */
|
|
410
|
+
content() {
|
|
411
|
+
return this._file.text();
|
|
412
|
+
}
|
|
413
|
+
/** The underlying file handle. */
|
|
414
|
+
file() {
|
|
415
|
+
return this._file;
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// src/errors.ts
|
|
420
|
+
var MushError = class extends Error {
|
|
421
|
+
constructor(message, options) {
|
|
422
|
+
super(message, options);
|
|
423
|
+
this.name = "MushError";
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
var ApiError = class extends MushError {
|
|
427
|
+
status;
|
|
428
|
+
problem;
|
|
429
|
+
constructor(problem, options) {
|
|
430
|
+
super(problem.detail, options);
|
|
431
|
+
this.name = "ApiError";
|
|
432
|
+
this.status = problem.status;
|
|
433
|
+
this.problem = problem;
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
var NotFoundError = class extends ApiError {
|
|
437
|
+
constructor(problem, options) {
|
|
438
|
+
super(problem, options);
|
|
439
|
+
this.name = "NotFoundError";
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
var AuthenticationError = class extends ApiError {
|
|
443
|
+
constructor(problem, options) {
|
|
444
|
+
super(problem, options);
|
|
445
|
+
this.name = "AuthenticationError";
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
var ForbiddenError = class extends ApiError {
|
|
449
|
+
constructor(problem, options) {
|
|
450
|
+
super(problem, options);
|
|
451
|
+
this.name = "ForbiddenError";
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
var ValidationError = class extends ApiError {
|
|
455
|
+
errors;
|
|
456
|
+
constructor(problem, options) {
|
|
457
|
+
super(problem, options);
|
|
458
|
+
this.name = "ValidationError";
|
|
459
|
+
this.errors = problem.errors;
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
var RateLimitError = class extends ApiError {
|
|
463
|
+
retryAfter;
|
|
464
|
+
constructor(problem, retryAfter, options) {
|
|
465
|
+
super(problem, options);
|
|
466
|
+
this.name = "RateLimitError";
|
|
467
|
+
this.retryAfter = retryAfter;
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
var NetworkError = class extends MushError {
|
|
471
|
+
constructor(message, options) {
|
|
472
|
+
super(message, options);
|
|
473
|
+
this.name = "NetworkError";
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
var TimeoutError = class extends NetworkError {
|
|
477
|
+
constructor(message, options) {
|
|
478
|
+
super(message, options);
|
|
479
|
+
this.name = "TimeoutError";
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
var CacheError = class extends MushError {
|
|
483
|
+
constructor(message, options) {
|
|
484
|
+
super(message, options);
|
|
485
|
+
this.name = "CacheError";
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
var IntegrityError = class extends CacheError {
|
|
489
|
+
constructor(expected, actual, options) {
|
|
490
|
+
super(`SHA256 mismatch: expected ${expected}, got ${actual}`, options);
|
|
491
|
+
this.expected = expected;
|
|
492
|
+
this.actual = actual;
|
|
493
|
+
this.name = "IntegrityError";
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
var SchemaError = class extends MushError {
|
|
497
|
+
constructor(message, options) {
|
|
498
|
+
super(message, options);
|
|
499
|
+
this.name = "SchemaError";
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
// src/ref.ts
|
|
504
|
+
var REF_PATTERN = /^([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+)(?::(.+)|@sha256:([a-fA-F0-9]+))?$/;
|
|
505
|
+
var BundleRef = class _BundleRef {
|
|
506
|
+
namespace;
|
|
507
|
+
slug;
|
|
508
|
+
version;
|
|
509
|
+
digest;
|
|
510
|
+
constructor(namespace, slug, version, digest) {
|
|
511
|
+
this.namespace = namespace;
|
|
512
|
+
this.slug = slug;
|
|
513
|
+
this.version = version;
|
|
514
|
+
this.digest = digest;
|
|
515
|
+
}
|
|
516
|
+
/** Parse a ref string into a BundleRef. */
|
|
517
|
+
static parse(ref) {
|
|
518
|
+
const match = ref.match(REF_PATTERN);
|
|
519
|
+
if (!match) {
|
|
520
|
+
throw new MushError(
|
|
521
|
+
`Invalid bundle ref "${ref}": expected "namespace/slug", "namespace/slug:version", or "namespace/slug@sha256:digest"`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
const [, namespace, slug, version, digest] = match;
|
|
525
|
+
return new _BundleRef(namespace, slug, version, digest);
|
|
526
|
+
}
|
|
527
|
+
/** Return the base ref without version or digest: "namespace/slug". */
|
|
528
|
+
toBaseRef() {
|
|
529
|
+
return `${this.namespace}/${this.slug}`;
|
|
530
|
+
}
|
|
531
|
+
/** Return the full ref string. */
|
|
532
|
+
toString() {
|
|
533
|
+
if (this.digest) {
|
|
534
|
+
return `${this.namespace}/${this.slug}@sha256:${this.digest}`;
|
|
535
|
+
}
|
|
536
|
+
if (this.version) {
|
|
537
|
+
return `${this.namespace}/${this.slug}:${this.version}`;
|
|
538
|
+
}
|
|
539
|
+
return `${this.namespace}/${this.slug}`;
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
// src/selection.ts
|
|
544
|
+
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
545
|
+
import { dirname as dirname2, join as join3, resolve as resolve3 } from "path";
|
|
546
|
+
var Selection = class {
|
|
547
|
+
_bundle;
|
|
548
|
+
_filter;
|
|
549
|
+
constructor(bundle, filter) {
|
|
550
|
+
this._bundle = bundle;
|
|
551
|
+
this._filter = filter;
|
|
552
|
+
}
|
|
553
|
+
/** The underlying bundle. */
|
|
554
|
+
get bundle() {
|
|
555
|
+
return this._bundle;
|
|
556
|
+
}
|
|
557
|
+
files() {
|
|
558
|
+
const result = [];
|
|
559
|
+
const seen = /* @__PURE__ */ new Set();
|
|
560
|
+
const addFile = (fh) => {
|
|
561
|
+
if (!seen.has(fh.logicalPath)) {
|
|
562
|
+
seen.add(fh.logicalPath);
|
|
563
|
+
result.push(fh);
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
if (this._filter.skills) {
|
|
567
|
+
for (const name of this._filter.skills) {
|
|
568
|
+
const skill = this._bundle.skills().find((s) => s.name === name);
|
|
569
|
+
if (skill) {
|
|
570
|
+
for (const f of skill.files()) {
|
|
571
|
+
addFile(f);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (this._filter.prompts) {
|
|
577
|
+
for (const name of this._filter.prompts) {
|
|
578
|
+
const prompt = this._bundle.prompts().find((p) => p.name === name);
|
|
579
|
+
if (prompt) {
|
|
580
|
+
for (const f of prompt.files()) {
|
|
581
|
+
addFile(f);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (this._filter.toolsets) {
|
|
587
|
+
for (const name of this._filter.toolsets) {
|
|
588
|
+
const toolset = this._bundle.toolsets().find((t) => t.name === name);
|
|
589
|
+
if (toolset) {
|
|
590
|
+
for (const f of toolset.files()) {
|
|
591
|
+
addFile(f);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (this._filter.agentSpecs) {
|
|
597
|
+
for (const name of this._filter.agentSpecs) {
|
|
598
|
+
const spec = this._bundle.agentSpecs().find((a) => a.name === name);
|
|
599
|
+
if (spec) {
|
|
600
|
+
for (const f of spec.files()) {
|
|
601
|
+
addFile(f);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (this._filter.paths) {
|
|
607
|
+
for (const path of this._filter.paths) {
|
|
608
|
+
const fh = this._bundle.file(path);
|
|
609
|
+
if (fh) {
|
|
610
|
+
addFile(fh);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return result;
|
|
615
|
+
}
|
|
616
|
+
skills() {
|
|
617
|
+
const names = this._filter.skills;
|
|
618
|
+
if (!names) {
|
|
619
|
+
return [];
|
|
620
|
+
}
|
|
621
|
+
return this._bundle.skills().filter((s) => names.includes(s.name));
|
|
622
|
+
}
|
|
623
|
+
prompts() {
|
|
624
|
+
const names = this._filter.prompts;
|
|
625
|
+
if (!names) {
|
|
626
|
+
return [];
|
|
627
|
+
}
|
|
628
|
+
return this._bundle.prompts().filter((p) => names.includes(p.name));
|
|
629
|
+
}
|
|
630
|
+
toolsets() {
|
|
631
|
+
const names = this._filter.toolsets;
|
|
632
|
+
if (!names) {
|
|
633
|
+
return [];
|
|
634
|
+
}
|
|
635
|
+
return this._bundle.toolsets().filter((t) => names.includes(t.name));
|
|
636
|
+
}
|
|
637
|
+
agentSpecs() {
|
|
638
|
+
const names = this._filter.agentSpecs;
|
|
639
|
+
if (!names) {
|
|
640
|
+
return [];
|
|
641
|
+
}
|
|
642
|
+
return this._bundle.agentSpecs().filter((a) => names.includes(a.name));
|
|
643
|
+
}
|
|
644
|
+
/** Write selected files to targetDir preserving logical paths. */
|
|
645
|
+
async materialize(targetDir) {
|
|
646
|
+
const written = [];
|
|
647
|
+
for (const fh of this.files()) {
|
|
648
|
+
const absPath = resolve3(join3(targetDir, fh.logicalPath));
|
|
649
|
+
await mkdir3(dirname2(absPath), { recursive: true });
|
|
650
|
+
await writeFile3(absPath, fh.bytes());
|
|
651
|
+
written.push(absPath);
|
|
652
|
+
}
|
|
653
|
+
return written;
|
|
654
|
+
}
|
|
655
|
+
// -- Export adapter convenience methods --
|
|
656
|
+
async exportClaudePlugin(opts) {
|
|
657
|
+
const { exportClaudePlugin: exportClaudePlugin2 } = await Promise.resolve().then(() => (init_claude(), claude_exports));
|
|
658
|
+
return exportClaudePlugin2(this, opts);
|
|
659
|
+
}
|
|
660
|
+
async installClaudeSkills(dir, opts) {
|
|
661
|
+
const { installClaudeSkills: installClaudeSkills2 } = await Promise.resolve().then(() => (init_claude(), claude_exports));
|
|
662
|
+
return installClaudeSkills2(this, dir, opts);
|
|
663
|
+
}
|
|
664
|
+
async installVSCodeSkills(dir, opts) {
|
|
665
|
+
const { installVSCodeSkills: installVSCodeSkills2 } = await Promise.resolve().then(() => (init_vscode(), vscode_exports));
|
|
666
|
+
return installVSCodeSkills2(this, dir, opts);
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
// src/bundle.ts
|
|
671
|
+
var Bundle = class {
|
|
672
|
+
ref;
|
|
673
|
+
version;
|
|
674
|
+
metadata;
|
|
675
|
+
_files;
|
|
676
|
+
_skills;
|
|
677
|
+
_prompts;
|
|
678
|
+
_toolsets;
|
|
679
|
+
_agentSpecs;
|
|
680
|
+
constructor(metadata, contents) {
|
|
681
|
+
this.metadata = metadata;
|
|
682
|
+
this.version = metadata.version;
|
|
683
|
+
this.ref = BundleRef.parse(metadata.ref);
|
|
684
|
+
this._files = /* @__PURE__ */ new Map();
|
|
685
|
+
if (metadata.manifest?.layers) {
|
|
686
|
+
for (const layer of metadata.manifest.layers) {
|
|
687
|
+
const buf = contents.get(layer.logicalPath);
|
|
688
|
+
if (buf) {
|
|
689
|
+
const fh = new FileHandle(
|
|
690
|
+
layer.logicalPath,
|
|
691
|
+
layer.assetType,
|
|
692
|
+
layer.contentSha256,
|
|
693
|
+
layer.sizeBytes,
|
|
694
|
+
buf,
|
|
695
|
+
layer.mediaType ?? void 0
|
|
696
|
+
);
|
|
697
|
+
this._files.set(layer.logicalPath, fh);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
this._skills = /* @__PURE__ */ new Map();
|
|
702
|
+
this._prompts = /* @__PURE__ */ new Map();
|
|
703
|
+
this._toolsets = /* @__PURE__ */ new Map();
|
|
704
|
+
this._agentSpecs = /* @__PURE__ */ new Map();
|
|
705
|
+
const skillGroups = /* @__PURE__ */ new Map();
|
|
706
|
+
for (const fh of this._files.values()) {
|
|
707
|
+
switch (fh.assetType) {
|
|
708
|
+
case "skill": {
|
|
709
|
+
const skillName = extractSkillName(fh.logicalPath);
|
|
710
|
+
if (skillName) {
|
|
711
|
+
let group = skillGroups.get(skillName);
|
|
712
|
+
if (!group) {
|
|
713
|
+
group = [];
|
|
714
|
+
skillGroups.set(skillName, group);
|
|
715
|
+
}
|
|
716
|
+
group.push(fh);
|
|
717
|
+
}
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
case "prompt": {
|
|
721
|
+
const name = baseFileName(fh.logicalPath);
|
|
722
|
+
this._prompts.set(name, new PromptHandle(name, fh));
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
case "tool_config": {
|
|
726
|
+
const name = baseFileName(fh.logicalPath);
|
|
727
|
+
this._toolsets.set(name, new ToolsetHandle(name, fh));
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
case "agent_definition": {
|
|
731
|
+
const name = baseFileName(fh.logicalPath);
|
|
732
|
+
this._agentSpecs.set(name, new AgentSpecHandle(name, fh));
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
for (const [name, files] of skillGroups) {
|
|
738
|
+
this._skills.set(name, new SkillHandle(name, files));
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
// -- File access --
|
|
742
|
+
file(path) {
|
|
743
|
+
return this._files.get(path);
|
|
744
|
+
}
|
|
745
|
+
files() {
|
|
746
|
+
return [...this._files.values()];
|
|
747
|
+
}
|
|
748
|
+
// -- Domain handles --
|
|
749
|
+
skills() {
|
|
750
|
+
return [...this._skills.values()];
|
|
751
|
+
}
|
|
752
|
+
skill(name) {
|
|
753
|
+
const h = this._skills.get(name);
|
|
754
|
+
if (!h) {
|
|
755
|
+
throw new Error(`Skill "${name}" not found in bundle`);
|
|
756
|
+
}
|
|
757
|
+
return h;
|
|
758
|
+
}
|
|
759
|
+
prompts() {
|
|
760
|
+
return [...this._prompts.values()];
|
|
761
|
+
}
|
|
762
|
+
prompt(name) {
|
|
763
|
+
const h = this._prompts.get(name);
|
|
764
|
+
if (!h) {
|
|
765
|
+
throw new Error(`Prompt "${name}" not found in bundle`);
|
|
766
|
+
}
|
|
767
|
+
return h;
|
|
768
|
+
}
|
|
769
|
+
toolsets() {
|
|
770
|
+
return [...this._toolsets.values()];
|
|
771
|
+
}
|
|
772
|
+
toolset(name) {
|
|
773
|
+
const h = this._toolsets.get(name);
|
|
774
|
+
if (!h) {
|
|
775
|
+
throw new Error(`Toolset "${name}" not found in bundle`);
|
|
776
|
+
}
|
|
777
|
+
return h;
|
|
778
|
+
}
|
|
779
|
+
agentSpecs() {
|
|
780
|
+
return [...this._agentSpecs.values()];
|
|
781
|
+
}
|
|
782
|
+
agentSpec(name) {
|
|
783
|
+
const h = this._agentSpecs.get(name);
|
|
784
|
+
if (!h) {
|
|
785
|
+
throw new Error(`AgentSpec "${name}" not found in bundle`);
|
|
786
|
+
}
|
|
787
|
+
return h;
|
|
788
|
+
}
|
|
789
|
+
// -- Filtering --
|
|
790
|
+
select(filter) {
|
|
791
|
+
return new Selection(this, filter);
|
|
792
|
+
}
|
|
793
|
+
// -- Integrity --
|
|
794
|
+
verify() {
|
|
795
|
+
const errors = [];
|
|
796
|
+
for (const fh of this._files.values()) {
|
|
797
|
+
const actual = createHash("sha256").update(fh.bytes()).digest("hex");
|
|
798
|
+
if (actual !== fh.sha256) {
|
|
799
|
+
errors.push({ path: fh.logicalPath, expected: fh.sha256, actual });
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return { ok: errors.length === 0, errors };
|
|
803
|
+
}
|
|
804
|
+
// -- Lockfile --
|
|
805
|
+
async writeLockfile(path) {
|
|
806
|
+
const lock = {
|
|
807
|
+
ref: this.ref.toString(),
|
|
808
|
+
version: this.version,
|
|
809
|
+
files: [...this._files.values()].map((f) => ({
|
|
810
|
+
logicalPath: f.logicalPath,
|
|
811
|
+
sha256: f.sha256,
|
|
812
|
+
sizeBytes: f.sizeBytes
|
|
813
|
+
}))
|
|
814
|
+
};
|
|
815
|
+
await mkdir4(dirname3(path), { recursive: true });
|
|
816
|
+
await writeFile4(path, JSON.stringify(lock, null, 2));
|
|
817
|
+
}
|
|
818
|
+
// -- Export adapter convenience methods --
|
|
819
|
+
async exportClaudePlugin(opts) {
|
|
820
|
+
const { exportClaudePlugin: exportClaudePlugin2 } = await Promise.resolve().then(() => (init_claude(), claude_exports));
|
|
821
|
+
return exportClaudePlugin2(this, opts);
|
|
822
|
+
}
|
|
823
|
+
async installClaudeSkills(dir, opts) {
|
|
824
|
+
const { installClaudeSkills: installClaudeSkills2 } = await Promise.resolve().then(() => (init_claude(), claude_exports));
|
|
825
|
+
return installClaudeSkills2(this, dir, opts);
|
|
826
|
+
}
|
|
827
|
+
async installVSCodeSkills(dir, opts) {
|
|
828
|
+
const { installVSCodeSkills: installVSCodeSkills2 } = await Promise.resolve().then(() => (init_vscode(), vscode_exports));
|
|
829
|
+
return installVSCodeSkills2(this, dir, opts);
|
|
830
|
+
}
|
|
831
|
+
// -- Deprecated compat --
|
|
832
|
+
/** @deprecated Use `file(path)` instead. */
|
|
833
|
+
getAsset(path) {
|
|
834
|
+
const fh = this._files.get(path);
|
|
835
|
+
if (!fh) {
|
|
836
|
+
return void 0;
|
|
837
|
+
}
|
|
838
|
+
return {
|
|
839
|
+
logicalPath: fh.logicalPath,
|
|
840
|
+
assetType: fh.assetType,
|
|
841
|
+
content: fh.text(),
|
|
842
|
+
sha256: fh.sha256,
|
|
843
|
+
mediaType: fh.mediaType
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
/** @deprecated Use `files().filter(...)` instead. */
|
|
847
|
+
getAssetsByType(type) {
|
|
848
|
+
return [...this._files.values()].filter((fh) => fh.assetType === type).map((fh) => ({
|
|
849
|
+
logicalPath: fh.logicalPath,
|
|
850
|
+
assetType: fh.assetType,
|
|
851
|
+
content: fh.text(),
|
|
852
|
+
sha256: fh.sha256,
|
|
853
|
+
mediaType: fh.mediaType
|
|
854
|
+
}));
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
function extractSkillName(logicalPath) {
|
|
858
|
+
if (!logicalPath.startsWith("skills/")) {
|
|
859
|
+
return void 0;
|
|
860
|
+
}
|
|
861
|
+
const rest = logicalPath.slice("skills/".length);
|
|
862
|
+
const slashIdx = rest.indexOf("/");
|
|
863
|
+
if (slashIdx === -1) {
|
|
864
|
+
return void 0;
|
|
865
|
+
}
|
|
866
|
+
const name = rest.slice(0, slashIdx);
|
|
867
|
+
return name || void 0;
|
|
868
|
+
}
|
|
869
|
+
function baseFileName(logicalPath) {
|
|
870
|
+
const parts = logicalPath.split("/");
|
|
871
|
+
const last = parts[parts.length - 1] ?? logicalPath;
|
|
872
|
+
const dotIdx = last.lastIndexOf(".");
|
|
873
|
+
return dotIdx > 0 ? last.slice(0, dotIdx) : last;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// src/cache.ts
|
|
877
|
+
import { createHash as createHash2, randomUUID } from "crypto";
|
|
878
|
+
import { existsSync } from "fs";
|
|
879
|
+
import { mkdir as mkdir5, readFile, readdir, rename, rm, stat, unlink, writeFile as writeFile5 } from "fs/promises";
|
|
880
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
881
|
+
var JSON_EXT_RE = /\.json$/;
|
|
882
|
+
var CACHEDIR_TAG_CONTENT = `Signature: 8a477f597d28d172789f06886806bc55
|
|
883
|
+
# This file is a cache directory tag created by musher.
|
|
884
|
+
# For information, see https://bford.info/cachedir/spec.html
|
|
885
|
+
`;
|
|
886
|
+
var BundleCache = class {
|
|
887
|
+
constructor(cacheDir, registryUrl, manifestTtlSeconds, refTtlSeconds) {
|
|
888
|
+
this.cacheDir = cacheDir;
|
|
889
|
+
this.manifestTtlSeconds = manifestTtlSeconds;
|
|
890
|
+
this.refTtlSeconds = refTtlSeconds;
|
|
891
|
+
this.hostId = computeHostId(registryUrl);
|
|
892
|
+
}
|
|
893
|
+
hostId;
|
|
894
|
+
// -- Blob storage -------------------------------------------------------------
|
|
895
|
+
blobPath(digest) {
|
|
896
|
+
const prefix = digest.slice(0, 2);
|
|
897
|
+
return join4(this.cacheDir, "blobs", "sha256", prefix, digest);
|
|
898
|
+
}
|
|
899
|
+
async writeBlob(content) {
|
|
900
|
+
const digest = createHash2("sha256").update(content).digest("hex");
|
|
901
|
+
const target = this.blobPath(digest);
|
|
902
|
+
if (existsSync(target)) {
|
|
903
|
+
return digest;
|
|
904
|
+
}
|
|
905
|
+
await this.atomicWrite(target, content);
|
|
906
|
+
return digest;
|
|
907
|
+
}
|
|
908
|
+
// -- Manifest storage ---------------------------------------------------------
|
|
909
|
+
manifestDir(namespace, slug) {
|
|
910
|
+
return join4(this.cacheDir, "manifests", this.hostId, namespace, slug);
|
|
911
|
+
}
|
|
912
|
+
manifestPath(namespace, slug, version) {
|
|
913
|
+
return join4(this.manifestDir(namespace, slug), `${version}.json`);
|
|
914
|
+
}
|
|
915
|
+
metaPath(namespace, slug, version) {
|
|
916
|
+
return join4(this.manifestDir(namespace, slug), `${version}.meta.json`);
|
|
917
|
+
}
|
|
918
|
+
// -- Ref storage --------------------------------------------------------------
|
|
919
|
+
refPath(namespace, slug, ref) {
|
|
920
|
+
return join4(this.cacheDir, "refs", this.hostId, namespace, slug, `${ref}.json`);
|
|
921
|
+
}
|
|
922
|
+
/** Cache a ref → version mapping with TTL. */
|
|
923
|
+
async cacheRef(namespace, slug, ref, version) {
|
|
924
|
+
const entry = {
|
|
925
|
+
version,
|
|
926
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
927
|
+
ttlSeconds: this.refTtlSeconds
|
|
928
|
+
};
|
|
929
|
+
const target = this.refPath(namespace, slug, ref);
|
|
930
|
+
await this.atomicWrite(target, Buffer.from(JSON.stringify(entry, null, 2)));
|
|
931
|
+
}
|
|
932
|
+
/** Resolve a cached ref → version if still fresh. Returns null if expired or missing. */
|
|
933
|
+
async resolveRef(namespace, slug, ref) {
|
|
934
|
+
try {
|
|
935
|
+
const raw = await readFile(this.refPath(namespace, slug, ref), "utf-8");
|
|
936
|
+
const entry = JSON.parse(raw);
|
|
937
|
+
const fetchedAt = new Date(entry.fetchedAt).getTime();
|
|
938
|
+
const ttl = (entry.ttlSeconds ?? this.refTtlSeconds) * 1e3;
|
|
939
|
+
if (Date.now() - fetchedAt < ttl) {
|
|
940
|
+
return entry.version;
|
|
941
|
+
}
|
|
942
|
+
return null;
|
|
943
|
+
} catch {
|
|
944
|
+
return null;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
// -- Freshness ----------------------------------------------------------------
|
|
948
|
+
/** Check if a cached manifest is still fresh. */
|
|
949
|
+
async isFresh(namespace, slug, version) {
|
|
950
|
+
try {
|
|
951
|
+
const raw = await readFile(this.metaPath(namespace, slug, version), "utf-8");
|
|
952
|
+
const meta = JSON.parse(raw);
|
|
953
|
+
const fetchedAt = new Date(meta.fetchedAt).getTime();
|
|
954
|
+
const ttl = (meta.ttlSeconds ?? this.manifestTtlSeconds) * 1e3;
|
|
955
|
+
return Date.now() - fetchedAt < ttl;
|
|
956
|
+
} catch {
|
|
957
|
+
return false;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
/** Load only the manifest JSON (no blob content). Returns null if not cached. */
|
|
961
|
+
async loadManifest(namespace, slug, version) {
|
|
962
|
+
const mPath = this.manifestPath(namespace, slug, version);
|
|
963
|
+
if (!existsSync(mPath)) {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
try {
|
|
967
|
+
const raw = await readFile(mPath, "utf-8");
|
|
968
|
+
return JSON.parse(raw);
|
|
969
|
+
} catch {
|
|
970
|
+
return null;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
// -- Write --------------------------------------------------------------------
|
|
974
|
+
/** Write only the manifest and metadata to the cache (no blobs). */
|
|
975
|
+
async writeManifest(manifest) {
|
|
976
|
+
try {
|
|
977
|
+
await this.ensureCacheDirTag();
|
|
978
|
+
const mPath = this.manifestPath(manifest.namespace, manifest.slug, manifest.version);
|
|
979
|
+
await this.atomicWrite(mPath, Buffer.from(JSON.stringify(manifest, null, 2)));
|
|
980
|
+
const meta = {
|
|
981
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
982
|
+
ttlSeconds: this.manifestTtlSeconds,
|
|
983
|
+
ociDigest: manifest.ociDigest ?? void 0
|
|
984
|
+
};
|
|
985
|
+
const metPath = this.metaPath(manifest.namespace, manifest.slug, manifest.version);
|
|
986
|
+
await this.atomicWrite(metPath, Buffer.from(JSON.stringify(meta, null, 2)));
|
|
987
|
+
} catch (error) {
|
|
988
|
+
throw new CacheError(
|
|
989
|
+
`Failed to write manifest cache: ${error instanceof Error ? error.message : String(error)}`,
|
|
990
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
/** Write a resolved bundle and its assets to the cache. */
|
|
995
|
+
async write(manifest, assets) {
|
|
996
|
+
try {
|
|
997
|
+
await this.ensureCacheDirTag();
|
|
998
|
+
for (const [, content] of assets) {
|
|
999
|
+
const buf = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
|
|
1000
|
+
await this.writeBlob(buf);
|
|
1001
|
+
}
|
|
1002
|
+
await this.writeManifest(manifest);
|
|
1003
|
+
return {
|
|
1004
|
+
ref: manifest.ref,
|
|
1005
|
+
version: manifest.version,
|
|
1006
|
+
cacheDir: this.manifestDir(manifest.namespace, manifest.slug),
|
|
1007
|
+
manifest
|
|
1008
|
+
};
|
|
1009
|
+
} catch (error) {
|
|
1010
|
+
throw new CacheError(
|
|
1011
|
+
`Failed to write cache: ${error instanceof Error ? error.message : String(error)}`,
|
|
1012
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
// -- Load ---------------------------------------------------------------------
|
|
1017
|
+
/** Load a cached bundle from disk, verifying SHA256 integrity. Returns a Bundle. */
|
|
1018
|
+
async load(namespace, slug, version) {
|
|
1019
|
+
const mPath = this.manifestPath(namespace, slug, version);
|
|
1020
|
+
if (!existsSync(mPath)) {
|
|
1021
|
+
return null;
|
|
1022
|
+
}
|
|
1023
|
+
try {
|
|
1024
|
+
const raw = await readFile(mPath, "utf-8");
|
|
1025
|
+
const manifest = JSON.parse(raw);
|
|
1026
|
+
const contents = /* @__PURE__ */ new Map();
|
|
1027
|
+
if (manifest.manifest?.layers) {
|
|
1028
|
+
for (const layer of manifest.manifest.layers) {
|
|
1029
|
+
const buf = await readFile(this.blobPath(layer.contentSha256));
|
|
1030
|
+
const hash = createHash2("sha256").update(buf).digest("hex");
|
|
1031
|
+
if (hash !== layer.contentSha256) {
|
|
1032
|
+
throw new IntegrityError(layer.contentSha256, hash);
|
|
1033
|
+
}
|
|
1034
|
+
contents.set(layer.logicalPath, buf);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
return new Bundle(manifest, contents);
|
|
1038
|
+
} catch (error) {
|
|
1039
|
+
if (error instanceof IntegrityError) {
|
|
1040
|
+
throw error;
|
|
1041
|
+
}
|
|
1042
|
+
throw new CacheError(
|
|
1043
|
+
`Failed to load cache: ${error instanceof Error ? error.message : String(error)}`,
|
|
1044
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
// -- Cleanup ------------------------------------------------------------------
|
|
1049
|
+
/** Remove expired cache entries and garbage-collect unreferenced blobs. */
|
|
1050
|
+
async clean() {
|
|
1051
|
+
try {
|
|
1052
|
+
const referencedDigests = /* @__PURE__ */ new Set();
|
|
1053
|
+
await this.cleanManifests(referencedDigests);
|
|
1054
|
+
await this.cleanRefs();
|
|
1055
|
+
await this.gcBlobs(referencedDigests);
|
|
1056
|
+
await this.removeLegacy();
|
|
1057
|
+
} catch (error) {
|
|
1058
|
+
throw new CacheError(
|
|
1059
|
+
`Failed to clean cache: ${error instanceof Error ? error.message : String(error)}`,
|
|
1060
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
/** Remove all cached data. */
|
|
1065
|
+
async purge() {
|
|
1066
|
+
try {
|
|
1067
|
+
const dirs = ["manifests", "refs", "blobs", "temp", "bundles"];
|
|
1068
|
+
for (const dir of dirs) {
|
|
1069
|
+
const p = join4(this.cacheDir, dir);
|
|
1070
|
+
if (existsSync(p)) {
|
|
1071
|
+
await rm(p, { recursive: true, force: true });
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
} catch (error) {
|
|
1075
|
+
throw new CacheError(
|
|
1076
|
+
`Failed to purge cache: ${error instanceof Error ? error.message : String(error)}`,
|
|
1077
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
// -- Internals ----------------------------------------------------------------
|
|
1082
|
+
/** Atomic write via temp file + rename. */
|
|
1083
|
+
async atomicWrite(targetPath, data) {
|
|
1084
|
+
const tempDir = join4(this.cacheDir, "temp");
|
|
1085
|
+
await mkdir5(tempDir, { recursive: true });
|
|
1086
|
+
const tempPath = join4(tempDir, `${randomUUID()}.tmp`);
|
|
1087
|
+
try {
|
|
1088
|
+
await writeFile5(tempPath, data);
|
|
1089
|
+
await mkdir5(dirname4(targetPath), { recursive: true });
|
|
1090
|
+
try {
|
|
1091
|
+
await rename(tempPath, targetPath);
|
|
1092
|
+
} catch {
|
|
1093
|
+
try {
|
|
1094
|
+
await unlink(targetPath);
|
|
1095
|
+
} catch {
|
|
1096
|
+
}
|
|
1097
|
+
await rename(tempPath, targetPath);
|
|
1098
|
+
}
|
|
1099
|
+
} finally {
|
|
1100
|
+
try {
|
|
1101
|
+
await unlink(tempPath);
|
|
1102
|
+
} catch {
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
/** Write CACHEDIR.TAG if it doesn't exist. */
|
|
1107
|
+
async ensureCacheDirTag() {
|
|
1108
|
+
const tagPath = join4(this.cacheDir, "CACHEDIR.TAG");
|
|
1109
|
+
if (!existsSync(tagPath)) {
|
|
1110
|
+
await mkdir5(this.cacheDir, { recursive: true });
|
|
1111
|
+
await writeFile5(tagPath, CACHEDIR_TAG_CONTENT);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
/** Walk manifests, remove expired, collect digests from surviving ones. */
|
|
1115
|
+
async cleanManifests(referencedDigests) {
|
|
1116
|
+
const manifestsRoot = join4(this.cacheDir, "manifests");
|
|
1117
|
+
if (!existsSync(manifestsRoot)) {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
for (const hostId of await safeReaddir(manifestsRoot)) {
|
|
1121
|
+
const hostDir = join4(manifestsRoot, hostId);
|
|
1122
|
+
if (!await isDir(hostDir)) {
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
for (const ns of await safeReaddir(hostDir)) {
|
|
1126
|
+
const nsDir = join4(hostDir, ns);
|
|
1127
|
+
if (!await isDir(nsDir)) {
|
|
1128
|
+
continue;
|
|
1129
|
+
}
|
|
1130
|
+
for (const slug of await safeReaddir(nsDir)) {
|
|
1131
|
+
const slugDir = join4(nsDir, slug);
|
|
1132
|
+
if (!await isDir(slugDir)) {
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
for (const file of await safeReaddir(slugDir)) {
|
|
1136
|
+
if (!file.endsWith(".json") || file.endsWith(".meta.json")) {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
const version = file.replace(JSON_EXT_RE, "");
|
|
1140
|
+
const fresh = await this.isFresh(ns, slug, version);
|
|
1141
|
+
if (fresh) {
|
|
1142
|
+
await this.collectDigests(join4(slugDir, file), referencedDigests);
|
|
1143
|
+
} else {
|
|
1144
|
+
await safeRm(join4(slugDir, file));
|
|
1145
|
+
await safeRm(join4(slugDir, `${version}.meta.json`));
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
/** Collect blob digests referenced by a manifest file. */
|
|
1153
|
+
async collectDigests(manifestFile, digests) {
|
|
1154
|
+
try {
|
|
1155
|
+
const raw = await readFile(manifestFile, "utf-8");
|
|
1156
|
+
const manifest = JSON.parse(raw);
|
|
1157
|
+
if (manifest.manifest?.layers) {
|
|
1158
|
+
for (const layer of manifest.manifest.layers) {
|
|
1159
|
+
digests.add(layer.contentSha256);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
} catch {
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
/** Walk refs and remove expired entries. */
|
|
1166
|
+
async cleanRefs() {
|
|
1167
|
+
const refsRoot = join4(this.cacheDir, "refs");
|
|
1168
|
+
if (!existsSync(refsRoot)) {
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
for (const hostId of await safeReaddir(refsRoot)) {
|
|
1172
|
+
const hostDir = join4(refsRoot, hostId);
|
|
1173
|
+
if (!await isDir(hostDir)) {
|
|
1174
|
+
continue;
|
|
1175
|
+
}
|
|
1176
|
+
for (const ns of await safeReaddir(hostDir)) {
|
|
1177
|
+
const nsDir = join4(hostDir, ns);
|
|
1178
|
+
if (!await isDir(nsDir)) {
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
for (const slug of await safeReaddir(nsDir)) {
|
|
1182
|
+
const slugDir = join4(nsDir, slug);
|
|
1183
|
+
if (!await isDir(slugDir)) {
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
for (const file of await safeReaddir(slugDir)) {
|
|
1187
|
+
if (!file.endsWith(".json")) {
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
const ref = file.replace(JSON_EXT_RE, "");
|
|
1191
|
+
const version = await this.resolveRef(ns, slug, ref);
|
|
1192
|
+
if (version === null) {
|
|
1193
|
+
await safeRm(join4(slugDir, file));
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
/** Remove blobs not referenced by any surviving manifest. */
|
|
1201
|
+
async gcBlobs(referencedDigests) {
|
|
1202
|
+
const blobsRoot = join4(this.cacheDir, "blobs", "sha256");
|
|
1203
|
+
if (!existsSync(blobsRoot)) {
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
for (const prefix of await safeReaddir(blobsRoot)) {
|
|
1207
|
+
const prefixDir = join4(blobsRoot, prefix);
|
|
1208
|
+
if (!await isDir(prefixDir)) {
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
for (const digest of await safeReaddir(prefixDir)) {
|
|
1212
|
+
if (!referencedDigests.has(digest)) {
|
|
1213
|
+
await safeRm(join4(prefixDir, digest));
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
/** Remove legacy bundles/ directory if present. */
|
|
1219
|
+
async removeLegacy() {
|
|
1220
|
+
const legacyDir = join4(this.cacheDir, "bundles");
|
|
1221
|
+
if (existsSync(legacyDir)) {
|
|
1222
|
+
await rm(legacyDir, { recursive: true, force: true });
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
function computeHostId(registryUrl) {
|
|
1227
|
+
try {
|
|
1228
|
+
const url = new URL(registryUrl);
|
|
1229
|
+
return url.host.replace(/[:/]/g, "_");
|
|
1230
|
+
} catch {
|
|
1231
|
+
return registryUrl.replace(/[:/]/g, "_");
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
async function isDir(path) {
|
|
1235
|
+
try {
|
|
1236
|
+
return (await stat(path)).isDirectory();
|
|
1237
|
+
} catch {
|
|
1238
|
+
return false;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
async function safeReaddir(path) {
|
|
1242
|
+
try {
|
|
1243
|
+
return await readdir(path);
|
|
1244
|
+
} catch {
|
|
1245
|
+
return [];
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
async function safeRm(path) {
|
|
1249
|
+
try {
|
|
1250
|
+
await unlink(path);
|
|
1251
|
+
} catch {
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
// src/config.ts
|
|
1256
|
+
import { readFileSync, statSync } from "fs";
|
|
1257
|
+
import { join as join6 } from "path";
|
|
1258
|
+
|
|
1259
|
+
// src/keyring.ts
|
|
1260
|
+
import { execFileSync } from "child_process";
|
|
1261
|
+
var ACCOUNT = "api-key";
|
|
1262
|
+
function serviceName(host) {
|
|
1263
|
+
return `musher/${host}`;
|
|
1264
|
+
}
|
|
1265
|
+
function tryKeyringLookup(service) {
|
|
1266
|
+
try {
|
|
1267
|
+
const platform = process.platform;
|
|
1268
|
+
if (platform === "darwin") {
|
|
1269
|
+
const result = execFileSync(
|
|
1270
|
+
"security",
|
|
1271
|
+
["find-generic-password", "-s", service, "-a", ACCOUNT, "-w"],
|
|
1272
|
+
{
|
|
1273
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1274
|
+
timeout: 5e3
|
|
1275
|
+
}
|
|
1276
|
+
);
|
|
1277
|
+
return result.toString("utf-8").trim() || void 0;
|
|
1278
|
+
}
|
|
1279
|
+
if (platform === "linux") {
|
|
1280
|
+
const result = execFileSync(
|
|
1281
|
+
"secret-tool",
|
|
1282
|
+
["lookup", "service", service, "username", ACCOUNT],
|
|
1283
|
+
{
|
|
1284
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1285
|
+
timeout: 5e3
|
|
1286
|
+
}
|
|
1287
|
+
);
|
|
1288
|
+
return result.toString("utf-8").trim() || void 0;
|
|
1289
|
+
}
|
|
1290
|
+
if (platform === "win32") {
|
|
1291
|
+
const result = execFileSync(
|
|
1292
|
+
"powershell.exe",
|
|
1293
|
+
[
|
|
1294
|
+
"-NoProfile",
|
|
1295
|
+
"-NonInteractive",
|
|
1296
|
+
"-Command",
|
|
1297
|
+
`$cred = Get-StoredCredential -Target '${service.replace(/'/g, "''")}'; if ($cred) { $cred.GetNetworkCredential().Password }`
|
|
1298
|
+
],
|
|
1299
|
+
{
|
|
1300
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1301
|
+
timeout: 5e3
|
|
1302
|
+
}
|
|
1303
|
+
);
|
|
1304
|
+
return result.toString("utf-8").trim() || void 0;
|
|
1305
|
+
}
|
|
1306
|
+
return void 0;
|
|
1307
|
+
} catch {
|
|
1308
|
+
return void 0;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function readKeyring(host = "api.musher.dev") {
|
|
1312
|
+
return tryKeyringLookup(serviceName(host));
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// src/paths.ts
|
|
1316
|
+
import { homedir, tmpdir } from "os";
|
|
1317
|
+
import { isAbsolute, join as join5 } from "path";
|
|
1318
|
+
var APP_NAME = "musher";
|
|
1319
|
+
function env(name) {
|
|
1320
|
+
const value = process.env[name];
|
|
1321
|
+
return value || void 0;
|
|
1322
|
+
}
|
|
1323
|
+
function absEnv(name) {
|
|
1324
|
+
const value = env(name);
|
|
1325
|
+
if (value && isAbsolute(value)) {
|
|
1326
|
+
return value;
|
|
1327
|
+
}
|
|
1328
|
+
return void 0;
|
|
1329
|
+
}
|
|
1330
|
+
function resolveDir(envKey, subdir, platformDefault) {
|
|
1331
|
+
const perDir = absEnv(envKey);
|
|
1332
|
+
if (perDir) {
|
|
1333
|
+
return perDir;
|
|
1334
|
+
}
|
|
1335
|
+
const umbrella = absEnv("MUSHER_HOME");
|
|
1336
|
+
if (umbrella) {
|
|
1337
|
+
return join5(umbrella, subdir);
|
|
1338
|
+
}
|
|
1339
|
+
return platformDefault();
|
|
1340
|
+
}
|
|
1341
|
+
function xdgDefault(xdgVar, fallbackSuffix) {
|
|
1342
|
+
const xdg = absEnv(xdgVar);
|
|
1343
|
+
if (xdg) {
|
|
1344
|
+
return join5(xdg, APP_NAME);
|
|
1345
|
+
}
|
|
1346
|
+
return join5(homedir(), fallbackSuffix, APP_NAME);
|
|
1347
|
+
}
|
|
1348
|
+
function linuxDirs() {
|
|
1349
|
+
return {
|
|
1350
|
+
cache: resolveDir("MUSHER_CACHE_HOME", "cache", () => xdgDefault("XDG_CACHE_HOME", ".cache")),
|
|
1351
|
+
config: resolveDir(
|
|
1352
|
+
"MUSHER_CONFIG_HOME",
|
|
1353
|
+
"config",
|
|
1354
|
+
() => xdgDefault("XDG_CONFIG_HOME", ".config")
|
|
1355
|
+
),
|
|
1356
|
+
data: resolveDir(
|
|
1357
|
+
"MUSHER_DATA_HOME",
|
|
1358
|
+
"data",
|
|
1359
|
+
() => xdgDefault("XDG_DATA_HOME", join5(".local", "share"))
|
|
1360
|
+
),
|
|
1361
|
+
state: resolveDir(
|
|
1362
|
+
"MUSHER_STATE_HOME",
|
|
1363
|
+
"state",
|
|
1364
|
+
() => xdgDefault("XDG_STATE_HOME", join5(".local", "state"))
|
|
1365
|
+
),
|
|
1366
|
+
runtime: resolveDir("MUSHER_RUNTIME_DIR", "runtime", () => {
|
|
1367
|
+
const xdg = absEnv("XDG_RUNTIME_DIR");
|
|
1368
|
+
if (xdg) {
|
|
1369
|
+
return join5(xdg, APP_NAME);
|
|
1370
|
+
}
|
|
1371
|
+
return join5(tmpdir(), APP_NAME, "run");
|
|
1372
|
+
})
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
function darwinDirs() {
|
|
1376
|
+
const home = homedir();
|
|
1377
|
+
const appSupport = join5(home, "Library", "Application Support", APP_NAME);
|
|
1378
|
+
return {
|
|
1379
|
+
cache: resolveDir(
|
|
1380
|
+
"MUSHER_CACHE_HOME",
|
|
1381
|
+
"cache",
|
|
1382
|
+
() => join5(home, "Library", "Caches", APP_NAME)
|
|
1383
|
+
),
|
|
1384
|
+
config: resolveDir("MUSHER_CONFIG_HOME", "config", () => join5(appSupport, "config")),
|
|
1385
|
+
data: resolveDir("MUSHER_DATA_HOME", "data", () => join5(appSupport, "data")),
|
|
1386
|
+
state: resolveDir("MUSHER_STATE_HOME", "state", () => join5(appSupport, "state")),
|
|
1387
|
+
runtime: resolveDir("MUSHER_RUNTIME_DIR", "runtime", () => join5(tmpdir(), APP_NAME, "run"))
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
function win32Dirs() {
|
|
1391
|
+
const localAppData = env("LOCALAPPDATA") ?? join5(homedir(), "AppData", "Local");
|
|
1392
|
+
const base = join5(localAppData, APP_NAME);
|
|
1393
|
+
return {
|
|
1394
|
+
cache: resolveDir("MUSHER_CACHE_HOME", "cache", () => join5(base, "cache")),
|
|
1395
|
+
config: resolveDir("MUSHER_CONFIG_HOME", "config", () => join5(base, "config")),
|
|
1396
|
+
data: resolveDir("MUSHER_DATA_HOME", "data", () => join5(base, "data")),
|
|
1397
|
+
state: resolveDir("MUSHER_STATE_HOME", "state", () => join5(base, "state")),
|
|
1398
|
+
runtime: resolveDir("MUSHER_RUNTIME_DIR", "runtime", () => join5(tmpdir(), APP_NAME, "run"))
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
function resolveMusherDirs() {
|
|
1402
|
+
switch (process.platform) {
|
|
1403
|
+
case "darwin":
|
|
1404
|
+
return darwinDirs();
|
|
1405
|
+
case "win32":
|
|
1406
|
+
return win32Dirs();
|
|
1407
|
+
default:
|
|
1408
|
+
return linuxDirs();
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// src/config.ts
|
|
1413
|
+
var DEFAULT_BASE_URL = "https://api.musher.dev";
|
|
1414
|
+
var DEFAULT_MANIFEST_TTL = 86400;
|
|
1415
|
+
var DEFAULT_REF_TTL = 300;
|
|
1416
|
+
var DEFAULT_TIMEOUT = 6e4;
|
|
1417
|
+
var DEFAULT_RETRIES = 3;
|
|
1418
|
+
function computeHostId2(host) {
|
|
1419
|
+
return host.replace(/[:/]/g, "_");
|
|
1420
|
+
}
|
|
1421
|
+
function readApiKeyFile(dataDir, host = "api.musher.dev") {
|
|
1422
|
+
try {
|
|
1423
|
+
const dir = dataDir ?? resolveMusherDirs().data;
|
|
1424
|
+
const filePath = join6(dir, "credentials", computeHostId2(host), "api-key");
|
|
1425
|
+
const content = readFileSync(filePath, "utf-8").trim();
|
|
1426
|
+
if (!content) {
|
|
1427
|
+
return void 0;
|
|
1428
|
+
}
|
|
1429
|
+
if (process.platform !== "win32") {
|
|
1430
|
+
const mode = statSync(filePath).mode;
|
|
1431
|
+
if (mode & 63) {
|
|
1432
|
+
process.emitWarning(
|
|
1433
|
+
`Ignoring ${filePath}: file permissions are too open (must not be readable by group or others). Run: chmod 600 ${filePath}`,
|
|
1434
|
+
"MusherSecurityWarning"
|
|
1435
|
+
);
|
|
1436
|
+
return void 0;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
return content;
|
|
1440
|
+
} catch {
|
|
1441
|
+
return void 0;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
function env2(name) {
|
|
1445
|
+
const value = process.env[name];
|
|
1446
|
+
return value || void 0;
|
|
1447
|
+
}
|
|
1448
|
+
function resolveConfig(config) {
|
|
1449
|
+
const dirs = resolveMusherDirs();
|
|
1450
|
+
const configDir = config?.configDir ?? dirs.config;
|
|
1451
|
+
const baseUrl = config?.baseUrl ?? env2("MUSHER_API_URL") ?? DEFAULT_BASE_URL;
|
|
1452
|
+
let keyringHost;
|
|
1453
|
+
try {
|
|
1454
|
+
keyringHost = new URL(baseUrl).host;
|
|
1455
|
+
} catch {
|
|
1456
|
+
keyringHost = "api.musher.dev";
|
|
1457
|
+
}
|
|
1458
|
+
return {
|
|
1459
|
+
baseUrl,
|
|
1460
|
+
apiKey: config?.apiKey ?? env2("MUSHER_API_KEY") ?? readKeyring(keyringHost) ?? readApiKeyFile(dirs.data, keyringHost),
|
|
1461
|
+
cacheDir: config?.cacheDir ?? dirs.cache,
|
|
1462
|
+
configDir,
|
|
1463
|
+
manifestTtlSeconds: config?.manifestTtlSeconds ?? DEFAULT_MANIFEST_TTL,
|
|
1464
|
+
refTtlSeconds: config?.refTtlSeconds ?? DEFAULT_REF_TTL,
|
|
1465
|
+
timeout: config?.timeout ?? DEFAULT_TIMEOUT,
|
|
1466
|
+
retries: config?.retries ?? DEFAULT_RETRIES
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// src/http.ts
|
|
1471
|
+
var TRAILING_SLASH_RE = /\/$/;
|
|
1472
|
+
var HttpTransport = class {
|
|
1473
|
+
constructor(config) {
|
|
1474
|
+
this.config = config;
|
|
1475
|
+
}
|
|
1476
|
+
async request(method, path, schema, options) {
|
|
1477
|
+
const url = this.buildUrl(path, options?.params);
|
|
1478
|
+
const headers = this.buildHeaders();
|
|
1479
|
+
let lastError;
|
|
1480
|
+
for (let attempt = 0; attempt <= this.config.retries; attempt++) {
|
|
1481
|
+
try {
|
|
1482
|
+
const init = { method, headers };
|
|
1483
|
+
if (options?.body) {
|
|
1484
|
+
init.body = JSON.stringify(options.body);
|
|
1485
|
+
}
|
|
1486
|
+
const response = await this.fetchWithTimeout(url, init);
|
|
1487
|
+
if (!response.ok) {
|
|
1488
|
+
const error = await this.mapError(response);
|
|
1489
|
+
if (response.status === 429 || response.status >= 500) {
|
|
1490
|
+
lastError = error;
|
|
1491
|
+
if (attempt < this.config.retries) {
|
|
1492
|
+
await this.backoff(attempt, error);
|
|
1493
|
+
continue;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
throw error;
|
|
1497
|
+
}
|
|
1498
|
+
const json = await response.json();
|
|
1499
|
+
return this.parse(schema, json);
|
|
1500
|
+
} catch (error) {
|
|
1501
|
+
if (error instanceof ApiError) {
|
|
1502
|
+
throw error;
|
|
1503
|
+
}
|
|
1504
|
+
if (error instanceof SchemaError) {
|
|
1505
|
+
throw error;
|
|
1506
|
+
}
|
|
1507
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
1508
|
+
if (attempt < this.config.retries) {
|
|
1509
|
+
await this.backoff(attempt);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
throw lastError ?? new NetworkError("Request failed after retries");
|
|
1514
|
+
}
|
|
1515
|
+
buildUrl(path, params) {
|
|
1516
|
+
const base = this.config.baseUrl.replace(TRAILING_SLASH_RE, "");
|
|
1517
|
+
const url = new URL(`${base}${path}`);
|
|
1518
|
+
if (params) {
|
|
1519
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1520
|
+
if (value !== void 0) {
|
|
1521
|
+
url.searchParams.set(key, String(value));
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
return url.toString();
|
|
1526
|
+
}
|
|
1527
|
+
buildHeaders() {
|
|
1528
|
+
const headers = {
|
|
1529
|
+
"Content-Type": "application/json",
|
|
1530
|
+
Accept: "application/json"
|
|
1531
|
+
};
|
|
1532
|
+
if (this.config.apiKey) {
|
|
1533
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
1534
|
+
}
|
|
1535
|
+
return headers;
|
|
1536
|
+
}
|
|
1537
|
+
async fetchWithTimeout(url, init) {
|
|
1538
|
+
const controller = new AbortController();
|
|
1539
|
+
const timer = setTimeout(() => controller.abort(), this.config.timeout);
|
|
1540
|
+
try {
|
|
1541
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
1542
|
+
} catch (error) {
|
|
1543
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1544
|
+
throw new TimeoutError(`Request timed out after ${this.config.timeout}ms`);
|
|
1545
|
+
}
|
|
1546
|
+
throw new NetworkError(error instanceof Error ? error.message : "Network request failed", {
|
|
1547
|
+
cause: error instanceof Error ? error : void 0
|
|
1548
|
+
});
|
|
1549
|
+
} finally {
|
|
1550
|
+
clearTimeout(timer);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
async mapError(response) {
|
|
1554
|
+
let problem;
|
|
1555
|
+
try {
|
|
1556
|
+
const body = await response.json();
|
|
1557
|
+
problem = {
|
|
1558
|
+
type: body.type ?? "about:blank",
|
|
1559
|
+
title: body.title ?? response.statusText,
|
|
1560
|
+
status: response.status,
|
|
1561
|
+
detail: body.detail ?? response.statusText,
|
|
1562
|
+
instance: body.instance,
|
|
1563
|
+
traceId: body.traceId
|
|
1564
|
+
};
|
|
1565
|
+
} catch {
|
|
1566
|
+
problem = {
|
|
1567
|
+
type: "about:blank",
|
|
1568
|
+
title: response.statusText,
|
|
1569
|
+
status: response.status,
|
|
1570
|
+
detail: response.statusText
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
switch (response.status) {
|
|
1574
|
+
case 401:
|
|
1575
|
+
return new AuthenticationError(problem);
|
|
1576
|
+
case 403:
|
|
1577
|
+
return new ForbiddenError(problem);
|
|
1578
|
+
case 404:
|
|
1579
|
+
return new NotFoundError(problem);
|
|
1580
|
+
case 422:
|
|
1581
|
+
return new ValidationError(
|
|
1582
|
+
problem
|
|
1583
|
+
);
|
|
1584
|
+
case 429: {
|
|
1585
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
1586
|
+
return new RateLimitError(
|
|
1587
|
+
problem,
|
|
1588
|
+
retryAfter ? Number.parseInt(retryAfter, 10) : void 0
|
|
1589
|
+
);
|
|
1590
|
+
}
|
|
1591
|
+
default:
|
|
1592
|
+
return new ApiError(problem);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
parse(schema, data) {
|
|
1596
|
+
const result = schema.safeParse(data);
|
|
1597
|
+
if (!result.success) {
|
|
1598
|
+
throw new SchemaError(`API response validation failed: ${result.error.message}`);
|
|
1599
|
+
}
|
|
1600
|
+
return result.data;
|
|
1601
|
+
}
|
|
1602
|
+
async backoff(attempt, error) {
|
|
1603
|
+
let delay = Math.min(1e3 * 2 ** attempt, 1e4);
|
|
1604
|
+
if (error instanceof RateLimitError && error.retryAfter) {
|
|
1605
|
+
delay = error.retryAfter * 1e3;
|
|
1606
|
+
}
|
|
1607
|
+
delay += Math.random() * 500;
|
|
1608
|
+
await new Promise((resolve5) => setTimeout(resolve5, delay));
|
|
1609
|
+
}
|
|
1610
|
+
};
|
|
1611
|
+
|
|
1612
|
+
// src/schemas/asset.ts
|
|
1613
|
+
import { z } from "zod";
|
|
1614
|
+
var AssetSummaryOutputSchema = z.object({
|
|
1615
|
+
id: z.string().uuid(),
|
|
1616
|
+
bundleId: z.string().uuid(),
|
|
1617
|
+
assetType: z.string(),
|
|
1618
|
+
logicalPath: z.string(),
|
|
1619
|
+
contentSha256: z.string(),
|
|
1620
|
+
contentSizeBytes: z.number().int().nullable().optional(),
|
|
1621
|
+
mediaType: z.string().nullable().optional(),
|
|
1622
|
+
createdAt: z.string().datetime(),
|
|
1623
|
+
updatedAt: z.string().datetime().nullable().optional()
|
|
1624
|
+
});
|
|
1625
|
+
var AssetDetailOutputSchema = AssetSummaryOutputSchema.extend({
|
|
1626
|
+
contentText: z.string().nullable().optional()
|
|
1627
|
+
});
|
|
1628
|
+
|
|
1629
|
+
// src/schemas/bundle.ts
|
|
1630
|
+
import { z as z3 } from "zod";
|
|
1631
|
+
|
|
1632
|
+
// src/schemas/common.ts
|
|
1633
|
+
import { z as z2 } from "zod";
|
|
1634
|
+
var BundleVisibility = z2.enum(["private", "public"]);
|
|
1635
|
+
var BundleVersionState = z2.enum(["published", "yanked"]);
|
|
1636
|
+
var AssetType = z2.enum([
|
|
1637
|
+
"agent_definition",
|
|
1638
|
+
"skill",
|
|
1639
|
+
"tool_config",
|
|
1640
|
+
"prompt",
|
|
1641
|
+
"config",
|
|
1642
|
+
"other"
|
|
1643
|
+
]);
|
|
1644
|
+
var BundleSourceType = z2.enum(["console", "registry"]);
|
|
1645
|
+
var PaginationMetaSchema = z2.object({
|
|
1646
|
+
nextCursor: z2.string().nullable(),
|
|
1647
|
+
hasMore: z2.boolean()
|
|
1648
|
+
});
|
|
1649
|
+
function paginatedSchema(itemSchema) {
|
|
1650
|
+
return z2.object({
|
|
1651
|
+
data: z2.array(itemSchema),
|
|
1652
|
+
meta: PaginationMetaSchema
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
// src/schemas/bundle.ts
|
|
1657
|
+
var BundleOutputSchema = z3.object({
|
|
1658
|
+
id: z3.string().uuid(),
|
|
1659
|
+
namespace: z3.string(),
|
|
1660
|
+
slug: z3.string(),
|
|
1661
|
+
ref: z3.string(),
|
|
1662
|
+
name: z3.string(),
|
|
1663
|
+
description: z3.string().nullable().optional(),
|
|
1664
|
+
visibility: BundleVisibility,
|
|
1665
|
+
sourceType: BundleSourceType,
|
|
1666
|
+
readmeFormat: z3.string().nullable().optional(),
|
|
1667
|
+
createdAt: z3.string().datetime(),
|
|
1668
|
+
updatedAt: z3.string().datetime().nullable().optional()
|
|
1669
|
+
});
|
|
1670
|
+
var BundleDetailOutputSchema = BundleOutputSchema.extend({
|
|
1671
|
+
readmeContent: z3.string().nullable().optional(),
|
|
1672
|
+
latestVersion: z3.string().nullable().optional(),
|
|
1673
|
+
versionCount: z3.number().int(),
|
|
1674
|
+
assetCount: z3.number().int()
|
|
1675
|
+
});
|
|
1676
|
+
|
|
1677
|
+
// src/schemas/resolve.ts
|
|
1678
|
+
import { z as z4 } from "zod";
|
|
1679
|
+
var BundleLayerOutputSchema = z4.object({
|
|
1680
|
+
assetId: z4.string(),
|
|
1681
|
+
logicalPath: z4.string(),
|
|
1682
|
+
assetType: z4.string(),
|
|
1683
|
+
contentSha256: z4.string(),
|
|
1684
|
+
sizeBytes: z4.number().int(),
|
|
1685
|
+
mediaType: z4.string().nullable().optional()
|
|
1686
|
+
});
|
|
1687
|
+
var BundleManifestOutputSchema = z4.object({
|
|
1688
|
+
layers: z4.array(BundleLayerOutputSchema)
|
|
1689
|
+
});
|
|
1690
|
+
var BundleResolveOutputSchema = z4.object({
|
|
1691
|
+
bundleId: z4.string().uuid(),
|
|
1692
|
+
versionId: z4.string().uuid(),
|
|
1693
|
+
namespace: z4.string(),
|
|
1694
|
+
slug: z4.string(),
|
|
1695
|
+
ref: z4.string(),
|
|
1696
|
+
version: z4.string(),
|
|
1697
|
+
sourceType: BundleSourceType,
|
|
1698
|
+
ociRef: z4.string().nullable().optional(),
|
|
1699
|
+
ociDigest: z4.string().nullable().optional(),
|
|
1700
|
+
state: BundleVersionState,
|
|
1701
|
+
manifest: BundleManifestOutputSchema.nullable().optional()
|
|
1702
|
+
});
|
|
1703
|
+
|
|
1704
|
+
// src/schemas/version.ts
|
|
1705
|
+
import { z as z5 } from "zod";
|
|
1706
|
+
var BundleVersionSummaryOutputSchema = z5.object({
|
|
1707
|
+
id: z5.string().uuid(),
|
|
1708
|
+
bundleId: z5.string().uuid(),
|
|
1709
|
+
version: z5.string(),
|
|
1710
|
+
state: BundleVersionState,
|
|
1711
|
+
ociRef: z5.string().nullable().optional(),
|
|
1712
|
+
ociDigest: z5.string().nullable().optional(),
|
|
1713
|
+
publishedBy: z5.string().nullable().optional(),
|
|
1714
|
+
yankedBy: z5.string().nullable().optional(),
|
|
1715
|
+
yankedAt: z5.string().datetime().nullable().optional(),
|
|
1716
|
+
yankReason: z5.string().nullable().optional(),
|
|
1717
|
+
createdAt: z5.string().datetime()
|
|
1718
|
+
});
|
|
1719
|
+
var ManifestAssetOutputSchema = z5.object({
|
|
1720
|
+
assetId: z5.string(),
|
|
1721
|
+
logicalPath: z5.string(),
|
|
1722
|
+
assetType: z5.string(),
|
|
1723
|
+
contentSha256: z5.string(),
|
|
1724
|
+
sizeBytes: z5.number().int(),
|
|
1725
|
+
mediaType: z5.string().nullable().optional()
|
|
1726
|
+
});
|
|
1727
|
+
var ManifestDetailOutputSchema = z5.object({
|
|
1728
|
+
namespace: z5.string(),
|
|
1729
|
+
bundleSlug: z5.string(),
|
|
1730
|
+
version: z5.string(),
|
|
1731
|
+
assets: z5.array(ManifestAssetOutputSchema)
|
|
1732
|
+
});
|
|
1733
|
+
var BundleVersionDetailOutputSchema = z5.object({
|
|
1734
|
+
id: z5.string().uuid(),
|
|
1735
|
+
bundleId: z5.string().uuid(),
|
|
1736
|
+
version: z5.string(),
|
|
1737
|
+
state: BundleVersionState,
|
|
1738
|
+
ociRef: z5.string().nullable().optional(),
|
|
1739
|
+
ociDigest: z5.string().nullable().optional(),
|
|
1740
|
+
manifest: ManifestDetailOutputSchema.nullable().optional(),
|
|
1741
|
+
publishedBy: z5.string().nullable().optional(),
|
|
1742
|
+
yankedBy: z5.string().nullable().optional(),
|
|
1743
|
+
yankedAt: z5.string().datetime().nullable().optional(),
|
|
1744
|
+
yankReason: z5.string().nullable().optional(),
|
|
1745
|
+
createdAt: z5.string().datetime()
|
|
1746
|
+
});
|
|
1747
|
+
|
|
1748
|
+
// src/resources/bundles.ts
|
|
1749
|
+
var BundlesResource = class {
|
|
1750
|
+
constructor(http) {
|
|
1751
|
+
this.http = http;
|
|
1752
|
+
}
|
|
1753
|
+
async get(namespace, bundle) {
|
|
1754
|
+
return this.http.request(
|
|
1755
|
+
"GET",
|
|
1756
|
+
`/v1/namespaces/${enc(namespace)}/bundles/${enc(bundle)}`,
|
|
1757
|
+
BundleDetailOutputSchema
|
|
1758
|
+
);
|
|
1759
|
+
}
|
|
1760
|
+
async list(namespace, params) {
|
|
1761
|
+
return this.http.request(
|
|
1762
|
+
"GET",
|
|
1763
|
+
`/v1/namespaces/${enc(namespace)}/bundles`,
|
|
1764
|
+
paginatedSchema(BundleOutputSchema),
|
|
1765
|
+
{
|
|
1766
|
+
params: {
|
|
1767
|
+
cursor: params?.cursor,
|
|
1768
|
+
limit: params?.limit
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
async resolve(namespace, bundle, version, digest) {
|
|
1774
|
+
const params = {};
|
|
1775
|
+
if (version) {
|
|
1776
|
+
params["version"] = version;
|
|
1777
|
+
}
|
|
1778
|
+
if (digest) {
|
|
1779
|
+
params["digest"] = digest;
|
|
1780
|
+
}
|
|
1781
|
+
const hasParams = Object.keys(params).length > 0;
|
|
1782
|
+
return this.http.request(
|
|
1783
|
+
"GET",
|
|
1784
|
+
`/v1/namespaces/${enc(namespace)}/bundles/${enc(bundle)}:resolve`,
|
|
1785
|
+
BundleResolveOutputSchema,
|
|
1786
|
+
hasParams ? { params } : void 0
|
|
1787
|
+
);
|
|
1788
|
+
}
|
|
1789
|
+
async listVersions(namespace, bundle, params) {
|
|
1790
|
+
return this.http.request(
|
|
1791
|
+
"GET",
|
|
1792
|
+
`/v1/namespaces/${enc(namespace)}/bundles/${enc(bundle)}/versions`,
|
|
1793
|
+
paginatedSchema(BundleVersionSummaryOutputSchema),
|
|
1794
|
+
{
|
|
1795
|
+
params: {
|
|
1796
|
+
cursor: params?.cursor,
|
|
1797
|
+
limit: params?.limit
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
);
|
|
1801
|
+
}
|
|
1802
|
+
async getVersion(namespace, bundle, version) {
|
|
1803
|
+
return this.http.request(
|
|
1804
|
+
"GET",
|
|
1805
|
+
`/v1/namespaces/${enc(namespace)}/bundles/${enc(bundle)}/versions/${enc(version)}`,
|
|
1806
|
+
BundleVersionDetailOutputSchema
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1809
|
+
async listAssets(namespace, bundle, params) {
|
|
1810
|
+
return this.http.request(
|
|
1811
|
+
"GET",
|
|
1812
|
+
`/v1/namespaces/${enc(namespace)}/bundles/${enc(bundle)}/assets`,
|
|
1813
|
+
paginatedSchema(AssetSummaryOutputSchema),
|
|
1814
|
+
{
|
|
1815
|
+
params: {
|
|
1816
|
+
cursor: params?.cursor,
|
|
1817
|
+
limit: params?.limit
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
);
|
|
1821
|
+
}
|
|
1822
|
+
async getAsset(namespace, bundle, assetId, version) {
|
|
1823
|
+
return this.http.request(
|
|
1824
|
+
"GET",
|
|
1825
|
+
`/v1/namespaces/${enc(namespace)}/bundles/${enc(bundle)}/assets/${enc(assetId)}`,
|
|
1826
|
+
AssetDetailOutputSchema,
|
|
1827
|
+
{ params: { version } }
|
|
1828
|
+
);
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
function enc(value) {
|
|
1832
|
+
return encodeURIComponent(value);
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
// src/client.ts
|
|
1836
|
+
var _loadDeprecationWarned = false;
|
|
1837
|
+
var MusherClient = class {
|
|
1838
|
+
bundles;
|
|
1839
|
+
_cache;
|
|
1840
|
+
_http;
|
|
1841
|
+
constructor(config) {
|
|
1842
|
+
const resolved = resolveConfig(config);
|
|
1843
|
+
this._http = new HttpTransport(resolved);
|
|
1844
|
+
this._cache = new BundleCache(
|
|
1845
|
+
resolved.cacheDir,
|
|
1846
|
+
resolved.baseUrl,
|
|
1847
|
+
resolved.manifestTtlSeconds,
|
|
1848
|
+
resolved.refTtlSeconds
|
|
1849
|
+
);
|
|
1850
|
+
this.bundles = new BundlesResource(this._http);
|
|
1851
|
+
}
|
|
1852
|
+
/**
|
|
1853
|
+
* Resolve a bundle via the API, download all assets, and write to disk cache.
|
|
1854
|
+
* Returns a Bundle object.
|
|
1855
|
+
*
|
|
1856
|
+
* @param ref - Bundle reference (e.g. "namespace/slug", "namespace/slug:version").
|
|
1857
|
+
* @param version - Optional semver constraint. Defaults to latest.
|
|
1858
|
+
*/
|
|
1859
|
+
async pull(ref, version) {
|
|
1860
|
+
const parsed = BundleRef.parse(ref);
|
|
1861
|
+
const resolvedVersion = version ?? parsed.version;
|
|
1862
|
+
const resolved = await this.bundles.resolve(
|
|
1863
|
+
parsed.namespace,
|
|
1864
|
+
parsed.slug,
|
|
1865
|
+
resolvedVersion,
|
|
1866
|
+
parsed.digest
|
|
1867
|
+
);
|
|
1868
|
+
const assets = /* @__PURE__ */ new Map();
|
|
1869
|
+
if (resolved.manifest?.layers) {
|
|
1870
|
+
for (const layer of resolved.manifest.layers) {
|
|
1871
|
+
const asset = await this.bundles.getAsset(
|
|
1872
|
+
parsed.namespace,
|
|
1873
|
+
parsed.slug,
|
|
1874
|
+
layer.assetId,
|
|
1875
|
+
resolved.version
|
|
1876
|
+
);
|
|
1877
|
+
if (asset.contentText != null) {
|
|
1878
|
+
const buf = Buffer.from(asset.contentText, "utf-8");
|
|
1879
|
+
const hash = createHash3("sha256").update(buf).digest("hex");
|
|
1880
|
+
if (hash !== layer.contentSha256) {
|
|
1881
|
+
throw new IntegrityError(layer.contentSha256, hash);
|
|
1882
|
+
}
|
|
1883
|
+
assets.set(layer.logicalPath, buf);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
await this._cache.write(resolved, assets);
|
|
1888
|
+
if (!parsed.digest) {
|
|
1889
|
+
const refAlias = resolvedVersion ?? "latest";
|
|
1890
|
+
await this._cache.cacheRef(parsed.namespace, parsed.slug, refAlias, resolved.version);
|
|
1891
|
+
}
|
|
1892
|
+
return new Bundle(resolved, assets);
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Resolve bundle metadata without downloading content.
|
|
1896
|
+
* Checks the manifest cache (with TTL) before calling the API.
|
|
1897
|
+
*
|
|
1898
|
+
* @param ref - Bundle reference.
|
|
1899
|
+
* @param version - Optional semver constraint.
|
|
1900
|
+
*/
|
|
1901
|
+
async resolve(ref, version) {
|
|
1902
|
+
const parsed = BundleRef.parse(ref);
|
|
1903
|
+
let resolvedVersion = version ?? parsed.version;
|
|
1904
|
+
if (!(resolvedVersion || parsed.digest)) {
|
|
1905
|
+
const cachedVersion = await this._cache.resolveRef(parsed.namespace, parsed.slug, "latest");
|
|
1906
|
+
if (cachedVersion) {
|
|
1907
|
+
resolvedVersion = cachedVersion;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
if (resolvedVersion) {
|
|
1911
|
+
const fresh = await this._cache.isFresh(parsed.namespace, parsed.slug, resolvedVersion);
|
|
1912
|
+
if (fresh) {
|
|
1913
|
+
const manifest = await this._cache.loadManifest(
|
|
1914
|
+
parsed.namespace,
|
|
1915
|
+
parsed.slug,
|
|
1916
|
+
resolvedVersion
|
|
1917
|
+
);
|
|
1918
|
+
if (manifest) {
|
|
1919
|
+
return manifest;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
const resolved = await this.bundles.resolve(
|
|
1924
|
+
parsed.namespace,
|
|
1925
|
+
parsed.slug,
|
|
1926
|
+
resolvedVersion,
|
|
1927
|
+
parsed.digest
|
|
1928
|
+
);
|
|
1929
|
+
await this._cache.writeManifest(resolved);
|
|
1930
|
+
if (!parsed.digest) {
|
|
1931
|
+
const refAlias = resolvedVersion ?? "latest";
|
|
1932
|
+
await this._cache.cacheRef(parsed.namespace, parsed.slug, refAlias, resolved.version);
|
|
1933
|
+
}
|
|
1934
|
+
return resolved;
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* @deprecated Use `pull()` instead. This method will be removed in a future version.
|
|
1938
|
+
*
|
|
1939
|
+
* Load a bundle into memory. Checks cache first (TTL-aware), pulls if stale.
|
|
1940
|
+
*/
|
|
1941
|
+
async load(ref, version) {
|
|
1942
|
+
if (!_loadDeprecationWarned) {
|
|
1943
|
+
_loadDeprecationWarned = true;
|
|
1944
|
+
process.emitWarning(
|
|
1945
|
+
"MusherClient.load() is deprecated. Use pull() instead.",
|
|
1946
|
+
"DeprecationWarning"
|
|
1947
|
+
);
|
|
1948
|
+
}
|
|
1949
|
+
const parsed = BundleRef.parse(ref);
|
|
1950
|
+
let resolvedVersion = version ?? parsed.version;
|
|
1951
|
+
if (!(resolvedVersion || parsed.digest)) {
|
|
1952
|
+
const cachedVersion = await this._cache.resolveRef(parsed.namespace, parsed.slug, "latest");
|
|
1953
|
+
if (cachedVersion) {
|
|
1954
|
+
resolvedVersion = cachedVersion;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
if (resolvedVersion) {
|
|
1958
|
+
const fresh = await this._cache.isFresh(parsed.namespace, parsed.slug, resolvedVersion);
|
|
1959
|
+
if (fresh) {
|
|
1960
|
+
const loaded = await this._cache.load(parsed.namespace, parsed.slug, resolvedVersion);
|
|
1961
|
+
if (loaded) {
|
|
1962
|
+
return loaded;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
return this.pull(ref, version);
|
|
1967
|
+
}
|
|
1968
|
+
/** Cache management utilities. */
|
|
1969
|
+
cache = {
|
|
1970
|
+
/** Remove expired cache entries and garbage-collect unreferenced blobs. */
|
|
1971
|
+
clean: () => this._cache.clean(),
|
|
1972
|
+
/** Remove all cached data. */
|
|
1973
|
+
purge: () => this._cache.purge()
|
|
1974
|
+
};
|
|
1975
|
+
};
|
|
1976
|
+
|
|
1977
|
+
// src/index.ts
|
|
1978
|
+
init_frontmatter();
|
|
1979
|
+
|
|
1980
|
+
// src/adapters/index.ts
|
|
1981
|
+
init_claude();
|
|
1982
|
+
init_openai();
|
|
1983
|
+
init_vscode();
|
|
1984
|
+
|
|
1985
|
+
// src/convenience.ts
|
|
1986
|
+
var _config;
|
|
1987
|
+
var _client;
|
|
1988
|
+
function configure(config) {
|
|
1989
|
+
_config = config;
|
|
1990
|
+
_client = void 0;
|
|
1991
|
+
}
|
|
1992
|
+
function getClient() {
|
|
1993
|
+
if (!_client) {
|
|
1994
|
+
_client = new MusherClient(_config);
|
|
1995
|
+
}
|
|
1996
|
+
return _client;
|
|
1997
|
+
}
|
|
1998
|
+
async function pull(ref, version) {
|
|
1999
|
+
return getClient().pull(ref, version);
|
|
2000
|
+
}
|
|
2001
|
+
async function resolve4(ref, version) {
|
|
2002
|
+
return getClient().resolve(ref, version);
|
|
2003
|
+
}
|
|
2004
|
+
export {
|
|
2005
|
+
AgentSpecHandle,
|
|
2006
|
+
ApiError,
|
|
2007
|
+
AssetDetailOutputSchema,
|
|
2008
|
+
AssetSummaryOutputSchema,
|
|
2009
|
+
AssetType,
|
|
2010
|
+
AuthenticationError,
|
|
2011
|
+
Bundle,
|
|
2012
|
+
BundleDetailOutputSchema,
|
|
2013
|
+
BundleLayerOutputSchema,
|
|
2014
|
+
BundleManifestOutputSchema,
|
|
2015
|
+
BundleOutputSchema,
|
|
2016
|
+
BundleRef,
|
|
2017
|
+
BundleResolveOutputSchema,
|
|
2018
|
+
BundleSourceType,
|
|
2019
|
+
BundleVersionDetailOutputSchema,
|
|
2020
|
+
BundleVersionState,
|
|
2021
|
+
BundleVersionSummaryOutputSchema,
|
|
2022
|
+
BundleVisibility,
|
|
2023
|
+
CacheError,
|
|
2024
|
+
FileHandle,
|
|
2025
|
+
ForbiddenError,
|
|
2026
|
+
IntegrityError,
|
|
2027
|
+
ManifestAssetOutputSchema,
|
|
2028
|
+
ManifestDetailOutputSchema,
|
|
2029
|
+
MushError,
|
|
2030
|
+
MusherClient,
|
|
2031
|
+
NetworkError,
|
|
2032
|
+
NotFoundError,
|
|
2033
|
+
PaginationMetaSchema,
|
|
2034
|
+
PromptHandle,
|
|
2035
|
+
RateLimitError,
|
|
2036
|
+
SchemaError,
|
|
2037
|
+
Selection,
|
|
2038
|
+
SkillHandle,
|
|
2039
|
+
TimeoutError,
|
|
2040
|
+
ToolsetHandle,
|
|
2041
|
+
ValidationError,
|
|
2042
|
+
configure,
|
|
2043
|
+
exportClaudePlugin,
|
|
2044
|
+
exportOpenAIInlineSkill,
|
|
2045
|
+
exportOpenAILocalSkill,
|
|
2046
|
+
extractDescription,
|
|
2047
|
+
getClient,
|
|
2048
|
+
installClaudeSkills,
|
|
2049
|
+
installVSCodeSkills,
|
|
2050
|
+
paginatedSchema,
|
|
2051
|
+
parseFrontmatter,
|
|
2052
|
+
pull,
|
|
2053
|
+
resolve4 as resolve,
|
|
2054
|
+
resolveMusherDirs
|
|
2055
|
+
};
|
|
2056
|
+
//# sourceMappingURL=index.js.map
|