@webskill/sdk 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/browser.d.ts +219 -0
- package/dist/browser.js +856 -0
- package/dist/dist-9fczg9Pa.js +1122 -0
- package/dist/dist-B_ldNWwT.js +1975 -0
- package/dist/dist-RcqvzAGF.js +15382 -0
- package/dist/governance.d.ts +440 -0
- package/dist/governance.js +844 -0
- package/dist/index-88KNOnbr.d.ts +955 -0
- package/dist/index-Bq-bTWh3.d.ts +219 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/mcp.d.ts +237 -0
- package/dist/mcp.js +513 -0
- package/dist/node.d.ts +3 -0
- package/dist/node.js +4 -0
- package/dist/ui-react.d.ts +40 -0
- package/dist/ui-react.js +239 -0
- package/dist/ui-vue.d.ts +72 -0
- package/dist/ui-vue.js +196 -0
- package/dist/ui.d.ts +171 -0
- package/dist/ui.js +4 -0
- package/package.json +91 -0
package/dist/ui-react.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { _ as interactionToFormModel, f as collectValues, x as shapeInteractionValue, y as renderMiniMarkdown } from "./dist-RcqvzAGF.js";
|
|
2
|
+
import { useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
//#region ../ui-react/dist/index.js
|
|
6
|
+
/**
|
|
7
|
+
* React 桥接状态:UiBridge 实现 + useSyncExternalStore 兼容订阅。
|
|
8
|
+
* 应用创建实例传给 Runtime 与组件:<InteractionForm bridge={bridge} />。
|
|
9
|
+
*/
|
|
10
|
+
var ReactBridgeState = class {
|
|
11
|
+
pending = null;
|
|
12
|
+
latestResult = null;
|
|
13
|
+
streamingText = "";
|
|
14
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
15
|
+
#resolvers = /* @__PURE__ */ new Map();
|
|
16
|
+
/** React useSyncExternalStore 兼容订阅 */
|
|
17
|
+
subscribe = (listener) => {
|
|
18
|
+
this.#listeners.add(listener);
|
|
19
|
+
return () => this.#listeners.delete(listener);
|
|
20
|
+
};
|
|
21
|
+
#emit() {
|
|
22
|
+
for (const listener of [...this.#listeners]) listener();
|
|
23
|
+
}
|
|
24
|
+
request(input) {
|
|
25
|
+
this.pending = input;
|
|
26
|
+
this.#emit();
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
this.#resolvers.set(input.id, resolve);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** 组件提交/取消回调 */
|
|
32
|
+
resolve(response) {
|
|
33
|
+
const resolver = this.#resolvers.get(response.id);
|
|
34
|
+
if (!resolver) return;
|
|
35
|
+
this.#resolvers.delete(response.id);
|
|
36
|
+
this.pending = null;
|
|
37
|
+
this.#emit();
|
|
38
|
+
resolver(response);
|
|
39
|
+
}
|
|
40
|
+
async renderResult(input) {
|
|
41
|
+
this.latestResult = input;
|
|
42
|
+
this.#emit();
|
|
43
|
+
}
|
|
44
|
+
async onTextDelta(_runId, delta) {
|
|
45
|
+
this.streamingText += delta;
|
|
46
|
+
this.#emit();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
function Control({ control }) {
|
|
50
|
+
const label = /* @__PURE__ */ jsxs("label", {
|
|
51
|
+
className: "webskill-form__label",
|
|
52
|
+
children: [control.label, control.required ? " *" : ""]
|
|
53
|
+
});
|
|
54
|
+
const description = control.description ? /* @__PURE__ */ jsx("div", {
|
|
55
|
+
className: "webskill-form__description",
|
|
56
|
+
children: control.description
|
|
57
|
+
}) : null;
|
|
58
|
+
switch (control.control) {
|
|
59
|
+
case "boolean": return /* @__PURE__ */ jsxs("div", {
|
|
60
|
+
className: "webskill-form__control",
|
|
61
|
+
"data-control-name": control.name,
|
|
62
|
+
children: [
|
|
63
|
+
label,
|
|
64
|
+
description,
|
|
65
|
+
/* @__PURE__ */ jsx("input", {
|
|
66
|
+
type: "checkbox",
|
|
67
|
+
className: "webskill-form__input",
|
|
68
|
+
"data-webskill-control": control.name,
|
|
69
|
+
defaultChecked: control.defaultValue === true
|
|
70
|
+
})
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
case "select": return /* @__PURE__ */ jsxs("div", {
|
|
74
|
+
className: "webskill-form__control",
|
|
75
|
+
"data-control-name": control.name,
|
|
76
|
+
children: [
|
|
77
|
+
label,
|
|
78
|
+
description,
|
|
79
|
+
/* @__PURE__ */ jsx("select", {
|
|
80
|
+
className: "webskill-form__input",
|
|
81
|
+
"data-webskill-control": control.name,
|
|
82
|
+
children: (control.options ?? []).map((option, i) => /* @__PURE__ */ jsx("option", {
|
|
83
|
+
value: JSON.stringify(option.value),
|
|
84
|
+
...control.defaultValue !== void 0 && JSON.stringify(control.defaultValue) === JSON.stringify(option.value) ? { selected: true } : {},
|
|
85
|
+
children: option.label
|
|
86
|
+
}, i))
|
|
87
|
+
})
|
|
88
|
+
]
|
|
89
|
+
});
|
|
90
|
+
case "textarea": return /* @__PURE__ */ jsxs("div", {
|
|
91
|
+
className: "webskill-form__control",
|
|
92
|
+
"data-control-name": control.name,
|
|
93
|
+
children: [
|
|
94
|
+
label,
|
|
95
|
+
description,
|
|
96
|
+
/* @__PURE__ */ jsx("textarea", {
|
|
97
|
+
className: "webskill-form__input",
|
|
98
|
+
"data-webskill-control": control.name,
|
|
99
|
+
defaultValue: control.defaultValue !== void 0 ? String(control.defaultValue) : ""
|
|
100
|
+
})
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
default: return /* @__PURE__ */ jsxs("div", {
|
|
104
|
+
className: "webskill-form__control",
|
|
105
|
+
"data-control-name": control.name,
|
|
106
|
+
children: [
|
|
107
|
+
label,
|
|
108
|
+
description,
|
|
109
|
+
/* @__PURE__ */ jsx("input", {
|
|
110
|
+
type: control.control === "number" ? "number" : "text",
|
|
111
|
+
className: "webskill-form__input",
|
|
112
|
+
"data-webskill-control": control.name,
|
|
113
|
+
defaultValue: control.defaultValue !== void 0 ? String(control.defaultValue) : ""
|
|
114
|
+
})
|
|
115
|
+
]
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** 订阅 bridge.pending 渲染交互表单;提交/取消经 bridge.resolve 回传 */
|
|
120
|
+
function InteractionForm({ bridge }) {
|
|
121
|
+
const pending = useSyncExternalStore(bridge.subscribe, () => bridge.pending);
|
|
122
|
+
const formRef = useRef(null);
|
|
123
|
+
const [invalid, setInvalid] = useState([]);
|
|
124
|
+
if (!pending) return null;
|
|
125
|
+
const model = interactionToFormModel(pending);
|
|
126
|
+
return /* @__PURE__ */ jsxs("form", {
|
|
127
|
+
ref: formRef,
|
|
128
|
+
className: "webskill-form",
|
|
129
|
+
noValidate: true,
|
|
130
|
+
onSubmit: (event) => {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
if (!formRef.current) return;
|
|
133
|
+
const { values, missingRequired } = collectValues(model.controls, formRef.current);
|
|
134
|
+
if (missingRequired.length > 0) {
|
|
135
|
+
setInvalid(missingRequired);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
bridge.resolve({
|
|
139
|
+
id: pending.id,
|
|
140
|
+
value: shapeInteractionValue(model, values)
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
children: [
|
|
144
|
+
model.title ? /* @__PURE__ */ jsx("div", {
|
|
145
|
+
className: "webskill-form__title",
|
|
146
|
+
children: model.title
|
|
147
|
+
}) : null,
|
|
148
|
+
model.message && model.kind === "form" ? /* @__PURE__ */ jsx("p", {
|
|
149
|
+
className: "webskill-form__message",
|
|
150
|
+
children: model.message
|
|
151
|
+
}) : null,
|
|
152
|
+
model.controls.map((control) => /* @__PURE__ */ jsxs("div", {
|
|
153
|
+
className: invalid.includes(control.name) ? "webskill-form__control--invalid" : void 0,
|
|
154
|
+
children: [/* @__PURE__ */ jsx(Control, { control }), invalid.includes(control.name) ? /* @__PURE__ */ jsx("div", {
|
|
155
|
+
className: "webskill-form__error",
|
|
156
|
+
children: "This field is required"
|
|
157
|
+
}) : null]
|
|
158
|
+
}, control.name)),
|
|
159
|
+
/* @__PURE__ */ jsxs("div", {
|
|
160
|
+
className: "webskill-form__actions",
|
|
161
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
162
|
+
type: "submit",
|
|
163
|
+
className: "webskill-form__button webskill-form__button--primary",
|
|
164
|
+
children: model.submitLabel
|
|
165
|
+
}), model.cancelLabel ? /* @__PURE__ */ jsx("button", {
|
|
166
|
+
type: "button",
|
|
167
|
+
className: "webskill-form__button webskill-form__button--ghost",
|
|
168
|
+
"data-webskill-cancel": true,
|
|
169
|
+
onClick: () => bridge.resolve({
|
|
170
|
+
id: pending.id,
|
|
171
|
+
cancelled: true
|
|
172
|
+
}),
|
|
173
|
+
children: model.cancelLabel
|
|
174
|
+
}) : null]
|
|
175
|
+
})
|
|
176
|
+
]
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function MarkdownBlock({ text }) {
|
|
180
|
+
const ref = useRef(null);
|
|
181
|
+
useLayoutEffect(() => {
|
|
182
|
+
const el = ref.current;
|
|
183
|
+
if (!el) return;
|
|
184
|
+
el.replaceChildren(renderMiniMarkdown(text, el.ownerDocument));
|
|
185
|
+
}, [text]);
|
|
186
|
+
return /* @__PURE__ */ jsx("div", { ref });
|
|
187
|
+
}
|
|
188
|
+
function Block({ block }) {
|
|
189
|
+
switch (block.type) {
|
|
190
|
+
case "markdown": return /* @__PURE__ */ jsx(MarkdownBlock, { text: block.text });
|
|
191
|
+
case "json": return /* @__PURE__ */ jsx("pre", {
|
|
192
|
+
className: "webskill-result__json",
|
|
193
|
+
children: JSON.stringify(block.data, null, 2)
|
|
194
|
+
});
|
|
195
|
+
case "table": return /* @__PURE__ */ jsxs("table", { children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: block.columns.map((column) => /* @__PURE__ */ jsx("th", { children: column }, column)) }) }), /* @__PURE__ */ jsx("tbody", { children: block.rows.map((row, i) => /* @__PURE__ */ jsx("tr", { children: row.map((cell, j) => /* @__PURE__ */ jsx("td", { children: typeof cell === "object" ? JSON.stringify(cell) : String(cell ?? "") }, j)) }, i)) })] });
|
|
196
|
+
case "image": return /* @__PURE__ */ jsx("img", {
|
|
197
|
+
src: block.url,
|
|
198
|
+
alt: block.alt ?? ""
|
|
199
|
+
});
|
|
200
|
+
case "file": {
|
|
201
|
+
const meta = [block.mimeType, block.size !== void 0 ? `${block.size} bytes` : void 0].filter(Boolean).join(", ");
|
|
202
|
+
return /* @__PURE__ */ jsx("div", {
|
|
203
|
+
className: "webskill-result__file",
|
|
204
|
+
children: meta ? `${block.path} (${meta})` : block.path
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/** 订阅 bridge.latestResult 渲染五种 RenderBlock */
|
|
210
|
+
function ResultBlocks({ bridge }) {
|
|
211
|
+
const result = useSyncExternalStore(bridge.subscribe, () => bridge.latestResult);
|
|
212
|
+
if (!result) return null;
|
|
213
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
214
|
+
className: "webskill-result",
|
|
215
|
+
children: [
|
|
216
|
+
result.title ? /* @__PURE__ */ jsx("div", {
|
|
217
|
+
className: "webskill-form__title",
|
|
218
|
+
children: result.title
|
|
219
|
+
}) : null,
|
|
220
|
+
result.summary ? /* @__PURE__ */ jsx("div", {
|
|
221
|
+
className: "webskill-result__summary",
|
|
222
|
+
children: result.summary
|
|
223
|
+
}) : null,
|
|
224
|
+
result.blocks.map((block, i) => /* @__PURE__ */ jsx(Block, { block }, i))
|
|
225
|
+
]
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/** 订阅 bridge.streamingText 增量渲染流式文本 */
|
|
229
|
+
function StreamingText({ bridge }) {
|
|
230
|
+
const text = useSyncExternalStore(bridge.subscribe, () => bridge.streamingText);
|
|
231
|
+
if (text === "") return null;
|
|
232
|
+
return /* @__PURE__ */ jsx("div", {
|
|
233
|
+
className: "webskill-streaming-text",
|
|
234
|
+
children: text
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
//#endregion
|
|
239
|
+
export { InteractionForm, ReactBridgeState, ResultBlocks, StreamingText };
|
package/dist/ui-vue.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { C as InteractionRequest, X as RenderResultRequest, pt as UiBridge, w as InteractionResponse } from "./index-88KNOnbr.js";
|
|
2
|
+
import { PropType } from "vue";
|
|
3
|
+
//#region ../ui-vue/dist/index.d.ts
|
|
4
|
+
//#region src/bridgeState.d.ts
|
|
5
|
+
interface VueBridgeSnapshot {
|
|
6
|
+
pending: InteractionRequest | null;
|
|
7
|
+
latestResult: RenderResultRequest | null;
|
|
8
|
+
streamingText: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Vue 桥接状态:UiBridge 实现 + reactive 快照。
|
|
12
|
+
* 应用创建实例传给 Runtime 与组件(组件经 props 接收 bridge)。
|
|
13
|
+
*/
|
|
14
|
+
declare class VueBridgeState implements UiBridge {
|
|
15
|
+
#private;
|
|
16
|
+
readonly state: VueBridgeSnapshot;
|
|
17
|
+
request(input: InteractionRequest): Promise<InteractionResponse>;
|
|
18
|
+
/** 组件提交/取消回调 */
|
|
19
|
+
resolve(response: InteractionResponse): void;
|
|
20
|
+
renderResult(input: RenderResultRequest): Promise<void>;
|
|
21
|
+
onTextDelta(_runId: string, delta: string): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/components/interactionForm.d.ts
|
|
25
|
+
/** 订阅 bridge.state.pending 渲染交互表单(h() 渲染函数,无 SFC) */
|
|
26
|
+
declare const InteractionForm: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
27
|
+
bridge: {
|
|
28
|
+
type: PropType<VueBridgeState>;
|
|
29
|
+
required: true;
|
|
30
|
+
};
|
|
31
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}> | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
34
|
+
bridge: {
|
|
35
|
+
type: PropType<VueBridgeState>;
|
|
36
|
+
required: true;
|
|
37
|
+
};
|
|
38
|
+
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/components/resultBlocks.d.ts
|
|
41
|
+
/** 订阅 bridge.state.latestResult 渲染五种 RenderBlock */
|
|
42
|
+
declare const ResultBlocks: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
43
|
+
bridge: {
|
|
44
|
+
type: PropType<VueBridgeState>;
|
|
45
|
+
required: true;
|
|
46
|
+
};
|
|
47
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
48
|
+
[key: string]: any;
|
|
49
|
+
}> | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
50
|
+
bridge: {
|
|
51
|
+
type: PropType<VueBridgeState>;
|
|
52
|
+
required: true;
|
|
53
|
+
};
|
|
54
|
+
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/components/streamingText.d.ts
|
|
57
|
+
/** 订阅 bridge.state.streamingText 增量渲染流式文本 */
|
|
58
|
+
declare const StreamingText: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
59
|
+
bridge: {
|
|
60
|
+
type: PropType<VueBridgeState>;
|
|
61
|
+
required: true;
|
|
62
|
+
};
|
|
63
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
64
|
+
[key: string]: any;
|
|
65
|
+
}> | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
66
|
+
bridge: {
|
|
67
|
+
type: PropType<VueBridgeState>;
|
|
68
|
+
required: true;
|
|
69
|
+
};
|
|
70
|
+
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
71
|
+
//#endregion
|
|
72
|
+
export { InteractionForm, ResultBlocks, StreamingText, type VueBridgeSnapshot, VueBridgeState };
|
package/dist/ui-vue.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { _ as interactionToFormModel, f as collectValues, x as shapeInteractionValue, y as renderMiniMarkdown } from "./dist-RcqvzAGF.js";
|
|
2
|
+
import { defineComponent, h, reactive, ref } from "vue";
|
|
3
|
+
|
|
4
|
+
//#region ../ui-vue/dist/index.js
|
|
5
|
+
/**
|
|
6
|
+
* Vue 桥接状态:UiBridge 实现 + reactive 快照。
|
|
7
|
+
* 应用创建实例传给 Runtime 与组件(组件经 props 接收 bridge)。
|
|
8
|
+
*/
|
|
9
|
+
var VueBridgeState = class {
|
|
10
|
+
state = reactive({
|
|
11
|
+
pending: null,
|
|
12
|
+
latestResult: null,
|
|
13
|
+
streamingText: ""
|
|
14
|
+
});
|
|
15
|
+
#resolvers = /* @__PURE__ */ new Map();
|
|
16
|
+
request(input) {
|
|
17
|
+
this.state.pending = input;
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
this.#resolvers.set(input.id, resolve);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** 组件提交/取消回调 */
|
|
23
|
+
resolve(response) {
|
|
24
|
+
const resolver = this.#resolvers.get(response.id);
|
|
25
|
+
if (!resolver) return;
|
|
26
|
+
this.#resolvers.delete(response.id);
|
|
27
|
+
this.state.pending = null;
|
|
28
|
+
resolver(response);
|
|
29
|
+
}
|
|
30
|
+
async renderResult(input) {
|
|
31
|
+
this.state.latestResult = input;
|
|
32
|
+
}
|
|
33
|
+
async onTextDelta(_runId, delta) {
|
|
34
|
+
this.state.streamingText += delta;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
function renderControl(control) {
|
|
38
|
+
const label = h("label", { class: "webskill-form__label" }, [control.label, ...control.required ? [" *"] : []]);
|
|
39
|
+
const description = control.description ? h("div", { class: "webskill-form__description" }, control.description) : null;
|
|
40
|
+
let input;
|
|
41
|
+
switch (control.control) {
|
|
42
|
+
case "boolean":
|
|
43
|
+
input = h("input", {
|
|
44
|
+
type: "checkbox",
|
|
45
|
+
class: "webskill-form__input",
|
|
46
|
+
"data-webskill-control": control.name,
|
|
47
|
+
checked: control.defaultValue === true
|
|
48
|
+
});
|
|
49
|
+
break;
|
|
50
|
+
case "select":
|
|
51
|
+
input = h("select", {
|
|
52
|
+
class: "webskill-form__input",
|
|
53
|
+
"data-webskill-control": control.name
|
|
54
|
+
}, (control.options ?? []).map((option) => h("option", {
|
|
55
|
+
value: JSON.stringify(option.value),
|
|
56
|
+
selected: control.defaultValue !== void 0 && JSON.stringify(control.defaultValue) === JSON.stringify(option.value)
|
|
57
|
+
}, option.label)));
|
|
58
|
+
break;
|
|
59
|
+
case "textarea":
|
|
60
|
+
input = h("textarea", {
|
|
61
|
+
class: "webskill-form__input",
|
|
62
|
+
"data-webskill-control": control.name,
|
|
63
|
+
value: control.defaultValue !== void 0 ? String(control.defaultValue) : ""
|
|
64
|
+
});
|
|
65
|
+
break;
|
|
66
|
+
default: input = h("input", {
|
|
67
|
+
type: control.control === "number" ? "number" : "text",
|
|
68
|
+
class: "webskill-form__input",
|
|
69
|
+
"data-webskill-control": control.name,
|
|
70
|
+
value: control.defaultValue !== void 0 ? String(control.defaultValue) : ""
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return h("div", {
|
|
74
|
+
class: "webskill-form__control",
|
|
75
|
+
"data-control-name": control.name
|
|
76
|
+
}, [
|
|
77
|
+
label,
|
|
78
|
+
description,
|
|
79
|
+
input
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
82
|
+
/** 订阅 bridge.state.pending 渲染交互表单(h() 渲染函数,无 SFC) */
|
|
83
|
+
const InteractionForm = defineComponent({
|
|
84
|
+
name: "WebSkillInteractionForm",
|
|
85
|
+
props: { bridge: {
|
|
86
|
+
type: Object,
|
|
87
|
+
required: true
|
|
88
|
+
} },
|
|
89
|
+
setup(props) {
|
|
90
|
+
const formRef = ref(null);
|
|
91
|
+
const invalid = ref([]);
|
|
92
|
+
return () => {
|
|
93
|
+
const pending = props.bridge.state.pending;
|
|
94
|
+
if (!pending) return null;
|
|
95
|
+
const model = interactionToFormModel(pending);
|
|
96
|
+
return h("form", {
|
|
97
|
+
ref: formRef,
|
|
98
|
+
class: "webskill-form",
|
|
99
|
+
novalidate: true,
|
|
100
|
+
onSubmit: (event) => {
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
if (!formRef.value) return;
|
|
103
|
+
const { values, missingRequired } = collectValues(model.controls, formRef.value);
|
|
104
|
+
if (missingRequired.length > 0) {
|
|
105
|
+
invalid.value = missingRequired;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
props.bridge.resolve({
|
|
109
|
+
id: pending.id,
|
|
110
|
+
value: shapeInteractionValue(model, values)
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}, [
|
|
114
|
+
model.title ? h("div", { class: "webskill-form__title" }, model.title) : null,
|
|
115
|
+
model.message && model.kind === "form" ? h("p", { class: "webskill-form__message" }, model.message) : null,
|
|
116
|
+
...model.controls.map((control) => h("div", { key: control.name }, [renderControl(control), invalid.value.includes(control.name) ? h("div", { class: "webskill-form__error" }, "This field is required") : null])),
|
|
117
|
+
h("div", { class: "webskill-form__actions" }, [h("button", {
|
|
118
|
+
type: "submit",
|
|
119
|
+
class: "webskill-form__button webskill-form__button--primary"
|
|
120
|
+
}, model.submitLabel), model.cancelLabel ? h("button", {
|
|
121
|
+
type: "button",
|
|
122
|
+
class: "webskill-form__button webskill-form__button--ghost",
|
|
123
|
+
"data-webskill-cancel": "",
|
|
124
|
+
onClick: () => props.bridge.resolve({
|
|
125
|
+
id: pending.id,
|
|
126
|
+
cancelled: true
|
|
127
|
+
})
|
|
128
|
+
}, model.cancelLabel) : null])
|
|
129
|
+
]);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
function renderBlock(block, key) {
|
|
134
|
+
switch (block.type) {
|
|
135
|
+
case "markdown": return h("div", {
|
|
136
|
+
key,
|
|
137
|
+
onVnodeMounted: (vnode) => {
|
|
138
|
+
const el = vnode.el;
|
|
139
|
+
if (el) el.replaceChildren(renderMiniMarkdown(block.text, el.ownerDocument));
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
case "json": return h("pre", {
|
|
143
|
+
key,
|
|
144
|
+
class: "webskill-result__json"
|
|
145
|
+
}, JSON.stringify(block.data, null, 2));
|
|
146
|
+
case "table": return h("table", { key }, [h("thead", [h("tr", block.columns.map((column) => h("th", column)))]), h("tbody", block.rows.map((row, i) => h("tr", { key: i }, row.map((cell, j) => h("td", { key: j }, typeof cell === "object" ? JSON.stringify(cell) : String(cell ?? ""))))))]);
|
|
147
|
+
case "image": return h("img", {
|
|
148
|
+
key,
|
|
149
|
+
src: block.url,
|
|
150
|
+
alt: block.alt ?? ""
|
|
151
|
+
});
|
|
152
|
+
case "file": {
|
|
153
|
+
const meta = [block.mimeType, block.size !== void 0 ? `${block.size} bytes` : void 0].filter(Boolean).join(", ");
|
|
154
|
+
return h("div", {
|
|
155
|
+
key,
|
|
156
|
+
class: "webskill-result__file"
|
|
157
|
+
}, meta ? `${block.path} (${meta})` : block.path);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** 订阅 bridge.state.latestResult 渲染五种 RenderBlock */
|
|
162
|
+
const ResultBlocks = defineComponent({
|
|
163
|
+
name: "WebSkillResultBlocks",
|
|
164
|
+
props: { bridge: {
|
|
165
|
+
type: Object,
|
|
166
|
+
required: true
|
|
167
|
+
} },
|
|
168
|
+
setup(props) {
|
|
169
|
+
return () => {
|
|
170
|
+
const result = props.bridge.state.latestResult;
|
|
171
|
+
if (!result) return null;
|
|
172
|
+
return h("div", { class: "webskill-result" }, [
|
|
173
|
+
result.title ? h("div", { class: "webskill-form__title" }, result.title) : null,
|
|
174
|
+
result.summary ? h("div", { class: "webskill-result__summary" }, result.summary) : null,
|
|
175
|
+
...result.blocks.map((block, i) => renderBlock(block, i))
|
|
176
|
+
]);
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
/** 订阅 bridge.state.streamingText 增量渲染流式文本 */
|
|
181
|
+
const StreamingText = defineComponent({
|
|
182
|
+
name: "WebSkillStreamingText",
|
|
183
|
+
props: { bridge: {
|
|
184
|
+
type: Object,
|
|
185
|
+
required: true
|
|
186
|
+
} },
|
|
187
|
+
setup(props) {
|
|
188
|
+
return () => {
|
|
189
|
+
const text = props.bridge.state.streamingText;
|
|
190
|
+
return text === "" ? null : h("div", { class: "webskill-streaming-text" }, text);
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
//#endregion
|
|
196
|
+
export { InteractionForm, ResultBlocks, StreamingText, VueBridgeState };
|
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { C as InteractionRequest, X as RenderResultRequest, Y as RenderBlock, pt as UiBridge, vt as buildRenderResult, w as InteractionResponse } from "./index-88KNOnbr.js";
|
|
2
|
+
//#region ../ui/dist/index.d.ts
|
|
3
|
+
//#region src/model/formModel.d.ts
|
|
4
|
+
interface FormModel {
|
|
5
|
+
kind: 'ask' | 'confirm' | 'form' | 'select';
|
|
6
|
+
title?: string;
|
|
7
|
+
message?: string;
|
|
8
|
+
controls: ControlModel[];
|
|
9
|
+
submitLabel: string;
|
|
10
|
+
cancelLabel?: string;
|
|
11
|
+
}
|
|
12
|
+
interface ControlModel {
|
|
13
|
+
name: string;
|
|
14
|
+
label: string;
|
|
15
|
+
control: 'text' | 'number' | 'boolean' | 'select' | 'textarea';
|
|
16
|
+
required?: boolean;
|
|
17
|
+
description?: string;
|
|
18
|
+
defaultValue?: unknown;
|
|
19
|
+
options?: Array<{
|
|
20
|
+
label: string;
|
|
21
|
+
value: unknown;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
/** 提交值按请求类型归形(WebFormBridge 与框架组件库共享单一来源) */
|
|
25
|
+
declare function shapeInteractionValue(model: FormModel, values: Record<string, unknown>): unknown;
|
|
26
|
+
/** 四类 InteractionRequest → 统一中间模型(框架无关) */
|
|
27
|
+
declare function interactionToFormModel(request: InteractionRequest): FormModel;
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/model/collectValues.d.ts
|
|
30
|
+
interface CollectedValues {
|
|
31
|
+
values: Record<string, unknown>;
|
|
32
|
+
/** required 但为空的控件名(阻止提交) */
|
|
33
|
+
missingRequired: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 严格类型化控件值收集:
|
|
37
|
+
* - number 空串 → undefined(非 0)
|
|
38
|
+
* - select 用 option 值本身经 JSON 编码比对命中(对象值可正确命中)
|
|
39
|
+
* - required 空值列入 missingRequired(由调用方阻止提交并标记)
|
|
40
|
+
*/
|
|
41
|
+
declare function collectValues(controls: ControlModel[], container: ParentNode): CollectedValues;
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/markdown/miniMarkdown.d.ts
|
|
44
|
+
/**
|
|
45
|
+
* 最小子集 markdown → DOM:标题(#..###)、无序列表、代码块(```)、加粗、链接。
|
|
46
|
+
* 全部内容经 textContent 写入,用户内容天然转义防 HTML 注入。
|
|
47
|
+
*/
|
|
48
|
+
declare function renderMiniMarkdown(text: string, doc: Document): HTMLElement;
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/web/webFormBridge.d.ts
|
|
51
|
+
/**
|
|
52
|
+
* 框架无关原生 DOM 的 UiBridge:request 渲染表单并返回 Promise
|
|
53
|
+
* (提交 resolve / 取消 {cancelled:true}),完成后卸载 DOM。
|
|
54
|
+
* document 可注入(jsdom 测试);BEM class 样式可覆写。
|
|
55
|
+
*/
|
|
56
|
+
declare class WebFormBridge implements UiBridge {
|
|
57
|
+
#private;
|
|
58
|
+
constructor(options: {
|
|
59
|
+
mount: HTMLElement;
|
|
60
|
+
document?: Document;
|
|
61
|
+
styles?: boolean;
|
|
62
|
+
});
|
|
63
|
+
request(input: InteractionRequest): Promise<InteractionResponse>;
|
|
64
|
+
renderResult(input: RenderResultRequest): Promise<void>;
|
|
65
|
+
progress(input: {
|
|
66
|
+
runId: string;
|
|
67
|
+
message: string;
|
|
68
|
+
value?: number;
|
|
69
|
+
}): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/web/resultRenderer.d.ts
|
|
73
|
+
/** RenderBlock → DOM(markdown/json/table/image/file 五种) */
|
|
74
|
+
declare function renderBlocks(container: HTMLElement, blocks: RenderBlock[], doc: Document): void;
|
|
75
|
+
/** 完整 RenderResultRequest → DOM(summary 头 + blocks) */
|
|
76
|
+
declare function renderRenderResult(request: RenderResultRequest, doc: Document): HTMLElement;
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/web/styles.d.ts
|
|
79
|
+
/** 最小样式(BEM class,应用可整体覆写) */
|
|
80
|
+
declare const WEBSKILL_STYLES_CSS = "\n.webskill-form { display: block; padding: 12px; border: 1px solid #d0d0d0; border-radius: 8px; font-family: system-ui, sans-serif; max-width: 420px; }\n.webskill-form__title { margin: 0 0 8px; font-size: 15px; font-weight: 600; }\n.webskill-form__message { margin: 0 0 10px; font-size: 14px; }\n.webskill-form__control { margin-bottom: 10px; display: flex; flex-direction: column; gap: 4px; }\n.webskill-form__control--invalid .webskill-form__input { border-color: #d33; }\n.webskill-form__label { font-size: 13px; font-weight: 500; }\n.webskill-form__label-required::after { content: ' *'; color: #d33; }\n.webskill-form__description { font-size: 12px; color: #666; }\n.webskill-form__error { font-size: 12px; color: #d33; display: none; }\n.webskill-form__control--invalid .webskill-form__error { display: block; }\n.webskill-form__input { padding: 6px 8px; border: 1px solid #bbb; border-radius: 4px; font-size: 14px; font-family: inherit; }\n.webskill-form__actions { display: flex; gap: 8px; margin-top: 12px; }\n.webskill-form__button { padding: 6px 14px; border-radius: 4px; border: 1px solid #bbb; background: #fff; cursor: pointer; font-size: 14px; }\n.webskill-form__button--primary { background: #2563eb; border-color: #2563eb; color: #fff; }\n.webskill-form__button--ghost { background: transparent; }\n.webskill-result { font-family: system-ui, sans-serif; font-size: 14px; }\n.webskill-result__summary { font-size: 12px; color: #666; margin-bottom: 6px; }\n.webskill-result__json { background: #f5f5f5; padding: 8px; border-radius: 4px; overflow-x: auto; }\n.webskill-result__file { padding: 4px 0; font-size: 13px; }\n.webskill-md pre { background: #f5f5f5; padding: 8px; border-radius: 4px; overflow-x: auto; }\n.webskill-md h1, .webskill-md h2, .webskill-md h3 { margin: 8px 0 4px; }\n.webskill-md p { margin: 4px 0; }\n.webskill-md ul { margin: 4px 0; padding-left: 20px; }\n";
|
|
81
|
+
/** 每个 document 只注入一次 */
|
|
82
|
+
declare function ensureStyles(doc: Document): void;
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/adapters/vercel/vercelPayload.d.ts
|
|
85
|
+
/** Vercel AI SDK v7 ToolCallPart 兼容结构 */
|
|
86
|
+
interface VercelToolInvocation {
|
|
87
|
+
type: 'tool-call';
|
|
88
|
+
toolCallId: string;
|
|
89
|
+
toolName: string;
|
|
90
|
+
input: unknown;
|
|
91
|
+
}
|
|
92
|
+
declare const VERCEL_INTERACTION_TOOL_NAME = "webskill_interaction";
|
|
93
|
+
/** InteractionRequest → SDK tool UI 结构(应用聊天 UI 直接消费) */
|
|
94
|
+
declare function toVercelToolInvocation(request: InteractionRequest): VercelToolInvocation;
|
|
95
|
+
/** SDK 回传(tool result / output)→ InteractionResponse */
|
|
96
|
+
declare function fromVercelToolResult(part: unknown): InteractionResponse;
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/adapters/vercel/vercelUiBridge.d.ts
|
|
99
|
+
/**
|
|
100
|
+
* 薄桥接类:request → SDK tool UI 结构 → 应用的聊天 UI(sendInteraction 回调实现)
|
|
101
|
+
* → SDK 回传 → InteractionResponse。
|
|
102
|
+
*/
|
|
103
|
+
declare class VercelUiBridge implements UiBridge {
|
|
104
|
+
#private;
|
|
105
|
+
constructor(deps: {
|
|
106
|
+
sendInteraction: (invocation: VercelToolInvocation) => Promise<unknown>;
|
|
107
|
+
});
|
|
108
|
+
request(input: InteractionRequest): Promise<InteractionResponse>;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/adapters/openui/openuiLang.d.ts
|
|
112
|
+
/**
|
|
113
|
+
* InteractionRequest ↔ openui-lang 双向转换。
|
|
114
|
+
* openui-lang 语法(@openuidev/react-lang 实测):
|
|
115
|
+
* - 每行一条语句 `identifier = Expression`,`root = Component(...)` 为入口
|
|
116
|
+
* - 组件调用参数为位置参数;字符串双引号反斜杠转义
|
|
117
|
+
* - 组件词汇(与应用 Library 约定):Form(title, children)、
|
|
118
|
+
* TextField(name, label, required?, defaultValue?)、NumberField、BooleanField(name, label, default?)、
|
|
119
|
+
* SelectField(name, label, options)、SubmitButton(label, actionType, requestId)、
|
|
120
|
+
* CancelButton(label, actionType, requestId)
|
|
121
|
+
*/
|
|
122
|
+
declare const OPENUI_SUBMIT_ACTION = "webskill:submit";
|
|
123
|
+
declare const OPENUI_CANCEL_ACTION = "webskill:cancel";
|
|
124
|
+
declare function toOpenUiLang(request: InteractionRequest): string;
|
|
125
|
+
/** OpenUI ActionEvent → InteractionResponse(formState 优先,取消单独分支) */
|
|
126
|
+
declare function fromOpenUiAction(action: unknown): InteractionResponse;
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/adapters/a2ui/a2uiMessages.d.ts
|
|
129
|
+
/**
|
|
130
|
+
* InteractionRequest → A2UI 协议消息(v0.9.1,Basic Catalog)。
|
|
131
|
+
* 依据 https://a2ui.org/specification/v0.9-a2ui/:
|
|
132
|
+
* - 信封 {version, createSurface|updateComponents|updateDataModel|deleteSurface}
|
|
133
|
+
* - 组件为扁平邻接表(id 引用),root 必需
|
|
134
|
+
* - 输入组件与数据模型经 JSON Pointer 双向绑定;action.context 回传
|
|
135
|
+
* - createSurface.sendDataModel: true → 客户端动作时携带完整数据模型
|
|
136
|
+
*
|
|
137
|
+
* action 约定:submit → {name: 'webskill:submit', context: {requestId}};
|
|
138
|
+
* cancel → {name: 'webskill:cancel', context: {requestId}}。
|
|
139
|
+
*/
|
|
140
|
+
declare const A2UI_VERSION = "v0.9.1";
|
|
141
|
+
declare const A2UI_BASIC_CATALOG_ID = "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json";
|
|
142
|
+
declare const A2UI_SUBMIT_ACTION = "webskill:submit";
|
|
143
|
+
declare const A2UI_CANCEL_ACTION = "webskill:cancel";
|
|
144
|
+
interface A2uiMessage {
|
|
145
|
+
version: string;
|
|
146
|
+
[key: string]: unknown;
|
|
147
|
+
}
|
|
148
|
+
declare function toA2uiMessages(request: InteractionRequest): A2uiMessage[];
|
|
149
|
+
/** A2UI client→server action 事件 → InteractionResponse */
|
|
150
|
+
declare function fromA2uiAction(event: unknown): InteractionResponse;
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/a2uiRuntime/litRendererBridge.d.ts
|
|
153
|
+
/**
|
|
154
|
+
* A2UI 运行时桥(Lit 渲染器路径):
|
|
155
|
+
* InteractionRequest →(7A 转换器)→ A2UI v0.9.1 消息序列 →
|
|
156
|
+
* @a2ui/web_core MessageProcessor 处理 → @a2ui/lit 渲染到容器 →
|
|
157
|
+
* action 事件(合并双向绑定的表单数据模型)→ fromA2uiAction → InteractionResponse。
|
|
158
|
+
*
|
|
159
|
+
* 版本对齐:渲染器 @a2ui/lit@0.10.x 经 v0_9 入口同时接受 v0.9 / v0.9.1 消息
|
|
160
|
+
* (schema 枚举 ['v0.9','v0.9.1']),与 7A v0.9.1 转换产物直接兼容。
|
|
161
|
+
*/
|
|
162
|
+
declare class LitRendererBridge implements UiBridge {
|
|
163
|
+
#private;
|
|
164
|
+
constructor(deps: {
|
|
165
|
+
mount: HTMLElement;
|
|
166
|
+
document?: Document;
|
|
167
|
+
});
|
|
168
|
+
request(input: InteractionRequest): Promise<InteractionResponse>;
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
export { A2UI_BASIC_CATALOG_ID, A2UI_CANCEL_ACTION, A2UI_SUBMIT_ACTION, A2UI_VERSION, type A2uiMessage, type CollectedValues, type ControlModel, type FormModel, LitRendererBridge, OPENUI_CANCEL_ACTION, OPENUI_SUBMIT_ACTION, VERCEL_INTERACTION_TOOL_NAME, type VercelToolInvocation, VercelUiBridge, WEBSKILL_STYLES_CSS, WebFormBridge, buildRenderResult, collectValues, ensureStyles, fromA2uiAction, fromOpenUiAction, fromVercelToolResult, interactionToFormModel, renderBlocks, renderMiniMarkdown, renderRenderResult, shapeInteractionValue, toA2uiMessages, toOpenUiLang, toVercelToolInvocation };
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { S as buildRenderResult } from "./dist-B_ldNWwT.js";
|
|
2
|
+
import { C as toOpenUiLang, S as toA2uiMessages, _ as interactionToFormModel, a as LitRendererBridge, b as renderRenderResult, c as VERCEL_INTERACTION_TOOL_NAME, d as WebFormBridge, f as collectValues, g as fromVercelToolResult, h as fromOpenUiAction, i as A2UI_VERSION, l as VercelUiBridge, m as fromA2uiAction, n as A2UI_CANCEL_ACTION, o as OPENUI_CANCEL_ACTION, p as ensureStyles, r as A2UI_SUBMIT_ACTION, s as OPENUI_SUBMIT_ACTION, t as A2UI_BASIC_CATALOG_ID, u as WEBSKILL_STYLES_CSS, v as renderBlocks, w as toVercelToolInvocation, x as shapeInteractionValue, y as renderMiniMarkdown } from "./dist-RcqvzAGF.js";
|
|
3
|
+
|
|
4
|
+
export { A2UI_BASIC_CATALOG_ID, A2UI_CANCEL_ACTION, A2UI_SUBMIT_ACTION, A2UI_VERSION, LitRendererBridge, OPENUI_CANCEL_ACTION, OPENUI_SUBMIT_ACTION, VERCEL_INTERACTION_TOOL_NAME, VercelUiBridge, WEBSKILL_STYLES_CSS, WebFormBridge, buildRenderResult, collectValues, ensureStyles, fromA2uiAction, fromOpenUiAction, fromVercelToolResult, interactionToFormModel, renderBlocks, renderMiniMarkdown, renderRenderResult, shapeInteractionValue, toA2uiMessages, toOpenUiLang, toVercelToolInvocation };
|