@blaxel/core 0.2.0 → 0.2.1-dev.46

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.
@@ -63,16 +63,13 @@ class BlaxelMcpClientTransport {
63
63
  this.onerror?.(error);
64
64
  };
65
65
  this._socket.onopen = () => {
66
- logger_js_1.logger.debug("WebSocket opened");
67
66
  resolve();
68
67
  };
69
68
  this._socket.onclose = () => {
70
- logger_js_1.logger.debug("WebSocket closed");
71
69
  this.onclose?.();
72
70
  this._socket = undefined;
73
71
  };
74
72
  this._socket.onmessage = (event) => {
75
- logger_js_1.logger.debug("WebSocket message received");
76
73
  let message;
77
74
  try {
78
75
  let dataString;
@@ -15,4 +15,5 @@ export declare class SandboxAction {
15
15
  get forcedUrl(): string | null;
16
16
  get url(): string;
17
17
  handleResponseError(response: Response, data: unknown, error: unknown): void;
18
+ websocket(path: string): WebSocket | null;
18
19
  }
@@ -70,5 +70,37 @@ class SandboxAction {
70
70
  throw new ResponseError(response, data, error);
71
71
  }
72
72
  }
73
+ websocket(path) {
74
+ let ws = null;
75
+ // Build ws:// or wss:// URL from baseUrl
76
+ let baseUrl = this.url.replace(/^http/, 'ws');
77
+ if (baseUrl.endsWith('/'))
78
+ baseUrl = baseUrl.slice(0, -1);
79
+ const wsUrl = `${baseUrl}/ws/${path}?token=${settings_js_1.settings.token}`;
80
+ // Use isomorphic WebSocket: browser or Node.js
81
+ let WS = undefined;
82
+ if (typeof globalThis.WebSocket !== 'undefined') {
83
+ WS = globalThis.WebSocket;
84
+ }
85
+ else {
86
+ try {
87
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
88
+ WS = require('ws');
89
+ }
90
+ catch {
91
+ WS = undefined;
92
+ }
93
+ }
94
+ if (!WS)
95
+ throw new Error('WebSocket is not available in this environment');
96
+ try {
97
+ ws = typeof WS === 'function' ? new WS(wsUrl) : new WS(wsUrl);
98
+ }
99
+ catch (err) {
100
+ console.error('WebSocket connection error:', err);
101
+ throw err;
102
+ }
103
+ return ws;
104
+ }
73
105
  }
74
106
  exports.SandboxAction = SandboxAction;
@@ -1,5 +1,5 @@
1
1
  import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';
2
- import type { DeleteFilesystemByPathData, GetFilesystemByPathData, PutFilesystemByPathData, DeleteNetworkProcessByPidMonitorData, PostNetworkProcessByPidMonitorData, GetNetworkProcessByPidPortsData, GetProcessData, PostProcessData, DeleteProcessByIdentifierData, GetProcessByIdentifierData, DeleteProcessByIdentifierKillData, GetProcessByIdentifierLogsData, GetProcessByIdentifierLogsStreamData } from './types.gen';
2
+ import type { DeleteFilesystemByPathData, GetFilesystemByPathData, PutFilesystemByPathData, DeleteNetworkProcessByPidMonitorData, PostNetworkProcessByPidMonitorData, GetNetworkProcessByPidPortsData, GetProcessData, PostProcessData, DeleteProcessByIdentifierData, GetProcessByIdentifierData, DeleteProcessByIdentifierKillData, GetProcessByIdentifierLogsData, GetProcessByIdentifierLogsStreamData, GetWatchFilesystemByPathData } from './types.gen';
3
3
  export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
4
4
  /**
5
5
  * You can provide a client instance returned by `createClient()` instead of
@@ -82,9 +82,12 @@ export declare const getProcessByIdentifierLogs: <ThrowOnError extends boolean =
82
82
  [key: string]: string;
83
83
  }, import("./types.gen").ErrorResponse, ThrowOnError>;
84
84
  /**
85
- * Get process logs in realtime
86
- * Get the stdout and stderr output of a process in realtime
85
+ * Stream process logs in real time
86
+ * Streams the stdout and stderr output of a process in real time, one line per log, prefixed with 'stdout:' or 'stderr:'. Closes when the process exits or the client disconnects.
87
87
  */
88
- export declare const getProcessByIdentifierLogsStream: <ThrowOnError extends boolean = false>(options: Options<GetProcessByIdentifierLogsStreamData, ThrowOnError>) => import("@hey-api/client-fetch").RequestResult<{
89
- [key: string]: string;
90
- }, import("./types.gen").ErrorResponse, ThrowOnError>;
88
+ export declare const getProcessByIdentifierLogsStream: <ThrowOnError extends boolean = false>(options: Options<GetProcessByIdentifierLogsStreamData, ThrowOnError>) => import("@hey-api/client-fetch").RequestResult<string, import("./types.gen").ErrorResponse, ThrowOnError>;
89
+ /**
90
+ * Stream file modification events in a directory
91
+ * Streams the path of modified files (one per line) in the given directory. Closes when the client disconnects.
92
+ */
93
+ export declare const getWatchFilesystemByPath: <ThrowOnError extends boolean = false>(options: Options<GetWatchFilesystemByPathData, ThrowOnError>) => import("@hey-api/client-fetch").RequestResult<string, import("./types.gen").ErrorResponse, ThrowOnError>;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  // This file is auto-generated by @hey-api/openapi-ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.getProcessByIdentifierLogsStream = exports.getProcessByIdentifierLogs = exports.deleteProcessByIdentifierKill = exports.getProcessByIdentifier = exports.deleteProcessByIdentifier = exports.postProcess = exports.getProcess = exports.getNetworkProcessByPidPorts = exports.postNetworkProcessByPidMonitor = exports.deleteNetworkProcessByPidMonitor = exports.putFilesystemByPath = exports.getFilesystemByPath = exports.deleteFilesystemByPath = void 0;
4
+ exports.getWatchFilesystemByPath = exports.getProcessByIdentifierLogsStream = exports.getProcessByIdentifierLogs = exports.deleteProcessByIdentifierKill = exports.getProcessByIdentifier = exports.deleteProcessByIdentifier = exports.postProcess = exports.getProcess = exports.getNetworkProcessByPidPorts = exports.postNetworkProcessByPidMonitor = exports.deleteNetworkProcessByPidMonitor = exports.putFilesystemByPath = exports.getFilesystemByPath = exports.deleteFilesystemByPath = void 0;
5
5
  const client_gen_1 = require("./client.gen");
6
6
  /**
7
7
  * Delete file or directory
@@ -152,8 +152,8 @@ const getProcessByIdentifierLogs = (options) => {
152
152
  };
153
153
  exports.getProcessByIdentifierLogs = getProcessByIdentifierLogs;
154
154
  /**
155
- * Get process logs in realtime
156
- * Get the stdout and stderr output of a process in realtime
155
+ * Stream process logs in real time
156
+ * Streams the stdout and stderr output of a process in real time, one line per log, prefixed with 'stdout:' or 'stderr:'. Closes when the process exits or the client disconnects.
157
157
  */
158
158
  const getProcessByIdentifierLogsStream = (options) => {
159
159
  return (options.client ?? client_gen_1.client).get({
@@ -162,3 +162,14 @@ const getProcessByIdentifierLogsStream = (options) => {
162
162
  });
163
163
  };
164
164
  exports.getProcessByIdentifierLogsStream = getProcessByIdentifierLogsStream;
165
+ /**
166
+ * Stream file modification events in a directory
167
+ * Streams the path of modified files (one per line) in the given directory. Closes when the client disconnects.
168
+ */
169
+ const getWatchFilesystemByPath = (options) => {
170
+ return (options.client ?? client_gen_1.client).get({
171
+ url: '/watch/filesystem/{path}',
172
+ ...options
173
+ });
174
+ };
175
+ exports.getWatchFilesystemByPath = getWatchFilesystemByPath;
@@ -43,7 +43,6 @@ export type ProcessKillRequest = {
43
43
  export type ProcessRequest = {
44
44
  command: string;
45
45
  name?: string;
46
- streamLogs?: boolean;
47
46
  timeout?: number;
48
47
  waitForCompletion?: boolean;
49
48
  waitForPorts?: Array<number>;
@@ -391,12 +390,7 @@ export type GetProcessByIdentifierLogsData = {
391
390
  */
392
391
  identifier: string;
393
392
  };
394
- query?: {
395
- /**
396
- * Stream logs
397
- */
398
- stream?: boolean;
399
- };
393
+ query?: never;
400
394
  url: '/process/{identifier}/logs';
401
395
  };
402
396
  export type GetProcessByIdentifierLogsErrors = {
@@ -427,12 +421,7 @@ export type GetProcessByIdentifierLogsStreamData = {
427
421
  */
428
422
  identifier: string;
429
423
  };
430
- query?: {
431
- /**
432
- * Stream logs
433
- */
434
- stream?: boolean;
435
- };
424
+ query?: never;
436
425
  url: '/process/{identifier}/logs/stream';
437
426
  };
438
427
  export type GetProcessByIdentifierLogsStreamErrors = {
@@ -448,13 +437,40 @@ export type GetProcessByIdentifierLogsStreamErrors = {
448
437
  export type GetProcessByIdentifierLogsStreamError = GetProcessByIdentifierLogsStreamErrors[keyof GetProcessByIdentifierLogsStreamErrors];
449
438
  export type GetProcessByIdentifierLogsStreamResponses = {
450
439
  /**
451
- * Process logs
440
+ * Stream of process logs, one line per log (prefixed with stdout:/stderr:)
452
441
  */
453
- 200: {
454
- [key: string]: string;
455
- };
442
+ 200: string;
456
443
  };
457
444
  export type GetProcessByIdentifierLogsStreamResponse = GetProcessByIdentifierLogsStreamResponses[keyof GetProcessByIdentifierLogsStreamResponses];
445
+ export type GetWatchFilesystemByPathData = {
446
+ body?: never;
447
+ path: {
448
+ /**
449
+ * Directory path to watch
450
+ */
451
+ path: string;
452
+ };
453
+ query?: never;
454
+ url: '/watch/filesystem/{path}';
455
+ };
456
+ export type GetWatchFilesystemByPathErrors = {
457
+ /**
458
+ * Invalid path
459
+ */
460
+ 400: ErrorResponse;
461
+ /**
462
+ * Internal server error
463
+ */
464
+ 500: ErrorResponse;
465
+ };
466
+ export type GetWatchFilesystemByPathError = GetWatchFilesystemByPathErrors[keyof GetWatchFilesystemByPathErrors];
467
+ export type GetWatchFilesystemByPathResponses = {
468
+ /**
469
+ * Stream of modified file paths, one per line
470
+ */
471
+ 200: string;
472
+ };
473
+ export type GetWatchFilesystemByPathResponse = GetWatchFilesystemByPathResponses[keyof GetWatchFilesystemByPathResponses];
458
474
  export type ClientOptions = {
459
475
  baseUrl: `${string}://localhost:8080` | (string & {});
460
476
  };
@@ -14,5 +14,31 @@ export declare class SandboxFileSystem extends SandboxAction {
14
14
  rm(path: string, recursive?: boolean): Promise<SuccessResponse>;
15
15
  ls(path: string): Promise<Directory>;
16
16
  cp(source: string, destination: string): Promise<CopyResponse>;
17
+ /**
18
+ * Watch for changes in a directory. Calls the callback with the changed file path (and optionally its content).
19
+ * Returns a handle with a close() method to stop watching.
20
+ * @param path Directory to watch
21
+ * @param callback Function called on each change: (filePath, content?)
22
+ * @param withContent If true, also fetches and passes the file content (default: false)
23
+ */
24
+ watch(path: string, callback: (filePath: string, content?: string) => void | Promise<void>, options?: {
25
+ ws?: boolean;
26
+ onError?: (error: Error) => void;
27
+ withContent: boolean;
28
+ }): {
29
+ close: () => void;
30
+ };
31
+ wsWatch(path: string, callback: (filePath: string, content?: string) => void | Promise<void>, options?: {
32
+ onError?: (error: Error) => void;
33
+ withContent: boolean;
34
+ }): {
35
+ close: () => void;
36
+ };
37
+ sseWatch(path: string, callback: (filePath: string, content?: string) => void | Promise<void>, options?: {
38
+ onError?: (error: Error) => void;
39
+ withContent: boolean;
40
+ }): {
41
+ close: () => void;
42
+ };
17
43
  private formatPath;
18
44
  }
@@ -111,6 +111,197 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
111
111
  }
112
112
  throw new Error("Unsupported file type");
113
113
  }
114
+ /**
115
+ * Watch for changes in a directory. Calls the callback with the changed file path (and optionally its content).
116
+ * Returns a handle with a close() method to stop watching.
117
+ * @param path Directory to watch
118
+ * @param callback Function called on each change: (filePath, content?)
119
+ * @param withContent If true, also fetches and passes the file content (default: false)
120
+ */
121
+ watch(path, callback, options) {
122
+ if (options?.ws) {
123
+ return this.wsWatch(path, callback, options);
124
+ }
125
+ return this.sseWatch(path, callback, options);
126
+ }
127
+ wsWatch(path, callback, options) {
128
+ path = this.formatPath(path);
129
+ let closed = false;
130
+ let ws = this.websocket(`watch/filesystem${path.startsWith('/') ? path : '/' + path}`);
131
+ let pingInterval = null;
132
+ let pongTimeout = null;
133
+ const PING_INTERVAL_MS = 30000;
134
+ const PONG_TIMEOUT_MS = 10000;
135
+ function sendPing() {
136
+ if (ws && ws.readyState === ws.OPEN) {
137
+ try {
138
+ ws.send(JSON.stringify({ type: 'ping' }));
139
+ }
140
+ catch { }
141
+ // Set pong timeout
142
+ if (pongTimeout)
143
+ clearTimeout(pongTimeout);
144
+ pongTimeout = setTimeout(() => {
145
+ // No pong received in time, close connection
146
+ if (ws && typeof ws.close === 'function')
147
+ ws.close();
148
+ }, PONG_TIMEOUT_MS);
149
+ }
150
+ }
151
+ if (ws) {
152
+ ws.onmessage = async (event) => {
153
+ if (closed)
154
+ return;
155
+ let data;
156
+ try {
157
+ data = typeof event.data === 'string' ? event.data : event.data;
158
+ if (!data)
159
+ return;
160
+ // Accept both JSON and plain string (file path)
161
+ let payload;
162
+ try {
163
+ payload = JSON.parse(data);
164
+ }
165
+ catch {
166
+ payload = { name: data, event: undefined };
167
+ }
168
+ // Handle ping/pong
169
+ if (payload.type === 'ping') {
170
+ // Respond to ping with pong
171
+ if (ws && ws.readyState === ws.OPEN) {
172
+ try {
173
+ ws.send(JSON.stringify({ type: 'pong' }));
174
+ }
175
+ catch { }
176
+ }
177
+ return;
178
+ }
179
+ if (payload.type === 'pong') {
180
+ // Pong received, clear pong timeout
181
+ if (pongTimeout)
182
+ clearTimeout(pongTimeout);
183
+ pongTimeout = null;
184
+ return;
185
+ }
186
+ const filePath = payload.name || payload.path || data;
187
+ if (!filePath)
188
+ return;
189
+ if (options?.withContent) {
190
+ try {
191
+ const content = await this.read(filePath);
192
+ await callback(filePath, content);
193
+ }
194
+ catch (e) {
195
+ await callback(filePath, undefined);
196
+ }
197
+ }
198
+ else {
199
+ await callback(filePath);
200
+ }
201
+ }
202
+ catch (err) {
203
+ if (options?.onError)
204
+ options.onError(err);
205
+ }
206
+ };
207
+ ws.onerror = (err) => {
208
+ if (options?.onError)
209
+ options.onError(err instanceof Error ? err : new Error(String(err)));
210
+ closed = true;
211
+ if (ws && typeof ws.close === 'function')
212
+ ws.close();
213
+ };
214
+ ws.onclose = () => {
215
+ closed = true;
216
+ ws = null;
217
+ if (pingInterval)
218
+ clearInterval(pingInterval);
219
+ if (pongTimeout)
220
+ clearTimeout(pongTimeout);
221
+ };
222
+ // Start ping interval
223
+ pingInterval = setInterval(sendPing, PING_INTERVAL_MS);
224
+ }
225
+ return {
226
+ close: () => {
227
+ closed = true;
228
+ if (ws && typeof ws.close === 'function')
229
+ ws.close();
230
+ ws = null;
231
+ if (pingInterval)
232
+ clearInterval(pingInterval);
233
+ if (pongTimeout)
234
+ clearTimeout(pongTimeout);
235
+ },
236
+ };
237
+ }
238
+ sseWatch(path, callback, options) {
239
+ path = this.formatPath(path);
240
+ let closed = false;
241
+ let controller = new AbortController();
242
+ const start = async () => {
243
+ const { response, data, error } = await (0, index_js_1.getWatchFilesystemByPath)({
244
+ path: { path },
245
+ baseUrl: this.url,
246
+ parseAs: 'stream',
247
+ signal: controller.signal,
248
+ });
249
+ if (error)
250
+ throw error;
251
+ const stream = data ?? response.body;
252
+ if (!stream)
253
+ throw new Error('No stream returned');
254
+ const reader = stream.getReader();
255
+ let buffer = '';
256
+ const decoder = new TextDecoder();
257
+ try {
258
+ while (!closed) {
259
+ const { value, done } = await reader.read();
260
+ if (done)
261
+ break;
262
+ buffer += decoder.decode(value, { stream: true });
263
+ let lines = buffer.split('\n');
264
+ buffer = lines.pop();
265
+ for (const line of lines) {
266
+ const filePath = line.trim();
267
+ if (!filePath)
268
+ continue;
269
+ if (options?.withContent) {
270
+ try {
271
+ const content = await this.read(filePath);
272
+ await callback(filePath, content);
273
+ }
274
+ catch (e) {
275
+ await callback(filePath, undefined);
276
+ }
277
+ }
278
+ else {
279
+ await callback(filePath);
280
+ }
281
+ }
282
+ }
283
+ }
284
+ finally {
285
+ reader.releaseLock();
286
+ }
287
+ };
288
+ start().catch((err) => {
289
+ // Suppress AbortError when closing
290
+ if (!(err && err.name === 'AbortError')) {
291
+ if (options?.onError) {
292
+ options.onError(err);
293
+ }
294
+ }
295
+ closed = true;
296
+ controller?.abort();
297
+ });
298
+ return {
299
+ close: () => {
300
+ closed = true;
301
+ controller?.abort();
302
+ },
303
+ };
304
+ }
114
305
  formatPath(path) {
115
306
  if (path === "/") {
116
307
  return path;
@@ -3,7 +3,33 @@ import { SandboxAction } from "./action.js";
3
3
  import { DeleteProcessByIdentifierKillResponse, DeleteProcessByIdentifierResponse, GetProcessByIdentifierResponse, GetProcessResponse, PostProcessResponse, ProcessRequest } from "./client/index.js";
4
4
  export declare class SandboxProcess extends SandboxAction {
5
5
  constructor(sandbox: Sandbox);
6
+ streamLogs(identifier: string, options: {
7
+ ws?: boolean;
8
+ onLog?: (log: string) => void;
9
+ onStdout?: (stdout: string) => void;
10
+ onStderr?: (stderr: string) => void;
11
+ }): {
12
+ close: () => void;
13
+ };
14
+ wsStreamLogs(identifier: string, options: {
15
+ onLog?: (log: string) => void;
16
+ onStdout?: (stdout: string) => void;
17
+ onStderr?: (stderr: string) => void;
18
+ }): {
19
+ close: () => void;
20
+ };
21
+ sseStreamLogs(identifier: string, options: {
22
+ onLog?: (log: string) => void;
23
+ onStdout?: (stdout: string) => void;
24
+ onStderr?: (stderr: string) => void;
25
+ }): {
26
+ close: () => void;
27
+ };
6
28
  exec(process: ProcessRequest): Promise<PostProcessResponse>;
29
+ wait(identifier: string, { maxWait, interval }?: {
30
+ maxWait?: number;
31
+ interval?: number;
32
+ }): Promise<GetProcessByIdentifierResponse>;
7
33
  get(identifier: string): Promise<GetProcessByIdentifierResponse>;
8
34
  list(): Promise<GetProcessResponse>;
9
35
  stop(identifier: string): Promise<DeleteProcessByIdentifierResponse>;
@@ -1,12 +1,176 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SandboxProcess = void 0;
4
+ const settings_js_1 = require("../common/settings.js");
4
5
  const action_js_1 = require("./action.js");
5
6
  const index_js_1 = require("./client/index.js");
6
7
  class SandboxProcess extends action_js_1.SandboxAction {
7
8
  constructor(sandbox) {
8
9
  super(sandbox);
9
10
  }
11
+ streamLogs(identifier, options) {
12
+ if (options.ws) {
13
+ return this.wsStreamLogs(identifier, options);
14
+ }
15
+ return this.sseStreamLogs(identifier, options);
16
+ }
17
+ wsStreamLogs(identifier, options) {
18
+ let closed = false;
19
+ let ws = this.websocket(`process/${identifier}/logs/stream`);
20
+ let pingInterval = null;
21
+ let pongTimeout = null;
22
+ const PING_INTERVAL_MS = 30000;
23
+ const PONG_TIMEOUT_MS = 10000;
24
+ function sendPing() {
25
+ if (ws && ws.readyState === ws.OPEN) {
26
+ try {
27
+ ws.send(JSON.stringify({ type: 'ping' }));
28
+ }
29
+ catch { }
30
+ // Set pong timeout
31
+ if (pongTimeout)
32
+ clearTimeout(pongTimeout);
33
+ pongTimeout = setTimeout(() => {
34
+ // No pong received in time, close connection
35
+ if (ws && typeof ws.close === 'function')
36
+ ws.close();
37
+ }, PONG_TIMEOUT_MS);
38
+ }
39
+ }
40
+ if (ws) {
41
+ ws.onmessage = (event) => {
42
+ if (closed)
43
+ return;
44
+ let data;
45
+ try {
46
+ data = typeof event.data === 'string' ? event.data : event.data;
47
+ if (!data)
48
+ return;
49
+ let payload;
50
+ try {
51
+ payload = JSON.parse(data);
52
+ }
53
+ catch {
54
+ payload = { log: data };
55
+ }
56
+ // Handle ping/pong
57
+ if (payload.type === 'ping') {
58
+ // Respond to ping with pong
59
+ if (ws && ws.readyState === ws.OPEN) {
60
+ try {
61
+ ws.send(JSON.stringify({ type: 'pong' }));
62
+ }
63
+ catch { }
64
+ }
65
+ return;
66
+ }
67
+ if (payload.type === 'pong') {
68
+ // Pong received, clear pong timeout
69
+ if (pongTimeout)
70
+ clearTimeout(pongTimeout);
71
+ pongTimeout = null;
72
+ return;
73
+ }
74
+ if (payload.type === 'log') {
75
+ const logLine = payload.log || "";
76
+ if (typeof logLine === 'string') {
77
+ if (logLine.startsWith('stdout:')) {
78
+ options.onStdout?.(logLine.slice(7));
79
+ options.onLog?.(logLine.slice(7));
80
+ }
81
+ else if (logLine.startsWith('stderr:')) {
82
+ options.onStderr?.(logLine.slice(7));
83
+ options.onLog?.(logLine.slice(7));
84
+ }
85
+ else {
86
+ options.onLog?.(logLine);
87
+ }
88
+ }
89
+ }
90
+ }
91
+ catch (err) {
92
+ console.error('WebSocket log stream error:', err);
93
+ }
94
+ };
95
+ ws.onerror = (err) => {
96
+ closed = true;
97
+ if (ws && typeof ws.close === 'function')
98
+ ws.close();
99
+ };
100
+ ws.onclose = () => {
101
+ closed = true;
102
+ ws = null;
103
+ if (pingInterval)
104
+ clearInterval(pingInterval);
105
+ if (pongTimeout)
106
+ clearTimeout(pongTimeout);
107
+ };
108
+ // Start ping interval
109
+ pingInterval = setInterval(sendPing, PING_INTERVAL_MS);
110
+ }
111
+ return {
112
+ close: () => {
113
+ closed = true;
114
+ if (ws && typeof ws.close === 'function')
115
+ ws.close();
116
+ ws = null;
117
+ if (pingInterval)
118
+ clearInterval(pingInterval);
119
+ if (pongTimeout)
120
+ clearTimeout(pongTimeout);
121
+ },
122
+ };
123
+ }
124
+ sseStreamLogs(identifier, options) {
125
+ const controller = new AbortController();
126
+ (async () => {
127
+ try {
128
+ const stream = await fetch(`${this.url}/process/${identifier}/logs/stream`, {
129
+ method: 'GET',
130
+ signal: controller.signal,
131
+ headers: settings_js_1.settings.headers,
132
+ });
133
+ if (stream.status !== 200) {
134
+ throw new Error(`Failed to stream logs: ${await stream.text()}`);
135
+ }
136
+ if (!stream.body)
137
+ throw new Error('No stream body');
138
+ const reader = stream.body.getReader();
139
+ const decoder = new TextDecoder();
140
+ let buffer = '';
141
+ while (true) {
142
+ const { done, value } = await reader.read();
143
+ if (done)
144
+ break;
145
+ buffer += decoder.decode(value, { stream: true });
146
+ let lines = buffer.split(/\r?\n/);
147
+ buffer = lines.pop();
148
+ for (const line of lines) {
149
+ if (line.startsWith('stdout:')) {
150
+ options.onStdout?.(line.slice(7));
151
+ options.onLog?.(line.slice(7));
152
+ }
153
+ else if (line.startsWith('stderr:')) {
154
+ options.onStderr?.(line.slice(7));
155
+ options.onLog?.(line.slice(7));
156
+ }
157
+ else {
158
+ options.onLog?.(line);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ catch (err) {
164
+ if (err && err.name !== 'AbortError') {
165
+ console.error("Stream error:", err);
166
+ throw err;
167
+ }
168
+ }
169
+ })();
170
+ return {
171
+ close: () => controller.abort(),
172
+ };
173
+ }
10
174
  async exec(process) {
11
175
  const { response, data, error } = await (0, index_js_1.postProcess)({
12
176
  body: process,
@@ -15,6 +179,25 @@ class SandboxProcess extends action_js_1.SandboxAction {
15
179
  this.handleResponseError(response, data, error);
16
180
  return data;
17
181
  }
182
+ async wait(identifier, { maxWait = 60000, interval = 1000 } = {}) {
183
+ const startTime = Date.now();
184
+ let status = "running";
185
+ let data = await this.get(identifier);
186
+ while (status === "running") {
187
+ await new Promise((resolve) => setTimeout(resolve, interval));
188
+ try {
189
+ data = await this.get(identifier);
190
+ status = data.status ?? "running";
191
+ }
192
+ catch {
193
+ break;
194
+ }
195
+ if (Date.now() - startTime > maxWait) {
196
+ throw new Error("Process did not finish in time");
197
+ }
198
+ }
199
+ return data;
200
+ }
18
201
  async get(identifier) {
19
202
  const { response, data, error } = await (0, index_js_1.getProcessByIdentifier)({
20
203
  path: { identifier },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blaxel/core",
3
- "version": "0.2.0",
3
+ "version": "0.2.1-dev.46",
4
4
  "description": "Blaxel Core SDK for TypeScript",
5
5
  "license": "MIT",
6
6
  "author": "Blaxel, INC (https://blaxel.ai)",
@@ -1,32 +0,0 @@
1
- declare class BlBatch {
2
- /**
3
- * Lists all remote batch jobs for a given name
4
- * @param name - The name of the job to list
5
- */
6
- list(name: string): void;
7
- /**
8
- * Creates a new remote batch job configuration.
9
- * This is used for remote execution management, not for local execution.
10
- * @param name - The name of the job
11
- * @param args - Array of argument arrays for each job in the batch
12
- * @returns The batch configuration object
13
- */
14
- create(name: string, args: any[][]): {
15
- name: string;
16
- args: any[][];
17
- };
18
- /**
19
- * Kills a remote batch job
20
- * @param name - The name of the job
21
- * @param batchId - The ID of the batch to kill
22
- */
23
- kill(name: string, batchId: string): void;
24
- /**
25
- * Retrieves information about a remote batch job
26
- * @param name - The name of the job
27
- * @param batchId - The ID of the batch to get information for
28
- */
29
- get(name: string, batchId: string): void;
30
- }
31
- export declare const blBatch: BlBatch;
32
- export {};
@@ -1,40 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.blBatch = void 0;
4
- class BlBatch {
5
- /**
6
- * Lists all remote batch jobs for a given name
7
- * @param name - The name of the job to list
8
- */
9
- list(name) {
10
- }
11
- /**
12
- * Creates a new remote batch job configuration.
13
- * This is used for remote execution management, not for local execution.
14
- * @param name - The name of the job
15
- * @param args - Array of argument arrays for each job in the batch
16
- * @returns The batch configuration object
17
- */
18
- create(name, args) {
19
- const batch = {
20
- name,
21
- args,
22
- };
23
- return batch;
24
- }
25
- /**
26
- * Kills a remote batch job
27
- * @param name - The name of the job
28
- * @param batchId - The ID of the batch to kill
29
- */
30
- kill(name, batchId) {
31
- }
32
- /**
33
- * Retrieves information about a remote batch job
34
- * @param name - The name of the job
35
- * @param batchId - The ID of the batch to get information for
36
- */
37
- get(name, batchId) {
38
- }
39
- }
40
- exports.blBatch = new BlBatch();
@@ -1 +0,0 @@
1
- export * from "./jobs.js";
@@ -1,17 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./jobs.js"), exports);
@@ -1,33 +0,0 @@
1
- declare class BlJob {
2
- start(func: (...args: any[]) => Promise<void>): void;
3
- /**
4
- * Lists all remote batch jobs for a given name
5
- * @param name - The name of the job to list
6
- */
7
- list(name: string): void;
8
- /**
9
- * Creates a new remote batch job configuration.
10
- * This is used for remote execution management, not for local execution.
11
- * @param name - The name of the job
12
- * @param args - Array of argument arrays for each job in the batch
13
- * @returns The batch configuration object
14
- */
15
- create(name: string, args: any[][]): {
16
- name: string;
17
- args: any[][];
18
- };
19
- /**
20
- * Kills a remote batch job
21
- * @param name - The name of the job
22
- * @param batchId - The ID of the batch to kill
23
- */
24
- kill(name: string, batchId: string): void;
25
- /**
26
- * Retrieves information about a remote batch job
27
- * @param name - The name of the job
28
- * @param batchId - The ID of the batch to get information for
29
- */
30
- get(name: string, batchId: string): void;
31
- }
32
- export declare const blJob: BlJob;
33
- export {};
package/dist/jobs/job.js DELETED
@@ -1,51 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.blJob = void 0;
4
- function retrieveArguments() {
5
- const args = process.argv.slice(2);
6
- return args;
7
- }
8
- class BlJob {
9
- /*
10
- Run a job defined in a function, it's run in the current process
11
- */
12
- start(func) {
13
- const args = retrieveArguments();
14
- func(...args);
15
- }
16
- /**
17
- * Lists all remote batch jobs for a given name
18
- * @param name - The name of the job to list
19
- */
20
- list(name) {
21
- }
22
- /**
23
- * Creates a new remote batch job configuration.
24
- * This is used for remote execution management, not for local execution.
25
- * @param name - The name of the job
26
- * @param args - Array of argument arrays for each job in the batch
27
- * @returns The batch configuration object
28
- */
29
- create(name, args) {
30
- const batch = {
31
- name,
32
- args,
33
- };
34
- return batch;
35
- }
36
- /**
37
- * Kills a remote batch job
38
- * @param name - The name of the job
39
- * @param batchId - The ID of the batch to kill
40
- */
41
- kill(name, batchId) {
42
- }
43
- /**
44
- * Retrieves information about a remote batch job
45
- * @param name - The name of the job
46
- * @param batchId - The ID of the batch to get information for
47
- */
48
- get(name, batchId) {
49
- }
50
- }
51
- exports.blJob = new BlJob();
@@ -1,10 +0,0 @@
1
- declare class BlJob {
2
- getArguments(): Promise<{
3
- [key: number]: any;
4
- }>;
5
- get indexKey(): string;
6
- get index(): number;
7
- start(func: (args: any) => Promise<void>): Promise<void>;
8
- }
9
- export declare const blJob: BlJob;
10
- export {};
package/dist/jobs/jobs.js DELETED
@@ -1,42 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.blJob = void 0;
7
- const yargs_1 = __importDefault(require("yargs"));
8
- const helpers_1 = require("yargs/helpers");
9
- const env_js_1 = require("../common/env.js");
10
- class BlJob {
11
- async getArguments() {
12
- if (!env_js_1.env.BL_BATCH_DATA_URL) {
13
- const argv = await (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
14
- .parseAsync();
15
- return argv;
16
- }
17
- const response = await fetch(env_js_1.env.BL_BATCH_DATA_URL);
18
- const data = await response.json();
19
- return data.args[this.index] ?? {};
20
- }
21
- get indexKey() {
22
- return env_js_1.env.BL_BATCH_INDEX_KEY ?? "TASK_INDEX";
23
- }
24
- get index() {
25
- return env_js_1.env[this.indexKey] ? Number(env_js_1.env[this.indexKey]) ?? 0 : 0;
26
- }
27
- /*
28
- Run a job defined in a function, it's run in the current process
29
- */
30
- async start(func) {
31
- try {
32
- const parsedArgs = await this.getArguments();
33
- await func(parsedArgs);
34
- process.exit(0);
35
- }
36
- catch (error) {
37
- console.error('Job execution failed:', error);
38
- process.exit(1);
39
- }
40
- }
41
- }
42
- exports.blJob = new BlJob();