@webskill/sdk 0.2.0 → 0.2.1

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 CHANGED
@@ -15,12 +15,16 @@ governance.
15
15
  the LLM, guardrails, lifecycle events, memory, human interaction
16
16
  (missing-parameter forms, confirmations, authorization prompts), and
17
17
  resumable interrupted runs.
18
- - **Script sandbox** — `worker_threads` sandbox for Node, Web Worker sandbox for
19
- the browser. Deny-by-default network policy, deny-by-default builtin-module
20
- allowlist, per-capability forced approval, timeouts kill runaway scripts.
21
- **Honest note:** these executors reduce the capability surface they are NOT
22
- a security boundary. Scripts share the host process/origin and bypasses
23
- exist; never rely on them to isolate untrusted scripts.
18
+ - **Script sandbox** — two tiers. Isolation-grade: Node `ProcessSandboxExecutor`
19
+ (`child_process.fork` + `--permission`, experimental; fs scoped to the skill
20
+ root, workers/child processes/addons denied by default note the permission
21
+ model has NO network dimension, so `fetch`/`WebSocket` stay patch-enforced and
22
+ bare `node:net` imports remain a documented residual), and the browser opaque
23
+ origin sandbox (`sandbox="allow-scripts"` iframe hosting a classic Worker
24
+ `type:"module"` workers cannot load in opaque origins, verified). Capability
25
+ tier (NOT a security boundary): `SandboxedScriptExecutor` /
26
+ `BrowserWorkerScriptExecutor` in blob-Worker mode, with deny-by-default
27
+ network policy, builtin-module allowlist, forced approval, timeouts.
24
28
  - **`navigator.webskill`** — a browser facade (`discover` / `read` / `validate` /
25
29
  `run` / `install` / `uninstall`) assembled explicitly in one call.
26
30
  - **MCP** — call page-provided tools and consume page-declared dynamic skills
@@ -102,7 +106,8 @@ const run = await api.run('Summarize the references of skill greeter.');
102
106
 
103
107
  ### `@webskill/sdk/node`
104
108
 
105
- Node host: `NodeFS`, script executors (in-process + `worker_threads` sandbox),
109
+ Node host: `NodeFS`, script executors (in-process, `worker_threads` sandbox, and
110
+ `ProcessSandboxExecutor` for real process isolation),
106
111
  file-backed stores, `SkillManager` (install/export incl. multi-skill packs),
107
112
  CLI UI bridge, schema inference.
108
113
 
@@ -117,9 +122,10 @@ console.log((await manager.verifyIntegrity('greeter')).ok);
117
122
 
118
123
  ### `@webskill/sdk/browser`
119
124
 
120
- Browser host: OPFS provider, Web Worker script sandbox (deny-by-default network
121
- policy), worker runtime host/client, `BrowserSkillManager`, and the
122
- `navigator.webskill` assembler. See
125
+ Browser host: OPFS provider, opaque origin iframe script sandbox (default on
126
+ the main thread; engines running inside a Worker automatically fall back to the
127
+ blob Worker form — no iframe is available there, documented), worker runtime
128
+ host/client, `BrowserSkillManager`, and the `navigator.webskill` assembler. See
123
129
  [`examples/quickstart-browser`](../examples/quickstart-browser).
124
130
 
125
131
  ```js
package/dist/browser.d.ts CHANGED
@@ -1,5 +1,5 @@
1
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";
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-gjFuBevI.js";
3
3
  //#region ../browser/dist/index.d.ts
4
4
  //#region src/fs/featureDetection.d.ts
5
5
  /** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
@@ -123,6 +123,8 @@ interface WorkerLike {
123
123
  terminate(): void;
124
124
  }
125
125
  type WorkerFactory = () => WorkerLike;
126
+ /** 沙箱形态:opaque iframe(默认,主线程可用 document 时)/ blob Worker(Worker 内引擎回退) */
127
+ type SandboxMode = 'iframe' | 'worker' | 'auto';
126
128
  /**
127
129
  * Web Worker 脚本沙箱执行器:loadDefinition 与 execute 都在 Worker 内完成,
128
130
  * 主线程从不 import 技能脚本;每次执行独立 Worker;超时 terminate 强杀。
@@ -133,6 +135,12 @@ declare class BrowserWorkerScriptExecutor implements ScriptExecutor {
133
135
  constructor(deps: {
134
136
  fs: FileSystemProvider;
135
137
  workerFactory?: WorkerFactory;
138
+ /**
139
+ * 沙箱形态:'auto'(默认)——主线程用 opaque origin iframe(sandbox=allow-scripts srcdoc,
140
+ * 真实隔离);Worker 内引擎自动回退 blob Worker(无 iframe 可用,文档明示)。
141
+ * 显式 'worker' 强制 blob Worker(0.1.x 旧形态)。
142
+ */
143
+ sandbox?: SandboxMode;
136
144
  capabilities?: BridgeCapabilities;
137
145
  /** 网络策略:默认 'deny-all'(白名单见 runtime/sandbox/networkPolicy) */
138
146
  networkPolicy?: NetworkPolicy;
@@ -153,6 +161,31 @@ declare class BrowserWorkerScriptExecutor implements ScriptExecutor {
153
161
  }): Promise<ToolResult>;
154
162
  }
155
163
  //#endregion
164
+ //#region src/executor/iframeSandbox.d.ts
165
+ /**
166
+ * opaque origin 沙箱 iframe(sandbox="allow-scripts" srcdoc):
167
+ * iframe 内建 module Worker(Blob URL),iframe window 作 postMessage 中继。
168
+ * opaque origin:沙箱内摸不到页面 DOM/localStorage/OPFS(独立 storage 分区)。
169
+ * 就绪握手:iframe 加载完成发 sandbox-ready 后才放行出站消息(防丢失)。
170
+ */
171
+ declare class IframeWorkerLike implements WorkerLike {
172
+ #private;
173
+ constructor(iframe: HTMLIFrameElement);
174
+ get ready(): boolean;
175
+ markReady(): void;
176
+ postMessage(data: unknown): void;
177
+ addEventListener(_type: 'message', listener: (event: {
178
+ data: unknown;
179
+ }) => void): void;
180
+ emit(data: unknown): void;
181
+ terminate(): void;
182
+ }
183
+ /**
184
+ * 创建 opaque iframe 沙箱 Worker:sandbox="allow-scripts" srcdoc iframe 承载 module Worker。
185
+ * 返回 IframeWorkerLike(协议与 blob Worker 完全一致:load/execute/bridge/network-blocked)。
186
+ */
187
+ declare function createIframeWorker(bootstrapSource: string, doc?: Document): IframeWorkerLike;
188
+ //#endregion
156
189
  //#region src/navigator.d.ts
157
190
  declare global {
158
191
  /** 0.0.6:installWebSkillNavigator 显式装配后可用(不自动挂全局) */
@@ -343,4 +376,4 @@ declare class WorkerUiBridge implements UiBridge {
343
376
  onTextDelta(runId: string, delta: string): Promise<void>;
344
377
  }
345
378
  //#endregion
346
- export { type BridgeCapabilities, type BridgeRequest, type BridgeResponse, BrowserSkillManager, BrowserWorkerScriptExecutor, type LlmClientConfig, type MainToWorkerMessage, OpfsProvider, type SerializedError, TsTranspiler, type TypeScriptSupportOptions, WORKER_BOOTSTRAP_SOURCE, type WorkerEvent, type WorkerFactory, type WorkerLike, type WorkerRequest, WorkerRuntimeClient, type WorkerRuntimeClientDeps, type WorkerRuntimeHostOverrides, type WorkerScopeLike, WorkerUiBridge, bridgeError, extractZipWeb, installWebSkillNavigator, isOpfsAvailable, parseBridgeRequest, parseMainMessage, parseWorkerEvent, sha256HexWeb, startWorkerRuntimeHost };
379
+ export { type BridgeCapabilities, type BridgeRequest, type BridgeResponse, BrowserSkillManager, BrowserWorkerScriptExecutor, IframeWorkerLike, type LlmClientConfig, type MainToWorkerMessage, OpfsProvider, type SandboxMode, type SerializedError, TsTranspiler, type TypeScriptSupportOptions, WORKER_BOOTSTRAP_SOURCE, type WorkerEvent, type WorkerFactory, type WorkerLike, type WorkerRequest, WorkerRuntimeClient, type WorkerRuntimeClientDeps, type WorkerRuntimeHostOverrides, type WorkerScopeLike, WorkerUiBridge, bridgeError, createIframeWorker, extractZipWeb, installWebSkillNavigator, isOpfsAvailable, parseBridgeRequest, parseMainMessage, parseWorkerEvent, sha256HexWeb, startWorkerRuntimeHost };
package/dist/browser.js CHANGED
@@ -1,5 +1,5 @@
1
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";
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-DrySSQ5R.js";
3
3
  import { n as MockLlmClient } from "./testing-B4pq6JYa.js";
4
4
 
5
5
  //#region ../browser/dist/index.js
@@ -713,13 +713,106 @@ var BrowserSkillManager = class {
713
713
  await atomicWriteText(this.#fs, lockfilePath(this.#managedRoot), JSON.stringify(lockfile, null, 2));
714
714
  }
715
715
  };
716
- const defaultWorkerFactory = () => {
716
+ const ENVELOPE = "__webskill_sandbox__";
717
+ /**
718
+ * opaque origin 沙箱 iframe(sandbox="allow-scripts" srcdoc):
719
+ * iframe 内建 module Worker(Blob URL),iframe window 作 postMessage 中继。
720
+ * opaque origin:沙箱内摸不到页面 DOM/localStorage/OPFS(独立 storage 分区)。
721
+ * 就绪握手:iframe 加载完成发 sandbox-ready 后才放行出站消息(防丢失)。
722
+ */
723
+ var IframeWorkerLike = class {
724
+ #iframe;
725
+ #ready = false;
726
+ #queue = [];
727
+ constructor(iframe) {
728
+ this.#iframe = iframe;
729
+ }
730
+ get ready() {
731
+ return this.#ready;
732
+ }
733
+ markReady() {
734
+ this.#ready = true;
735
+ for (const payload of this.#queue) this.#post(payload);
736
+ this.#queue = [];
737
+ }
738
+ #post(payload) {
739
+ this.#iframe.contentWindow?.postMessage({
740
+ [ENVELOPE]: true,
741
+ payload
742
+ }, "*");
743
+ }
744
+ postMessage(data) {
745
+ if (this.#ready) this.#post(data);
746
+ else this.#queue.push(data);
747
+ }
748
+ addEventListener(_type, listener) {
749
+ this.#listeners.add(listener);
750
+ }
751
+ #listeners = /* @__PURE__ */ new Set();
752
+ emit(data) {
753
+ for (const listener of this.#listeners) listener({ data });
754
+ }
755
+ terminate() {
756
+ this.#iframe.remove();
757
+ }
758
+ };
759
+ /** srcdoc 文档:内嵌 Worker 引导源码 + 双向中继(BOOTSTRAP 经 JSON 字符串注入,防 <\/script> 截断) */
760
+ function sandboxDocument(bootstrapSource) {
761
+ const bootstrapJson = JSON.stringify(bootstrapSource).replace(/<\//g, "<\\/");
762
+ return `<!doctype html>
763
+ <html><head><meta charset="utf-8"></head><body><script>
764
+ const ENVELOPE = ${JSON.stringify(ENVELOPE)};
765
+ const blob = new Blob([${bootstrapJson}], { type: 'text/javascript' });
766
+ const url = URL.createObjectURL(blob);
767
+ // 注意:opaque origin 下 module Worker 无法加载(实证),classic Worker + data: URL 动态导入可用
768
+ const worker = new Worker(url);
769
+ URL.revokeObjectURL(url);
770
+ window.addEventListener('message', (event) => {
771
+ const data = event.data;
772
+ if (data && data[ENVELOPE]) worker.postMessage(data.payload);
773
+ });
774
+ worker.addEventListener('message', (event) => {
775
+ parent.postMessage({ [ENVELOPE]: true, payload: event.data }, '*');
776
+ });
777
+ worker.addEventListener('error', (event) => {
778
+ parent.postMessage({ [ENVELOPE]: true, payload: { type: 'sandbox-worker-error', message: event.message } }, '*');
779
+ });
780
+ parent.postMessage({ [ENVELOPE]: true, payload: { type: 'sandbox-ready' } }, '*');
781
+ <\/script></body></html>`;
782
+ }
783
+ /**
784
+ * 创建 opaque iframe 沙箱 Worker:sandbox="allow-scripts" srcdoc iframe 承载 module Worker。
785
+ * 返回 IframeWorkerLike(协议与 blob Worker 完全一致:load/execute/bridge/network-blocked)。
786
+ */
787
+ function createIframeWorker(bootstrapSource, doc = document) {
788
+ const iframe = doc.createElement("iframe");
789
+ iframe.setAttribute("sandbox", "allow-scripts");
790
+ iframe.style.display = "none";
791
+ iframe.srcdoc = sandboxDocument(bootstrapSource);
792
+ const worker = new IframeWorkerLike(iframe);
793
+ (doc.defaultView ?? window).addEventListener("message", (event) => {
794
+ const data = event.data;
795
+ if (!data || data[ENVELOPE] !== true) return;
796
+ if (data.payload && data.payload.type === "sandbox-ready") {
797
+ worker.markReady();
798
+ return;
799
+ }
800
+ worker.emit(data.payload);
801
+ });
802
+ doc.body.appendChild(iframe);
803
+ return worker;
804
+ }
805
+ const blobWorkerFactory = () => {
717
806
  const blob = new Blob([WORKER_BOOTSTRAP_SOURCE], { type: "text/javascript" });
718
807
  const url = URL.createObjectURL(blob);
719
808
  const worker = new Worker(url, { type: "module" });
720
809
  URL.revokeObjectURL(url);
721
810
  return worker;
722
811
  };
812
+ const resolveWorkerFactory = (mode) => {
813
+ if (mode === "iframe" || mode === "auto" && typeof document !== "undefined") return () => createIframeWorker(WORKER_BOOTSTRAP_SOURCE);
814
+ return blobWorkerFactory;
815
+ };
723
816
  const baseName = (p) => p.split("/").pop() ?? p;
724
817
  const messageOf = (e) => e instanceof Error ? e.message : String(e);
725
818
  /**
@@ -736,7 +829,7 @@ var BrowserWorkerScriptExecutor = class {
736
829
  #transpiler;
737
830
  constructor(deps) {
738
831
  this.#fs = deps.fs;
739
- this.#workerFactory = deps.workerFactory ?? defaultWorkerFactory;
832
+ this.#workerFactory = deps.workerFactory ?? resolveWorkerFactory(deps.sandbox ?? "auto");
740
833
  this.#transpiler = deps.typescript ? new TsTranspiler({
741
834
  fs: deps.fs,
742
835
  options: deps.typescript
@@ -1483,4 +1576,4 @@ var WorkerRuntimeClient = class {
1483
1576
  };
1484
1577
 
1485
1578
  //#endregion
1486
- export { BrowserSkillManager, BrowserWorkerScriptExecutor, OpfsProvider, TsTranspiler, WORKER_BOOTSTRAP_SOURCE, WorkerRuntimeClient, WorkerUiBridge, bridgeError, extractZipWeb, installWebSkillNavigator, isOpfsAvailable, parseBridgeRequest, parseMainMessage, parseWorkerEvent, sha256HexWeb, startWorkerRuntimeHost };
1579
+ export { BrowserSkillManager, BrowserWorkerScriptExecutor, IframeWorkerLike, OpfsProvider, TsTranspiler, WORKER_BOOTSTRAP_SOURCE, WorkerRuntimeClient, WorkerUiBridge, bridgeError, createIframeWorker, extractZipWeb, installWebSkillNavigator, isOpfsAvailable, parseBridgeRequest, parseMainMessage, parseWorkerEvent, sha256HexWeb, startWorkerRuntimeHost };
@@ -1,20 +1,20 @@
1
1
  import { A as unzipWithLimits, C as parseSkillMarkdown, M as verifyManifest, O as resolveArchiveLimits, T as readResponseWithLimit, b as isValidSkillName, f as atomicWriteText, i as SKILL_MANIFEST_FILE, j as validateSkills, k as resolveInsideRoot, m as buildManifest, 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, M as networkUrlHost, N as normalizeToolContent, P as parseBridgeRequest, c as FsArtifactStore, l as FsMemoryStore, o as CapabilityApproval } from "./dist-BS5OpedX.js";
2
+ import { A as isNetworkAllowed, C as bridgeError, M as networkUrlHost, N as normalizeToolContent, P as parseBridgeRequest, c as FsArtifactStore, l as FsMemoryStore, o as CapabilityApproval } from "./dist-DrySSQ5R.js";
3
3
  import { createRequire } from "node:module";
4
4
  import { unzipSync, zipSync } from "fflate";
5
- import { existsSync, promises } from "node:fs";
5
+ import { existsSync, promises, realpathSync } from "node:fs";
6
6
  import path from "node:path";
7
7
  import { tmpdir } from "node:os";
8
8
  import { fileURLToPath, pathToFileURL } from "node:url";
9
9
  import { format, promisify } from "node:util";
10
10
  import { Worker } from "node:worker_threads";
11
+ import { mkdtemp, rm } from "node:fs/promises";
12
+ import { execFile, fork } from "node:child_process";
11
13
  import { createInterface } from "node:readline/promises";
12
- import { mkdtemp } from "node:fs/promises";
13
- import { execFile } from "node:child_process";
14
14
  import { createHash } from "node:crypto";
15
15
 
16
16
  //#region ../node/dist/index.js
17
- const toPlatform$3 = (p) => p.split("/").join(path.sep);
17
+ const toPlatform$4 = (p) => p.split("/").join(path.sep);
18
18
  const toPosix = (p) => p.split(path.sep).join("/");
19
19
  const isEnoent = (e) => typeof e === "object" && e !== null && e.code === "ENOENT";
20
20
  /** realpath 最近现存祖先并拼回剩余段(写入目标尚不存在时的等价判定) */
@@ -46,41 +46,41 @@ var NodeFS = class {
46
46
  /** root 模式:目标 realpath 必须落在 root realpath 前缀内(符号链接逃逸防护) */
47
47
  async #assertContained(p) {
48
48
  if (!this.#root) return;
49
- const rootReal = await promises.realpath(toPlatform$3(this.#root));
50
- const targetReal = await realpathNearest(toPlatform$3(p));
49
+ const rootReal = await promises.realpath(toPlatform$4(this.#root));
50
+ const targetReal = await realpathNearest(toPlatform$4(p));
51
51
  if (targetReal !== rootReal && !targetReal.startsWith(rootReal + path.sep)) throw new WebSkillError("FS_PATH_OUTSIDE_ROOT", `Path escapes root via symbolic link: ${p} (resolves outside ${this.#root})`);
52
52
  }
53
53
  async readText(p) {
54
54
  await this.#assertContained(p);
55
55
  try {
56
- return await promises.readFile(toPlatform$3(p), "utf8");
56
+ return await promises.readFile(toPlatform$4(p), "utf8");
57
57
  } catch (e) {
58
58
  throw this.#mapError(e, p);
59
59
  }
60
60
  }
61
61
  async writeText(p, content) {
62
62
  await this.#assertContained(p);
63
- const target = toPlatform$3(p);
63
+ const target = toPlatform$4(p);
64
64
  await promises.mkdir(path.dirname(target), { recursive: true });
65
65
  await promises.writeFile(target, content, "utf8");
66
66
  }
67
67
  async readBinary(p) {
68
68
  await this.#assertContained(p);
69
69
  try {
70
- return await promises.readFile(toPlatform$3(p));
70
+ return await promises.readFile(toPlatform$4(p));
71
71
  } catch (e) {
72
72
  throw this.#mapError(e, p);
73
73
  }
74
74
  }
75
75
  async writeBinary(p, content) {
76
76
  await this.#assertContained(p);
77
- const target = toPlatform$3(p);
77
+ const target = toPlatform$4(p);
78
78
  await promises.mkdir(path.dirname(target), { recursive: true });
79
79
  await promises.writeFile(target, content);
80
80
  }
81
81
  async exists(p) {
82
82
  try {
83
- await promises.access(toPlatform$3(p));
83
+ await promises.access(toPlatform$4(p));
84
84
  return true;
85
85
  } catch {
86
86
  return false;
@@ -88,7 +88,7 @@ var NodeFS = class {
88
88
  }
89
89
  async stat(p) {
90
90
  try {
91
- const s = await promises.stat(toPlatform$3(p));
91
+ const s = await promises.stat(toPlatform$4(p));
92
92
  return {
93
93
  path: toPosix(p),
94
94
  type: s.isDirectory() ? "directory" : "file",
@@ -102,29 +102,29 @@ var NodeFS = class {
102
102
  async list(p) {
103
103
  let dirents;
104
104
  try {
105
- dirents = await promises.readdir(toPlatform$3(p), { withFileTypes: true });
105
+ dirents = await promises.readdir(toPlatform$4(p), { withFileTypes: true });
106
106
  } catch (e) {
107
107
  throw this.#mapError(e, p);
108
108
  }
109
109
  return dirents.map((d) => ({
110
- path: toPosix(path.join(toPlatform$3(p), d.name)),
110
+ path: toPosix(path.join(toPlatform$4(p), d.name)),
111
111
  type: d.isDirectory() ? "directory" : "file"
112
112
  }));
113
113
  }
114
114
  async mkdir(p) {
115
- await promises.mkdir(toPlatform$3(p), { recursive: true });
115
+ await promises.mkdir(toPlatform$4(p), { recursive: true });
116
116
  }
117
117
  async remove(p, options) {
118
118
  try {
119
- await promises.rm(toPlatform$3(p), { recursive: options?.recursive ?? false });
119
+ await promises.rm(toPlatform$4(p), { recursive: options?.recursive ?? false });
120
120
  } catch (e) {
121
121
  throw this.#mapError(e, p);
122
122
  }
123
123
  }
124
124
  async rename(from, to) {
125
- await promises.mkdir(path.dirname(toPlatform$3(to)), { recursive: true });
125
+ await promises.mkdir(path.dirname(toPlatform$4(to)), { recursive: true });
126
126
  try {
127
- await promises.rename(toPlatform$3(from), toPlatform$3(to));
127
+ await promises.rename(toPlatform$4(from), toPlatform$4(to));
128
128
  } catch (e) {
129
129
  throw this.#mapError(e, from);
130
130
  }
@@ -134,7 +134,7 @@ var NodeFS = class {
134
134
  return e;
135
135
  }
136
136
  };
137
- const baseName$1 = (p) => p.split("/").pop() ?? p;
137
+ const baseName$2 = (p) => p.split("/").pop() ?? p;
138
138
  /**
139
139
  * 进程内脚本执行器。接口按沙箱语义设计(context 无隐式宿主访问),
140
140
  * worker_threads 沙箱见 deferred-items D1。
@@ -173,7 +173,7 @@ var NodeScriptExecutor = class {
173
173
  }
174
174
  async loadDefinition(skillRoot, scriptName) {
175
175
  const scriptPath = await this.#locateScript(skillRoot, scriptName);
176
- const skillName = baseName$1(skillRoot);
176
+ const skillName = baseName$2(skillRoot);
177
177
  let module;
178
178
  try {
179
179
  module = await this.#loadModule(scriptPath);
@@ -277,14 +277,14 @@ var NodeScriptExecutor = class {
277
277
  };
278
278
  }
279
279
  };
280
- const baseName = (p) => p.split("/").pop() ?? p;
281
- const toPlatform$2 = (p) => p.split("/").join(path.sep);
282
- const messageOf$5 = (e) => e instanceof Error ? e.message : String(e);
280
+ const baseName$1 = (p) => p.split("/").pop() ?? p;
281
+ const toPlatform$3 = (p) => p.split("/").join(path.sep);
282
+ const messageOf$6 = (e) => e instanceof Error ? e.message : String(e);
283
283
  /**
284
284
  * 网络策略判定函数源码(注入 Worker;匹配逻辑单一来源在
285
285
  * runtime/sandbox/networkPolicy.ts,Worker 线程不走 vitest 别名故注入而非 import)
286
286
  */
287
- const NETWORK_POLICY_LIB = `${isNetworkAllowed.toString()}\n${networkUrlHost.toString()}`;
287
+ const NETWORK_POLICY_LIB$1 = `${isNetworkAllowed.toString()}\n${networkUrlHost.toString()}`;
288
288
  /** Worker 入口文件:src 为同目录 .ts;node dist 为 dist/executor/;sdk dist 为平铺 dist/ */
289
289
  function workerEntryPath() {
290
290
  const dir = path.dirname(fileURLToPath(import.meta.url));
@@ -336,10 +336,10 @@ var SandboxedScriptExecutor = class {
336
336
  }
337
337
  async loadDefinition(skillRoot, scriptName) {
338
338
  const scriptPath = await this.#locateScript(skillRoot, scriptName);
339
- const skillName = baseName(skillRoot);
339
+ const skillName = baseName$1(skillRoot);
340
340
  const result = await this.#runWorker({
341
341
  mode: "load",
342
- scriptPath: toPlatform$2(scriptPath),
342
+ scriptPath: toPlatform$3(scriptPath),
343
343
  scriptName,
344
344
  scriptSource: await this.#fs.readText(scriptPath)
345
345
  });
@@ -363,14 +363,14 @@ var SandboxedScriptExecutor = class {
363
363
  content: [],
364
364
  error: {
365
365
  code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
366
- message: messageOf$5(e)
366
+ message: messageOf$6(e)
367
367
  }
368
368
  };
369
369
  }
370
370
  try {
371
371
  const result = await this.#runWorker({
372
372
  mode: "execute",
373
- scriptPath: toPlatform$2(scriptPath),
373
+ scriptPath: toPlatform$3(scriptPath),
374
374
  scriptName,
375
375
  skillName: context.skillName,
376
376
  runId: context.runId,
@@ -403,7 +403,7 @@ var SandboxedScriptExecutor = class {
403
403
  content: [],
404
404
  error: {
405
405
  code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
406
- message: messageOf$5(e)
406
+ message: messageOf$6(e)
407
407
  }
408
408
  };
409
409
  }
@@ -417,7 +417,7 @@ var SandboxedScriptExecutor = class {
417
417
  workerData: {
418
418
  ...workerData,
419
419
  networkPolicy: this.#networkPolicy,
420
- networkPolicyLib: NETWORK_POLICY_LIB,
420
+ networkPolicyLib: NETWORK_POLICY_LIB$1,
421
421
  allowedModules: this.#options.allowedModules ?? []
422
422
  },
423
423
  env,
@@ -454,7 +454,7 @@ var SandboxedScriptExecutor = class {
454
454
  respond(bridgeError("unknown", "TOOL_EXECUTION_FAILED", "invalid bridge request"));
455
455
  return;
456
456
  }
457
- onBridge(request).then(respond, (e) => respond(bridgeError(request.id, "TOOL_EXECUTION_FAILED", messageOf$5(e))));
457
+ onBridge(request).then(respond, (e) => respond(bridgeError(request.id, "TOOL_EXECUTION_FAILED", messageOf$6(e))));
458
458
  return;
459
459
  }
460
460
  if (msg?.type === "load-result" || msg?.type === "execute-result") done(() => resolve(msg));
@@ -466,6 +466,328 @@ var SandboxedScriptExecutor = class {
466
466
  });
467
467
  }
468
468
  /** 能力桥宿主侧处理:能力关闭/授权拒绝即 TOOL_UNSUPPORTED;判定逻辑共用 runtime/sandbox/approval */
469
+ async #handleBridge(request, context) {
470
+ const gate = async (capability, message, details) => {
471
+ const decision = await this.#approval.authorize({
472
+ runId: context.runId,
473
+ capability,
474
+ mode: this.#capabilities[capability],
475
+ message,
476
+ ...details !== void 0 ? { details } : {}
477
+ });
478
+ if (decision === "allowed") return void 0;
479
+ return bridgeError(request.id, "TOOL_UNSUPPORTED", decision === "disabled" ? `Capability "${capability}" is disabled` : `Capability "${capability}" was denied by the user`);
480
+ };
481
+ try {
482
+ switch (request.kind) {
483
+ case "readReference": {
484
+ const denied = await gate("readReference", `Script "${context.skillName}" wants to read reference "${request.path}"`, { path: request.path });
485
+ if (denied) return denied;
486
+ return {
487
+ id: request.id,
488
+ ok: true,
489
+ value: await context.readReference(request.path)
490
+ };
491
+ }
492
+ case "writeArtifact": {
493
+ const denied = await gate("writeArtifact", `Script "${context.skillName}" wants to write artifact "${request.path}"`, {
494
+ path: request.path,
495
+ mimeType: request.mimeType
496
+ });
497
+ if (denied) return denied;
498
+ const content = typeof request.content === "string" ? request.content : new Uint8Array(request.content);
499
+ const artifact = await context.writeArtifact(request.path, content, { ...request.mimeType ? { mimeType: request.mimeType } : {} });
500
+ return {
501
+ id: request.id,
502
+ ok: true,
503
+ value: artifact
504
+ };
505
+ }
506
+ case "confirm": {
507
+ if (!context.confirm) return bridgeError(request.id, "TOOL_UNSUPPORTED", "Capability \"confirm\" is disabled");
508
+ const denied = await gate("confirm", `Script "${context.skillName}" asks for confirmation: ${request.message}`);
509
+ if (denied) return denied;
510
+ return {
511
+ id: request.id,
512
+ ok: true,
513
+ value: await context.confirm(request.message)
514
+ };
515
+ }
516
+ }
517
+ } catch (e) {
518
+ return bridgeError(request.id, e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED", messageOf$6(e));
519
+ }
520
+ }
521
+ };
522
+ const baseName = (p) => p.split("/").pop() ?? p;
523
+ const toPlatform$2 = (p) => p.split("/").join(path.sep);
524
+ const messageOf$5 = (e) => e instanceof Error ? e.message : String(e);
525
+ const NETWORK_POLICY_LIB = `${isNetworkAllowed.toString()}\n${networkUrlHost.toString()}`;
526
+ const readAllow = (p) => [`--allow-fs-read=${p}`, `--allow-fs-read=${realpathSync(p)}`];
527
+ const writeAllow = (p) => [`--allow-fs-write=${p}`, `--allow-fs-write=${realpathSync(p)}`];
528
+ /** 子进程入口文件:src 为同目录 .ts;node dist 为 dist/executor/;sdk dist 为平铺 dist/ */
529
+ function processEntryPath() {
530
+ const dir = path.dirname(fileURLToPath(import.meta.url));
531
+ const candidates = [
532
+ path.join(dir, "processSandboxEntry.js"),
533
+ path.join(dir, "processSandboxEntry.ts"),
534
+ path.join(dir, "executor", "processSandboxEntry.js")
535
+ ];
536
+ for (const candidate of candidates) if (existsSync(candidate)) return candidate;
537
+ throw new WebSkillError("TOOL_EXECUTION_FAILED", `Process sandbox entry not found (tried: ${candidates.join(", ")})`);
538
+ }
539
+ /**
540
+ * child_process.fork + --permission 进程沙箱(真实进程隔离)。
541
+ * 每个子进程以 `--permission --allow-fs-read=<入口目录> --allow-fs-read=<skillRoot>
542
+ * --allow-fs-write=<artifactDir>` 启动:fs 维度由权限模型管控(experimental),
543
+ * 子进程/Worker/addons 默认禁;网络无权限维度仍靠 fetch/WebSocket patch。
544
+ * 能力桥协议与 worker_threads 沙箱同一形态(readReference/writeArtifact/confirm + 授权)。
545
+ * 温池(默认 2):同 skillRoot 复用子进程;执行后 kill 并补位重生;并发上限即池大小。
546
+ * 超时 kill(同步死循环可杀);非零退出码 → TOOL_EXECUTION_FAILED。
547
+ */
548
+ var ProcessSandboxExecutor = class {
549
+ #fs;
550
+ #options;
551
+ #capabilities;
552
+ #networkPolicy;
553
+ #approval;
554
+ #slots = [];
555
+ #waiters = [];
556
+ constructor(fs, options = {}) {
557
+ this.#fs = fs;
558
+ this.#options = options;
559
+ this.#capabilities = {
560
+ readReference: options.capabilities?.readReference ?? true,
561
+ writeArtifact: options.capabilities?.writeArtifact ?? true,
562
+ confirm: options.capabilities?.confirm ?? true
563
+ };
564
+ this.#networkPolicy = options.networkPolicy ?? "deny-all";
565
+ this.#approval = new CapabilityApproval({
566
+ ...options.uiBridge ? { uiBridge: options.uiBridge } : {},
567
+ ...options.approvalScope ? { scope: options.approvalScope } : {}
568
+ });
569
+ }
570
+ get poolSize() {
571
+ return this.#options.poolSize ?? 2;
572
+ }
573
+ /** 池全部子进程销毁(测试收尾/进程退出前调用) */
574
+ async dispose() {
575
+ for (const slot of this.#slots) {
576
+ slot.child.kill();
577
+ await rm(slot.artifactDir, {
578
+ recursive: true,
579
+ force: true
580
+ }).catch(() => void 0);
581
+ }
582
+ this.#slots = [];
583
+ this.#waiters = [];
584
+ }
585
+ async #locateScript(skillRoot, scriptName) {
586
+ const tsPath = `${skillRoot}/scripts/${scriptName}.ts`;
587
+ const jsPath = `${skillRoot}/scripts/${scriptName}.js`;
588
+ const [hasTs, hasJs] = await Promise.all([this.#fs.exists(tsPath), this.#fs.exists(jsPath)]);
589
+ if (hasTs && hasJs) throw new WebSkillError("TOOL_EXECUTION_FAILED", `Script "${scriptName}" is ambiguous: both .ts and .js exist under ${skillRoot}/scripts`);
590
+ if (!hasTs && !hasJs) throw new WebSkillError("TOOL_NOT_FOUND", `Script "${scriptName}" not found under ${skillRoot}/scripts`);
591
+ return hasTs ? tsPath : jsPath;
592
+ }
593
+ /** 取一个可用子进程(同 key 复用 / 淘汰 idle 重生 / 扩容 / 排队) */
594
+ async #acquire(key) {
595
+ for (;;) {
596
+ const idle = this.#slots.filter((s) => !s.busy);
597
+ const match = idle.find((s) => s.key === key);
598
+ if (match) {
599
+ match.busy = true;
600
+ return match;
601
+ }
602
+ if (idle.length > 0) {
603
+ const victim = idle[0];
604
+ victim.child.kill();
605
+ await rm(victim.artifactDir, {
606
+ recursive: true,
607
+ force: true
608
+ }).catch(() => void 0);
609
+ this.#slots.splice(this.#slots.indexOf(victim), 1);
610
+ }
611
+ if (this.#slots.length < this.poolSize) {
612
+ const slot = await this.#spawnSlot(key);
613
+ slot.busy = true;
614
+ this.#slots.push(slot);
615
+ return slot;
616
+ }
617
+ await new Promise((resolve) => this.#waiters.push(resolve));
618
+ }
619
+ }
620
+ /** 执行后回收:kill 旧子进程,补位重生同 key 新子进程(温池保持),唤醒排队 */
621
+ #recycle(slot) {
622
+ slot.child.kill();
623
+ const index = this.#slots.indexOf(slot);
624
+ if (index >= 0) this.#slots.splice(index, 1);
625
+ rm(slot.artifactDir, {
626
+ recursive: true,
627
+ force: true
628
+ }).catch(() => void 0);
629
+ this.#spawnSlot(slot.key).then((fresh) => {
630
+ this.#slots.push(fresh);
631
+ }).catch(() => void 0).finally(() => {
632
+ const waiter = this.#waiters.shift();
633
+ if (waiter) waiter();
634
+ });
635
+ }
636
+ async #spawnSlot(key) {
637
+ const artifactDir = await mkdtemp(path.join(tmpdir(), "webskill-psbx-out-")).then((d) => d.split(path.sep).join("/"));
638
+ const entry = processEntryPath();
639
+ return {
640
+ key,
641
+ child: fork(entry, [], {
642
+ execArgv: [
643
+ "--permission",
644
+ ...readAllow(path.dirname(entry)),
645
+ ...readAllow(toPlatform$2(key)),
646
+ ...readAllow(artifactDir),
647
+ ...writeAllow(artifactDir)
648
+ ],
649
+ silent: false
650
+ }),
651
+ busy: false,
652
+ artifactDir
653
+ };
654
+ }
655
+ async loadDefinition(skillRoot, scriptName) {
656
+ const scriptPath = await this.#locateScript(skillRoot, scriptName);
657
+ const skillName = baseName(skillRoot);
658
+ const slot = await this.#acquire(toPlatform$2(skillRoot));
659
+ try {
660
+ const response = await this.#runTask(slot, {
661
+ mode: "load",
662
+ scriptPath: toPlatform$2(scriptPath),
663
+ scriptName,
664
+ scriptSource: await this.#fs.readText(scriptPath),
665
+ scratchDir: slot.artifactDir
666
+ }, 3e4);
667
+ if (!response.ok) throw new WebSkillError("TOOL_EXECUTION_FAILED", `Failed to load script "${scriptName}": ${response.error?.message ?? "unknown error"}`);
668
+ return {
669
+ name: `${skillName}__${scriptName}`,
670
+ description: response.definition?.description,
671
+ inputSchema: response.definition?.inputSchema,
672
+ source: "script",
673
+ skillName
674
+ };
675
+ } finally {
676
+ this.#recycle(slot);
677
+ }
678
+ }
679
+ async execute(input) {
680
+ const { skillRoot, scriptName, args, context, timeoutMs } = input;
681
+ let scriptPath;
682
+ try {
683
+ scriptPath = await this.#locateScript(skillRoot, scriptName);
684
+ } catch (e) {
685
+ return {
686
+ ok: false,
687
+ content: [],
688
+ error: {
689
+ code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
690
+ message: messageOf$5(e)
691
+ }
692
+ };
693
+ }
694
+ const slot = await this.#acquire(toPlatform$2(skillRoot));
695
+ try {
696
+ const result = await this.#runTask(slot, {
697
+ mode: "execute",
698
+ scriptPath: toPlatform$2(scriptPath),
699
+ scriptName,
700
+ skillName: context.skillName,
701
+ runId: context.runId,
702
+ args,
703
+ scriptSource: await this.#fs.readText(scriptPath),
704
+ scratchDir: slot.artifactDir
705
+ }, timeoutMs, (request) => this.#handleBridge(request, context), (host) => context.onWarning?.(`Network request blocked by sandbox network policy: ${host}`));
706
+ if (!result.ok) {
707
+ const stderrSummary = result.stderr?.length ? ` | stderr: ${result.stderr.join(" | ").slice(0, 500)}` : "";
708
+ return {
709
+ ok: false,
710
+ content: [],
711
+ error: {
712
+ code: result.error?.code ?? "TOOL_EXECUTION_FAILED",
713
+ message: `${result.error?.message ?? "script execution failed"}${stderrSummary}`
714
+ }
715
+ };
716
+ }
717
+ const content = normalizeToolContent(result.value);
718
+ if (result.stdout?.length) content.push({
719
+ type: "text",
720
+ text: result.stdout.join("\n")
721
+ });
722
+ return {
723
+ ok: true,
724
+ content
725
+ };
726
+ } catch (e) {
727
+ return {
728
+ ok: false,
729
+ content: [],
730
+ error: {
731
+ code: e instanceof WebSkillError ? e.code : "TOOL_EXECUTION_FAILED",
732
+ message: messageOf$5(e)
733
+ }
734
+ };
735
+ } finally {
736
+ this.#recycle(slot);
737
+ }
738
+ }
739
+ /** 在子进程上跑一个任务;超时 kill;非零退出码 → TOOL_EXECUTION_FAILED */
740
+ #runTask(slot, task, timeoutMs, onBridge, onNetworkBlocked) {
741
+ const { child } = slot;
742
+ return new Promise((resolve, reject) => {
743
+ let settled = false;
744
+ const timer = setTimeout(() => {
745
+ if (settled) return;
746
+ settled = true;
747
+ child.kill();
748
+ reject(new WebSkillError("RUN_TIMEOUT", `Process sandbox task timed out after ${timeoutMs}ms (child killed)`));
749
+ }, timeoutMs);
750
+ const done = (fn) => {
751
+ if (settled) return;
752
+ settled = true;
753
+ clearTimeout(timer);
754
+ fn();
755
+ };
756
+ child.on("message", (msg) => {
757
+ if (msg?.type === "network-blocked") {
758
+ if (typeof msg.host === "string") onNetworkBlocked?.(msg.host);
759
+ return;
760
+ }
761
+ if (msg?.type === "bridge" && onBridge) {
762
+ const request = parseBridgeRequest(msg.request);
763
+ const respond = (response) => child.send({
764
+ type: "bridge-response",
765
+ response
766
+ });
767
+ if (!request) {
768
+ respond(bridgeError("unknown", "TOOL_EXECUTION_FAILED", "invalid bridge request"));
769
+ return;
770
+ }
771
+ onBridge(request).then(respond, (e) => respond(bridgeError(request.id, "TOOL_EXECUTION_FAILED", messageOf$5(e))));
772
+ return;
773
+ }
774
+ if (msg?.type === "load-result" || msg?.type === "execute-result") done(() => resolve(msg));
775
+ });
776
+ child.on("error", (e) => done(() => reject(e)));
777
+ child.on("exit", (code) => {
778
+ if (!settled) done(() => reject(new WebSkillError("TOOL_EXECUTION_FAILED", `Sandbox child exited ${code === null ? "by signal" : `with code ${code}`} without producing a result`)));
779
+ });
780
+ child.send({
781
+ type: "task",
782
+ task: {
783
+ ...task,
784
+ networkPolicy: this.#networkPolicy,
785
+ networkPolicyLib: NETWORK_POLICY_LIB
786
+ }
787
+ });
788
+ });
789
+ }
790
+ /** 能力桥宿主侧处理:与 worker_threads 沙箱同一判定(runtime/sandbox/approval 单一来源) */
469
791
  async #handleBridge(request, context) {
470
792
  const gate = async (capability, message, details) => {
471
793
  const decision = await this.#approval.authorize({
@@ -1500,4 +1822,4 @@ var SkillManager = class {
1500
1822
  };
1501
1823
 
1502
1824
  //#endregion
1503
- export { NodeScriptExecutor as a, SkillManager as c, NodeFS as i, exportArchive as l, FileArtifactStore as n, OxcSchemaInferer as o, FileMemoryStore as r, SandboxedScriptExecutor as s, CliUiBridge as t, readArchiveManifest as u };
1825
+ export { NodeScriptExecutor as a, SandboxedScriptExecutor as c, readArchiveManifest as d, NodeFS as i, SkillManager as l, FileArtifactStore as n, OxcSchemaInferer as o, FileMemoryStore as r, ProcessSandboxExecutor as s, CliUiBridge as t, exportArchive as u };
@@ -2090,6 +2090,10 @@ var WebSkillRuntime = class {
2090
2090
  get session() {
2091
2091
  return this.#session;
2092
2092
  }
2093
+ /** 当前装配的脚本执行器(治理默认执行器断言等只读场景) */
2094
+ get executor() {
2095
+ return this.#deps.executor;
2096
+ }
2093
2097
  /** 生命周期事件总线(只读观测) */
2094
2098
  get events() {
2095
2099
  return this.#events;
@@ -1,6 +1,6 @@
1
1
  import { F as SkillDocument, N as SkillCatalogEntry, c as LlmClient, u as LlmMessage, v as UiBridge, w as FileSystemProvider, z as SkillManifest } from "./types-CKm5G_eQ-8W8FnP4u.js";
2
- import { dt as WebSkillRuntime, q as RuntimeRun, tt as SkillStateGuard } from "./index-CySxIvRz.js";
3
- import { l as SkillManager } from "./index-Vn63HJRO.js";
2
+ import { Q as ScriptExecutor, dt as WebSkillRuntime, ft as WebSkillRuntimeDeps, q as RuntimeRun, tt as SkillStateGuard } from "./index-gjFuBevI.js";
3
+ import { d as SkillManager } from "./index-BmzysJX5.js";
4
4
  //#region ../governance/dist/index.d.ts
5
5
  //#region src/types.d.ts
6
6
  type CandidateStatus = 'draft' | 'pending-review' | 'approved' | 'published' | 'rejected';
@@ -357,6 +357,16 @@ declare class EvaluationRunner {
357
357
  run(tasks: EvaluationTask[]): Promise<EvaluationReport>;
358
358
  }
359
359
  //#endregion
360
+ //#region src/evaluation/evaluationRuntime.d.ts
361
+ /**
362
+ * 治理评估专用 runtime 装配(不可信技能试用路径):
363
+ * 默认注入 ProcessSandboxExecutor(fork + --permission 真实进程隔离);
364
+ * 可配置 executor 切回 SandboxedScriptExecutor(worker_threads 能力面收敛形态)。
365
+ */
366
+ declare function createEvaluationRuntime(deps: WebSkillRuntimeDeps & {
367
+ executor?: ScriptExecutor;
368
+ }): WebSkillRuntime;
369
+ //#endregion
360
370
  //#region src/evaluation/testSuggestion.d.ts
361
371
  /** 失败 trace → 回归评估任务建议(prompt 复现 + expected 错误模式) */
362
372
  declare function suggestFromFailedRun(run: RuntimeRun): EvaluationTask;
@@ -474,4 +484,4 @@ declare function createMissHook(deps: {
474
484
  */
475
485
  declare function completeText(llm: LlmClient, messages: LlmMessage[]): Promise<string>;
476
486
  //#endregion
477
- export { AlwaysHumanApprovalPolicy, type ApprovalDecision, type ApprovalPolicy, ApprovalWorkflow, type AuditEvent, type AuditLog, type CandidateFile, type CandidateRisk, type CandidateSkill, type CandidateSource, type CandidateStatus, CandidateStore, CompositeApprovalPolicy, DependencyGraph, DocumentSkillExtractor, type EvaluationReport, type EvaluationResult, EvaluationRunner, type EvaluationTask, FailureAnalyzer, type FailureDiagnosis, FsAuditLog, LlmCandidateGenerator, type RepairOption, RepairPlanner, SCORING_WEIGHTS, type ScoreInput, SimilarityDetector, SkillScorer, type SkillState, SkillStatePolicy, type SkillVersion, SkillVersionStore, type SourceDocument, candidateToCatalogEntry, completeText, createMissHook, jaccardSimilarity, normalizeCandidate, normalizeCandidateFiles, normalizeRisk, parseJsonObject, readDocument, sanitizeCandidateName, suggestFromFailedRun, validateCandidate };
487
+ export { AlwaysHumanApprovalPolicy, type ApprovalDecision, type ApprovalPolicy, ApprovalWorkflow, type AuditEvent, type AuditLog, type CandidateFile, type CandidateRisk, type CandidateSkill, type CandidateSource, type CandidateStatus, CandidateStore, CompositeApprovalPolicy, DependencyGraph, DocumentSkillExtractor, type EvaluationReport, type EvaluationResult, EvaluationRunner, type EvaluationTask, FailureAnalyzer, type FailureDiagnosis, FsAuditLog, LlmCandidateGenerator, type RepairOption, RepairPlanner, SCORING_WEIGHTS, type ScoreInput, SimilarityDetector, SkillScorer, type SkillState, SkillStatePolicy, type SkillVersion, SkillVersionStore, type SourceDocument, candidateToCatalogEntry, completeText, createEvaluationRuntime, createMissHook, jaccardSimilarity, normalizeCandidate, normalizeCandidateFiles, normalizeRisk, parseJsonObject, readDocument, sanitizeCandidateName, suggestFromFailedRun, validateCandidate };
@@ -1,5 +1,6 @@
1
1
  import { A as unzipWithLimits, b as isValidSkillName, d as assertSafePathSegment, j as validateSkills, k as resolveInsideRoot, u as WebSkillError } from "./dist-D0saNPi_.js";
2
- import { i as NodeFS, l as exportArchive } from "./dist-Dj6QjHBn.js";
2
+ import { S as WebSkillRuntime } from "./dist-DrySSQ5R.js";
3
+ import { i as NodeFS, s as ProcessSandboxExecutor, u as exportArchive } from "./dist-BRGQcLqR.js";
3
4
  import path from "node:path";
4
5
  import { tmpdir } from "node:os";
5
6
  import { mkdtemp } from "node:fs/promises";
@@ -840,6 +841,17 @@ var EvaluationRunner = class {
840
841
  };
841
842
  }
842
843
  };
844
+ /**
845
+ * 治理评估专用 runtime 装配(不可信技能试用路径):
846
+ * 默认注入 ProcessSandboxExecutor(fork + --permission 真实进程隔离);
847
+ * 可配置 executor 切回 SandboxedScriptExecutor(worker_threads 能力面收敛形态)。
848
+ */
849
+ function createEvaluationRuntime(deps) {
850
+ return new WebSkillRuntime({
851
+ ...deps,
852
+ executor: deps.executor ?? new ProcessSandboxExecutor(deps.fs)
853
+ });
854
+ }
843
855
  /** 失败 trace → 回归评估任务建议(prompt 复现 + expected 错误模式) */
844
856
  function suggestFromFailedRun(run) {
845
857
  const errorPatterns = run.trace.filter((e) => e.type === "tool.failed" || e.type === "run.failed").map((e) => String(e.data?.["code"] ?? e.message ?? "")).filter(Boolean);
@@ -1025,4 +1037,4 @@ function createMissHook(deps) {
1025
1037
  }
1026
1038
 
1027
1039
  //#endregion
1028
- export { AlwaysHumanApprovalPolicy, ApprovalWorkflow, CandidateStore, CompositeApprovalPolicy, DependencyGraph, DocumentSkillExtractor, EvaluationRunner, FailureAnalyzer, FsAuditLog, LlmCandidateGenerator, RepairPlanner, SCORING_WEIGHTS, SimilarityDetector, SkillScorer, SkillStatePolicy, SkillVersionStore, candidateToCatalogEntry, completeText, createMissHook, jaccardSimilarity, normalizeCandidate, normalizeCandidateFiles, normalizeRisk, parseJsonObject, readDocument, sanitizeCandidateName, suggestFromFailedRun, validateCandidate };
1040
+ export { AlwaysHumanApprovalPolicy, ApprovalWorkflow, CandidateStore, CompositeApprovalPolicy, DependencyGraph, DocumentSkillExtractor, EvaluationRunner, FailureAnalyzer, FsAuditLog, LlmCandidateGenerator, RepairPlanner, SCORING_WEIGHTS, SimilarityDetector, SkillScorer, SkillStatePolicy, SkillVersionStore, candidateToCatalogEntry, completeText, createEvaluationRuntime, createMissHook, jaccardSimilarity, normalizeCandidate, normalizeCandidateFiles, normalizeRisk, parseJsonObject, readDocument, sanitizeCandidateName, suggestFromFailedRun, validateCandidate };
@@ -1,5 +1,5 @@
1
1
  import { C as FileStat, I as SkillInstallSource, K as VerifyResult, T as JsonSchema, W as SkillsLockfile, _ as RenderResultRequest, 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, X as SchemaInferer, Z as ScriptExecutionContext, b as FsArtifactStore, d as BridgeCapabilities, it as ToolResult, nt as ToolDefinition, u as ApprovalScope, x as FsMemoryStore } from "./index-CySxIvRz.js";
2
+ import { N as NetworkPolicy, Q as ScriptExecutor, X as SchemaInferer, Z as ScriptExecutionContext, b as FsArtifactStore, d as BridgeCapabilities, it as ToolResult, nt as ToolDefinition, u as ApprovalScope, x as FsMemoryStore } from "./index-gjFuBevI.js";
3
3
  import { Readable, Writable } from "node:stream";
4
4
  //#region ../node/dist/index.d.ts
5
5
  //#region src/fs/nodeFs.d.ts
@@ -88,6 +88,43 @@ declare class SandboxedScriptExecutor implements ScriptExecutor {
88
88
  }): Promise<ToolResult>;
89
89
  }
90
90
  //#endregion
91
+ //#region src/executor/processSandboxExecutor.d.ts
92
+ interface ProcessSandboxOptions {
93
+ /** 温池大小(并发上限;执行后 kill 并补位重生),默认 2 */
94
+ poolSize?: number;
95
+ capabilities?: BridgeCapabilities;
96
+ /** 网络策略:默认 'deny-all'(权限模型无网络维度,补丁兜底;node:net 裸模块为已知残余面) */
97
+ networkPolicy?: NetworkPolicy;
98
+ /** require-approval 模式的授权询问出口 */
99
+ uiBridge?: UiBridge;
100
+ /** 授权粒度:默认 'once-per-run' */
101
+ approvalScope?: ApprovalScope;
102
+ }
103
+ /**
104
+ * child_process.fork + --permission 进程沙箱(真实进程隔离)。
105
+ * 每个子进程以 `--permission --allow-fs-read=<入口目录> --allow-fs-read=<skillRoot>
106
+ * --allow-fs-write=<artifactDir>` 启动:fs 维度由权限模型管控(experimental),
107
+ * 子进程/Worker/addons 默认禁;网络无权限维度仍靠 fetch/WebSocket patch。
108
+ * 能力桥协议与 worker_threads 沙箱同一形态(readReference/writeArtifact/confirm + 授权)。
109
+ * 温池(默认 2):同 skillRoot 复用子进程;执行后 kill 并补位重生;并发上限即池大小。
110
+ * 超时 kill(同步死循环可杀);非零退出码 → TOOL_EXECUTION_FAILED。
111
+ */
112
+ declare class ProcessSandboxExecutor implements ScriptExecutor {
113
+ #private;
114
+ constructor(fs: FileSystemProvider, options?: ProcessSandboxOptions);
115
+ get poolSize(): number;
116
+ /** 池全部子进程销毁(测试收尾/进程退出前调用) */
117
+ dispose(): Promise<void>;
118
+ loadDefinition(skillRoot: string, scriptName: string): Promise<ToolDefinition>;
119
+ execute(input: {
120
+ skillRoot: string;
121
+ scriptName: string;
122
+ args: Record<string, unknown>;
123
+ context: ScriptExecutionContext;
124
+ timeoutMs: number;
125
+ }): Promise<ToolResult>;
126
+ }
127
+ //#endregion
91
128
  //#region src/schema/oxcSchemaInferer.d.ts
92
129
  /**
93
130
  * D2 Schema 推导(OXC 静态文本分析,宿主侧执行,不进沙箱)。
@@ -191,4 +228,4 @@ declare function exportArchive(fs: FileSystemProvider, skillRoot: string, option
191
228
  /** 只解出归档中的 webskill.skill-manifest.json 条目(安装前预览) */
192
229
  declare function readArchiveManifest(fs: FileSystemProvider, archivePath: string): Promise<SkillManifest>;
193
230
  //#endregion
194
- export { NodeScriptExecutor as a, SandboxedScriptExecutor as c, readArchiveManifest as d, NodeFS as i, SkillManager as l, FileArtifactStore as n, OxcSchemaInferer as o, FileMemoryStore as r, SandboxOptions as s, CliUiBridge as t, exportArchive as u };
231
+ export { NodeScriptExecutor as a, ProcessSandboxOptions as c, SkillManager as d, exportArchive as f, NodeFS as i, SandboxOptions as l, FileArtifactStore as n, OxcSchemaInferer as o, readArchiveManifest as p, FileMemoryStore as r, ProcessSandboxExecutor as s, CliUiBridge as t, SandboxedScriptExecutor as u };
@@ -778,6 +778,8 @@ declare class WebSkillRuntime {
778
778
  #private;
779
779
  constructor(deps: WebSkillRuntimeDeps);
780
780
  get session(): RuntimeSession;
781
+ /** 当前装配的脚本执行器(治理默认执行器断言等只读场景) */
782
+ get executor(): ScriptExecutor | undefined;
781
783
  /** 生命周期事件总线(只读观测) */
782
784
  get events(): EventBus;
783
785
  /**
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { $ as checkDependencyCycles, A as SKILL_NAME_PATTERN, B as SkillMetadata, C as FileStat, D as SKILLS_LOCKFILE, E as MemoryFS, F as SkillDocument, G as ValidationReport, H as SkillReader, I as SkillInstallSource, J as WebSkillErrorCode, K as VerifyResult, L as SkillIssue, M as SkillCatalog, N as SkillCatalogEntry, O as SKILL_MANIFEST_FILE, P as SkillDiscovery, Q as buildManifest, R as SkillLocation, S as DiscoveryResult, T as JsonSchema, U as SkillSource, V as SkillPackManifest, W as SkillsLockfile, X as atomicWriteText, Y as assertSafePathSegment, Z as buildCatalog, _ as RenderResultRequest, _t as xmlRenderer, a as InteractionPolicy, at as jsonRenderer, b as CatalogRenderer, c as LlmClient, ct as parseSkillPackManifest, d as LlmResponse, dt as renderCatalogJson, et as checkSkillRules, f as LlmStreamEvent, ft as resolveArchiveLimits, g as RenderBlock, gt as verifyManifest, h as MemoryStore, ht as validateSkills, i as FormField, it as isValidSkillName, j as SKILL_PACK_FILE, k as SKILL_NAME_MAX_LENGTH, l as LlmCompleteInput, lt as readResponseWithLimit, m as LlmToolSpec, mt as unzipWithLimits, n as ArtifactStore, nt as escapeXml, o as InteractionRequest, ot as normalizePath, p as LlmToolCall, pt as resolveInsideRoot, q as WebSkillError, r as ChartSpec, rt as exportSkills, s as InteractionResponse, st as parseSkillMarkdown, t as Artifact, tt as computeDigest, u as LlmMessage, ut as renderAvailableSkillsXml, v as UiBridge, w as FileSystemProvider, x as DEFAULT_ARCHIVE_LIMITS, y as ArchiveLimits, z as SkillManifest } from "./types-CKm5G_eQ-8W8FnP4u.js";
2
- import { $ as SerializingMemoryStore, A as LifecycleHook, B as RUN_SNAPSHOT_SCHEMA_VERSION, C as FullDisclosureRouter, Ct as normalizeToolContent, D as HookRunnerOptions, Dt as toLlmToolSpec, E as HookRunner, Et as schemaToForm, F as OpenAiCompatibleClientConfig, G as RunTerminationReason, H as RunResult, I as ProgressiveRouter, J as RuntimeSession, K as RuntimePhase, L as READ_SKILL_FILE_INPUT_SCHEMA, M as LifecycleListener, N as NetworkPolicy, O as InstalledSkillManifest, Ot as toVercelToolSpecs, P as OpenAiCompatibleClient, Q as ScriptExecutor, R as READ_SKILL_FILE_TOOL, S as FsRunSnapshotStore, St as networkUrlHost, T as GoogleGenAiClientConfig, Tt as resolveToolName, U as RunSnapshot, V as RouteResult, W as RunSnapshotStore, X as SchemaInferer, Y as RuntimeSessionHandle, Z as ScriptExecutionContext, _ as EventBus, _t as extractChartSpec, a as AgentLoopConfig, at as TraceClock, b as FsArtifactStore, bt as isNetworkAllowed, c as AnthropicClientConfig, ct as TraceRecorder, d as BridgeCapabilities, dt as WebSkillRuntime, et as SkillRouter, f as BridgeCapability, ft as WebSkillRuntimeDeps, g as CapabilityMode, gt as createWebSkillApi, h as CapabilityApproval, ht as createScriptContext, i as AgentLoop, it as ToolResult, j as LifecycleHookContext, k as LifecycleEvent, l as ApprovalDecision, lt as VercelToolSpec, m as BridgeResponse, mt as buildRenderResult, n as ASK_USER_TOOL, nt as ToolDefinition, o as AgentLoopDeps, ot as TraceEvent, p as BridgeRequest, pt as bridgeError, q as RuntimeRun, r as ASK_USER_TOOL_NAME, rt as ToolResolution, s as AnthropicClient, st as TraceEventType, t as ASK_USER_INPUT_SCHEMA, tt as SkillStateGuard, u as ApprovalScope, ut as WebSkillApi, v as ExternalSkillProvider, vt as fromVercelResult, w as GoogleGenAiClient, wt as parseBridgeRequest, x as FsMemoryStore, xt as mergeCatalogEntries, y as ExternalToolSource, yt as fromVercelStreamPart, z as READ_SKILL_FILE_TOOL_NAME } from "./index-CySxIvRz.js";
2
+ import { $ as SerializingMemoryStore, A as LifecycleHook, B as RUN_SNAPSHOT_SCHEMA_VERSION, C as FullDisclosureRouter, Ct as normalizeToolContent, D as HookRunnerOptions, Dt as toLlmToolSpec, E as HookRunner, Et as schemaToForm, F as OpenAiCompatibleClientConfig, G as RunTerminationReason, H as RunResult, I as ProgressiveRouter, J as RuntimeSession, K as RuntimePhase, L as READ_SKILL_FILE_INPUT_SCHEMA, M as LifecycleListener, N as NetworkPolicy, O as InstalledSkillManifest, Ot as toVercelToolSpecs, P as OpenAiCompatibleClient, Q as ScriptExecutor, R as READ_SKILL_FILE_TOOL, S as FsRunSnapshotStore, St as networkUrlHost, T as GoogleGenAiClientConfig, Tt as resolveToolName, U as RunSnapshot, V as RouteResult, W as RunSnapshotStore, X as SchemaInferer, Y as RuntimeSessionHandle, Z as ScriptExecutionContext, _ as EventBus, _t as extractChartSpec, a as AgentLoopConfig, at as TraceClock, b as FsArtifactStore, bt as isNetworkAllowed, c as AnthropicClientConfig, ct as TraceRecorder, d as BridgeCapabilities, dt as WebSkillRuntime, et as SkillRouter, f as BridgeCapability, ft as WebSkillRuntimeDeps, g as CapabilityMode, gt as createWebSkillApi, h as CapabilityApproval, ht as createScriptContext, i as AgentLoop, it as ToolResult, j as LifecycleHookContext, k as LifecycleEvent, l as ApprovalDecision, lt as VercelToolSpec, m as BridgeResponse, mt as buildRenderResult, n as ASK_USER_TOOL, nt as ToolDefinition, o as AgentLoopDeps, ot as TraceEvent, p as BridgeRequest, pt as bridgeError, q as RuntimeRun, r as ASK_USER_TOOL_NAME, rt as ToolResolution, s as AnthropicClient, st as TraceEventType, t as ASK_USER_INPUT_SCHEMA, tt as SkillStateGuard, u as ApprovalScope, ut as WebSkillApi, v as ExternalSkillProvider, vt as fromVercelResult, w as GoogleGenAiClient, wt as parseBridgeRequest, x as FsMemoryStore, xt as mergeCatalogEntries, y as ExternalToolSource, yt as fromVercelStreamPart, z as READ_SKILL_FILE_TOOL_NAME } from "./index-gjFuBevI.js";
3
3
  export { ASK_USER_INPUT_SCHEMA, ASK_USER_TOOL, ASK_USER_TOOL_NAME, AgentLoop, type AgentLoopConfig, type AgentLoopDeps, AnthropicClient, type AnthropicClientConfig, type ApprovalDecision, type ApprovalScope, type ArchiveLimits, type Artifact, type ArtifactStore, type BridgeCapabilities, type BridgeCapability, type BridgeRequest, type BridgeResponse, CapabilityApproval, type CapabilityMode, type CatalogRenderer, type ChartSpec, DEFAULT_ARCHIVE_LIMITS, type DiscoveryResult, EventBus, type ExternalSkillProvider, type ExternalToolSource, type FileStat, type FileSystemProvider, type FormField, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FullDisclosureRouter, GoogleGenAiClient, type GoogleGenAiClientConfig, HookRunner, type HookRunnerOptions, type InstalledSkillManifest, type InteractionPolicy, type InteractionRequest, type InteractionResponse, type JsonSchema, type LifecycleEvent, type LifecycleHook, type LifecycleHookContext, type LifecycleListener, type LlmClient, type LlmCompleteInput, type LlmMessage, type LlmResponse, type LlmStreamEvent, type LlmToolCall, type LlmToolSpec, MemoryFS, type MemoryStore, type NetworkPolicy, OpenAiCompatibleClient, type OpenAiCompatibleClientConfig, ProgressiveRouter, READ_SKILL_FILE_INPUT_SCHEMA, READ_SKILL_FILE_TOOL, READ_SKILL_FILE_TOOL_NAME, RUN_SNAPSHOT_SCHEMA_VERSION, type RenderBlock, type RenderResultRequest, type RouteResult, type RunResult, type RunSnapshot, type RunSnapshotStore, type RunTerminationReason, type RuntimePhase, type RuntimeRun, type RuntimeSession, type RuntimeSessionHandle, SKILLS_LOCKFILE, SKILL_MANIFEST_FILE, SKILL_NAME_MAX_LENGTH, SKILL_NAME_PATTERN, SKILL_PACK_FILE, type SchemaInferer, type ScriptExecutionContext, type ScriptExecutor, SerializingMemoryStore, type SkillCatalog, type SkillCatalogEntry, SkillDiscovery, type SkillDocument, type SkillInstallSource, type SkillIssue, type SkillLocation, type SkillManifest, type SkillMetadata, type SkillPackManifest, SkillReader, type SkillRouter, type SkillSource, type SkillStateGuard, type SkillsLockfile, type ToolDefinition, type ToolResolution, type ToolResult, type TraceClock, type TraceEvent, type TraceEventType, TraceRecorder, type UiBridge, type ValidationReport, type VercelToolSpec, type VerifyResult, type WebSkillApi, WebSkillError, type WebSkillErrorCode, WebSkillRuntime, type WebSkillRuntimeDeps, assertSafePathSegment, atomicWriteText, bridgeError, buildCatalog, buildManifest, buildRenderResult, checkDependencyCycles, checkSkillRules, computeDigest, createScriptContext, createWebSkillApi, escapeXml, exportSkills, extractChartSpec, fromVercelResult, fromVercelStreamPart, isNetworkAllowed, isValidSkillName, jsonRenderer, mergeCatalogEntries, networkUrlHost, normalizePath, normalizeToolContent, parseBridgeRequest, parseSkillMarkdown, parseSkillPackManifest, readResponseWithLimit, renderAvailableSkillsXml, renderCatalogJson, resolveArchiveLimits, resolveInsideRoot, resolveToolName, schemaToForm, toLlmToolSpec, toVercelToolSpecs, unzipWithLimits, validateSkills, verifyManifest, xmlRenderer };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { A as unzipWithLimits, C as parseSkillMarkdown, D as renderCatalogJson, E as renderAvailableSkillsXml, M as verifyManifest, N as xmlRenderer, O as resolveArchiveLimits, S as normalizePath, T as readResponseWithLimit, _ as computeDigest, a as SKILL_NAME_MAX_LENGTH, b as isValidSkillName, c as SkillDiscovery, d as assertSafePathSegment, f as atomicWriteText, g as checkSkillRules, h as checkDependencyCycles, i as SKILL_MANIFEST_FILE, j as validateSkills, k as resolveInsideRoot, l as SkillReader, m as buildManifest, n as MemoryFS, o as SKILL_NAME_PATTERN, p as buildCatalog, r as SKILLS_LOCKFILE, s as SKILL_PACK_FILE, t as DEFAULT_ARCHIVE_LIMITS, u as WebSkillError, v as escapeXml, w as parseSkillPackManifest, x as jsonRenderer, y as exportSkills } from "./dist-D0saNPi_.js";
2
- import { A as isNetworkAllowed, C as bridgeError, D as extractChartSpec, E as createWebSkillApi, F as resolveToolName, I as schemaToForm, L as toLlmToolSpec, M as networkUrlHost, N as normalizeToolContent, O as fromVercelResult, P as parseBridgeRequest, R as toVercelToolSpecs, S as WebSkillRuntime, T as createScriptContext, _ as READ_SKILL_FILE_TOOL, a as AnthropicClient, b as SerializingMemoryStore, c as FsArtifactStore, d as FullDisclosureRouter, f as GoogleGenAiClient, g as READ_SKILL_FILE_INPUT_SCHEMA, h as ProgressiveRouter, i as AgentLoop, j as mergeCatalogEntries, k as fromVercelStreamPart, l as FsMemoryStore, m as OpenAiCompatibleClient, n as ASK_USER_TOOL, o as CapabilityApproval, p as HookRunner, r as ASK_USER_TOOL_NAME, s as EventBus, t as ASK_USER_INPUT_SCHEMA, u as FsRunSnapshotStore, v as READ_SKILL_FILE_TOOL_NAME, w as buildRenderResult, x as TraceRecorder, y as RUN_SNAPSHOT_SCHEMA_VERSION } from "./dist-BS5OpedX.js";
2
+ import { A as isNetworkAllowed, C as bridgeError, D as extractChartSpec, E as createWebSkillApi, F as resolveToolName, I as schemaToForm, L as toLlmToolSpec, M as networkUrlHost, N as normalizeToolContent, O as fromVercelResult, P as parseBridgeRequest, R as toVercelToolSpecs, S as WebSkillRuntime, T as createScriptContext, _ as READ_SKILL_FILE_TOOL, a as AnthropicClient, b as SerializingMemoryStore, c as FsArtifactStore, d as FullDisclosureRouter, f as GoogleGenAiClient, g as READ_SKILL_FILE_INPUT_SCHEMA, h as ProgressiveRouter, i as AgentLoop, j as mergeCatalogEntries, k as fromVercelStreamPart, l as FsMemoryStore, m as OpenAiCompatibleClient, n as ASK_USER_TOOL, o as CapabilityApproval, p as HookRunner, r as ASK_USER_TOOL_NAME, s as EventBus, t as ASK_USER_INPUT_SCHEMA, u as FsRunSnapshotStore, v as READ_SKILL_FILE_TOOL_NAME, w as buildRenderResult, x as TraceRecorder, y as RUN_SNAPSHOT_SCHEMA_VERSION } from "./dist-DrySSQ5R.js";
3
3
 
4
4
  export { ASK_USER_INPUT_SCHEMA, ASK_USER_TOOL, ASK_USER_TOOL_NAME, AgentLoop, AnthropicClient, CapabilityApproval, DEFAULT_ARCHIVE_LIMITS, EventBus, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FullDisclosureRouter, GoogleGenAiClient, HookRunner, MemoryFS, OpenAiCompatibleClient, ProgressiveRouter, READ_SKILL_FILE_INPUT_SCHEMA, READ_SKILL_FILE_TOOL, READ_SKILL_FILE_TOOL_NAME, RUN_SNAPSHOT_SCHEMA_VERSION, SKILLS_LOCKFILE, SKILL_MANIFEST_FILE, SKILL_NAME_MAX_LENGTH, SKILL_NAME_PATTERN, SKILL_PACK_FILE, SerializingMemoryStore, SkillDiscovery, SkillReader, TraceRecorder, WebSkillError, WebSkillRuntime, assertSafePathSegment, atomicWriteText, bridgeError, buildCatalog, buildManifest, buildRenderResult, checkDependencyCycles, checkSkillRules, computeDigest, createScriptContext, createWebSkillApi, escapeXml, exportSkills, extractChartSpec, fromVercelResult, fromVercelStreamPart, isNetworkAllowed, isValidSkillName, jsonRenderer, mergeCatalogEntries, networkUrlHost, normalizePath, normalizeToolContent, parseBridgeRequest, parseSkillMarkdown, parseSkillPackManifest, readResponseWithLimit, renderAvailableSkillsXml, renderCatalogJson, resolveArchiveLimits, resolveInsideRoot, resolveToolName, schemaToForm, toLlmToolSpec, toVercelToolSpecs, unzipWithLimits, validateSkills, verifyManifest, xmlRenderer };
package/dist/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { F as SkillDocument, N as SkillCatalogEntry, T as JsonSchema, m as LlmToolSpec } from "./types-CKm5G_eQ-8W8FnP4u.js";
2
- import { it as ToolResult, v as ExternalSkillProvider, xt as mergeCatalogEntries, y as ExternalToolSource } from "./index-CySxIvRz.js";
2
+ import { it as ToolResult, v as ExternalSkillProvider, xt as mergeCatalogEntries, y as ExternalToolSource } from "./index-gjFuBevI.js";
3
3
  import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
4
4
  import { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
5
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/dist/mcp.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { u as WebSkillError } from "./dist-D0saNPi_.js";
2
- import { N as normalizeToolContent, j as mergeCatalogEntries } from "./dist-BS5OpedX.js";
2
+ import { N as normalizeToolContent, j as mergeCatalogEntries } from "./dist-DrySSQ5R.js";
3
3
 
4
4
  //#region ../mcp/dist/index.js
5
5
  /**
package/dist/node.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { D as SKILLS_LOCKFILE, I as SkillInstallSource, K as VerifyResult, O as SKILL_MANIFEST_FILE, W as SkillsLockfile, z as SkillManifest } from "./types-CKm5G_eQ-8W8FnP4u.js";
2
- import { ht as createScriptContext } from "./index-CySxIvRz.js";
2
+ import { ht as createScriptContext } from "./index-gjFuBevI.js";
3
3
  import { n as LlmEnvConfig, s as probeLlmCapabilities, t as LlmCapabilities } from "./env-BPUBZCwJ-4jat_SVG.js";
4
- import { a as NodeScriptExecutor, c as SandboxedScriptExecutor, d as readArchiveManifest, i as NodeFS, l as SkillManager, n as FileArtifactStore, o as OxcSchemaInferer, r as FileMemoryStore, s as SandboxOptions, t as CliUiBridge, u as exportArchive } from "./index-Vn63HJRO.js";
5
- export { CliUiBridge, FileArtifactStore, FileMemoryStore, type LlmCapabilities, type LlmEnvConfig, NodeFS, NodeScriptExecutor, OxcSchemaInferer, SKILLS_LOCKFILE, SKILL_MANIFEST_FILE, type SandboxOptions, SandboxedScriptExecutor, type SkillInstallSource, SkillManager, type SkillManifest, type SkillsLockfile, type VerifyResult, createScriptContext, exportArchive, probeLlmCapabilities, readArchiveManifest };
4
+ import { a as NodeScriptExecutor, c as ProcessSandboxOptions, d as SkillManager, f as exportArchive, i as NodeFS, l as SandboxOptions, n as FileArtifactStore, o as OxcSchemaInferer, p as readArchiveManifest, r as FileMemoryStore, s as ProcessSandboxExecutor, t as CliUiBridge, u as SandboxedScriptExecutor } from "./index-BmzysJX5.js";
5
+ export { CliUiBridge, FileArtifactStore, FileMemoryStore, type LlmCapabilities, type LlmEnvConfig, NodeFS, NodeScriptExecutor, OxcSchemaInferer, ProcessSandboxExecutor, type ProcessSandboxOptions, SKILLS_LOCKFILE, SKILL_MANIFEST_FILE, type SandboxOptions, SandboxedScriptExecutor, type SkillInstallSource, SkillManager, type SkillManifest, type SkillsLockfile, type VerifyResult, createScriptContext, exportArchive, probeLlmCapabilities, readArchiveManifest };
package/dist/node.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { i as SKILL_MANIFEST_FILE, r as SKILLS_LOCKFILE } from "./dist-D0saNPi_.js";
2
- import { T as createScriptContext } from "./dist-BS5OpedX.js";
2
+ import { T as createScriptContext } from "./dist-DrySSQ5R.js";
3
3
  import { i as probeLlmCapabilities } from "./env--jJB-TSX-04klhTYi.js";
4
- import { a as NodeScriptExecutor, c as SkillManager, i as NodeFS, l as exportArchive, n as FileArtifactStore, o as OxcSchemaInferer, r as FileMemoryStore, s as SandboxedScriptExecutor, t as CliUiBridge, u as readArchiveManifest } from "./dist-Dj6QjHBn.js";
4
+ import { a as NodeScriptExecutor, c as SandboxedScriptExecutor, d as readArchiveManifest, i as NodeFS, l as SkillManager, n as FileArtifactStore, o as OxcSchemaInferer, r as FileMemoryStore, s as ProcessSandboxExecutor, t as CliUiBridge, u as exportArchive } from "./dist-BRGQcLqR.js";
5
5
 
6
- export { CliUiBridge, FileArtifactStore, FileMemoryStore, NodeFS, NodeScriptExecutor, OxcSchemaInferer, SKILLS_LOCKFILE, SKILL_MANIFEST_FILE, SandboxedScriptExecutor, SkillManager, createScriptContext, exportArchive, probeLlmCapabilities, readArchiveManifest };
6
+ export { CliUiBridge, FileArtifactStore, FileMemoryStore, NodeFS, NodeScriptExecutor, OxcSchemaInferer, ProcessSandboxExecutor, SKILLS_LOCKFILE, SKILL_MANIFEST_FILE, SandboxedScriptExecutor, SkillManager, createScriptContext, exportArchive, probeLlmCapabilities, readArchiveManifest };
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,202 @@
1
+ import path from "node:path";
2
+ import { tmpdir } from "node:os";
3
+ import { pathToFileURL } from "node:url";
4
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
5
+
6
+ //#region ../node/dist/executor/processSandboxEntry.js
7
+ /**
8
+ * ProcessSandbox 子进程入口(child_process.fork + --permission)。
9
+ * 与 worker_threads 入口同构:任务经 workerData 等价的 argv[2] JSON 传入(fork 无 workerData,
10
+ * 经环境变量 PROCESS_SANDBOX_TASK 传 JSON);能力桥 RPC 经 process IPC。
11
+ *
12
+ * 权限模型(--permission,experimental)负责 fs 维度:读仅限 skillRoot(+入口目录),
13
+ * 写仅限 artifactDir;子进程/Worker/addons 默认禁(module.register 在其下不可用——
14
+ * 内部钩子线程同属 WorkerThreads,实证后已移除裸模块钩子)。网络无权限维度:
15
+ * 仍靠 globalThis.fetch/WebSocket patch(函数源码经任务 JSON 注入,单一来源在
16
+ * runtime/sandbox/networkPolicy.ts);node:net 裸模块为已知残余面(文档明示)。
17
+ * .js 走 data: URL 导入(permission 下实证可用);.ts 走中性临时目录 file URL。
18
+ */
19
+ const send = (message) => {
20
+ process.send?.(message);
21
+ };
22
+ const pending = /* @__PURE__ */ new Map();
23
+ let bridgeSeq = 0;
24
+ function callCapability(kind, payload) {
25
+ const request = {
26
+ kind,
27
+ id: `br-${++bridgeSeq}`,
28
+ ...payload
29
+ };
30
+ return new Promise((resolve, reject) => {
31
+ pending.set(request.id, {
32
+ resolve,
33
+ reject
34
+ });
35
+ send({
36
+ type: "bridge",
37
+ request
38
+ });
39
+ }).then((response) => {
40
+ const r = response;
41
+ if (!r.ok) {
42
+ const err = new Error(r.error?.message ?? "capability call failed");
43
+ err.code = r.error?.code ?? "TOOL_EXECUTION_FAILED";
44
+ throw err;
45
+ }
46
+ return r.value;
47
+ });
48
+ }
49
+ function blockedError(host) {
50
+ const err = /* @__PURE__ */ new Error(`Network request blocked by sandbox network policy: ${host}`);
51
+ err.code = "NETWORK_BLOCKED";
52
+ return err;
53
+ }
54
+ function setupNetworkGuards(task) {
55
+ const policy = task.networkPolicy ?? "deny-all";
56
+ if (!task.networkPolicyLib) return;
57
+ const { isNetworkAllowed, networkUrlHost } = new Function(`${task.networkPolicyLib}\nreturn { isNetworkAllowed: isNetworkAllowed, networkUrlHost: networkUrlHost };`)();
58
+ const gate = (rawUrl) => {
59
+ const url = String(rawUrl);
60
+ if (isNetworkAllowed(policy, url)) return null;
61
+ const host = networkUrlHost(url);
62
+ send({
63
+ type: "network-blocked",
64
+ host
65
+ });
66
+ return blockedError(host);
67
+ };
68
+ const originalFetch = globalThis["fetch"];
69
+ if (typeof originalFetch === "function") globalThis["fetch"] = (input, init) => {
70
+ const raw = typeof input === "string" ? input : input instanceof URL ? input.href : input?.url ?? String(input);
71
+ const blocked = gate(raw);
72
+ if (blocked) return Promise.reject(blocked);
73
+ return originalFetch(input, init);
74
+ };
75
+ const OriginalWebSocket = globalThis["WebSocket"];
76
+ if (typeof OriginalWebSocket === "function") globalThis["WebSocket"] = class extends OriginalWebSocket {
77
+ constructor(url, protocols) {
78
+ const blocked = gate(String(url));
79
+ if (blocked) throw blocked;
80
+ super(url, protocols);
81
+ }
82
+ };
83
+ }
84
+ let task_scratchDir;
85
+ async function loadModule(scriptPath, scriptSource) {
86
+ if (scriptPath.endsWith(".js") && scriptSource !== void 0) return await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(`${scriptSource}\n//# ${Date.now()}`)}`);
87
+ if (scriptPath.endsWith(".ts") && scriptSource !== void 0) {
88
+ const base = task_scratchDir ?? await mkdtemp(path.join(tmpdir(), "webskill-psbx-ts-"));
89
+ const dir = await mkdtemp(path.join(base, "ts-"));
90
+ try {
91
+ const file = path.join(dir, "script.ts");
92
+ await writeFile(file, scriptSource);
93
+ return await import(`${pathToFileURL(file).href}?t=${Date.now()}`);
94
+ } finally {
95
+ await rm(dir, {
96
+ recursive: true,
97
+ force: true
98
+ });
99
+ }
100
+ }
101
+ return await import(`${pathToFileURL(scriptPath).href}?t=${Date.now()}`);
102
+ }
103
+ async function main(task) {
104
+ task_scratchDir = task.scratchDir;
105
+ setupNetworkGuards(task);
106
+ process.on("message", (msg) => {
107
+ if (msg?.type === "bridge-response" && msg.response?.id) {
108
+ const entry = pending.get(msg.response.id);
109
+ if (entry) {
110
+ pending.delete(msg.response.id);
111
+ entry.resolve(msg.response);
112
+ }
113
+ }
114
+ });
115
+ if (task.mode === "load") {
116
+ try {
117
+ const mod = await loadModule(task.scriptPath, task.scriptSource);
118
+ send({
119
+ type: "load-result",
120
+ ok: true,
121
+ definition: {
122
+ description: typeof mod["description"] === "string" ? mod["description"] : void 0,
123
+ inputSchema: mod["inputSchema"] !== void 0 ? mod["inputSchema"] : void 0,
124
+ hasRun: typeof mod["run"] === "function"
125
+ }
126
+ });
127
+ } catch (e) {
128
+ const err = e;
129
+ send({
130
+ type: "load-result",
131
+ ok: false,
132
+ error: {
133
+ code: err?.code ?? "TOOL_EXECUTION_FAILED",
134
+ message: String(err?.message ?? e)
135
+ }
136
+ });
137
+ }
138
+ return;
139
+ }
140
+ const stdout = [];
141
+ const stderr = [];
142
+ const originals = {
143
+ log: console.log,
144
+ info: console.info,
145
+ debug: console.debug,
146
+ warn: console.warn,
147
+ error: console.error
148
+ };
149
+ console.log = console.info = console.debug = (...args) => stdout.push(args.map(String).join(" "));
150
+ console.warn = console.error = (...args) => stderr.push(args.map(String).join(" "));
151
+ try {
152
+ const mod = await loadModule(task.scriptPath, task.scriptSource);
153
+ if (typeof mod["run"] !== "function") throw new Error("Script does not export a run function");
154
+ const context = {
155
+ skillName: task.skillName ?? "",
156
+ runId: task.runId ?? "",
157
+ readReference: (path) => callCapability("readReference", { path }),
158
+ writeArtifact: (path, content, options) => callCapability("writeArtifact", {
159
+ path,
160
+ content: typeof content === "string" ? content : Array.from(content ?? []),
161
+ mimeType: options?.mimeType
162
+ }),
163
+ confirm: (message) => callCapability("confirm", { message })
164
+ };
165
+ const runFn = mod["run"];
166
+ const value = await runFn(task.args ?? {}, context);
167
+ send({
168
+ type: "execute-result",
169
+ ok: true,
170
+ value: value === void 0 ? null : value,
171
+ stdout,
172
+ stderr
173
+ });
174
+ } catch (e) {
175
+ const err = e;
176
+ send({
177
+ type: "execute-result",
178
+ ok: false,
179
+ error: {
180
+ code: err?.code ?? "TOOL_EXECUTION_FAILED",
181
+ message: String(err?.message ?? e)
182
+ },
183
+ stdout,
184
+ stderr
185
+ });
186
+ } finally {
187
+ console.log = originals.log;
188
+ console.info = originals.info;
189
+ console.debug = originals.debug;
190
+ console.warn = originals.warn;
191
+ console.error = originals.error;
192
+ }
193
+ }
194
+ process.on("message", (msg) => {
195
+ if (msg?.type === "task" && msg.task) main(msg.task).then(() => process.exit(0), (e) => {
196
+ console.error(e);
197
+ process.exit(1);
198
+ });
199
+ });
200
+
201
+ //#endregion
202
+ export { };
package/dist/ui.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { _ as RenderResultRequest, g as RenderBlock, o as InteractionRequest, r as ChartSpec, s as InteractionResponse, v as UiBridge } from "./types-CKm5G_eQ-8W8FnP4u.js";
2
- import { mt as buildRenderResult } from "./index-CySxIvRz.js";
2
+ import { mt as buildRenderResult } from "./index-gjFuBevI.js";
3
3
  //#region ../ui/dist/index.d.ts
4
4
  //#region src/model/formModel.d.ts
5
5
  interface FormModel {
package/dist/ui.js CHANGED
@@ -1,4 +1,4 @@
1
- import { w as buildRenderResult } from "./dist-BS5OpedX.js";
1
+ import { w as buildRenderResult } from "./dist-DrySSQ5R.js";
2
2
  import { C as renderRenderResult, D as toVercelToolInvocation, E as toOpenUiLang, S as renderMiniMarkdown, T as toA2uiMessages, _ as fromOpenUiAction, a as CHART_PALETTE, b as renderBlocks, c as OPENUI_SUBMIT_ACTION, d as WEBSKILL_STYLES_CSS, f as WebFormBridge, g as fromA2uiAction, h as ensureStyles, i as A2UI_VERSION, l as VERCEL_INTERACTION_TOOL_NAME, m as collectValues, n as A2UI_CANCEL_ACTION, o as LitRendererBridge, p as chartToTable, r as A2UI_SUBMIT_ACTION, s as OPENUI_CANCEL_ACTION, t as A2UI_BASIC_CATALOG_ID, u as VercelUiBridge, v as fromVercelToolResult, w as shapeInteractionValue, x as renderMiniChart, y as interactionToFormModel } from "./dist-BJobG0i-.js";
3
3
 
4
4
  export { A2UI_BASIC_CATALOG_ID, A2UI_CANCEL_ACTION, A2UI_SUBMIT_ACTION, A2UI_VERSION, CHART_PALETTE, LitRendererBridge, OPENUI_CANCEL_ACTION, OPENUI_SUBMIT_ACTION, VERCEL_INTERACTION_TOOL_NAME, VercelUiBridge, WEBSKILL_STYLES_CSS, WebFormBridge, buildRenderResult, chartToTable, collectValues, ensureStyles, fromA2uiAction, fromOpenUiAction, fromVercelToolResult, interactionToFormModel, renderBlocks, renderMiniChart, renderMiniMarkdown, renderRenderResult, shapeInteractionValue, toA2uiMessages, toOpenUiLang, toVercelToolInvocation };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webskill/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "WebSkill \u2014 browser/Node agent skill runtime (skills, tools, MCP, governance, UI)",
5
5
  "license": "MIT",
6
6
  "type": "module",