@webskill/sdk 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +3 -2
- package/dist/browser.js +47 -15
- package/dist/{dist-NM4Mylx4.js → dist-B77plHjw.js} +230 -105
- package/dist/{dist-D0saNPi_.js → dist-BQzncxXg.js} +98 -5
- package/dist/{dist-CvIMVwr3.js → dist-Chk8iB-E.js} +101 -38
- package/dist/{dist-BJobG0i-.js → dist-DNSG9FqC.js} +108 -69
- package/dist/governance.d.ts +13 -5
- package/dist/governance.js +50 -20
- package/dist/{index-iqcz3_NS.d.ts → index-CsDJvYGV.d.ts} +8 -3
- package/dist/{index-gjFuBevI.d.ts → index-DfINBEOy.d.ts} +29 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +7 -3
- package/dist/mcp.js +35 -18
- package/dist/node.d.ts +3 -3
- package/dist/node.js +3 -3
- package/dist/processSandboxEntry.js +11 -0
- package/dist/sandboxWorkerEntry.js +11 -0
- package/dist/{testing-B4pq6JYa.js → testing-BUoXvm1u.js} +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/{types-CKm5G_eQ-8W8FnP4u.d.ts → types-CKm5G_eQ-krKWW8WV.d.ts} +40 -7
- package/dist/ui-react.d.ts +3 -1
- package/dist/ui-react.js +8 -1
- package/dist/ui-vue.d.ts +3 -1
- package/dist/ui-vue.js +8 -1
- package/dist/ui.d.ts +20 -6
- package/dist/ui.js +3 -3
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as WebSkillError } from "./dist-
|
|
1
|
+
import { u as WebSkillError } from "./dist-BQzncxXg.js";
|
|
2
2
|
|
|
3
3
|
//#region ../ui/dist/index.js
|
|
4
4
|
/** 提交值按请求类型归形(WebFormBridge 与框架组件库共享单一来源) */
|
|
@@ -78,8 +78,16 @@ function interactionToFormModel(request) {
|
|
|
78
78
|
}
|
|
79
79
|
const isEmpty = (v) => v === void 0 || v === "";
|
|
80
80
|
/**
|
|
81
|
+
* 控件名 → 属性选择器:优先 CSS.escape(标识符形式,免引号转义);
|
|
82
|
+
* 无 CSS.escape 的环境退化为引号包裹 + 转义反斜杠/双引号(防选择器注入崩溃)
|
|
83
|
+
*/
|
|
84
|
+
function controlSelector(name) {
|
|
85
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") return `[data-webskill-control=${CSS.escape(name)}]`;
|
|
86
|
+
return `[data-webskill-control="${name.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"]`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
81
89
|
* 严格类型化控件值收集:
|
|
82
|
-
* - number 空串 → undefined(非 0)
|
|
90
|
+
* - number 空串 → undefined(非 0);非数值输入(NaN)按缺失处理(required 时入 missingRequired)
|
|
83
91
|
* - select 用 option 值本身经 JSON 编码比对命中(对象值可正确命中)
|
|
84
92
|
* - required 空值列入 missingRequired(由调用方阻止提交并标记)
|
|
85
93
|
*/
|
|
@@ -87,13 +95,17 @@ function collectValues(controls, container) {
|
|
|
87
95
|
const values = {};
|
|
88
96
|
const missingRequired = [];
|
|
89
97
|
for (const control of controls) {
|
|
90
|
-
const el = container.querySelector(
|
|
98
|
+
const el = container.querySelector(controlSelector(control.name));
|
|
91
99
|
let value;
|
|
92
100
|
if (el === null) value = void 0;
|
|
93
101
|
else if (control.control === "boolean") value = el.checked;
|
|
94
102
|
else if (control.control === "number") {
|
|
95
103
|
const raw = el.value.trim();
|
|
96
|
-
|
|
104
|
+
if (raw === "") value = void 0;
|
|
105
|
+
else {
|
|
106
|
+
const n = Number(raw);
|
|
107
|
+
value = Number.isNaN(n) ? void 0 : n;
|
|
108
|
+
}
|
|
97
109
|
} else if (control.control === "select") {
|
|
98
110
|
const attr = el.value;
|
|
99
111
|
const hit = control.options?.find((o) => JSON.stringify(o.value) === attr);
|
|
@@ -736,12 +748,15 @@ var VercelUiBridge = class {
|
|
|
736
748
|
* - 每行一条语句 `identifier = Expression`,`root = Component(...)` 为入口
|
|
737
749
|
* - 组件调用参数为位置参数;字符串双引号反斜杠转义
|
|
738
750
|
* - 组件词汇(与应用 Library 约定):Form(title, children)、
|
|
751
|
+
* Text(content)(纯展示文本,authorize 的能力描述用)、
|
|
739
752
|
* TextField(name, label, required?, defaultValue?)、NumberField、BooleanField(name, label, default?)、
|
|
740
753
|
* SelectField(name, label, options)、SubmitButton(label, actionType, requestId)、
|
|
741
754
|
* CancelButton(label, actionType, requestId)
|
|
742
755
|
*/
|
|
743
756
|
const OPENUI_SUBMIT_ACTION = "webskill:submit";
|
|
744
757
|
const OPENUI_CANCEL_ACTION = "webskill:cancel";
|
|
758
|
+
/** authorize 的 Allow 专用动作(提交即批准 → value:true;Deny 走 cancel → cancelled:true) */
|
|
759
|
+
const OPENUI_AUTHORIZE_ACTION = "webskill:authorize";
|
|
745
760
|
const str = (s) => JSON.stringify(s);
|
|
746
761
|
const val = (v) => JSON.stringify(v ?? null);
|
|
747
762
|
function fieldStatements(fields) {
|
|
@@ -808,18 +823,19 @@ function toOpenUiLang(request) {
|
|
|
808
823
|
break;
|
|
809
824
|
case "authorize":
|
|
810
825
|
title = "Authorization required";
|
|
811
|
-
fields = [
|
|
812
|
-
name: "approved",
|
|
813
|
-
label: request.message,
|
|
814
|
-
control: "boolean",
|
|
815
|
-
defaultValue: false
|
|
816
|
-
}];
|
|
826
|
+
fields = [];
|
|
817
827
|
break;
|
|
818
828
|
}
|
|
819
829
|
lines.push(...fieldStatements(fields));
|
|
820
830
|
const children = fields.map((_, i) => `f${i}`);
|
|
821
|
-
|
|
822
|
-
|
|
831
|
+
const isAuthorize = request.type === "authorize";
|
|
832
|
+
if (isAuthorize) {
|
|
833
|
+
lines.push(`message = Text(${str(request.message)})`);
|
|
834
|
+
children.push("message");
|
|
835
|
+
}
|
|
836
|
+
const submitAction = isAuthorize ? OPENUI_AUTHORIZE_ACTION : OPENUI_SUBMIT_ACTION;
|
|
837
|
+
lines.push(`submit = SubmitButton(${str(isAuthorize ? "Allow" : "Submit")}, ${str(submitAction)}, ${str(request.id)})`);
|
|
838
|
+
lines.push(`cancel = CancelButton(${str(isAuthorize ? "Deny" : "Cancel")}, ${str(OPENUI_CANCEL_ACTION)}, ${str(request.id)})`);
|
|
823
839
|
lines.push(`root = Form(${val(title)}, [${[
|
|
824
840
|
...children,
|
|
825
841
|
"submit",
|
|
@@ -838,6 +854,10 @@ function fromOpenUiAction(action) {
|
|
|
838
854
|
id,
|
|
839
855
|
cancelled: true
|
|
840
856
|
};
|
|
857
|
+
if (a.type === "webskill:authorize") return {
|
|
858
|
+
id,
|
|
859
|
+
value: true
|
|
860
|
+
};
|
|
841
861
|
if (a.type === "webskill:submit") {
|
|
842
862
|
const formState = a.formState;
|
|
843
863
|
if (formState) return {
|
|
@@ -1002,20 +1022,70 @@ function toA2uiMessages(request) {
|
|
|
1002
1022
|
return messages;
|
|
1003
1023
|
}
|
|
1004
1024
|
/**
|
|
1005
|
-
*
|
|
1025
|
+
* 按 InteractionRequest.type 解码提交值(绑定模型原样回传的是表单对象):
|
|
1026
|
+
* confirm 仅 confirmed===true 批准;select 还原原始 option 值(非字符串值经 JSON 编码比对);
|
|
1027
|
+
* form 的 number 字段转 number(NaN 保留原值);ask 取 answer;authorize 提交即批准。
|
|
1028
|
+
* 纯转换器:任何直接使用 fromA2uiAction 的消费者都应经此拿到正确类型。
|
|
1029
|
+
* @experimental
|
|
1030
|
+
*/
|
|
1031
|
+
function decodeInteractionResponse(request, response) {
|
|
1032
|
+
if (response.cancelled) return response;
|
|
1033
|
+
const values = response.value;
|
|
1034
|
+
switch (request.type) {
|
|
1035
|
+
case "confirm": return {
|
|
1036
|
+
...response,
|
|
1037
|
+
value: values?.confirmed === true
|
|
1038
|
+
};
|
|
1039
|
+
case "ask": return {
|
|
1040
|
+
...response,
|
|
1041
|
+
value: values?.answer
|
|
1042
|
+
};
|
|
1043
|
+
case "select": {
|
|
1044
|
+
const raw = values?.selected;
|
|
1045
|
+
const option = request.options.find((o) => o.value === raw || JSON.stringify(o.value) === raw);
|
|
1046
|
+
return {
|
|
1047
|
+
...response,
|
|
1048
|
+
value: option ? option.value : raw
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
case "form": {
|
|
1052
|
+
if (typeof values !== "object" || values === null) return response;
|
|
1053
|
+
const out = { ...values };
|
|
1054
|
+
for (const field of request.fields) if (field.type === "number" && out[field.name] !== void 0) {
|
|
1055
|
+
const n = Number(out[field.name]);
|
|
1056
|
+
if (!Number.isNaN(n)) out[field.name] = n;
|
|
1057
|
+
}
|
|
1058
|
+
return {
|
|
1059
|
+
...response,
|
|
1060
|
+
value: out
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
case "authorize": return {
|
|
1064
|
+
...response,
|
|
1065
|
+
value: true
|
|
1066
|
+
};
|
|
1067
|
+
default: return response;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* A2UI client→server action 事件 → InteractionResponse。
|
|
1072
|
+
* 传入 request 时按类型解码提交值(见 decodeInteractionResponse)。
|
|
1006
1073
|
* @experimental
|
|
1007
1074
|
*/
|
|
1008
|
-
function fromA2uiAction(event) {
|
|
1075
|
+
function fromA2uiAction(event, request) {
|
|
1009
1076
|
const e = event ?? {};
|
|
1010
1077
|
const id = String(e.context?.["requestId"] ?? "");
|
|
1011
1078
|
if (e.name === "webskill:cancel") return {
|
|
1012
1079
|
id,
|
|
1013
1080
|
cancelled: true
|
|
1014
1081
|
};
|
|
1015
|
-
if (e.name === "webskill:submit")
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1082
|
+
if (e.name === "webskill:submit") {
|
|
1083
|
+
const response = {
|
|
1084
|
+
id,
|
|
1085
|
+
value: e.context?.["values"] ?? e.context?.["form"] ?? e.context
|
|
1086
|
+
};
|
|
1087
|
+
return request ? decodeInteractionResponse(request, response) : response;
|
|
1088
|
+
}
|
|
1019
1089
|
return {
|
|
1020
1090
|
id,
|
|
1021
1091
|
value: e.context
|
|
@@ -1041,6 +1111,8 @@ function loadA2uiRuntime() {
|
|
|
1041
1111
|
var LitRendererBridge = class {
|
|
1042
1112
|
#mount;
|
|
1043
1113
|
#doc;
|
|
1114
|
+
/** 进行中的交互:id → resolve 与 surface 元素(cancel 时清理并以 cancelled resolve) */
|
|
1115
|
+
#pending = /* @__PURE__ */ new Map();
|
|
1044
1116
|
constructor(deps) {
|
|
1045
1117
|
this.#mount = deps.mount;
|
|
1046
1118
|
this.#doc = deps.document ?? deps.mount.ownerDocument;
|
|
@@ -1050,75 +1122,42 @@ var LitRendererBridge = class {
|
|
|
1050
1122
|
const messages = toA2uiMessages(input);
|
|
1051
1123
|
const surfaceId = `webskill-${input.id}`;
|
|
1052
1124
|
return new Promise((resolve) => {
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1125
|
+
const entry = { resolve };
|
|
1126
|
+
this.#pending.set(input.id, entry);
|
|
1055
1127
|
const processor = new MessageProcessor([basicCatalog], (action) => {
|
|
1056
1128
|
const surfaces = processor.getClientDataModel("v0.9.1")?.surfaces;
|
|
1057
1129
|
const formValues = typeof surfaces?.[surfaceId] === "object" && surfaces[surfaceId] !== null ? surfaces[surfaceId]["form"] ?? action.context : action.context;
|
|
1058
|
-
|
|
1059
|
-
|
|
1130
|
+
this.#pending.delete(input.id);
|
|
1131
|
+
entry.surfaceEl?.remove();
|
|
1132
|
+
resolve(fromA2uiAction({
|
|
1060
1133
|
...action,
|
|
1061
1134
|
context: {
|
|
1062
1135
|
...action.context,
|
|
1063
1136
|
values: formValues
|
|
1064
1137
|
}
|
|
1065
|
-
});
|
|
1066
|
-
resolve(this.#decodeValue(input, response));
|
|
1138
|
+
}, input));
|
|
1067
1139
|
}, { version: "v0.9.1" });
|
|
1068
1140
|
processor.onSurfaceCreated((surface) => {
|
|
1069
1141
|
const el = this.#doc.createElement("a2ui-surface");
|
|
1070
1142
|
el.surface = surface;
|
|
1071
|
-
surfaceEl = el;
|
|
1143
|
+
entry.surfaceEl = el;
|
|
1072
1144
|
this.#mount.appendChild(el);
|
|
1073
1145
|
});
|
|
1074
1146
|
processor.processMessages(messages);
|
|
1075
1147
|
});
|
|
1076
1148
|
}
|
|
1077
|
-
/**
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
...response,
|
|
1088
|
-
value: values?.confirmed === true
|
|
1089
|
-
};
|
|
1090
|
-
case "ask": return {
|
|
1091
|
-
...response,
|
|
1092
|
-
value: values?.answer
|
|
1093
|
-
};
|
|
1094
|
-
case "select": {
|
|
1095
|
-
const raw = values?.selected;
|
|
1096
|
-
const option = input.options.find((o) => o.value === raw || JSON.stringify(o.value) === raw);
|
|
1097
|
-
return {
|
|
1098
|
-
...response,
|
|
1099
|
-
value: option ? option.value : raw
|
|
1100
|
-
};
|
|
1101
|
-
}
|
|
1102
|
-
case "form": {
|
|
1103
|
-
if (typeof values !== "object" || values === null) return response;
|
|
1104
|
-
const out = { ...values };
|
|
1105
|
-
for (const field of input.fields) if (field.type === "number" && out[field.name] !== void 0) {
|
|
1106
|
-
const n = Number(out[field.name]);
|
|
1107
|
-
if (!Number.isNaN(n)) out[field.name] = n;
|
|
1108
|
-
}
|
|
1109
|
-
return {
|
|
1110
|
-
...response,
|
|
1111
|
-
value: out
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
case "authorize": return {
|
|
1115
|
-
...response,
|
|
1116
|
-
value: true
|
|
1117
|
-
};
|
|
1118
|
-
default: return response;
|
|
1119
|
-
}
|
|
1149
|
+
/** runtime 交互超时/取消:移除 surface 并以 cancelled resolve pending Promise(防悬挂 + DOM 残留) */
|
|
1150
|
+
cancel(id) {
|
|
1151
|
+
const entry = this.#pending.get(id);
|
|
1152
|
+
if (!entry) return;
|
|
1153
|
+
this.#pending.delete(id);
|
|
1154
|
+
entry.surfaceEl?.remove();
|
|
1155
|
+
entry.resolve({
|
|
1156
|
+
id,
|
|
1157
|
+
cancelled: true
|
|
1158
|
+
});
|
|
1120
1159
|
}
|
|
1121
1160
|
};
|
|
1122
1161
|
|
|
1123
1162
|
//#endregion
|
|
1124
|
-
export {
|
|
1163
|
+
export { renderMiniChart as C, toA2uiMessages as D, shapeInteractionValue as E, toOpenUiLang as O, renderBlocks as S, renderRenderResult as T, ensureStyles as _, CHART_PALETTE as a, fromVercelToolResult as b, OPENUI_CANCEL_ACTION as c, VercelUiBridge as d, WEBSKILL_STYLES_CSS as f, decodeInteractionResponse as g, collectValues as h, A2UI_VERSION as i, toVercelToolInvocation as k, OPENUI_SUBMIT_ACTION as l, chartToTable as m, A2UI_CANCEL_ACTION as n, LitRendererBridge as o, WebFormBridge as p, A2UI_SUBMIT_ACTION as r, OPENUI_AUTHORIZE_ACTION as s, A2UI_BASIC_CATALOG_ID as t, VERCEL_INTERACTION_TOOL_NAME as u, fromA2uiAction as v, renderMiniMarkdown as w, interactionToFormModel as x, fromOpenUiAction as y };
|
package/dist/governance.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Q as ScriptExecutor, dt as WebSkillRuntime, ft as WebSkillRuntimeDeps, q as RuntimeRun, tt as SkillStateGuard } from "./index-
|
|
3
|
-
import { d as SkillManager } from "./index-
|
|
1
|
+
import { B as SkillManifest, I as SkillDocument, P as SkillCatalogEntry, c as LlmClient, u as LlmMessage, v as UiBridge, w as FileSystemProvider } from "./types-CKm5G_eQ-krKWW8WV.js";
|
|
2
|
+
import { Q as ScriptExecutor, dt as WebSkillRuntime, ft as WebSkillRuntimeDeps, q as RuntimeRun, tt as SkillStateGuard } from "./index-DfINBEOy.js";
|
|
3
|
+
import { d as SkillManager } from "./index-CsDJvYGV.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';
|
|
@@ -143,7 +143,8 @@ declare class CompositeApprovalPolicy implements ApprovalPolicy {
|
|
|
143
143
|
}
|
|
144
144
|
//#endregion
|
|
145
145
|
//#region src/versioning/skillVersionStore.d.ts
|
|
146
|
-
/** 版本存储:manifest 快照 + parentVersionId 链;回滚 =
|
|
146
|
+
/** 版本存储:manifest 快照 + parentVersionId 链;回滚 = 追加新版本(谱系不断)。
|
|
147
|
+
* 保留策略:maxArchivesPerSkill(默认 5)超出时清理最旧版本(json + zip 归档一并删除)。 */
|
|
147
148
|
declare class SkillVersionStore {
|
|
148
149
|
#private;
|
|
149
150
|
constructor(deps: {
|
|
@@ -152,6 +153,8 @@ declare class SkillVersionStore {
|
|
|
152
153
|
now?: () => string;
|
|
153
154
|
createId?: () => string;
|
|
154
155
|
audit?: AuditLog;
|
|
156
|
+
/** 每技能保留的版本/归档上限(默认 5,超出清理最旧) */
|
|
157
|
+
maxArchivesPerSkill?: number;
|
|
155
158
|
});
|
|
156
159
|
add(skillName: string, input: {
|
|
157
160
|
reason: string;
|
|
@@ -209,7 +212,12 @@ interface AuditChainVerification {
|
|
|
209
212
|
brokenAt?: number;
|
|
210
213
|
reason?: string;
|
|
211
214
|
}
|
|
212
|
-
/**
|
|
215
|
+
/**
|
|
216
|
+
* JSONL 追加式审计日志(<managedRoot>/.webskill/audit.jsonl):
|
|
217
|
+
* 真追加(fs.appendText,不整读改写)+ lastHash 内存缓存(同实例不重读)。
|
|
218
|
+
* 语义:**tamper-evident, not tamper-proof**——verifyChain 能检出篡改/删除/换序,
|
|
219
|
+
* 但不阻止有写权限者直接改写文件;更强保证需要签名信任体系(未排期)。
|
|
220
|
+
*/
|
|
213
221
|
declare class FsAuditLog implements AuditLog {
|
|
214
222
|
#private;
|
|
215
223
|
constructor(deps: {
|
package/dist/governance.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { S as WebSkillRuntime } from "./dist-
|
|
3
|
-
import { i as NodeFS, s as ProcessSandboxExecutor, u as exportArchive } from "./dist-
|
|
1
|
+
import { C as messageOf, M as unzipWithLimits, N as validateSkills, f as assertSafePathSegment, j as resolveInsideRoot, u as WebSkillError, x as isValidSkillName } from "./dist-BQzncxXg.js";
|
|
2
|
+
import { S as WebSkillRuntime } from "./dist-B77plHjw.js";
|
|
3
|
+
import { i as NodeFS, s as ProcessSandboxExecutor, u as exportArchive } from "./dist-Chk8iB-E.js";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { mkdtemp } from "node:fs/promises";
|
|
@@ -242,7 +242,6 @@ var CompositeApprovalPolicy = class {
|
|
|
242
242
|
return this.#fallback.evaluate(candidate);
|
|
243
243
|
}
|
|
244
244
|
};
|
|
245
|
-
const messageOf$1 = (e) => e instanceof Error ? e.message : String(e);
|
|
246
245
|
/** 审批工作流:review(UiBridge confirm 真实接线)/ publish(校验→安装→版本→审计) */
|
|
247
246
|
var ApprovalWorkflow = class {
|
|
248
247
|
#policy;
|
|
@@ -331,7 +330,7 @@ var ApprovalWorkflow = class {
|
|
|
331
330
|
return manifest;
|
|
332
331
|
} catch (e) {
|
|
333
332
|
if (e instanceof WebSkillError) throw e;
|
|
334
|
-
throw new WebSkillError("GOVERNANCE_FAILED", `Failed to publish candidate "${candidateId}": ${messageOf
|
|
333
|
+
throw new WebSkillError("GOVERNANCE_FAILED", `Failed to publish candidate "${candidateId}": ${messageOf(e)}`, e);
|
|
335
334
|
} finally {
|
|
336
335
|
try {
|
|
337
336
|
await this.#fs.remove(stagingRoot, { recursive: true });
|
|
@@ -382,7 +381,7 @@ var ApprovalWorkflow = class {
|
|
|
382
381
|
return manifest;
|
|
383
382
|
} catch (e) {
|
|
384
383
|
if (e instanceof WebSkillError) throw e;
|
|
385
|
-
throw new WebSkillError("GOVERNANCE_FAILED", `Failed to roll back "${skillName}" to version "${versionId}": ${messageOf
|
|
384
|
+
throw new WebSkillError("GOVERNANCE_FAILED", `Failed to roll back "${skillName}" to version "${versionId}": ${messageOf(e)}`, e);
|
|
386
385
|
} finally {
|
|
387
386
|
try {
|
|
388
387
|
await this.#fs.remove(stagingRoot, { recursive: true });
|
|
@@ -404,29 +403,42 @@ function canonical(event) {
|
|
|
404
403
|
prevHash: event.prevHash
|
|
405
404
|
});
|
|
406
405
|
}
|
|
407
|
-
/**
|
|
406
|
+
/**
|
|
407
|
+
* JSONL 追加式审计日志(<managedRoot>/.webskill/audit.jsonl):
|
|
408
|
+
* 真追加(fs.appendText,不整读改写)+ lastHash 内存缓存(同实例不重读)。
|
|
409
|
+
* 语义:**tamper-evident, not tamper-proof**——verifyChain 能检出篡改/删除/换序,
|
|
410
|
+
* 但不阻止有写权限者直接改写文件;更强保证需要签名信任体系(未排期)。
|
|
411
|
+
*/
|
|
408
412
|
var FsAuditLog = class {
|
|
409
413
|
#root;
|
|
410
414
|
#fs;
|
|
411
415
|
#now;
|
|
412
416
|
#createId;
|
|
417
|
+
/** lastHash 内存缓存(同实例内追加不重读文件) */
|
|
418
|
+
#lastHash;
|
|
419
|
+
#lastHashSeeded = false;
|
|
413
420
|
constructor(deps) {
|
|
414
421
|
this.#root = deps.root.replace(/\/+$/, "");
|
|
415
422
|
this.#fs = deps.fs;
|
|
416
423
|
this.#now = deps.now;
|
|
417
424
|
this.#createId = deps.createId;
|
|
418
425
|
}
|
|
419
|
-
|
|
426
|
+
/** 首追加时播种 lastHash(读一次尾部;尾行损坏 → 抛错,不静默重开链) */
|
|
427
|
+
async #seedLastHash() {
|
|
428
|
+
this.#lastHashSeeded = true;
|
|
420
429
|
const path = fileOf$1(this.#root);
|
|
421
|
-
|
|
422
|
-
const lines =
|
|
423
|
-
|
|
424
|
-
|
|
430
|
+
if (!await this.#fs.exists(path)) return "GENESIS";
|
|
431
|
+
const lines = (await this.#fs.readText(path)).split("\n").filter((l) => l.trim() !== "");
|
|
432
|
+
if (lines.length === 0) return "GENESIS";
|
|
433
|
+
try {
|
|
425
434
|
const last = JSON.parse(lines.at(-1));
|
|
426
|
-
|
|
427
|
-
} catch {
|
|
428
|
-
|
|
435
|
+
return last.hash ?? sha256Hex(canonical(last));
|
|
436
|
+
} catch (e) {
|
|
437
|
+
throw new WebSkillError("GOVERNANCE_FAILED", `Audit log tail line at ${path} is corrupted; refusing to append (the chain must not silently restart)`, e);
|
|
429
438
|
}
|
|
439
|
+
}
|
|
440
|
+
async append(event) {
|
|
441
|
+
const prevHash = this.#lastHashSeeded ? this.#lastHash : await this.#seedLastHash();
|
|
430
442
|
const full = {
|
|
431
443
|
id: event.id ?? this.#createId?.() ?? `audit-${Math.random().toString(36).slice(2, 10)}`,
|
|
432
444
|
ts: event.ts ?? this.#now?.() ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -437,8 +449,8 @@ var FsAuditLog = class {
|
|
|
437
449
|
prevHash
|
|
438
450
|
};
|
|
439
451
|
full.hash = sha256Hex(canonical(full));
|
|
440
|
-
|
|
441
|
-
|
|
452
|
+
await this.#fs.appendText(fileOf$1(this.#root), `${JSON.stringify(full)}\n`);
|
|
453
|
+
this.#lastHash = full.hash;
|
|
442
454
|
return full;
|
|
443
455
|
}
|
|
444
456
|
async query(filter) {
|
|
@@ -448,7 +460,12 @@ var FsAuditLog = class {
|
|
|
448
460
|
const events = [];
|
|
449
461
|
for (const line of raw.split("\n")) {
|
|
450
462
|
if (line.trim() === "") continue;
|
|
451
|
-
|
|
463
|
+
let event;
|
|
464
|
+
try {
|
|
465
|
+
event = JSON.parse(line);
|
|
466
|
+
} catch {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
452
469
|
if (filter.target !== void 0 && event.target !== filter.target) continue;
|
|
453
470
|
if (filter.type !== void 0 && event.type !== filter.type) continue;
|
|
454
471
|
if (filter.since !== void 0 && event.ts < filter.since) continue;
|
|
@@ -493,19 +510,22 @@ const dirOf = (root, skillName) => {
|
|
|
493
510
|
assertSafePathSegment(skillName, "skill name");
|
|
494
511
|
return `${root}/.webskill/versions/${skillName}`;
|
|
495
512
|
};
|
|
496
|
-
/** 版本存储:manifest 快照 + parentVersionId 链;回滚 =
|
|
513
|
+
/** 版本存储:manifest 快照 + parentVersionId 链;回滚 = 追加新版本(谱系不断)。
|
|
514
|
+
* 保留策略:maxArchivesPerSkill(默认 5)超出时清理最旧版本(json + zip 归档一并删除)。 */
|
|
497
515
|
var SkillVersionStore = class {
|
|
498
516
|
#root;
|
|
499
517
|
#fs;
|
|
500
518
|
#now;
|
|
501
519
|
#createId;
|
|
502
520
|
#audit;
|
|
521
|
+
#maxArchives;
|
|
503
522
|
constructor(deps) {
|
|
504
523
|
this.#root = deps.root.replace(/\/+$/, "");
|
|
505
524
|
this.#fs = deps.fs;
|
|
506
525
|
this.#now = deps.now;
|
|
507
526
|
this.#createId = deps.createId;
|
|
508
527
|
this.#audit = deps.audit;
|
|
528
|
+
this.#maxArchives = deps.maxArchivesPerSkill ?? 5;
|
|
509
529
|
}
|
|
510
530
|
async add(skillName, input) {
|
|
511
531
|
const existing = await this.list(skillName);
|
|
@@ -523,8 +543,19 @@ var SkillVersionStore = class {
|
|
|
523
543
|
version.archivePath = archivePath;
|
|
524
544
|
}
|
|
525
545
|
await this.#fs.writeText(`${dirOf(this.#root, skillName)}/${version.versionId}.json`, JSON.stringify(version, null, 2));
|
|
546
|
+
await this.#prune(skillName);
|
|
526
547
|
return version;
|
|
527
548
|
}
|
|
549
|
+
/** 保留策略:超出 maxArchivesPerSkill 时按 createdAt 清理最旧版本(json + zip) */
|
|
550
|
+
async #prune(skillName) {
|
|
551
|
+
const versions = await this.list(skillName);
|
|
552
|
+
const excess = versions.length - this.#maxArchives;
|
|
553
|
+
if (excess <= 0) return;
|
|
554
|
+
for (const old of versions.slice(0, excess)) {
|
|
555
|
+
await this.#fs.remove(`${dirOf(this.#root, skillName)}/${old.versionId}.json`);
|
|
556
|
+
if (old.archivePath && await this.#fs.exists(old.archivePath)) await this.#fs.remove(old.archivePath);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
528
559
|
/** 读取版本归档字节(applyRollback 用;未捕获归档的旧版本 → GOVERNANCE_FAILED) */
|
|
529
560
|
async readArchive(skillName, versionId) {
|
|
530
561
|
const version = await this.get(skillName, versionId);
|
|
@@ -786,7 +817,6 @@ var SkillStatePolicy = class {
|
|
|
786
817
|
});
|
|
787
818
|
}
|
|
788
819
|
};
|
|
789
|
-
const messageOf = (e) => e instanceof Error ? e.message : String(e);
|
|
790
820
|
function matchExpected(expected, output, run) {
|
|
791
821
|
if (expected === void 0) return run.status === "completed";
|
|
792
822
|
if (typeof expected === "string") return output.includes(expected);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
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-
|
|
1
|
+
import { B as SkillManifest, C as FileStat, G as SkillsLockfile, L as SkillInstallSource, T as JsonSchema, _ as RenderResultRequest, o as InteractionRequest, q as VerifyResult, s as InteractionResponse, v as UiBridge, w as FileSystemProvider, y as ArchiveLimits } from "./types-CKm5G_eQ-krKWW8WV.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-DfINBEOy.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
|
|
@@ -14,8 +14,11 @@ declare class NodeFS implements FileSystemProvider {
|
|
|
14
14
|
constructor(deps?: {
|
|
15
15
|
root?: string;
|
|
16
16
|
});
|
|
17
|
+
/** root 模式:全部方法(read/write/exists/stat/list/mkdir/remove/rename)目标 realpath 必须落在 root realpath 前缀内 */
|
|
18
|
+
withRoot(root: string): NodeFS;
|
|
17
19
|
readText(p: string): Promise<string>;
|
|
18
20
|
writeText(p: string, content: string): Promise<void>;
|
|
21
|
+
appendText(p: string, content: string): Promise<void>;
|
|
19
22
|
readBinary(p: string): Promise<Uint8Array>;
|
|
20
23
|
writeBinary(p: string, content: Uint8Array): Promise<void>;
|
|
21
24
|
exists(p: string): Promise<boolean>;
|
|
@@ -103,6 +106,8 @@ interface ProcessSandboxOptions {
|
|
|
103
106
|
uiBridge?: UiBridge;
|
|
104
107
|
/** 授权粒度:默认 'once-per-run' */
|
|
105
108
|
approvalScope?: ApprovalScope;
|
|
109
|
+
/** 池维护告警出口(recycle 重生失败等;默认 console.warn) */
|
|
110
|
+
onWarning?: (message: string) => void;
|
|
106
111
|
}
|
|
107
112
|
/**
|
|
108
113
|
* child_process.fork + --permission 进程沙箱(真实进程隔离)。
|
|
@@ -117,7 +122,7 @@ declare class ProcessSandboxExecutor implements ScriptExecutor {
|
|
|
117
122
|
#private;
|
|
118
123
|
constructor(fs: FileSystemProvider, options?: ProcessSandboxOptions);
|
|
119
124
|
get poolSize(): number;
|
|
120
|
-
/**
|
|
125
|
+
/** 池全部子进程销毁(测试收尾/进程退出前调用);排队中的 acquire 一律 reject(不悬挂) */
|
|
121
126
|
dispose(): Promise<void>;
|
|
122
127
|
loadDefinition(skillRoot: string, scriptName: string): Promise<ToolDefinition>;
|
|
123
128
|
execute(input: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as SkillManifest, F as SkillDiscovery, I as SkillDocument, K as ValidationReport, L as SkillInstallSource, N as SkillCatalog, P as SkillCatalogEntry, S as DiscoveryResult, T as JsonSchema, Y as WebSkillErrorCode, _ as RenderResultRequest, a as InteractionPolicy, c as LlmClient, d as LlmResponse, f as LlmStreamEvent, g as RenderBlock, h as MemoryStore, i as FormField, l as LlmCompleteInput, m as LlmToolSpec, n as ArtifactStore, o as InteractionRequest, r as ChartSpec, t as Artifact, u as LlmMessage, v as UiBridge, w as FileSystemProvider } from "./types-CKm5G_eQ-krKWW8WV.js";
|
|
2
2
|
//#region ../runtime/dist/index.d.ts
|
|
3
3
|
//#region src/llm/openAiCompatibleClient.d.ts
|
|
4
4
|
interface OpenAiCompatibleClientConfig {
|
|
@@ -33,6 +33,11 @@ interface AnthropicClientConfig {
|
|
|
33
33
|
requestTimeoutMs?: number;
|
|
34
34
|
/** max_tokens(Messages API 必填),默认 4096 */
|
|
35
35
|
maxTokens?: number;
|
|
36
|
+
/**
|
|
37
|
+
* 浏览器直调 opt-in:true 时发送 anthropic-dangerous-direct-browser-access 头
|
|
38
|
+
* (默认 false 不发——仅在明确运行于浏览器且无服务端代理时开启)
|
|
39
|
+
*/
|
|
40
|
+
dangerouslyAllowDirectBrowserAccess?: boolean;
|
|
36
41
|
}
|
|
37
42
|
/** Anthropic Messages API 客户端(零依赖 fetch;Node/浏览器通用) */
|
|
38
43
|
declare class AnthropicClient implements LlmClient {
|
|
@@ -265,6 +270,8 @@ interface AgentLoopConfig {
|
|
|
265
270
|
renderResult?: 'off';
|
|
266
271
|
/** 工具结果回喂上限(字节,默认 100_000;超长头尾保留 + 完整内容落 artifact) */
|
|
267
272
|
toolResultMaxBytes?: number;
|
|
273
|
+
/** session paramHistory 保留条数上限(默认 50,超出裁最旧) */
|
|
274
|
+
paramHistoryLimit?: number;
|
|
268
275
|
}
|
|
269
276
|
/** 技能状态拦截 port(治理装配;无注入默认全放行) */
|
|
270
277
|
interface SkillStateGuard {
|
|
@@ -374,6 +381,7 @@ type LifecycleListener = (event: LifecycleEvent) => void;
|
|
|
374
381
|
/** 生命周期事件总线:只读观测,支持按阶段或通配订阅 */
|
|
375
382
|
declare class EventBus {
|
|
376
383
|
#private;
|
|
384
|
+
constructor(onListenerError?: (error: unknown, event: LifecycleEvent) => void);
|
|
377
385
|
/** 返回取消订阅函数 */
|
|
378
386
|
on(phase: RuntimePhase | '*', listener: LifecycleListener): () => void;
|
|
379
387
|
/** 当前订阅者数量(流式 delta 零订阅零开销判断用) */
|
|
@@ -431,6 +439,8 @@ declare class FsMemoryStore implements MemoryStore {
|
|
|
431
439
|
declare class SerializingMemoryStore implements MemoryStore {
|
|
432
440
|
#private;
|
|
433
441
|
constructor(inner: MemoryStore);
|
|
442
|
+
/** 当前链上的 scope 数(监控/测试用;空闲时应回落到 0) */
|
|
443
|
+
get trackedScopeCount(): number;
|
|
434
444
|
get(scope: string, key: string): Promise<unknown>;
|
|
435
445
|
set(scope: string, key: string, value: unknown): Promise<void>;
|
|
436
446
|
delete(scope: string, key: string): Promise<void>;
|
|
@@ -540,6 +550,18 @@ declare function isNetworkAllowed(policy: NetworkPolicy, url: string): boolean;
|
|
|
540
550
|
/** 阻断 trace 用的脱敏 host(解析失败返回占位,不记录完整 URL) */
|
|
541
551
|
declare function networkUrlHost(url: string): string;
|
|
542
552
|
//#endregion
|
|
553
|
+
//#region src/sandbox/errorCodes.d.ts
|
|
554
|
+
/**
|
|
555
|
+
* 错误码白名单归一:沙箱/桥消息里出现的非白名单码(DOMException 数值码、
|
|
556
|
+
* Node 任意 ERR_* 码等)一律归为 TOOL_EXECUTION_FAILED。
|
|
557
|
+
*/
|
|
558
|
+
declare function normalizeErrorCode(code: unknown): WebSkillErrorCode;
|
|
559
|
+
/** 归一化后的 (code, message):非白名单码保留在 message 尾部([original code: X]) */
|
|
560
|
+
declare function normalizeToolError(code: unknown, message: string): {
|
|
561
|
+
code: WebSkillErrorCode;
|
|
562
|
+
message: string;
|
|
563
|
+
};
|
|
564
|
+
//#endregion
|
|
543
565
|
//#region src/sandbox/approval.d.ts
|
|
544
566
|
/** 桥消息对应的三类能力 */
|
|
545
567
|
type BridgeCapability = 'readReference' | 'writeArtifact' | 'confirm';
|
|
@@ -632,6 +654,8 @@ interface RunSnapshot {
|
|
|
632
654
|
renderBlocks?: RenderBlock[];
|
|
633
655
|
/** 交互 id 序号(resume 后续算,避免 id 冲突;旧快照缺省从 0 起) */
|
|
634
656
|
interactionSeq?: number;
|
|
657
|
+
/** 已累计的交互等待 ms(resume 后续算,保持 totalTimeout 排除交互等待的语义;旧快照缺省为 0) */
|
|
658
|
+
pausedMs?: number;
|
|
635
659
|
/** 进入 interrupted 时计算的过期时间 */
|
|
636
660
|
interactionExpiresAt: string;
|
|
637
661
|
config: {
|
|
@@ -790,9 +814,12 @@ declare class WebSkillRuntime {
|
|
|
790
814
|
/**
|
|
791
815
|
* 多会话:session 对象的 run(prompt) 跨 run 延续消息历史(同一 session 对话上下文连续)。
|
|
792
816
|
* 既有 runtime.run(prompt) 保持无状态单次语义不变。
|
|
817
|
+
* 同 handle 并发 run 经 per-handle 队列串行化(防历史 last-writer-wins 丢轮次);
|
|
818
|
+
* maxHistoryMessages(默认 100)超出时滚动裁剪中段(保留首尾;边界对齐 tool 契约)。
|
|
793
819
|
*/
|
|
794
820
|
createSession(options?: {
|
|
795
821
|
sessionId?: string;
|
|
822
|
+
maxHistoryMessages?: number;
|
|
796
823
|
}): RuntimeSessionHandle;
|
|
797
824
|
discover(): Promise<DiscoveryResult>;
|
|
798
825
|
run(userPrompt: string, options?: {
|
|
@@ -810,4 +837,4 @@ declare class WebSkillRuntime {
|
|
|
810
837
|
resumeRun(runId: string): Promise<RunResult>;
|
|
811
838
|
}
|
|
812
839
|
//#endregion
|
|
813
|
-
export { SerializingMemoryStore as $, LifecycleHook as A, RUN_SNAPSHOT_SCHEMA_VERSION as B, FullDisclosureRouter as C,
|
|
840
|
+
export { SerializingMemoryStore as $, LifecycleHook as A, toVercelToolSpecs as At, RUN_SNAPSHOT_SCHEMA_VERSION as B, FullDisclosureRouter as C, normalizeErrorCode as Ct, HookRunnerOptions as D, resolveToolName as Dt, HookRunner as E, parseBridgeRequest as Et, OpenAiCompatibleClientConfig as F, RunTerminationReason as G, RunResult as H, ProgressiveRouter as I, RuntimeSession as J, RuntimePhase as K, READ_SKILL_FILE_INPUT_SCHEMA as L, LifecycleListener as M, NetworkPolicy as N, InstalledSkillManifest as O, schemaToForm as Ot, OpenAiCompatibleClient as P, ScriptExecutor as Q, READ_SKILL_FILE_TOOL as R, FsRunSnapshotStore as S, networkUrlHost as St, GoogleGenAiClientConfig as T, normalizeToolError as Tt, RunSnapshot as U, RouteResult as V, RunSnapshotStore as W, SchemaInferer as X, RuntimeSessionHandle as Y, ScriptExecutionContext as Z, EventBus as _, extractChartSpec as _t, AgentLoopConfig as a, TraceClock as at, FsArtifactStore as b, isNetworkAllowed as bt, AnthropicClientConfig as c, TraceRecorder as ct, BridgeCapabilities as d, WebSkillRuntime as dt, SkillRouter as et, BridgeCapability as f, WebSkillRuntimeDeps as ft, CapabilityMode as g, createWebSkillApi as gt, CapabilityApproval as h, createScriptContext as ht, AgentLoop as i, ToolResult as it, LifecycleHookContext as j, LifecycleEvent as k, toLlmToolSpec as kt, ApprovalDecision as l, VercelToolSpec as lt, BridgeResponse as m, buildRenderResult as mt, ASK_USER_TOOL as n, ToolDefinition as nt, AgentLoopDeps as o, TraceEvent as ot, BridgeRequest as p, bridgeError as pt, RuntimeRun as q, ASK_USER_TOOL_NAME as r, ToolResolution as rt, AnthropicClient as s, TraceEventType as st, ASK_USER_INPUT_SCHEMA as t, SkillStateGuard as tt, ApprovalScope as u, WebSkillApi as ut, ExternalSkillProvider as v, fromVercelResult as vt, GoogleGenAiClient as w, normalizeToolContent as wt, FsMemoryStore as x, mergeCatalogEntries as xt, ExternalToolSource as y, fromVercelStreamPart as yt, READ_SKILL_FILE_TOOL_NAME as z };
|