@modootoday/pmcp 0.9.1 → 0.10.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.
- package/README.md +177 -8
- package/dist/attachment-tU5FZrgZ.d.ts +17 -0
- package/dist/chunk-HNAVQG6R.js +1198 -0
- package/dist/chunk-JRSR4JBR.js +88 -0
- package/dist/chunk-MSTKY33C.js +147 -0
- package/dist/{chunk-VQHNG3KM.js → chunk-VRZFM2M5.js} +53 -75
- package/dist/chunk-XEH4ZGGJ.js +336 -0
- package/dist/chunk-XOH2CW6P.js +297 -0
- package/dist/{chunk-QCOJ3WCM.js → chunk-ZUFFVIHQ.js} +166 -117
- package/dist/cli.js +507 -74
- package/dist/config-CJ43_oBS.d.ts +49 -0
- package/dist/embed-BBhv3y5x.d.ts +53 -0
- package/dist/encoder-DGRy9skC.d.ts +36 -0
- package/dist/http.d.ts +7 -38
- package/dist/http.js +18 -7
- package/dist/index.d.ts +25 -44
- package/dist/index.js +31 -11
- package/dist/mailbox/index.d.ts +44 -0
- package/dist/mailbox/index.js +387 -0
- package/dist/{server-BBZDjzWZ.d.ts → server-CwWcRjqz.d.ts} +135 -15
- package/dist/service-qpCsOXkQ.d.ts +116 -0
- package/dist/stdio.d.ts +10 -4
- package/dist/stdio.js +9 -4
- package/package.json +5 -1
- package/dist/catalog-BU5VLLyd.d.ts +0 -121
- package/dist/chunk-7DD2ADL2.js +0 -160
- package/dist/chunk-AJYMOFVX.js +0 -41
- package/dist/chunk-R2QPKEI4.js +0 -134
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HEARTBEAT_INTERVAL_MS,
|
|
3
|
+
MailboxError,
|
|
4
|
+
MailboxService,
|
|
5
|
+
MailboxStore,
|
|
6
|
+
PRESENCE_TTL_MS,
|
|
7
|
+
SCHEMA_VERSION,
|
|
8
|
+
attachMailbox,
|
|
9
|
+
failure,
|
|
10
|
+
initializeMailboxConfig,
|
|
11
|
+
mailboxConfigFrom,
|
|
12
|
+
openDatabase,
|
|
13
|
+
readBinding,
|
|
14
|
+
readCredential,
|
|
15
|
+
restoreBackup,
|
|
16
|
+
writeBindingFile,
|
|
17
|
+
writePrivateJson
|
|
18
|
+
} from "../chunk-HNAVQG6R.js";
|
|
19
|
+
import "../chunk-MSTKY33C.js";
|
|
20
|
+
import {
|
|
21
|
+
inboxSchema,
|
|
22
|
+
messageSchema,
|
|
23
|
+
operationSchema,
|
|
24
|
+
registerMailboxTools,
|
|
25
|
+
sendSchema,
|
|
26
|
+
sessionsSchema
|
|
27
|
+
} from "../chunk-XOH2CW6P.js";
|
|
28
|
+
|
|
29
|
+
// src/mailbox/notifications.ts
|
|
30
|
+
import { randomUUID } from "crypto";
|
|
31
|
+
var NotificationOutbox = class {
|
|
32
|
+
constructor(store) {
|
|
33
|
+
this.store = store;
|
|
34
|
+
}
|
|
35
|
+
store;
|
|
36
|
+
discover(binding, nativeTarget) {
|
|
37
|
+
return this.store.run(() => {
|
|
38
|
+
this.store.assertBinding(binding);
|
|
39
|
+
if (!nativeTarget) {
|
|
40
|
+
throw new MailboxError("invalid_target", "Native target is required");
|
|
41
|
+
}
|
|
42
|
+
this.store.database.run(
|
|
43
|
+
"UPDATE mailbox_notifications SET state = 'stale', lease_token = NULL, lease_until_ms = NULL WHERE actor_id = ? AND session_id != ? AND state IN ('pending', 'leased')",
|
|
44
|
+
binding.actorId,
|
|
45
|
+
binding.sessionId
|
|
46
|
+
);
|
|
47
|
+
this.store.database.run(
|
|
48
|
+
"INSERT OR IGNORE INTO mailbox_notifications(message_id, actor_id, session_id, generation, native_target, state, next_attempt_ms) SELECT DISTINCT r.message_id, r.actor_id, ?, ?, ?, 'pending', ? FROM mailbox_receipts r WHERE r.actor_id = ? AND r.ack_at_ms IS NULL AND (r.session_id IS NULL OR r.session_id = ?) AND NOT EXISTS (SELECT 1 FROM mailbox_notifications n WHERE n.message_id = r.message_id AND n.actor_id = r.actor_id AND n.session_id = ?) ORDER BY r.message_id LIMIT 100",
|
|
49
|
+
binding.sessionId,
|
|
50
|
+
binding.generation,
|
|
51
|
+
nativeTarget,
|
|
52
|
+
this.store.now(),
|
|
53
|
+
binding.actorId,
|
|
54
|
+
binding.sessionId,
|
|
55
|
+
binding.sessionId
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
retryUnsent(binding, claim) {
|
|
60
|
+
return this.store.run(() => {
|
|
61
|
+
this.assertLease(binding, claim);
|
|
62
|
+
const attempts = Number(claim.attempts);
|
|
63
|
+
this.store.database.run(
|
|
64
|
+
"UPDATE mailbox_notifications SET state = ?, outcome_code = 'known_unsent', lease_token = NULL, lease_until_ms = NULL, next_attempt_ms = ? WHERE message_id = ? AND actor_id = ? AND session_id = ?",
|
|
65
|
+
attempts >= 3 ? "failed" : "pending",
|
|
66
|
+
this.store.now() + Math.min(2e3, 100 * 2 ** (attempts - 1)),
|
|
67
|
+
claim.message_id,
|
|
68
|
+
binding.actorId,
|
|
69
|
+
binding.sessionId
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
reconcile(binding, messageId, delivered) {
|
|
74
|
+
return this.store.run(() => {
|
|
75
|
+
this.store.assertBinding(binding);
|
|
76
|
+
const row = this.store.database.get(
|
|
77
|
+
"SELECT state, attempts FROM mailbox_notifications WHERE message_id = ? AND actor_id = ? AND session_id = ?",
|
|
78
|
+
messageId,
|
|
79
|
+
binding.actorId,
|
|
80
|
+
binding.sessionId
|
|
81
|
+
);
|
|
82
|
+
if (row?.state !== "uncertain") {
|
|
83
|
+
throw new MailboxError(
|
|
84
|
+
"reconciliation_conflict",
|
|
85
|
+
"Only uncertain delivery can be reconciled"
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
let state = "pending";
|
|
89
|
+
if (delivered) {
|
|
90
|
+
state = "accepted";
|
|
91
|
+
} else if (Number(row.attempts) >= 3) {
|
|
92
|
+
state = "failed";
|
|
93
|
+
}
|
|
94
|
+
this.store.database.run(
|
|
95
|
+
"UPDATE mailbox_notifications SET state = ?, outcome_code = 'operator_reconciled', next_attempt_ms = ? WHERE message_id = ? AND actor_id = ? AND session_id = ?",
|
|
96
|
+
state,
|
|
97
|
+
this.store.now(),
|
|
98
|
+
messageId,
|
|
99
|
+
binding.actorId,
|
|
100
|
+
binding.sessionId
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
enqueue(messageId, binding, nativeTarget) {
|
|
105
|
+
return this.store.run(() => {
|
|
106
|
+
this.store.assertBinding(binding);
|
|
107
|
+
const receipt = this.store.database.get(
|
|
108
|
+
"SELECT session_id FROM mailbox_receipts WHERE message_id = ? AND actor_id = ? AND (session_id IS NULL OR session_id = ?)",
|
|
109
|
+
messageId,
|
|
110
|
+
binding.actorId,
|
|
111
|
+
binding.sessionId
|
|
112
|
+
);
|
|
113
|
+
if (!receipt || !nativeTarget) {
|
|
114
|
+
throw new MailboxError(
|
|
115
|
+
"unknown_message",
|
|
116
|
+
"Notification target is not a recipient"
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
this.store.database.run(
|
|
120
|
+
"INSERT OR IGNORE INTO mailbox_notifications(message_id, actor_id, session_id, generation, native_target, state, next_attempt_ms) VALUES (?, ?, ?, ?, ?, 'pending', ?)",
|
|
121
|
+
messageId,
|
|
122
|
+
binding.actorId,
|
|
123
|
+
binding.sessionId,
|
|
124
|
+
binding.generation,
|
|
125
|
+
nativeTarget,
|
|
126
|
+
this.store.now()
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
claim(binding) {
|
|
131
|
+
return this.store.run(() => {
|
|
132
|
+
this.store.assertBinding(binding);
|
|
133
|
+
this.store.database.run(
|
|
134
|
+
"UPDATE mailbox_notifications SET state = 'uncertain', outcome_code = 'dispatcher_lost', lease_token = NULL WHERE actor_id = ? AND state = 'leased' AND lease_until_ms <= ?",
|
|
135
|
+
binding.actorId,
|
|
136
|
+
this.store.now()
|
|
137
|
+
);
|
|
138
|
+
const row = this.store.database.get(
|
|
139
|
+
"SELECT * FROM mailbox_notifications WHERE actor_id = ? AND session_id = ? AND generation = ? AND state = 'pending' AND next_attempt_ms <= ? AND attempts < 3 ORDER BY next_attempt_ms LIMIT 1",
|
|
140
|
+
binding.actorId,
|
|
141
|
+
binding.sessionId,
|
|
142
|
+
binding.generation,
|
|
143
|
+
this.store.now()
|
|
144
|
+
);
|
|
145
|
+
if (!row) {
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
const leaseToken = randomUUID();
|
|
149
|
+
this.store.database.run(
|
|
150
|
+
"UPDATE mailbox_notifications SET state = 'leased', attempts = attempts + 1, lease_token = ?, lease_until_ms = ? WHERE message_id = ? AND actor_id = ? AND session_id = ?",
|
|
151
|
+
leaseToken,
|
|
152
|
+
this.store.now() + 2e4,
|
|
153
|
+
row.message_id,
|
|
154
|
+
binding.actorId,
|
|
155
|
+
binding.sessionId
|
|
156
|
+
);
|
|
157
|
+
return { ...row, leaseToken, attempts: Number(row.attempts) + 1 };
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
renew(binding, claim) {
|
|
161
|
+
return this.store.run(() => {
|
|
162
|
+
this.assertLease(binding, claim);
|
|
163
|
+
this.store.database.run(
|
|
164
|
+
"UPDATE mailbox_notifications SET lease_until_ms = ? WHERE message_id = ? AND actor_id = ? AND session_id = ?",
|
|
165
|
+
this.store.now() + 2e4,
|
|
166
|
+
claim.message_id,
|
|
167
|
+
binding.actorId,
|
|
168
|
+
binding.sessionId
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
assertLease(binding, claim) {
|
|
173
|
+
this.store.assertBinding(binding);
|
|
174
|
+
const lease = this.store.database.get(
|
|
175
|
+
"SELECT state, lease_token, lease_until_ms FROM mailbox_notifications WHERE message_id = ? AND actor_id = ? AND session_id = ?",
|
|
176
|
+
claim.message_id,
|
|
177
|
+
binding.actorId,
|
|
178
|
+
binding.sessionId
|
|
179
|
+
);
|
|
180
|
+
if (lease?.state !== "leased" || lease.lease_token !== claim.leaseToken || Number(lease.lease_until_ms) <= this.store.now()) {
|
|
181
|
+
throw new MailboxError(
|
|
182
|
+
"stale_lease",
|
|
183
|
+
"Notification lease is no longer current"
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
complete(binding, claim, outcome) {
|
|
188
|
+
return this.store.run(() => {
|
|
189
|
+
this.assertLease(binding, claim);
|
|
190
|
+
this.store.database.run(
|
|
191
|
+
"UPDATE mailbox_notifications SET state = ?, outcome_code = ?, lease_token = NULL, lease_until_ms = NULL WHERE message_id = ? AND actor_id = ? AND session_id = ?",
|
|
192
|
+
outcome,
|
|
193
|
+
outcome,
|
|
194
|
+
claim.message_id,
|
|
195
|
+
binding.actorId,
|
|
196
|
+
binding.sessionId
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// src/mailbox/recovery.ts
|
|
203
|
+
import {
|
|
204
|
+
createCipheriv,
|
|
205
|
+
createDecipheriv,
|
|
206
|
+
createHash,
|
|
207
|
+
randomBytes,
|
|
208
|
+
randomUUID as randomUUID2
|
|
209
|
+
} from "crypto";
|
|
210
|
+
import {
|
|
211
|
+
closeSync,
|
|
212
|
+
existsSync,
|
|
213
|
+
fsyncSync,
|
|
214
|
+
linkSync,
|
|
215
|
+
mkdirSync,
|
|
216
|
+
openSync,
|
|
217
|
+
readFileSync,
|
|
218
|
+
rmSync,
|
|
219
|
+
unlinkSync,
|
|
220
|
+
writeFileSync
|
|
221
|
+
} from "fs";
|
|
222
|
+
import { dirname, join } from "path";
|
|
223
|
+
var hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
224
|
+
async function createRecoveryBundle(store, credentials, destination, key) {
|
|
225
|
+
if (key.length !== 32 || existsSync(destination)) {
|
|
226
|
+
throw new MailboxError(
|
|
227
|
+
"invalid_recovery_target",
|
|
228
|
+
"Use a new destination and a 32-byte recovery key"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
const temporary = `${destination}.${randomUUID2()}.tmp`;
|
|
232
|
+
const snapshot = `${temporary}.sqlite`;
|
|
233
|
+
mkdirSync(dirname(destination), { recursive: true, mode: 448 });
|
|
234
|
+
try {
|
|
235
|
+
await store.backup(snapshot);
|
|
236
|
+
const probe = await openDatabase(snapshot);
|
|
237
|
+
try {
|
|
238
|
+
if (probe.get("PRAGMA integrity_check")?.integrity_check !== "ok" || probe.all("PRAGMA foreign_key_check").length > 0) {
|
|
239
|
+
throw new MailboxError(
|
|
240
|
+
"storage_corrupt",
|
|
241
|
+
"Recovery snapshot failed validation"
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
for (const credential of credentials) {
|
|
245
|
+
if (credential.projectId !== store.config.projectId || probe.get(
|
|
246
|
+
"SELECT credential_hash FROM mailbox_actors WHERE id = ?",
|
|
247
|
+
credential.actorId
|
|
248
|
+
)?.credential_hash !== hash(credential.secret)) {
|
|
249
|
+
throw new MailboxError(
|
|
250
|
+
"credential_mismatch",
|
|
251
|
+
"Escrow credential does not match the snapshot"
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} finally {
|
|
256
|
+
probe.close();
|
|
257
|
+
}
|
|
258
|
+
const database = readFileSync(snapshot);
|
|
259
|
+
const manifest = {
|
|
260
|
+
version: 1,
|
|
261
|
+
schemaVersion: SCHEMA_VERSION,
|
|
262
|
+
projectId: store.config.projectId,
|
|
263
|
+
createdAtMs: store.now(),
|
|
264
|
+
providerRuntime: process.version,
|
|
265
|
+
databaseSha256: hash(database),
|
|
266
|
+
database: database.toString("base64"),
|
|
267
|
+
credentials
|
|
268
|
+
};
|
|
269
|
+
const iv = randomBytes(12);
|
|
270
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
271
|
+
cipher.setAAD(Buffer.from("pmcp-mailbox-recovery-v1"));
|
|
272
|
+
const encrypted = Buffer.concat([
|
|
273
|
+
cipher.update(JSON.stringify(manifest)),
|
|
274
|
+
cipher.final()
|
|
275
|
+
]);
|
|
276
|
+
writeFileSync(
|
|
277
|
+
temporary,
|
|
278
|
+
Buffer.concat([Buffer.from("PMB1"), iv, cipher.getAuthTag(), encrypted]),
|
|
279
|
+
{ flag: "wx", mode: 384 }
|
|
280
|
+
);
|
|
281
|
+
const handle = openSync(temporary, "r");
|
|
282
|
+
try {
|
|
283
|
+
fsyncSync(handle);
|
|
284
|
+
} finally {
|
|
285
|
+
closeSync(handle);
|
|
286
|
+
}
|
|
287
|
+
linkSync(temporary, destination);
|
|
288
|
+
const directory = openSync(dirname(destination), "r");
|
|
289
|
+
try {
|
|
290
|
+
fsyncSync(directory);
|
|
291
|
+
} finally {
|
|
292
|
+
closeSync(directory);
|
|
293
|
+
}
|
|
294
|
+
} finally {
|
|
295
|
+
for (const file of [
|
|
296
|
+
temporary,
|
|
297
|
+
snapshot,
|
|
298
|
+
`${snapshot}-wal`,
|
|
299
|
+
`${snapshot}-shm`
|
|
300
|
+
]) {
|
|
301
|
+
rmSync(file, { force: true });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async function restoreRecoveryBundle(source, config, key) {
|
|
306
|
+
let manifest;
|
|
307
|
+
try {
|
|
308
|
+
const bundle = readFileSync(source);
|
|
309
|
+
if (key.length !== 32 || bundle.subarray(0, 4).toString() !== "PMB1") {
|
|
310
|
+
throw new Error("Invalid header");
|
|
311
|
+
}
|
|
312
|
+
const decipher = createDecipheriv(
|
|
313
|
+
"aes-256-gcm",
|
|
314
|
+
key,
|
|
315
|
+
bundle.subarray(4, 16)
|
|
316
|
+
);
|
|
317
|
+
decipher.setAAD(Buffer.from("pmcp-mailbox-recovery-v1"));
|
|
318
|
+
decipher.setAuthTag(bundle.subarray(16, 32));
|
|
319
|
+
manifest = JSON.parse(
|
|
320
|
+
Buffer.concat([
|
|
321
|
+
decipher.update(bundle.subarray(32)),
|
|
322
|
+
decipher.final()
|
|
323
|
+
]).toString("utf8")
|
|
324
|
+
);
|
|
325
|
+
if (manifest.version !== 1 || !Number.isInteger(manifest.schemaVersion) || manifest.schemaVersion < 1 || manifest.schemaVersion > SCHEMA_VERSION || manifest.projectId !== config.projectId || hash(Buffer.from(manifest.database, "base64")) !== manifest.databaseSha256) {
|
|
326
|
+
throw new Error("Invalid manifest");
|
|
327
|
+
}
|
|
328
|
+
} catch {
|
|
329
|
+
throw new MailboxError(
|
|
330
|
+
"invalid_recovery_bundle",
|
|
331
|
+
"Recovery bundle cannot be authenticated or validated"
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
const directory = dirname(config.databasePath);
|
|
335
|
+
mkdirSync(directory, { mode: 448 });
|
|
336
|
+
const snapshot = join(directory, ".snapshot.sqlite");
|
|
337
|
+
let store;
|
|
338
|
+
try {
|
|
339
|
+
writeFileSync(snapshot, Buffer.from(manifest.database, "base64"), {
|
|
340
|
+
flag: "wx",
|
|
341
|
+
mode: 384
|
|
342
|
+
});
|
|
343
|
+
await restoreBackup(snapshot, config);
|
|
344
|
+
store = await MailboxStore.open(config);
|
|
345
|
+
const actors = await store.run(
|
|
346
|
+
() => store.database.all("SELECT id FROM mailbox_actors ORDER BY id"),
|
|
347
|
+
false
|
|
348
|
+
);
|
|
349
|
+
const credentials = [];
|
|
350
|
+
for (const actor of actors) {
|
|
351
|
+
credentials.push(await store.rotateActor(actor.id));
|
|
352
|
+
}
|
|
353
|
+
await store.close();
|
|
354
|
+
unlinkSync(snapshot);
|
|
355
|
+
return credentials;
|
|
356
|
+
} catch (error) {
|
|
357
|
+
await store?.close();
|
|
358
|
+
rmSync(directory, { recursive: true, force: true });
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
export {
|
|
363
|
+
HEARTBEAT_INTERVAL_MS,
|
|
364
|
+
MailboxError,
|
|
365
|
+
MailboxService,
|
|
366
|
+
MailboxStore,
|
|
367
|
+
NotificationOutbox,
|
|
368
|
+
PRESENCE_TTL_MS,
|
|
369
|
+
attachMailbox,
|
|
370
|
+
createRecoveryBundle,
|
|
371
|
+
failure,
|
|
372
|
+
inboxSchema,
|
|
373
|
+
initializeMailboxConfig,
|
|
374
|
+
mailboxConfigFrom,
|
|
375
|
+
messageSchema,
|
|
376
|
+
operationSchema,
|
|
377
|
+
readBinding,
|
|
378
|
+
readCredential,
|
|
379
|
+
registerMailboxTools,
|
|
380
|
+
restoreBackup,
|
|
381
|
+
restoreRecoveryBundle,
|
|
382
|
+
sendSchema,
|
|
383
|
+
sessionsSchema,
|
|
384
|
+
writeBindingFile,
|
|
385
|
+
writePrivateJson
|
|
386
|
+
};
|
|
387
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -1,6 +1,125 @@
|
|
|
1
|
-
import { S as SkillEntry, M as MetadataValue, C as CatalogOptions, A as AssetKind } from './catalog-BU5VLLyd.js';
|
|
2
1
|
import { z } from 'zod';
|
|
3
2
|
|
|
3
|
+
/**
|
|
4
|
+
* SKILL.md frontmatter, read as YAML the way a host reads it, and checked against
|
|
5
|
+
* the Agent Skills specification (https://agentskills.io/specification).
|
|
6
|
+
*/
|
|
7
|
+
type MetadataLeaf = string | readonly string[];
|
|
8
|
+
/** A metadata value: a leaf, or a map of leaves one level below the metadata key. */
|
|
9
|
+
type MetadataValue = MetadataLeaf | {
|
|
10
|
+
readonly [key: string]: MetadataLeaf;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Parsed frontmatter. A value with an unquoted ": " is a common authoring slip
|
|
14
|
+
* the client guide says to recover from by quoting it, so that is retried once.
|
|
15
|
+
*/
|
|
16
|
+
declare function frontmatterObject(source: string): Record<string, unknown> | null;
|
|
17
|
+
declare const NAME_MAX = 64;
|
|
18
|
+
declare const DESCRIPTION_MAX = 1024;
|
|
19
|
+
declare const COMPATIBILITY_MAX = 500;
|
|
20
|
+
/**
|
|
21
|
+
* What the specification requires of one SKILL.md, as messages; empty when valid.
|
|
22
|
+
* directory is the name of the folder holding it.
|
|
23
|
+
*/
|
|
24
|
+
declare function specProblems(front: Record<string, unknown> | null, directory: string): string[];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The skills the installed packages brought with them.
|
|
28
|
+
*
|
|
29
|
+
* Derived from `node_modules` at read time rather than from a list somebody
|
|
30
|
+
* maintains: installing a package is how its skill arrives and uninstalling it
|
|
31
|
+
* is how the skill leaves, so the catalog is always true of the project.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
interface SkillEntry {
|
|
35
|
+
/** `<package>/<slug>`. Unique, and stable while the package is installed. */
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly package: string;
|
|
38
|
+
readonly slug: string;
|
|
39
|
+
/** Frontmatter description. The only text `find` ranks against. */
|
|
40
|
+
readonly description: string;
|
|
41
|
+
/** Absolute path to the SKILL.md. `call` reads the body from here. */
|
|
42
|
+
readonly path: string;
|
|
43
|
+
/** open, free or paid. Set for marketplace skills, absent for packages. */
|
|
44
|
+
readonly tier?: string;
|
|
45
|
+
/** The frontmatter `metadata:` block; maps such as `requires` are kept one level down. */
|
|
46
|
+
readonly metadata?: Readonly<Record<string, MetadataValue>>;
|
|
47
|
+
/** Extra search terms. Ranked with the description, never used as triggers. */
|
|
48
|
+
readonly keywords?: readonly string[];
|
|
49
|
+
/** What the entry is. Absent means a skill. */
|
|
50
|
+
readonly kind?: AssetKind;
|
|
51
|
+
/**
|
|
52
|
+
* What describe and read may open: a directory, or one file for an asset
|
|
53
|
+
* that is a single file. Absent means the directory holding `path`.
|
|
54
|
+
*/
|
|
55
|
+
readonly root?: string;
|
|
56
|
+
}
|
|
57
|
+
/** Everything the catalog serves. Commands are skills; workflows have no kind of their own. */
|
|
58
|
+
declare const ASSET_KINDS: readonly ["skill", "agent", "hook", "mcp"];
|
|
59
|
+
type AssetKind = (typeof ASSET_KINDS)[number];
|
|
60
|
+
declare const kindOf: (entry: Pick<SkillEntry, "kind">) => AssetKind;
|
|
61
|
+
interface CatalogOptions {
|
|
62
|
+
/** Directories to walk. Each is a `node_modules` root, not a package root. */
|
|
63
|
+
readonly roots: readonly string[];
|
|
64
|
+
/** Package name prefixes to consider. Empty means every package. */
|
|
65
|
+
readonly scopes?: readonly string[];
|
|
66
|
+
/** Marketplace directories, each holding .claude-plugin/marketplace.json. */
|
|
67
|
+
readonly marketplaces?: readonly string[];
|
|
68
|
+
/** Package directories read directly, for a package no node_modules links. */
|
|
69
|
+
readonly packages?: readonly string[];
|
|
70
|
+
/** Workspace roots: the root and every member its workspaces globs name. */
|
|
71
|
+
readonly workspaces?: readonly string[];
|
|
72
|
+
/** Told about every marketplace skill left out, and why. */
|
|
73
|
+
readonly onReject?: (rejection: {
|
|
74
|
+
path: string;
|
|
75
|
+
reason: string;
|
|
76
|
+
}) => void;
|
|
77
|
+
/**
|
|
78
|
+
* Told about a skill that is served but breaks the Agent Skills specification,
|
|
79
|
+
* or that lost a name collision. Loading stays lenient, as the client guide asks.
|
|
80
|
+
*/
|
|
81
|
+
readonly onWarning?: (warning: {
|
|
82
|
+
path: string;
|
|
83
|
+
reason: string;
|
|
84
|
+
}) => void;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Directories a walk does not enter. Kept as short as the evidence allows: a
|
|
88
|
+
* name here is a package whose skill will never be found, and the cost of one
|
|
89
|
+
* extra readdir is far below the cost of missing a skill. Measured 20260906 --
|
|
90
|
+
* `lib` was on this list and hid playwright's `lib/skill/SKILL.md`, which is
|
|
91
|
+
* the most widely installed skill in the real world.
|
|
92
|
+
*/
|
|
93
|
+
declare const SKIPPED_DIRS: ReadonlySet<string>;
|
|
94
|
+
/**
|
|
95
|
+
* How deep below a package root a SKILL.md is looked for. Three reaches every
|
|
96
|
+
* convention in use (skills, .agent/skills, .claude/skills, .gemini/skills,
|
|
97
|
+
* .codex/skills); four was measured to find nothing more.
|
|
98
|
+
*/
|
|
99
|
+
declare const MAX_SKILL_DEPTH = 3;
|
|
100
|
+
/**
|
|
101
|
+
* Top-level scalar fields of a SKILL.md frontmatter, parsed as YAML, so a folded
|
|
102
|
+
* `description: >-` arrives as its text rather than as the indicator.
|
|
103
|
+
*/
|
|
104
|
+
declare function readFrontmatter(source: string): Record<string, string>;
|
|
105
|
+
/** The body after the frontmatter, or the whole file when there is none. */
|
|
106
|
+
declare function readBody(source: string): string;
|
|
107
|
+
/**
|
|
108
|
+
* Every skill reachable from the given roots, sorted by name.
|
|
109
|
+
*
|
|
110
|
+
* A package with no skills directory contributes nothing and costs one
|
|
111
|
+
* `existsSync` per convention. A skill with no description is skipped rather
|
|
112
|
+
* than listed:
|
|
113
|
+
* `find` ranks descriptions, so one without a description can only ever be
|
|
114
|
+
* found by already knowing its name, and listing it would spend a session's
|
|
115
|
+
* tokens on an entry that cannot answer.
|
|
116
|
+
*/
|
|
117
|
+
declare function readCatalog(options: CatalogOptions): SkillEntry[];
|
|
118
|
+
/** A catalog entry without its body, which is what `catalog` returns. */
|
|
119
|
+
declare function withoutBody({ path, ...rest }: SkillEntry): Omit<SkillEntry, "path">;
|
|
120
|
+
/** Path relative to a root, for a message a reader can act on. */
|
|
121
|
+
declare function displayPath(root: string, entry: SkillEntry): string;
|
|
122
|
+
|
|
4
123
|
/**
|
|
5
124
|
* The intent key and the cache contract.
|
|
6
125
|
*
|
|
@@ -56,6 +175,7 @@ interface FindResult {
|
|
|
56
175
|
*/
|
|
57
176
|
interface Embedder {
|
|
58
177
|
readonly modelId: string;
|
|
178
|
+
readonly indexKey?: string;
|
|
59
179
|
readonly dims: number;
|
|
60
180
|
/** Encodes an intent, the query side. */
|
|
61
181
|
embed(text: string): Promise<Float32Array>;
|
|
@@ -233,7 +353,7 @@ declare const catalogItemSchema: z.ZodObject<{
|
|
|
233
353
|
antigravity: "antigravity";
|
|
234
354
|
}>>>;
|
|
235
355
|
verified: z.ZodOptional<z.ZodBoolean>;
|
|
236
|
-
}, z.core.$
|
|
356
|
+
}, z.core.$loose>;
|
|
237
357
|
declare const catalogResponseSchema: z.ZodObject<{
|
|
238
358
|
count: z.ZodNumber;
|
|
239
359
|
page: z.ZodNumber;
|
|
@@ -259,21 +379,21 @@ declare const catalogResponseSchema: z.ZodObject<{
|
|
|
259
379
|
antigravity: "antigravity";
|
|
260
380
|
}>>>;
|
|
261
381
|
verified: z.ZodOptional<z.ZodBoolean>;
|
|
262
|
-
}, z.core.$
|
|
263
|
-
}, z.core.$
|
|
264
|
-
}, z.core.$
|
|
382
|
+
}, z.core.$loose>>;
|
|
383
|
+
}, z.core.$loose>>;
|
|
384
|
+
}, z.core.$loose>;
|
|
265
385
|
declare const findResultSchema: z.ZodObject<{
|
|
266
386
|
ranking: z.ZodEnum<{
|
|
267
387
|
semantic: "semantic";
|
|
268
388
|
lexical: "lexical";
|
|
269
389
|
}>;
|
|
270
|
-
runtime: z.ZodNullable<z.ZodEnum<{
|
|
390
|
+
runtime: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
271
391
|
"claude-code": "claude-code";
|
|
272
392
|
"codex-cli": "codex-cli";
|
|
273
393
|
"gemini-cli": "gemini-cli";
|
|
274
394
|
"grok-cli": "grok-cli";
|
|
275
395
|
antigravity: "antigravity";
|
|
276
|
-
}
|
|
396
|
+
}>>>;
|
|
277
397
|
matches: z.ZodArray<z.ZodObject<{
|
|
278
398
|
name: z.ZodString;
|
|
279
399
|
package: z.ZodString;
|
|
@@ -287,8 +407,8 @@ declare const findResultSchema: z.ZodObject<{
|
|
|
287
407
|
antigravity: "antigravity";
|
|
288
408
|
}>>>;
|
|
289
409
|
verified: z.ZodOptional<z.ZodBoolean>;
|
|
290
|
-
}, z.core.$
|
|
291
|
-
}, z.core.$
|
|
410
|
+
}, z.core.$loose>>;
|
|
411
|
+
}, z.core.$loose>;
|
|
292
412
|
declare const describeResponseSchema: z.ZodObject<{
|
|
293
413
|
name: z.ZodString;
|
|
294
414
|
kind: z.ZodEnum<{
|
|
@@ -300,15 +420,15 @@ declare const describeResponseSchema: z.ZodObject<{
|
|
|
300
420
|
package: z.ZodString;
|
|
301
421
|
description: z.ZodString;
|
|
302
422
|
tier: z.ZodOptional<z.ZodString>;
|
|
303
|
-
metadata: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.
|
|
423
|
+
metadata: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodReadonly<z.ZodArray<z.ZodString>>]>>;
|
|
304
424
|
frontmatter: z.ZodString;
|
|
305
425
|
files: z.ZodArray<z.ZodObject<{
|
|
306
426
|
path: z.ZodString;
|
|
307
427
|
bytes: z.ZodNumber;
|
|
308
428
|
sha256: z.ZodString;
|
|
309
|
-
}, z.core.$
|
|
429
|
+
}, z.core.$loose>>;
|
|
310
430
|
filesTruncated: z.ZodBoolean;
|
|
311
|
-
}, z.core.$
|
|
431
|
+
}, z.core.$loose>;
|
|
312
432
|
declare const bundleResponseSchema: z.ZodObject<{
|
|
313
433
|
name: z.ZodString;
|
|
314
434
|
truncated: z.ZodBoolean;
|
|
@@ -318,8 +438,8 @@ declare const bundleResponseSchema: z.ZodObject<{
|
|
|
318
438
|
sha256: z.ZodString;
|
|
319
439
|
uri: z.ZodString;
|
|
320
440
|
mimeType: z.ZodString;
|
|
321
|
-
}, z.core.$
|
|
322
|
-
}, z.core.$
|
|
441
|
+
}, z.core.$loose>>;
|
|
442
|
+
}, z.core.$loose>;
|
|
323
443
|
type CatalogItem = z.infer<typeof catalogItemSchema>;
|
|
324
444
|
type CatalogResponse = z.infer<typeof catalogResponseSchema>;
|
|
325
445
|
type FindResponse = z.infer<typeof findResultSchema>;
|
|
@@ -421,4 +541,4 @@ declare function createSkillTools(options: SkillServerOptions): {
|
|
|
421
541
|
};
|
|
422
542
|
type SkillTools = ReturnType<typeof createSkillTools>;
|
|
423
543
|
|
|
424
|
-
export {
|
|
544
|
+
export { lexicalScore as $, ASSET_KINDS as A, type BundleFile as B, type CatalogOptions as C, DEFAULT_FIND_LIMIT as D, type Embedder as E, type FileKind as F, type SkillFileListing as G, type SkillFilters as H, type IntentCache as I, bundleResponseSchema as J, type KindFilter as K, bundleSkill as L, type MetadataValue as M, NAME_MAX as N, catalogResponseSchema as O, classify as P, cosine as Q, type Ranking as R, type SkillServerOptions as S, createSkillTools as T, describeResponseSchema as U, detectRuntime as V, displayPath as W, find as X, findResultSchema as Y, isUtf8Text as Z, kindOf as _, type SkillEntry as a, listSkillFiles as a0, mimeOf as a1, normaliseIntent as a2, frontmatterObject as a3, readBody as a4, readCatalog as a5, readFrontmatter as a6, readSkillFile as a7, runtimeOfClient as a8, specProblems as a9, verifiedRuntimesOf as aa, withoutBody as ab, type SkillTools as b, type AssetKind as c, CATALOG_PAGE_MAX as d, CATALOG_PAGE_SIZE as e, COMPATIBILITY_MAX as f, type CallResponse as g, type CatalogItem as h, type CatalogResponse as i, type Classified as j, DESCRIBE_FILE_LIMIT as k, DESCRIPTION_MAX as l, type FindOptions as m, type FindResult as n, type IntentCacheStats as o, MAX_SKILL_DEPTH as p, type Match as q, type MetadataLeaf as r, READ_BUDGET_BYTES as s, RUNTIMES as t, type ReadResult as u, type Runtime as v, SKIPPED_DIRS as w, type SkillBundle as x, type SkillFile as y, type SkillFileContent as z };
|