chatablex-web-sdk 1.0.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 +199 -0
- package/README.zh-CN.md +199 -0
- package/dist/index.d.mts +280 -0
- package/dist/index.d.ts +280 -0
- package/dist/index.js +366 -0
- package/dist/index.mjs +337 -0
- package/package.json +50 -0
- package/src/bridge.ts +162 -0
- package/src/index.ts +115 -0
- package/src/modules/ai.ts +18 -0
- package/src/modules/events.ts +23 -0
- package/src/modules/skills.ts +14 -0
- package/src/modules/storage.ts +18 -0
- package/src/modules/tool.ts +54 -0
- package/src/modules/tools.ts +18 -0
- package/src/modules/ui.ts +26 -0
- package/src/types.ts +270 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatableX Web SDK — Type Definitions
|
|
3
|
+
*/
|
|
4
|
+
interface ChatOptions {
|
|
5
|
+
sessionId?: string;
|
|
6
|
+
context?: Record<string, unknown>;
|
|
7
|
+
tools?: string[];
|
|
8
|
+
skills?: string[];
|
|
9
|
+
stream?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface ChatResponse {
|
|
12
|
+
content: string;
|
|
13
|
+
sessionId: string;
|
|
14
|
+
messageId: string;
|
|
15
|
+
toolResults?: ToolResult[];
|
|
16
|
+
finished: boolean;
|
|
17
|
+
model?: string;
|
|
18
|
+
usage?: {
|
|
19
|
+
promptTokens: number;
|
|
20
|
+
completionTokens: number;
|
|
21
|
+
totalTokens: number;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
interface SessionContext {
|
|
25
|
+
sessionId: string;
|
|
26
|
+
name: string;
|
|
27
|
+
messages: Message[];
|
|
28
|
+
activeTools: string[];
|
|
29
|
+
activeSkills: string[];
|
|
30
|
+
createdAt: string;
|
|
31
|
+
updatedAt: string;
|
|
32
|
+
}
|
|
33
|
+
interface Message {
|
|
34
|
+
id: string;
|
|
35
|
+
role: 'user' | 'assistant' | 'system' | 'tool';
|
|
36
|
+
content: string;
|
|
37
|
+
timestamp: string;
|
|
38
|
+
toolCalls?: ToolCall[];
|
|
39
|
+
}
|
|
40
|
+
interface ToolInfo {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
version: string;
|
|
44
|
+
description: string;
|
|
45
|
+
}
|
|
46
|
+
interface ToolParameter {
|
|
47
|
+
name: string;
|
|
48
|
+
type: string;
|
|
49
|
+
description: string;
|
|
50
|
+
required: boolean;
|
|
51
|
+
default?: unknown;
|
|
52
|
+
enum?: string[];
|
|
53
|
+
}
|
|
54
|
+
interface ToolCall {
|
|
55
|
+
id: string;
|
|
56
|
+
toolId: string;
|
|
57
|
+
name: string;
|
|
58
|
+
params: Record<string, unknown>;
|
|
59
|
+
status: 'pending' | 'running' | 'success' | 'error' | 'cancelled';
|
|
60
|
+
}
|
|
61
|
+
interface ToolResult {
|
|
62
|
+
success: boolean;
|
|
63
|
+
data?: unknown;
|
|
64
|
+
error?: string;
|
|
65
|
+
toolId: string;
|
|
66
|
+
duration?: number;
|
|
67
|
+
}
|
|
68
|
+
type ToolExecuteHandler = (params: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
69
|
+
interface Skill {
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
description: string;
|
|
73
|
+
version: string;
|
|
74
|
+
author?: string;
|
|
75
|
+
category?: string;
|
|
76
|
+
toolIds: string[];
|
|
77
|
+
variables: SkillVariable[];
|
|
78
|
+
installed: boolean;
|
|
79
|
+
}
|
|
80
|
+
interface SkillVariable {
|
|
81
|
+
name: string;
|
|
82
|
+
type: string;
|
|
83
|
+
description: string;
|
|
84
|
+
required: boolean;
|
|
85
|
+
default?: unknown;
|
|
86
|
+
}
|
|
87
|
+
interface SkillResult {
|
|
88
|
+
success: boolean;
|
|
89
|
+
data?: unknown;
|
|
90
|
+
error?: string;
|
|
91
|
+
skillId: string;
|
|
92
|
+
toolResults?: ToolResult[];
|
|
93
|
+
}
|
|
94
|
+
type NotificationType = 'info' | 'success' | 'warning' | 'error';
|
|
95
|
+
interface FilePickerOptions {
|
|
96
|
+
type?: 'any' | 'image' | 'video' | 'audio' | 'custom';
|
|
97
|
+
multiple?: boolean;
|
|
98
|
+
allowedExtensions?: string[];
|
|
99
|
+
}
|
|
100
|
+
interface TabConfig {
|
|
101
|
+
id: string;
|
|
102
|
+
title: string;
|
|
103
|
+
icon?: string;
|
|
104
|
+
type: 'chat' | 'tool' | 'skill' | 'custom';
|
|
105
|
+
data?: Record<string, unknown>;
|
|
106
|
+
}
|
|
107
|
+
interface StateUpdate {
|
|
108
|
+
refreshMessages?: boolean;
|
|
109
|
+
closeWebUI?: boolean;
|
|
110
|
+
[key: string]: unknown;
|
|
111
|
+
}
|
|
112
|
+
type EventType = 'aiResponse' | 'toolExecution' | 'userMessage' | 'streamingContent' | 'close';
|
|
113
|
+
interface AiResponseEventData extends ChatResponse {
|
|
114
|
+
}
|
|
115
|
+
interface ToolExecutionEventData {
|
|
116
|
+
toolCall: ToolCall;
|
|
117
|
+
result?: ToolResult;
|
|
118
|
+
}
|
|
119
|
+
interface UserMessageEventData {
|
|
120
|
+
message: string;
|
|
121
|
+
timestamp: string;
|
|
122
|
+
}
|
|
123
|
+
interface StreamingContentEventData {
|
|
124
|
+
content: string;
|
|
125
|
+
finished?: boolean;
|
|
126
|
+
}
|
|
127
|
+
interface CloseEventData {
|
|
128
|
+
toolId: string;
|
|
129
|
+
}
|
|
130
|
+
interface EventCallbackMap {
|
|
131
|
+
aiResponse: (data: AiResponseEventData) => void;
|
|
132
|
+
toolExecution: (data: ToolExecutionEventData) => void;
|
|
133
|
+
userMessage: (data: UserMessageEventData) => void;
|
|
134
|
+
streamingContent: (data: StreamingContentEventData) => void;
|
|
135
|
+
close: (data: CloseEventData) => void;
|
|
136
|
+
}
|
|
137
|
+
type Unsubscribe = () => void;
|
|
138
|
+
interface ChatableXInitConfig {
|
|
139
|
+
/** Your app / tool id (must match manifest.json id) */
|
|
140
|
+
appId: string;
|
|
141
|
+
/** SDK version override (default: SDK built-in version) */
|
|
142
|
+
version?: string;
|
|
143
|
+
/** Enable debug logging (default: false) */
|
|
144
|
+
debug?: boolean;
|
|
145
|
+
/** Timeout in ms for the handshake with Flutter (default: 10000) */
|
|
146
|
+
timeout?: number;
|
|
147
|
+
}
|
|
148
|
+
interface ChatableXAI {
|
|
149
|
+
chat(message: string, options?: ChatOptions): Promise<ChatResponse>;
|
|
150
|
+
chatStream(message: string, options?: ChatOptions): Promise<unknown>;
|
|
151
|
+
getContext(): Promise<SessionContext>;
|
|
152
|
+
}
|
|
153
|
+
interface ChatableXTools {
|
|
154
|
+
list(): Promise<ToolInfo[]>;
|
|
155
|
+
execute(toolId: string, params: Record<string, unknown>): Promise<ToolResult>;
|
|
156
|
+
executeWithConfirm(toolId: string, params: Record<string, unknown>): Promise<ToolResult>;
|
|
157
|
+
}
|
|
158
|
+
interface ChatableXSkills {
|
|
159
|
+
list(): Promise<Skill[]>;
|
|
160
|
+
execute(skillId: string, variables: Record<string, unknown>): Promise<SkillResult>;
|
|
161
|
+
}
|
|
162
|
+
interface ChatableXUI {
|
|
163
|
+
showNotification(message: string, type?: NotificationType): Promise<void>;
|
|
164
|
+
showConfirm(title: string, message: string): Promise<boolean>;
|
|
165
|
+
pickFile(options?: FilePickerOptions): Promise<string | null>;
|
|
166
|
+
openTab(config: TabConfig): Promise<void>;
|
|
167
|
+
updateState(state: StateUpdate): Promise<void>;
|
|
168
|
+
}
|
|
169
|
+
interface ChatableXEvents {
|
|
170
|
+
on<T extends EventType>(eventType: T, callback: EventCallbackMap[T]): Unsubscribe;
|
|
171
|
+
onAiResponse(callback: EventCallbackMap['aiResponse']): Unsubscribe;
|
|
172
|
+
onToolExecution(callback: EventCallbackMap['toolExecution']): Unsubscribe;
|
|
173
|
+
onUserMessage(callback: EventCallbackMap['userMessage']): Unsubscribe;
|
|
174
|
+
}
|
|
175
|
+
interface ChatableXStorage {
|
|
176
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
177
|
+
set<T = unknown>(key: string, value: T): Promise<void>;
|
|
178
|
+
delete(key: string): Promise<void>;
|
|
179
|
+
}
|
|
180
|
+
interface ChatableXToolModule {
|
|
181
|
+
getInfo(): ToolInfo;
|
|
182
|
+
onExecute(handler: ToolExecuteHandler): void;
|
|
183
|
+
}
|
|
184
|
+
interface ChatableXSDK {
|
|
185
|
+
ai: ChatableXAI;
|
|
186
|
+
tools: ChatableXTools;
|
|
187
|
+
skills: ChatableXSkills;
|
|
188
|
+
ui: ChatableXUI;
|
|
189
|
+
events: ChatableXEvents;
|
|
190
|
+
storage: ChatableXStorage;
|
|
191
|
+
tool: ChatableXToolModule;
|
|
192
|
+
}
|
|
193
|
+
declare global {
|
|
194
|
+
interface Window {
|
|
195
|
+
/** SDK instance — set after ChatableX.init() */
|
|
196
|
+
ChatableX?: ChatableXSDK;
|
|
197
|
+
/** Flutter → JS message receiver — set by SDK */
|
|
198
|
+
ChatableXReceive?: (jsonStr: string) => void;
|
|
199
|
+
/** Flutter's JavaScriptChannel (set by Flutter WebView) */
|
|
200
|
+
ChatableXBridge?: {
|
|
201
|
+
postMessage: (msg: string) => void;
|
|
202
|
+
};
|
|
203
|
+
/** Direct dispatch function for Flutter's executeInWebUI */
|
|
204
|
+
__CHATABLEX_DISPATCH__?: (params: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Low-level WebView bridge between the Web SDK and the Flutter host.
|
|
210
|
+
*
|
|
211
|
+
* Communication:
|
|
212
|
+
* JS → Flutter : window.ChatableXBridge.postMessage(JSON.stringify(msg))
|
|
213
|
+
* Flutter → JS : controller.runJavaScript("window.ChatableXReceive('...')")
|
|
214
|
+
*/
|
|
215
|
+
type EventHandler = (data: unknown) => void;
|
|
216
|
+
declare class Bridge {
|
|
217
|
+
private _msgId;
|
|
218
|
+
private _pending;
|
|
219
|
+
private _listeners;
|
|
220
|
+
private _debug;
|
|
221
|
+
constructor(debug?: boolean);
|
|
222
|
+
/** Install the global ChatableXReceive handler so Flutter can push data in. */
|
|
223
|
+
install(): void;
|
|
224
|
+
/** Wait for ChatableXBridge (set by Flutter) to become available. */
|
|
225
|
+
waitForBridge(timeoutMs: number): Promise<void>;
|
|
226
|
+
/** Send a request to Flutter and wait for a response. */
|
|
227
|
+
sendMessage(method: string, params?: Record<string, unknown>, requestTimeoutMs?: number): Promise<unknown>;
|
|
228
|
+
private _handleResponse;
|
|
229
|
+
private _handleEvent;
|
|
230
|
+
addEventListener(eventType: string, handler: EventHandler): () => void;
|
|
231
|
+
/** Dispatch a synthetic event (used internally). */
|
|
232
|
+
dispatchEvent(eventType: string, data: unknown): void;
|
|
233
|
+
private _nextId;
|
|
234
|
+
private _log;
|
|
235
|
+
destroy(): void;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* chatablex-web-sdk
|
|
240
|
+
*
|
|
241
|
+
* Runtime SDK for ChatableX AI App (WebUI) development.
|
|
242
|
+
* Developers install this package and call `ChatableX.init()` to connect
|
|
243
|
+
* their web app to the ChatableX Flutter host.
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```ts
|
|
247
|
+
* import { ChatableX } from 'chatablex-web-sdk';
|
|
248
|
+
*
|
|
249
|
+
* const sdk = await ChatableX.init({ appId: 'counter-app' });
|
|
250
|
+
*
|
|
251
|
+
* sdk.tool.onExecute(async (params) => {
|
|
252
|
+
* // handle LLM-driven tool calls
|
|
253
|
+
* return { success: true, data: 'done' };
|
|
254
|
+
* });
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
|
|
258
|
+
declare const SDK_VERSION = "1.0.0";
|
|
259
|
+
/**
|
|
260
|
+
* Main entry point. Provides `ChatableX.init()` to bootstrap the SDK.
|
|
261
|
+
*/
|
|
262
|
+
declare const ChatableX: {
|
|
263
|
+
/**
|
|
264
|
+
* Initialize the SDK and establish the bridge with the Flutter host.
|
|
265
|
+
*
|
|
266
|
+
* 1. Sets up `window.ChatableXReceive` (Flutter → JS message handler).
|
|
267
|
+
* 2. Waits for `window.ChatableXBridge` (Flutter's JavaScriptChannel).
|
|
268
|
+
* 3. Sends `sdk_init` handshake and receives tool config from Flutter.
|
|
269
|
+
* 4. Returns the fully-initialised SDK instance.
|
|
270
|
+
*/
|
|
271
|
+
init(config: ChatableXInitConfig): Promise<ChatableXSDK>;
|
|
272
|
+
/** Get the current SDK instance (throws if not initialised). */
|
|
273
|
+
getInstance(): ChatableXSDK;
|
|
274
|
+
/** Check whether the SDK has been initialised. */
|
|
275
|
+
isReady(): boolean;
|
|
276
|
+
/** SDK version */
|
|
277
|
+
version: string;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
export { type AiResponseEventData, Bridge, type ChatOptions, type ChatResponse, ChatableX, type ChatableXAI, type ChatableXEvents, type ChatableXInitConfig, type ChatableXSDK, type ChatableXSkills, type ChatableXStorage, type ChatableXToolModule, type ChatableXTools, type ChatableXUI, type CloseEventData, type EventCallbackMap, type EventType, type FilePickerOptions, type Message, type NotificationType, SDK_VERSION, type SessionContext, type Skill, type SkillResult, type SkillVariable, type StateUpdate, type StreamingContentEventData, type TabConfig, type ToolCall, type ToolExecuteHandler, type ToolExecutionEventData, type ToolInfo, type ToolParameter, type ToolResult, type Unsubscribe, type UserMessageEventData };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Bridge: () => Bridge,
|
|
24
|
+
ChatableX: () => ChatableX,
|
|
25
|
+
SDK_VERSION: () => SDK_VERSION
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/bridge.ts
|
|
30
|
+
var Bridge = class {
|
|
31
|
+
constructor(debug = false) {
|
|
32
|
+
this._msgId = 0;
|
|
33
|
+
this._pending = /* @__PURE__ */ new Map();
|
|
34
|
+
this._listeners = /* @__PURE__ */ new Map();
|
|
35
|
+
this._debug = debug;
|
|
36
|
+
}
|
|
37
|
+
// -------------------------------------------------------------------------
|
|
38
|
+
// Lifecycle
|
|
39
|
+
// -------------------------------------------------------------------------
|
|
40
|
+
/** Install the global ChatableXReceive handler so Flutter can push data in. */
|
|
41
|
+
install() {
|
|
42
|
+
window.ChatableXReceive = (jsonStr) => {
|
|
43
|
+
try {
|
|
44
|
+
const data = JSON.parse(jsonStr);
|
|
45
|
+
if (data.type === "response") {
|
|
46
|
+
this._handleResponse(data);
|
|
47
|
+
} else if (data.type === "event") {
|
|
48
|
+
this._handleEvent(data);
|
|
49
|
+
}
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.error("[ChatableX Bridge] receive parse error:", e);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
this._log("ChatableXReceive installed");
|
|
55
|
+
}
|
|
56
|
+
/** Wait for ChatableXBridge (set by Flutter) to become available. */
|
|
57
|
+
waitForBridge(timeoutMs) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
if (window.ChatableXBridge) {
|
|
60
|
+
resolve();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const start = Date.now();
|
|
64
|
+
const check = setInterval(() => {
|
|
65
|
+
if (window.ChatableXBridge) {
|
|
66
|
+
clearInterval(check);
|
|
67
|
+
resolve();
|
|
68
|
+
} else if (Date.now() - start > timeoutMs) {
|
|
69
|
+
clearInterval(check);
|
|
70
|
+
reject(new Error(`ChatableXBridge not available after ${timeoutMs}ms`));
|
|
71
|
+
}
|
|
72
|
+
}, 50);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// -------------------------------------------------------------------------
|
|
76
|
+
// Request / Response
|
|
77
|
+
// -------------------------------------------------------------------------
|
|
78
|
+
/** Send a request to Flutter and wait for a response. */
|
|
79
|
+
sendMessage(method, params = {}, requestTimeoutMs = 3e4) {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const id = this._nextId();
|
|
82
|
+
const message = { id, method, params, timestamp: Date.now() };
|
|
83
|
+
const timer = setTimeout(() => {
|
|
84
|
+
if (this._pending.has(id)) {
|
|
85
|
+
this._pending.delete(id);
|
|
86
|
+
reject(new Error(`Request timeout: ${method}`));
|
|
87
|
+
}
|
|
88
|
+
}, requestTimeoutMs);
|
|
89
|
+
this._pending.set(id, { resolve, reject, timer });
|
|
90
|
+
if (window.ChatableXBridge) {
|
|
91
|
+
window.ChatableXBridge.postMessage(JSON.stringify(message));
|
|
92
|
+
} else {
|
|
93
|
+
clearTimeout(timer);
|
|
94
|
+
this._pending.delete(id);
|
|
95
|
+
reject(new Error("ChatableXBridge not available"));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
_handleResponse(data) {
|
|
100
|
+
const pending = this._pending.get(data.id);
|
|
101
|
+
if (!pending) return;
|
|
102
|
+
this._pending.delete(data.id);
|
|
103
|
+
clearTimeout(pending.timer);
|
|
104
|
+
if (data.success) {
|
|
105
|
+
pending.resolve(data.data);
|
|
106
|
+
} else {
|
|
107
|
+
pending.reject(new Error(data.error ?? "Unknown error"));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// -------------------------------------------------------------------------
|
|
111
|
+
// Events
|
|
112
|
+
// -------------------------------------------------------------------------
|
|
113
|
+
_handleEvent(data) {
|
|
114
|
+
const handlers = this._listeners.get(data.eventType);
|
|
115
|
+
if (handlers) {
|
|
116
|
+
for (const fn of handlers) {
|
|
117
|
+
try {
|
|
118
|
+
fn(data.data);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.error("[ChatableX] event handler error:", e);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
addEventListener(eventType, handler) {
|
|
126
|
+
if (!this._listeners.has(eventType)) {
|
|
127
|
+
this._listeners.set(eventType, /* @__PURE__ */ new Set());
|
|
128
|
+
}
|
|
129
|
+
this._listeners.get(eventType).add(handler);
|
|
130
|
+
return () => {
|
|
131
|
+
const set = this._listeners.get(eventType);
|
|
132
|
+
if (set) {
|
|
133
|
+
set.delete(handler);
|
|
134
|
+
if (set.size === 0) this._listeners.delete(eventType);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/** Dispatch a synthetic event (used internally). */
|
|
139
|
+
dispatchEvent(eventType, data) {
|
|
140
|
+
this._handleEvent({ eventType, data });
|
|
141
|
+
}
|
|
142
|
+
// -------------------------------------------------------------------------
|
|
143
|
+
// Internals
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
_nextId() {
|
|
146
|
+
return `ctx_${++this._msgId}_${Date.now()}`;
|
|
147
|
+
}
|
|
148
|
+
_log(...args) {
|
|
149
|
+
if (this._debug) console.log("[ChatableX Bridge]", ...args);
|
|
150
|
+
}
|
|
151
|
+
destroy() {
|
|
152
|
+
for (const [, p] of this._pending) {
|
|
153
|
+
clearTimeout(p.timer);
|
|
154
|
+
p.reject(new Error("Bridge destroyed"));
|
|
155
|
+
}
|
|
156
|
+
this._pending.clear();
|
|
157
|
+
this._listeners.clear();
|
|
158
|
+
window.ChatableXReceive = void 0;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// src/modules/tool.ts
|
|
163
|
+
function createToolModule(bridge, appId) {
|
|
164
|
+
let _info = { id: appId, name: appId, version: "1.0.0", description: "" };
|
|
165
|
+
let _handler = null;
|
|
166
|
+
const dispatch = async (params) => {
|
|
167
|
+
if (!_handler) {
|
|
168
|
+
return { success: false, error: "No execute handler registered" };
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
return await _handler(params);
|
|
172
|
+
} catch (e) {
|
|
173
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
174
|
+
return { success: false, error: msg };
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
window.__CHATABLEX_DISPATCH__ = dispatch;
|
|
178
|
+
bridge.addEventListener("toolExecution", async (data) => {
|
|
179
|
+
const params = data;
|
|
180
|
+
const requestId = params._requestId;
|
|
181
|
+
const result = await dispatch(params);
|
|
182
|
+
if (requestId && window.ChatableXBridge) {
|
|
183
|
+
window.ChatableXBridge.postMessage(JSON.stringify({
|
|
184
|
+
method: "tool.executeResult",
|
|
185
|
+
params: { _requestId: requestId, ...result }
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
return {
|
|
190
|
+
getInfo() {
|
|
191
|
+
return { ..._info };
|
|
192
|
+
},
|
|
193
|
+
onExecute(handler) {
|
|
194
|
+
_handler = handler;
|
|
195
|
+
},
|
|
196
|
+
/** @internal — called by SDK after handshake to fill in tool metadata */
|
|
197
|
+
_setInfo(info) {
|
|
198
|
+
_info = { ..._info, ...info };
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/modules/events.ts
|
|
204
|
+
function createEventsModule(bridge) {
|
|
205
|
+
return {
|
|
206
|
+
on(eventType, callback) {
|
|
207
|
+
bridge.sendMessage("events.subscribe", { eventType }).catch(() => {
|
|
208
|
+
});
|
|
209
|
+
return bridge.addEventListener(eventType, callback);
|
|
210
|
+
},
|
|
211
|
+
onAiResponse(callback) {
|
|
212
|
+
return this.on("aiResponse", callback);
|
|
213
|
+
},
|
|
214
|
+
onToolExecution(callback) {
|
|
215
|
+
return this.on("toolExecution", callback);
|
|
216
|
+
},
|
|
217
|
+
onUserMessage(callback) {
|
|
218
|
+
return this.on("userMessage", callback);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// src/modules/ai.ts
|
|
224
|
+
function createAIModule(bridge) {
|
|
225
|
+
return {
|
|
226
|
+
chat(message, options) {
|
|
227
|
+
return bridge.sendMessage("ai.chat", { message, ...options });
|
|
228
|
+
},
|
|
229
|
+
chatStream(message, options) {
|
|
230
|
+
return bridge.sendMessage("ai.chatStream", { message, ...options });
|
|
231
|
+
},
|
|
232
|
+
getContext() {
|
|
233
|
+
return bridge.sendMessage("ai.getContext", {});
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/modules/ui.ts
|
|
239
|
+
function createUIModule(bridge) {
|
|
240
|
+
return {
|
|
241
|
+
showNotification(message, type = "info") {
|
|
242
|
+
return bridge.sendMessage("ui.showNotification", { message, type });
|
|
243
|
+
},
|
|
244
|
+
showConfirm(title, message) {
|
|
245
|
+
return bridge.sendMessage("ui.showConfirm", { title, message });
|
|
246
|
+
},
|
|
247
|
+
pickFile(options) {
|
|
248
|
+
return bridge.sendMessage("ui.pickFile", options ?? {});
|
|
249
|
+
},
|
|
250
|
+
openTab(config) {
|
|
251
|
+
return bridge.sendMessage("ui.openTab", config);
|
|
252
|
+
},
|
|
253
|
+
updateState(state) {
|
|
254
|
+
return bridge.sendMessage("ui.updateState", state);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/modules/storage.ts
|
|
260
|
+
function createStorageModule(bridge) {
|
|
261
|
+
return {
|
|
262
|
+
get(key) {
|
|
263
|
+
return bridge.sendMessage("storage.get", { key });
|
|
264
|
+
},
|
|
265
|
+
set(key, value) {
|
|
266
|
+
return bridge.sendMessage("storage.set", { key, value });
|
|
267
|
+
},
|
|
268
|
+
delete(key) {
|
|
269
|
+
return bridge.sendMessage("storage.delete", { key });
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/modules/tools.ts
|
|
275
|
+
function createToolsModule(bridge) {
|
|
276
|
+
return {
|
|
277
|
+
list() {
|
|
278
|
+
return bridge.sendMessage("tools.list", {});
|
|
279
|
+
},
|
|
280
|
+
execute(toolId, params) {
|
|
281
|
+
return bridge.sendMessage("tools.execute", { toolId, params });
|
|
282
|
+
},
|
|
283
|
+
executeWithConfirm(toolId, params) {
|
|
284
|
+
return bridge.sendMessage("tools.executeWithConfirm", { toolId, params });
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/modules/skills.ts
|
|
290
|
+
function createSkillsModule(bridge) {
|
|
291
|
+
return {
|
|
292
|
+
list() {
|
|
293
|
+
return bridge.sendMessage("skills.list", {});
|
|
294
|
+
},
|
|
295
|
+
execute(skillId, variables) {
|
|
296
|
+
return bridge.sendMessage("skills.execute", { skillId, variables });
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/index.ts
|
|
302
|
+
var SDK_VERSION = "1.0.0";
|
|
303
|
+
var _instance = null;
|
|
304
|
+
var ChatableX = {
|
|
305
|
+
/**
|
|
306
|
+
* Initialize the SDK and establish the bridge with the Flutter host.
|
|
307
|
+
*
|
|
308
|
+
* 1. Sets up `window.ChatableXReceive` (Flutter → JS message handler).
|
|
309
|
+
* 2. Waits for `window.ChatableXBridge` (Flutter's JavaScriptChannel).
|
|
310
|
+
* 3. Sends `sdk_init` handshake and receives tool config from Flutter.
|
|
311
|
+
* 4. Returns the fully-initialised SDK instance.
|
|
312
|
+
*/
|
|
313
|
+
async init(config) {
|
|
314
|
+
if (_instance) return _instance;
|
|
315
|
+
const debug = config.debug ?? false;
|
|
316
|
+
const timeout = config.timeout ?? 1e4;
|
|
317
|
+
const bridge = new Bridge(debug);
|
|
318
|
+
bridge.install();
|
|
319
|
+
await bridge.waitForBridge(timeout);
|
|
320
|
+
if (debug) console.log("[ChatableX] Bridge connected, sending sdk_init");
|
|
321
|
+
let toolConfig = {};
|
|
322
|
+
try {
|
|
323
|
+
const resp = await bridge.sendMessage("sdk_init", {
|
|
324
|
+
appId: config.appId,
|
|
325
|
+
sdkVersion: SDK_VERSION
|
|
326
|
+
});
|
|
327
|
+
if (resp && typeof resp === "object") {
|
|
328
|
+
toolConfig = resp;
|
|
329
|
+
}
|
|
330
|
+
} catch {
|
|
331
|
+
if (debug) console.warn("[ChatableX] sdk_init handshake failed, continuing with defaults");
|
|
332
|
+
}
|
|
333
|
+
const toolModule = createToolModule(bridge, config.appId);
|
|
334
|
+
if (toolConfig) toolModule._setInfo(toolConfig);
|
|
335
|
+
const sdk = {
|
|
336
|
+
ai: createAIModule(bridge),
|
|
337
|
+
tools: createToolsModule(bridge),
|
|
338
|
+
skills: createSkillsModule(bridge),
|
|
339
|
+
ui: createUIModule(bridge),
|
|
340
|
+
events: createEventsModule(bridge),
|
|
341
|
+
storage: createStorageModule(bridge),
|
|
342
|
+
tool: toolModule
|
|
343
|
+
};
|
|
344
|
+
window.ChatableX = sdk;
|
|
345
|
+
_instance = sdk;
|
|
346
|
+
if (debug) console.log(`[ChatableX] SDK v${SDK_VERSION} ready for: ${config.appId}`);
|
|
347
|
+
return sdk;
|
|
348
|
+
},
|
|
349
|
+
/** Get the current SDK instance (throws if not initialised). */
|
|
350
|
+
getInstance() {
|
|
351
|
+
if (!_instance) throw new Error("ChatableX SDK not initialised. Call ChatableX.init() first.");
|
|
352
|
+
return _instance;
|
|
353
|
+
},
|
|
354
|
+
/** Check whether the SDK has been initialised. */
|
|
355
|
+
isReady() {
|
|
356
|
+
return _instance !== null;
|
|
357
|
+
},
|
|
358
|
+
/** SDK version */
|
|
359
|
+
version: SDK_VERSION
|
|
360
|
+
};
|
|
361
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
362
|
+
0 && (module.exports = {
|
|
363
|
+
Bridge,
|
|
364
|
+
ChatableX,
|
|
365
|
+
SDK_VERSION
|
|
366
|
+
});
|