@webskill/sdk 0.7.0 → 0.9.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 (35) hide show
  1. package/dist/agent.d.ts +1 -1
  2. package/dist/agent.js +1 -1
  3. package/dist/browser.d.ts +151 -4
  4. package/dist/browser.js +269 -30
  5. package/dist/{catalogComponents-Dr5dFMAb-DKH_7VPI.js → catalogComponents-DfxxfUvn-T7Ic8QFV.js} +4026 -997
  6. package/dist/{dist-DnYG2-eY.js → dist-BQe1uglQ.js} +498 -118
  7. package/dist/{dist-8oQRa8Xz.js → dist-Bev6i6Ip.js} +102 -7
  8. package/dist/{dist-D0qW6e40.js → dist-CFmkV45C.js} +234 -27
  9. package/dist/{eventTypes-DjIQpt8Y-Bj3vghj4.js → eventTypes-DbOpAECr-BjcjZVms.js} +15 -2
  10. package/dist/governance.d.ts +24 -3
  11. package/dist/governance.js +30 -11
  12. package/dist/{index-BwsK9lGk.d.ts → index-DXNTIa-6.d.ts} +163 -6
  13. package/dist/{index-Ba3xFtfz.d.ts → index-lLcCpHE-.d.ts} +2 -2
  14. package/dist/{index-DkbABR43.d.ts → index-znZjobkr.d.ts} +144 -76
  15. package/dist/index.d.ts +11 -3
  16. package/dist/index.js +13 -4
  17. package/dist/mcp.d.ts +13 -2
  18. package/dist/mcp.js +19 -5
  19. package/dist/{memoryArtifactStore-52Zn9npI-BMPYwvoy.js → memoryArtifactStore-52Zn9npI-LbCQaqyx.js} +1 -1
  20. package/dist/node.d.ts +3 -3
  21. package/dist/node.js +6 -6
  22. package/dist/{openUiLibrary-Bdrji9qK-D2LxmM-a.js → openUiLibrary-DURlAxjk-Do_yqg3u.js} +3 -3
  23. package/dist/{skillVersionStore-Bl-ElD45-CWPvGvoq.d.ts → skillVersionStore-Bl-ElD45-dMJ8Ybhb.d.ts} +1 -1
  24. package/dist/{testing-CYTFqkDm.js → testing-Csg5ljNm.js} +2 -2
  25. package/dist/testing.d.ts +1 -1
  26. package/dist/testing.js +2 -2
  27. package/dist/{types-CcxRLdJG-DCXyw1US.d.ts → types-B3n0cMZu-BDhheIhX.d.ts} +54 -6
  28. package/dist/ui-react.d.ts +19 -6
  29. package/dist/ui-react.js +156 -84
  30. package/dist/ui-vue.d.ts +1 -1
  31. package/dist/ui-vue.js +2 -2
  32. package/dist/ui.d.ts +4 -4
  33. package/dist/ui.js +3 -3
  34. package/dist/{webskillLitCatalog-_mugzRHx-B_54vxum.js → webskillLitCatalog-DwTwSBFt-BG7kL-Es.js} +22 -3
  35. package/package.json +2 -2
@@ -76,6 +76,14 @@ function verifyManifest(manifest, actualHashes, actualFiles = [...actualHashes.k
76
76
  };
77
77
  }
78
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
+ /**
79
87
  * 原子写文本:先写临时文件再 rename(进程崩溃不留下半写文件;lockfile 等账本场景)。
80
88
  * 原子性强弱取决于 provider 的 rename 实现:NodeFS/MemoryFS 为真 rename;
81
89
  * OPFS 无原生 move,rename 是 copy+delete(非原子,崩溃可能残留双份),
@@ -656,7 +664,7 @@ async function listFilesRecursive(fs, root, prefix = "") {
656
664
  for (const entry of await fs.list(root)) {
657
665
  const rel = prefix === "" ? baseName$1(entry.path) : `${prefix}/${baseName$1(entry.path)}`;
658
666
  if (entry.type === "directory") out.push(...await listFilesRecursive(fs, entry.path, rel));
659
- else out.push(rel);
667
+ else if (!isAtomicTempPath(baseName$1(entry.path))) out.push(rel);
660
668
  }
661
669
  return out;
662
670
  }
@@ -714,6 +722,87 @@ function parseSkillPackManifest(text) {
714
722
  }
715
723
  return raw;
716
724
  }
725
+ const SKILL_FILE = "SKILL.md";
726
+ function normalize(path) {
727
+ return path.replace(/^\.?\//, "");
728
+ }
729
+ /**
730
+ * 按归档条目表判定包结构。
731
+ *
732
+ * 入参是条目表而不是文件系统:预览侧只有解压后的 entries,没有落盘的目录。
733
+ * 只看路径,不读内容——内容合法性由 checkSkillRules 负责,两者不重叠。
734
+ */
735
+ function detectSkillArchiveShape(entries) {
736
+ const paths = [];
737
+ for (const raw of entries) {
738
+ const p = normalize(raw);
739
+ if (p !== "") paths.push(p);
740
+ }
741
+ if (paths.length === 0) return {
742
+ shape: "invalid",
743
+ reason: "Archive is empty."
744
+ };
745
+ if (paths.includes("webskill.skill-pack.json")) return { shape: "pack-set" };
746
+ if (paths.includes(SKILL_FILE)) return { shape: "flat" };
747
+ const topLevels = /* @__PURE__ */ new Set();
748
+ let hasLooseRootFile = false;
749
+ for (const p of paths) {
750
+ const slash = p.indexOf("/");
751
+ if (slash <= 0) hasLooseRootFile = true;
752
+ else topLevels.add(p.slice(0, slash));
753
+ }
754
+ if (!hasLooseRootFile && topLevels.size === 1) {
755
+ const [dir] = [...topLevels];
756
+ if (dir !== void 0) {
757
+ if (paths.includes(`${dir}/webskill.skill-pack.json`)) return {
758
+ shape: "pack-set",
759
+ rootDir: dir
760
+ };
761
+ if (paths.includes(`${dir}/${SKILL_FILE}`)) return {
762
+ shape: "nested-single",
763
+ rootDir: dir
764
+ };
765
+ }
766
+ }
767
+ return {
768
+ shape: "invalid",
769
+ reason: `Archive does not contain a ${SKILL_FILE} at its root or in a single top-level directory.`
770
+ };
771
+ }
772
+ /** nested-single 时剥掉顶层目录,其余形态原样返回 */
773
+ function stripArchiveRoot(entries, detection) {
774
+ if (detection.rootDir === void 0) return entries;
775
+ const prefix = `${detection.rootDir}/`;
776
+ const out = {};
777
+ for (const [path, value] of Object.entries(entries)) {
778
+ const p = normalize(path);
779
+ if (!p.startsWith(prefix)) continue;
780
+ const rel = p.slice(prefix.length);
781
+ if (rel !== "") out[rel] = value;
782
+ }
783
+ return out;
784
+ }
785
+ /**
786
+ * 文件系统入口:安装管线拿到的是解压后的目录,不是条目表。
787
+ *
788
+ * 只探测判定所需的少量路径(不递归全量列举),再交给上面的纯函数——
789
+ * 判定规则仍然只有一份,这正是 S7 的目的。
790
+ */
791
+ async function detectSkillArchiveShapeFromFs(fs, contentDir) {
792
+ const sample = [];
793
+ const top = await fs.list(contentDir);
794
+ for (const entry of top) {
795
+ const name = entry.path.slice(contentDir.length + 1);
796
+ if (entry.type !== "directory") {
797
+ sample.push(name);
798
+ continue;
799
+ }
800
+ if (await fs.exists(`${entry.path}/${SKILL_FILE}`)) sample.push(`${name}/${SKILL_FILE}`);
801
+ else if (await fs.exists(`${entry.path}/webskill.skill-pack.json`)) sample.push(`${name}/${SKILL_PACK_FILE}`);
802
+ else sample.push(`${name}/`);
803
+ }
804
+ return detectSkillArchiveShape(sample);
805
+ }
717
806
  const DEFAULT_ARCHIVE_LIMITS = {
718
807
  maxDownloadBytes: 64 * 1024 * 1024,
719
808
  maxEntryBytes: 64 * 1024 * 1024,
@@ -854,12 +943,15 @@ const baseName = (path) => path.split("/").pop() ?? path;
854
943
  var SkillDiscovery = class {
855
944
  #fs;
856
945
  #roots;
946
+ /** 扫描范围之外已知存在的技能名:只影响 dependencies 存在性判定,不产生目录项 */
947
+ #extraKnownSkillNames;
857
948
  /** name → skillRoot,与 SkillReader 共享同一 Map 引用 */
858
949
  #index = /* @__PURE__ */ new Map();
859
950
  #reader;
860
- constructor(fs, roots) {
951
+ constructor(fs, roots, options) {
861
952
  this.#fs = fs;
862
953
  this.#roots = roots;
954
+ this.#extraKnownSkillNames = options?.extraKnownSkillNames ?? [];
863
955
  this.#reader = new SkillReader(fs, this.#index);
864
956
  }
865
957
  async discover() {
@@ -867,7 +959,7 @@ var SkillDiscovery = class {
867
959
  const entries = [];
868
960
  const issues = [];
869
961
  const candidates = [];
870
- const knownSkillNames = /* @__PURE__ */ new Set();
962
+ const knownSkillNames = new Set(this.#extraKnownSkillNames);
871
963
  const adjacency = /* @__PURE__ */ new Map();
872
964
  for (const [rootIndex, root] of this.#roots.entries()) {
873
965
  if (!await this.#fs.exists(root)) {
@@ -940,7 +1032,7 @@ var SkillDiscovery = class {
940
1032
  });
941
1033
  }
942
1034
  issues.push(...cycles.issues);
943
- const admitted = new Set(entries.map((e) => e.name));
1035
+ const admitted = /* @__PURE__ */ new Set([...entries.map((e) => e.name), ...this.#extraKnownSkillNames]);
944
1036
  let changed = true;
945
1037
  while (changed) {
946
1038
  changed = false;
@@ -996,9 +1088,12 @@ var SkillDiscovery = class {
996
1088
  /**
997
1089
  * 合规校验。内部直接复用 SkillDiscovery 的扫描逻辑,
998
1090
  * 规则判定同样落在 checkSkillRules 单一来源上。
1091
+ *
1092
+ * `extraKnownSkillNames`:扫描范围之外已存在的技能名。安装时只扫刚解出来的那一个目录,
1093
+ * 不告诉它库里还有谁,任何指向包外的 dependencies 都会被判成不存在。
999
1094
  */
1000
- async function validateSkills(fs, roots) {
1001
- const { issues } = await new SkillDiscovery(fs, roots).discover();
1095
+ async function validateSkills(fs, roots, options) {
1096
+ const { issues } = await new SkillDiscovery(fs, roots, options).discover();
1002
1097
  return {
1003
1098
  ok: !issues.some((i) => i.severity === "error"),
1004
1099
  issues
@@ -1035,4 +1130,4 @@ const xmlRenderer = {
1035
1130
  };
1036
1131
 
1037
1132
  //#endregion
1038
- export { parseSkillMarkdown as A, unzipWithLimits as B, escapeXml as C, keyIdOf as D, jsonRenderer as E, renderCatalogJson as F, verifyManifest as H, resolveArchiveLimits as I, resolveInsideRoot as L, readResponseWithLimit as M, readSkillSignature as N, messageOf as O, renderAvailableSkillsXml as P, signSkill as R, computeDigest as S, isValidSkillName as T, verifySkillSignature as U, validateSkills as V, xmlRenderer as W, atomicWriteText as _, SIGNATURE_SCHEMA_VERSION as a, checkDependencyCycles as b, SKILL_NAME_MAX_LENGTH as c, SKILL_SIGNATURE_FILE as d, SkillDiscovery as f, assertSafePathSegment as g, assertRemoteUrlAllowed as h, MemoryFS as i, parseSkillPackManifest as j, normalizePath as k, SKILL_NAME_PATTERN as l, WebSkillError as m, FsTrustedKeyStore as n, SKILLS_LOCKFILE as o, SkillReader as p, MANIFEST_EXCLUDED_FILES as r, SKILL_MANIFEST_FILE as s, DEFAULT_ARCHIVE_LIMITS as t, SKILL_PACK_FILE as u, buildCatalog as v, exportSkills as w, checkSkillRules as x, buildManifest as y, signaturePayloadBytes as z };
1133
+ export { jsonRenderer as A, resolveArchiveLimits as B, computeDigest as C, exportSkills as D, escapeXml as E, parseSkillPackManifest as F, unzipWithLimits as G, signSkill as H, readResponseWithLimit as I, verifySkillSignature as J, validateSkills as K, readSkillSignature as L, messageOf as M, normalizePath as N, isAtomicTempPath as O, parseSkillMarkdown as P, renderAvailableSkillsXml as R, checkSkillRules as S, detectSkillArchiveShapeFromFs as T, signaturePayloadBytes as U, resolveInsideRoot as V, stripArchiveRoot as W, xmlRenderer as Y, assertSafePathSegment as _, MemoryFS as a, buildManifest as b, SKILL_MANIFEST_FILE as c, SKILL_PACK_FILE as d, SKILL_SIGNATURE_FILE as f, assertRemoteUrlAllowed as g, WebSkillError as h, MANIFEST_EXCLUDED_FILES as i, keyIdOf as j, isValidSkillName as k, SKILL_NAME_MAX_LENGTH as l, SkillReader as m, DEFAULT_ARCHIVE_LIMITS as n, SIGNATURE_SCHEMA_VERSION as o, SkillDiscovery as p, verifyManifest as q, FsTrustedKeyStore as r, SKILLS_LOCKFILE as s, ATOMIC_TMP_SUFFIX_PATTERN as t, SKILL_NAME_PATTERN as u, atomicWriteText as v, detectSkillArchiveShape as w, checkDependencyCycles as x, buildCatalog as y, renderCatalogJson as z };