@webskill/sdk 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +2 -2
- package/dist/agent.js +274 -42
- package/dist/browser.d.ts +127 -5
- package/dist/browser.js +611 -17
- package/dist/{catalogComponents-DV7cPpUm-C77AEEx9.js → catalogComponents-Dr5dFMAb-DKH_7VPI.js} +848 -974
- package/dist/{dist-6C03DShK.js → dist-D0qW6e40.js} +1213 -210
- package/dist/{dist-bewtXYlO.js → dist-DnYG2-eY.js} +53 -14
- package/dist/{env--jJB-TSX-04klhTYi.js → env-8cY40DXB-CGnEVZby.js} +7 -6
- package/dist/{env-BPUBZCwJ-4jat_SVG.d.ts → env-AK3cSMEA-Dli6QU5E.d.ts} +4 -3
- package/dist/eventTypes-DjIQpt8Y-Bj3vghj4.js +32 -0
- package/dist/governance.d.ts +78 -11
- package/dist/governance.js +173 -50
- package/dist/{index-D_7ZZjkl.d.ts → index-Ba3xFtfz.d.ts} +222 -8
- package/dist/{index-Bsqg4ftU.d.ts → index-BwsK9lGk.d.ts} +13 -7
- package/dist/{index-vBz_FC9w.d.ts → index-DkbABR43.d.ts} +336 -52
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +159 -11
- package/dist/mcp.js +276 -45
- package/dist/{memoryArtifactStore-BtOeB_hm-tj3fC5ip.js → memoryArtifactStore-52Zn9npI-BMPYwvoy.js} +10 -2
- package/dist/node.d.ts +4 -4
- package/dist/node.js +11 -3
- package/dist/{openUiLibrary-W3Ce896k-ClFTRZFs.js → openUiLibrary-Bdrji9qK-D2LxmM-a.js} +3 -3
- package/dist/{skillVersionStore-BzLbzFOL-CxwIewHJ.d.ts → skillVersionStore-Bl-ElD45-CWPvGvoq.d.ts} +8 -2
- package/dist/{testing-DDCJWvgA.js → testing-CYTFqkDm.js} +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +3 -3
- package/dist/{types-D_hoCri8-BnNPiZCi.d.ts → types-CcxRLdJG-DCXyw1US.d.ts} +62 -10
- package/dist/ui-react.d.ts +5 -3
- package/dist/ui-react.js +72 -47
- package/dist/ui-vue.d.ts +1 -1
- package/dist/ui-vue.js +13 -8
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +2 -2
- package/dist/{webskillLitCatalog-_mugzRHx-DiuJpCuf.js → webskillLitCatalog-_mugzRHx-B_54vxum.js} +1 -1
- package/package.json +1 -1
package/dist/mcp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { O as messageOf, h as assertRemoteUrlAllowed, m as WebSkillError } from "./dist-8oQRa8Xz.js";
|
|
2
|
-
import {
|
|
2
|
+
import { nt as mergeCatalogEntries, st as normalizeToolContent } from "./dist-D0qW6e40.js";
|
|
3
3
|
|
|
4
4
|
//#region ../mcp/dist/index.js
|
|
5
5
|
/**
|
|
@@ -182,6 +182,8 @@ const failure$1 = (code, message) => ({
|
|
|
182
182
|
message
|
|
183
183
|
}
|
|
184
184
|
});
|
|
185
|
+
/** 需要重新授权不是「端点不可用」:压平成后者,模型和界面都会去查网络(0.7.0 FR-22.3) */
|
|
186
|
+
const endpointFailure = (e, message) => failure$1(e instanceof WebSkillError && e.code.startsWith("MCP_OAUTH_") ? e.code : "MCP_ENDPOINT_UNAVAILABLE", message);
|
|
185
187
|
/**
|
|
186
188
|
* endpoint:toolName 调用 + 工具清单缓存(TTL 默认 1s + registry 版本号双失效)。
|
|
187
189
|
*/
|
|
@@ -204,7 +206,7 @@ var McpToolResolver = class {
|
|
|
204
206
|
try {
|
|
205
207
|
names = await this.#toolNames(endpoint, client);
|
|
206
208
|
} catch (e) {
|
|
207
|
-
return
|
|
209
|
+
return endpointFailure(e, `Failed to list tools: ${messageOf(e)}`);
|
|
208
210
|
}
|
|
209
211
|
if (!names.has(toolName)) {
|
|
210
212
|
this.#cache.delete(endpoint);
|
|
@@ -222,7 +224,7 @@ var McpToolResolver = class {
|
|
|
222
224
|
});
|
|
223
225
|
return this.#normalizeCallResult(raw);
|
|
224
226
|
} catch (e) {
|
|
225
|
-
return
|
|
227
|
+
return endpointFailure(e, `Tool "${toolName}" on endpoint "${endpoint}" failed: ${messageOf(e)}`);
|
|
226
228
|
}
|
|
227
229
|
}
|
|
228
230
|
async #toolNames(endpoint, client) {
|
|
@@ -392,15 +394,39 @@ const failure = (code, message) => ({
|
|
|
392
394
|
message
|
|
393
395
|
}
|
|
394
396
|
});
|
|
395
|
-
/**
|
|
397
|
+
/** getTools() 返回的工具 → 描述符:inputSchema 为 JSON 字符串时解析为对象,并保留原始对象 */
|
|
398
|
+
function toDescriptor(raw) {
|
|
399
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
400
|
+
const t = raw;
|
|
401
|
+
const name = String(t["name"] ?? "");
|
|
402
|
+
if (name === "") return void 0;
|
|
403
|
+
const descriptor = {
|
|
404
|
+
name,
|
|
405
|
+
raw
|
|
406
|
+
};
|
|
407
|
+
if (typeof t["description"] === "string") descriptor.description = t["description"];
|
|
408
|
+
if (typeof t["origin"] === "string") descriptor.origin = t["origin"];
|
|
409
|
+
if (typeof t["annotations"] === "object" && t["annotations"] !== null) {
|
|
410
|
+
const annotations = t["annotations"];
|
|
411
|
+
descriptor.annotations = {
|
|
412
|
+
...typeof annotations["readOnlyHint"] === "boolean" ? { readOnlyHint: annotations["readOnlyHint"] } : {},
|
|
413
|
+
...typeof annotations["untrustedContentHint"] === "boolean" ? { untrustedContentHint: annotations["untrustedContentHint"] } : {}
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
const schema = t["inputSchema"];
|
|
417
|
+
if (typeof schema === "string" && schema.trim() !== "") try {
|
|
418
|
+
descriptor.inputSchema = JSON.parse(schema);
|
|
419
|
+
} catch {
|
|
420
|
+
descriptor.inputSchema = schema;
|
|
421
|
+
}
|
|
422
|
+
else if (typeof schema === "object" && schema !== null) descriptor.inputSchema = schema;
|
|
423
|
+
return descriptor;
|
|
424
|
+
}
|
|
425
|
+
/** 从 getTools 返回值提取工具描述(容忍数组或 {tools:[]} 两种形状) */
|
|
396
426
|
function extractToolDescriptors(raw) {
|
|
397
427
|
const list = Array.isArray(raw) ? raw : typeof raw === "object" && raw !== null && Array.isArray(raw.tools) ? raw.tools : void 0;
|
|
398
428
|
if (!list) return void 0;
|
|
399
|
-
return list.filter((
|
|
400
|
-
name: String(t["name"] ?? ""),
|
|
401
|
-
...typeof t["description"] === "string" ? { description: t["description"] } : {},
|
|
402
|
-
...typeof t["inputSchema"] === "object" && t["inputSchema"] !== null ? { inputSchema: t["inputSchema"] } : {}
|
|
403
|
-
})).filter((t) => t.name !== "");
|
|
429
|
+
return list.map(toDescriptor).filter((d) => d !== void 0);
|
|
404
430
|
}
|
|
405
431
|
/**
|
|
406
432
|
* Experimental WebMCP Adapter(mcp# 分支):默认关闭显式启用;
|
|
@@ -416,35 +442,45 @@ var ExperimentalWebMcpAdapter = class {
|
|
|
416
442
|
this.#enabled = options?.enabled ?? false;
|
|
417
443
|
}
|
|
418
444
|
isAvailable() {
|
|
419
|
-
|
|
445
|
+
const api = this.#resolveApi();
|
|
446
|
+
return this.#enabled && typeof api?.executeTool === "function";
|
|
420
447
|
}
|
|
421
|
-
/** 工具清单(
|
|
448
|
+
/** 工具清单(getTools 缺失时返回 undefined;调用失败时告警后返回 undefined) */
|
|
422
449
|
async listTools() {
|
|
423
450
|
const api = this.#resolveApi();
|
|
424
|
-
if (typeof api?.
|
|
451
|
+
if (typeof api?.getTools !== "function") return void 0;
|
|
425
452
|
try {
|
|
426
|
-
return extractToolDescriptors(await api.
|
|
453
|
+
return extractToolDescriptors(await api.getTools());
|
|
427
454
|
} catch (e) {
|
|
428
|
-
console.warn(`[webskill]
|
|
455
|
+
console.warn(`[webskill] document.modelContext.getTools() failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
429
456
|
return;
|
|
430
457
|
}
|
|
431
458
|
}
|
|
432
|
-
/** 工具名清单(
|
|
459
|
+
/** 工具名清单(getTools 缺失/失败时返回 undefined) */
|
|
433
460
|
async listToolNames() {
|
|
434
461
|
return (await this.listTools())?.map((t) => t.name);
|
|
435
462
|
}
|
|
436
463
|
async call(toolName, args) {
|
|
437
464
|
if (!this.#enabled) return failure("TOOL_UNSUPPORTED", "WebMCP adapter is disabled (enable it explicitly to use mcp# tools)");
|
|
438
465
|
const api = this.#resolveApi();
|
|
439
|
-
if (typeof api?.executeTool !== "function") return failure("TOOL_UNSUPPORTED", "
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
466
|
+
if (typeof api?.executeTool !== "function") return failure("TOOL_UNSUPPORTED", "document.modelContext is not available");
|
|
467
|
+
const argsJson = JSON.stringify(args);
|
|
468
|
+
if (typeof api.getTools === "function") try {
|
|
469
|
+
const descriptors = extractToolDescriptors(await api.getTools());
|
|
470
|
+
const match = descriptors?.find((t) => t.name === toolName);
|
|
471
|
+
if (descriptors !== void 0 && match === void 0) return failure("MCP_TOOL_NOT_FOUND", `WebMCP tool "${toolName}" not found`);
|
|
472
|
+
const tool = match?.raw ?? { name: toolName };
|
|
473
|
+
return {
|
|
474
|
+
ok: true,
|
|
475
|
+
content: normalizeToolContent(await api.executeTool(tool, argsJson))
|
|
476
|
+
};
|
|
477
|
+
} catch (e) {
|
|
478
|
+
return failure("TOOL_EXECUTION_FAILED", `WebMCP tool "${toolName}" failed: ${messageOf(e)}`);
|
|
479
|
+
}
|
|
444
480
|
try {
|
|
445
481
|
return {
|
|
446
482
|
ok: true,
|
|
447
|
-
content: normalizeToolContent(await api.executeTool(toolName,
|
|
483
|
+
content: normalizeToolContent(await api.executeTool(toolName, argsJson))
|
|
448
484
|
};
|
|
449
485
|
} catch (e) {
|
|
450
486
|
return failure("TOOL_EXECUTION_FAILED", `WebMCP tool "${toolName}" failed: ${messageOf(e)}`);
|
|
@@ -531,22 +567,182 @@ var McpRuntimePlugin = class {
|
|
|
531
567
|
};
|
|
532
568
|
}
|
|
533
569
|
};
|
|
570
|
+
/**
|
|
571
|
+
* 构造授权相关错误。`message` 只含端点名与阶段,`details` 只有 `{ endpoint, stage }`——
|
|
572
|
+
* 不带 `cause`:SDK 的错误 message 可能回显授权 URL 或 token 端点的响应体(AC-22.6)。
|
|
573
|
+
*/
|
|
574
|
+
function oauthError(code, endpoint, stage, reason) {
|
|
575
|
+
return new WebSkillError(code, `Remote MCP endpoint "${endpoint}" ${reason} (stage: ${stage})`, {
|
|
576
|
+
endpoint,
|
|
577
|
+
stage
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
/** 显式的内存存储,供 examples 与测试使用。**不是缺省行为**——是宿主主动选的(AC-22.7)。 @experimental */
|
|
581
|
+
function createMemoryOAuthStores() {
|
|
582
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
583
|
+
const handshakes = /* @__PURE__ */ new Map();
|
|
584
|
+
return {
|
|
585
|
+
tokens: {
|
|
586
|
+
load: (endpoint) => Promise.resolve(tokens.get(endpoint)),
|
|
587
|
+
save: (endpoint, value) => {
|
|
588
|
+
tokens.set(endpoint, value);
|
|
589
|
+
return Promise.resolve();
|
|
590
|
+
},
|
|
591
|
+
clear: (endpoint) => {
|
|
592
|
+
tokens.delete(endpoint);
|
|
593
|
+
return Promise.resolve();
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
handshake: {
|
|
597
|
+
load: (endpoint) => Promise.resolve(handshakes.get(endpoint)),
|
|
598
|
+
save: (endpoint, value) => {
|
|
599
|
+
handshakes.set(endpoint, value);
|
|
600
|
+
return Promise.resolve();
|
|
601
|
+
},
|
|
602
|
+
clear: (endpoint) => {
|
|
603
|
+
handshakes.delete(endpoint);
|
|
604
|
+
return Promise.resolve();
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
const randomState = () => {
|
|
610
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
611
|
+
crypto.getRandomValues(bytes);
|
|
612
|
+
return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
613
|
+
};
|
|
614
|
+
/**
|
|
615
|
+
* `McpOAuthConfig` → SDK `OAuthClientProvider` 适配器。
|
|
616
|
+
*
|
|
617
|
+
* `state()` 与 `saveCodeVerifier()` 由 SDK 分两次调用(顺序上前者在先),
|
|
618
|
+
* 两者合成一条握手记录:谁先到都把当前已知部分写进去,回程时两项都在。
|
|
619
|
+
* @experimental
|
|
620
|
+
*/
|
|
621
|
+
function createOAuthProvider(endpoint, config) {
|
|
622
|
+
const { client, tokens: tokenStore, handshake } = config;
|
|
623
|
+
let pending = {};
|
|
624
|
+
const persist = async (patch) => {
|
|
625
|
+
pending = {
|
|
626
|
+
...pending,
|
|
627
|
+
...patch
|
|
628
|
+
};
|
|
629
|
+
await handshake.save(endpoint, {
|
|
630
|
+
codeVerifier: pending.codeVerifier ?? "",
|
|
631
|
+
state: pending.state ?? ""
|
|
632
|
+
});
|
|
633
|
+
};
|
|
634
|
+
return {
|
|
635
|
+
get redirectUrl() {
|
|
636
|
+
return client.redirectUri;
|
|
637
|
+
},
|
|
638
|
+
get clientMetadata() {
|
|
639
|
+
return {
|
|
640
|
+
client_name: "WebSkill SDK",
|
|
641
|
+
redirect_uris: [client.redirectUri],
|
|
642
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
643
|
+
response_types: ["code"],
|
|
644
|
+
token_endpoint_auth_method: client.clientSecret === void 0 ? "none" : "client_secret_post",
|
|
645
|
+
...client.scopes && client.scopes.length > 0 ? { scope: client.scopes.join(" ") } : {}
|
|
646
|
+
};
|
|
647
|
+
},
|
|
648
|
+
async state() {
|
|
649
|
+
const value = randomState();
|
|
650
|
+
await persist({ state: value });
|
|
651
|
+
return value;
|
|
652
|
+
},
|
|
653
|
+
clientInformation() {
|
|
654
|
+
return Promise.resolve({
|
|
655
|
+
client_id: client.clientId,
|
|
656
|
+
...client.clientSecret !== void 0 ? { client_secret: client.clientSecret } : {}
|
|
657
|
+
});
|
|
658
|
+
},
|
|
659
|
+
async tokens() {
|
|
660
|
+
const saved = await tokenStore.load(endpoint);
|
|
661
|
+
if (!saved) return void 0;
|
|
662
|
+
return {
|
|
663
|
+
access_token: saved.accessToken,
|
|
664
|
+
token_type: saved.tokenType ?? "Bearer",
|
|
665
|
+
...saved.expiresAt !== void 0 ? { expires_in: Math.max(0, Math.round((saved.expiresAt - Date.now()) / 1e3)) } : {},
|
|
666
|
+
...saved.refreshToken !== void 0 ? { refresh_token: saved.refreshToken } : {},
|
|
667
|
+
...saved.scope !== void 0 ? { scope: saved.scope } : {}
|
|
668
|
+
};
|
|
669
|
+
},
|
|
670
|
+
async saveTokens(next) {
|
|
671
|
+
await tokenStore.save(endpoint, {
|
|
672
|
+
accessToken: next.access_token,
|
|
673
|
+
...next.expires_in !== void 0 ? { expiresAt: Date.now() + next.expires_in * 1e3 } : {},
|
|
674
|
+
...next.refresh_token !== void 0 ? { refreshToken: next.refresh_token } : {},
|
|
675
|
+
...next.token_type !== void 0 ? { tokenType: next.token_type } : {},
|
|
676
|
+
...next.scope !== void 0 ? { scope: next.scope } : {}
|
|
677
|
+
});
|
|
678
|
+
},
|
|
679
|
+
async redirectToAuthorization(url) {
|
|
680
|
+
await config.openAuthorization(url);
|
|
681
|
+
},
|
|
682
|
+
async saveCodeVerifier(codeVerifier) {
|
|
683
|
+
await persist({ codeVerifier });
|
|
684
|
+
},
|
|
685
|
+
async codeVerifier() {
|
|
686
|
+
const saved = await handshake.load(endpoint);
|
|
687
|
+
if (!saved?.codeVerifier) throw oauthError("MCP_OAUTH_FAILED", endpoint, "exchange", "has no stored PKCE verifier for this handshake");
|
|
688
|
+
return saved.codeVerifier;
|
|
689
|
+
},
|
|
690
|
+
async invalidateCredentials(scope) {
|
|
691
|
+
if (scope === "all" || scope === "tokens") await tokenStore.clear(endpoint);
|
|
692
|
+
if (scope === "all" || scope === "verifier") await handshake.clear(endpoint);
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
}
|
|
534
696
|
async function loadMcpSdk() {
|
|
535
697
|
try {
|
|
536
|
-
const [client, sse, streamable] = await Promise.all([
|
|
698
|
+
const [client, sse, streamable, auth, errors] = await Promise.all([
|
|
537
699
|
import("@modelcontextprotocol/sdk/client/index.js"),
|
|
538
700
|
import("@modelcontextprotocol/sdk/client/sse.js"),
|
|
539
|
-
import("@modelcontextprotocol/sdk/client/streamableHttp.js")
|
|
701
|
+
import("@modelcontextprotocol/sdk/client/streamableHttp.js"),
|
|
702
|
+
import("@modelcontextprotocol/sdk/client/auth.js"),
|
|
703
|
+
import("@modelcontextprotocol/sdk/server/auth/errors.js")
|
|
540
704
|
]);
|
|
541
705
|
return {
|
|
542
706
|
Client: client.Client,
|
|
543
707
|
SSEClientTransport: sse.SSEClientTransport,
|
|
544
|
-
StreamableHTTPClientTransport: streamable.StreamableHTTPClientTransport
|
|
708
|
+
StreamableHTTPClientTransport: streamable.StreamableHTTPClientTransport,
|
|
709
|
+
UnauthorizedError: auth.UnauthorizedError,
|
|
710
|
+
OAuthError: errors.OAuthError
|
|
545
711
|
};
|
|
546
712
|
} catch (e) {
|
|
547
713
|
throw new WebSkillError("MCP_ENDPOINT_UNAVAILABLE", "The \"@modelcontextprotocol/sdk\" package is required for remote MCP endpoints; install it first (npm i @modelcontextprotocol/sdk)", e);
|
|
548
714
|
}
|
|
549
715
|
}
|
|
716
|
+
/** 运行期的未授权(含刷新失败)必须在 endpoint 层转成 `WebSkillError`,否则 401 会以工具结果的形式进模型上下文 */
|
|
717
|
+
function wrapOAuthClient(client, endpoint, oauth, isUnauthorized) {
|
|
718
|
+
const guard = async (work) => {
|
|
719
|
+
try {
|
|
720
|
+
return await work();
|
|
721
|
+
} catch (e) {
|
|
722
|
+
if (!isUnauthorized(e)) throw e;
|
|
723
|
+
await oauth.tokens.clear(endpoint);
|
|
724
|
+
throw oauthError("MCP_OAUTH_REQUIRED", endpoint, "refresh", "needs authorization again");
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
return {
|
|
728
|
+
listTools: () => guard(() => client.listTools()),
|
|
729
|
+
callTool: (input) => guard(() => client.callTool(input)),
|
|
730
|
+
listPrompts: () => guard(() => client.listPrompts()),
|
|
731
|
+
getPrompt: (input) => guard(() => client.getPrompt(input)),
|
|
732
|
+
listResources: () => guard(() => client.listResources()),
|
|
733
|
+
readResource: (input) => guard(() => client.readResource(input))
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
/** 授权注入的完整性校验:缺一项就是宿主的配置错误,不是运行期授权问题(AC-22.7) */
|
|
737
|
+
function assertOAuthConfigured(endpoint, oauth) {
|
|
738
|
+
const missing = [];
|
|
739
|
+
if (!oauth.client?.clientId) missing.push("client.clientId");
|
|
740
|
+
if (!oauth.client?.redirectUri) missing.push("client.redirectUri");
|
|
741
|
+
if (!oauth.tokens) missing.push("tokens");
|
|
742
|
+
if (!oauth.handshake) missing.push("handshake");
|
|
743
|
+
if (typeof oauth.openAuthorization !== "function") missing.push("openAuthorization");
|
|
744
|
+
if (missing.length > 0) throw oauthError("MCP_OAUTH_NOT_CONFIGURED", endpoint, "discover", `is missing OAuth wiring: ${missing.join(", ")}`);
|
|
745
|
+
}
|
|
550
746
|
/**
|
|
551
747
|
* 远程 MCP endpoint 装配:SDK 官方 StreamableHTTPClientTransport(默认)/
|
|
552
748
|
* SSEClientTransport(遗留)连接远端 server,注册进 EndpointRegistry——
|
|
@@ -554,7 +750,7 @@ async function loadMcpSdk() {
|
|
|
554
750
|
* 返回 close 句柄:断开后 unregister(临时技能随既有生命周期自然消失)。
|
|
555
751
|
*/
|
|
556
752
|
async function connectRemoteEndpoint(registry, config) {
|
|
557
|
-
const { Client, SSEClientTransport, StreamableHTTPClientTransport } = await loadMcpSdk();
|
|
753
|
+
const { Client, SSEClientTransport, StreamableHTTPClientTransport, UnauthorizedError, OAuthError } = await loadMcpSdk();
|
|
558
754
|
let url;
|
|
559
755
|
try {
|
|
560
756
|
url = new URL(config.url);
|
|
@@ -565,46 +761,81 @@ async function connectRemoteEndpoint(registry, config) {
|
|
|
565
761
|
allowHttp: config.allowHttp ?? false,
|
|
566
762
|
allowPrivateHosts: config.allowPrivateHosts ?? false
|
|
567
763
|
});
|
|
764
|
+
const oauth = config.oauth;
|
|
765
|
+
if (oauth) assertOAuthConfigured(config.endpoint, oauth);
|
|
766
|
+
const authProvider = oauth ? createOAuthProvider(config.endpoint, oauth) : void 0;
|
|
568
767
|
const requestInit = config.headers ? { headers: config.headers } : void 0;
|
|
569
|
-
const
|
|
768
|
+
const transportOptions = {
|
|
769
|
+
...requestInit ? { requestInit } : {},
|
|
770
|
+
...authProvider ? { authProvider } : {}
|
|
771
|
+
};
|
|
772
|
+
const makeTransport = () => config.transport === "sse" ? new SSEClientTransport(url, transportOptions) : new StreamableHTTPClientTransport(url, transportOptions);
|
|
570
773
|
const client = new Client({
|
|
571
774
|
name: "webskill-remote-client",
|
|
572
775
|
version: "0.1.0"
|
|
573
776
|
});
|
|
777
|
+
const isUnauthorized = (e) => e instanceof UnauthorizedError || e instanceof OAuthError;
|
|
574
778
|
const unavailable = (e) => new WebSkillError("MCP_ENDPOINT_UNAVAILABLE", `Failed to connect remote MCP endpoint "${config.endpoint}" at ${config.url}: ${messageOf(e)}`, e);
|
|
575
|
-
|
|
779
|
+
let closed = false;
|
|
780
|
+
const close = async () => {
|
|
781
|
+
if (closed) return;
|
|
782
|
+
closed = true;
|
|
783
|
+
registry.unregister(config.endpoint);
|
|
784
|
+
try {
|
|
785
|
+
await client.close();
|
|
786
|
+
} catch (e) {
|
|
787
|
+
throw new WebSkillError("MCP_ENDPOINT_UNAVAILABLE", `Failed to close remote MCP endpoint "${config.endpoint}": ${messageOf(e)}`, e);
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
const register = () => {
|
|
791
|
+
try {
|
|
792
|
+
const entry = client;
|
|
793
|
+
registry.register(config.endpoint, oauth ? wrapOAuthClient(entry, config.endpoint, oauth, isUnauthorized) : entry);
|
|
794
|
+
} catch (e) {
|
|
795
|
+
client.close().catch(() => void 0);
|
|
796
|
+
throw unavailable(e);
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
const openConnection = async (transport) => {
|
|
576
800
|
let connect = client.connect(transport);
|
|
577
801
|
if (config.timeoutMs && config.timeoutMs > 0) {
|
|
578
802
|
const timeoutMs = config.timeoutMs;
|
|
579
803
|
connect = Promise.race([connect, new Promise((_resolve, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Connection timed out after ${timeoutMs}ms`)), timeoutMs))]);
|
|
580
804
|
}
|
|
581
805
|
await connect;
|
|
582
|
-
}
|
|
806
|
+
};
|
|
807
|
+
const finishAuthorization = async (code, state) => {
|
|
808
|
+
const saved = await oauth.handshake.load(config.endpoint);
|
|
809
|
+
if (!saved || saved.state === "" || saved.state !== state) throw oauthError("MCP_OAUTH_FAILED", config.endpoint, "authorize", "received a mismatched authorization state");
|
|
810
|
+
const transport = makeTransport();
|
|
583
811
|
try {
|
|
584
|
-
await
|
|
585
|
-
} catch {
|
|
586
|
-
|
|
587
|
-
|
|
812
|
+
await transport.finishAuth(code);
|
|
813
|
+
} catch (e) {
|
|
814
|
+
throw oauthError(isUnauthorized(e) ? "MCP_OAUTH_REQUIRED" : "MCP_OAUTH_FAILED", config.endpoint, "exchange", "could not exchange the authorization code");
|
|
815
|
+
}
|
|
816
|
+
await oauth.handshake.clear(config.endpoint);
|
|
817
|
+
await openConnection(transport);
|
|
818
|
+
register();
|
|
819
|
+
};
|
|
588
820
|
try {
|
|
589
|
-
|
|
821
|
+
await openConnection(makeTransport());
|
|
590
822
|
} catch (e) {
|
|
823
|
+
if (oauth && isUnauthorized(e)) return {
|
|
824
|
+
close,
|
|
825
|
+
finishAuthorization,
|
|
826
|
+
authorizationRequired: true
|
|
827
|
+
};
|
|
591
828
|
try {
|
|
592
829
|
await client.close();
|
|
593
830
|
} catch {}
|
|
594
831
|
throw unavailable(e);
|
|
595
832
|
}
|
|
596
|
-
|
|
597
|
-
return {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
try {
|
|
602
|
-
await client.close();
|
|
603
|
-
} catch (e) {
|
|
604
|
-
throw new WebSkillError("MCP_ENDPOINT_UNAVAILABLE", `Failed to close remote MCP endpoint "${config.endpoint}": ${messageOf(e)}`, e);
|
|
605
|
-
}
|
|
606
|
-
} };
|
|
833
|
+
register();
|
|
834
|
+
return {
|
|
835
|
+
close,
|
|
836
|
+
...oauth ? { finishAuthorization } : {}
|
|
837
|
+
};
|
|
607
838
|
}
|
|
608
839
|
|
|
609
840
|
//#endregion
|
|
610
|
-
export { EndpointRegistry, ExperimentalWebMcpAdapter, McpRuntimePlugin, McpToolResolver, MessageChannelTransport, TemporarySkillProvider, catalogMerge, connectRemoteEndpoint, endpointToolLlmName, parseEndpointToolLlmName, parseWebMcpToolLlmName, serveSkillAsMcp, validateJsonRpcMessage, webMcpToolLlmName };
|
|
841
|
+
export { EndpointRegistry, ExperimentalWebMcpAdapter, McpRuntimePlugin, McpToolResolver, MessageChannelTransport, TemporarySkillProvider, catalogMerge, connectRemoteEndpoint, createMemoryOAuthStores, createOAuthProvider, endpointToolLlmName, parseEndpointToolLlmName, parseWebMcpToolLlmName, serveSkillAsMcp, validateJsonRpcMessage, webMcpToolLlmName };
|
package/dist/{memoryArtifactStore-BtOeB_hm-tj3fC5ip.js → memoryArtifactStore-52Zn9npI-BMPYwvoy.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { m as WebSkillError } from "./dist-8oQRa8Xz.js";
|
|
2
2
|
|
|
3
|
-
//#region ../runtime/dist/memoryArtifactStore-
|
|
3
|
+
//#region ../runtime/dist/memoryArtifactStore-52Zn9npI.js
|
|
4
4
|
/** 纯文本消息内容的构造快捷方式(引擎内部绝大多数消息仍是纯文本) */
|
|
5
5
|
const textParts = (text) => [{
|
|
6
6
|
type: "text",
|
|
@@ -8,6 +8,14 @@ const textParts = (text) => [{
|
|
|
8
8
|
}];
|
|
9
9
|
/** 取出 parts 中的文本(非文本分片在纯文本语境下无法表达,此处按丢弃处理——调用方须先校验) */
|
|
10
10
|
const partsToText = (parts) => (parts ?? []).reduce((acc, part) => part.type === "text" ? acc + part.text : acc, "");
|
|
11
|
+
/**
|
|
12
|
+
* run 的提示词文本投影:`RuntimeRun.userPrompt` 与快照都是字符串字段,
|
|
13
|
+
* 而多模态提示词里的图片/文件分片没有文本形态,用占位符标注,避免记录成空串。
|
|
14
|
+
*/
|
|
15
|
+
const promptText = (prompt) => {
|
|
16
|
+
if (typeof prompt === "string") return prompt;
|
|
17
|
+
return prompt.map((part) => part.type === "text" ? part.text : `[${part.type}: ${part.mimeType}]`).join("\n");
|
|
18
|
+
};
|
|
11
19
|
const isContentPart = (value) => {
|
|
12
20
|
if (typeof value !== "object" || value === null) return false;
|
|
13
21
|
const part = value;
|
|
@@ -75,4 +83,4 @@ var MemoryArtifactStore = class {
|
|
|
75
83
|
};
|
|
76
84
|
|
|
77
85
|
//#endregion
|
|
78
|
-
export {
|
|
86
|
+
export { textParts as a, rejectUnsupportedPart as i, partsToText as n, validateLlmMessages as o, promptText as r, MemoryArtifactStore as t };
|
package/dist/node.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { a as AuditLog, p as CandidateStore, r as ApprovalPolicy, u as CandidateSkill, v as SkillVersionStore } from "./skillVersionStore-
|
|
4
|
-
import { n as LlmEnvConfig, s as probeLlmCapabilities, t as LlmCapabilities } from "./env-
|
|
1
|
+
import { A as ArchiveLimits, F as FileStat, G as SKILLS_LOCKFILE, I as FileSystemProvider, K as SKILL_MANIFEST_FILE, R as JsonSchema, Z as SignatureAuditSink, _t as UnsignedPolicy, c as InteractionResponse, ht as TrustedKeyStore, ot as SkillManagerPort, pt as SkillsLockfile, rt as SkillInstallSource, s as InteractionRequest, st as SkillManifest, x as UiBridge, y as RenderResultRequest, yt as VerifyResult } from "./types-CcxRLdJG-DCXyw1US.js";
|
|
2
|
+
import { Et as ScriptExecutor, Kt as ToolResult, M as FsMemoryStore, Tt as ScriptExecutionContext, Wt as ToolDefinition, Y as NetworkPolicy, _ as BridgeCapabilities, f as ApprovalScope, hn as createScriptContext, j as FsArtifactStore, ln as WebSkillRuntime, un as WebSkillRuntimeDeps, wt as SchemaInferer } from "./index-DkbABR43.js";
|
|
3
|
+
import { a as AuditLog, p as CandidateStore, r as ApprovalPolicy, u as CandidateSkill, v as SkillVersionStore } from "./skillVersionStore-Bl-ElD45-CWPvGvoq.js";
|
|
4
|
+
import { n as LlmEnvConfig, s as probeLlmCapabilities, t as LlmCapabilities } from "./env-AK3cSMEA-Dli6QU5E.js";
|
|
5
5
|
import { Readable, Writable } from "node:stream";
|
|
6
6
|
//#region ../node/dist/index.d.ts
|
|
7
7
|
//#region src/fs/nodeFs.d.ts
|
package/dist/node.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { A as parseSkillMarkdown, B as unzipWithLimits, H as verifyManifest, I as resolveArchiveLimits, L as resolveInsideRoot, M as readResponseWithLimit, N as readSkillSignature, O as messageOf, T as isValidSkillName, U as verifySkillSignature, V as validateSkills, _ as atomicWriteText, g as assertSafePathSegment, h as assertRemoteUrlAllowed, j as parseSkillPackManifest, m as WebSkillError, o as SKILLS_LOCKFILE, r as MANIFEST_EXCLUDED_FILES, s as SKILL_MANIFEST_FILE, u as SKILL_PACK_FILE, w as exportSkills, y as buildManifest } from "./dist-8oQRa8Xz.js";
|
|
2
|
-
import {
|
|
3
|
-
import { i as probeLlmCapabilities } from "./env
|
|
2
|
+
import { B as bridgeError, H as createScriptContext, L as WebSkillRuntime, c as CapabilityApproval, ct as normalizeToolError, h as FsMemoryStore, it as networkPolicyLibSource, lt as parseBridgeRequest, m as FsArtifactStore, st as normalizeToolContent } from "./dist-D0qW6e40.js";
|
|
3
|
+
import { i as probeLlmCapabilities } from "./env-8cY40DXB-CGnEVZby.js";
|
|
4
|
+
import { t as AUDIT_EVENT_TYPES } from "./eventTypes-DjIQpt8Y-Bj3vghj4.js";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
5
6
|
import { unzipSync, zipSync } from "fflate";
|
|
6
7
|
import { existsSync, promises, realpathSync } from "node:fs";
|
|
@@ -1148,6 +1149,13 @@ var CliUiBridge = class {
|
|
|
1148
1149
|
value: answer === "y" || answer === "yes"
|
|
1149
1150
|
};
|
|
1150
1151
|
}
|
|
1152
|
+
case "file-pick": {
|
|
1153
|
+
const answer = (await this.#question(`${input.message}\nLocal file path (empty to decline): `)).trim();
|
|
1154
|
+
return answer === "" ? { id: input.id } : {
|
|
1155
|
+
id: input.id,
|
|
1156
|
+
value: { path: answer }
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1151
1159
|
}
|
|
1152
1160
|
}
|
|
1153
1161
|
async progress(input) {
|
|
@@ -2252,7 +2260,7 @@ var ApprovalWorkflow = class {
|
|
|
2252
2260
|
});
|
|
2253
2261
|
await this.#store.updateStatus(candidateId, "published");
|
|
2254
2262
|
await this.#audit.append({
|
|
2255
|
-
type:
|
|
2263
|
+
type: AUDIT_EVENT_TYPES.skillPublished,
|
|
2256
2264
|
target: candidate.name,
|
|
2257
2265
|
actor: input.actor,
|
|
2258
2266
|
data: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { nt as uiCatalog } from "./dist-
|
|
2
|
-
import { t as CatalogNode } from "./catalogComponents-
|
|
1
|
+
import { nt as uiCatalog } from "./dist-DnYG2-eY.js";
|
|
2
|
+
import { t as CatalogNode } from "./catalogComponents-Dr5dFMAb-DKH_7VPI.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { Component, Fragment, createContext, useCallback, useContext, useEffect, useInsertionEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -3761,7 +3761,7 @@ function Renderer({ response, library, isStreaming = false, onAction, onStateUpd
|
|
|
3761
3761
|
const FormValidationContext = createContext(null);
|
|
3762
3762
|
|
|
3763
3763
|
//#endregion
|
|
3764
|
-
//#region ../ui-react/dist/openUiLibrary-
|
|
3764
|
+
//#region ../ui-react/dist/openUiLibrary-Bdrji9qK.js
|
|
3765
3765
|
const propsFor = (props, container) => container ? props.extend({ children: z.array(z.any()).optional() }) : props;
|
|
3766
3766
|
/**
|
|
3767
3767
|
* catalog 的 OpenUI 投影:`defineComponent` 复用同一份 zod schema 与描述,
|
package/dist/{skillVersionStore-BzLbzFOL-CxwIewHJ.d.ts → skillVersionStore-Bl-ElD45-CWPvGvoq.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region ../governance/dist/skillVersionStore-
|
|
1
|
+
import { H as PageQuery, I as FileSystemProvider, V as Page, et as SkillCatalogEntry, st as SkillManifest } from "./types-CcxRLdJG-DCXyw1US.js";
|
|
2
|
+
//#region ../governance/dist/skillVersionStore-Bl-ElD45.d.ts
|
|
3
3
|
//#region src/types.d.ts
|
|
4
4
|
type CandidateStatus = 'draft' | 'pending-review' | 'approved' | 'published' | 'rejected';
|
|
5
5
|
/** `generated` 是 0.5.0 的技能自动生成来源(需求 12 号 AC-9.1) */
|
|
@@ -49,6 +49,12 @@ interface AuditQueryFilter {
|
|
|
49
49
|
since?: string;
|
|
50
50
|
/** 含端点(`ts <= until`) */
|
|
51
51
|
until?: string;
|
|
52
|
+
/**
|
|
53
|
+
* true 时链校验失败不抛错,改为在结果里报告断点(`chainBrokenAt` / `chainReason`)。
|
|
54
|
+
* **仅用于人工查看的降级展示**:程序化消费方(合规导出等)拿到部分结果而不知情
|
|
55
|
+
* 比直接失败更危险。默认 false,保持既有行为。
|
|
56
|
+
*/
|
|
57
|
+
tolerateBrokenChain?: boolean;
|
|
52
58
|
}
|
|
53
59
|
interface AuditLog {
|
|
54
60
|
append(event: Omit<AuditEvent, 'id' | 'ts'> & {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { m as WebSkillError } from "./dist-8oQRa8Xz.js";
|
|
2
|
-
import {
|
|
2
|
+
import { a as textParts, n as partsToText } from "./memoryArtifactStore-52Zn9npI-BMPYwvoy.js";
|
|
3
3
|
|
|
4
4
|
//#region ../runtime/dist/testing.js
|
|
5
5
|
/**
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as MemoryStore,
|
|
2
|
-
import { a as loadGoogleConfigFromEnv, i as loadAnthropicConfigFromEnv, n as LlmEnvConfig, o as loadLlmConfigFromEnv, r as ProviderEnvConfig } from "./env-
|
|
1
|
+
import { _ as MemoryStore, c as InteractionResponse, l as LlmClient, m as LlmStreamEvent, n as ArtifactStore, p as LlmResponse, s as InteractionRequest, t as Artifact, u as LlmCompleteInput, x as UiBridge } from "./types-CcxRLdJG-DCXyw1US.js";
|
|
2
|
+
import { a as loadGoogleConfigFromEnv, i as loadAnthropicConfigFromEnv, n as LlmEnvConfig, o as loadLlmConfigFromEnv, r as ProviderEnvConfig } from "./env-AK3cSMEA-Dli6QU5E.js";
|
|
3
3
|
//#region ../runtime/dist/testing.d.ts
|
|
4
4
|
//#region src/llm/mockLlmClient.d.ts
|
|
5
5
|
type MockLlmHandler = (input: LlmCompleteInput) => LlmResponse | Promise<LlmResponse>;
|
package/dist/testing.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as MemoryArtifactStore } from "./memoryArtifactStore-
|
|
2
|
-
import { n as loadGoogleConfigFromEnv, r as loadLlmConfigFromEnv, t as loadAnthropicConfigFromEnv } from "./env
|
|
3
|
-
import { n as MockLlmClient, r as MockUiBridge, t as InMemoryStore } from "./testing-
|
|
1
|
+
import { t as MemoryArtifactStore } from "./memoryArtifactStore-52Zn9npI-BMPYwvoy.js";
|
|
2
|
+
import { n as loadGoogleConfigFromEnv, r as loadLlmConfigFromEnv, t as loadAnthropicConfigFromEnv } from "./env-8cY40DXB-CGnEVZby.js";
|
|
3
|
+
import { n as MockLlmClient, r as MockUiBridge, t as InMemoryStore } from "./testing-CYTFqkDm.js";
|
|
4
4
|
|
|
5
5
|
export { InMemoryStore, MemoryArtifactStore, MockLlmClient, MockUiBridge, loadAnthropicConfigFromEnv, loadGoogleConfigFromEnv, loadLlmConfigFromEnv };
|