@webskill/sdk 0.17.0 → 0.19.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 +1 -1
- package/dist/agent.js +13 -6
- package/dist/{approval-CBPnGPIm.js → approval-Bbh_Apwg.js} +24 -3
- package/dist/browser.d.ts +36 -2
- package/dist/browser.js +203 -5
- package/dist/governance.js +1 -1
- package/dist/{index-1sQZ4VBN.d.ts → index-DQ3-6GFu.d.ts} +27 -6
- package/dist/{index-CaLsVI-m.d.ts → index-ad11wflI.d.ts} +15 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/{llm-B7lLH0ZI.js → llm-eIQNO9tr.js} +67 -12
- package/dist/mcp.d.ts +277 -1
- package/dist/mcp.js +455 -1
- package/dist/{miniChart-lauNtZu8.js → miniChart-D0nYMzz8.js} +2 -1
- package/dist/node.js +1 -1
- package/dist/{openUiLibrary-BJtCG5B1.js → openUiLibrary-b0i7F_oZ.js} +1 -1
- package/dist/{openUiSpecLang-B4QQ9Tfo.js → openUiSpecLang-CLXJEeRU.js} +66 -13
- package/dist/testing.js +1 -1
- package/dist/{types-BuacBov_.js → types-DOJI5YC3.js} +4 -3
- package/dist/ui-react.d.ts +1 -1
- package/dist/ui-react.js +21 -5
- package/dist/ui-vue.js +1 -1
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +15 -5
- package/dist/{webSkillApi-Ce2OO5S5.js → webSkillApi-CsvA69qk.js} +1 -1
- package/dist/{webskillLitCatalog-BBkQjKuN.js → webskillLitCatalog-W06npwej.js} +3 -3
- package/package.json +1 -1
package/dist/mcp.js
CHANGED
|
@@ -1107,4 +1107,458 @@ async function connectRemoteEndpoint(registry, config) {
|
|
|
1107
1107
|
}
|
|
1108
1108
|
|
|
1109
1109
|
//#endregion
|
|
1110
|
-
|
|
1110
|
+
//#region ../mcp/src/pageHost/mcpVisibilityStore.ts
|
|
1111
|
+
/** WebMCP 在名单里占用的保留端点名 */
|
|
1112
|
+
const WEB_MCP_ENDPOINT = "webmcp";
|
|
1113
|
+
const toolKey = (endpoint, tool) => `${endpoint}/${tool}`;
|
|
1114
|
+
const readList = (storage, key) => {
|
|
1115
|
+
try {
|
|
1116
|
+
const raw = storage?.getItem(key);
|
|
1117
|
+
const parsed = raw ? JSON.parse(raw) : [];
|
|
1118
|
+
return new Set(Array.isArray(parsed) ? parsed.filter((x) => typeof x === "string") : []);
|
|
1119
|
+
} catch {
|
|
1120
|
+
return /* @__PURE__ */ new Set();
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
const writeList = (storage, key, list) => {
|
|
1124
|
+
try {
|
|
1125
|
+
storage?.setItem(key, JSON.stringify([...list]));
|
|
1126
|
+
} catch {}
|
|
1127
|
+
};
|
|
1128
|
+
const toggle = (list, key, present) => {
|
|
1129
|
+
if (present) list.add(key);
|
|
1130
|
+
else list.delete(key);
|
|
1131
|
+
return list;
|
|
1132
|
+
};
|
|
1133
|
+
/**
|
|
1134
|
+
* localStorage 默认实现。键形:
|
|
1135
|
+
* `<ns>.mcp.endpoints-disabled` / `<ns>.mcp.tools-disabled` /
|
|
1136
|
+
* `<ns>.mcp.tools-on-demand` / `<ns>.webmcp-enabled`。
|
|
1137
|
+
*
|
|
1138
|
+
* 名单里的工具**一律带 endpoint**(`<endpoint>/<tool>`)。旧实现存裸工具名,
|
|
1139
|
+
* 两个端点注册同名工具时禁用其一会连坐另一个——那个缺陷在这里一次修完。
|
|
1140
|
+
*
|
|
1141
|
+
* 一律实时读存储、不做进程内缓存:console 改完当轮生效是既有语义。
|
|
1142
|
+
*/
|
|
1143
|
+
function createLocalStorageMcpVisibility(namespace) {
|
|
1144
|
+
const endpointsKey = `${namespace}.mcp.endpoints-disabled`;
|
|
1145
|
+
const toolsKey = `${namespace}.mcp.tools-disabled`;
|
|
1146
|
+
const onDemandKey = `${namespace}.mcp.tools-on-demand`;
|
|
1147
|
+
const webMcpKey = `${namespace}.webmcp-enabled`;
|
|
1148
|
+
const storage = () => {
|
|
1149
|
+
try {
|
|
1150
|
+
return globalThis.localStorage;
|
|
1151
|
+
} catch {
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
return {
|
|
1156
|
+
isEndpointEnabled: (endpoint) => !readList(storage(), endpointsKey).has(endpoint),
|
|
1157
|
+
setEndpointEnabled: (endpoint, enabled) => writeList(storage(), endpointsKey, toggle(readList(storage(), endpointsKey), endpoint, !enabled)),
|
|
1158
|
+
isToolEnabled: (endpoint, tool) => !readList(storage(), toolsKey).has(toolKey(endpoint, tool)),
|
|
1159
|
+
setToolEnabled: (endpoint, tool, enabled) => writeList(storage(), toolsKey, toggle(readList(storage(), toolsKey), toolKey(endpoint, tool), !enabled)),
|
|
1160
|
+
toolDisclosure: (endpoint, tool) => readList(storage(), onDemandKey).has(toolKey(endpoint, tool)) ? "on-demand" : "always",
|
|
1161
|
+
setToolDisclosure: (endpoint, tool, value) => writeList(storage(), onDemandKey, toggle(readList(storage(), onDemandKey), toolKey(endpoint, tool), value === "on-demand")),
|
|
1162
|
+
isWebMcpEnabled: () => {
|
|
1163
|
+
try {
|
|
1164
|
+
return storage()?.getItem(webMcpKey) !== "0";
|
|
1165
|
+
} catch {
|
|
1166
|
+
return true;
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
setWebMcpEnabled: (enabled) => {
|
|
1170
|
+
try {
|
|
1171
|
+
storage()?.setItem(webMcpKey, enabled ? "1" : "0");
|
|
1172
|
+
} catch {}
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
//#endregion
|
|
1178
|
+
//#region ../mcp/src/pageHost/createPageMcpEndpoint.ts
|
|
1179
|
+
const resolve = (value) => value === void 0 ? [] : typeof value === "function" ? value() : value;
|
|
1180
|
+
/** 裸返回值 → MCP 响应。structuredContent 只在返回值是非数组对象时给出(规范用 z.record) */
|
|
1181
|
+
function toMcpResult(value) {
|
|
1182
|
+
const content = [{
|
|
1183
|
+
type: "text",
|
|
1184
|
+
text: JSON.stringify(value ?? null)
|
|
1185
|
+
}];
|
|
1186
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? {
|
|
1187
|
+
content,
|
|
1188
|
+
structuredContent: value
|
|
1189
|
+
} : { content };
|
|
1190
|
+
}
|
|
1191
|
+
/** zod 为 optional peer:与 serveSkillAsMcp 同款懒加载 */
|
|
1192
|
+
async function toZodShape(schema) {
|
|
1193
|
+
const { fromJSONSchema } = await import("zod");
|
|
1194
|
+
return fromJSONSchema(schema);
|
|
1195
|
+
}
|
|
1196
|
+
const defaultWebMcpApi = () => {
|
|
1197
|
+
const doc = globalThis.document;
|
|
1198
|
+
const nav = globalThis.navigator;
|
|
1199
|
+
return doc?.modelContext ?? nav?.modelContext;
|
|
1200
|
+
};
|
|
1201
|
+
function createPageMcpEndpoint(options) {
|
|
1202
|
+
const { endpoint } = options;
|
|
1203
|
+
const visibility = options.visibility ?? createLocalStorageMcpVisibility(endpoint);
|
|
1204
|
+
const registry = new EndpointRegistry();
|
|
1205
|
+
const pageSkills = new TemporarySkillProvider({
|
|
1206
|
+
registry,
|
|
1207
|
+
endpoint
|
|
1208
|
+
});
|
|
1209
|
+
const webMcpList = options.webMcp === false ? [] : options.webMcp === void 0 ? [new ExperimentalWebMcpAdapter(defaultWebMcpApi, { enabled: visibility.isWebMcpEnabled() })] : options.webMcp instanceof ExperimentalWebMcpAdapter ? [options.webMcp] : options.webMcp;
|
|
1210
|
+
const webmcp = webMcpList[0];
|
|
1211
|
+
const plugin = new McpRuntimePlugin({
|
|
1212
|
+
registry,
|
|
1213
|
+
webMcp: webMcpList,
|
|
1214
|
+
endpoints: [endpoint],
|
|
1215
|
+
...options.onWarning ? { onWarning: options.onWarning } : {},
|
|
1216
|
+
...options.trust ? { trust: options.trust } : {},
|
|
1217
|
+
visibility: {
|
|
1218
|
+
isEndpointEnabled: (name) => visibility.isEndpointEnabled(name),
|
|
1219
|
+
isEndpointToolEnabled: (name, tool) => visibility.isToolEnabled(name, tool),
|
|
1220
|
+
isWebMcpToolEnabled: (tool) => visibility.isWebMcpEnabled() && visibility.isToolEnabled("webmcp", tool),
|
|
1221
|
+
endpointToolDisclosure: (name, tool) => {
|
|
1222
|
+
const value = visibility.toolDisclosure(name, tool);
|
|
1223
|
+
return value === "always" ? void 0 : value;
|
|
1224
|
+
},
|
|
1225
|
+
webMcpToolDisclosure: (tool) => {
|
|
1226
|
+
const value = visibility.toolDisclosure(WEB_MCP_ENDPOINT, tool);
|
|
1227
|
+
return value === "always" ? void 0 : value;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
const toolSource = {
|
|
1232
|
+
kind: "mcp",
|
|
1233
|
+
listToolSpecs: () => plugin.listToolSpecs(),
|
|
1234
|
+
canHandle: (name) => plugin.canHandle(name),
|
|
1235
|
+
call: (name, args) => plugin.call(name, args),
|
|
1236
|
+
argCaptureTrust: (name) => plugin.argCaptureTrust(name),
|
|
1237
|
+
listSkills: () => pageSkills.listSkills(),
|
|
1238
|
+
loadSkill: (name) => pageSkills.loadSkill(name),
|
|
1239
|
+
readFile: (name, path) => pageSkills.readFile(name, path),
|
|
1240
|
+
...options.systemPrompt ? { systemPrompt: options.systemPrompt } : {}
|
|
1241
|
+
};
|
|
1242
|
+
let current;
|
|
1243
|
+
let serving;
|
|
1244
|
+
async function serveNow() {
|
|
1245
|
+
const [{ McpServer: McpServerCtor }, { Client: ClientCtor }] = await Promise.all([import("@modelcontextprotocol/sdk/server/mcp.js"), import("@modelcontextprotocol/sdk/client/index.js")]);
|
|
1246
|
+
const server = new McpServerCtor({
|
|
1247
|
+
name: options.serverInfo?.name ?? endpoint,
|
|
1248
|
+
version: options.serverInfo?.version ?? "1.0.0"
|
|
1249
|
+
});
|
|
1250
|
+
for (const tool of resolve(options.tools)) server.registerTool(tool.spec.name, {
|
|
1251
|
+
description: tool.spec.description ?? "",
|
|
1252
|
+
...tool.spec.inputSchema ? { inputSchema: await toZodShape(tool.spec.inputSchema) } : {}
|
|
1253
|
+
}, async (args) => toMcpResult(await tool.run(args ?? {})));
|
|
1254
|
+
for (const skill of resolve(options.skills)) await serveSkillAsMcp(server, skill);
|
|
1255
|
+
const channel = new MessageChannel();
|
|
1256
|
+
await server.connect(new MessageChannelTransport(channel.port2));
|
|
1257
|
+
const client = new ClientCtor({
|
|
1258
|
+
name: options.clientInfo?.name ?? `${endpoint}-host`,
|
|
1259
|
+
version: options.clientInfo?.version ?? "1.0.0"
|
|
1260
|
+
});
|
|
1261
|
+
await client.connect(new MessageChannelTransport(channel.port1));
|
|
1262
|
+
registry.set(endpoint, client);
|
|
1263
|
+
const prev = current;
|
|
1264
|
+
current = {
|
|
1265
|
+
server,
|
|
1266
|
+
client
|
|
1267
|
+
};
|
|
1268
|
+
if (prev) {
|
|
1269
|
+
await prev.client.close().catch(() => void 0);
|
|
1270
|
+
await prev.server.close().catch(() => void 0);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
function serve() {
|
|
1274
|
+
serving = (serving ?? Promise.resolve()).catch(() => void 0).then(() => serveNow());
|
|
1275
|
+
return serving;
|
|
1276
|
+
}
|
|
1277
|
+
return {
|
|
1278
|
+
endpoint,
|
|
1279
|
+
registry,
|
|
1280
|
+
plugin,
|
|
1281
|
+
pageSkills,
|
|
1282
|
+
webmcp,
|
|
1283
|
+
webMcpSources: webMcpList,
|
|
1284
|
+
toolSource,
|
|
1285
|
+
visibility,
|
|
1286
|
+
ready: serve().catch(() => void 0),
|
|
1287
|
+
serve,
|
|
1288
|
+
async close() {
|
|
1289
|
+
const prev = current;
|
|
1290
|
+
current = void 0;
|
|
1291
|
+
registry.unregister(endpoint);
|
|
1292
|
+
if (prev) {
|
|
1293
|
+
await prev.client.close().catch(() => void 0);
|
|
1294
|
+
await prev.server.close().catch(() => void 0);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
//#endregion
|
|
1301
|
+
//#region ../mcp/src/pageHost/createConnectFacade.ts
|
|
1302
|
+
const SCHEMA_SUMMARY_MAX = 120;
|
|
1303
|
+
/** localStorage 版远程端点存储(键 `<ns>.mcp.remote-endpoints`) */
|
|
1304
|
+
function createLocalStorageRemoteEndpoints(namespace) {
|
|
1305
|
+
const key = `${namespace}.mcp.remote-endpoints`;
|
|
1306
|
+
return {
|
|
1307
|
+
load() {
|
|
1308
|
+
try {
|
|
1309
|
+
const raw = globalThis.localStorage?.getItem(key);
|
|
1310
|
+
const parsed = raw ? JSON.parse(raw) : [];
|
|
1311
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
1312
|
+
} catch {
|
|
1313
|
+
return [];
|
|
1314
|
+
}
|
|
1315
|
+
},
|
|
1316
|
+
save(configs) {
|
|
1317
|
+
try {
|
|
1318
|
+
globalThis.localStorage?.setItem(key, JSON.stringify(configs));
|
|
1319
|
+
} catch {}
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
function createConnectFacade(options) {
|
|
1324
|
+
const host = options.host;
|
|
1325
|
+
const builtin = host.endpoint;
|
|
1326
|
+
const visibility = host.visibility;
|
|
1327
|
+
const store = options.remoteEndpoints === false ? void 0 : options.remoteEndpoints ?? void 0;
|
|
1328
|
+
let remoteConfigs = store?.load() ?? [];
|
|
1329
|
+
const connections = /* @__PURE__ */ new Map();
|
|
1330
|
+
const unsupported = (what) => {
|
|
1331
|
+
throw new WebSkillError("TOOL_UNSUPPORTED", `${what} is not supported by this host: it does not persist remote MCP endpoints.`);
|
|
1332
|
+
};
|
|
1333
|
+
const persist = () => store?.save(remoteConfigs);
|
|
1334
|
+
/**
|
|
1335
|
+
* 配置对象上的 `disabledTools` 是**镜像**:唯一写入源是 `visibility`。
|
|
1336
|
+
* 两处各写一份的话,「治理在工作」就得靠手工保持同步。
|
|
1337
|
+
*/
|
|
1338
|
+
const mirrorDisabledTools = (config, tools) => {
|
|
1339
|
+
const disabled = tools.filter((tool) => !visibility.isToolEnabled(config.name, tool));
|
|
1340
|
+
if (disabled.length > 0) config.disabledTools = disabled;
|
|
1341
|
+
else delete config.disabledTools;
|
|
1342
|
+
};
|
|
1343
|
+
const listOf = async (endpoint) => {
|
|
1344
|
+
const { tools } = await host.registry.get(endpoint).listTools();
|
|
1345
|
+
return tools;
|
|
1346
|
+
};
|
|
1347
|
+
const toolView = (endpoint, tool) => ({
|
|
1348
|
+
endpoint,
|
|
1349
|
+
name: tool.name,
|
|
1350
|
+
enabled: visibility.isToolEnabled(endpoint, tool.name),
|
|
1351
|
+
disclosure: visibility.toolDisclosure(endpoint, tool.name),
|
|
1352
|
+
...tool.description !== void 0 ? { description: tool.description } : {},
|
|
1353
|
+
...tool.inputSchema !== void 0 ? {
|
|
1354
|
+
schemaSummary: JSON.stringify(tool.inputSchema).slice(0, SCHEMA_SUMMARY_MAX),
|
|
1355
|
+
inputSchema: tool.inputSchema
|
|
1356
|
+
} : {}
|
|
1357
|
+
});
|
|
1358
|
+
async function connectRemote(config) {
|
|
1359
|
+
const prev = connections.get(config.name);
|
|
1360
|
+
if (prev) await prev.close().catch(() => void 0);
|
|
1361
|
+
connections.set(config.name, {
|
|
1362
|
+
close: () => Promise.resolve(),
|
|
1363
|
+
status: "connecting"
|
|
1364
|
+
});
|
|
1365
|
+
try {
|
|
1366
|
+
const handle = await connectRemoteEndpoint(host.registry, {
|
|
1367
|
+
endpoint: config.name,
|
|
1368
|
+
url: config.url,
|
|
1369
|
+
...config.transport ? { transport: config.transport } : {},
|
|
1370
|
+
...config.headers ? { headers: config.headers } : {},
|
|
1371
|
+
allowHttp: config.allowHttp ?? false,
|
|
1372
|
+
allowPrivateHosts: config.allowPrivateHosts ?? false
|
|
1373
|
+
});
|
|
1374
|
+
connections.set(config.name, {
|
|
1375
|
+
close: handle.close,
|
|
1376
|
+
status: "connected"
|
|
1377
|
+
});
|
|
1378
|
+
try {
|
|
1379
|
+
const tools = await listOf(config.name);
|
|
1380
|
+
connections.set(config.name, {
|
|
1381
|
+
close: handle.close,
|
|
1382
|
+
status: "connected",
|
|
1383
|
+
toolCount: tools.length
|
|
1384
|
+
});
|
|
1385
|
+
} catch {}
|
|
1386
|
+
} catch (e) {
|
|
1387
|
+
connections.set(config.name, {
|
|
1388
|
+
close: () => Promise.resolve(),
|
|
1389
|
+
status: "failed",
|
|
1390
|
+
error: messageOf(e)
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
for (const config of remoteConfigs) connectRemote(config);
|
|
1395
|
+
const endpointView = (config) => {
|
|
1396
|
+
const conn = connections.get(config.name);
|
|
1397
|
+
return {
|
|
1398
|
+
config: {
|
|
1399
|
+
...config,
|
|
1400
|
+
enabled: visibility.isEndpointEnabled(config.name)
|
|
1401
|
+
},
|
|
1402
|
+
status: conn?.status ?? "unavailable",
|
|
1403
|
+
...conn?.error !== void 0 ? { error: conn.error } : {},
|
|
1404
|
+
...conn?.toolCount !== void 0 ? { toolCount: conn.toolCount } : {}
|
|
1405
|
+
};
|
|
1406
|
+
};
|
|
1407
|
+
/**
|
|
1408
|
+
* 一个 WebMCP 来源的视图。多来源时逐个建、**不合并**——
|
|
1409
|
+
* 合并后「部分来源可用」这个问题就没法回答了(分册 26 FR-26.5)。
|
|
1410
|
+
*/
|
|
1411
|
+
const webMcpSourceOf = (adapter) => ({
|
|
1412
|
+
...adapter.sourceId !== void 0 ? {
|
|
1413
|
+
id: adapter.sourceId,
|
|
1414
|
+
label: adapter.sourceId
|
|
1415
|
+
} : {},
|
|
1416
|
+
isAvailable: () => adapter.isAvailable(),
|
|
1417
|
+
isEnabled: () => visibility.isWebMcpEnabled() && adapter.isEnabled(),
|
|
1418
|
+
setEnabled: (on) => {
|
|
1419
|
+
adapter.setEnabled(on);
|
|
1420
|
+
visibility.setWebMcpEnabled(on);
|
|
1421
|
+
},
|
|
1422
|
+
listTools: async () => {
|
|
1423
|
+
if (!adapter.isEnabled() || !visibility.isWebMcpEnabled()) return [];
|
|
1424
|
+
return (await adapter.listTools() ?? []).map((tool) => ({
|
|
1425
|
+
endpoint: WEB_MCP_ENDPOINT,
|
|
1426
|
+
name: tool.name,
|
|
1427
|
+
enabled: visibility.isToolEnabled(WEB_MCP_ENDPOINT, tool.name),
|
|
1428
|
+
disclosure: visibility.toolDisclosure(WEB_MCP_ENDPOINT, tool.name),
|
|
1429
|
+
...tool.description !== void 0 ? { description: tool.description } : {},
|
|
1430
|
+
...tool.origin !== void 0 ? { origin: tool.origin } : {},
|
|
1431
|
+
...tool.annotations !== void 0 ? { annotations: tool.annotations } : {}
|
|
1432
|
+
}));
|
|
1433
|
+
},
|
|
1434
|
+
setToolEnabled: (tool, enabled) => visibility.setToolEnabled(WEB_MCP_ENDPOINT, tool, enabled),
|
|
1435
|
+
setToolDisclosure: (tool, disclosure) => visibility.setToolDisclosure(WEB_MCP_ENDPOINT, tool, disclosure)
|
|
1436
|
+
});
|
|
1437
|
+
const sources = host.webMcpSources.map(webMcpSourceOf);
|
|
1438
|
+
/**
|
|
1439
|
+
* 装配期建一次并复用:每次读都新建对象的话,React 侧把它放进 `useMemo`
|
|
1440
|
+
* 依赖表就会每帧重算——引用不稳定在这个仓库里烧出过无限重渲染。
|
|
1441
|
+
*/
|
|
1442
|
+
const webmcp = sources.length > 1 ? {
|
|
1443
|
+
...sources[0],
|
|
1444
|
+
sources
|
|
1445
|
+
} : sources[0] ?? {
|
|
1446
|
+
isAvailable: () => false,
|
|
1447
|
+
isEnabled: () => false,
|
|
1448
|
+
setEnabled: () => void 0,
|
|
1449
|
+
listTools: async () => []
|
|
1450
|
+
};
|
|
1451
|
+
return {
|
|
1452
|
+
async listEndpoints() {
|
|
1453
|
+
const builtinTools = await listOf(builtin).catch(() => []);
|
|
1454
|
+
const builtinConfig = {
|
|
1455
|
+
name: builtin,
|
|
1456
|
+
url: options.builtinEndpointUrl ?? `in-process://${builtin}`,
|
|
1457
|
+
enabled: visibility.isEndpointEnabled(builtin)
|
|
1458
|
+
};
|
|
1459
|
+
mirrorDisabledTools(builtinConfig, builtinTools.map((t) => t.name));
|
|
1460
|
+
return [{
|
|
1461
|
+
config: builtinConfig,
|
|
1462
|
+
status: "connected",
|
|
1463
|
+
toolCount: builtinTools.length
|
|
1464
|
+
}, ...remoteConfigs.map(endpointView)];
|
|
1465
|
+
},
|
|
1466
|
+
async addEndpoint(config) {
|
|
1467
|
+
if (!store) unsupported("Adding an endpoint by hand");
|
|
1468
|
+
if (config.name === builtin || remoteConfigs.some((c) => c.name === config.name)) throw new WebSkillError("VALIDATION_FAILED", `Endpoint "${config.name}" is already configured`);
|
|
1469
|
+
remoteConfigs = [...remoteConfigs, config];
|
|
1470
|
+
persist();
|
|
1471
|
+
await connectRemote(config);
|
|
1472
|
+
const conn = connections.get(config.name);
|
|
1473
|
+
if (conn?.status === "failed") throw new WebSkillError("MCP_ENDPOINT_UNAVAILABLE", conn.error ?? `Failed to connect endpoint "${config.name}"`);
|
|
1474
|
+
},
|
|
1475
|
+
async removeEndpoint(name) {
|
|
1476
|
+
if (!store) unsupported("Removing an endpoint by hand");
|
|
1477
|
+
if (name === builtin) throw new WebSkillError("VALIDATION_FAILED", `The built-in "${builtin}" endpoint cannot be removed: it is this page itself.`);
|
|
1478
|
+
const conn = connections.get(name);
|
|
1479
|
+
if (conn) {
|
|
1480
|
+
await conn.close().catch(() => void 0);
|
|
1481
|
+
connections.delete(name);
|
|
1482
|
+
}
|
|
1483
|
+
host.registry.unregister(name);
|
|
1484
|
+
remoteConfigs = remoteConfigs.filter((c) => c.name !== name);
|
|
1485
|
+
persist();
|
|
1486
|
+
},
|
|
1487
|
+
async reconnect(name) {
|
|
1488
|
+
if (name === builtin) {
|
|
1489
|
+
await host.serve();
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
const config = remoteConfigs.find((c) => c.name === name);
|
|
1493
|
+
if (!config) throw new WebSkillError("VALIDATION_FAILED", `Endpoint "${name}" is not configured`);
|
|
1494
|
+
await connectRemote(config);
|
|
1495
|
+
},
|
|
1496
|
+
async listTools() {
|
|
1497
|
+
const out = [];
|
|
1498
|
+
for (const tool of await listOf(builtin).catch(() => [])) out.push(toolView(builtin, tool));
|
|
1499
|
+
for (const config of remoteConfigs) {
|
|
1500
|
+
if (connections.get(config.name)?.status !== "connected") continue;
|
|
1501
|
+
for (const tool of await listOf(config.name).catch(() => [])) out.push(toolView(config.name, tool));
|
|
1502
|
+
}
|
|
1503
|
+
return out;
|
|
1504
|
+
},
|
|
1505
|
+
async testEndpoint(name) {
|
|
1506
|
+
const start = Date.now();
|
|
1507
|
+
try {
|
|
1508
|
+
await listOf(name);
|
|
1509
|
+
return {
|
|
1510
|
+
ok: true,
|
|
1511
|
+
latencyMs: Date.now() - start,
|
|
1512
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1513
|
+
};
|
|
1514
|
+
} catch (e) {
|
|
1515
|
+
return {
|
|
1516
|
+
ok: false,
|
|
1517
|
+
latencyMs: Date.now() - start,
|
|
1518
|
+
error: messageOf(e),
|
|
1519
|
+
detail: e instanceof Error ? e.stack ?? e.message : String(e),
|
|
1520
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
},
|
|
1524
|
+
async temporarySkills() {
|
|
1525
|
+
if (options.temporarySkills) return options.temporarySkills();
|
|
1526
|
+
return (await host.pageSkills.listSkills().catch(() => [])).map((skill) => ({
|
|
1527
|
+
name: skill.name,
|
|
1528
|
+
...skill.description !== void 0 ? { description: skill.description } : {},
|
|
1529
|
+
source: skill.source ?? builtin,
|
|
1530
|
+
...skill.root !== void 0 ? { origin: skill.root } : {}
|
|
1531
|
+
}));
|
|
1532
|
+
},
|
|
1533
|
+
...options.temporarySkillDetail ? { temporarySkillDetail: async (name, origin) => options.temporarySkillDetail(name, origin) } : {},
|
|
1534
|
+
...options.pagePerception ? { pagePerception: async () => options.pagePerception() } : {},
|
|
1535
|
+
...options.pageActionConsent ? {
|
|
1536
|
+
pageActionConsents: () => options.pageActionConsent.list(),
|
|
1537
|
+
forgetPageActionConsent: (id) => options.pageActionConsent.forget(id),
|
|
1538
|
+
forgetPageActionConsentScope: (scope) => options.pageActionConsent.forgetScope(scope)
|
|
1539
|
+
} : {},
|
|
1540
|
+
async setEndpointEnabled(name, enabled) {
|
|
1541
|
+
visibility.setEndpointEnabled(name, enabled);
|
|
1542
|
+
const config = remoteConfigs.find((c) => c.name === name);
|
|
1543
|
+
if (config) {
|
|
1544
|
+
config.enabled = enabled;
|
|
1545
|
+
persist();
|
|
1546
|
+
}
|
|
1547
|
+
},
|
|
1548
|
+
async setToolEnabled(endpoint, tool, enabled) {
|
|
1549
|
+
visibility.setToolEnabled(endpoint, tool, enabled);
|
|
1550
|
+
const config = remoteConfigs.find((c) => c.name === endpoint);
|
|
1551
|
+
if (config) {
|
|
1552
|
+
mirrorDisabledTools(config, (await listOf(endpoint).catch(() => [])).map((t) => t.name));
|
|
1553
|
+
persist();
|
|
1554
|
+
}
|
|
1555
|
+
},
|
|
1556
|
+
async setToolDisclosure(endpoint, tool, disclosure) {
|
|
1557
|
+
visibility.setToolDisclosure(endpoint, tool, disclosure);
|
|
1558
|
+
},
|
|
1559
|
+
webmcp
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
//#endregion
|
|
1564
|
+
export { EndpointRegistry, ExperimentalWebMcpAdapter, McpRuntimePlugin, McpToolResolver, MessageChannelTransport, PAGE_HOST_ANCHOR_KEY, TemporarySkillProvider, WEB_MCP_ENDPOINT, WEB_MCP_SOURCE_ID_MAX, catalogMerge, connectRemoteEndpoint, createConnectFacade, createLocalStorageMcpVisibility, createLocalStorageRemoteEndpoints, createMemoryOAuthStores, createOAuthProvider, createPageMcpEndpoint, declareDataSourcesToPageHost, endpointToolLlmName, notifyPageHost, parseEndpointToolLlmName, parseWebMcpToolLlmName, serveSkillAsMcp, validateJsonRpcMessage, webMcpToolLlmName };
|
|
@@ -29,7 +29,8 @@ const DEFAULT_SURFACE_FORM_TEXTS = {
|
|
|
29
29
|
readOnlySnapshot: "This is a saved record — its actions are no longer available.",
|
|
30
30
|
suggested: DEFAULT_INTERACTION_TEXTS.suggested,
|
|
31
31
|
useSuggestion: DEFAULT_INTERACTION_TEXTS.useSuggestion,
|
|
32
|
-
openDocument: "Open document"
|
|
32
|
+
openDocument: "Open document",
|
|
33
|
+
noOptions: "No options are available for this field."
|
|
33
34
|
};
|
|
34
35
|
function resolveSurfaceFormTexts(texts) {
|
|
35
36
|
return texts ? {
|
package/dist/node.js
CHANGED
|
@@ -2,7 +2,7 @@ import { n as messageOf, t as WebSkillError } from "./errors-BDZNpC13.js";
|
|
|
2
2
|
import { A as SKILL_MANIFEST_FILE, D as verifySkillSignature, M as buildManifest, O as MANIFEST_EXCLUDED_FILES, P as verifyManifest, b as parseSkillMarkdown, c as unzipWithLimits, f as SKILL_PACK_FILE, k as SKILLS_LOCKFILE, m as parseSkillPackManifest, o as readResponseWithLimit, p as exportSkills, s as resolveArchiveLimits, t as validateSkills, w as readSkillSignature, y as isValidSkillName } from "./skill-CAJMsLod.js";
|
|
3
3
|
import { a as atomicWriteText, o as isAtomicTempPath, r as resolveInsideRoot, t as assertSafePathSegment } from "./pathSecurity-B1owvJAF.js";
|
|
4
4
|
import { t as assertRemoteUrlAllowed } from "./urlSafety-CiSuCJvX.js";
|
|
5
|
-
import { B as createScriptContext, a as networkPolicyLibSource, c as bridgeError, d as FsMemoryStore, f as WebSkillRuntime, l as parseBridgeRequest, r as normalizeToolError, s as UPLOAD_FILES_UNAVAILABLE, t as CapabilityApproval, u as FsArtifactStore } from "./approval-
|
|
5
|
+
import { B as createScriptContext, a as networkPolicyLibSource, c as bridgeError, d as FsMemoryStore, f as WebSkillRuntime, l as parseBridgeRequest, r as normalizeToolError, s as UPLOAD_FILES_UNAVAILABLE, t as CapabilityApproval, u as FsArtifactStore } from "./approval-Bbh_Apwg.js";
|
|
6
6
|
import { n as normalizeToolContent } from "./external-_ZRQe-V9.js";
|
|
7
7
|
import { i as probeLlmCapabilities } from "./env-Bj1MI2Ww.js";
|
|
8
8
|
import { t as AUDIT_EVENT_TYPES } from "./eventTypes-CwciaqCU.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { g as uiCatalog } from "./openUiSpecLang-CLXJEeRU.js";
|
|
2
2
|
import { t as CatalogNode } from "./ui-react.js";
|
|
3
3
|
import { Renderer, createLibrary, defineComponent } from "./dist-CTsxblSD.js";
|
|
4
4
|
import { z } from "zod";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as interactionToFormModel } from "./miniChart-
|
|
1
|
+
import { u as interactionToFormModel } from "./miniChart-D0nYMzz8.js";
|
|
2
2
|
import { toJSONSchema, z } from "zod";
|
|
3
3
|
|
|
4
4
|
//#region ../ui/src/chart/echart.ts
|
|
@@ -738,11 +738,17 @@ const fieldCondition = z.lazy(() => z.union([
|
|
|
738
738
|
z.object({ allOf: z.array(fieldCondition).min(1) }),
|
|
739
739
|
z.object({ anyOf: z.array(fieldCondition).min(1) })
|
|
740
740
|
]));
|
|
741
|
-
/**
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
741
|
+
/** 选项写死在声明里。异步取值(`optionsSource`)四档渲染器都没实现,已从模型可见接口撤下,见 deferred-items D41 */
|
|
742
|
+
const optionScalar = z.union([z.string(), z.number()]);
|
|
743
|
+
/**
|
|
744
|
+
* 对选项形状宽容:裸标量、缺 label、缺 value 都收。
|
|
745
|
+
* 严格 schema 的代价是整个 `options` 被 sanitize 丢掉 —— 用户拿到的是空下拉,
|
|
746
|
+
* 而不是「少了一个选项」。归一化归 `normalizeFieldOptions`。
|
|
747
|
+
*/
|
|
748
|
+
const fieldOptions = z.array(z.union([optionScalar, z.object({
|
|
749
|
+
label: optionScalar.optional(),
|
|
750
|
+
value: optionScalar.optional()
|
|
751
|
+
}).refine((option) => option.label !== void 0 || option.value !== void 0, { message: "Option needs at least one of label / value" })]));
|
|
746
752
|
/** Tabs 的面板与 Grid 的格子必须自成容器,否则「每容器至多一个表单」就没有落脚点 */
|
|
747
753
|
const PANEL = ["Card", "Stack"];
|
|
748
754
|
/**
|
|
@@ -1028,8 +1034,12 @@ const uiCatalog = defineUiCatalog({
|
|
|
1028
1034
|
z.boolean(),
|
|
1029
1035
|
z.null()
|
|
1030
1036
|
]))),
|
|
1031
|
-
/**
|
|
1032
|
-
|
|
1037
|
+
/**
|
|
1038
|
+
* 列宽权重。**这里只管形状,不管取值**:长度不符、负数、全零都由
|
|
1039
|
+
* `normalizeColumnWidths` 整项忽略并回退等权重。在 schema 上写 `positive()`
|
|
1040
|
+
* 会让一个越界的权重把整张表打成降级提示——那正是分册 25 要求不能发生的事。
|
|
1041
|
+
*/
|
|
1042
|
+
columnWidths: z.array(z.number()).optional()
|
|
1033
1043
|
}),
|
|
1034
1044
|
example: {
|
|
1035
1045
|
component: "Table",
|
|
@@ -1169,12 +1179,10 @@ const uiCatalog = defineUiCatalog({
|
|
|
1169
1179
|
description: z.string().optional(),
|
|
1170
1180
|
defaultValue: z.unknown().optional(),
|
|
1171
1181
|
options: fieldOptions.optional(),
|
|
1172
|
-
/** 声明后选项由宕主异步提供;与写死的 options 互斥 */
|
|
1173
|
-
optionsSource: z.string().optional(),
|
|
1174
1182
|
visibleWhen: fieldCondition.optional()
|
|
1175
1183
|
}),
|
|
1176
1184
|
constraints: [
|
|
1177
|
-
"select / multi-select must
|
|
1185
|
+
"select / multi-select must list every choice in options; an empty options array leaves the user with nothing to pick",
|
|
1178
1186
|
"password values are never persisted, never echoed and never enter the user profile",
|
|
1179
1187
|
"visibleWhen may only reference fields declared in the same Form"
|
|
1180
1188
|
],
|
|
@@ -1184,7 +1192,13 @@ const uiCatalog = defineUiCatalog({
|
|
|
1184
1192
|
name: "range",
|
|
1185
1193
|
label: "Range",
|
|
1186
1194
|
type: "select",
|
|
1187
|
-
options: [
|
|
1195
|
+
options: [{
|
|
1196
|
+
label: "Last 7 days",
|
|
1197
|
+
value: "7d"
|
|
1198
|
+
}, {
|
|
1199
|
+
label: "Last 30 days",
|
|
1200
|
+
value: "30d"
|
|
1201
|
+
}]
|
|
1188
1202
|
}
|
|
1189
1203
|
}
|
|
1190
1204
|
},
|
|
@@ -1720,6 +1734,45 @@ function resolveColumnWidths(weights, available, minWidth) {
|
|
|
1720
1734
|
return result;
|
|
1721
1735
|
}
|
|
1722
1736
|
|
|
1737
|
+
//#endregion
|
|
1738
|
+
//#region ../ui/src/catalog/fieldOptions.ts
|
|
1739
|
+
const isScalar = (value) => typeof value === "string" || typeof value === "number";
|
|
1740
|
+
/**
|
|
1741
|
+
* 模型给出的选项形状五花八门:裸标量(`['January', 'February']`)、只有 `label`、
|
|
1742
|
+
* 只有 `value`。整项丢掉的代价是下拉变空,必填字段再也交不出去,
|
|
1743
|
+
* 所以能补的一律补齐;补不出标签的(既无 label 又无 value)才丢。
|
|
1744
|
+
*/
|
|
1745
|
+
function normalizeFieldOptions(raw) {
|
|
1746
|
+
if (!Array.isArray(raw)) return [];
|
|
1747
|
+
const options = [];
|
|
1748
|
+
for (const item of raw) {
|
|
1749
|
+
if (isScalar(item)) {
|
|
1750
|
+
options.push({
|
|
1751
|
+
label: String(item),
|
|
1752
|
+
value: item
|
|
1753
|
+
});
|
|
1754
|
+
continue;
|
|
1755
|
+
}
|
|
1756
|
+
if (item === null || typeof item !== "object") continue;
|
|
1757
|
+
const record = item;
|
|
1758
|
+
const label = record["label"];
|
|
1759
|
+
const value = record["value"];
|
|
1760
|
+
if (isScalar(label) && isScalar(value)) options.push({
|
|
1761
|
+
label: String(label),
|
|
1762
|
+
value
|
|
1763
|
+
});
|
|
1764
|
+
else if (isScalar(label)) options.push({
|
|
1765
|
+
label: String(label),
|
|
1766
|
+
value: label
|
|
1767
|
+
});
|
|
1768
|
+
else if (isScalar(value)) options.push({
|
|
1769
|
+
label: String(value),
|
|
1770
|
+
value
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
return options;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1723
1776
|
//#endregion
|
|
1724
1777
|
//#region ../ui/src/catalog/toJsonRenderSpec.ts
|
|
1725
1778
|
/**
|
|
@@ -1847,4 +1900,4 @@ function toOpenUiSpecLang(spec, catalog = uiCatalog) {
|
|
|
1847
1900
|
}
|
|
1848
1901
|
|
|
1849
1902
|
//#endregion
|
|
1850
|
-
export {
|
|
1903
|
+
export { DEFAULT_CHART_FONT_SIZES as C, toEchartsOption as D, resolveChartFontSizes as E, gaugePercent as S, mountEchart as T, defineUiCatalog as _, SPEC_TABLE_MIN_COLUMN_VAR as a, CATALOG_SCHEMA_MAX as b, resolveColumnWidths as c, collectFormScopes as d, collectScopedValues as f, uiCatalog as g, UI_CATALOG_PROMPT_BUDGET_BYTES as h, normalizeFieldOptions as i, MAX_CONDITION_DEPTH as l, UI_CATALOG_GROUPS as m, interactionToUiSpec as n, SPEC_TABLE_MIN_COLUMN_WIDTH as o, qualifyFieldName as p, toJsonRenderSpec as r, normalizeColumnWidths as s, toOpenUiSpecLang as t, evaluateFieldCondition as u, CATALOG_BUDGET_STAGE as v, chartSpecFromProps as w, PLANNED_INCREMENT as x, CATALOG_PROMPT_MAX as y };
|
package/dist/testing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as MockLlmClient } from "./llm-
|
|
1
|
+
import { t as MockLlmClient } from "./llm-eIQNO9tr.js";
|
|
2
2
|
import { t as MemoryArtifactStore } from "./memoryArtifactStore-D6dLeY55.js";
|
|
3
3
|
import { n as loadGoogleConfigFromEnv, r as loadLlmConfigFromEnv, t as loadAnthropicConfigFromEnv } from "./env-Bj1MI2Ww.js";
|
|
4
4
|
|
|
@@ -58,12 +58,12 @@ function toActionFrameScopes(scope) {
|
|
|
58
58
|
}];
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
|
-
* 本版的操作集(FR-25.1 / 0.16.0 FR-13.6
|
|
61
|
+
* 本版的操作集(FR-25.1 / 0.16.0 FR-13.6 / 0.18.0 FR-10.1)。拖拽仍不在内(D64);
|
|
62
62
|
* 导航只有 `back` 一个,且它**不接受地址**——可达面被浏览器历史封死(SC-1)。
|
|
63
63
|
*
|
|
64
|
-
* `select`/`set`/`attach`
|
|
64
|
+
* `select`/`set`/`attach`/`scroll` 与既有几个走**同一条** policy 路径,
|
|
65
65
|
* 范围白名单、逐次确认、审计三条硬约束因此天然覆盖到它们——
|
|
66
|
-
* 前提是新动作不绕过 policy
|
|
66
|
+
* 前提是新动作不绕过 policy 直调执行器(AC-25.4 守这一点)。
|
|
67
67
|
* @experimental
|
|
68
68
|
*/
|
|
69
69
|
const PAGE_ACTION_KINDS = [
|
|
@@ -73,6 +73,7 @@ const PAGE_ACTION_KINDS = [
|
|
|
73
73
|
"select",
|
|
74
74
|
"set",
|
|
75
75
|
"attach",
|
|
76
|
+
"scroll",
|
|
76
77
|
"back"
|
|
77
78
|
];
|
|
78
79
|
/**
|
package/dist/ui-react.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as UiSpecActionCapability, D as UiSpecSnapshot, O as UiSurfaceActionRequest, Ot as UiSpecNode, S as UiBridge, T as UiSpecEvent, b as RenderResultRequest, c as InteractionResponse, k as UiSurfaceActionResponse, s as InteractionRequest, w as UiSpecDrafts } from "./types-Btpdd1y--BcxQ10Fa.js";
|
|
2
2
|
import { N as DocumentSurfacePort } from "./index-BDyXe-a5.js";
|
|
3
|
-
import {
|
|
3
|
+
import { F as InteractionSpecLabels, J as SurfaceFormTexts, Y as SurfaceHostControlTexts, v as ChartFontSizes } from "./index-ad11wflI.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import React$1, { ComponentType, ReactNode } from "react";
|
|
6
6
|
import "react/jsx-runtime";
|