@webskill/sdk 0.1.4 → 0.2.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/dist/browser.d.ts +9 -4
- package/dist/browser.js +25 -22
- package/dist/{dist-MTc2KB03.js → dist-BJobG0i-.js} +30 -7
- package/dist/{dist-DAn07zHu.js → dist-BS5OpedX.js} +279 -63
- package/dist/{dist-DvoBsChX.js → dist-D0saNPi_.js} +154 -10
- package/dist/{dist-BVXZF7H_.js → dist-Dj6QjHBn.js} +148 -200
- package/dist/env--jJB-TSX-04klhTYi.js +143 -0
- package/dist/env-BPUBZCwJ-4jat_SVG.d.ts +38 -0
- package/dist/governance.d.ts +38 -6
- package/dist/governance.js +171 -11
- package/dist/{index-BSS3EWY-.d.ts → index-CySxIvRz.d.ts} +68 -3
- package/dist/{index-npFMt2Zw.d.ts → index-Vn63HJRO.d.ts} +15 -38
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +3 -3
- package/dist/mcp.js +35 -9
- package/dist/node.d.ts +5 -4
- package/dist/node.js +5 -4
- package/dist/sandboxWorkerEntry.js +20 -3
- package/dist/{testing-S8SN9Lc6.js → testing-B4pq6JYa.js} +1 -1
- package/dist/testing.d.ts +3 -2
- package/dist/testing.js +3 -2
- package/dist/{types-CgNC-oQu-DYrxhD3z.d.ts → types-CKm5G_eQ-8W8FnP4u.d.ts} +41 -4
- package/dist/ui-react.d.ts +4 -2
- package/dist/ui-react.js +25 -4
- package/dist/ui-vue.d.ts +3 -2
- package/dist/ui-vue.js +44 -15
- package/dist/ui.d.ts +5 -3
- package/dist/ui.js +2 -2
- package/package.json +21 -9
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { C as FileStat, I as SkillInstallSource, K as VerifyResult, W as SkillsLockfile, _ as RenderResultRequest, a as InteractionPolicy, c as LlmClient, d as LlmResponse, f as LlmStreamEvent, o as InteractionRequest, s as InteractionResponse, v as UiBridge, w as FileSystemProvider, y as ArchiveLimits, z as SkillManifest } from "./types-CKm5G_eQ-8W8FnP4u.js";
|
|
2
|
+
import { 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, wt as parseBridgeRequest, y as ExternalToolSource } from "./index-CySxIvRz.js";
|
|
3
3
|
//#region ../browser/dist/index.d.ts
|
|
4
4
|
//#region src/fs/featureDetection.d.ts
|
|
5
5
|
/** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
|
|
@@ -24,6 +24,7 @@ declare class OpfsProvider implements FileSystemProvider {
|
|
|
24
24
|
remove(p: string, options?: {
|
|
25
25
|
recursive?: boolean;
|
|
26
26
|
}): Promise<void>;
|
|
27
|
+
rename(from: string, to: string): Promise<void>;
|
|
27
28
|
}
|
|
28
29
|
//#endregion
|
|
29
30
|
//#region src/executor/workerBootstrap.d.ts
|
|
@@ -83,6 +84,10 @@ declare class BrowserSkillManager {
|
|
|
83
84
|
fs: FileSystemProvider;
|
|
84
85
|
managedRoot: string;
|
|
85
86
|
fetchImpl?: typeof fetch;
|
|
87
|
+
/** 归档体积三重上限(缺省 DEFAULT_ARCHIVE_LIMITS) */
|
|
88
|
+
archiveLimits?: ArchiveLimits;
|
|
89
|
+
/** install/uninstall 成功后的变更回调(宿主接线缓存失效,如 WebSkillRuntime.invalidate) */
|
|
90
|
+
onChanged?: () => void;
|
|
86
91
|
});
|
|
87
92
|
install(source: SkillInstallSource, options?: {
|
|
88
93
|
expectedSha256?: string;
|
|
@@ -99,10 +104,10 @@ declare class BrowserSkillManager {
|
|
|
99
104
|
//#endregion
|
|
100
105
|
//#region src/skillManagement/zipExtract.d.ts
|
|
101
106
|
/**
|
|
102
|
-
* zip 解包(fflate
|
|
107
|
+
* zip 解包(fflate 流式,单条目/总解压体积上限,浏览器无依赖)。
|
|
103
108
|
* 每个条目路径过 resolveInsideRoot:拒绝 `..` 段、绝对路径、反斜杠穿越(与 node 规则一致)。
|
|
104
109
|
*/
|
|
105
|
-
declare function extractZipWeb(fs: FileSystemProvider, data: Uint8Array, destRoot: string): Promise<void>;
|
|
110
|
+
declare function extractZipWeb(fs: FileSystemProvider, data: Uint8Array, destRoot: string, limits?: ArchiveLimits): Promise<void>;
|
|
106
111
|
//#endregion
|
|
107
112
|
//#region src/skillManagement/checksumWeb.d.ts
|
|
108
113
|
/** WebCrypto sha256(浏览器/Worker 通用;Node 20+ 全局 crypto 同样可用) */
|
package/dist/browser.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as
|
|
3
|
-
import { n as MockLlmClient } from "./testing-
|
|
4
|
-
import { unzipSync } from "fflate";
|
|
1
|
+
import { A as unzipWithLimits, C as parseSkillMarkdown, M as verifyManifest, T as readResponseWithLimit, b as isValidSkillName, c as SkillDiscovery, f as atomicWriteText, i as SKILL_MANIFEST_FILE, j as validateSkills, k as resolveInsideRoot, m as buildManifest, p as buildCatalog, r as SKILLS_LOCKFILE, s as SKILL_PACK_FILE, u as WebSkillError, w as parseSkillPackManifest, y as exportSkills } from "./dist-D0saNPi_.js";
|
|
2
|
+
import { A as isNetworkAllowed, C as bridgeError, E as createWebSkillApi, M as networkUrlHost, N as normalizeToolContent, P as parseBridgeRequest, 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-BS5OpedX.js";
|
|
3
|
+
import { n as MockLlmClient } from "./testing-B4pq6JYa.js";
|
|
5
4
|
|
|
6
5
|
//#region ../browser/dist/index.js
|
|
7
6
|
/** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
|
|
@@ -133,6 +132,11 @@ var OpfsProvider = class {
|
|
|
133
132
|
await (await this.#walkDir(`/${segments.join("/")}`, false)).removeEntry(name, { recursive: options?.recursive ?? false });
|
|
134
133
|
});
|
|
135
134
|
}
|
|
135
|
+
async rename(from, to) {
|
|
136
|
+
if ((await this.stat(from)).type === "directory") throw new WebSkillError("FS_PERMISSION_DENIED", `rename of directories is not supported on OPFS: ${from}`);
|
|
137
|
+
await this.writeBinary(to, await this.readBinary(from));
|
|
138
|
+
await this.remove(from);
|
|
139
|
+
}
|
|
136
140
|
};
|
|
137
141
|
/**
|
|
138
142
|
* Worker 引导源码(字符串 → Blob URL → new Worker(url, {type:'module'}))。
|
|
@@ -421,17 +425,11 @@ async function removeDirQuiet(fs, dir) {
|
|
|
421
425
|
} catch {}
|
|
422
426
|
}
|
|
423
427
|
/**
|
|
424
|
-
* zip 解包(fflate
|
|
428
|
+
* zip 解包(fflate 流式,单条目/总解压体积上限,浏览器无依赖)。
|
|
425
429
|
* 每个条目路径过 resolveInsideRoot:拒绝 `..` 段、绝对路径、反斜杠穿越(与 node 规则一致)。
|
|
426
430
|
*/
|
|
427
|
-
async function extractZipWeb(fs, data, destRoot) {
|
|
428
|
-
|
|
429
|
-
try {
|
|
430
|
-
entries = unzipSync(data);
|
|
431
|
-
} catch (e) {
|
|
432
|
-
throw new WebSkillError("INSTALL_FAILED", `Failed to read zip archive: ${e instanceof Error ? e.message : String(e)}`, e);
|
|
433
|
-
}
|
|
434
|
-
for (const [entryPath, content] of Object.entries(entries)) {
|
|
431
|
+
async function extractZipWeb(fs, data, destRoot, limits) {
|
|
432
|
+
for (const [entryPath, content] of await unzipWithLimits(data, limits)) {
|
|
435
433
|
const target = resolveInsideRoot(destRoot, entryPath);
|
|
436
434
|
if (entryPath.endsWith("/")) await fs.mkdir(target);
|
|
437
435
|
else await fs.writeBinary(target, content);
|
|
@@ -450,10 +448,14 @@ var BrowserSkillManager = class {
|
|
|
450
448
|
#fs;
|
|
451
449
|
#managedRoot;
|
|
452
450
|
#fetchImpl;
|
|
451
|
+
#archiveLimits;
|
|
452
|
+
#onChanged;
|
|
453
453
|
constructor(deps) {
|
|
454
454
|
this.#fs = deps.fs;
|
|
455
455
|
this.#managedRoot = deps.managedRoot.replace(/\/+$/, "");
|
|
456
456
|
this.#fetchImpl = deps.fetchImpl;
|
|
457
|
+
this.#archiveLimits = deps.archiveLimits;
|
|
458
|
+
this.#onChanged = deps.onChanged;
|
|
457
459
|
}
|
|
458
460
|
async install(source, options) {
|
|
459
461
|
const fs = this.#fs;
|
|
@@ -470,7 +472,7 @@ var BrowserSkillManager = class {
|
|
|
470
472
|
throw new WebSkillError("INSTALL_FAILED", `Download failed: ${messageOf$1(e)}`, e);
|
|
471
473
|
}
|
|
472
474
|
if (!res.ok) throw new WebSkillError("INSTALL_FAILED", `Download failed with HTTP ${res.status}`);
|
|
473
|
-
data =
|
|
475
|
+
data = await readResponseWithLimit(res, this.#archiveLimits);
|
|
474
476
|
} else data = new Uint8Array(source.data);
|
|
475
477
|
if (options?.expectedSha256 !== void 0) {
|
|
476
478
|
const actual = await sha256HexWeb(data);
|
|
@@ -478,7 +480,7 @@ var BrowserSkillManager = class {
|
|
|
478
480
|
}
|
|
479
481
|
const contentDir = `${stagingRoot}/content`;
|
|
480
482
|
await fs.mkdir(contentDir);
|
|
481
|
-
await extractZipWeb(fs, data, contentDir);
|
|
483
|
+
await extractZipWeb(fs, data, contentDir, this.#archiveLimits);
|
|
482
484
|
const packFilePath = `${contentDir}/${SKILL_PACK_FILE}`;
|
|
483
485
|
if (await fs.exists(packFilePath)) return (await this.#installPack({
|
|
484
486
|
contentDir,
|
|
@@ -534,6 +536,7 @@ var BrowserSkillManager = class {
|
|
|
534
536
|
throw e;
|
|
535
537
|
}
|
|
536
538
|
targetDir = void 0;
|
|
539
|
+
this.#onChanged?.();
|
|
537
540
|
return manifest;
|
|
538
541
|
} catch (e) {
|
|
539
542
|
if (targetDir) await removeDirQuiet(fs, targetDir);
|
|
@@ -611,6 +614,7 @@ var BrowserSkillManager = class {
|
|
|
611
614
|
installed.length = 0;
|
|
612
615
|
throw e;
|
|
613
616
|
}
|
|
617
|
+
this.#onChanged?.();
|
|
614
618
|
return manifests;
|
|
615
619
|
} catch (e) {
|
|
616
620
|
for (const targetDir of installed) await removeDirQuiet(fs, targetDir);
|
|
@@ -641,6 +645,7 @@ var BrowserSkillManager = class {
|
|
|
641
645
|
try {
|
|
642
646
|
await this.#fs.remove(targetDir, { recursive: true });
|
|
643
647
|
await this.#removeLockEntry(name);
|
|
648
|
+
this.#onChanged?.();
|
|
644
649
|
} catch (e) {
|
|
645
650
|
throw new WebSkillError("UNINSTALL_FAILED", `Failed to uninstall "${name}": ${messageOf$1(e)}`, e);
|
|
646
651
|
}
|
|
@@ -649,11 +654,9 @@ var BrowserSkillManager = class {
|
|
|
649
654
|
if (!isValidSkillName(name)) throw new WebSkillError("INTEGRITY_FAILED", `Invalid skill name (path traversal rejected): ${JSON.stringify(name)}`);
|
|
650
655
|
const manifest = await this.#readManifest(`${this.#managedRoot}/${name}`);
|
|
651
656
|
const actualHashes = /* @__PURE__ */ new Map();
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
}
|
|
656
|
-
return verifyManifest(manifest, actualHashes);
|
|
657
|
+
const actualFiles = await listFiles(this.#fs, `${this.#managedRoot}/${name}`);
|
|
658
|
+
for (const rel of actualFiles) actualHashes.set(rel, await sha256HexWeb(await this.#fs.readBinary(`${this.#managedRoot}/${name}/${rel}`)));
|
|
659
|
+
return verifyManifest(manifest, actualHashes, actualFiles);
|
|
657
660
|
}
|
|
658
661
|
async listInstalled() {
|
|
659
662
|
const path = lockfilePath(this.#managedRoot);
|
|
@@ -702,12 +705,12 @@ var BrowserSkillManager = class {
|
|
|
702
705
|
async #upsertLockEntry(name, entry) {
|
|
703
706
|
const lockfile = await this.listInstalled();
|
|
704
707
|
lockfile.skills[name] = entry;
|
|
705
|
-
await this.#fs
|
|
708
|
+
await atomicWriteText(this.#fs, lockfilePath(this.#managedRoot), JSON.stringify(lockfile, null, 2));
|
|
706
709
|
}
|
|
707
710
|
async #removeLockEntry(name) {
|
|
708
711
|
const lockfile = await this.listInstalled();
|
|
709
712
|
delete lockfile.skills[name];
|
|
710
|
-
await this.#fs
|
|
713
|
+
await atomicWriteText(this.#fs, lockfilePath(this.#managedRoot), JSON.stringify(lockfile, null, 2));
|
|
711
714
|
}
|
|
712
715
|
};
|
|
713
716
|
const defaultWorkerFactory = () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { u as WebSkillError } from "./dist-D0saNPi_.js";
|
|
2
2
|
|
|
3
3
|
//#region ../ui/dist/index.js
|
|
4
4
|
/** 提交值按请求类型归形(WebFormBridge 与框架组件库共享单一来源) */
|
|
@@ -524,6 +524,8 @@ var WebFormBridge = class {
|
|
|
524
524
|
#doc;
|
|
525
525
|
#styles;
|
|
526
526
|
#progressEl;
|
|
527
|
+
/** 等待中的 request 取消句柄(id → 触发 cancelled 并卸载 DOM) */
|
|
528
|
+
#pending = /* @__PURE__ */ new Map();
|
|
527
529
|
constructor(options) {
|
|
528
530
|
this.#mount = options.mount;
|
|
529
531
|
this.#doc = options.document ?? options.mount.ownerDocument;
|
|
@@ -535,13 +537,27 @@ var WebFormBridge = class {
|
|
|
535
537
|
const model = interactionToFormModel(input);
|
|
536
538
|
const { form, cleanup } = this.#renderForm(model);
|
|
537
539
|
return new Promise((resolve) => {
|
|
540
|
+
const cancel = () => {
|
|
541
|
+
cleanup();
|
|
542
|
+
this.#pending.delete(input.id);
|
|
543
|
+
resolve({
|
|
544
|
+
id: input.id,
|
|
545
|
+
cancelled: true
|
|
546
|
+
});
|
|
547
|
+
};
|
|
548
|
+
this.#pending.set(input.id, cancel);
|
|
538
549
|
form.addEventListener("submit", (event) => {
|
|
539
550
|
event.preventDefault();
|
|
540
551
|
const { values, missingRequired } = collectValues(model.controls, form);
|
|
541
552
|
if (missingRequired.length > 0) {
|
|
542
|
-
for (const name of missingRequired)
|
|
553
|
+
for (const name of missingRequired) {
|
|
554
|
+
const wrapper = form.querySelector(`[${CONTROL_TAG}="${name}"]`)?.closest(".webskill-form__control");
|
|
555
|
+
wrapper?.classList.add("webskill-form__control--invalid");
|
|
556
|
+
wrapper?.querySelector(".webskill-form__input")?.setAttribute("aria-invalid", "true");
|
|
557
|
+
}
|
|
543
558
|
return;
|
|
544
559
|
}
|
|
560
|
+
this.#pending.delete(input.id);
|
|
545
561
|
cleanup();
|
|
546
562
|
resolve({
|
|
547
563
|
id: input.id,
|
|
@@ -550,15 +566,15 @@ var WebFormBridge = class {
|
|
|
550
566
|
});
|
|
551
567
|
form.querySelector("[data-webskill-cancel]")?.addEventListener("click", (event) => {
|
|
552
568
|
event.preventDefault();
|
|
553
|
-
|
|
554
|
-
resolve({
|
|
555
|
-
id: input.id,
|
|
556
|
-
cancelled: true
|
|
557
|
-
});
|
|
569
|
+
cancel();
|
|
558
570
|
});
|
|
559
571
|
this.#mount.appendChild(form);
|
|
560
572
|
});
|
|
561
573
|
}
|
|
574
|
+
/** 尽力取消等待中的 request(超时后清理遗留表单) */
|
|
575
|
+
cancel(id) {
|
|
576
|
+
this.#pending.get(id)?.();
|
|
577
|
+
}
|
|
562
578
|
async renderResult(input) {
|
|
563
579
|
if (this.#styles) ensureStyles(this.#doc);
|
|
564
580
|
this.#clearProgress();
|
|
@@ -622,9 +638,12 @@ var WebFormBridge = class {
|
|
|
622
638
|
const doc = this.#doc;
|
|
623
639
|
const wrapper = doc.createElement("div");
|
|
624
640
|
wrapper.className = "webskill-form__control";
|
|
641
|
+
const controlId = `webskill-field-${control.name}`;
|
|
642
|
+
const errorId = `webskill-error-${control.name}`;
|
|
625
643
|
const label = doc.createElement("label");
|
|
626
644
|
label.className = control.required ? "webskill-form__label webskill-form__label-required" : "webskill-form__label";
|
|
627
645
|
label.textContent = control.label;
|
|
646
|
+
label.htmlFor = controlId;
|
|
628
647
|
wrapper.appendChild(label);
|
|
629
648
|
if (control.description) {
|
|
630
649
|
const desc = doc.createElement("div");
|
|
@@ -663,9 +682,13 @@ var WebFormBridge = class {
|
|
|
663
682
|
input = textInput;
|
|
664
683
|
}
|
|
665
684
|
input.setAttribute(CONTROL_TAG, control.name);
|
|
685
|
+
input.id = controlId;
|
|
686
|
+
input.setAttribute("aria-describedby", errorId);
|
|
666
687
|
wrapper.appendChild(input);
|
|
667
688
|
const error = doc.createElement("div");
|
|
668
689
|
error.className = "webskill-form__error";
|
|
690
|
+
error.id = errorId;
|
|
691
|
+
error.setAttribute("role", "alert");
|
|
669
692
|
error.textContent = "This field is required";
|
|
670
693
|
wrapper.appendChild(error);
|
|
671
694
|
return wrapper;
|