@webskill/sdk 0.2.4 → 0.2.6
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/dist/browser.d.ts +2 -2
- package/dist/browser.js +17 -13
- package/dist/{dist-D7MsoMPx.js → dist-BQzncxXg.js} +70 -3
- package/dist/{dist-CV64gN62.js → dist-BXpDDZpR.js} +255 -116
- package/dist/{dist-CeNAzFYi.js → dist-DNSG9FqC.js} +108 -69
- package/dist/{dist-Chgf2tcy.js → dist-DWFDb1Ww.js} +76 -31
- package/dist/governance.d.ts +98 -94
- package/dist/governance.js +221 -198
- package/dist/{index-DZShzhon.d.ts → index-DJOha4b6.d.ts} +23 -1
- package/dist/{index-DrHelz72.d.ts → index-DRlYzdr2.d.ts} +5 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +6 -2
- package/dist/mcp.js +14 -14
- package/dist/node.d.ts +3 -3
- package/dist/node.js +3 -3
- package/dist/{testing-BN18eqbD.js → testing-BUoXvm1u.js} +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/{types-CKm5G_eQ-BqyXnvoR.d.ts → types-CKm5G_eQ-krKWW8WV.d.ts} +32 -2
- package/dist/ui-react.d.ts +3 -1
- package/dist/ui-react.js +8 -1
- package/dist/ui-vue.d.ts +3 -1
- package/dist/ui-vue.js +8 -1
- package/dist/ui.d.ts +20 -6
- package/dist/ui.js +3 -3
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Et as parseBridgeRequest, N as NetworkPolicy, Q as ScriptExecutor, Z as ScriptExecutionContext, d as BridgeCapabilities, it as ToolResult, m as BridgeResponse, nt as ToolDefinition, p as BridgeRequest, pt as bridgeError, u as ApprovalScope, ut as WebSkillApi, v as ExternalSkillProvider, y as ExternalToolSource } from "./index-
|
|
1
|
+
import { B as SkillManifest, C as FileStat, G as SkillsLockfile, L as SkillInstallSource, _ as RenderResultRequest, a as InteractionPolicy, c as LlmClient, d as LlmResponse, f as LlmStreamEvent, o as InteractionRequest, q as VerifyResult, s as InteractionResponse, v as UiBridge, w as FileSystemProvider, y as ArchiveLimits } from "./types-CKm5G_eQ-krKWW8WV.js";
|
|
2
|
+
import { Et as parseBridgeRequest, N as NetworkPolicy, Q as ScriptExecutor, Z as ScriptExecutionContext, d as BridgeCapabilities, it as ToolResult, m as BridgeResponse, nt as ToolDefinition, p as BridgeRequest, pt as bridgeError, u as ApprovalScope, ut as WebSkillApi, v as ExternalSkillProvider, y as ExternalToolSource } from "./index-DJOha4b6.js";
|
|
3
3
|
//#region ../browser/dist/index.d.ts
|
|
4
4
|
//#region src/fs/featureDetection.d.ts
|
|
5
5
|
/** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
|
package/dist/browser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as isNetworkAllowed, C as bridgeError, E as createWebSkillApi, F as normalizeToolError, I as parseBridgeRequest, M as networkUrlHost, P as normalizeToolContent, c as FsArtifactStore, h as ProgressiveRouter, i as AgentLoop, j as mergeCatalogEntries, l as FsMemoryStore, m as OpenAiCompatibleClient, o as CapabilityApproval, u as FsRunSnapshotStore, y as RUN_SNAPSHOT_SCHEMA_VERSION } from "./dist-
|
|
3
|
-
import { n as MockLlmClient } from "./testing-
|
|
1
|
+
import { C as messageOf, D as readResponseWithLimit, E as parseSkillPackManifest, M as unzipWithLimits, N as validateSkills, P as verifyManifest, T as parseSkillMarkdown, b as exportSkills, c as SkillDiscovery, d as assertRemoteUrlAllowed, h as buildManifest, i as SKILL_MANIFEST_FILE, j as resolveInsideRoot, m as buildCatalog, p as atomicWriteText, r as SKILLS_LOCKFILE, s as SKILL_PACK_FILE, u as WebSkillError, x as isValidSkillName } from "./dist-BQzncxXg.js";
|
|
2
|
+
import { A as isNetworkAllowed, C as bridgeError, E as createWebSkillApi, F as normalizeToolError, I as parseBridgeRequest, M as networkUrlHost, P as normalizeToolContent, c as FsArtifactStore, h as ProgressiveRouter, i as AgentLoop, j as mergeCatalogEntries, l as FsMemoryStore, m as OpenAiCompatibleClient, o as CapabilityApproval, u as FsRunSnapshotStore, y as RUN_SNAPSHOT_SCHEMA_VERSION } from "./dist-BXpDDZpR.js";
|
|
3
|
+
import { n as MockLlmClient } from "./testing-BUoXvm1u.js";
|
|
4
4
|
|
|
5
5
|
//#region ../browser/dist/index.js
|
|
6
6
|
/** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
|
|
@@ -462,8 +462,7 @@ async function extractZipWeb(fs, data, destRoot, limits) {
|
|
|
462
462
|
else await fs.writeBinary(target, content);
|
|
463
463
|
}
|
|
464
464
|
}
|
|
465
|
-
const
|
|
466
|
-
const asInstallFailed = (e) => e instanceof WebSkillError && (e.code === "INSTALL_FAILED" || e.code === "TOOL_UNSUPPORTED") ? e : new WebSkillError("INSTALL_FAILED", `Install failed: ${messageOf$1(e)}`, e);
|
|
465
|
+
const asInstallFailed = (e) => e instanceof WebSkillError && (e.code === "INSTALL_FAILED" || e.code === "TOOL_UNSUPPORTED") ? e : new WebSkillError("INSTALL_FAILED", `Install failed: ${messageOf(e)}`, e);
|
|
467
466
|
const lockfilePath = (root) => `${root}/${SKILLS_LOCKFILE}`;
|
|
468
467
|
/**
|
|
469
468
|
* D5:浏览器技能安装(http(s) zip / ArrayBuffer;tar/git/npm → TOOL_UNSUPPORTED)。
|
|
@@ -494,9 +493,15 @@ var BrowserSkillManager = class {
|
|
|
494
493
|
if (source.type === "http") {
|
|
495
494
|
let res;
|
|
496
495
|
try {
|
|
497
|
-
|
|
496
|
+
const absolute = new URL(source.url, typeof location !== "undefined" ? location.href : void 0).href;
|
|
497
|
+
const url = assertRemoteUrlAllowed(absolute, {
|
|
498
|
+
allowHttp: source.allowHttp ?? false,
|
|
499
|
+
allowPrivateHosts: source.allowPrivateHosts ?? false
|
|
500
|
+
});
|
|
501
|
+
res = await (this.#fetchImpl ?? fetch)(url.href);
|
|
498
502
|
} catch (e) {
|
|
499
|
-
|
|
503
|
+
if (e instanceof WebSkillError) throw e;
|
|
504
|
+
throw new WebSkillError("INSTALL_FAILED", `Download failed: ${messageOf(e)}`, e);
|
|
500
505
|
}
|
|
501
506
|
if (!res.ok) throw new WebSkillError("INSTALL_FAILED", `Download failed with HTTP ${res.status}`);
|
|
502
507
|
data = await readResponseWithLimit(res, this.#archiveLimits);
|
|
@@ -526,7 +531,7 @@ var BrowserSkillManager = class {
|
|
|
526
531
|
name = metadata.name;
|
|
527
532
|
version = typeof metadata["version"] === "string" ? metadata["version"] : void 0;
|
|
528
533
|
} catch (e) {
|
|
529
|
-
throw new WebSkillError("INSTALL_FAILED", `Failed to read skill name from SKILL.md: ${messageOf
|
|
534
|
+
throw new WebSkillError("INSTALL_FAILED", `Failed to read skill name from SKILL.md: ${messageOf(e)}`, e);
|
|
530
535
|
}
|
|
531
536
|
if (!isValidSkillName(name)) throw new WebSkillError("INSTALL_FAILED", `Invalid skill name in SKILL.md (path traversal rejected): ${JSON.stringify(name)}`);
|
|
532
537
|
const finalRoot = `${stagingRoot}/final`;
|
|
@@ -592,7 +597,7 @@ var BrowserSkillManager = class {
|
|
|
592
597
|
name = metadata.name;
|
|
593
598
|
versions.set(name, typeof metadata["version"] === "string" ? metadata["version"] : void 0);
|
|
594
599
|
} catch (e) {
|
|
595
|
-
throw new WebSkillError("INSTALL_FAILED", `Failed to read skill name from SKILL.md of pack entry "${entry.name}": ${messageOf
|
|
600
|
+
throw new WebSkillError("INSTALL_FAILED", `Failed to read skill name from SKILL.md of pack entry "${entry.name}": ${messageOf(e)}`, e);
|
|
596
601
|
}
|
|
597
602
|
if (name !== entry.name) throw new WebSkillError("INSTALL_FAILED", `Skill pack entry "${entry.name}" does not match the SKILL.md name "${name}"`);
|
|
598
603
|
await copyDir(fs, skillDir, `${finalRoot}/${name}`);
|
|
@@ -674,7 +679,7 @@ var BrowserSkillManager = class {
|
|
|
674
679
|
await this.#removeLockEntry(name);
|
|
675
680
|
this.#onChanged?.();
|
|
676
681
|
} catch (e) {
|
|
677
|
-
throw new WebSkillError("UNINSTALL_FAILED", `Failed to uninstall "${name}": ${messageOf
|
|
682
|
+
throw new WebSkillError("UNINSTALL_FAILED", `Failed to uninstall "${name}": ${messageOf(e)}`, e);
|
|
678
683
|
}
|
|
679
684
|
}
|
|
680
685
|
async verifyIntegrity(name) {
|
|
@@ -694,7 +699,7 @@ var BrowserSkillManager = class {
|
|
|
694
699
|
try {
|
|
695
700
|
return JSON.parse(await this.#fs.readText(path));
|
|
696
701
|
} catch (e) {
|
|
697
|
-
throw new WebSkillError("INTEGRITY_FAILED", `Skills lockfile at ${path} is corrupted: ${messageOf
|
|
702
|
+
throw new WebSkillError("INTEGRITY_FAILED", `Skills lockfile at ${path} is corrupted: ${messageOf(e)}`, e);
|
|
698
703
|
}
|
|
699
704
|
}
|
|
700
705
|
async #createManifest(skillRoot, input) {
|
|
@@ -726,7 +731,7 @@ var BrowserSkillManager = class {
|
|
|
726
731
|
try {
|
|
727
732
|
return JSON.parse(await this.#fs.readText(path));
|
|
728
733
|
} catch (e) {
|
|
729
|
-
throw new WebSkillError("INTEGRITY_FAILED", `Skill manifest at ${path} is corrupted: ${messageOf
|
|
734
|
+
throw new WebSkillError("INTEGRITY_FAILED", `Skill manifest at ${path} is corrupted: ${messageOf(e)}`, e);
|
|
730
735
|
}
|
|
731
736
|
}
|
|
732
737
|
async #upsertLockEntry(name, entry) {
|
|
@@ -864,7 +869,6 @@ const resolveWorkerFactory = (mode) => {
|
|
|
864
869
|
return blobWorkerFactory;
|
|
865
870
|
};
|
|
866
871
|
const baseName = (p) => p.split("/").pop() ?? p;
|
|
867
|
-
const messageOf = (e) => e instanceof Error ? e.message : String(e);
|
|
868
872
|
/**
|
|
869
873
|
* Web Worker 脚本沙箱执行器:loadDefinition 与 execute 都在 Worker 内完成,
|
|
870
874
|
* 主线程从不 import 技能脚本;每次执行独立 Worker;超时 terminate 强杀。
|
|
@@ -16,6 +16,10 @@ var WebSkillError = class extends Error {
|
|
|
16
16
|
this.details = details;
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
|
+
/** unknown 异常取消息文本(全仓单一来源,勿再本地重复定义) */
|
|
20
|
+
function messageOf(e) {
|
|
21
|
+
return e instanceof Error ? e.message : String(e);
|
|
22
|
+
}
|
|
19
23
|
/** 技能目录内的安装清单文件名(不参与 files 列表与 digest) */
|
|
20
24
|
const SKILL_MANIFEST_FILE = "webskill.skill-manifest.json";
|
|
21
25
|
/** managed root 下的锁定文件名(不参与 files 列表与 digest) */
|
|
@@ -200,7 +204,12 @@ var MemoryFS = class {
|
|
|
200
204
|
this.#entries.delete(src);
|
|
201
205
|
}
|
|
202
206
|
};
|
|
203
|
-
/**
|
|
207
|
+
/**
|
|
208
|
+
* 原子写文本:先写临时文件再 rename(进程崩溃不留下半写文件;lockfile 等账本场景)。
|
|
209
|
+
* 原子性强弱取决于 provider 的 rename 实现:NodeFS/MemoryFS 为真 rename;
|
|
210
|
+
* OPFS 无原生 move,rename 是 copy+delete(非原子,崩溃可能残留双份),
|
|
211
|
+
* 对崩溃一致性要求极高的场景在浏览器侧需知悉此前提。
|
|
212
|
+
*/
|
|
204
213
|
async function atomicWriteText(fs, path, content) {
|
|
205
214
|
const tmp = `${path}.tmp-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
206
215
|
await fs.writeText(tmp, content);
|
|
@@ -243,6 +252,65 @@ function resolveInsideRoot(root, relativePath) {
|
|
|
243
252
|
const joined = normalizedRoot === "" ? clean.join("/") : `${normalizedRoot}/${clean.join("/")}`;
|
|
244
253
|
return root.startsWith("/") || root.startsWith("\\") ? `/${joined}` : joined;
|
|
245
254
|
}
|
|
255
|
+
const LOOPBACK_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "localhost."]);
|
|
256
|
+
function ipv4Octets(host) {
|
|
257
|
+
const parts = host.split(".");
|
|
258
|
+
if (parts.length !== 4) return void 0;
|
|
259
|
+
const octets = parts.map((p) => /^\d{1,3}$/.test(p) ? Number(p) : NaN);
|
|
260
|
+
return octets.every((n) => n >= 0 && n <= 255) ? octets : void 0;
|
|
261
|
+
}
|
|
262
|
+
function isPrivateIpv4(octets) {
|
|
263
|
+
const [a, b] = octets;
|
|
264
|
+
return a === 0 || a === 10 || a === 127 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 169 && b === 254;
|
|
265
|
+
}
|
|
266
|
+
function isPrivateIpv6(host) {
|
|
267
|
+
const h = host.toLowerCase();
|
|
268
|
+
if (h === "::1" || h === "::") return true;
|
|
269
|
+
const mapped = h.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
|
|
270
|
+
if (mapped) {
|
|
271
|
+
const octets = ipv4Octets(mapped[1]);
|
|
272
|
+
return octets !== void 0 && isPrivateIpv4(octets);
|
|
273
|
+
}
|
|
274
|
+
const mappedHex = h.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
|
|
275
|
+
if (mappedHex) {
|
|
276
|
+
const hi = parseInt(mappedHex[1], 16);
|
|
277
|
+
const lo = parseInt(mappedHex[2], 16);
|
|
278
|
+
return isPrivateIpv4([
|
|
279
|
+
hi >> 8,
|
|
280
|
+
hi & 255,
|
|
281
|
+
lo >> 8,
|
|
282
|
+
lo & 255
|
|
283
|
+
]);
|
|
284
|
+
}
|
|
285
|
+
const first = h.split(":")[0];
|
|
286
|
+
if (/^[0-9a-f]{1,4}$/.test(first)) {
|
|
287
|
+
const n = parseInt(first, 16);
|
|
288
|
+
if ((n & 65472) === 65152) return true;
|
|
289
|
+
if ((n & 65024) === 64512) return true;
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
function isPrivateHost(hostname) {
|
|
294
|
+
const host = hostname.toLowerCase();
|
|
295
|
+
if (LOOPBACK_HOSTNAMES.has(host) || host.endsWith(".localhost")) return true;
|
|
296
|
+
const v4 = ipv4Octets(host);
|
|
297
|
+
if (v4) return isPrivateIpv4(v4);
|
|
298
|
+
if (host.startsWith("[") && host.endsWith("]")) return isPrivateIpv6(host.slice(1, -1));
|
|
299
|
+
if (host.includes(":")) return isPrivateIpv6(host);
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* 校验远程 URL 是否允许连接;违规抛 NETWORK_BLOCKED。
|
|
304
|
+
* rawUrl 非合法 URL 时抛 TypeError(由调用方按自身错误码包装)。
|
|
305
|
+
*/
|
|
306
|
+
function assertRemoteUrlAllowed(rawUrl, policy = {}) {
|
|
307
|
+
const url = new URL(rawUrl);
|
|
308
|
+
if (url.protocol === "http:") {
|
|
309
|
+
if (!policy.allowHttp) throw new WebSkillError("NETWORK_BLOCKED", `Insecure URL scheme "http:" is not allowed (opt in via allowHttp): ${url.href}`);
|
|
310
|
+
} else if (url.protocol !== "https:") throw new WebSkillError("NETWORK_BLOCKED", `URL scheme "${url.protocol}" is not allowed (https only): ${url.href}`);
|
|
311
|
+
if (!policy.allowPrivateHosts && isPrivateHost(url.hostname)) throw new WebSkillError("NETWORK_BLOCKED", `Refusing to connect to a private/loopback/link-local address: ${url.hostname}`);
|
|
312
|
+
return url;
|
|
313
|
+
}
|
|
246
314
|
const FRONTMATTER_RE = /^---[^\S\r\n]*\r?\n([\s\S]*?)\r?\n---[^\S\r\n]*(?:\r?\n|$)([\s\S]*)$/;
|
|
247
315
|
const invalid = (message, details) => {
|
|
248
316
|
throw new WebSkillError("SKILL_INVALID_METADATA", message, details);
|
|
@@ -383,7 +451,6 @@ function checkDependencyCycles(adjacency) {
|
|
|
383
451
|
*/
|
|
384
452
|
/** 包级清单文件名(位于 zip 根,不参与各技能 digest) */
|
|
385
453
|
const SKILL_PACK_FILE = "webskill.skill-pack.json";
|
|
386
|
-
const messageOf = (e) => e instanceof Error ? e.message : String(e);
|
|
387
454
|
const baseName$1 = (p) => p.split("/").pop() ?? p;
|
|
388
455
|
async function listFilesRecursive(fs, root, prefix = "") {
|
|
389
456
|
const out = [];
|
|
@@ -769,4 +836,4 @@ const xmlRenderer = {
|
|
|
769
836
|
};
|
|
770
837
|
|
|
771
838
|
//#endregion
|
|
772
|
-
export {
|
|
839
|
+
export { resolveArchiveLimits as A, messageOf as C, readResponseWithLimit as D, parseSkillPackManifest as E, xmlRenderer as F, unzipWithLimits as M, validateSkills as N, renderAvailableSkillsXml as O, verifyManifest as P, jsonRenderer as S, parseSkillMarkdown as T, checkSkillRules as _, SKILL_NAME_MAX_LENGTH as a, exportSkills as b, SkillDiscovery as c, assertRemoteUrlAllowed as d, assertSafePathSegment as f, checkDependencyCycles as g, buildManifest as h, SKILL_MANIFEST_FILE as i, resolveInsideRoot as j, renderCatalogJson as k, SkillReader as l, buildCatalog as m, MemoryFS as n, SKILL_NAME_PATTERN as o, atomicWriteText as p, SKILLS_LOCKFILE as r, SKILL_PACK_FILE as s, DEFAULT_ARCHIVE_LIMITS as t, WebSkillError as u, computeDigest as v, normalizePath as w, isValidSkillName as x, escapeXml as y };
|