@capybara-hq/plugin-sdk 0.0.0-stage → 0.1.0-rc.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.
Files changed (70) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +27 -2
  4. package/dist/agent-template.d.ts +47 -0
  5. package/dist/agent-template.js +123 -0
  6. package/dist/bootstrap.d.ts +67 -0
  7. package/dist/bootstrap.js +109 -0
  8. package/dist/cards.d.ts +64 -0
  9. package/dist/cards.js +217 -0
  10. package/dist/channel.d.ts +230 -0
  11. package/dist/channel.js +26 -0
  12. package/dist/connector-auth.d.ts +64 -0
  13. package/dist/connector-auth.js +96 -0
  14. package/dist/connector.d.ts +154 -0
  15. package/dist/connector.js +1 -0
  16. package/dist/ctx.d.ts +176 -0
  17. package/dist/ctx.js +1 -0
  18. package/dist/define-extension.d.ts +41 -0
  19. package/dist/define-extension.js +85 -0
  20. package/dist/directory.d.ts +29 -0
  21. package/dist/directory.js +1 -0
  22. package/dist/extension-point.d.ts +29 -0
  23. package/dist/extension-point.js +36 -0
  24. package/dist/file-preview.d.ts +64 -0
  25. package/dist/file-preview.js +72 -0
  26. package/dist/frontmatter.d.ts +12 -0
  27. package/dist/frontmatter.js +36 -0
  28. package/dist/hooks.d.ts +194 -0
  29. package/dist/hooks.js +14 -0
  30. package/dist/identity.d.ts +44 -0
  31. package/dist/identity.js +1 -0
  32. package/dist/index.d.ts +27 -0
  33. package/dist/index.js +27 -0
  34. package/dist/marketplace.d.ts +99 -0
  35. package/dist/marketplace.js +256 -0
  36. package/dist/model-params.d.ts +82 -0
  37. package/dist/model-params.js +222 -0
  38. package/dist/model.d.ts +227 -0
  39. package/dist/model.js +152 -0
  40. package/dist/permissions.d.ts +67 -0
  41. package/dist/permissions.js +271 -0
  42. package/dist/plugin-manifest.d.ts +141 -0
  43. package/dist/plugin-manifest.js +322 -0
  44. package/dist/plugin-package.d.ts +139 -0
  45. package/dist/plugin-package.js +448 -0
  46. package/dist/sandbox.d.ts +152 -0
  47. package/dist/sandbox.js +96 -0
  48. package/dist/secret.d.ts +5 -0
  49. package/dist/secret.js +8 -0
  50. package/dist/semver-range.d.ts +6 -0
  51. package/dist/semver-range.js +12 -0
  52. package/dist/skill-invocation.d.ts +83 -0
  53. package/dist/skill-invocation.js +201 -0
  54. package/dist/skill-keys.d.ts +9 -0
  55. package/dist/skill-keys.js +16 -0
  56. package/dist/skill.d.ts +77 -0
  57. package/dist/skill.js +244 -0
  58. package/dist/storage.d.ts +96 -0
  59. package/dist/storage.js +9 -0
  60. package/dist/tool.d.ts +154 -0
  61. package/dist/tool.js +218 -0
  62. package/dist/ui.d.ts +265 -0
  63. package/dist/ui.js +358 -0
  64. package/dist/url.d.ts +2 -0
  65. package/dist/url.js +12 -0
  66. package/package.json +40 -3
  67. package/schemas/bootstrap.v1.schema.json +125 -0
  68. package/schemas/connector-template.v1.schema.json +116 -0
  69. package/schemas/marketplace.v1.schema.json +270 -0
  70. package/schemas/plugin.v1.schema.json +330 -0
package/dist/tool.js ADDED
@@ -0,0 +1,218 @@
1
+ import { checkParamValue } from "./model-params.js";
2
+ import { isHttpUrl } from "./url.js";
3
+ /** Longest {@link ToolSpec.title}, in code points. */
4
+ export const TOOL_TITLE_MAX = 80;
5
+ /** Most {@link ToolSpec.keyArgs}. */
6
+ export const TOOL_KEY_ARGS_MAX = 3;
7
+ /**
8
+ * Control (Cc) and format (Cf) characters: newlines, C0 / C1 controls, bidi marks, overrides and
9
+ * isolates (U+061C, U+200E/F, U+202A–202E, U+2066–2069), invisible ones (U+200B, U+2060, U+FEFF)
10
+ * and tag characters. They could reorder, hide or break a one-line label, so display titles and key
11
+ * argument names never carry them. ZWNJ (U+200C) and ZWJ (U+200D) are allowed: Persian and other
12
+ * scripts and emoji sequences need them.
13
+ */
14
+ const CONTROL_OR_FORMAT = /(?![\u200C\u200D])[\p{Cc}\p{Cf}]/u;
15
+ /**
16
+ * Checks the display-only {@link ToolSpec.title} and {@link ToolSpec.keyArgs}; returns the problems
17
+ * (empty = valid). The title is 1–80 code points, not blank and free of control and format
18
+ * characters (Unicode Cc / Cf); keyArgs are at most 3 unique strings, each a property of
19
+ * `inputSchema.properties` and free of Cc / Cf characters.
20
+ */
21
+ export function validateToolPresentation(spec) {
22
+ const out = [];
23
+ const title = spec.title;
24
+ if (title !== undefined &&
25
+ (typeof title !== "string" || title.trim() === "" || [...title].length > TOOL_TITLE_MAX)) {
26
+ out.push(`title must be a non-blank string of at most ${TOOL_TITLE_MAX} characters`);
27
+ }
28
+ else if (typeof title === "string" && CONTROL_OR_FORMAT.test(title)) {
29
+ out.push("title must not contain control, bidi or invisible characters");
30
+ }
31
+ const keyArgs = spec.keyArgs;
32
+ if (keyArgs === undefined)
33
+ return out;
34
+ if (!Array.isArray(keyArgs) || keyArgs.some((k) => typeof k !== "string")) {
35
+ out.push("keyArgs must be an array of input property names");
36
+ return out;
37
+ }
38
+ if (keyArgs.length > TOOL_KEY_ARGS_MAX)
39
+ out.push(`keyArgs must name at most ${TOOL_KEY_ARGS_MAX} input properties`);
40
+ if (new Set(keyArgs).size !== keyArgs.length)
41
+ out.push("keyArgs must not repeat a name");
42
+ if (keyArgs.some((k) => CONTROL_OR_FORMAT.test(k)))
43
+ out.push("keyArgs names must not contain control, bidi or invisible characters");
44
+ const schema = spec.inputSchema;
45
+ const props = typeof schema?.properties === "object" && schema.properties !== null
46
+ ? schema.properties
47
+ : {};
48
+ for (const k of keyArgs) {
49
+ if (!Object.hasOwn(props, k))
50
+ out.push(`keyArgs names "${k}", which is not a property of inputSchema`);
51
+ }
52
+ return out;
53
+ }
54
+ const DISPLAY_MAX = 200;
55
+ /** 校验 {@link ToolDisplay},返回问题列表(空 = 合法);长度按 Unicode 码点计。 */
56
+ export function validateToolDisplay(d) {
57
+ if (typeof d !== "object" || d === null || Array.isArray(d))
58
+ return ["display must be an object"];
59
+ const s = d.summary;
60
+ if (typeof s !== "object" || s === null || Array.isArray(s))
61
+ return ["display.summary must be an object with zh and en"];
62
+ const out = [];
63
+ for (const k of ["zh", "en"]) {
64
+ const v = s[k];
65
+ if (typeof v !== "string" || v === "" || [...v].length > DISPLAY_MAX)
66
+ out.push(`display.summary.${k} must be a non-empty string of at most ${DISPLAY_MAX} characters`);
67
+ }
68
+ return out;
69
+ }
70
+ /** 工具执行失败:message 给模型看,须为英文且可操作。 */
71
+ export class ToolError extends Error {
72
+ kind;
73
+ constructor(message, opts) {
74
+ super(message);
75
+ this.name = "ToolError";
76
+ this.kind = opts?.kind ?? "error";
77
+ }
78
+ }
79
+ /** 插件工具名规则。 */
80
+ export const EXTENSION_TOOL_NAME = /^[a-zA-Z0-9_-]{1,40}$/;
81
+ /** 校验插件注册的工具声明,返回问题列表(空 = 合法)。 */
82
+ export function validateToolSpec(spec) {
83
+ const out = [];
84
+ if (typeof spec?.name !== "string" || !EXTENSION_TOOL_NAME.test(spec.name))
85
+ out.push(`name must match ${EXTENSION_TOOL_NAME}`);
86
+ if (typeof spec?.description !== "string" || spec.description.trim() === "")
87
+ out.push("description must be a non-empty string");
88
+ if (typeof spec?.inputSchema !== "object" || spec.inputSchema === null)
89
+ out.push("inputSchema must be an object");
90
+ if (spec?.defaultPolicy !== undefined &&
91
+ spec.defaultPolicy !== "allow" &&
92
+ spec.defaultPolicy !== "ask")
93
+ out.push('defaultPolicy must be "allow" or "ask"');
94
+ if (typeof spec?.inputSchema === "object" && spec.inputSchema !== null)
95
+ out.push(...validateToolPresentation(spec));
96
+ return out;
97
+ }
98
+ const SCHEMA_KEYS = {
99
+ string: ["type", "enum", "maxLength"],
100
+ boolean: ["type"],
101
+ number: ["type", "minimum", "maximum"],
102
+ integer: ["type", "minimum", "maximum"],
103
+ };
104
+ /** 检查 schema 是否落在 ParamSchema 子集内,返回问题列表。 */
105
+ function schemaProblems(schema) {
106
+ if (typeof schema !== "object" || schema === null || Array.isArray(schema))
107
+ return ["schema must be an object"];
108
+ const s = schema;
109
+ const allowed = typeof s.type === "string" ? SCHEMA_KEYS[s.type] : undefined;
110
+ if (!allowed)
111
+ return [`unsupported schema type ${JSON.stringify(s.type)}`];
112
+ const out = [];
113
+ for (const k of Object.keys(s))
114
+ if (!allowed.includes(k))
115
+ out.push(`unsupported schema keyword "${k}"`);
116
+ if ("enum" in s) {
117
+ if (!Array.isArray(s.enum) || s.enum.length === 0 || s.enum.some((x) => typeof x !== "string"))
118
+ out.push("enum must be a non-empty array of strings");
119
+ if ("maxLength" in s)
120
+ out.push("enum and maxLength cannot be combined");
121
+ }
122
+ if ("maxLength" in s && (!Number.isInteger(s.maxLength) || s.maxLength < 0))
123
+ out.push("maxLength must be a non-negative integer");
124
+ for (const k of ["minimum", "maximum"]) {
125
+ if (k in s && (typeof s[k] !== "number" || !Number.isFinite(s[k])))
126
+ out.push(`${k} must be a finite number`);
127
+ }
128
+ if (typeof s.minimum === "number" && typeof s.maximum === "number" && s.minimum > s.maximum)
129
+ out.push("minimum must be <= maximum");
130
+ return out;
131
+ }
132
+ const isEnum = (s) => s.type === "string" && "enum" in s;
133
+ /** 校验交互表单的声明,返回问题列表(空 = 合法)。 */
134
+ export function validateInputForm(form) {
135
+ const out = [];
136
+ if (!form || !Array.isArray(form.fields))
137
+ return ["form.fields must be an array"];
138
+ if (form.fields.length === 0)
139
+ out.push("form must have at least one field");
140
+ if (form.url !== undefined && !isHttpUrl(form.url))
141
+ out.push("url must be an absolute http(s) URL of at most 2000 characters");
142
+ const seen = new Set();
143
+ for (const f of form.fields) {
144
+ const at = `field ${JSON.stringify(f?.key)}`;
145
+ if (typeof f?.key !== "string" || f.key === "") {
146
+ out.push("field key must be a non-empty string");
147
+ continue;
148
+ }
149
+ if (seen.has(f.key))
150
+ out.push(`${at}: duplicate key`);
151
+ seen.add(f.key);
152
+ const probs = schemaProblems(f.schema);
153
+ for (const p of probs)
154
+ out.push(`${at}: ${p}`);
155
+ if (probs.length > 0)
156
+ continue;
157
+ if ((f.multiSelect || f.allowOther) && !isEnum(f.schema))
158
+ out.push(`${at}: multiSelect and allowOther require a string enum schema`);
159
+ }
160
+ return out;
161
+ }
162
+ /** 校验用户提交的答案是否符合表单,返回问题列表(空 = 合法)。 */
163
+ export function validateAnswer(form, answer) {
164
+ const out = [];
165
+ const byKey = new Map(form.fields.map((f) => [f.key, f]));
166
+ for (const k of Object.keys(answer))
167
+ if (!byKey.has(k))
168
+ out.push(`unknown field ${JSON.stringify(k)}`);
169
+ for (const f of form.fields) {
170
+ const at = `field ${JSON.stringify(f.key)}`;
171
+ // 只认答案自身的键:`toString` / `constructor` 等继承属性不算已回答
172
+ const v = Object.hasOwn(answer, f.key) ? answer[f.key] : undefined;
173
+ if (v === undefined) {
174
+ if (f.required)
175
+ out.push(`${at}: is required`);
176
+ continue;
177
+ }
178
+ if (f.multiSelect) {
179
+ if (!Array.isArray(v) || v.some((x) => typeof x !== "string")) {
180
+ out.push(`${at}: must be an array of strings`);
181
+ continue;
182
+ }
183
+ if (f.required && v.length === 0)
184
+ out.push(`${at}: is required`);
185
+ const seen = new Set();
186
+ for (const item of v) {
187
+ if (seen.has(item)) {
188
+ out.push(`${at}: duplicate value ${JSON.stringify(item)}`);
189
+ continue;
190
+ }
191
+ seen.add(item);
192
+ const why = otherOrValue(f, item);
193
+ if (why)
194
+ out.push(`${at}: ${why}`);
195
+ }
196
+ continue;
197
+ }
198
+ if (Array.isArray(v)) {
199
+ out.push(`${at}: must not be an array`);
200
+ continue;
201
+ }
202
+ if (f.required && v === "") {
203
+ out.push(`${at}: is required`);
204
+ continue;
205
+ }
206
+ const why = otherOrValue(f, v);
207
+ if (why)
208
+ out.push(`${at}: ${why}`);
209
+ }
210
+ return out;
211
+ }
212
+ /** allowOther 的枚举字段接受任意非空文本,其余按 schema 校验。 */
213
+ function otherOrValue(f, v) {
214
+ if (f.allowOther && isEnum(f.schema)) {
215
+ return typeof v === "string" && v !== "" ? undefined : "other text must be a non-empty string";
216
+ }
217
+ return checkParamValue(f.schema, v);
218
+ }
package/dist/ui.d.ts ADDED
@@ -0,0 +1,265 @@
1
+ import type { Locale } from "./cards.js";
2
+ import { type FormAnswer, type InputForm } from "./tool.js";
3
+ /**
4
+ * Where a ui contribution goes (UI refresh spec §5.3):
5
+ * - `panel`: a right panel of the conversation page, opened from the extension's icon in the title
6
+ * bar (one at a time);
7
+ * - `section`: a section of the conversation's properties panel;
8
+ * - `inline`: an inline card in the conversation, hanging off a tool result (`ToolResult.card`);
9
+ * - `page`: an org page under "Extensions";
10
+ * - `file-preview`: an embed previewer of `file-preview@1` (embed only, see `FilePreviewer.embed`);
11
+ * - `conversation`: the old right-side tab; it means `panel` (see {@link normalizePlace}).
12
+ */
13
+ export type UiPlace = "conversation" | "panel" | "section" | "inline" | "page" | "file-preview";
14
+ /** A place with the old `conversation` mapped to `panel`. */
15
+ export declare function normalizePlace(place: UiPlace): Exclude<UiPlace, "conversation">;
16
+ /**
17
+ * Icons a contribution may use (design system icon names; `puzzle` is the default and is drawn as
18
+ * the design `extension` icon). The title-bar button of a panel shows it.
19
+ */
20
+ export declare const UI_ICONS: readonly ["list-checks", "layout-grid", "file", "chart", "calendar", "users", "bot", "book", "message-square", "puzzle"];
21
+ export type UiIcon = (typeof UI_ICONS)[number];
22
+ /**
23
+ * An "open" target: a `panel` contribution of the same extension, scrolled to the block or item
24
+ * whose `id` equals `item` (the panel's render receives it as {@link UiRenderArgs.item}).
25
+ */
26
+ export interface UiOpen {
27
+ contribution: string;
28
+ item?: string;
29
+ }
30
+ /** 界面贡献的种类:`view` = 渲染 schema(由我们的组件画);`embed` = 插件自己的页面放进 iframe。 */
31
+ export type UiKind = "view" | "embed";
32
+ /**
33
+ * 插件清单 `ui` 里的一项界面贡献。`id` 必须同时出现在 `permissions.ui.views`(kind view)
34
+ * 或 `permissions.ui.embeds`(kind embed)里;`embed` 的页面由插件路由提供。
35
+ */
36
+ export interface UiContribution {
37
+ id: string;
38
+ title: {
39
+ zh: string;
40
+ en: string;
41
+ };
42
+ place: UiPlace;
43
+ kind: UiKind;
44
+ /** 仅 embed:插件路由下 `/ui/` 开头的路径,须在 `permissions.http` 中声明为 GET。 */
45
+ path?: string;
46
+ /** Icon from {@link UI_ICONS}; default `puzzle`. */
47
+ icon?: UiIcon;
48
+ /**
49
+ * A retired id kept answering for a release: it is not listed, and fetching it renders (or
50
+ * embeds) the named contribution of the same extension and kind. It still needs its entry in
51
+ * `permissions.ui`, and no `ctx.ui.view` of its own.
52
+ */
53
+ aliasOf?: string;
54
+ }
55
+ /**
56
+ * 渲染 schema 的块。文本是给人看的、按原样展示(插件自行决定语言,通常取 `locale`)。
57
+ * `text.markdown` 不支持原生 HTML,链接只允许 http(s) 绝对地址。
58
+ */
59
+ export type UiBlock = {
60
+ type: "heading";
61
+ text: string;
62
+ } | {
63
+ type: "text";
64
+ markdown: string;
65
+ } | {
66
+ type: "checklist";
67
+ items: {
68
+ text: string;
69
+ status: "pending" | "in_progress" | "completed";
70
+ /** Anchor for an open target's `item`. */
71
+ id?: string;
72
+ open?: UiOpen;
73
+ }[];
74
+ } | {
75
+ type: "keyValue";
76
+ items: {
77
+ label: string;
78
+ value: string;
79
+ id?: string;
80
+ open?: UiOpen;
81
+ }[];
82
+ } | {
83
+ type: "table";
84
+ columns: {
85
+ key: string;
86
+ label: string;
87
+ }[];
88
+ /**
89
+ * 每行按列 key 取单元格,可以缺列。渲染时用 `Object.hasOwn(row, key)` 判断单元格是否存在,
90
+ * 不要用 `row[key]` 或 `in`:列 key 可能是 `constructor` / `toString` 之类,会取到原型链上的值。
91
+ */
92
+ rows: Record<string, string>[];
93
+ /** Per row (same index), an open target or null; never a key inside a row. */
94
+ open?: (UiOpen | null)[];
95
+ } | {
96
+ type: "code";
97
+ language?: string;
98
+ text: string;
99
+ } | {
100
+ type: "image";
101
+ url: string;
102
+ alt: string;
103
+ } | {
104
+ type: "chart";
105
+ kind: "bar" | "line";
106
+ series: {
107
+ label: string;
108
+ value: number;
109
+ }[];
110
+ } | {
111
+ type: "actions";
112
+ buttons: {
113
+ actionId: string;
114
+ label: string;
115
+ payload?: unknown;
116
+ danger?: boolean;
117
+ }[];
118
+ } | {
119
+ type: "form";
120
+ form: InputForm;
121
+ actionId: string;
122
+ submitLabel: string;
123
+ };
124
+ /** 插件视图:有上限的块列表;`badge` 显示在标签上(如「2/4」)。 */
125
+ export interface UiView {
126
+ badge?: string;
127
+ /** The ⤢ of an inline card's title row and a properties section's header. */
128
+ open?: UiOpen;
129
+ blocks: UiBlock[];
130
+ }
131
+ /**
132
+ * 视图上限({@link validateUiView} 统一检查):
133
+ * - `blocks`:块数;
134
+ * - `tableRows`:表格行数,也用作 keyValue 项数、表格列数、图表点数、按钮数的上限;
135
+ * - `checklistItems`:清单项数;
136
+ * - `textChars`:整个视图所有文本(含表单 JSON)的字符总数;
137
+ * - `payloadBytes`:单个按钮 `payload` 的 JSON 序列化后 UTF-8 字节数。
138
+ */
139
+ export declare const UI_LIMITS: {
140
+ readonly blocks: 50;
141
+ readonly tableRows: 200;
142
+ readonly checklistItems: 100;
143
+ readonly textChars: 20000;
144
+ readonly payloadBytes: 2000;
145
+ };
146
+ /**
147
+ * 校验插件返回的视图,返回问题列表(空 = 合法)。链接与图片只允许 http(s) 绝对地址;
148
+ * Markdown 的检查是纵深防御,网页渲染时仍须只认 http(s) 链接、不渲染原生 HTML。
149
+ * 按钮与表单的 `actionId` 在整个视图内唯一(动作回调靠它区分来源)。
150
+ */
151
+ export declare function validateUiView(v: unknown): string[];
152
+ /**
153
+ * 调用者在组织内的角色(由高到低 owner / admin / member / guest)。`place: "page"` 的贡献只给 member 以上
154
+ * (内核对 guest 返回 404,插件收不到 guest 的组织页面请求);对话位置的贡献给能访问该对话的任何角色。
155
+ */
156
+ export type UiRole = "owner" | "admin" | "member" | "guest";
157
+ /** 内核调用插件 `render` 时传入的参数(身份由内核鉴权后给出,插件拿不到用户的会话凭据)。 */
158
+ export interface UiRenderArgs {
159
+ orgId: string;
160
+ accountId: string;
161
+ /** 调用者此刻在 `orgId` 的角色(内核按成员关系填,插件可据此收紧按钮等,见 {@link UiRole})。 */
162
+ role: UiRole;
163
+ /** 仅对话视图。 */
164
+ conversationId?: string;
165
+ locale: Locale;
166
+ signal: AbortSignal;
167
+ /** A panel opened from an open target: the block or item `id` to scroll to. */
168
+ item?: string;
169
+ /**
170
+ * An inline card: the tool call it hangs off and the `key` from `ToolResult.card`. Attested by the
171
+ * kernel: given only to an `inline` view, and only when a `tool_result` of `conversationId`
172
+ * recorded this very card (same extension, view and key); scope lookups by `conversationId` too.
173
+ */
174
+ card?: {
175
+ callId: string;
176
+ key?: string;
177
+ };
178
+ }
179
+ /** 视图里的按钮或表单提交:`actionId` 来自块;表单提交时带 `answer`。 */
180
+ export interface UiActionArgs extends UiRenderArgs {
181
+ actionId: string;
182
+ payload?: unknown;
183
+ answer?: FormAnswer;
184
+ }
185
+ /** 动作结果:一条提示,或一份新的视图。 */
186
+ export type UiActionResult = {
187
+ toast?: {
188
+ zh: string;
189
+ en: string;
190
+ };
191
+ } | {
192
+ view: UiView;
193
+ };
194
+ /** 嵌入令牌里的声明(经 `ctx.ui.verifyEmbedToken` 校验后可信)。`exp` 为 Unix 秒。 */
195
+ export interface EmbedClaims {
196
+ plugin: string;
197
+ orgId: string;
198
+ accountId: string;
199
+ /** 调用者的角色:签发时写入;`ctx.ui.verifyEmbedToken` 返回校验时刻的当前角色。 */
200
+ role: UiRole;
201
+ conversationId?: string;
202
+ /** Only a `file-preview` embed's token: the one file (of `conversationId`) it previews. */
203
+ fileId?: string;
204
+ contribution: string;
205
+ exp: number;
206
+ }
207
+ /** 外层页面与嵌入 iframe 之间 `postMessage` 协议的版本标识。 */
208
+ export declare const EMBED_PROTOCOL = "capybara-embed/1";
209
+ /** 外层页面发给 iframe 的消息。 */
210
+ export type EmbedParentMessage = {
211
+ protocol: typeof EMBED_PROTOCOL;
212
+ type: "init";
213
+ token: string;
214
+ locale: Locale;
215
+ theme: "dark" | "light";
216
+ tokens: Record<string, string>;
217
+ context: {
218
+ orgId: string;
219
+ conversationId?: string;
220
+ };
221
+ } | {
222
+ protocol: typeof EMBED_PROTOCOL;
223
+ type: "token";
224
+ token: string;
225
+ } | {
226
+ protocol: typeof EMBED_PROTOCOL;
227
+ type: "theme";
228
+ theme: "dark" | "light";
229
+ tokens: Record<string, string>;
230
+ }
231
+ /**
232
+ * Only to a `file-preview` embed, once, after `init`: the file being previewed. The embed never
233
+ * gets a URL for it.
234
+ */
235
+ | {
236
+ protocol: typeof EMBED_PROTOCOL;
237
+ type: "file";
238
+ name: string;
239
+ contentType: string;
240
+ size: number;
241
+ data: ArrayBuffer;
242
+ };
243
+ /** iframe 发给外层页面的消息。 */
244
+ export type EmbedChildMessage =
245
+ /** 高度为 CSS 像素,0–10000。 */
246
+ {
247
+ protocol: typeof EMBED_PROTOCOL;
248
+ type: "resize";
249
+ height: number;
250
+ }
251
+ /** 只允许站内路径:单个 `/` 开头、没有 `//`、没有 `.` / `..` 段(含 `%2e` 编码)、规范化后不变。 */
252
+ | {
253
+ protocol: typeof EMBED_PROTOCOL;
254
+ type: "navigate";
255
+ path: string;
256
+ } | {
257
+ protocol: typeof EMBED_PROTOCOL;
258
+ type: "toast";
259
+ text: string;
260
+ };
261
+ /**
262
+ * 解析 iframe 发来的消息:协议、类型、字段都对才返回(只留已知字段的新对象),否则返回 null。
263
+ * 外层页面还须先核对消息来源是这个 iframe。
264
+ */
265
+ export declare function parseEmbedChildMessage(m: unknown): EmbedChildMessage | null;