@webskill/sdk 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/agent.d.ts +3 -3
  2. package/dist/agent.js +2442 -3
  3. package/dist/{dist-Bfga1TmS.js → approval-BnLLCOG0.js} +235 -2932
  4. package/dist/browser.d.ts +106 -5
  5. package/dist/browser.js +364 -10
  6. package/dist/{openUiLibrary-BKXW7Iwx-DjvEnMlJ.js → dist-CTsxblSD.js} +496 -28
  7. package/dist/{echarts-De78wXqV.js → echarts-BE7oV_Dl.js} +1 -1
  8. package/dist/{env-8cY40DXB-CGnEVZby.js → env-Bj1MI2Ww.js} +1 -1
  9. package/dist/errors-BDZNpC13.js +22 -0
  10. package/dist/{eventTypes-FllCrX-Z-DNDeHWoG.js → eventTypes-Y07N8IAC.js} +14 -2
  11. package/dist/external-_ZRQe-V9.js +53 -0
  12. package/dist/governance.d.ts +15 -3
  13. package/dist/governance.js +86 -4
  14. package/dist/{index-DQwGvHAI.d.ts → index-CQYo4tcT.d.ts} +85 -6
  15. package/dist/{index-sd-gVKey.d.ts → index-CU2md8R1.d.ts} +26 -18
  16. package/dist/{index-CWoKOFBP.d.ts → index-Oc3H89TJ.d.ts} +159 -3
  17. package/dist/index.d.ts +4 -4
  18. package/dist/index.js +1001 -5
  19. package/dist/kind-DxgS8LM-.js +77 -0
  20. package/dist/linkedDocument-xNXF-z8n.js +94 -0
  21. package/dist/llm-B7lLH0ZI.js +1767 -0
  22. package/dist/mcp.d.ts +2 -2
  23. package/dist/mcp.js +41 -4
  24. package/dist/memoryArtifactStore-D6dLeY55.js +48 -0
  25. package/dist/miniChart-owdCdaw-.js +533 -0
  26. package/dist/node.d.ts +3 -3
  27. package/dist/node.js +106 -9
  28. package/dist/openUiLibrary-CY_ADoe0.js +30 -0
  29. package/dist/openUiSpecLang-DuOCdw9G.js +1849 -0
  30. package/dist/pathSecurity-B1owvJAF.js +65 -0
  31. package/dist/processSandboxEntry.js +5 -1
  32. package/dist/renderResult-D9Q-Vu2x.js +143 -0
  33. package/dist/{rolldown-runtime-BOF7iYI8.js → rolldown-runtime-BHkdefai.js} +1 -2
  34. package/dist/sandboxWorkerEntry.js +5 -1
  35. package/dist/{dist-D5YhlCdY.js → skill-CAJMsLod.js} +44 -372
  36. package/dist/{skillVersionStore-D-qHk9ZE-C0OFDTQY.d.ts → skillVersionStore-B24Jjjry-C4zomhMZ.d.ts} +14 -2
  37. package/dist/surface-DVGiCmwq.js +145 -0
  38. package/dist/testing.d.ts +1 -1
  39. package/dist/testing.js +64 -3
  40. package/dist/{types-C3V6lAeO-BgeOvc1Y.d.ts → types-C3V6lAeO-BIf7UB2e.d.ts} +17 -1
  41. package/dist/types-TUydnxaj.js +78 -0
  42. package/dist/ui-react.d.ts +12 -5
  43. package/dist/ui-react.js +128548 -4729
  44. package/dist/ui-vue.d.ts +1 -1
  45. package/dist/ui-vue.js +11 -2
  46. package/dist/ui.d.ts +3 -3
  47. package/dist/ui.js +1254 -3
  48. package/dist/urlSafety-CiSuCJvX.js +65 -0
  49. package/dist/webSkillApi-DOOyz2G5.js +106 -0
  50. package/dist/{webskillLitCatalog-D_zCqeQF-swXsWoAe.js → webskillLitCatalog-DSqVFQjK.js} +11 -3
  51. package/package.json +1 -1
  52. package/dist/catalogComponents-BgAJN0p8-Cr55ouOg.js +0 -124384
  53. package/dist/client-BCM6Z3yq-qUQNkKrK.js +0 -7787
  54. package/dist/dist-BnqAkSS6.js +0 -2384
  55. package/dist/dist-CiaDIqkV.js +0 -3509
  56. package/dist/dist-DxyxmXPU-DGC-dpXf.js +0 -5621
  57. package/dist/memoryArtifactStore-52Zn9npI-BxtUkrgV.js +0 -86
  58. package/dist/stdio-CFMoANJJ-BxrTeXh7.js +0 -31
  59. package/dist/testing-Cm8MseLF.js +0 -142
  60. package/dist/types-WovEf4ED-CZSDiiBU.js +0 -6215
@@ -1,31 +1,15 @@
1
+ import { n as messageOf, t as WebSkillError } from "./errors-BDZNpC13.js";
2
+ import { a as atomicWriteText, o as isAtomicTempPath, r as resolveInsideRoot } from "./pathSecurity-B1owvJAF.js";
1
3
  import { parse } from "yaml";
2
4
  import { Unzip, UnzipInflate, zipSync } from "fflate";
3
5
 
4
- //#region ../core/dist/index.js
5
- /**
6
- * 所有公开 API 抛出的结构化错误,code 供上层可编程处理
7
- * @stable
8
- */
9
- var WebSkillError = class extends Error {
10
- code;
11
- details;
12
- constructor(code, message, details) {
13
- super(message);
14
- this.name = "WebSkillError";
15
- this.code = code;
16
- this.details = details;
17
- }
18
- };
19
- /** unknown 异常取消息文本(全仓单一来源,勿再本地重复定义) */
20
- function messageOf(e) {
21
- return e instanceof Error ? e.message : String(e);
22
- }
6
+ //#region ../core/src/skill/manifest.ts
23
7
  /** 技能目录内的安装清单文件名(不参与 files 列表与 digest) */
24
8
  const SKILL_MANIFEST_FILE = "webskill.skill-manifest.json";
25
9
  /** managed root 下的锁定文件名(不参与 files 列表与 digest) */
26
10
  const SKILLS_LOCKFILE = "skills.lock.json";
27
11
  /** 技能目录内的签名文件名(不参与 files 列表与 digest) */
28
- const SKILL_SIGNATURE_FILE = "webskill.skill-signature.json";
12
+ const SKILL_SIGNATURE_FILE$1 = "webskill.skill-signature.json";
29
13
  /**
30
14
  * 不参与 digest 的技能目录内文件。
31
15
  *
@@ -36,7 +20,7 @@ const SKILL_SIGNATURE_FILE = "webskill.skill-signature.json";
36
20
  const MANIFEST_EXCLUDED_FILES = /* @__PURE__ */ new Set([
37
21
  SKILL_MANIFEST_FILE,
38
22
  SKILLS_LOCKFILE,
39
- SKILL_SIGNATURE_FILE
23
+ SKILL_SIGNATURE_FILE$1
40
24
  ]);
41
25
  /** 整体 digest:逐文件 "<path>:<sha256>\n" 拼接串(文件按 path 排序)的 sha256(hash 由调用方提供,可为 WebCrypto 异步实现) */
42
26
  async function computeDigest(files, sha256) {
@@ -75,27 +59,11 @@ function verifyManifest(manifest, actualHashes, actualFiles = [...actualHashes.k
75
59
  missing
76
60
  };
77
61
  }
78
- /**
79
- * 原子写的临时文件名后缀模式;列举/打包侧据此排除中断残留(0.9.0 分册 11)。
80
- * 单一来源:不要在别处手写正则(AC-G10),复制正则必然漂移。
81
- * @stable
82
- */
83
- const ATOMIC_TMP_SUFFIX_PATTERN = /\.tmp-\d+-[a-z0-9]+$/;
84
- /** 该路径是否是 atomicWriteText 留下的临时文件 */
85
- const isAtomicTempPath = (path) => ATOMIC_TMP_SUFFIX_PATTERN.test(path);
86
- /**
87
- * 原子写文本:先写临时文件再 rename(进程崩溃不留下半写文件;lockfile 等账本场景)。
88
- * 原子性强弱取决于 provider 的 rename 实现:NodeFS/MemoryFS 为真 rename;
89
- * OPFS 无原生 move,rename 是 copy+delete(非原子,崩溃可能残留双份),
90
- * 对崩溃一致性要求极高的场景在浏览器侧需知悉此前提。
91
- */
92
- async function atomicWriteText(fs, path, content) {
93
- const tmp = `${path}.tmp-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
94
- await fs.writeText(tmp, content);
95
- await fs.rename(tmp, path);
96
- }
62
+
63
+ //#endregion
64
+ //#region ../core/src/skill/signature.ts
97
65
  /** 技能目录内的签名文件名(与 manifest 同级,不参与 files 列表与 digest) */
98
- const SKILL_SIGNATURE_FILE$1 = "webskill.skill-signature.json";
66
+ const SKILL_SIGNATURE_FILE = "webskill.skill-signature.json";
99
67
  const SIGNATURE_SCHEMA_VERSION = 1;
100
68
  const encoder = new TextEncoder();
101
69
  const toBase64Url = (bytes) => {
@@ -171,7 +139,7 @@ function parseSignature(raw, origin) {
171
139
  }
172
140
  /** 从技能目录读签名文件;文件不存在返回 undefined(缺签名由策略层判定,不是错误) */
173
141
  async function readSkillSignature(fs, skillRoot) {
174
- const path = `${skillRoot}/${SKILL_SIGNATURE_FILE$1}`;
142
+ const path = `${skillRoot}/${SKILL_SIGNATURE_FILE}`;
175
143
  if (!await fs.exists(path)) return void 0;
176
144
  let parsed;
177
145
  try {
@@ -193,7 +161,7 @@ async function verifySkillSignature(input) {
193
161
  const { manifest, signature, trustedKeys, policy } = input;
194
162
  const skill = JSON.stringify(manifest.name);
195
163
  if (!signature) {
196
- if (policy.unsigned === "deny") throw new WebSkillError("SIGNATURE_MISSING", `Skill ${skill} carries no ${SKILL_SIGNATURE_FILE$1} and the unsigned policy is "deny"`);
164
+ if (policy.unsigned === "deny") throw new WebSkillError("SIGNATURE_MISSING", `Skill ${skill} carries no ${SKILL_SIGNATURE_FILE} and the unsigned policy is "deny"`);
197
165
  return {
198
166
  ok: true,
199
167
  unsigned: true,
@@ -279,303 +247,9 @@ var FsTrustedKeyStore = class {
279
247
  await this.#write((await this.#read()).filter((key) => key.keyId !== keyId));
280
248
  }
281
249
  };
282
- const ROOT = "/";
283
- /** 内存文件系统:路径一律规范化为 `/` 分隔,写入自动创建父目录 */
284
- var MemoryFS = class {
285
- kind = "memory";
286
- #entries = /* @__PURE__ */ new Map();
287
- constructor() {
288
- this.#entries.set(ROOT, {
289
- type: "directory",
290
- content: /* @__PURE__ */ new Uint8Array(0),
291
- mtimeMs: Date.now()
292
- });
293
- }
294
- #normalize(path) {
295
- const out = [];
296
- for (const seg of path.replace(/\\/g, "/").split("/")) {
297
- if (seg === "" || seg === ".") continue;
298
- if (seg === "..") out.pop();
299
- else out.push(seg);
300
- }
301
- return ROOT + out.join("/");
302
- }
303
- #parentOf(key) {
304
- const i = key.lastIndexOf("/");
305
- return i <= 0 ? ROOT : key.slice(0, i);
306
- }
307
- #ensureParents(key) {
308
- const chain = [];
309
- let dir = this.#parentOf(key);
310
- while (!this.#entries.has(dir)) {
311
- chain.push(dir);
312
- dir = this.#parentOf(dir);
313
- }
314
- for (const d of chain.reverse()) this.#entries.set(d, {
315
- type: "directory",
316
- content: /* @__PURE__ */ new Uint8Array(0),
317
- mtimeMs: Date.now()
318
- });
319
- }
320
- #getFile(key) {
321
- const entry = this.#entries.get(key);
322
- if (!entry || entry.type !== "file") throw new WebSkillError("FS_NOT_FOUND", `File not found: ${key}`);
323
- return entry;
324
- }
325
- #getDirectory(key) {
326
- const entry = this.#entries.get(key);
327
- if (!entry || entry.type !== "directory") throw new WebSkillError("FS_NOT_FOUND", `Directory not found: ${key}`);
328
- return entry;
329
- }
330
- async readText(path) {
331
- return new TextDecoder().decode(this.#getFile(this.#normalize(path)).content);
332
- }
333
- async writeText(path, content) {
334
- await this.writeBinary(path, new TextEncoder().encode(content));
335
- }
336
- async appendText(path, content) {
337
- const key = this.#normalize(path);
338
- const existing = this.#entries.get(key);
339
- if (existing && existing.type !== "file") throw new WebSkillError("FS_PERMISSION_DENIED", `Cannot append to a directory: ${key}`);
340
- const prior = existing ? new TextDecoder().decode(existing.content) : "";
341
- this.#ensureParents(key);
342
- this.#entries.set(key, {
343
- type: "file",
344
- content: new TextEncoder().encode(prior + content),
345
- mtimeMs: Date.now()
346
- });
347
- }
348
- async readBinary(path) {
349
- return this.#getFile(this.#normalize(path)).content;
350
- }
351
- async writeBinary(path, content) {
352
- const key = this.#normalize(path);
353
- this.#ensureParents(key);
354
- this.#entries.set(key, {
355
- type: "file",
356
- content,
357
- mtimeMs: Date.now()
358
- });
359
- }
360
- async exists(path) {
361
- return this.#entries.has(this.#normalize(path));
362
- }
363
- async stat(path) {
364
- const key = this.#normalize(path);
365
- const entry = this.#entries.get(key);
366
- if (!entry) throw new WebSkillError("FS_NOT_FOUND", `Path not found: ${key}`);
367
- return {
368
- path: key,
369
- type: entry.type,
370
- size: entry.type === "file" ? entry.content.length : void 0,
371
- mtimeMs: entry.mtimeMs
372
- };
373
- }
374
- async list(path) {
375
- const key = this.#normalize(path);
376
- this.#getDirectory(key);
377
- const prefix = key === ROOT ? ROOT : key + "/";
378
- const out = [];
379
- for (const [k, entry] of this.#entries) {
380
- if (k === key || !k.startsWith(prefix)) continue;
381
- if (k.slice(prefix.length).includes("/")) continue;
382
- out.push({
383
- path: k,
384
- type: entry.type,
385
- size: entry.type === "file" ? entry.content.length : void 0,
386
- mtimeMs: entry.mtimeMs
387
- });
388
- }
389
- return out;
390
- }
391
- async mkdir(path) {
392
- const key = this.#normalize(path);
393
- if (this.#entries.has(key)) return;
394
- this.#ensureParents(key);
395
- this.#entries.set(key, {
396
- type: "directory",
397
- content: /* @__PURE__ */ new Uint8Array(0),
398
- mtimeMs: Date.now()
399
- });
400
- }
401
- async remove(path, options) {
402
- const key = this.#normalize(path);
403
- if (!this.#entries.get(key)) throw new WebSkillError("FS_NOT_FOUND", `Path not found: ${key}`);
404
- if (key === ROOT) throw new WebSkillError("FS_PATH_OUTSIDE_ROOT", "Cannot delete root directory");
405
- const prefix = key + "/";
406
- const descendants = [...this.#entries.keys()].filter((k) => k.startsWith(prefix));
407
- if (descendants.length > 0 && !options?.recursive) throw new WebSkillError("FS_PATH_OUTSIDE_ROOT", `Directory not empty, use recursive: ${key}`);
408
- for (const k of descendants) this.#entries.delete(k);
409
- this.#entries.delete(key);
410
- }
411
- async rename(from, to) {
412
- const src = this.#normalize(from);
413
- const dst = this.#normalize(to);
414
- const entry = this.#entries.get(src);
415
- if (!entry) throw new WebSkillError("FS_NOT_FOUND", `Path not found: ${src}`);
416
- const prefix = src + "/";
417
- const descendants = [...this.#entries.entries()].filter(([k]) => k.startsWith(prefix));
418
- this.#ensureParents(dst);
419
- this.#entries.set(dst, entry);
420
- for (const [k, v] of descendants) this.#entries.set(dst + k.slice(src.length), v);
421
- for (const [k] of descendants) this.#entries.delete(k);
422
- this.#entries.delete(src);
423
- }
424
- };
425
- /** 统一分隔符为 `/`,去除 `.` 段与重复分隔符(不解析 `..`) */
426
- function normalizePath(path) {
427
- return path.replace(/\\/g, "/").split("/").filter((s) => s !== "" && s !== ".").join("/");
428
- }
429
- /**
430
- * 外部标识(runId、candidateId、skillName、versionId 等)作为单一路径段使用前的统一校验:
431
- * 拒绝空串、`.`、`..`、含 `/` 或 `\`、含 `:`(Windows 盘符/ADS)。
432
- * 违规抛 FS_PATH_OUTSIDE_ROOT(kind 用于错误消息定位,如 "runId")。
433
- */
434
- const CONTROL_CHARS_RE = /[\x00-\x1f\x7f]/;
435
- function assertSafePathSegment(segment, kind) {
436
- if (CONTROL_CHARS_RE.test(segment)) throw new WebSkillError("FS_PATH_OUTSIDE_ROOT", `Invalid ${kind} (control characters including NUL are not allowed): ${JSON.stringify(segment)}`);
437
- if (segment === "" || segment === "." || segment === ".." || segment.includes("/") || segment.includes("\\") || segment.includes(":")) throw new WebSkillError("FS_PATH_OUTSIDE_ROOT", `Invalid ${kind} (must be a single safe path segment): ${JSON.stringify(segment)}`);
438
- }
439
- /**
440
- * 将相对路径安全地解析到 root 之内。
441
- * 拒绝空路径、绝对路径(`/`、`\\`、`C:\` 形式)与任何 `..` 段。
442
- *
443
- * 注意:本函数仅做**词法**校验,不解析符号链接——root 内经符号链接指向
444
- * 外部的目标无法被词法检查拦截(realpath 防护见 NodeFS root 模式与
445
- * 安装管线的 lstat 符号链接扫描)。
446
- */
447
- function resolveInsideRoot(root, relativePath) {
448
- const fail = (reason) => {
449
- throw new WebSkillError("FS_PATH_OUTSIDE_ROOT", `Path outside root: ${reason} (path: ${JSON.stringify(relativePath)})`);
450
- };
451
- if (relativePath.trim() === "") fail("empty path");
452
- if (relativePath.startsWith("/") || relativePath.startsWith("\\") || /^[a-zA-Z]:[\\/]/.test(relativePath)) fail("absolute paths not allowed");
453
- const segments = relativePath.replace(/\\/g, "/").split("/");
454
- if (CONTROL_CHARS_RE.test(relativePath)) fail("control characters including NUL are not allowed");
455
- if (segments.some((s) => s === "..")) fail("`..` segments not allowed");
456
- const clean = segments.filter((s) => s !== "" && s !== ".");
457
- if (clean.length === 0) fail("path resolves to empty");
458
- const normalizedRoot = normalizePath(root);
459
- const joined = normalizedRoot === "" ? clean.join("/") : `${normalizedRoot}/${clean.join("/")}`;
460
- return root.startsWith("/") || root.startsWith("\\") ? `/${joined}` : joined;
461
- }
462
- const LOOPBACK_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "localhost."]);
463
- function ipv4Octets(host) {
464
- const parts = host.split(".");
465
- if (parts.length !== 4) return void 0;
466
- const octets = parts.map((p) => /^\d{1,3}$/.test(p) ? Number(p) : NaN);
467
- return octets.every((n) => n >= 0 && n <= 255) ? octets : void 0;
468
- }
469
- function isPrivateIpv4(octets) {
470
- const [a, b] = octets;
471
- return a === 0 || a === 10 || a === 127 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 169 && b === 254;
472
- }
473
- function isPrivateIpv6(host) {
474
- const h = host.toLowerCase();
475
- if (h === "::1" || h === "::") return true;
476
- const mapped = h.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
477
- if (mapped) {
478
- const octets = ipv4Octets(mapped[1]);
479
- return octets !== void 0 && isPrivateIpv4(octets);
480
- }
481
- const mappedHex = h.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
482
- if (mappedHex) {
483
- const hi = parseInt(mappedHex[1], 16);
484
- const lo = parseInt(mappedHex[2], 16);
485
- return isPrivateIpv4([
486
- hi >> 8,
487
- hi & 255,
488
- lo >> 8,
489
- lo & 255
490
- ]);
491
- }
492
- const first = h.split(":")[0];
493
- if (/^[0-9a-f]{1,4}$/.test(first)) {
494
- const n = parseInt(first, 16);
495
- if ((n & 65472) === 65152) return true;
496
- if ((n & 65024) === 64512) return true;
497
- }
498
- return false;
499
- }
500
- function isPrivateHost(hostname) {
501
- const host = hostname.toLowerCase();
502
- if (LOOPBACK_HOSTNAMES.has(host) || host.endsWith(".localhost")) return true;
503
- const v4 = ipv4Octets(host);
504
- if (v4) return isPrivateIpv4(v4);
505
- if (host.startsWith("[") && host.endsWith("]")) return isPrivateIpv6(host.slice(1, -1));
506
- if (host.includes(":")) return isPrivateIpv6(host);
507
- return false;
508
- }
509
- /**
510
- * 校验远程 URL 是否允许连接;违规抛 NETWORK_BLOCKED。
511
- * rawUrl 非合法 URL 时抛 TypeError(由调用方按自身错误码包装)。
512
- */
513
- function assertRemoteUrlAllowed(rawUrl, policy = {}) {
514
- const url = new URL(rawUrl);
515
- if (url.protocol === "http:") {
516
- if (!policy.allowHttp) throw new WebSkillError("NETWORK_BLOCKED", `Insecure URL scheme "http:" is not allowed (opt in via allowHttp): ${url.href}`);
517
- } else if (url.protocol !== "https:") throw new WebSkillError("NETWORK_BLOCKED", `URL scheme "${url.protocol}" is not allowed (https only): ${url.href}`);
518
- if (!policy.allowPrivateHosts && isPrivateHost(url.hostname)) throw new WebSkillError("NETWORK_BLOCKED", `Refusing to connect to a private/loopback/link-local address: ${url.hostname}`);
519
- return url;
520
- }
521
- /** 可作为图片分片外发的 MIME 类型 @experimental */
522
- const IMAGE_MIME_TYPES = [
523
- "image/png",
524
- "image/jpeg",
525
- "image/webp",
526
- "image/gif"
527
- ];
528
- /**
529
- * 可按文本读取的扩展名。accept 白名单与判定白名单共用这一份——
530
- * 0.5.x 两处不一致,`log`/`yaml`/`yml` 能通过判定却在文件选择器里选不到。
531
- */
532
- const TEXT_EXTENSIONS = [
533
- "txt",
534
- "md",
535
- "csv",
536
- "json",
537
- "log",
538
- "yaml",
539
- "yml"
540
- ];
541
- /** 非文本非图片但仍可整体外发的类型(provider 的 file 分片) @experimental */
542
- const FILE_MIME_TYPES = ["application/pdf"];
543
- /**
544
- * 需要**客户端先抽取成文本**才能外发的类型(FR-23.9 / 0.13.0 FR-12.5)。
545
- * 不走直通的 `file`:docx / xlsx 发给 provider 会被当成二进制垃圾。
546
- */
547
- const DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
548
- const XLSX_MIME = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
549
- /** 文本类附件进模型上下文的字符上限(0.6.0) @experimental */
550
- const ATTACHMENT_TEXT_LIMIT = 32 * 1024;
551
- const TEXT_EXTENSION_SET = new Set(TEXT_EXTENSIONS);
552
- const IMAGE_MIME_SET = new Set(IMAGE_MIME_TYPES);
553
- const FILE_MIME_SET = new Set(FILE_MIME_TYPES);
554
- const EXTRACTED_MIME_SET = /* @__PURE__ */ new Set([DOCX_MIME, XLSX_MIME]);
555
- const EXTRACTED_EXTENSION_SET = /* @__PURE__ */ new Set(["docx", "xlsx"]);
556
- function extensionOf(fileName) {
557
- return fileName.split(".").pop()?.toLowerCase() ?? "";
558
- }
559
- /** 分类集中一处,不散落在各 UI 分支里 @experimental */
560
- function classifyAttachment(contentType, fileName) {
561
- const type = contentType.toLowerCase();
562
- if (IMAGE_MIME_SET.has(type)) return "image";
563
- if (EXTRACTED_MIME_SET.has(type)) return "document-text";
564
- if (FILE_MIME_SET.has(type)) return "file";
565
- if (type.startsWith("text/") || type === "application/json") return "text";
566
- if (type === "" && TEXT_EXTENSION_SET.has(extensionOf(fileName))) return "text";
567
- if (type === "" && EXTRACTED_EXTENSION_SET.has(extensionOf(fileName))) return "document-text";
568
- }
569
- /**
570
- * 文本类附件进模型上下文的最终形态。两条入口共用,差别只有出处那一段(0.14.0 D-20-6)。
571
- * @experimental
572
- */
573
- function formatAttachmentText(input) {
574
- const clipped = input.body.length > 32768 ? `${input.body.slice(0, ATTACHMENT_TEXT_LIMIT)}\n[truncated]` : input.body;
575
- const bodyOnly = input.kind === "document-text" ? ", body text only" : "";
576
- const from = input.origin === "download" ? ", from this device's downloads" : "";
577
- return `--- Attachment: ${input.name} (${input.contentType}${bodyOnly}${from}) ---\n${clipped}`;
578
- }
250
+
251
+ //#endregion
252
+ //#region ../core/src/skill/parser.ts
579
253
  const FRONTMATTER_RE = /^---[^\S\r\n]*\r?\n([\s\S]*?)\r?\n---[^\S\r\n]*(?:\r?\n|$)([\s\S]*)$/;
580
254
  const invalid = (message, details) => {
581
255
  throw new WebSkillError("SKILL_INVALID_METADATA", message, details);
@@ -599,12 +273,18 @@ function parseSkillMarkdown(raw) {
599
273
  body: bodyRaw.trim()
600
274
  };
601
275
  }
276
+
277
+ //#endregion
278
+ //#region ../core/src/skill/naming.ts
602
279
  const SKILL_NAME_MAX_LENGTH = 64;
603
280
  /** Agent Skills 官方命名规范:小写字母/数字/连字符,不允许首尾或连续连字符 */
604
281
  const SKILL_NAME_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
605
282
  function isValidSkillName(name) {
606
283
  return name.length <= 64 && SKILL_NAME_PATTERN.test(name);
607
284
  }
285
+
286
+ //#endregion
287
+ //#region ../core/src/skill/rules.ts
608
288
  const SCRIPT_EXTENSION_RE = /\.(ts|js)$/;
609
289
  /** D2 schema sidecar(scripts/<name>.schema.json)属于协议内文件,不算非法脚本 */
610
290
  const SCHEMA_SIDECAR_RE = /\.schema\.json$/;
@@ -707,6 +387,9 @@ function checkDependencyCycles(adjacency) {
707
387
  involved
708
388
  };
709
389
  }
390
+
391
+ //#endregion
392
+ //#region ../core/src/skill/skillPack.ts
710
393
  /**
711
394
  * 技能包集(skill pack)格式与打包逻辑(node/browser 共用单一来源):
712
395
  * webskill.skill-pack.json { schemaVersion: 1, skills: [{ name, digest }] }
@@ -780,6 +463,9 @@ function parseSkillPackManifest(text) {
780
463
  }
781
464
  return raw;
782
465
  }
466
+
467
+ //#endregion
468
+ //#region ../core/src/skill/archiveShape.ts
783
469
  const SKILL_FILE = "SKILL.md";
784
470
  function normalize(path) {
785
471
  return path.replace(/^\.?\//, "");
@@ -812,7 +498,7 @@ function detectSkillArchiveShape(entries) {
812
498
  if (!hasLooseRootFile && topLevels.size === 1) {
813
499
  const [dir] = [...topLevels];
814
500
  if (dir !== void 0) {
815
- if (paths.includes(`${dir}/webskill.skill-pack.json`)) return {
501
+ if (paths.includes(`${dir}/${"webskill.skill-pack.json"}`)) return {
816
502
  shape: "pack-set",
817
503
  rootDir: dir
818
504
  };
@@ -856,11 +542,14 @@ async function detectSkillArchiveShapeFromFs(fs, contentDir) {
856
542
  continue;
857
543
  }
858
544
  if (await fs.exists(`${entry.path}/${SKILL_FILE}`)) sample.push(`${name}/${SKILL_FILE}`);
859
- else if (await fs.exists(`${entry.path}/webskill.skill-pack.json`)) sample.push(`${name}/${SKILL_PACK_FILE}`);
545
+ else if (await fs.exists(`${entry.path}/${"webskill.skill-pack.json"}`)) sample.push(`${name}/${SKILL_PACK_FILE}`);
860
546
  else sample.push(`${name}/`);
861
547
  }
862
548
  return detectSkillArchiveShape(sample);
863
549
  }
550
+
551
+ //#endregion
552
+ //#region ../core/src/skill/zipLimits.ts
864
553
  const DEFAULT_ARCHIVE_LIMITS = {
865
554
  maxDownloadBytes: 64 * 1024 * 1024,
866
555
  maxEntryBytes: 64 * 1024 * 1024,
@@ -970,10 +659,16 @@ async function readResponseWithLimit(res, limits) {
970
659
  }
971
660
  return concat(chunks, size);
972
661
  }
662
+
663
+ //#endregion
664
+ //#region ../core/src/skill/catalog.ts
973
665
  /** 由条目列表构建 Catalog,保证按名称排序 */
974
666
  function buildCatalog(entries) {
975
667
  return { entries: [...entries].sort((a, b) => a.name.localeCompare(b.name)) };
976
668
  }
669
+
670
+ //#endregion
671
+ //#region ../core/src/skill/reader.ts
977
672
  /** 按技能名读取技能根目录内的文件,全部读取强制经过路径安全检查 */
978
673
  var SkillReader = class {
979
674
  #fs;
@@ -997,6 +692,9 @@ var SkillReader = class {
997
692
  return this.#fs.readBinary(resolveInsideRoot(this.#rootFor(skillName), relativePath));
998
693
  }
999
694
  };
695
+
696
+ //#endregion
697
+ //#region ../core/src/skill/discovery.ts
1000
698
  const baseName = (path) => path.split("/").pop() ?? path;
1001
699
  var SkillDiscovery = class {
1002
700
  #fs;
@@ -1143,6 +841,9 @@ var SkillDiscovery = class {
1143
841
  return (await this.#fs.list(scriptsDir)).filter((s) => s.type === "file").map((s) => baseName(s.path));
1144
842
  }
1145
843
  };
844
+
845
+ //#endregion
846
+ //#region ../core/src/skill/validator.ts
1146
847
  /**
1147
848
  * 合规校验。内部直接复用 SkillDiscovery 的扫描逻辑,
1148
849
  * 规则判定同样落在 checkSkillRules 单一来源上。
@@ -1157,35 +858,6 @@ async function validateSkills(fs, roots, options) {
1157
858
  issues
1158
859
  };
1159
860
  }
1160
- function renderCatalogJson(catalog) {
1161
- return JSON.stringify(catalog, null, 2);
1162
- }
1163
- const jsonRenderer = {
1164
- format: "json",
1165
- render: renderCatalogJson
1166
- };
1167
- /** 转义 XML 五个特殊字符:& < > " ' */
1168
- function escapeXml(text) {
1169
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
1170
- }
1171
- function renderAvailableSkillsXml(catalog) {
1172
- const lines = ["<available_skills>"];
1173
- for (const skill of catalog.entries) {
1174
- lines.push(" <skill>");
1175
- lines.push(` <name>${escapeXml(skill.name)}</name>`);
1176
- lines.push(` <description>${escapeXml(skill.description)}</description>`);
1177
- if (skill.version !== void 0) lines.push(` <version>${escapeXml(skill.version)}</version>`);
1178
- if (skill.license !== void 0) lines.push(` <license>${escapeXml(skill.license)}</license>`);
1179
- lines.push(` <source>${escapeXml(skill.source)}</source>`);
1180
- lines.push(" </skill>");
1181
- }
1182
- lines.push("</available_skills>");
1183
- return lines.join("\n");
1184
- }
1185
- const xmlRenderer = {
1186
- format: "xml",
1187
- render: renderAvailableSkillsXml
1188
- };
1189
861
 
1190
862
  //#endregion
1191
- export { unzipWithLimits as $, computeDigest as A, messageOf as B, assertSafePathSegment as C, checkDependencyCycles as D, buildManifest as E, formatAttachmentText as F, readSkillSignature as G, parseSkillMarkdown as H, isAtomicTempPath as I, resolveArchiveLimits as J, renderAvailableSkillsXml as K, isValidSkillName as L, detectSkillArchiveShapeFromFs as M, escapeXml as N, checkSkillRules as O, exportSkills as P, stripArchiveRoot as Q, jsonRenderer as R, assertRemoteUrlAllowed as S, buildCatalog as T, parseSkillPackManifest as U, normalizePath as V, readResponseWithLimit as W, signSkill as X, resolveInsideRoot as Y, signaturePayloadBytes as Z, SkillDiscovery as _, FILE_MIME_TYPES as a, WebSkillError as b, MANIFEST_EXCLUDED_FILES as c, SKILLS_LOCKFILE as d, validateSkills as et, SKILL_MANIFEST_FILE as f, SKILL_SIGNATURE_FILE as g, SKILL_PACK_FILE as h, DOCX_MIME as i, detectSkillArchiveShape as j, classifyAttachment as k, MemoryFS as l, SKILL_NAME_PATTERN as m, ATTACHMENT_TEXT_LIMIT as n, verifySkillSignature as nt, FsTrustedKeyStore as o, SKILL_NAME_MAX_LENGTH as p, renderCatalogJson as q, DEFAULT_ARCHIVE_LIMITS as r, xmlRenderer as rt, IMAGE_MIME_TYPES as s, ATOMIC_TMP_SUFFIX_PATTERN as t, verifyManifest as tt, SIGNATURE_SCHEMA_VERSION as u, SkillReader as v, atomicWriteText as w, XLSX_MIME as x, TEXT_EXTENSIONS as y, keyIdOf as z };
863
+ export { SKILL_MANIFEST_FILE as A, keyIdOf as C, verifySkillSignature as D, signaturePayloadBytes as E, buildManifest as M, computeDigest as N, MANIFEST_EXCLUDED_FILES as O, verifyManifest as P, SIGNATURE_SCHEMA_VERSION as S, signSkill as T, SKILL_NAME_MAX_LENGTH as _, DEFAULT_ARCHIVE_LIMITS as a, parseSkillMarkdown as b, unzipWithLimits as c, stripArchiveRoot as d, SKILL_PACK_FILE as f, checkSkillRules as g, checkDependencyCycles as h, buildCatalog as i, SKILL_SIGNATURE_FILE$1 as j, SKILLS_LOCKFILE as k, detectSkillArchiveShape as l, parseSkillPackManifest as m, SkillDiscovery as n, readResponseWithLimit as o, exportSkills as p, SkillReader as r, resolveArchiveLimits as s, validateSkills as t, detectSkillArchiveShapeFromFs as u, SKILL_NAME_PATTERN as v, readSkillSignature as w, FsTrustedKeyStore as x, isValidSkillName as y };
@@ -1,5 +1,5 @@
1
- import { U as FileSystemProvider, X as PageQuery, Y as Page, _t as SkillManifest, ut as SkillCatalogEntry } from "./types-C3V6lAeO-BgeOvc1Y.js";
2
- //#region ../governance/dist/skillVersionStore-D-qHk9ZE.d.ts
1
+ import { U as FileSystemProvider, X as PageQuery, Y as Page, _t as SkillManifest, ut as SkillCatalogEntry } from "./types-C3V6lAeO-BIf7UB2e.js";
2
+ //#region ../governance/dist/skillVersionStore-B24Jjjry.d.ts
3
3
  //#region src/types.d.ts
4
4
  type CandidateStatus = 'draft' | 'pending-review' | 'approved' | 'published' | 'rejected';
5
5
  /** `generated` 是 0.5.0 的技能自动生成来源(需求 12 号 AC-9.1) */
@@ -94,6 +94,8 @@ declare class CandidateStore {
94
94
  constructor(deps: {
95
95
  root: string;
96
96
  fs: FileSystemProvider;
97
+ /** 治理根下该技能是否仍装着;不注入则 published 一律拒删(见 `remove`) */
98
+ isSkillInstalled?: (name: string) => Promise<boolean>;
97
99
  });
98
100
  save(candidate: CandidateSkill): Promise<void>;
99
101
  get(id: string): Promise<CandidateSkill>;
@@ -106,6 +108,16 @@ declare class CandidateStore {
106
108
  status?: CandidateStatus;
107
109
  } & PageQuery): Promise<CandidatePage>;
108
110
  updateStatus(id: string, status: CandidateStatus, now?: string): Promise<CandidateSkill>;
111
+ /**
112
+ * 把候选记录从队列里删掉。
113
+ *
114
+ * `approved` 恒拒删:它是「已批准但还没发布」,删掉等于让批准过的东西凭空消失;
115
+ * 要清理先 `reject`(留下理由)再删。
116
+ * `published` 是已安装技能的来源凭证,技能还装着时同样拒删;技能卸载后它只剩堆在队列里,
117
+ * 此时可删(0.15.0 分册 16 D-16-2)。未注入 `isSkillInstalled` 时判断不了装没装,
118
+ * 退化为「published 一律拒删」——宁可留着清不掉,也不能把还装着的技能的来源凭证删了。
119
+ */
120
+ remove(id: string): Promise<void>;
109
121
  }
110
122
  /** 硬门禁:仅 published 可转换为 Catalog 条目,否则 APPROVAL_REQUIRED */
111
123
  declare function candidateToCatalogEntry(candidate: CandidateSkill): SkillCatalogEntry;