@webmcp-auto-ui/sdk 2.5.37 → 2.5.38
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/package.json +4 -1
- package/src/canvas-to-notebook.ts +167 -0
- package/src/index.ts +13 -1
- package/src/recipes/index.ts +1 -1
- package/src/stores/canvas.ts +1 -0
- package/src/unwrap.ts +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webmcp-auto-ui/sdk",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.38",
|
|
4
4
|
"description": "Skills CRUD, HyperSkill format, Svelte 5 stores",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -8,14 +8,17 @@
|
|
|
8
8
|
"main": "./src/index.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
11
12
|
"svelte": "./src/index.ts",
|
|
12
13
|
"import": "./src/index.ts"
|
|
13
14
|
},
|
|
14
15
|
"./canvas": {
|
|
16
|
+
"types": "./dist/canvas.d.ts",
|
|
15
17
|
"svelte": "./src/canvas.ts",
|
|
16
18
|
"import": "./src/canvas.ts"
|
|
17
19
|
},
|
|
18
20
|
"./canvas-vanilla": {
|
|
21
|
+
"types": "./dist/canvas-vanilla.d.ts",
|
|
19
22
|
"import": "./src/canvas-vanilla.ts"
|
|
20
23
|
}
|
|
21
24
|
},
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas → HyperSkill notebook serializer.
|
|
3
|
+
* Each block becomes one or more SQL/JS/MD cells. Dynamic when lineage is
|
|
4
|
+
* present, snapshot fallback otherwise.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface WidgetLineage {
|
|
8
|
+
widgetType: string;
|
|
9
|
+
widgetParams: Record<string, unknown>;
|
|
10
|
+
toolCalls: Array<{
|
|
11
|
+
serverName: string;
|
|
12
|
+
toolName: string;
|
|
13
|
+
args: Record<string, unknown>;
|
|
14
|
+
resultPreview?: string;
|
|
15
|
+
}>;
|
|
16
|
+
originRecipe?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CanvasToNotebookOptions {
|
|
20
|
+
title?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
/** Active MCP servers (name, url) — copied as `servers:` in frontmatter. */
|
|
23
|
+
servers?: Array<{ name: string; url: string }>;
|
|
24
|
+
/** Active bundled WebMCP server ids (e.g. ['autoui', 'deckgl']). */
|
|
25
|
+
webmcpServers?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface NotebookCell {
|
|
29
|
+
kind: 'sql' | 'js' | 'md';
|
|
30
|
+
content: string;
|
|
31
|
+
varname?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CanvasBlock {
|
|
35
|
+
id: string;
|
|
36
|
+
type: string;
|
|
37
|
+
data: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const SQL_TOOL_RE = /(^|[._-])query_sql$|^(query|run|execute)(_sql)?$/i;
|
|
41
|
+
const ARRAY_PARAM_KEYS = ['rows', 'data', 'items', 'cards'] as const;
|
|
42
|
+
|
|
43
|
+
/** Convert a single block + lineage into one or more notebook cells. */
|
|
44
|
+
export function lineageToCells(
|
|
45
|
+
block: CanvasBlock,
|
|
46
|
+
lineage: WidgetLineage | null,
|
|
47
|
+
): NotebookCell[] {
|
|
48
|
+
const cells: NotebookCell[] = [];
|
|
49
|
+
|
|
50
|
+
if (lineage?.originRecipe) {
|
|
51
|
+
cells.push({ kind: 'md', content: `> Generated from recipe: \`${lineage.originRecipe}\`` });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Snapshot fallback — no lineage.
|
|
55
|
+
if (!lineage || !lineage.toolCalls || lineage.toolCalls.length === 0) {
|
|
56
|
+
cells.push({
|
|
57
|
+
kind: 'js',
|
|
58
|
+
content: `await widget('${block.type}', ${JSON.stringify(block.data, null, 2)});`,
|
|
59
|
+
});
|
|
60
|
+
return cells;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Single SQL tool call → SQL cell + JS widget cell.
|
|
64
|
+
if (lineage.toolCalls.length === 1 && SQL_TOOL_RE.test(lineage.toolCalls[0]!.toolName)) {
|
|
65
|
+
const tc = lineage.toolCalls[0]!;
|
|
66
|
+
const sql = typeof tc.args['sql'] === 'string' ? (tc.args['sql'] as string) : '';
|
|
67
|
+
const varname = 'rows';
|
|
68
|
+
cells.push({ kind: 'sql', content: sql, varname });
|
|
69
|
+
|
|
70
|
+
const arrayKey = pickArrayParamKey(lineage.widgetParams);
|
|
71
|
+
let widgetCall: string;
|
|
72
|
+
if (arrayKey) {
|
|
73
|
+
const others = stripKeys(lineage.widgetParams, [arrayKey]);
|
|
74
|
+
const merged = { ...others, [arrayKey]: '__ROWS__' };
|
|
75
|
+
const json = JSON.stringify(merged, null, 2).replace('"__ROWS__"', varname);
|
|
76
|
+
widgetCall = `await widget('${block.type}', ${json});`;
|
|
77
|
+
} else {
|
|
78
|
+
widgetCall = `// TODO: map rows -> widget params\nawait widget('${block.type}', ${JSON.stringify(lineage.widgetParams, null, 2)});`;
|
|
79
|
+
}
|
|
80
|
+
cells.push({ kind: 'js', content: widgetCall });
|
|
81
|
+
return cells;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// General case — replay tool calls in order.
|
|
85
|
+
const lines: string[] = [];
|
|
86
|
+
lines.push('const unwrap = (r) => (r?.data ?? r?.results ?? r?.items ?? r ?? []);');
|
|
87
|
+
lineage.toolCalls.forEach((tc, i) => {
|
|
88
|
+
lines.push(
|
|
89
|
+
`const r${i} = await call('${tc.serverName}', '${tc.toolName}', ${JSON.stringify(tc.args, null, 2)});`,
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const lastIdx = lineage.toolCalls.length - 1;
|
|
94
|
+
const arrayKey = pickArrayParamKey(lineage.widgetParams);
|
|
95
|
+
if (arrayKey) {
|
|
96
|
+
lines.push(`const ${arrayKey} = unwrap(r${lastIdx});`);
|
|
97
|
+
const others = stripKeys(lineage.widgetParams, [arrayKey]);
|
|
98
|
+
const merged = { ...others, [arrayKey]: '__ITEMS__' };
|
|
99
|
+
const json = JSON.stringify(merged, null, 2).replace('"__ITEMS__"', arrayKey);
|
|
100
|
+
lines.push(`await widget('${block.type}', ${json});`);
|
|
101
|
+
} else {
|
|
102
|
+
lines.push(`await widget('${block.type}', ${JSON.stringify(lineage.widgetParams, null, 2)});`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
cells.push({ kind: 'js', content: lines.join('\n') });
|
|
106
|
+
return cells;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Build the full notebook markdown from a snapshot of the canvas. */
|
|
110
|
+
export function canvasToNotebookMarkdown(
|
|
111
|
+
blocks: CanvasBlock[],
|
|
112
|
+
getLineage: (blockId: string) => WidgetLineage | null,
|
|
113
|
+
opts: CanvasToNotebookOptions = {},
|
|
114
|
+
): string {
|
|
115
|
+
const fm: string[] = ['---'];
|
|
116
|
+
fm.push(`title: ${yamlQuote(opts.title ?? 'Canvas snapshot')}`);
|
|
117
|
+
if (opts.description) fm.push(`description: ${yamlQuote(opts.description)}`);
|
|
118
|
+
if (opts.servers && opts.servers.length > 0) {
|
|
119
|
+
fm.push('servers:');
|
|
120
|
+
for (const s of opts.servers) {
|
|
121
|
+
fm.push(` - name: ${yamlQuote(s.name)}`);
|
|
122
|
+
fm.push(` url: ${yamlQuote(s.url)}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (opts.webmcpServers && opts.webmcpServers.length > 0) {
|
|
126
|
+
fm.push(`webmcp_servers: [${opts.webmcpServers.map((s) => yamlQuote(s)).join(', ')}]`);
|
|
127
|
+
}
|
|
128
|
+
fm.push('---', '');
|
|
129
|
+
|
|
130
|
+
const body: string[] = [];
|
|
131
|
+
for (const block of blocks) {
|
|
132
|
+
const cells = lineageToCells(block, getLineage(block.id));
|
|
133
|
+
for (const cell of cells) {
|
|
134
|
+
body.push(renderCell(cell), '');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return fm.join('\n') + '\n' + body.join('\n').replace(/\n+$/, '') + '\n';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function renderCell(cell: NotebookCell): string {
|
|
142
|
+
if (cell.kind === 'md') return cell.content;
|
|
143
|
+
if (cell.kind === 'sql') {
|
|
144
|
+
const meta = cell.varname ? `-- @meta {"varname": "${cell.varname}"}\n` : '';
|
|
145
|
+
return '```sql\n' + meta + cell.content + '\n```';
|
|
146
|
+
}
|
|
147
|
+
return '```js\n' + cell.content + '\n```';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function pickArrayParamKey(params: Record<string, unknown>): string | null {
|
|
151
|
+
for (const k of ARRAY_PARAM_KEYS) {
|
|
152
|
+
if (k in params && Array.isArray(params[k])) return k;
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function stripKeys(obj: Record<string, unknown>, keys: string[]): Record<string, unknown> {
|
|
158
|
+
const out: Record<string, unknown> = {};
|
|
159
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
160
|
+
if (!keys.includes(k)) out[k] = v;
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function yamlQuote(s: string): string {
|
|
166
|
+
return '"' + s.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
|
|
167
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -91,7 +91,7 @@ export type { RemoteMcpRegistryEntry } from './remote-mcp-registry.js';
|
|
|
91
91
|
// import { canvas } from '@webmcp-auto-ui/sdk/canvas'
|
|
92
92
|
|
|
93
93
|
// Recipe runner — markdown-fence parser + JS/TS/SQL/etc executor over MCP
|
|
94
|
-
export { parseBody, runCode, estimateTokens, safeStringify, findCodeParamName, buildToolArgs } from './recipes/index.js';
|
|
94
|
+
export { parseBody, runCode, estimateTokens, safeStringify, findCodeParamName, buildToolArgs, parseWidgetDisplayCall } from './recipes/index.js';
|
|
95
95
|
export type { ParsedSegment, RunResult, RunLog, RunTab, RecipeData } from './recipes/index.js';
|
|
96
96
|
|
|
97
97
|
// Short URL — domain-dependent compact token
|
|
@@ -99,3 +99,15 @@ export { buildShortUrl, getShortToken } from './short-url.js';
|
|
|
99
99
|
|
|
100
100
|
// Widget sample-data extractor — parses the `## Example` block of a recipe
|
|
101
101
|
export { extractSampleData } from './widget-sample-data.js';
|
|
102
|
+
|
|
103
|
+
// MCP response normalizer — flattens common envelope shapes to array
|
|
104
|
+
export { unwrap } from './unwrap.js';
|
|
105
|
+
|
|
106
|
+
// Canvas → HyperSkill notebook serializer
|
|
107
|
+
export { lineageToCells, canvasToNotebookMarkdown } from './canvas-to-notebook.js';
|
|
108
|
+
export type {
|
|
109
|
+
WidgetLineage,
|
|
110
|
+
CanvasToNotebookOptions,
|
|
111
|
+
NotebookCell,
|
|
112
|
+
CanvasBlock,
|
|
113
|
+
} from './canvas-to-notebook.js';
|
package/src/recipes/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { parseBody } from './parse.js';
|
|
2
|
-
export { runCode, estimateTokens, safeStringify, findCodeParamName, buildToolArgs } from './runner.js';
|
|
2
|
+
export { runCode, estimateTokens, safeStringify, findCodeParamName, buildToolArgs, parseWidgetDisplayCall } from './runner.js';
|
|
3
3
|
export type { ParsedSegment, RunResult, RunLog, RunTab, RecipeData } from './types.js';
|
package/src/stores/canvas.ts
CHANGED
|
@@ -290,6 +290,7 @@ function createCanvasVanilla() {
|
|
|
290
290
|
|
|
291
291
|
if (srv.enabled && !srv.connected && !liveUrls.has(srv.url)) {
|
|
292
292
|
inFlight.add(name);
|
|
293
|
+
setDataServerMeta(name, { connecting: true, error: undefined });
|
|
293
294
|
try {
|
|
294
295
|
const opts = srv.headers ? { headers: srv.headers } : undefined;
|
|
295
296
|
const { name: actualName, tools } = await multiClient.addServer(srv.url, opts);
|
package/src/unwrap.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize MCP tool responses to an array. Handles common envelopes.
|
|
3
|
+
* Tries: direct array → .data → .results → .rows → .entries → .items → .feed.entry → .properties.parameter → []
|
|
4
|
+
*/
|
|
5
|
+
export function unwrap(r: unknown): any[] {
|
|
6
|
+
if (Array.isArray(r)) return r;
|
|
7
|
+
if (r == null || typeof r !== 'object') return [];
|
|
8
|
+
const o = r as any;
|
|
9
|
+
return o.data ?? o.results ?? o.rows ?? o.entries ?? o.items
|
|
10
|
+
?? o.feed?.entry ?? o.properties?.parameter ?? [];
|
|
11
|
+
}
|