@zkly-superbuddy/embed-sdk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.esm.js +142 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.umd.js +151 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Agent OS Embed SDK
|
|
2
|
+
|
|
3
|
+
Lightweight SDK for embedding Agent OS conversations in external web applications.
|
|
4
|
+
The host page creates an iframe that loads the standalone frontend-embed chat page
|
|
5
|
+
and communicates with it over `postMessage`.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @agentos/embed-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
`createOpenConversation(config)` exchanges `appKey`/`appSecret` for an embed token
|
|
16
|
+
(`POST {apiUrl}/open/v1/embed/session`) and returns a `Promise<EmbedInstance>`
|
|
17
|
+
that resolves once the iframe has loaded and the embed page has posted its `ready`
|
|
18
|
+
event. If the token exchange fails the promise rejects; if the ready event does
|
|
19
|
+
not arrive within `readyTimeoutMs` (default 15000 ms) the promise rejects and the
|
|
20
|
+
iframe is removed. Invalid configuration (missing container, mismatched origin)
|
|
21
|
+
throws synchronously.
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
import { createOpenConversation } from '@agentos/embed-sdk';
|
|
25
|
+
|
|
26
|
+
const instance = await createOpenConversation({
|
|
27
|
+
appKey: '<app-key>',
|
|
28
|
+
appSecret: '<app-secret>',
|
|
29
|
+
container: '#agent-os-container',
|
|
30
|
+
expert: '42', // optional project/expert id
|
|
31
|
+
envVars: { MY_FLAG: 'on' }, // optional env vars for the task
|
|
32
|
+
autoResize: true,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
instance.on('state', (event) => {
|
|
36
|
+
console.log('task state', event.payload);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
instance.on('close', () => instance.destroy());
|
|
40
|
+
|
|
41
|
+
// Send a control command to the embed page.
|
|
42
|
+
instance.postMessage('close');
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Configuration
|
|
46
|
+
|
|
47
|
+
| Option | Description |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `appKey` | Application key (the tenant API key `name`). Used to exchange the embed token. |
|
|
50
|
+
| `appSecret` | Application secret (the plaintext key returned once at API key creation). |
|
|
51
|
+
| `embedUrl` | URL of the deployed embed page. Defaults to `https://superbuddy.top/open-conversation/`. |
|
|
52
|
+
| `apiUrl` | Base URL of the Agent OS API for token exchange. Defaults to `https://superbuddy.top/company-os`. |
|
|
53
|
+
| `container` | HTMLElement or CSS selector the iframe is appended to. |
|
|
54
|
+
| `title` | Optional title, appended as `title`. Shown above the input box while the conversation has no messages. |
|
|
55
|
+
| `expert` | Optional expert/project id, appended as `expert`. Effective only when the first message creates the task. |
|
|
56
|
+
| `envVars` | Optional string map, appended as `env_vars` (JSON). Effective only when the first message creates the task. |
|
|
57
|
+
| `capabilities` | Optional capability map, appended as `capabilities` (JSON). |
|
|
58
|
+
| `conversationId` | Optional conversation id, appended as `conversation_id`. |
|
|
59
|
+
| `origin` | Expected embed origin. Defaults to the origin of `embedUrl`. Use `'*'` to disable origin checks (not recommended). |
|
|
60
|
+
| `width` / `height` | Initial iframe size. Defaults: `100%` width; height is `100%` when `autoResize` is on, otherwise `600px`. |
|
|
61
|
+
| `autoResize` | When `true`, the iframe fills its container (width and height `100%`). The `resize` event is still forwarded to the host but no longer changes the iframe height. |
|
|
62
|
+
### Events (iframe → host)
|
|
63
|
+
|
|
64
|
+
All events are `{ version: 1, type, conversation_id?, payload?, error? }` objects.
|
|
65
|
+
Subscribe with `instance.on(type, callback)` or `instance.on('*', callback)`;
|
|
66
|
+
`on()` returns an unsubscribe function.
|
|
67
|
+
|
|
68
|
+
| Type | Meaning |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| `ready` | Embed page loaded and initialized; resolves the `createOpenConversation` promise. |
|
|
71
|
+
| `session` | Session/conversation established; carries `conversation_id`. |
|
|
72
|
+
| `state` | Task/conversation state changed; details in `payload`. |
|
|
73
|
+
| `error` | An error occurred; details in `error.code` / `error.message`. |
|
|
74
|
+
| `close` | The embed requests to be closed; call `instance.destroy()`. |
|
|
75
|
+
| `resize` | Content height changed; `payload.height` (px). Forwarded to the host; the SDK does not resize the iframe itself. |
|
|
76
|
+
|
|
77
|
+
### Host → iframe
|
|
78
|
+
|
|
79
|
+
`instance.postMessage(type, payload?)` sends `{ version: 1, type, payload }` to the
|
|
80
|
+
iframe (e.g. `close` and other control commands).
|
|
81
|
+
|
|
82
|
+
## Security
|
|
83
|
+
|
|
84
|
+
Incoming messages are accepted only from the created iframe's `contentWindow` and,
|
|
85
|
+
unless `origin: '*'` is configured, only when `message.origin` matches the embed
|
|
86
|
+
origin. Outgoing messages are targeted at the same origin. Prefer the default
|
|
87
|
+
strict origin checking over `'*'`.
|
|
88
|
+
|
|
89
|
+
## Development
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm install
|
|
93
|
+
npm run build # rollup: dist/index.umd.js + dist/index.esm.js + dist/index.d.ts
|
|
94
|
+
npm run typecheck # tsc --noEmit
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent OS Embed SDK
|
|
3
|
+
*
|
|
4
|
+
* Lightweight SDK for embedding Agent OS conversations in external web applications.
|
|
5
|
+
* The host page provides app_key/app_secret; the SDK exchanges them for an embed
|
|
6
|
+
* token via POST {apiUrl}/open/v1/embed/session, then creates an iframe loading
|
|
7
|
+
* the standalone embed chat page and communicates with it over postMessage.
|
|
8
|
+
*/
|
|
9
|
+
export interface OpenConversationConfig {
|
|
10
|
+
/** 应用 Key(租户 API 密钥的 name) */
|
|
11
|
+
appKey: string;
|
|
12
|
+
/** 应用 Secret(创建密钥时返回的明文 key) */
|
|
13
|
+
appSecret: string;
|
|
14
|
+
/** URL of the deployed embed page. Defaults to https://superbuddy.top/open-conversation/ */
|
|
15
|
+
embedUrl?: string;
|
|
16
|
+
/** Base URL of the Agent OS API used for token exchange. Defaults to https://superbuddy.top/company-os */
|
|
17
|
+
apiUrl?: string;
|
|
18
|
+
container: HTMLElement | string;
|
|
19
|
+
/** 标题,仅在对话还没有内容时显示在输入框上方 */
|
|
20
|
+
title?: string;
|
|
21
|
+
/** 专家归属(个人项目 project_id),仅首条消息创建任务时生效 */
|
|
22
|
+
expert?: string;
|
|
23
|
+
/** 环境变量字典,传递给本地任务的 task_vars,仅首条消息创建任务时生效 */
|
|
24
|
+
envVars?: Record<string, string>;
|
|
25
|
+
capabilities?: Record<string, unknown>;
|
|
26
|
+
conversationId?: string;
|
|
27
|
+
origin?: string;
|
|
28
|
+
width?: string;
|
|
29
|
+
height?: string;
|
|
30
|
+
sandbox?: string;
|
|
31
|
+
allow?: string;
|
|
32
|
+
/** Fill the parent container (width/height 100%) instead of tracking content height. */
|
|
33
|
+
autoResize?: boolean;
|
|
34
|
+
/** Maximum time (ms) to wait for the embed 'ready' event before rejecting. Defaults to 15000. */
|
|
35
|
+
readyTimeoutMs?: number;
|
|
36
|
+
onReady?: (event: EmbedEvent) => void;
|
|
37
|
+
onEvent?: (event: EmbedEvent) => void;
|
|
38
|
+
onError?: (event: EmbedEvent) => void;
|
|
39
|
+
onClose?: (event: EmbedEvent) => void;
|
|
40
|
+
/** Optional callback invoked when the iframe has loaded. */
|
|
41
|
+
onLoad?: () => void;
|
|
42
|
+
}
|
|
43
|
+
export type EmbedEventType = 'ready' | 'session' | 'state' | 'error' | 'close' | 'resize';
|
|
44
|
+
export interface EmbedEvent {
|
|
45
|
+
version: 1;
|
|
46
|
+
type: EmbedEventType;
|
|
47
|
+
conversation_id?: string;
|
|
48
|
+
payload?: Record<string, unknown>;
|
|
49
|
+
error?: {
|
|
50
|
+
code: string;
|
|
51
|
+
message: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export interface EmbedInstance {
|
|
55
|
+
iframe: HTMLIFrameElement;
|
|
56
|
+
open(): void;
|
|
57
|
+
destroy(): void;
|
|
58
|
+
on(type: EmbedEventType | '*', callback: (event: EmbedEvent) => void): () => void;
|
|
59
|
+
postMessage(type: string, payload?: Record<string, unknown>): void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Version information
|
|
63
|
+
*/
|
|
64
|
+
export declare const VERSION = "0.2.0";
|
|
65
|
+
/**
|
|
66
|
+
* Exchanges app_key/app_secret for an embed token, creates the embed iframe and
|
|
67
|
+
* resolves once the embed page posts its 'ready' event (validated against the
|
|
68
|
+
* iframe window and origin). Rejects if the token exchange fails or the ready
|
|
69
|
+
* event does not arrive within `readyTimeoutMs` (default 15s).
|
|
70
|
+
*/
|
|
71
|
+
export declare function createOpenConversation(config: OpenConversationConfig): Promise<EmbedInstance>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent OS Embed SDK
|
|
3
|
+
*
|
|
4
|
+
* Lightweight SDK for embedding Agent OS conversations in external web applications.
|
|
5
|
+
* The host page provides app_key/app_secret; the SDK exchanges them for an embed
|
|
6
|
+
* token via POST {apiUrl}/open/v1/embed/session, then creates an iframe loading
|
|
7
|
+
* the standalone embed chat page and communicates with it over postMessage.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Version information
|
|
11
|
+
*/
|
|
12
|
+
const VERSION = '0.2.0';
|
|
13
|
+
const DEFAULT_READY_TIMEOUT_MS = 15000;
|
|
14
|
+
const DEFAULT_EMBED_URL = 'https://superbuddy.top/open-conversation/';
|
|
15
|
+
const DEFAULT_API_URL = 'https://superbuddy.top/company-os';
|
|
16
|
+
/**
|
|
17
|
+
* Exchanges app_key/app_secret for an embed token, creates the embed iframe and
|
|
18
|
+
* resolves once the embed page posts its 'ready' event (validated against the
|
|
19
|
+
* iframe window and origin). Rejects if the token exchange fails or the ready
|
|
20
|
+
* event does not arrive within `readyTimeoutMs` (default 15s).
|
|
21
|
+
*/
|
|
22
|
+
async function createOpenConversation(config) {
|
|
23
|
+
if (!config.appKey || !config.appSecret)
|
|
24
|
+
throw new Error('appKey and appSecret are required');
|
|
25
|
+
const resolved = { ...config, embedUrl: config.embedUrl || DEFAULT_EMBED_URL };
|
|
26
|
+
const token = await exchangeEmbedToken(config);
|
|
27
|
+
const instance = buildInstance(resolved, token);
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
let settled = false;
|
|
30
|
+
const offReady = instance.on('ready', () => {
|
|
31
|
+
settled = true;
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
offReady();
|
|
34
|
+
resolve(instance);
|
|
35
|
+
});
|
|
36
|
+
const timer = setTimeout(() => {
|
|
37
|
+
if (settled)
|
|
38
|
+
return;
|
|
39
|
+
offReady();
|
|
40
|
+
instance.destroy();
|
|
41
|
+
reject(new Error(`Embed did not become ready within ${config.readyTimeoutMs || DEFAULT_READY_TIMEOUT_MS}ms`));
|
|
42
|
+
}, config.readyTimeoutMs || DEFAULT_READY_TIMEOUT_MS);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function exchangeEmbedToken(config) {
|
|
46
|
+
const endpoint = `${(config.apiUrl || DEFAULT_API_URL).replace(/\/+$/, '')}/open/v1/embed/session`;
|
|
47
|
+
let response;
|
|
48
|
+
try {
|
|
49
|
+
response = await fetch(endpoint, {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: { 'Content-Type': 'application/json' },
|
|
52
|
+
body: JSON.stringify({ app_key: config.appKey, app_secret: config.appSecret }),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw new Error(`Embed token request failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
57
|
+
}
|
|
58
|
+
const data = await response.json().catch(() => undefined);
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const detail = typeof data?.detail === 'string' ? data.detail : response.statusText;
|
|
61
|
+
throw new Error(`Embed token exchange failed (HTTP ${response.status}): ${detail}`);
|
|
62
|
+
}
|
|
63
|
+
if (typeof data?.access_token !== 'string' || !data.access_token) {
|
|
64
|
+
throw new Error('Embed token exchange response is missing access_token');
|
|
65
|
+
}
|
|
66
|
+
return data.access_token;
|
|
67
|
+
}
|
|
68
|
+
function buildInstance(config, token) {
|
|
69
|
+
if (typeof document === 'undefined')
|
|
70
|
+
throw new Error('createOpenConversation requires a browser');
|
|
71
|
+
const container = typeof config.container === 'string' ? document.querySelector(config.container) : config.container;
|
|
72
|
+
if (!(container instanceof HTMLElement))
|
|
73
|
+
throw new Error('Embed container was not found');
|
|
74
|
+
const targetOrigin = config.origin || new URL(config.embedUrl, window.location.href).origin;
|
|
75
|
+
if (targetOrigin !== '*' && new URL(config.embedUrl, window.location.href).origin !== targetOrigin) {
|
|
76
|
+
throw new Error('Embed origin must match embedUrl origin');
|
|
77
|
+
}
|
|
78
|
+
const iframe = document.createElement('iframe');
|
|
79
|
+
iframe.src = buildUrl(config, token);
|
|
80
|
+
iframe.title = 'Agent OS conversation';
|
|
81
|
+
iframe.style.width = config.width || '100%';
|
|
82
|
+
iframe.style.height = config.height || '100%';
|
|
83
|
+
iframe.style.border = '0';
|
|
84
|
+
iframe.setAttribute('allow', config.allow || 'clipboard-write');
|
|
85
|
+
iframe.setAttribute('sandbox', config.sandbox || 'allow-scripts allow-forms allow-same-origin');
|
|
86
|
+
const listeners = new Map();
|
|
87
|
+
let active = true;
|
|
88
|
+
const emit = (event) => {
|
|
89
|
+
listeners.get(event.type)?.forEach((callback) => callback(event));
|
|
90
|
+
listeners.get('*')?.forEach((callback) => callback(event));
|
|
91
|
+
if (event.type === 'ready')
|
|
92
|
+
config.onReady?.(event);
|
|
93
|
+
if (event.type === 'error')
|
|
94
|
+
config.onError?.(event);
|
|
95
|
+
if (event.type === 'close')
|
|
96
|
+
config.onClose?.(event);
|
|
97
|
+
config.onEvent?.(event);
|
|
98
|
+
};
|
|
99
|
+
const receive = (message) => {
|
|
100
|
+
if (!active || message.source !== iframe.contentWindow || (targetOrigin !== '*' && message.origin !== targetOrigin))
|
|
101
|
+
return;
|
|
102
|
+
const event = message.data?.type && message.data?.version === 1 ? message.data : undefined;
|
|
103
|
+
if (!event)
|
|
104
|
+
return;
|
|
105
|
+
// autoResize 模式下 iframe 始终 100% 占满父容器,不再按内容高度调整;
|
|
106
|
+
// resize 事件仍透传给宿主,由宿主自行决定是否响应。
|
|
107
|
+
emit(event);
|
|
108
|
+
};
|
|
109
|
+
iframe.addEventListener('load', () => config.onLoad?.(), { once: true });
|
|
110
|
+
window.addEventListener('message', receive);
|
|
111
|
+
const open = () => { if (!iframe.isConnected)
|
|
112
|
+
container.appendChild(iframe); };
|
|
113
|
+
const destroy = () => { if (!active)
|
|
114
|
+
return; active = false; window.removeEventListener('message', receive); listeners.clear(); iframe.remove(); };
|
|
115
|
+
open();
|
|
116
|
+
const postMessage = (type, payload = {}) => {
|
|
117
|
+
if (!active || !iframe.contentWindow)
|
|
118
|
+
return;
|
|
119
|
+
iframe.contentWindow.postMessage({ version: 1, type, payload }, targetOrigin === '*' ? '*' : targetOrigin);
|
|
120
|
+
};
|
|
121
|
+
return { iframe, open, destroy, postMessage, on(type, callback) { const set = listeners.get(type) || new Set(); set.add(callback); listeners.set(type, set); return () => set.delete(callback); } };
|
|
122
|
+
}
|
|
123
|
+
function buildUrl(config, token) {
|
|
124
|
+
const url = new URL(config.embedUrl, window.location.href);
|
|
125
|
+
url.searchParams.set('token', token);
|
|
126
|
+
if (config.conversationId)
|
|
127
|
+
url.searchParams.set('conversation_id', config.conversationId);
|
|
128
|
+
if (config.capabilities)
|
|
129
|
+
url.searchParams.set('capabilities', JSON.stringify(config.capabilities));
|
|
130
|
+
if (config.title)
|
|
131
|
+
url.searchParams.set('title', config.title);
|
|
132
|
+
if (config.expert)
|
|
133
|
+
url.searchParams.set('expert', config.expert);
|
|
134
|
+
if (config.envVars)
|
|
135
|
+
url.searchParams.set('env_vars', JSON.stringify(config.envVars));
|
|
136
|
+
// 显式传入宿主 origin,embed 页据此确定 postMessage 目标,不再依赖 document.referrer。
|
|
137
|
+
url.searchParams.set('origin', window.location.origin);
|
|
138
|
+
return url.toString();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export { VERSION, createOpenConversation };
|
|
142
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * Agent OS Embed SDK\n *\n * Lightweight SDK for embedding Agent OS conversations in external web applications.\n * The host page provides app_key/app_secret; the SDK exchanges them for an embed\n * token via POST {apiUrl}/open/v1/embed/session, then creates an iframe loading\n * the standalone embed chat page and communicates with it over postMessage.\n */\n\nexport interface OpenConversationConfig {\n /** 应用 Key(租户 API 密钥的 name) */\n appKey: string;\n /** 应用 Secret(创建密钥时返回的明文 key) */\n appSecret: string;\n /** URL of the deployed embed page. Defaults to https://superbuddy.top/open-conversation/ */\n embedUrl?: string;\n /** Base URL of the Agent OS API used for token exchange. Defaults to https://superbuddy.top/company-os */\n apiUrl?: string;\n container: HTMLElement | string;\n /** 标题,仅在对话还没有内容时显示在输入框上方 */\n title?: string;\n /** 专家归属(个人项目 project_id),仅首条消息创建任务时生效 */\n expert?: string;\n /** 环境变量字典,传递给本地任务的 task_vars,仅首条消息创建任务时生效 */\n envVars?: Record<string, string>;\n capabilities?: Record<string, unknown>;\n conversationId?: string;\n origin?: string;\n width?: string;\n height?: string;\n sandbox?: string;\n allow?: string;\n /** Fill the parent container (width/height 100%) instead of tracking content height. */\n autoResize?: boolean;\n /** Maximum time (ms) to wait for the embed 'ready' event before rejecting. Defaults to 15000. */\n readyTimeoutMs?: number;\n onReady?: (event: EmbedEvent) => void;\n onEvent?: (event: EmbedEvent) => void;\n onError?: (event: EmbedEvent) => void;\n onClose?: (event: EmbedEvent) => void;\n /** Optional callback invoked when the iframe has loaded. */\n onLoad?: () => void;\n}\n\nexport type EmbedEventType = 'ready' | 'session' | 'state' | 'error' | 'close' | 'resize';\nexport interface EmbedEvent {\n version: 1;\n type: EmbedEventType;\n conversation_id?: string;\n payload?: Record<string, unknown>;\n error?: { code: string; message: string };\n}\n\nexport interface EmbedInstance {\n iframe: HTMLIFrameElement;\n open(): void;\n destroy(): void;\n on(type: EmbedEventType | '*', callback: (event: EmbedEvent) => void): () => void;\n postMessage(type: string, payload?: Record<string, unknown>): void;\n}\n\n/**\n * Version information\n */\nexport const VERSION = '0.2.0';\n\nconst DEFAULT_READY_TIMEOUT_MS = 15000;\nconst DEFAULT_EMBED_URL = 'https://superbuddy.top/open-conversation/';\nconst DEFAULT_API_URL = 'https://superbuddy.top/company-os';\n\ntype ResolvedConfig = OpenConversationConfig & { embedUrl: string };\n\n/**\n * Exchanges app_key/app_secret for an embed token, creates the embed iframe and\n * resolves once the embed page posts its 'ready' event (validated against the\n * iframe window and origin). Rejects if the token exchange fails or the ready\n * event does not arrive within `readyTimeoutMs` (default 15s).\n */\nexport async function createOpenConversation(config: OpenConversationConfig): Promise<EmbedInstance> {\n if (!config.appKey || !config.appSecret) throw new Error('appKey and appSecret are required');\n const resolved: ResolvedConfig = { ...config, embedUrl: config.embedUrl || DEFAULT_EMBED_URL };\n const token = await exchangeEmbedToken(config);\n const instance = buildInstance(resolved, token);\n return new Promise<EmbedInstance>((resolve, reject) => {\n let settled = false;\n const offReady = instance.on('ready', () => {\n settled = true;\n clearTimeout(timer);\n offReady();\n resolve(instance);\n });\n const timer = setTimeout(() => {\n if (settled) return;\n offReady();\n instance.destroy();\n reject(new Error(`Embed did not become ready within ${config.readyTimeoutMs || DEFAULT_READY_TIMEOUT_MS}ms`));\n }, config.readyTimeoutMs || DEFAULT_READY_TIMEOUT_MS);\n });\n}\n\nasync function exchangeEmbedToken(config: OpenConversationConfig): Promise<string> {\n const endpoint = `${(config.apiUrl || DEFAULT_API_URL).replace(/\\/+$/, '')}/open/v1/embed/session`;\n let response: Response;\n try {\n response = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ app_key: config.appKey, app_secret: config.appSecret }),\n });\n } catch (error) {\n throw new Error(`Embed token request failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n const data = await response.json().catch(() => undefined) as { access_token?: unknown; detail?: unknown } | undefined;\n if (!response.ok) {\n const detail = typeof data?.detail === 'string' ? data.detail : response.statusText;\n throw new Error(`Embed token exchange failed (HTTP ${response.status}): ${detail}`);\n }\n if (typeof data?.access_token !== 'string' || !data.access_token) {\n throw new Error('Embed token exchange response is missing access_token');\n }\n return data.access_token;\n}\n\nfunction buildInstance(config: ResolvedConfig, token: string): EmbedInstance {\n if (typeof document === 'undefined') throw new Error('createOpenConversation requires a browser');\n const container = typeof config.container === 'string' ? document.querySelector(config.container) : config.container;\n if (!(container instanceof HTMLElement)) throw new Error('Embed container was not found');\n const targetOrigin = config.origin || new URL(config.embedUrl, window.location.href).origin;\n if (targetOrigin !== '*' && new URL(config.embedUrl, window.location.href).origin !== targetOrigin) {\n throw new Error('Embed origin must match embedUrl origin');\n }\n const iframe = document.createElement('iframe');\n iframe.src = buildUrl(config, token);\n iframe.title = 'Agent OS conversation';\n iframe.style.width = config.width || '100%';\n iframe.style.height = config.height || '100%';\n iframe.style.border = '0';\n iframe.setAttribute('allow', config.allow || 'clipboard-write');\n iframe.setAttribute('sandbox', config.sandbox || 'allow-scripts allow-forms allow-same-origin');\n const listeners = new Map<string, Set<(event: EmbedEvent) => void>>();\n let active = true;\n const emit = (event: EmbedEvent) => {\n listeners.get(event.type)?.forEach((callback) => callback(event));\n listeners.get('*')?.forEach((callback) => callback(event));\n if (event.type === 'ready') config.onReady?.(event);\n if (event.type === 'error') config.onError?.(event);\n if (event.type === 'close') config.onClose?.(event);\n config.onEvent?.(event);\n };\n const receive = (message: MessageEvent) => {\n if (!active || message.source !== iframe.contentWindow || (targetOrigin !== '*' && message.origin !== targetOrigin)) return;\n const event = message.data?.type && message.data?.version === 1 ? message.data as EmbedEvent : undefined;\n if (!event) return;\n // autoResize 模式下 iframe 始终 100% 占满父容器,不再按内容高度调整;\n // resize 事件仍透传给宿主,由宿主自行决定是否响应。\n emit(event);\n };\n iframe.addEventListener('load', () => config.onLoad?.(), { once: true });\n window.addEventListener('message', receive);\n const open = () => { if (!iframe.isConnected) container.appendChild(iframe); };\n const destroy = () => { if (!active) return; active = false; window.removeEventListener('message', receive); listeners.clear(); iframe.remove(); };\n open();\n const postMessage = (type: string, payload: Record<string, unknown> = {}) => {\n if (!active || !iframe.contentWindow) return;\n iframe.contentWindow.postMessage({ version: 1, type, payload }, targetOrigin === '*' ? '*' : targetOrigin);\n };\n return { iframe, open, destroy, postMessage, on(type, callback) { const set = listeners.get(type) || new Set(); set.add(callback); listeners.set(type, set); return () => set.delete(callback); } };\n}\n\nfunction buildUrl(config: ResolvedConfig, token: string): string {\n const url = new URL(config.embedUrl, window.location.href);\n url.searchParams.set('token', token);\n if (config.conversationId) url.searchParams.set('conversation_id', config.conversationId);\n if (config.capabilities) url.searchParams.set('capabilities', JSON.stringify(config.capabilities));\n if (config.title) url.searchParams.set('title', config.title);\n if (config.expert) url.searchParams.set('expert', config.expert);\n if (config.envVars) url.searchParams.set('env_vars', JSON.stringify(config.envVars));\n // 显式传入宿主 origin,embed 页据此确定 postMessage 目标,不再依赖 document.referrer。\n url.searchParams.set('origin', window.location.origin);\n return url.toString();\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.AgentOSEmbed = {}));
|
|
5
|
+
})(this, (function (exports) { 'use strict';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Agent OS Embed SDK
|
|
9
|
+
*
|
|
10
|
+
* Lightweight SDK for embedding Agent OS conversations in external web applications.
|
|
11
|
+
* The host page provides app_key/app_secret; the SDK exchanges them for an embed
|
|
12
|
+
* token via POST {apiUrl}/open/v1/embed/session, then creates an iframe loading
|
|
13
|
+
* the standalone embed chat page and communicates with it over postMessage.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Version information
|
|
17
|
+
*/
|
|
18
|
+
const VERSION = '0.2.0';
|
|
19
|
+
const DEFAULT_READY_TIMEOUT_MS = 15000;
|
|
20
|
+
const DEFAULT_EMBED_URL = 'https://superbuddy.top/open-conversation/';
|
|
21
|
+
const DEFAULT_API_URL = 'https://superbuddy.top/company-os';
|
|
22
|
+
/**
|
|
23
|
+
* Exchanges app_key/app_secret for an embed token, creates the embed iframe and
|
|
24
|
+
* resolves once the embed page posts its 'ready' event (validated against the
|
|
25
|
+
* iframe window and origin). Rejects if the token exchange fails or the ready
|
|
26
|
+
* event does not arrive within `readyTimeoutMs` (default 15s).
|
|
27
|
+
*/
|
|
28
|
+
async function createOpenConversation(config) {
|
|
29
|
+
if (!config.appKey || !config.appSecret)
|
|
30
|
+
throw new Error('appKey and appSecret are required');
|
|
31
|
+
const resolved = { ...config, embedUrl: config.embedUrl || DEFAULT_EMBED_URL };
|
|
32
|
+
const token = await exchangeEmbedToken(config);
|
|
33
|
+
const instance = buildInstance(resolved, token);
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
let settled = false;
|
|
36
|
+
const offReady = instance.on('ready', () => {
|
|
37
|
+
settled = true;
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
offReady();
|
|
40
|
+
resolve(instance);
|
|
41
|
+
});
|
|
42
|
+
const timer = setTimeout(() => {
|
|
43
|
+
if (settled)
|
|
44
|
+
return;
|
|
45
|
+
offReady();
|
|
46
|
+
instance.destroy();
|
|
47
|
+
reject(new Error(`Embed did not become ready within ${config.readyTimeoutMs || DEFAULT_READY_TIMEOUT_MS}ms`));
|
|
48
|
+
}, config.readyTimeoutMs || DEFAULT_READY_TIMEOUT_MS);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async function exchangeEmbedToken(config) {
|
|
52
|
+
const endpoint = `${(config.apiUrl || DEFAULT_API_URL).replace(/\/+$/, '')}/open/v1/embed/session`;
|
|
53
|
+
let response;
|
|
54
|
+
try {
|
|
55
|
+
response = await fetch(endpoint, {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers: { 'Content-Type': 'application/json' },
|
|
58
|
+
body: JSON.stringify({ app_key: config.appKey, app_secret: config.appSecret }),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
throw new Error(`Embed token request failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
63
|
+
}
|
|
64
|
+
const data = await response.json().catch(() => undefined);
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
const detail = typeof data?.detail === 'string' ? data.detail : response.statusText;
|
|
67
|
+
throw new Error(`Embed token exchange failed (HTTP ${response.status}): ${detail}`);
|
|
68
|
+
}
|
|
69
|
+
if (typeof data?.access_token !== 'string' || !data.access_token) {
|
|
70
|
+
throw new Error('Embed token exchange response is missing access_token');
|
|
71
|
+
}
|
|
72
|
+
return data.access_token;
|
|
73
|
+
}
|
|
74
|
+
function buildInstance(config, token) {
|
|
75
|
+
if (typeof document === 'undefined')
|
|
76
|
+
throw new Error('createOpenConversation requires a browser');
|
|
77
|
+
const container = typeof config.container === 'string' ? document.querySelector(config.container) : config.container;
|
|
78
|
+
if (!(container instanceof HTMLElement))
|
|
79
|
+
throw new Error('Embed container was not found');
|
|
80
|
+
const targetOrigin = config.origin || new URL(config.embedUrl, window.location.href).origin;
|
|
81
|
+
if (targetOrigin !== '*' && new URL(config.embedUrl, window.location.href).origin !== targetOrigin) {
|
|
82
|
+
throw new Error('Embed origin must match embedUrl origin');
|
|
83
|
+
}
|
|
84
|
+
const iframe = document.createElement('iframe');
|
|
85
|
+
iframe.src = buildUrl(config, token);
|
|
86
|
+
iframe.title = 'Agent OS conversation';
|
|
87
|
+
iframe.style.width = config.width || '100%';
|
|
88
|
+
iframe.style.height = config.height || '100%';
|
|
89
|
+
iframe.style.border = '0';
|
|
90
|
+
iframe.setAttribute('allow', config.allow || 'clipboard-write');
|
|
91
|
+
iframe.setAttribute('sandbox', config.sandbox || 'allow-scripts allow-forms allow-same-origin');
|
|
92
|
+
const listeners = new Map();
|
|
93
|
+
let active = true;
|
|
94
|
+
const emit = (event) => {
|
|
95
|
+
listeners.get(event.type)?.forEach((callback) => callback(event));
|
|
96
|
+
listeners.get('*')?.forEach((callback) => callback(event));
|
|
97
|
+
if (event.type === 'ready')
|
|
98
|
+
config.onReady?.(event);
|
|
99
|
+
if (event.type === 'error')
|
|
100
|
+
config.onError?.(event);
|
|
101
|
+
if (event.type === 'close')
|
|
102
|
+
config.onClose?.(event);
|
|
103
|
+
config.onEvent?.(event);
|
|
104
|
+
};
|
|
105
|
+
const receive = (message) => {
|
|
106
|
+
if (!active || message.source !== iframe.contentWindow || (targetOrigin !== '*' && message.origin !== targetOrigin))
|
|
107
|
+
return;
|
|
108
|
+
const event = message.data?.type && message.data?.version === 1 ? message.data : undefined;
|
|
109
|
+
if (!event)
|
|
110
|
+
return;
|
|
111
|
+
// autoResize 模式下 iframe 始终 100% 占满父容器,不再按内容高度调整;
|
|
112
|
+
// resize 事件仍透传给宿主,由宿主自行决定是否响应。
|
|
113
|
+
emit(event);
|
|
114
|
+
};
|
|
115
|
+
iframe.addEventListener('load', () => config.onLoad?.(), { once: true });
|
|
116
|
+
window.addEventListener('message', receive);
|
|
117
|
+
const open = () => { if (!iframe.isConnected)
|
|
118
|
+
container.appendChild(iframe); };
|
|
119
|
+
const destroy = () => { if (!active)
|
|
120
|
+
return; active = false; window.removeEventListener('message', receive); listeners.clear(); iframe.remove(); };
|
|
121
|
+
open();
|
|
122
|
+
const postMessage = (type, payload = {}) => {
|
|
123
|
+
if (!active || !iframe.contentWindow)
|
|
124
|
+
return;
|
|
125
|
+
iframe.contentWindow.postMessage({ version: 1, type, payload }, targetOrigin === '*' ? '*' : targetOrigin);
|
|
126
|
+
};
|
|
127
|
+
return { iframe, open, destroy, postMessage, on(type, callback) { const set = listeners.get(type) || new Set(); set.add(callback); listeners.set(type, set); return () => set.delete(callback); } };
|
|
128
|
+
}
|
|
129
|
+
function buildUrl(config, token) {
|
|
130
|
+
const url = new URL(config.embedUrl, window.location.href);
|
|
131
|
+
url.searchParams.set('token', token);
|
|
132
|
+
if (config.conversationId)
|
|
133
|
+
url.searchParams.set('conversation_id', config.conversationId);
|
|
134
|
+
if (config.capabilities)
|
|
135
|
+
url.searchParams.set('capabilities', JSON.stringify(config.capabilities));
|
|
136
|
+
if (config.title)
|
|
137
|
+
url.searchParams.set('title', config.title);
|
|
138
|
+
if (config.expert)
|
|
139
|
+
url.searchParams.set('expert', config.expert);
|
|
140
|
+
if (config.envVars)
|
|
141
|
+
url.searchParams.set('env_vars', JSON.stringify(config.envVars));
|
|
142
|
+
// 显式传入宿主 origin,embed 页据此确定 postMessage 目标,不再依赖 document.referrer。
|
|
143
|
+
url.searchParams.set('origin', window.location.origin);
|
|
144
|
+
return url.toString();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
exports.VERSION = VERSION;
|
|
148
|
+
exports.createOpenConversation = createOpenConversation;
|
|
149
|
+
|
|
150
|
+
}));
|
|
151
|
+
//# sourceMappingURL=index.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * Agent OS Embed SDK\n *\n * Lightweight SDK for embedding Agent OS conversations in external web applications.\n * The host page provides app_key/app_secret; the SDK exchanges them for an embed\n * token via POST {apiUrl}/open/v1/embed/session, then creates an iframe loading\n * the standalone embed chat page and communicates with it over postMessage.\n */\n\nexport interface OpenConversationConfig {\n /** 应用 Key(租户 API 密钥的 name) */\n appKey: string;\n /** 应用 Secret(创建密钥时返回的明文 key) */\n appSecret: string;\n /** URL of the deployed embed page. Defaults to https://superbuddy.top/open-conversation/ */\n embedUrl?: string;\n /** Base URL of the Agent OS API used for token exchange. Defaults to https://superbuddy.top/company-os */\n apiUrl?: string;\n container: HTMLElement | string;\n /** 标题,仅在对话还没有内容时显示在输入框上方 */\n title?: string;\n /** 专家归属(个人项目 project_id),仅首条消息创建任务时生效 */\n expert?: string;\n /** 环境变量字典,传递给本地任务的 task_vars,仅首条消息创建任务时生效 */\n envVars?: Record<string, string>;\n capabilities?: Record<string, unknown>;\n conversationId?: string;\n origin?: string;\n width?: string;\n height?: string;\n sandbox?: string;\n allow?: string;\n /** Fill the parent container (width/height 100%) instead of tracking content height. */\n autoResize?: boolean;\n /** Maximum time (ms) to wait for the embed 'ready' event before rejecting. Defaults to 15000. */\n readyTimeoutMs?: number;\n onReady?: (event: EmbedEvent) => void;\n onEvent?: (event: EmbedEvent) => void;\n onError?: (event: EmbedEvent) => void;\n onClose?: (event: EmbedEvent) => void;\n /** Optional callback invoked when the iframe has loaded. */\n onLoad?: () => void;\n}\n\nexport type EmbedEventType = 'ready' | 'session' | 'state' | 'error' | 'close' | 'resize';\nexport interface EmbedEvent {\n version: 1;\n type: EmbedEventType;\n conversation_id?: string;\n payload?: Record<string, unknown>;\n error?: { code: string; message: string };\n}\n\nexport interface EmbedInstance {\n iframe: HTMLIFrameElement;\n open(): void;\n destroy(): void;\n on(type: EmbedEventType | '*', callback: (event: EmbedEvent) => void): () => void;\n postMessage(type: string, payload?: Record<string, unknown>): void;\n}\n\n/**\n * Version information\n */\nexport const VERSION = '0.2.0';\n\nconst DEFAULT_READY_TIMEOUT_MS = 15000;\nconst DEFAULT_EMBED_URL = 'https://superbuddy.top/open-conversation/';\nconst DEFAULT_API_URL = 'https://superbuddy.top/company-os';\n\ntype ResolvedConfig = OpenConversationConfig & { embedUrl: string };\n\n/**\n * Exchanges app_key/app_secret for an embed token, creates the embed iframe and\n * resolves once the embed page posts its 'ready' event (validated against the\n * iframe window and origin). Rejects if the token exchange fails or the ready\n * event does not arrive within `readyTimeoutMs` (default 15s).\n */\nexport async function createOpenConversation(config: OpenConversationConfig): Promise<EmbedInstance> {\n if (!config.appKey || !config.appSecret) throw new Error('appKey and appSecret are required');\n const resolved: ResolvedConfig = { ...config, embedUrl: config.embedUrl || DEFAULT_EMBED_URL };\n const token = await exchangeEmbedToken(config);\n const instance = buildInstance(resolved, token);\n return new Promise<EmbedInstance>((resolve, reject) => {\n let settled = false;\n const offReady = instance.on('ready', () => {\n settled = true;\n clearTimeout(timer);\n offReady();\n resolve(instance);\n });\n const timer = setTimeout(() => {\n if (settled) return;\n offReady();\n instance.destroy();\n reject(new Error(`Embed did not become ready within ${config.readyTimeoutMs || DEFAULT_READY_TIMEOUT_MS}ms`));\n }, config.readyTimeoutMs || DEFAULT_READY_TIMEOUT_MS);\n });\n}\n\nasync function exchangeEmbedToken(config: OpenConversationConfig): Promise<string> {\n const endpoint = `${(config.apiUrl || DEFAULT_API_URL).replace(/\\/+$/, '')}/open/v1/embed/session`;\n let response: Response;\n try {\n response = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ app_key: config.appKey, app_secret: config.appSecret }),\n });\n } catch (error) {\n throw new Error(`Embed token request failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n const data = await response.json().catch(() => undefined) as { access_token?: unknown; detail?: unknown } | undefined;\n if (!response.ok) {\n const detail = typeof data?.detail === 'string' ? data.detail : response.statusText;\n throw new Error(`Embed token exchange failed (HTTP ${response.status}): ${detail}`);\n }\n if (typeof data?.access_token !== 'string' || !data.access_token) {\n throw new Error('Embed token exchange response is missing access_token');\n }\n return data.access_token;\n}\n\nfunction buildInstance(config: ResolvedConfig, token: string): EmbedInstance {\n if (typeof document === 'undefined') throw new Error('createOpenConversation requires a browser');\n const container = typeof config.container === 'string' ? document.querySelector(config.container) : config.container;\n if (!(container instanceof HTMLElement)) throw new Error('Embed container was not found');\n const targetOrigin = config.origin || new URL(config.embedUrl, window.location.href).origin;\n if (targetOrigin !== '*' && new URL(config.embedUrl, window.location.href).origin !== targetOrigin) {\n throw new Error('Embed origin must match embedUrl origin');\n }\n const iframe = document.createElement('iframe');\n iframe.src = buildUrl(config, token);\n iframe.title = 'Agent OS conversation';\n iframe.style.width = config.width || '100%';\n iframe.style.height = config.height || '100%';\n iframe.style.border = '0';\n iframe.setAttribute('allow', config.allow || 'clipboard-write');\n iframe.setAttribute('sandbox', config.sandbox || 'allow-scripts allow-forms allow-same-origin');\n const listeners = new Map<string, Set<(event: EmbedEvent) => void>>();\n let active = true;\n const emit = (event: EmbedEvent) => {\n listeners.get(event.type)?.forEach((callback) => callback(event));\n listeners.get('*')?.forEach((callback) => callback(event));\n if (event.type === 'ready') config.onReady?.(event);\n if (event.type === 'error') config.onError?.(event);\n if (event.type === 'close') config.onClose?.(event);\n config.onEvent?.(event);\n };\n const receive = (message: MessageEvent) => {\n if (!active || message.source !== iframe.contentWindow || (targetOrigin !== '*' && message.origin !== targetOrigin)) return;\n const event = message.data?.type && message.data?.version === 1 ? message.data as EmbedEvent : undefined;\n if (!event) return;\n // autoResize 模式下 iframe 始终 100% 占满父容器,不再按内容高度调整;\n // resize 事件仍透传给宿主,由宿主自行决定是否响应。\n emit(event);\n };\n iframe.addEventListener('load', () => config.onLoad?.(), { once: true });\n window.addEventListener('message', receive);\n const open = () => { if (!iframe.isConnected) container.appendChild(iframe); };\n const destroy = () => { if (!active) return; active = false; window.removeEventListener('message', receive); listeners.clear(); iframe.remove(); };\n open();\n const postMessage = (type: string, payload: Record<string, unknown> = {}) => {\n if (!active || !iframe.contentWindow) return;\n iframe.contentWindow.postMessage({ version: 1, type, payload }, targetOrigin === '*' ? '*' : targetOrigin);\n };\n return { iframe, open, destroy, postMessage, on(type, callback) { const set = listeners.get(type) || new Set(); set.add(callback); listeners.set(type, set); return () => set.delete(callback); } };\n}\n\nfunction buildUrl(config: ResolvedConfig, token: string): string {\n const url = new URL(config.embedUrl, window.location.href);\n url.searchParams.set('token', token);\n if (config.conversationId) url.searchParams.set('conversation_id', config.conversationId);\n if (config.capabilities) url.searchParams.set('capabilities', JSON.stringify(config.capabilities));\n if (config.title) url.searchParams.set('title', config.title);\n if (config.expert) url.searchParams.set('expert', config.expert);\n if (config.envVars) url.searchParams.set('env_vars', JSON.stringify(config.envVars));\n // 显式传入宿主 origin,embed 页据此确定 postMessage 目标,不再依赖 document.referrer。\n url.searchParams.set('origin', window.location.origin);\n return url.toString();\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zkly-superbuddy/embed-sdk",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Lightweight SDK for embedding SuperBuddy conversations",
|
|
5
|
+
"main": "dist/index.umd.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "rollup -c",
|
|
13
|
+
"dev": "rollup -c -w",
|
|
14
|
+
"typecheck": "tsc --noEmit"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"superbuddy",
|
|
18
|
+
"embed",
|
|
19
|
+
"iframe",
|
|
20
|
+
"sdk"
|
|
21
|
+
],
|
|
22
|
+
"author": "SuperBuddy Team",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@rollup/plugin-node-resolve": "^15.0.0",
|
|
26
|
+
"@rollup/plugin-typescript": "^11.0.0",
|
|
27
|
+
"rollup": "^3.0.0",
|
|
28
|
+
"tslib": "^2.5.0",
|
|
29
|
+
"typescript": "^5.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|