@zhin.js/client 1.1.4 → 1.1.6
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 +94 -432
- package/client/agent-stream.ts +121 -0
- package/client/app.test.ts +37 -0
- package/client/app.ts +130 -138
- package/client/bootstrap/loadConsoleEntries.test.ts +23 -12
- package/client/bootstrap/loadConsoleEntries.ts +40 -27
- package/client/console-client-context.ts +24 -0
- package/client/console-client.test.ts +46 -0
- package/client/console-client.ts +70 -0
- package/client/console-events.ts +38 -0
- package/client/console-utils/remoteApi.test.ts +91 -0
- package/client/console-utils/remoteApi.ts +47 -9
- package/client/index.ts +92 -28
- package/client/mediaSrc.test.ts +49 -0
- package/client/mediaSrc.ts +84 -11
- package/client/message-content/MarkdownContent.tsx +128 -0
- package/client/message-content/markdown.test.ts +48 -0
- package/client/message-content/markdown.ts +247 -0
- package/client/persistence/idb-store.test.ts +121 -0
- package/client/persistence/idb-store.ts +40 -21
- package/client/segments.test.ts +16 -0
- package/client/segments.ts +39 -0
- package/client/transport/console-transport.test.ts +394 -0
- package/client/transport/console-transport.ts +676 -0
- package/client/transport/hooks.test.ts +21 -0
- package/client/transport/hooks.ts +535 -0
- package/client/transport/index.ts +23 -0
- package/client/{websocket → transport}/types.ts +16 -17
- package/client/types.ts +5 -8
- package/client/workroom/WorkroomRunsPage.tsx +355 -0
- package/client/workroom/registerWorkroomConsole.test.ts +25 -0
- package/client/workroom/registerWorkroomConsole.ts +17 -0
- package/dist/agent-stream.d.ts +17 -0
- package/dist/agent-stream.d.ts.map +1 -0
- package/dist/agent-stream.js +73 -0
- package/dist/agent-stream.js.map +1 -0
- package/dist/app.d.ts +17 -52
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +110 -95
- package/dist/app.js.map +1 -1
- package/dist/bootstrap/loadConsoleEntries.d.ts +10 -7
- package/dist/bootstrap/loadConsoleEntries.d.ts.map +1 -1
- package/dist/bootstrap/loadConsoleEntries.js +37 -11
- package/dist/bootstrap/loadConsoleEntries.js.map +1 -1
- package/dist/console-client-context.d.ts +9 -0
- package/dist/console-client-context.d.ts.map +1 -0
- package/dist/console-client-context.js +13 -0
- package/dist/console-client-context.js.map +1 -0
- package/dist/console-client.d.ts +25 -0
- package/dist/console-client.d.ts.map +1 -0
- package/dist/console-client.js +45 -0
- package/dist/console-client.js.map +1 -0
- package/dist/console-events.d.ts +8 -0
- package/dist/console-events.d.ts.map +1 -0
- package/dist/console-events.js +26 -0
- package/dist/console-events.js.map +1 -0
- package/dist/console-utils/remoteApi.d.ts +2 -1
- package/dist/console-utils/remoteApi.d.ts.map +1 -1
- package/dist/console-utils/remoteApi.js +46 -8
- package/dist/console-utils/remoteApi.js.map +1 -1
- package/dist/index.d.ts +14 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -8
- package/dist/index.js.map +1 -1
- package/dist/mediaSrc.d.ts +1 -1
- package/dist/mediaSrc.d.ts.map +1 -1
- package/dist/mediaSrc.js +91 -11
- package/dist/mediaSrc.js.map +1 -1
- package/dist/message-content/MarkdownContent.d.ts +11 -0
- package/dist/message-content/MarkdownContent.d.ts.map +1 -0
- package/dist/message-content/MarkdownContent.js +68 -0
- package/dist/message-content/MarkdownContent.js.map +1 -0
- package/dist/message-content/markdown.d.ts +47 -0
- package/dist/message-content/markdown.d.ts.map +1 -0
- package/dist/message-content/markdown.js +219 -0
- package/dist/message-content/markdown.js.map +1 -0
- package/dist/persistence/idb-store.d.ts +5 -2
- package/dist/persistence/idb-store.d.ts.map +1 -1
- package/dist/persistence/idb-store.js +29 -20
- package/dist/persistence/idb-store.js.map +1 -1
- package/dist/segments.d.ts +9 -0
- package/dist/segments.d.ts.map +1 -0
- package/dist/segments.js +35 -0
- package/dist/segments.js.map +1 -0
- package/dist/transport/console-transport.d.ts +217 -0
- package/dist/transport/console-transport.d.ts.map +1 -0
- package/dist/transport/console-transport.js +563 -0
- package/dist/transport/console-transport.js.map +1 -0
- package/dist/transport/hooks.d.ts +253 -0
- package/dist/transport/hooks.d.ts.map +1 -0
- package/dist/transport/hooks.js +483 -0
- package/dist/transport/hooks.js.map +1 -0
- package/dist/transport/index.d.ts +4 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +4 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/{websocket → transport}/types.d.ts +14 -13
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/{websocket → transport}/types.js +4 -9
- package/dist/transport/types.js.map +1 -0
- package/dist/types.d.ts +4 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/workroom/WorkroomRunsPage.d.ts +2 -0
- package/dist/workroom/WorkroomRunsPage.d.ts.map +1 -0
- package/dist/workroom/WorkroomRunsPage.js +111 -0
- package/dist/workroom/WorkroomRunsPage.js.map +1 -0
- package/dist/workroom/registerWorkroomConsole.d.ts +4 -0
- package/dist/workroom/registerWorkroomConsole.d.ts.map +1 -0
- package/dist/workroom/registerWorkroomConsole.js +16 -0
- package/dist/workroom/registerWorkroomConsole.js.map +1 -0
- package/package.json +18 -8
- package/client/runtime/index.ts +0 -12
- package/client/websocket/hooks.ts +0 -289
- package/client/websocket/index.ts +0 -6
- package/client/websocket/instance.ts +0 -24
- package/client/websocket/manager.ts +0 -426
- package/client/websocket/remote-settings.ts +0 -25
- package/dist/bootstrap/loadConsoleEntries.test.d.ts +0 -2
- package/dist/bootstrap/loadConsoleEntries.test.d.ts.map +0 -1
- package/dist/bootstrap/loadConsoleEntries.test.js +0 -27
- package/dist/bootstrap/loadConsoleEntries.test.js.map +0 -1
- package/dist/runtime/index.d.ts +0 -6
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js +0 -10
- package/dist/runtime/index.js.map +0 -1
- package/dist/websocket/hooks.d.ts +0 -105
- package/dist/websocket/hooks.d.ts.map +0 -1
- package/dist/websocket/hooks.js +0 -247
- package/dist/websocket/hooks.js.map +0 -1
- package/dist/websocket/index.d.ts +0 -7
- package/dist/websocket/index.d.ts.map +0 -1
- package/dist/websocket/index.js +0 -7
- package/dist/websocket/index.js.map +0 -1
- package/dist/websocket/instance.d.ts +0 -5
- package/dist/websocket/instance.d.ts.map +0 -1
- package/dist/websocket/instance.js +0 -21
- package/dist/websocket/instance.js.map +0 -1
- package/dist/websocket/manager.d.ts +0 -113
- package/dist/websocket/manager.d.ts.map +0 -1
- package/dist/websocket/manager.js +0 -403
- package/dist/websocket/manager.js.map +0 -1
- package/dist/websocket/remote-settings.d.ts +0 -5
- package/dist/websocket/remote-settings.d.ts.map +0 -1
- package/dist/websocket/remote-settings.js +0 -23
- package/dist/websocket/remote-settings.js.map +0 -1
- package/dist/websocket/types.d.ts.map +0 -1
- package/dist/websocket/types.js.map +0 -1
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
WebSocketMessage,
|
|
3
|
-
WebSocketConfig,
|
|
4
|
-
WebSocketCallbacks,
|
|
5
|
-
} from "./types";
|
|
6
|
-
import {
|
|
7
|
-
ConnectionState,
|
|
8
|
-
WebSocketError,
|
|
9
|
-
ConnectionError,
|
|
10
|
-
MessageError,
|
|
11
|
-
RequestTimeoutError,
|
|
12
|
-
} from "./types";
|
|
13
|
-
import { getApiBase, getStoredToken, resolveApiUrl } from "./remote-settings.js";
|
|
14
|
-
import { applyConsoleEvent } from "../persistence/idb-store.js";
|
|
15
|
-
|
|
16
|
-
export class WebSocketManager {
|
|
17
|
-
private ws: WebSocket | null = null;
|
|
18
|
-
private config: Required<WebSocketConfig>;
|
|
19
|
-
private callbacks: WebSocketCallbacks;
|
|
20
|
-
private state: ConnectionState = ConnectionState.DISCONNECTED;
|
|
21
|
-
private reconnectAttempts = 0;
|
|
22
|
-
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
23
|
-
private requestId = 0;
|
|
24
|
-
private pendingRequests = new Map<
|
|
25
|
-
number,
|
|
26
|
-
{ resolve: (value: unknown) => void; reject: (error: Error) => void; timer: ReturnType<typeof setTimeout> }
|
|
27
|
-
>();
|
|
28
|
-
private connectedListeners = new Set<(connected: boolean) => void>();
|
|
29
|
-
private sseAbort: AbortController | null = null;
|
|
30
|
-
private useRestTransport = true;
|
|
31
|
-
|
|
32
|
-
constructor(config: WebSocketConfig = {}, callbacks: WebSocketCallbacks = {}) {
|
|
33
|
-
this.config = {
|
|
34
|
-
url: this.buildWebSocketUrl(config.url),
|
|
35
|
-
reconnectInterval: config.reconnectInterval ?? 3000,
|
|
36
|
-
maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
|
|
37
|
-
requestTimeout: config.requestTimeout ?? 10000,
|
|
38
|
-
};
|
|
39
|
-
this.callbacks = callbacks;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
onConnectionChange(listener: (connected: boolean) => void): () => void {
|
|
43
|
-
this.connectedListeners.add(listener);
|
|
44
|
-
return () => this.connectedListeners.delete(listener);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
private notifyConnection(connected: boolean) {
|
|
48
|
-
for (const l of this.connectedListeners) l(connected);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
connect(): void {
|
|
52
|
-
if (this.useRestTransport) {
|
|
53
|
-
void this.connectRestSse();
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (this.state === ConnectionState.CONNECTED || this.state === ConnectionState.CONNECTING) return;
|
|
57
|
-
this.setState(ConnectionState.CONNECTING);
|
|
58
|
-
try {
|
|
59
|
-
this.ws = new WebSocket(this.config.url);
|
|
60
|
-
this.attachEventHandlers();
|
|
61
|
-
} catch (error) {
|
|
62
|
-
this.handleConnectionError(new ConnectionError("Failed to create WebSocket", error as Error));
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
private async connectRestSse(): Promise<void> {
|
|
67
|
-
const token = getStoredToken();
|
|
68
|
-
if (!token) {
|
|
69
|
-
this.setState(ConnectionState.DISCONNECTED);
|
|
70
|
-
this.notifyConnection(false);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
this.setState(ConnectionState.CONNECTING);
|
|
74
|
-
this.sseAbort?.abort();
|
|
75
|
-
this.sseAbort = new AbortController();
|
|
76
|
-
try {
|
|
77
|
-
const res = await fetch(resolveApiUrl("/api/events"), {
|
|
78
|
-
headers: {
|
|
79
|
-
Authorization: `Bearer ${token}`,
|
|
80
|
-
Accept: "text/event-stream",
|
|
81
|
-
},
|
|
82
|
-
signal: this.sseAbort.signal,
|
|
83
|
-
});
|
|
84
|
-
if (!res.ok || !res.body) {
|
|
85
|
-
throw new ConnectionError(`SSE failed: HTTP ${res.status}`);
|
|
86
|
-
}
|
|
87
|
-
this.setState(ConnectionState.CONNECTED);
|
|
88
|
-
this.reconnectAttempts = 0;
|
|
89
|
-
this.notifyConnection(true);
|
|
90
|
-
this.callbacks.onConnect?.();
|
|
91
|
-
void this.pumpSse(res.body);
|
|
92
|
-
} catch (error) {
|
|
93
|
-
if ((error as Error).name === "AbortError") return;
|
|
94
|
-
this.handleConnectionError(
|
|
95
|
-
error instanceof ConnectionError ? error : new ConnectionError("SSE connect failed", error as Error),
|
|
96
|
-
);
|
|
97
|
-
this.scheduleReconnect();
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private async pumpSse(body: ReadableStream<Uint8Array>): Promise<void> {
|
|
102
|
-
const reader = body.getReader();
|
|
103
|
-
const decoder = new TextDecoder();
|
|
104
|
-
let buffer = "";
|
|
105
|
-
try {
|
|
106
|
-
while (true) {
|
|
107
|
-
const { done, value } = await reader.read();
|
|
108
|
-
if (done) break;
|
|
109
|
-
buffer += decoder.decode(value, { stream: true });
|
|
110
|
-
const parts = buffer.split("\n\n");
|
|
111
|
-
buffer = parts.pop() ?? "";
|
|
112
|
-
for (const part of parts) {
|
|
113
|
-
const line = part.split("\n").find((l) => l.startsWith("data: "));
|
|
114
|
-
if (!line) continue;
|
|
115
|
-
const json = line.slice(6);
|
|
116
|
-
try {
|
|
117
|
-
const message = JSON.parse(json) as WebSocketMessage;
|
|
118
|
-
void applyConsoleEvent(message);
|
|
119
|
-
this.handleBroadcast(message);
|
|
120
|
-
this.callbacks.onMessage?.(message);
|
|
121
|
-
} catch {
|
|
122
|
-
/* skip */
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
} catch {
|
|
127
|
-
/* closed */
|
|
128
|
-
} finally {
|
|
129
|
-
if (this.state === ConnectionState.CONNECTED) {
|
|
130
|
-
this.notifyConnection(false);
|
|
131
|
-
this.setState(ConnectionState.RECONNECTING);
|
|
132
|
-
this.scheduleReconnect();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
disconnect(): void {
|
|
138
|
-
this.clearReconnectTimer();
|
|
139
|
-
this.clearPendingRequests();
|
|
140
|
-
this.sseAbort?.abort();
|
|
141
|
-
this.sseAbort = null;
|
|
142
|
-
if (this.ws) {
|
|
143
|
-
this.ws.close();
|
|
144
|
-
this.ws = null;
|
|
145
|
-
}
|
|
146
|
-
this.setState(ConnectionState.DISCONNECTED);
|
|
147
|
-
this.notifyConnection(false);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
send(message: unknown): void {
|
|
151
|
-
if (this.useRestTransport) {
|
|
152
|
-
void this.sendRequest(message);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
if (!this.isConnected()) throw new WebSocketError("WebSocket is not connected", "NOT_CONNECTED");
|
|
156
|
-
try {
|
|
157
|
-
this.ws!.send(JSON.stringify(message));
|
|
158
|
-
} catch (error) {
|
|
159
|
-
throw new MessageError("Failed to send message", error as Error);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async sendRequest<T = unknown>(message: unknown): Promise<T> {
|
|
164
|
-
if (this.useRestTransport) {
|
|
165
|
-
return this.sendRestRequest<T>(message);
|
|
166
|
-
}
|
|
167
|
-
if (!this.isConnected()) throw new WebSocketError("WebSocket is not connected", "NOT_CONNECTED");
|
|
168
|
-
return new Promise((resolve, reject) => {
|
|
169
|
-
const requestId = ++this.requestId;
|
|
170
|
-
const messageWithId = { ...(message as Record<string, unknown>), requestId };
|
|
171
|
-
const timer = setTimeout(() => {
|
|
172
|
-
this.pendingRequests.delete(requestId);
|
|
173
|
-
reject(new RequestTimeoutError(requestId));
|
|
174
|
-
}, this.config.requestTimeout);
|
|
175
|
-
this.pendingRequests.set(requestId, { resolve: resolve as (v: unknown) => void, reject, timer });
|
|
176
|
-
try {
|
|
177
|
-
this.ws!.send(JSON.stringify(messageWithId));
|
|
178
|
-
} catch (error) {
|
|
179
|
-
this.pendingRequests.delete(requestId);
|
|
180
|
-
clearTimeout(timer);
|
|
181
|
-
reject(new MessageError("Failed to send request", error as Error));
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
private async sendRestRequest<T>(message: unknown): Promise<T> {
|
|
187
|
-
const token = getStoredToken();
|
|
188
|
-
if (!token) throw new WebSocketError("Not authenticated", "NOT_CONNECTED");
|
|
189
|
-
const requestId = ++this.requestId;
|
|
190
|
-
const body = { ...(message as Record<string, unknown>), requestId };
|
|
191
|
-
const timer = setTimeout(() => {
|
|
192
|
-
/* handled below */
|
|
193
|
-
}, this.config.requestTimeout);
|
|
194
|
-
try {
|
|
195
|
-
const res = await fetch(resolveApiUrl("/api/console/request"), {
|
|
196
|
-
method: "POST",
|
|
197
|
-
headers: {
|
|
198
|
-
Authorization: `Bearer ${token}`,
|
|
199
|
-
"Content-Type": "application/json",
|
|
200
|
-
},
|
|
201
|
-
body: JSON.stringify(body),
|
|
202
|
-
signal: AbortSignal.timeout(this.config.requestTimeout),
|
|
203
|
-
});
|
|
204
|
-
clearTimeout(timer);
|
|
205
|
-
const json = (await res.json()) as {
|
|
206
|
-
success?: boolean;
|
|
207
|
-
data?: T;
|
|
208
|
-
error?: string;
|
|
209
|
-
requestId?: number;
|
|
210
|
-
};
|
|
211
|
-
if (!res.ok || json.success === false) {
|
|
212
|
-
throw new WebSocketError(json.error ?? `HTTP ${res.status}`, "SERVER_ERROR");
|
|
213
|
-
}
|
|
214
|
-
return json.data as T;
|
|
215
|
-
} catch (error) {
|
|
216
|
-
clearTimeout(timer);
|
|
217
|
-
if (error instanceof WebSocketError) throw error;
|
|
218
|
-
throw new MessageError("REST request failed", error as Error);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
isConnected(): boolean {
|
|
223
|
-
if (this.useRestTransport) {
|
|
224
|
-
return this.state === ConnectionState.CONNECTED;
|
|
225
|
-
}
|
|
226
|
-
return this.ws?.readyState === WebSocket.OPEN;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
getState(): ConnectionState {
|
|
230
|
-
return this.state;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
async getConfig(pluginName: string) {
|
|
234
|
-
return this.sendRequest<unknown>({ type: "config:get", pluginName });
|
|
235
|
-
}
|
|
236
|
-
async setConfig(pluginName: string, config: unknown) {
|
|
237
|
-
return this.sendRequest<{ success?: boolean; reloaded?: boolean; message?: string }>({
|
|
238
|
-
type: "config:set",
|
|
239
|
-
pluginName,
|
|
240
|
-
data: config,
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
async getSchema(pluginName: string) {
|
|
244
|
-
return this.sendRequest<unknown>({ type: "schema:get", pluginName });
|
|
245
|
-
}
|
|
246
|
-
async getAllConfigs() {
|
|
247
|
-
return this.sendRequest<Record<string, unknown>>({ type: "config:get-all" });
|
|
248
|
-
}
|
|
249
|
-
async getAllSchemas() {
|
|
250
|
-
return this.sendRequest<Record<string, unknown>>({ type: "schema:get-all" });
|
|
251
|
-
}
|
|
252
|
-
async getConfigYaml() {
|
|
253
|
-
return this.sendRequest<{ yaml: string; pluginKeys: string[] }>({ type: "config:get-yaml" });
|
|
254
|
-
}
|
|
255
|
-
async saveConfigYaml(yaml: string) {
|
|
256
|
-
return this.sendRequest<{ success: boolean; message?: string }>({ type: "config:save-yaml", yaml });
|
|
257
|
-
}
|
|
258
|
-
async getEnvList() {
|
|
259
|
-
return this.sendRequest<{ files: Array<{ name: string; exists: boolean }> }>({ type: "env:list" });
|
|
260
|
-
}
|
|
261
|
-
async getEnvFile(filename: string) {
|
|
262
|
-
return this.sendRequest<{ content: string }>({ type: "env:get", filename });
|
|
263
|
-
}
|
|
264
|
-
async saveEnvFile(filename: string, content: string) {
|
|
265
|
-
return this.sendRequest<{ success: boolean; message?: string }>({ type: "env:save", filename, content });
|
|
266
|
-
}
|
|
267
|
-
async getFileTree() {
|
|
268
|
-
return this.sendRequest<{ tree: import("./types").FileTreeNode[] }>({ type: "files:tree" });
|
|
269
|
-
}
|
|
270
|
-
async readFile(filePath: string) {
|
|
271
|
-
return this.sendRequest<{ content: string; size: number }>({ type: "files:read", filePath });
|
|
272
|
-
}
|
|
273
|
-
async saveFile(filePath: string, content: string) {
|
|
274
|
-
return this.sendRequest<{ success: boolean; message?: string }>({ type: "files:save", filePath, content });
|
|
275
|
-
}
|
|
276
|
-
async getDbInfo() {
|
|
277
|
-
return this.sendRequest<import("./types").DatabaseInfo>({ type: "db:info" });
|
|
278
|
-
}
|
|
279
|
-
async getDbTables() {
|
|
280
|
-
return this.sendRequest<{ tables: import("./types").TableInfo[] }>({ type: "db:tables" });
|
|
281
|
-
}
|
|
282
|
-
async dbSelect(table: string, page?: number, pageSize?: number, where?: unknown) {
|
|
283
|
-
return this.sendRequest<import("./types").SelectResult>({ type: "db:select", table, page, pageSize, where });
|
|
284
|
-
}
|
|
285
|
-
async dbInsert(table: string, row: unknown) {
|
|
286
|
-
return this.sendRequest<{ success: boolean }>({ type: "db:insert", table, row });
|
|
287
|
-
}
|
|
288
|
-
async dbUpdate(table: string, row: unknown, where: unknown) {
|
|
289
|
-
return this.sendRequest<{ success: boolean; affected: number }>({ type: "db:update", table, row, where });
|
|
290
|
-
}
|
|
291
|
-
async dbDelete(table: string, where: unknown) {
|
|
292
|
-
return this.sendRequest<{ success: boolean; deleted: number }>({ type: "db:delete", table, where });
|
|
293
|
-
}
|
|
294
|
-
async dbDropTable(table: string) {
|
|
295
|
-
return this.sendRequest<{ success: boolean }>({ type: "db:drop-table", table });
|
|
296
|
-
}
|
|
297
|
-
async kvGet(table: string, key: string) {
|
|
298
|
-
return this.sendRequest<{ key: string; value: unknown }>({ type: "db:kv:get", table, key });
|
|
299
|
-
}
|
|
300
|
-
async kvSet(table: string, key: string, value: unknown, ttl?: number) {
|
|
301
|
-
return this.sendRequest<{ success: boolean }>({ type: "db:kv:set", table, key, value, ttl });
|
|
302
|
-
}
|
|
303
|
-
async kvDelete(table: string, key: string) {
|
|
304
|
-
return this.sendRequest<{ success: boolean }>({ type: "db:kv:delete", table, key });
|
|
305
|
-
}
|
|
306
|
-
async kvGetEntries(table: string) {
|
|
307
|
-
return this.sendRequest<{ entries: import("./types").KvEntry[] }>({ type: "db:kv:entries", table });
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
private buildWebSocketUrl(customUrl?: string): string {
|
|
311
|
-
if (customUrl) return customUrl;
|
|
312
|
-
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
313
|
-
return `${protocol}//${window.location.host}/server`;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
private setState(newState: ConnectionState): void {
|
|
317
|
-
this.state = newState;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
private attachEventHandlers(): void {
|
|
321
|
-
if (!this.ws) return;
|
|
322
|
-
this.ws.onopen = () => this.handleConnectionOpen();
|
|
323
|
-
this.ws.onmessage = (event) => this.handleMessage(event);
|
|
324
|
-
this.ws.onclose = () => this.handleConnectionClose();
|
|
325
|
-
this.ws.onerror = (event) =>
|
|
326
|
-
this.handleConnectionError(new ConnectionError("WebSocket error", event as unknown as Error));
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
private handleConnectionOpen(): void {
|
|
330
|
-
this.setState(ConnectionState.CONNECTED);
|
|
331
|
-
this.reconnectAttempts = 0;
|
|
332
|
-
this.notifyConnection(true);
|
|
333
|
-
this.callbacks.onConnect?.();
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
private handleMessage(event: MessageEvent): void {
|
|
337
|
-
try {
|
|
338
|
-
const message = JSON.parse(event.data) as WebSocketMessage;
|
|
339
|
-
if (message.requestId && this.pendingRequests.has(message.requestId)) {
|
|
340
|
-
this.handleRequestResponse(message);
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
void applyConsoleEvent(message);
|
|
344
|
-
this.handleBroadcast(message);
|
|
345
|
-
this.callbacks.onMessage?.(message);
|
|
346
|
-
} catch (error) {
|
|
347
|
-
console.error("[WebSocket] Message parsing error:", error);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
private handleBroadcast(message: WebSocketMessage): void {
|
|
352
|
-
const t = message.type;
|
|
353
|
-
if (t === "bot:request" || t === "bot:notice" || t === "bot:message") {
|
|
354
|
-
if (typeof window !== "undefined") {
|
|
355
|
-
window.dispatchEvent(new CustomEvent("zhin-console-bot-push", { detail: message }));
|
|
356
|
-
}
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
if (t === "hmr:reload") {
|
|
360
|
-
console.info(`[HMR] File changed, reloading...`);
|
|
361
|
-
window.location.reload();
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
if (t === "system:restarting") {
|
|
365
|
-
console.info("[System] Server restarting, will reload shortly...");
|
|
366
|
-
setTimeout(() => window.location.reload(), 3000);
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
private handleRequestResponse(message: WebSocketMessage): void {
|
|
372
|
-
const { requestId } = message;
|
|
373
|
-
const pending = this.pendingRequests.get(requestId!);
|
|
374
|
-
if (!pending) return;
|
|
375
|
-
this.pendingRequests.delete(requestId!);
|
|
376
|
-
clearTimeout(pending.timer);
|
|
377
|
-
if (message.error) {
|
|
378
|
-
pending.reject(new WebSocketError(message.error, "SERVER_ERROR"));
|
|
379
|
-
} else {
|
|
380
|
-
pending.resolve(message.data);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
private handleConnectionClose(): void {
|
|
385
|
-
this.ws = null;
|
|
386
|
-
this.notifyConnection(false);
|
|
387
|
-
if (this.state === ConnectionState.DISCONNECTED) return;
|
|
388
|
-
this.setState(ConnectionState.RECONNECTING);
|
|
389
|
-
this.callbacks.onDisconnect?.();
|
|
390
|
-
this.scheduleReconnect();
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
private handleConnectionError(error: Error): void {
|
|
394
|
-
this.clearReconnectTimer();
|
|
395
|
-
this.setState(ConnectionState.ERROR);
|
|
396
|
-
console.error("[Console transport] Connection error:", error);
|
|
397
|
-
this.callbacks.onError?.(error as unknown as Event);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
private scheduleReconnect(): void {
|
|
401
|
-
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
402
|
-
this.setState(ConnectionState.ERROR);
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
this.reconnectAttempts++;
|
|
406
|
-
const delay = this.config.reconnectInterval * this.reconnectAttempts;
|
|
407
|
-
this.reconnectTimer = setTimeout(() => {
|
|
408
|
-
if (this.state === ConnectionState.RECONNECTING) this.connect();
|
|
409
|
-
}, delay);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
private clearReconnectTimer(): void {
|
|
413
|
-
if (this.reconnectTimer) {
|
|
414
|
-
clearTimeout(this.reconnectTimer);
|
|
415
|
-
this.reconnectTimer = null;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
private clearPendingRequests(): void {
|
|
420
|
-
for (const [, { reject, timer }] of this.pendingRequests) {
|
|
421
|
-
clearTimeout(timer);
|
|
422
|
-
reject(new WebSocketError("Connection closed", "CONNECTION_CLOSED"));
|
|
423
|
-
}
|
|
424
|
-
this.pendingRequests.clear();
|
|
425
|
-
}
|
|
426
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
const API_BASE_KEY = "zhin_api_base";
|
|
2
|
-
const TOKEN_KEY = "zhin_api_token";
|
|
3
|
-
|
|
4
|
-
export function getApiBase(): string {
|
|
5
|
-
const stored = localStorage.getItem(API_BASE_KEY)?.trim();
|
|
6
|
-
if (stored) return stored.replace(/\/$/, "");
|
|
7
|
-
if (typeof window !== "undefined") {
|
|
8
|
-
return window.location.origin;
|
|
9
|
-
}
|
|
10
|
-
return "";
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function setApiBase(base: string): void {
|
|
14
|
-
localStorage.setItem(API_BASE_KEY, base.replace(/\/$/, ""));
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function getStoredToken(): string | null {
|
|
18
|
-
return localStorage.getItem(TOKEN_KEY);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function resolveApiUrl(path: string): string {
|
|
22
|
-
const base = getApiBase();
|
|
23
|
-
const p = path.startsWith("/") ? path : `/${path}`;
|
|
24
|
-
return `${base}${p}`;
|
|
25
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loadConsoleEntries.test.d.ts","sourceRoot":"","sources":["../../client/bootstrap/loadConsoleEntries.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { createPluginRegisterHostApi, getRegisterFn } from "./loadConsoleEntries.js";
|
|
3
|
-
describe("console entries bootstrap", () => {
|
|
4
|
-
it("resolves named register export", () => {
|
|
5
|
-
const register = () => { };
|
|
6
|
-
expect(getRegisterFn({ register })).toBe(register);
|
|
7
|
-
});
|
|
8
|
-
it("resolves default register export", () => {
|
|
9
|
-
const register = () => { };
|
|
10
|
-
expect(getRegisterFn({ default: { register } })).toBe(register);
|
|
11
|
-
});
|
|
12
|
-
it("returns null when no register export exists", () => {
|
|
13
|
-
expect(getRegisterFn({ default: {} })).toBeNull();
|
|
14
|
-
});
|
|
15
|
-
it("creates a host API with addPage aliased to addRoute", () => {
|
|
16
|
-
const React = {};
|
|
17
|
-
const addRoute = vi.fn();
|
|
18
|
-
const addTool = vi.fn(() => "tool-id");
|
|
19
|
-
const api = createPluginRegisterHostApi({ React, addRoute, addTool });
|
|
20
|
-
api.addPage({ path: "/x", name: "x", element: "page" });
|
|
21
|
-
api.addRoute({ path: "/y", name: "y", element: "route" });
|
|
22
|
-
expect(api.React).toBe(React);
|
|
23
|
-
expect(addRoute).toHaveBeenCalledTimes(2);
|
|
24
|
-
expect(api.addTool({ id: "t", name: "T" })).toBe("tool-id");
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
//# sourceMappingURL=loadConsoleEntries.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loadConsoleEntries.test.js","sourceRoot":"","sources":["../../client/bootstrap/loadConsoleEntries.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,2BAA2B,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAErF,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;QAC1B,MAAM,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;QAC1B,MAAM,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,KAAK,GAAG,EAAS,CAAC;QACxB,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,2BAA2B,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAEtE,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAa,EAAE,CAAC,CAAC;QAC/D,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,OAAc,EAAE,CAAC,CAAC;QACjE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/runtime/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../client/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIzD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,aAAa,EAAE,MAAM,UAAU,CAAA;CAAE,QAE5E;AAED,wBAAgB,aAAa,IAAI,UAAU,CAG1C"}
|
package/dist/runtime/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/runtime/index.ts"],"names":[],"mappings":"AAEA,IAAI,YAAY,GAA8B,IAAI,CAAC;AAEnD,MAAM,UAAU,gBAAgB,CAAC,OAA4C;IAC3E,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,YAAY;QAAE,OAAO,YAAY,EAAE,CAAC;IACxC,OAAO,YAAY,CAAC;AACtB,CAAC"}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import type { UseWebSocketOptions } from "./types";
|
|
2
|
-
export declare function useWebSocket(options?: UseWebSocketOptions): {
|
|
3
|
-
connected: boolean;
|
|
4
|
-
connect: () => void;
|
|
5
|
-
disconnect: () => void;
|
|
6
|
-
send: (message: unknown) => void;
|
|
7
|
-
sendRequest: <T = unknown>(message: unknown) => Promise<T>;
|
|
8
|
-
manager: import("./manager").WebSocketManager;
|
|
9
|
-
};
|
|
10
|
-
export declare function useConfig(pluginName: string, options?: {
|
|
11
|
-
autoLoad?: boolean;
|
|
12
|
-
autoLoadSchema?: boolean;
|
|
13
|
-
}): {
|
|
14
|
-
config: unknown;
|
|
15
|
-
schema: unknown;
|
|
16
|
-
loading: boolean;
|
|
17
|
-
error: string | null;
|
|
18
|
-
connected: boolean;
|
|
19
|
-
getConfig: () => Promise<unknown>;
|
|
20
|
-
setConfig: (newConfig: unknown) => Promise<{
|
|
21
|
-
success?: boolean;
|
|
22
|
-
reloaded?: boolean;
|
|
23
|
-
message?: string;
|
|
24
|
-
}>;
|
|
25
|
-
getSchema: () => Promise<unknown>;
|
|
26
|
-
};
|
|
27
|
-
export declare function useConfigYaml(): {
|
|
28
|
-
yaml: string;
|
|
29
|
-
pluginKeys: string[];
|
|
30
|
-
loading: boolean;
|
|
31
|
-
error: string | null;
|
|
32
|
-
load: () => Promise<{
|
|
33
|
-
yaml: string;
|
|
34
|
-
pluginKeys: string[];
|
|
35
|
-
}>;
|
|
36
|
-
save: (content: string) => Promise<{
|
|
37
|
-
success: boolean;
|
|
38
|
-
message?: string;
|
|
39
|
-
}>;
|
|
40
|
-
};
|
|
41
|
-
export declare function useFiles(): {
|
|
42
|
-
tree: import("./types").FileTreeNode[];
|
|
43
|
-
loading: boolean;
|
|
44
|
-
error: string | null;
|
|
45
|
-
loadTree: () => Promise<import("./types").FileTreeNode[]>;
|
|
46
|
-
readFile: (filePath: string) => Promise<string>;
|
|
47
|
-
saveFile: (filePath: string, content: string) => Promise<{
|
|
48
|
-
success: boolean;
|
|
49
|
-
message?: string;
|
|
50
|
-
}>;
|
|
51
|
-
};
|
|
52
|
-
export declare function useEnvFiles(): {
|
|
53
|
-
files: {
|
|
54
|
-
name: string;
|
|
55
|
-
exists: boolean;
|
|
56
|
-
}[];
|
|
57
|
-
loading: boolean;
|
|
58
|
-
error: string | null;
|
|
59
|
-
listFiles: () => Promise<{
|
|
60
|
-
name: string;
|
|
61
|
-
exists: boolean;
|
|
62
|
-
}[]>;
|
|
63
|
-
getFile: (filename: string) => Promise<string>;
|
|
64
|
-
saveFile: (filename: string, content: string) => Promise<{
|
|
65
|
-
success: boolean;
|
|
66
|
-
message?: string;
|
|
67
|
-
}>;
|
|
68
|
-
};
|
|
69
|
-
export declare function useDatabase(): {
|
|
70
|
-
info: import("./types").DatabaseInfo | null;
|
|
71
|
-
tables: import("./types").TableInfo[];
|
|
72
|
-
loading: boolean;
|
|
73
|
-
error: string | null;
|
|
74
|
-
loadInfo: () => Promise<import("./types").DatabaseInfo>;
|
|
75
|
-
loadTables: () => Promise<import("./types").TableInfo[]>;
|
|
76
|
-
dropTable: (table: string) => Promise<{
|
|
77
|
-
success: boolean;
|
|
78
|
-
}>;
|
|
79
|
-
select: (table: string, page?: number, pageSize?: number, where?: unknown) => Promise<import("./types").SelectResult>;
|
|
80
|
-
insert: (table: string, row: unknown) => Promise<{
|
|
81
|
-
success: boolean;
|
|
82
|
-
}>;
|
|
83
|
-
update: (table: string, row: unknown, where: unknown) => Promise<{
|
|
84
|
-
success: boolean;
|
|
85
|
-
affected: number;
|
|
86
|
-
}>;
|
|
87
|
-
remove: (table: string, where: unknown) => Promise<{
|
|
88
|
-
success: boolean;
|
|
89
|
-
deleted: number;
|
|
90
|
-
}>;
|
|
91
|
-
kvGet: (table: string, key: string) => Promise<{
|
|
92
|
-
key: string;
|
|
93
|
-
value: unknown;
|
|
94
|
-
}>;
|
|
95
|
-
kvSet: (table: string, key: string, value: unknown, ttl?: number) => Promise<{
|
|
96
|
-
success: boolean;
|
|
97
|
-
}>;
|
|
98
|
-
kvDelete: (table: string, key: string) => Promise<{
|
|
99
|
-
success: boolean;
|
|
100
|
-
}>;
|
|
101
|
-
kvEntries: (table: string) => Promise<{
|
|
102
|
-
entries: import("./types").KvEntry[];
|
|
103
|
-
}>;
|
|
104
|
-
};
|
|
105
|
-
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../client/websocket/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB;;;;oBAWzB,OAAO;kBAEvC,CAAC,qBAAqB,OAAO;;EASjC;AAED,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE;;;;;;;2BA2BlF,OAAO;;;;;;EAqC5B;AAED,wBAAgB,aAAa;;;;;;;;;oBA2BT,MAAM;;;;EAsBzB;AAED,wBAAgB,QAAQ;;;;;yBAwBwB,MAAM;yBAEjC,MAAM,WAAW,MAAM;;;;EAS3C;AAED,wBAAgB,WAAW;;cAGwB,MAAM;gBAAU,OAAO;;;;;;;;wBAsBrD,MAAM;yBAQN,MAAM,WAAW,MAAM;;;;EAY3C;AAED,wBAAgB,WAAW;;;;;;;uBA0CmB,MAAM;;;oBAJT,MAAM,SAAS,MAAM,aAAa,MAAM,UAAU,OAAO;oBACzD,MAAM,OAAO,OAAO;;;oBACpB,MAAM,OAAO,OAAO,SAAS,OAAO;;;;oBACpC,MAAM,SAAS,OAAO;;;;mBAEvB,MAAM,OAAO,MAAM;;;;mBACnB,MAAM,OAAO,MAAM,SAAS,OAAO,QAAQ,MAAM;;;sBAC9C,MAAM,OAAO,MAAM;;;uBAClB,MAAM;;;EAanD"}
|