@cmdop/react 0.1.1
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 +271 -0
- package/dist/index.cjs +770 -0
- package/dist/index.d.cts +538 -0
- package/dist/index.d.ts +538 -0
- package/dist/index.js +732 -0
- package/package.json +57 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/index.tsx
|
|
32
|
+
var index_exports = {};
|
|
33
|
+
__export(index_exports, {
|
|
34
|
+
API_BASE_URL: () => import_core.API_BASE_URL,
|
|
35
|
+
AuthenticationError: () => import_core.AuthenticationError,
|
|
36
|
+
CMDOPError: () => import_core.CMDOPError,
|
|
37
|
+
CMDOPProvider: () => CMDOPProvider,
|
|
38
|
+
CMDOPWebSocketClient: () => CMDOPWebSocketClient,
|
|
39
|
+
CancelledError: () => import_core.CancelledError,
|
|
40
|
+
ConnectionError: () => import_core.ConnectionError,
|
|
41
|
+
DEFAULT_CONFIG: () => import_core.DEFAULT_CONFIG,
|
|
42
|
+
MachinesModule: () => import_core.MachinesModule,
|
|
43
|
+
NotFoundError: () => import_core.NotFoundError,
|
|
44
|
+
PermissionError: () => import_core.PermissionError,
|
|
45
|
+
ResourceExhaustedError: () => import_core.ResourceExhaustedError,
|
|
46
|
+
SessionError: () => import_core.SessionError,
|
|
47
|
+
SystemModule: () => import_core.SystemModule,
|
|
48
|
+
TimeoutError: () => import_core.TimeoutError,
|
|
49
|
+
UnavailableError: () => import_core.UnavailableError,
|
|
50
|
+
VERSION: () => import_core.VERSION,
|
|
51
|
+
WebSocketProvider: () => WebSocketProvider,
|
|
52
|
+
WorkspacesModule: () => import_core.WorkspacesModule,
|
|
53
|
+
api: () => import_core.api,
|
|
54
|
+
machines: () => import_core.machines,
|
|
55
|
+
system: () => import_core.system,
|
|
56
|
+
useAgent: () => useAgent,
|
|
57
|
+
useCMDOP: () => useCMDOP,
|
|
58
|
+
useMachine: () => useMachine,
|
|
59
|
+
useMachines: () => useMachines,
|
|
60
|
+
useRPC: () => useRPC,
|
|
61
|
+
useSubscription: () => useSubscription,
|
|
62
|
+
useTerminal: () => useTerminal,
|
|
63
|
+
useWebSocket: () => useWebSocket,
|
|
64
|
+
useWorkspace: () => useWorkspace,
|
|
65
|
+
useWorkspaces: () => useWorkspaces,
|
|
66
|
+
workspaces: () => import_core.workspaces
|
|
67
|
+
});
|
|
68
|
+
module.exports = __toCommonJS(index_exports);
|
|
69
|
+
var import_core = require("@cmdop/core");
|
|
70
|
+
|
|
71
|
+
// src/centrifugo/client.ts
|
|
72
|
+
var import_centrifuge = require("centrifuge");
|
|
73
|
+
var CMDOPWebSocketClient = class {
|
|
74
|
+
constructor(config) {
|
|
75
|
+
this.config = config;
|
|
76
|
+
}
|
|
77
|
+
centrifuge = null;
|
|
78
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
79
|
+
stateListeners = /* @__PURE__ */ new Set();
|
|
80
|
+
state = {
|
|
81
|
+
isConnected: false,
|
|
82
|
+
isConnecting: false,
|
|
83
|
+
error: null
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Connect to Centrifugo server
|
|
87
|
+
*/
|
|
88
|
+
async connect() {
|
|
89
|
+
if (this.centrifuge) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.updateState({ isConnecting: true, error: null });
|
|
93
|
+
try {
|
|
94
|
+
const token = await this.config.getToken();
|
|
95
|
+
this.centrifuge = new import_centrifuge.Centrifuge(this.config.url, {
|
|
96
|
+
token,
|
|
97
|
+
getToken: async () => {
|
|
98
|
+
return this.config.getToken();
|
|
99
|
+
},
|
|
100
|
+
debug: this.config.debug ?? false
|
|
101
|
+
});
|
|
102
|
+
this.centrifuge.on("connected", () => {
|
|
103
|
+
this.updateState({ isConnected: true, isConnecting: false, error: null });
|
|
104
|
+
});
|
|
105
|
+
this.centrifuge.on("disconnected", () => {
|
|
106
|
+
this.updateState({ isConnected: false, isConnecting: false });
|
|
107
|
+
});
|
|
108
|
+
this.centrifuge.on("error", (ctx) => {
|
|
109
|
+
this.updateState({ error: new Error(ctx.error?.message ?? "Connection error") });
|
|
110
|
+
});
|
|
111
|
+
this.centrifuge.connect();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
this.updateState({
|
|
114
|
+
isConnecting: false,
|
|
115
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
116
|
+
});
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Disconnect from server
|
|
122
|
+
*/
|
|
123
|
+
disconnect() {
|
|
124
|
+
if (this.centrifuge) {
|
|
125
|
+
this.subscriptions.forEach((sub) => sub.unsubscribe());
|
|
126
|
+
this.subscriptions.clear();
|
|
127
|
+
this.centrifuge.disconnect();
|
|
128
|
+
this.centrifuge = null;
|
|
129
|
+
this.updateState({ isConnected: false, isConnecting: false });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Subscribe to a channel
|
|
134
|
+
*/
|
|
135
|
+
subscribe(channel, onPublication, onError) {
|
|
136
|
+
if (!this.centrifuge) {
|
|
137
|
+
throw new Error("Not connected");
|
|
138
|
+
}
|
|
139
|
+
if (this.subscriptions.has(channel)) {
|
|
140
|
+
const existing = this.subscriptions.get(channel);
|
|
141
|
+
existing.on("publication", (ctx) => {
|
|
142
|
+
onPublication(ctx.data);
|
|
143
|
+
});
|
|
144
|
+
return () => this.unsubscribe(channel);
|
|
145
|
+
}
|
|
146
|
+
const subscription = this.centrifuge.newSubscription(channel);
|
|
147
|
+
subscription.on("publication", (ctx) => {
|
|
148
|
+
onPublication(ctx.data);
|
|
149
|
+
});
|
|
150
|
+
subscription.on("error", (ctx) => {
|
|
151
|
+
onError?.(new Error(ctx.error?.message ?? "Subscription error"));
|
|
152
|
+
});
|
|
153
|
+
subscription.subscribe();
|
|
154
|
+
this.subscriptions.set(channel, subscription);
|
|
155
|
+
return () => this.unsubscribe(channel);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Unsubscribe from a channel
|
|
159
|
+
*/
|
|
160
|
+
unsubscribe(channel) {
|
|
161
|
+
const subscription = this.subscriptions.get(channel);
|
|
162
|
+
if (subscription) {
|
|
163
|
+
subscription.unsubscribe();
|
|
164
|
+
this.subscriptions.delete(channel);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Make an RPC call via Centrifugo
|
|
169
|
+
*/
|
|
170
|
+
async rpc(method, data) {
|
|
171
|
+
if (!this.centrifuge) {
|
|
172
|
+
throw new Error("Not connected");
|
|
173
|
+
}
|
|
174
|
+
const result = await this.centrifuge.rpc(method, data);
|
|
175
|
+
return result.data;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Publish to a channel (fire-and-forget)
|
|
179
|
+
*/
|
|
180
|
+
async publish(channel, data) {
|
|
181
|
+
if (!this.centrifuge) {
|
|
182
|
+
throw new Error("Not connected");
|
|
183
|
+
}
|
|
184
|
+
const subscription = this.subscriptions.get(channel);
|
|
185
|
+
if (subscription) {
|
|
186
|
+
await subscription.publish(data);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get current connection state
|
|
191
|
+
*/
|
|
192
|
+
getState() {
|
|
193
|
+
return { ...this.state };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Add state change listener
|
|
197
|
+
*/
|
|
198
|
+
onStateChange(listener) {
|
|
199
|
+
this.stateListeners.add(listener);
|
|
200
|
+
return () => this.stateListeners.delete(listener);
|
|
201
|
+
}
|
|
202
|
+
updateState(partial) {
|
|
203
|
+
this.state = { ...this.state, ...partial };
|
|
204
|
+
this.stateListeners.forEach((listener) => listener(this.state));
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/centrifugo/provider.tsx
|
|
209
|
+
var import_react = require("react");
|
|
210
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
211
|
+
var WebSocketContext = (0, import_react.createContext)(null);
|
|
212
|
+
function WebSocketProvider({
|
|
213
|
+
children,
|
|
214
|
+
url,
|
|
215
|
+
getToken,
|
|
216
|
+
autoConnect = true,
|
|
217
|
+
debug = false
|
|
218
|
+
}) {
|
|
219
|
+
const clientRef = (0, import_react.useRef)(null);
|
|
220
|
+
const [state, setState] = (0, import_react.useState)({
|
|
221
|
+
isConnected: false,
|
|
222
|
+
isConnecting: false,
|
|
223
|
+
error: null
|
|
224
|
+
});
|
|
225
|
+
(0, import_react.useEffect)(() => {
|
|
226
|
+
const client = new CMDOPWebSocketClient({
|
|
227
|
+
url,
|
|
228
|
+
getToken,
|
|
229
|
+
debug
|
|
230
|
+
});
|
|
231
|
+
clientRef.current = client;
|
|
232
|
+
const unsubscribe = client.onStateChange(setState);
|
|
233
|
+
if (autoConnect) {
|
|
234
|
+
client.connect().catch((error) => {
|
|
235
|
+
console.error("[CMDOP WebSocket] Auto-connect failed:", error);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return () => {
|
|
239
|
+
unsubscribe();
|
|
240
|
+
client.disconnect();
|
|
241
|
+
clientRef.current = null;
|
|
242
|
+
};
|
|
243
|
+
}, [url, getToken, autoConnect, debug]);
|
|
244
|
+
const connect = (0, import_react.useCallback)(async () => {
|
|
245
|
+
if (clientRef.current) {
|
|
246
|
+
await clientRef.current.connect();
|
|
247
|
+
}
|
|
248
|
+
}, []);
|
|
249
|
+
const disconnect = (0, import_react.useCallback)(() => {
|
|
250
|
+
if (clientRef.current) {
|
|
251
|
+
clientRef.current.disconnect();
|
|
252
|
+
}
|
|
253
|
+
}, []);
|
|
254
|
+
const value = {
|
|
255
|
+
client: clientRef.current,
|
|
256
|
+
isConnected: state.isConnected,
|
|
257
|
+
isConnecting: state.isConnecting,
|
|
258
|
+
error: state.error,
|
|
259
|
+
connect,
|
|
260
|
+
disconnect
|
|
261
|
+
};
|
|
262
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WebSocketContext.Provider, { value, children });
|
|
263
|
+
}
|
|
264
|
+
function useWebSocket() {
|
|
265
|
+
const context = (0, import_react.useContext)(WebSocketContext);
|
|
266
|
+
if (!context) {
|
|
267
|
+
throw new Error("useWebSocket must be used within a WebSocketProvider");
|
|
268
|
+
}
|
|
269
|
+
return context;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/centrifugo/hooks.ts
|
|
273
|
+
var import_react2 = require("react");
|
|
274
|
+
function useSubscription(options) {
|
|
275
|
+
const { channel, enabled = true, onData, onError } = options;
|
|
276
|
+
const { client, isConnected } = useWebSocket();
|
|
277
|
+
const [data, setData] = (0, import_react2.useState)(null);
|
|
278
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
279
|
+
const [isSubscribed, setIsSubscribed] = (0, import_react2.useState)(false);
|
|
280
|
+
const onDataRef = (0, import_react2.useRef)(onData);
|
|
281
|
+
const onErrorRef = (0, import_react2.useRef)(onError);
|
|
282
|
+
onDataRef.current = onData;
|
|
283
|
+
onErrorRef.current = onError;
|
|
284
|
+
(0, import_react2.useEffect)(() => {
|
|
285
|
+
if (!client || !isConnected || !enabled || !channel) {
|
|
286
|
+
setIsSubscribed(false);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const handleData = (received) => {
|
|
290
|
+
setData(received);
|
|
291
|
+
onDataRef.current?.(received);
|
|
292
|
+
};
|
|
293
|
+
const handleError = (err) => {
|
|
294
|
+
setError(err);
|
|
295
|
+
onErrorRef.current?.(err);
|
|
296
|
+
};
|
|
297
|
+
try {
|
|
298
|
+
const unsubscribe = client.subscribe(channel, handleData, handleError);
|
|
299
|
+
setIsSubscribed(true);
|
|
300
|
+
setError(null);
|
|
301
|
+
return () => {
|
|
302
|
+
unsubscribe();
|
|
303
|
+
setIsSubscribed(false);
|
|
304
|
+
};
|
|
305
|
+
} catch (err) {
|
|
306
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
307
|
+
setError(error2);
|
|
308
|
+
onErrorRef.current?.(error2);
|
|
309
|
+
}
|
|
310
|
+
}, [client, isConnected, enabled, channel]);
|
|
311
|
+
return { data, error, isSubscribed };
|
|
312
|
+
}
|
|
313
|
+
function useRPC(options = {}) {
|
|
314
|
+
const { onError } = options;
|
|
315
|
+
const { client, isConnected } = useWebSocket();
|
|
316
|
+
const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
|
|
317
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
318
|
+
const onErrorRef = (0, import_react2.useRef)(onError);
|
|
319
|
+
onErrorRef.current = onError;
|
|
320
|
+
const call = (0, import_react2.useCallback)(
|
|
321
|
+
async (method, data) => {
|
|
322
|
+
if (!client || !isConnected) {
|
|
323
|
+
const err = new Error("WebSocket not connected");
|
|
324
|
+
setError(err);
|
|
325
|
+
onErrorRef.current?.(err);
|
|
326
|
+
throw err;
|
|
327
|
+
}
|
|
328
|
+
setIsLoading(true);
|
|
329
|
+
setError(null);
|
|
330
|
+
try {
|
|
331
|
+
const result = await client.rpc(method, data);
|
|
332
|
+
return result;
|
|
333
|
+
} catch (err) {
|
|
334
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
335
|
+
setError(error2);
|
|
336
|
+
onErrorRef.current?.(error2);
|
|
337
|
+
throw error2;
|
|
338
|
+
} finally {
|
|
339
|
+
setIsLoading(false);
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
[client, isConnected]
|
|
343
|
+
);
|
|
344
|
+
const reset = (0, import_react2.useCallback)(() => {
|
|
345
|
+
setError(null);
|
|
346
|
+
}, []);
|
|
347
|
+
return { call, isLoading, error, reset };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/hooks/useTerminal.ts
|
|
351
|
+
var import_react3 = require("react");
|
|
352
|
+
function useTerminal(options) {
|
|
353
|
+
const { sessionId, enabled = true, onOutput, onStatus, onError } = options;
|
|
354
|
+
const [output, setOutput] = (0, import_react3.useState)("");
|
|
355
|
+
const [status, setStatus] = (0, import_react3.useState)(null);
|
|
356
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
357
|
+
const [isConnecting, setIsConnecting] = (0, import_react3.useState)(false);
|
|
358
|
+
const onOutputRef = (0, import_react3.useRef)(onOutput);
|
|
359
|
+
const onStatusRef = (0, import_react3.useRef)(onStatus);
|
|
360
|
+
const onErrorRef = (0, import_react3.useRef)(onError);
|
|
361
|
+
onOutputRef.current = onOutput;
|
|
362
|
+
onStatusRef.current = onStatus;
|
|
363
|
+
onErrorRef.current = onError;
|
|
364
|
+
const { call, isLoading: isRpcLoading } = useRPC({
|
|
365
|
+
onError: (err) => {
|
|
366
|
+
setError(err);
|
|
367
|
+
onErrorRef.current?.(err);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
const { isSubscribed: isOutputSubscribed } = useSubscription({
|
|
371
|
+
channel: `terminal#${sessionId}#output`,
|
|
372
|
+
enabled: enabled && !!sessionId,
|
|
373
|
+
onData: (data) => {
|
|
374
|
+
const text = data.data;
|
|
375
|
+
setOutput((prev) => prev + text);
|
|
376
|
+
onOutputRef.current?.(text);
|
|
377
|
+
},
|
|
378
|
+
onError: (err) => {
|
|
379
|
+
setError(err);
|
|
380
|
+
onErrorRef.current?.(err);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
const { isSubscribed: isStatusSubscribed } = useSubscription({
|
|
384
|
+
channel: `terminal#${sessionId}#status`,
|
|
385
|
+
enabled: enabled && !!sessionId,
|
|
386
|
+
onData: (newStatus) => {
|
|
387
|
+
setStatus(newStatus);
|
|
388
|
+
onStatusRef.current?.(newStatus);
|
|
389
|
+
},
|
|
390
|
+
onError: (err) => {
|
|
391
|
+
setError(err);
|
|
392
|
+
onErrorRef.current?.(err);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
const isConnected = isOutputSubscribed && isStatusSubscribed;
|
|
396
|
+
const sendInput = (0, import_react3.useCallback)(
|
|
397
|
+
async (data) => {
|
|
398
|
+
if (!sessionId) return;
|
|
399
|
+
await call("terminal.input", {
|
|
400
|
+
session_id: sessionId,
|
|
401
|
+
data
|
|
402
|
+
});
|
|
403
|
+
},
|
|
404
|
+
[call, sessionId]
|
|
405
|
+
);
|
|
406
|
+
const resize = (0, import_react3.useCallback)(
|
|
407
|
+
async (cols, rows) => {
|
|
408
|
+
if (!sessionId) return;
|
|
409
|
+
await call("terminal.resize", {
|
|
410
|
+
session_id: sessionId,
|
|
411
|
+
cols,
|
|
412
|
+
rows
|
|
413
|
+
});
|
|
414
|
+
},
|
|
415
|
+
[call, sessionId]
|
|
416
|
+
);
|
|
417
|
+
const signal = (0, import_react3.useCallback)(
|
|
418
|
+
async (sig) => {
|
|
419
|
+
if (!sessionId) return;
|
|
420
|
+
const signalNum = typeof sig === "string" ? signalNameToNumber(sig) : sig;
|
|
421
|
+
await call("terminal.signal", {
|
|
422
|
+
session_id: sessionId,
|
|
423
|
+
signal: signalNum
|
|
424
|
+
});
|
|
425
|
+
},
|
|
426
|
+
[call, sessionId]
|
|
427
|
+
);
|
|
428
|
+
const clear = (0, import_react3.useCallback)(() => {
|
|
429
|
+
setOutput("");
|
|
430
|
+
}, []);
|
|
431
|
+
return {
|
|
432
|
+
isConnected,
|
|
433
|
+
isConnecting: isConnecting || isRpcLoading,
|
|
434
|
+
error,
|
|
435
|
+
output,
|
|
436
|
+
status,
|
|
437
|
+
sendInput,
|
|
438
|
+
resize,
|
|
439
|
+
signal,
|
|
440
|
+
clear
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
var SIGNAL_MAP = {
|
|
444
|
+
SIGHUP: 1,
|
|
445
|
+
SIGINT: 2,
|
|
446
|
+
SIGQUIT: 3,
|
|
447
|
+
SIGKILL: 9,
|
|
448
|
+
SIGTERM: 15,
|
|
449
|
+
SIGSTOP: 19,
|
|
450
|
+
SIGCONT: 18
|
|
451
|
+
};
|
|
452
|
+
function signalNameToNumber(name) {
|
|
453
|
+
const upper = name.toUpperCase();
|
|
454
|
+
return SIGNAL_MAP[upper] ?? parseInt(name, 10) ?? 15;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/hooks/useAgent.ts
|
|
458
|
+
var import_react4 = require("react");
|
|
459
|
+
function useAgent(options) {
|
|
460
|
+
const { sessionId, enabled = true, onToken, onToolCall, onToolResult, onDone, onError } = options;
|
|
461
|
+
const [isRunning, setIsRunning] = (0, import_react4.useState)(false);
|
|
462
|
+
const [streamingText, setStreamingText] = (0, import_react4.useState)("");
|
|
463
|
+
const [result, setResult] = (0, import_react4.useState)(null);
|
|
464
|
+
const [toolCalls, setToolCalls] = (0, import_react4.useState)([]);
|
|
465
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
466
|
+
const [requestId, setRequestId] = (0, import_react4.useState)(null);
|
|
467
|
+
const onTokenRef = (0, import_react4.useRef)(onToken);
|
|
468
|
+
const onToolCallRef = (0, import_react4.useRef)(onToolCall);
|
|
469
|
+
const onToolResultRef = (0, import_react4.useRef)(onToolResult);
|
|
470
|
+
const onDoneRef = (0, import_react4.useRef)(onDone);
|
|
471
|
+
const onErrorRef = (0, import_react4.useRef)(onError);
|
|
472
|
+
onTokenRef.current = onToken;
|
|
473
|
+
onToolCallRef.current = onToolCall;
|
|
474
|
+
onToolResultRef.current = onToolResult;
|
|
475
|
+
onDoneRef.current = onDone;
|
|
476
|
+
onErrorRef.current = onError;
|
|
477
|
+
const { call } = useRPC({
|
|
478
|
+
onError: (err) => {
|
|
479
|
+
setError(err);
|
|
480
|
+
setIsRunning(false);
|
|
481
|
+
onErrorRef.current?.(err);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
useSubscription({
|
|
485
|
+
channel: requestId ? `agent#${requestId}#events` : "",
|
|
486
|
+
enabled: enabled && !!requestId && isRunning,
|
|
487
|
+
onData: (event) => {
|
|
488
|
+
switch (event.type) {
|
|
489
|
+
case "token": {
|
|
490
|
+
const tokenData = event.data;
|
|
491
|
+
setStreamingText((prev) => prev + tokenData.text);
|
|
492
|
+
onTokenRef.current?.(tokenData.text);
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
case "tool_call": {
|
|
496
|
+
const toolCallData = event.data;
|
|
497
|
+
setToolCalls((prev) => [...prev, toolCallData]);
|
|
498
|
+
onToolCallRef.current?.(toolCallData);
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
case "tool_result": {
|
|
502
|
+
const toolResultData = event.data;
|
|
503
|
+
setToolCalls((prev) => prev.filter((tc) => tc.id !== toolResultData.id));
|
|
504
|
+
onToolResultRef.current?.(toolResultData);
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
case "done": {
|
|
508
|
+
const doneData = event.data;
|
|
509
|
+
setResult(doneData.text);
|
|
510
|
+
setIsRunning(false);
|
|
511
|
+
setRequestId(null);
|
|
512
|
+
onDoneRef.current?.(doneData);
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
case "error": {
|
|
516
|
+
const errorData = event.data;
|
|
517
|
+
const err = new Error(errorData.message);
|
|
518
|
+
setError(err);
|
|
519
|
+
setIsRunning(false);
|
|
520
|
+
setRequestId(null);
|
|
521
|
+
onErrorRef.current?.(err);
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
onError: (err) => {
|
|
527
|
+
setError(err);
|
|
528
|
+
setIsRunning(false);
|
|
529
|
+
onErrorRef.current?.(err);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
const run = (0, import_react4.useCallback)(
|
|
533
|
+
async (prompt, runOptions) => {
|
|
534
|
+
if (!sessionId) {
|
|
535
|
+
throw new Error("Session ID required");
|
|
536
|
+
}
|
|
537
|
+
setStreamingText("");
|
|
538
|
+
setResult(null);
|
|
539
|
+
setToolCalls([]);
|
|
540
|
+
setError(null);
|
|
541
|
+
setIsRunning(true);
|
|
542
|
+
try {
|
|
543
|
+
const response = await call("agent.run", {
|
|
544
|
+
session_id: sessionId,
|
|
545
|
+
prompt,
|
|
546
|
+
mode: runOptions?.mode,
|
|
547
|
+
timeout_seconds: runOptions?.timeoutSeconds,
|
|
548
|
+
output_schema: runOptions?.outputSchema
|
|
549
|
+
});
|
|
550
|
+
setRequestId(response.request_id);
|
|
551
|
+
if (response.text) {
|
|
552
|
+
setResult(response.text);
|
|
553
|
+
setIsRunning(false);
|
|
554
|
+
return response.text;
|
|
555
|
+
}
|
|
556
|
+
return new Promise((resolve, reject) => {
|
|
557
|
+
const checkDone = setInterval(() => {
|
|
558
|
+
if (result) {
|
|
559
|
+
clearInterval(checkDone);
|
|
560
|
+
resolve(result);
|
|
561
|
+
}
|
|
562
|
+
if (error) {
|
|
563
|
+
clearInterval(checkDone);
|
|
564
|
+
reject(error);
|
|
565
|
+
}
|
|
566
|
+
}, 100);
|
|
567
|
+
setTimeout(() => {
|
|
568
|
+
clearInterval(checkDone);
|
|
569
|
+
if (!result && !error) {
|
|
570
|
+
const timeoutError = new Error("Agent timeout");
|
|
571
|
+
setError(timeoutError);
|
|
572
|
+
setIsRunning(false);
|
|
573
|
+
reject(timeoutError);
|
|
574
|
+
}
|
|
575
|
+
}, 3e5);
|
|
576
|
+
});
|
|
577
|
+
} catch (err) {
|
|
578
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
579
|
+
setError(error2);
|
|
580
|
+
setIsRunning(false);
|
|
581
|
+
throw error2;
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
[call, sessionId, result, error]
|
|
585
|
+
);
|
|
586
|
+
const cancel = (0, import_react4.useCallback)(async () => {
|
|
587
|
+
if (!requestId) return;
|
|
588
|
+
try {
|
|
589
|
+
await call("agent.cancel", { request_id: requestId });
|
|
590
|
+
} finally {
|
|
591
|
+
setIsRunning(false);
|
|
592
|
+
setRequestId(null);
|
|
593
|
+
}
|
|
594
|
+
}, [call, requestId]);
|
|
595
|
+
const reset = (0, import_react4.useCallback)(() => {
|
|
596
|
+
setStreamingText("");
|
|
597
|
+
setResult(null);
|
|
598
|
+
setToolCalls([]);
|
|
599
|
+
setError(null);
|
|
600
|
+
setIsRunning(false);
|
|
601
|
+
setRequestId(null);
|
|
602
|
+
}, []);
|
|
603
|
+
return {
|
|
604
|
+
run,
|
|
605
|
+
isRunning,
|
|
606
|
+
streamingText,
|
|
607
|
+
result,
|
|
608
|
+
toolCalls,
|
|
609
|
+
error,
|
|
610
|
+
reset,
|
|
611
|
+
cancel
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// src/index.tsx
|
|
616
|
+
var import_react5 = require("react");
|
|
617
|
+
var import_react6 = require("react");
|
|
618
|
+
var import_core2 = require("@cmdop/core");
|
|
619
|
+
var import_swr = __toESM(require("swr"), 1);
|
|
620
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
621
|
+
var CMDOPContext = (0, import_react5.createContext)(null);
|
|
622
|
+
function CMDOPProvider({ children, apiKey, token }) {
|
|
623
|
+
const [currentToken, setCurrentToken] = (0, import_react6.useState)(token);
|
|
624
|
+
const setToken = (0, import_react6.useCallback)((newToken) => {
|
|
625
|
+
setCurrentToken(newToken);
|
|
626
|
+
import_core2.machines.setToken(newToken);
|
|
627
|
+
import_core2.workspaces.setToken(newToken);
|
|
628
|
+
}, []);
|
|
629
|
+
(0, import_react5.useMemo)(() => {
|
|
630
|
+
if (currentToken) {
|
|
631
|
+
import_core2.machines.setToken(currentToken);
|
|
632
|
+
import_core2.workspaces.setToken(currentToken);
|
|
633
|
+
}
|
|
634
|
+
}, [currentToken]);
|
|
635
|
+
const value = (0, import_react5.useMemo)(
|
|
636
|
+
() => ({
|
|
637
|
+
config: { apiKey },
|
|
638
|
+
isAuthenticated: !!currentToken,
|
|
639
|
+
setToken
|
|
640
|
+
}),
|
|
641
|
+
[apiKey, currentToken, setToken]
|
|
642
|
+
);
|
|
643
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CMDOPContext.Provider, { value, children });
|
|
644
|
+
}
|
|
645
|
+
function useCMDOP() {
|
|
646
|
+
const context = (0, import_react5.useContext)(CMDOPContext);
|
|
647
|
+
if (!context) {
|
|
648
|
+
throw new Error("useCMDOP must be used within a CMDOPProvider");
|
|
649
|
+
}
|
|
650
|
+
return context;
|
|
651
|
+
}
|
|
652
|
+
function useMachines(options = {}) {
|
|
653
|
+
const { page, pageSize, ...swrConfig } = options;
|
|
654
|
+
const { data, error, isLoading, isValidating, mutate } = (0, import_swr.default)(
|
|
655
|
+
["machines", page, pageSize],
|
|
656
|
+
async () => {
|
|
657
|
+
const response = await import_core2.machines.machines_machines.machinesList({
|
|
658
|
+
page,
|
|
659
|
+
page_size: pageSize
|
|
660
|
+
});
|
|
661
|
+
return response;
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
revalidateOnFocus: false,
|
|
665
|
+
...swrConfig
|
|
666
|
+
}
|
|
667
|
+
);
|
|
668
|
+
return {
|
|
669
|
+
machines: data?.results ?? [],
|
|
670
|
+
total: data?.count ?? 0,
|
|
671
|
+
isLoading,
|
|
672
|
+
isValidating,
|
|
673
|
+
error,
|
|
674
|
+
refetch: () => mutate()
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
function useMachine(machineId, options = {}) {
|
|
678
|
+
const { data, error, isLoading, mutate } = (0, import_swr.default)(
|
|
679
|
+
machineId ? ["machine", machineId] : null,
|
|
680
|
+
async () => {
|
|
681
|
+
if (!machineId) return null;
|
|
682
|
+
return import_core2.machines.machines_machines.machinesRetrieve(machineId);
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
revalidateOnFocus: false,
|
|
686
|
+
...options
|
|
687
|
+
}
|
|
688
|
+
);
|
|
689
|
+
return {
|
|
690
|
+
machine: data ?? null,
|
|
691
|
+
isLoading,
|
|
692
|
+
error,
|
|
693
|
+
refetch: () => mutate()
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
function useWorkspaces(options = {}) {
|
|
697
|
+
const { data, error, isLoading, isValidating, mutate } = (0, import_swr.default)(
|
|
698
|
+
"workspaces",
|
|
699
|
+
async () => {
|
|
700
|
+
return import_core2.workspaces.workspaces_workspaces.workspacesList();
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
revalidateOnFocus: false,
|
|
704
|
+
...options
|
|
705
|
+
}
|
|
706
|
+
);
|
|
707
|
+
return {
|
|
708
|
+
workspaces: data?.results ?? [],
|
|
709
|
+
total: data?.count ?? 0,
|
|
710
|
+
isLoading,
|
|
711
|
+
isValidating,
|
|
712
|
+
error,
|
|
713
|
+
refetch: () => mutate()
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
function useWorkspace(workspaceId, options = {}) {
|
|
717
|
+
const { data, error, isLoading, mutate } = (0, import_swr.default)(
|
|
718
|
+
workspaceId ? ["workspace", workspaceId] : null,
|
|
719
|
+
async () => {
|
|
720
|
+
if (!workspaceId) return null;
|
|
721
|
+
return import_core2.workspaces.workspaces_workspaces.workspacesRetrieve(workspaceId);
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
revalidateOnFocus: false,
|
|
725
|
+
...options
|
|
726
|
+
}
|
|
727
|
+
);
|
|
728
|
+
return {
|
|
729
|
+
workspace: data ?? null,
|
|
730
|
+
isLoading,
|
|
731
|
+
error,
|
|
732
|
+
refetch: () => mutate()
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
736
|
+
0 && (module.exports = {
|
|
737
|
+
API_BASE_URL,
|
|
738
|
+
AuthenticationError,
|
|
739
|
+
CMDOPError,
|
|
740
|
+
CMDOPProvider,
|
|
741
|
+
CMDOPWebSocketClient,
|
|
742
|
+
CancelledError,
|
|
743
|
+
ConnectionError,
|
|
744
|
+
DEFAULT_CONFIG,
|
|
745
|
+
MachinesModule,
|
|
746
|
+
NotFoundError,
|
|
747
|
+
PermissionError,
|
|
748
|
+
ResourceExhaustedError,
|
|
749
|
+
SessionError,
|
|
750
|
+
SystemModule,
|
|
751
|
+
TimeoutError,
|
|
752
|
+
UnavailableError,
|
|
753
|
+
VERSION,
|
|
754
|
+
WebSocketProvider,
|
|
755
|
+
WorkspacesModule,
|
|
756
|
+
api,
|
|
757
|
+
machines,
|
|
758
|
+
system,
|
|
759
|
+
useAgent,
|
|
760
|
+
useCMDOP,
|
|
761
|
+
useMachine,
|
|
762
|
+
useMachines,
|
|
763
|
+
useRPC,
|
|
764
|
+
useSubscription,
|
|
765
|
+
useTerminal,
|
|
766
|
+
useWebSocket,
|
|
767
|
+
useWorkspace,
|
|
768
|
+
useWorkspaces,
|
|
769
|
+
workspaces
|
|
770
|
+
});
|