@webskill/sdk 0.0.6 → 0.1.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 +180 -46
- package/dist/browser.d.ts +5 -1
- package/dist/browser.js +9 -2
- package/dist/{dist-33_zuOCm.js → dist-Cc3Mqi3_.js} +5 -1
- package/dist/{dist-nXiR40hi.js → dist-D5fsiETF.js} +36 -752
- package/dist/{dist-BQruQ9Hv.js → dist-Dv4a1Jkm.js} +15 -3
- package/dist/governance.d.ts +3 -2
- package/dist/governance.js +2 -2
- package/dist/{index-vi7GPemR.d.ts → index-Dc4X2N54.d.ts} +3 -1
- package/dist/{index-DCifjtJx.d.ts → index-oG5Nzgb9.d.ts} +19 -595
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/mcp.d.ts +3 -1
- package/dist/mcp.js +3 -1
- package/dist/memoryArtifactStore-C9lFVqPF-U8nXvqL9.js +637 -0
- package/dist/node.d.ts +4 -3
- package/dist/node.js +3 -2
- package/dist/testing-pn3NhXSV.js +117 -0
- package/dist/testing.d.ts +73 -0
- package/dist/testing.js +4 -0
- package/dist/types-CgNC-oQu-Dq_A1yA-.d.ts +539 -0
- package/dist/ui-react.d.ts +1 -1
- package/dist/ui-react.js +1 -1
- package/dist/ui-vue.d.ts +1 -1
- package/dist/ui-vue.js +1 -1
- package/dist/ui.d.ts +17 -4
- package/dist/ui.js +2 -2
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -9,22 +9,25 @@ governance.
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
- **Skill protocol** — Agent Skills compatible discovery, validation, cataloging
|
|
12
|
-
(JSON / XML), and
|
|
12
|
+
(JSON / XML), declarative `dependencies` and `allowed-tools`, skill packs
|
|
13
|
+
(multi-skill zip export/import with integrity digests).
|
|
13
14
|
- **Agent loop** — multi-turn tool calling with structured errors fed back to
|
|
14
|
-
the LLM, guardrails, lifecycle events, memory,
|
|
15
|
-
(missing-parameter forms, confirmations,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
the LLM, guardrails, lifecycle events, memory, human interaction
|
|
16
|
+
(missing-parameter forms, confirmations, authorization prompts), and
|
|
17
|
+
resumable interrupted runs.
|
|
18
|
+
- **Script sandbox** — `worker_threads` sandbox for Node, Web Worker sandbox for
|
|
19
|
+
the browser. Deny-by-default network policy, per-capability forced approval,
|
|
20
|
+
timeouts kill runaway scripts.
|
|
21
|
+
- **`navigator.webskill`** — a browser facade (`discover` / `read` / `validate` /
|
|
22
|
+
`run` / `install` / `uninstall`) assembled explicitly in one call.
|
|
20
23
|
- **MCP** — call page-provided tools and consume page-declared dynamic skills
|
|
21
24
|
over standard MCP transports (MessageChannel).
|
|
22
25
|
- **Governance** — LLM-generated skill candidates with mandatory review,
|
|
23
26
|
approval workflows, audit log, versioning/rollback, quarantine, evaluation,
|
|
24
27
|
and scoring.
|
|
25
|
-
- **UI** — framework-agnostic
|
|
26
|
-
adapters (Vercel AI SDK, OpenUI, A2UI), plus React and
|
|
27
|
-
primitives.
|
|
28
|
+
- **UI** — framework-agnostic forms, result rendering (markdown, tables, SVG
|
|
29
|
+
charts), generative UI adapters (Vercel AI SDK, OpenUI, A2UI), plus React and
|
|
30
|
+
Vue component primitives.
|
|
28
31
|
|
|
29
32
|
## Installation
|
|
30
33
|
|
|
@@ -35,53 +38,184 @@ npm install @webskill/sdk
|
|
|
35
38
|
React/Vue integrations require their respective peer dependencies
|
|
36
39
|
(`react` + `react-dom`, or `vue`), which are optional.
|
|
37
40
|
|
|
41
|
+
## Requirements
|
|
42
|
+
|
|
43
|
+
- Node.js >= 22.18 (native type stripping for `.ts` skill scripts)
|
|
44
|
+
- Modern browsers for the browser host (OPFS, Web Workers, ES modules)
|
|
45
|
+
|
|
38
46
|
## Quick start (Node)
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
});
|
|
48
|
+
Runs as-is with the bundled mock LLM — see
|
|
49
|
+
[`examples/quickstart-node`](../examples/quickstart-node) for the full project.
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
import { WebSkillRuntime } from '@webskill/sdk';
|
|
53
|
+
import { NodeFS, SandboxedScriptExecutor } from '@webskill/sdk/node';
|
|
54
|
+
import { MockLlmClient } from '@webskill/sdk/testing';
|
|
55
|
+
|
|
56
|
+
const fs = new NodeFS();
|
|
57
|
+
const llm = new MockLlmClient([
|
|
58
|
+
{ toolCalls: [{ id: 'c1', name: 'read_skill_file', arguments: { skillName: 'greeter' } }] },
|
|
59
|
+
{ toolCalls: [{ id: 'c2', name: 'greeter__greet', arguments: { text: 'world' } }] },
|
|
60
|
+
{ content: 'Greeted the world.' }
|
|
61
|
+
]);
|
|
55
62
|
|
|
56
|
-
|
|
57
|
-
const { entries, issues } = await runtime.discover();
|
|
58
|
-
console.log(
|
|
59
|
-
entries.map((e) => e.name),
|
|
60
|
-
issues
|
|
61
|
-
);
|
|
63
|
+
const runtime = new WebSkillRuntime({ fs, roots: ['./skills'], llm, executor: new SandboxedScriptExecutor(fs) });
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
const catalog = await runtime.discover();
|
|
66
|
+
console.log(catalog.entries.map((e) => e.name));
|
|
67
|
+
|
|
68
|
+
const { output, run } = await runtime.run('Greet the world.');
|
|
65
69
|
console.log(output, run.status, run.trace.length);
|
|
66
70
|
```
|
|
67
71
|
|
|
72
|
+
For a real LLM, replace the mock with `OpenAiCompatibleClient`:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
import { OpenAiCompatibleClient } from '@webskill/sdk';
|
|
76
|
+
|
|
77
|
+
const llm = new OpenAiCompatibleClient({
|
|
78
|
+
baseUrl: 'https://your-llm-endpoint/v1',
|
|
79
|
+
apiKey: process.env.LLM_API_KEY,
|
|
80
|
+
model: 'your-model'
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
68
84
|
## Subpath exports
|
|
69
85
|
|
|
70
|
-
|
|
71
|
-
| -------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
-
| `@webskill/sdk` | Core protocol + runtime engine (discovery, agent loop, LLM clients, routing, interaction, memory, artifacts, trace) |
|
|
73
|
-
| `@webskill/sdk/node` | Node host: NodeFS, script executors (in-process + sandboxed), file stores, skill manager, CLI UI bridge, env helpers |
|
|
74
|
-
| `@webskill/sdk/browser` | Browser host: OPFS provider, Web Worker script sandbox, worker runtime host/client |
|
|
75
|
-
| `@webskill/sdk/mcp` | MCP transports, endpoint registry, tool resolver, temporary skills, WebMCP adapter, runtime plugin |
|
|
76
|
-
| `@webskill/sdk/ui` | Framework-agnostic forms, result rendering, mini markdown, Vercel/OpenUI/A2UI adapters, A2UI Lit runtime |
|
|
77
|
-
| `@webskill/sdk/ui-react` | React bridge state + InteractionForm/ResultBlocks/StreamingText components |
|
|
78
|
-
| `@webskill/sdk/ui-vue` | Vue bridge state + equivalent components |
|
|
79
|
-
| `@webskill/sdk/governance` | Candidates, approval, audit, versioning, state policy, evaluation, scoring, documents |
|
|
86
|
+
### `@webskill/sdk` (main entry)
|
|
80
87
|
|
|
81
|
-
|
|
88
|
+
Core protocol + runtime engine: discovery, catalog, validation, agent loop, LLM
|
|
89
|
+
clients, routing, interaction types, memory, artifacts, tracing, network policy,
|
|
90
|
+
capability approval, and the environment-agnostic `createWebSkillApi` facade.
|
|
82
91
|
|
|
83
|
-
|
|
84
|
-
|
|
92
|
+
```js
|
|
93
|
+
import { SkillDiscovery, validateSkills, createWebSkillApi } from '@webskill/sdk';
|
|
94
|
+
|
|
95
|
+
const api = createWebSkillApi({ fs, roots: ['./skills'], llm, skillManager });
|
|
96
|
+
const report = await api.validate('./skills');
|
|
97
|
+
const run = await api.run('Summarize the references of skill greeter.');
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `@webskill/sdk/node`
|
|
101
|
+
|
|
102
|
+
Node host: `NodeFS`, script executors (in-process + `worker_threads` sandbox),
|
|
103
|
+
file-backed stores, `SkillManager` (install/export incl. multi-skill packs),
|
|
104
|
+
CLI UI bridge, schema inference.
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
import { NodeFS, SkillManager } from '@webskill/sdk/node';
|
|
108
|
+
|
|
109
|
+
const manager = new SkillManager({ managedRoot: './.webskill/skills' });
|
|
110
|
+
await manager.install({ type: 'http', url: 'https://example.com/greeter.zip' });
|
|
111
|
+
await manager.exportPack(['greeter', 'calculator'], { outPath: './pack.zip' });
|
|
112
|
+
console.log((await manager.verifyIntegrity('greeter')).ok);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `@webskill/sdk/browser`
|
|
116
|
+
|
|
117
|
+
Browser host: OPFS provider, Web Worker script sandbox (deny-by-default network
|
|
118
|
+
policy), worker runtime host/client, `BrowserSkillManager`, and the
|
|
119
|
+
`navigator.webskill` assembler. See
|
|
120
|
+
[`examples/quickstart-browser`](../examples/quickstart-browser).
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
import { installWebSkillNavigator } from '@webskill/browser';
|
|
124
|
+
|
|
125
|
+
const api = installWebSkillNavigator({ roots: ['/skills'], llm });
|
|
126
|
+
const catalog = await navigator.webskill.discover('/skills');
|
|
127
|
+
const run = await navigator.webskill.run('Greet the world.');
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `@webskill/sdk/mcp`
|
|
131
|
+
|
|
132
|
+
MCP integration: MessageChannel transport, endpoint registry, tool resolver,
|
|
133
|
+
temporary (page-declared) skills, WebMCP adapter, runtime plugin.
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
import { MessageChannelTransport, serveSkillAsMcp } from '@webskill/sdk/mcp';
|
|
137
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
138
|
+
|
|
139
|
+
const server = new McpServer({ name: 'page', version: '0.1.0' });
|
|
140
|
+
serveSkillAsMcp(server, { name: 'greeter', description: 'Greet someone', body: '# Greeter' });
|
|
141
|
+
await server.connect(new MessageChannelTransport(port));
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `@webskill/sdk/ui`
|
|
145
|
+
|
|
146
|
+
Framework-agnostic UI: form models + `WebFormBridge`, result rendering
|
|
147
|
+
(markdown / table / mini-chart SVG), mini markdown, generative UI adapters
|
|
148
|
+
(Vercel AI SDK, OpenUI, A2UI).
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
import { WebFormBridge } from '@webskill/sdk/ui';
|
|
152
|
+
|
|
153
|
+
const bridge = new WebFormBridge({ mount: document.querySelector('#ui') });
|
|
154
|
+
// pass as uiBridge to the runtime: forms, confirmations, and authorization
|
|
155
|
+
// prompts render as real DOM, results (incl. charts) render on completion.
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### `@webskill/sdk/ui-react`
|
|
159
|
+
|
|
160
|
+
React bridge state + `InteractionForm` / `ResultBlocks` / `StreamingText`
|
|
161
|
+
components (requires `react` + `react-dom`).
|
|
162
|
+
|
|
163
|
+
```jsx
|
|
164
|
+
import { ReactBridgeState, InteractionForm, ResultBlocks } from '@webskill/sdk/ui-react';
|
|
165
|
+
|
|
166
|
+
const bridge = new ReactBridgeState();
|
|
167
|
+
// <InteractionForm bridge={bridge} /> <ResultBlocks bridge={bridge} />
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `@webskill/sdk/ui-vue`
|
|
171
|
+
|
|
172
|
+
Vue bridge state + equivalent components (requires `vue`).
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
import { VueBridgeState, InteractionForm, ResultBlocks } from '@webskill/sdk/ui-vue';
|
|
176
|
+
|
|
177
|
+
const bridge = new VueBridgeState();
|
|
178
|
+
// <InteractionForm :bridge="bridge" /> <ResultBlocks :bridge="bridge" />
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### `@webskill/sdk/governance`
|
|
182
|
+
|
|
183
|
+
Governance: skill candidates with mandatory review, approval workflows, audit
|
|
184
|
+
log, versioning/rollback, quarantine, evaluation, scoring, dependency graph.
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
import { DependencyGraph } from '@webskill/sdk/governance';
|
|
188
|
+
|
|
189
|
+
const graph = DependencyGraph.buildFromCatalog(catalog.entries, documents);
|
|
190
|
+
console.log(graph.dependenciesOf('greeter'));
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### `@webskill/sdk/testing`
|
|
194
|
+
|
|
195
|
+
Test facilities (moved out of the main entry in 0.1.0): `MockLlmClient`,
|
|
196
|
+
`MockUiBridge`, `InMemoryStore`, `MemoryArtifactStore`.
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
import { MockLlmClient, MockUiBridge, InMemoryStore, MemoryArtifactStore } from '@webskill/sdk/testing';
|
|
200
|
+
|
|
201
|
+
const llm = new MockLlmClient([{ content: 'deterministic answer' }]);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Stability
|
|
205
|
+
|
|
206
|
+
This package follows semver starting at 0.1.0. Public symbols carry JSDoc
|
|
207
|
+
stability tags:
|
|
208
|
+
|
|
209
|
+
- **`@stable`** — covered by the semver contract: bug fixes ship in patch,
|
|
210
|
+
backwards-compatible capabilities in minor, breaking changes only in major
|
|
211
|
+
(with release notes).
|
|
212
|
+
- **`@experimental`** — may change in minor/patch releases. Currently:
|
|
213
|
+
`ExperimentalWebMcpAdapter`, `resumeRun` and the `RunSnapshot` format,
|
|
214
|
+
the A2UI/OpenUI adapters, and the `authorize` interaction type.
|
|
215
|
+
|
|
216
|
+
The public API surface of every subpath is pinned by checked-in `.d.ts`
|
|
217
|
+
snapshots (`api-snapshots/`); any intentional change requires
|
|
218
|
+
`pnpm api:update` and is reviewable in the diff.
|
|
85
219
|
|
|
86
220
|
## License
|
|
87
221
|
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { H as SkillsLockfile, L as SkillManifest, P as SkillInstallSource, S as FileSystemProvider, W as VerifyResult, _ as RenderResultRequest, a as InteractionPolicy, c as LlmClient, d as LlmResponse, f as LlmStreamEvent, o as InteractionRequest, s as InteractionResponse, v as UiBridge, x as FileStat } from "./types-CgNC-oQu-Dq_A1yA-.js";
|
|
2
|
+
import { K as ScriptExecutionContext, Y as ToolDefinition, Z as ToolResult, _ as ExternalToolSource, _t as parseBridgeRequest, c as ApprovalScope, d as BridgeRequest, f as BridgeResponse, g as ExternalSkillProvider, k as NetworkPolicy, l as BridgeCapabilities, ot as bridgeError, q as ScriptExecutor, rt as WebSkillApi } from "./index-oG5Nzgb9.js";
|
|
2
3
|
//#region ../browser/dist/index.d.ts
|
|
3
4
|
//#region src/fs/featureDetection.d.ts
|
|
4
5
|
/** 检测当前环境是否可用 OPFS(navigator.storage.getDirectory) */
|
|
@@ -74,6 +75,7 @@ declare class TsTranspiler {
|
|
|
74
75
|
* D5:浏览器技能安装(http(s) zip / ArrayBuffer;tar/git/npm → TOOL_UNSUPPORTED)。
|
|
75
76
|
* 流程与 Node 对齐:staging → 解析 name → validateSkills(失败零残留)→ managed root →
|
|
76
77
|
* manifest(WebCrypto sha256)→ skills.lock.json;zip-slip 防护;manifest/lockfile 与 Node 同格式。
|
|
78
|
+
* @stable
|
|
77
79
|
*/
|
|
78
80
|
declare class BrowserSkillManager {
|
|
79
81
|
#private;
|
|
@@ -119,6 +121,7 @@ type WorkerFactory = () => WorkerLike;
|
|
|
119
121
|
/**
|
|
120
122
|
* Web Worker 脚本沙箱执行器:loadDefinition 与 execute 都在 Worker 内完成,
|
|
121
123
|
* 主线程从不 import 技能脚本;每次执行独立 Worker;超时 terminate 强杀。
|
|
124
|
+
* @stable
|
|
122
125
|
*/
|
|
123
126
|
declare class BrowserWorkerScriptExecutor implements ScriptExecutor {
|
|
124
127
|
#private;
|
|
@@ -157,6 +160,7 @@ declare global {
|
|
|
157
160
|
* BrowserSkillManager → createWebSkillApi;target 提供时挂 target.navigator
|
|
158
161
|
* (缺失则补空对象),否则挂真实 navigator.webskill。
|
|
159
162
|
* target 可注入(测试);返回值即门面本体(挂载只是副作用)。
|
|
163
|
+
* @stable
|
|
160
164
|
*/
|
|
161
165
|
declare function installWebSkillNavigator(config: {
|
|
162
166
|
roots?: string[];
|
package/dist/browser.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { D as
|
|
1
|
+
import { D as verifyManifest, E as validateSkills, S as parseSkillPackManifest, T as resolveInsideRoot, _ as exportSkills, c as SkillDiscovery, d as buildCatalog, f as buildManifest, i as SKILL_MANIFEST_FILE, r as SKILLS_LOCKFILE, s as SKILL_PACK_FILE, u as WebSkillError, v as isValidSkillName, x as parseSkillMarkdown } from "./memoryArtifactStore-C9lFVqPF-U8nXvqL9.js";
|
|
2
|
+
import { A as normalizeToolContent, C as createWebSkillApi, D as isNetworkAllowed, O as mergeCatalogEntries, _ as RUN_SNAPSHOT_SCHEMA_VERSION, a as CapabilityApproval, b as bridgeError, c as FsMemoryStore, f as OpenAiCompatibleClient, i as AgentLoop, j as parseBridgeRequest, k as networkUrlHost, l as FsRunSnapshotStore, p as ProgressiveRouter, s as FsArtifactStore } from "./dist-D5fsiETF.js";
|
|
3
|
+
import { n as MockLlmClient } from "./testing-pn3NhXSV.js";
|
|
2
4
|
import { unzipSync } from "fflate";
|
|
3
5
|
|
|
4
6
|
//#region ../browser/dist/index.js
|
|
@@ -442,6 +444,7 @@ const lockfilePath = (root) => `${root}/${SKILLS_LOCKFILE}`;
|
|
|
442
444
|
* D5:浏览器技能安装(http(s) zip / ArrayBuffer;tar/git/npm → TOOL_UNSUPPORTED)。
|
|
443
445
|
* 流程与 Node 对齐:staging → 解析 name → validateSkills(失败零残留)→ managed root →
|
|
444
446
|
* manifest(WebCrypto sha256)→ skills.lock.json;zip-slip 防护;manifest/lockfile 与 Node 同格式。
|
|
447
|
+
* @stable
|
|
445
448
|
*/
|
|
446
449
|
var BrowserSkillManager = class {
|
|
447
450
|
#fs;
|
|
@@ -496,6 +499,7 @@ var BrowserSkillManager = class {
|
|
|
496
499
|
} catch (e) {
|
|
497
500
|
throw new WebSkillError("INSTALL_FAILED", `Failed to read skill name from SKILL.md: ${messageOf$1(e)}`, e);
|
|
498
501
|
}
|
|
502
|
+
if (!isValidSkillName(name)) throw new WebSkillError("INSTALL_FAILED", `Invalid skill name in SKILL.md (path traversal rejected): ${JSON.stringify(name)}`);
|
|
499
503
|
const finalRoot = `${stagingRoot}/final`;
|
|
500
504
|
const finalDir = `${finalRoot}/${name}`;
|
|
501
505
|
await copyDir(fs, skillDir, finalDir);
|
|
@@ -537,6 +541,7 @@ var BrowserSkillManager = class {
|
|
|
537
541
|
try {
|
|
538
542
|
const versions = /* @__PURE__ */ new Map();
|
|
539
543
|
for (const entry of pack.manifest.skills) {
|
|
544
|
+
if (!isValidSkillName(entry.name)) throw new WebSkillError("INSTALL_FAILED", `Invalid skill name in skill pack manifest (path traversal rejected): ${JSON.stringify(entry.name)}`);
|
|
540
545
|
const skillDir = `${pack.contentDir}/${entry.name}`;
|
|
541
546
|
if (!await fs.exists(`${skillDir}/SKILL.md`)) throw new WebSkillError("INSTALL_FAILED", `Skill pack is missing a directory for skill "${entry.name}"`);
|
|
542
547
|
let name;
|
|
@@ -685,6 +690,7 @@ const messageOf = (e) => e instanceof Error ? e.message : String(e);
|
|
|
685
690
|
/**
|
|
686
691
|
* Web Worker 脚本沙箱执行器:loadDefinition 与 execute 都在 Worker 内完成,
|
|
687
692
|
* 主线程从不 import 技能脚本;每次执行独立 Worker;超时 terminate 强杀。
|
|
693
|
+
* @stable
|
|
688
694
|
*/
|
|
689
695
|
var BrowserWorkerScriptExecutor = class {
|
|
690
696
|
#fs;
|
|
@@ -910,6 +916,7 @@ var BrowserWorkerScriptExecutor = class {
|
|
|
910
916
|
* BrowserSkillManager → createWebSkillApi;target 提供时挂 target.navigator
|
|
911
917
|
* (缺失则补空对象),否则挂真实 navigator.webskill。
|
|
912
918
|
* target 可注入(测试);返回值即门面本体(挂载只是副作用)。
|
|
919
|
+
* @stable
|
|
913
920
|
*/
|
|
914
921
|
function installWebSkillNavigator(config) {
|
|
915
922
|
const roots = config.roots ?? ["/skills"];
|
|
@@ -1132,7 +1139,7 @@ function startWorkerRuntimeHost(scope, overrides = {}) {
|
|
|
1132
1139
|
return [];
|
|
1133
1140
|
}
|
|
1134
1141
|
}))).flat();
|
|
1135
|
-
const catalog = providerEntries.length > 0 ? {
|
|
1142
|
+
const catalog = providerEntries.length > 0 ? { entries: mergeCatalogEntries(buildCatalog(entries).entries, providerEntries) } : buildCatalog(entries);
|
|
1136
1143
|
const route = await new ProgressiveRouter().route(catalog);
|
|
1137
1144
|
const uiBridge = new WorkerUiBridge(scope);
|
|
1138
1145
|
activeUiBridge = uiBridge;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as verifyManifest, E as validateSkills, S as parseSkillPackManifest, T as resolveInsideRoot, _ as exportSkills, f as buildManifest, i as SKILL_MANIFEST_FILE, r as SKILLS_LOCKFILE, s as SKILL_PACK_FILE, u as WebSkillError, v as isValidSkillName, x as parseSkillMarkdown } from "./memoryArtifactStore-C9lFVqPF-U8nXvqL9.js";
|
|
2
|
+
import { A as normalizeToolContent, D as isNetworkAllowed, a as CapabilityApproval, b as bridgeError, c as FsMemoryStore, j as parseBridgeRequest, k as networkUrlHost, s as FsArtifactStore } from "./dist-D5fsiETF.js";
|
|
2
3
|
import { unzipSync, zipSync } from "fflate";
|
|
3
4
|
import { existsSync, promises, readFileSync } from "node:fs";
|
|
4
5
|
import path from "node:path";
|
|
@@ -1126,6 +1127,7 @@ const asInstallFailed = (e) => e instanceof WebSkillError && e.code === "INSTALL
|
|
|
1126
1127
|
/**
|
|
1127
1128
|
* 技能管理门面:统一安装管线(staging → 解析 name → 校验 → 拷贝 → manifest → lockfile),
|
|
1128
1129
|
* 任何失败清理现场抛 INSTALL_FAILED。
|
|
1130
|
+
* @stable
|
|
1129
1131
|
*/
|
|
1130
1132
|
var SkillManager = class {
|
|
1131
1133
|
#managedRoot;
|
|
@@ -1176,6 +1178,7 @@ var SkillManager = class {
|
|
|
1176
1178
|
} catch (e) {
|
|
1177
1179
|
throw new WebSkillError("INSTALL_FAILED", `Failed to read skill name from SKILL.md: ${messageOf(e)}`, e);
|
|
1178
1180
|
}
|
|
1181
|
+
if (!isValidSkillName(name)) throw new WebSkillError("INSTALL_FAILED", `Invalid skill name in SKILL.md (path traversal rejected): ${JSON.stringify(name)}`);
|
|
1179
1182
|
const finalRoot = `${stagingRoot}/final`;
|
|
1180
1183
|
const finalDir = `${finalRoot}/${name}`;
|
|
1181
1184
|
await copyDir(fs, staged.skillDir, finalDir);
|
|
@@ -1219,6 +1222,7 @@ var SkillManager = class {
|
|
|
1219
1222
|
try {
|
|
1220
1223
|
const versions = /* @__PURE__ */ new Map();
|
|
1221
1224
|
for (const entry of pack.manifest.skills) {
|
|
1225
|
+
if (!isValidSkillName(entry.name)) throw new WebSkillError("INSTALL_FAILED", `Invalid skill name in skill pack manifest (path traversal rejected): ${JSON.stringify(entry.name)}`);
|
|
1222
1226
|
const skillDir = `${pack.contentDir}/${entry.name}`;
|
|
1223
1227
|
if (!await fs.exists(`${skillDir}/SKILL.md`)) throw new WebSkillError("INSTALL_FAILED", `Skill pack is missing a directory for skill "${entry.name}"`);
|
|
1224
1228
|
let name;
|