@blaxel/core 0.2.2-preview3 → 0.2.2

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.
@@ -10,11 +10,6 @@ exports.fs = fs;
10
10
  let os = null;
11
11
  exports.os = os;
12
12
  if (isNode) {
13
- try {
14
- exports.fs = fs = eval("require")("fs");
15
- exports.os = os = eval("require")("os");
16
- }
17
- catch (e) {
18
- console.warn("fs and os are not available in this environment");
19
- }
13
+ exports.fs = fs = eval("require")("fs");
14
+ exports.os = os = eval("require")("os");
20
15
  }
@@ -75,7 +75,7 @@ class Settings {
75
75
  if (!this.generation) {
76
76
  return "";
77
77
  }
78
- return env_js_1.env.BL_RUN_INTERNAL_HOST || "";
78
+ return env_js_1.env.BL_RUN_INTERNAL_HOSTNAME || "";
79
79
  }
80
80
  get runInternalProtocol() {
81
81
  return env_js_1.env.BL_RUN_INTERNAL_PROTOCOL || "https";
@@ -2,7 +2,6 @@ declare class BlJob {
2
2
  getArguments(): Promise<{
3
3
  [key: number]: any;
4
4
  }>;
5
- private parseCommandLineArgs;
6
5
  get indexKey(): string;
7
6
  get index(): number;
8
7
  start(func: (args: any) => Promise<void>): Promise<void>;
package/dist/jobs/jobs.js CHANGED
@@ -1,38 +1,25 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.blJob = void 0;
7
+ const yargs_1 = __importDefault(require("yargs"));
8
+ const helpers_1 = require("yargs/helpers");
4
9
  const env_js_1 = require("../common/env.js");
5
10
  class BlJob {
6
11
  async getArguments() {
7
12
  if (!env_js_1.env.BL_EXECUTION_DATA_URL) {
8
- const args = this.parseCommandLineArgs();
9
- return args;
13
+ const argv = await (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
14
+ .parseAsync();
15
+ return argv;
10
16
  }
11
17
  const response = await fetch(env_js_1.env.BL_EXECUTION_DATA_URL);
12
18
  const data = await response.json();
13
19
  return data.tasks[this.index] ?? {};
14
20
  }
15
- parseCommandLineArgs() {
16
- const args = process.argv.slice(2);
17
- const result = {};
18
- for (let i = 0; i < args.length; i++) {
19
- const arg = args[i];
20
- if (arg.startsWith('--')) {
21
- const key = arg.slice(2);
22
- const value = args[i + 1];
23
- if (value && !value.startsWith('--')) {
24
- result[key] = value;
25
- i++;
26
- }
27
- else {
28
- result[key] = 'true';
29
- }
30
- }
31
- }
32
- return result;
33
- }
34
21
  get indexKey() {
35
- return env_js_1.env.BL_TASK_KEY ?? "TASK_INDEX";
22
+ return env_js_1.env.BL_EXECUTION_INDEX_KEY ?? "TASK_INDEX";
36
23
  }
37
24
  get index() {
38
25
  return env_js_1.env[this.indexKey] ? Number(env_js_1.env[this.indexKey]) ?? 0 : 0;
@@ -16,4 +16,5 @@ export declare class SandboxAction {
16
16
  get forcedUrl(): string | null;
17
17
  get url(): string;
18
18
  handleResponseError(response: Response, data: unknown, error: unknown): void;
19
+ websocket(path: string): WebSocket | null;
19
20
  }
@@ -84,5 +84,45 @@ class SandboxAction {
84
84
  throw new ResponseError(response, data, error);
85
85
  }
86
86
  }
87
+ websocket(path) {
88
+ let ws = null;
89
+ // Build ws:// or wss:// URL from baseUrl
90
+ let baseUrl = this.url.replace(/^http/, 'ws');
91
+ if (baseUrl.endsWith('/'))
92
+ baseUrl = baseUrl.slice(0, -1);
93
+ let params = `token=${settings_js_1.settings.token}`;
94
+ if (this.sandbox.params) {
95
+ params = "";
96
+ for (const [key, value] of Object.entries(this.sandbox.params)) {
97
+ params += `${key}=${value}&`;
98
+ }
99
+ params = params.slice(0, -1);
100
+ }
101
+ const wsUrl = `${baseUrl}/ws/${path}?${params}`;
102
+ // Use isomorphic WebSocket: browser or Node.js
103
+ let WS = undefined;
104
+ if (typeof globalThis.WebSocket !== 'undefined') {
105
+ WS = globalThis.WebSocket;
106
+ }
107
+ else {
108
+ try {
109
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
110
+ WS = require('ws');
111
+ }
112
+ catch {
113
+ WS = undefined;
114
+ }
115
+ }
116
+ if (!WS)
117
+ throw new Error('WebSocket is not available in this environment');
118
+ try {
119
+ ws = typeof WS === 'function' ? new WS(wsUrl) : new WS(wsUrl);
120
+ }
121
+ catch (err) {
122
+ console.error('WebSocket connection error:', err);
123
+ throw err;
124
+ }
125
+ return ws;
126
+ }
87
127
  }
88
128
  exports.SandboxAction = SandboxAction;
@@ -14,7 +14,27 @@ 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
+ */
17
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?: {
18
38
  onError?: (error: Error) => void;
19
39
  withContent: boolean;
20
40
  }): {
@@ -117,7 +117,131 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
117
117
  }
118
118
  throw new Error("Unsupported file type");
119
119
  }
120
+ /**
121
+ * Watch for changes in a directory. Calls the callback with the changed file path (and optionally its content).
122
+ * Returns a handle with a close() method to stop watching.
123
+ * @param path Directory to watch
124
+ * @param callback Function called on each change: (filePath, content?)
125
+ * @param withContent If true, also fetches and passes the file content (default: false)
126
+ */
120
127
  watch(path, callback, options) {
128
+ if (options?.ws) {
129
+ return this.wsWatch(path, callback, options);
130
+ }
131
+ return this.sseWatch(path, callback, options);
132
+ }
133
+ wsWatch(path, callback, options) {
134
+ path = this.formatPath(path);
135
+ let closed = false;
136
+ let ws = this.websocket(`watch/filesystem${path.startsWith('/') ? path : '/' + path}`);
137
+ let pingInterval = null;
138
+ let pongTimeout = null;
139
+ const PING_INTERVAL_MS = 30000;
140
+ const PONG_TIMEOUT_MS = 10000;
141
+ function sendPing() {
142
+ if (ws && ws.readyState === ws.OPEN) {
143
+ try {
144
+ ws.send(JSON.stringify({ type: 'ping' }));
145
+ }
146
+ catch { }
147
+ // Set pong timeout
148
+ if (pongTimeout)
149
+ clearTimeout(pongTimeout);
150
+ pongTimeout = setTimeout(() => {
151
+ // No pong received in time, close connection
152
+ if (ws && typeof ws.close === 'function')
153
+ ws.close();
154
+ }, PONG_TIMEOUT_MS);
155
+ }
156
+ }
157
+ if (ws) {
158
+ ws.onmessage = async (event) => {
159
+ if (closed)
160
+ return;
161
+ let data;
162
+ try {
163
+ data = typeof event.data === 'string' ? event.data : event.data;
164
+ if (!data)
165
+ return;
166
+ // Accept both JSON and plain string (file path)
167
+ let payload;
168
+ try {
169
+ payload = JSON.parse(data);
170
+ }
171
+ catch {
172
+ payload = { name: data, event: undefined };
173
+ }
174
+ // Handle ping/pong
175
+ if (payload.type === 'ping') {
176
+ // Respond to ping with pong
177
+ if (ws && ws.readyState === ws.OPEN) {
178
+ try {
179
+ ws.send(JSON.stringify({ type: 'pong' }));
180
+ }
181
+ catch { }
182
+ }
183
+ return;
184
+ }
185
+ if (payload.type === 'pong') {
186
+ // Pong received, clear pong timeout
187
+ if (pongTimeout)
188
+ clearTimeout(pongTimeout);
189
+ pongTimeout = null;
190
+ return;
191
+ }
192
+ const filePath = payload.name || payload.path || data;
193
+ if (!filePath)
194
+ return;
195
+ if (options?.withContent) {
196
+ try {
197
+ const content = await this.read(filePath);
198
+ await callback(filePath, content);
199
+ }
200
+ catch (e) {
201
+ await callback(filePath, undefined);
202
+ }
203
+ }
204
+ else {
205
+ await callback(filePath);
206
+ }
207
+ }
208
+ catch (err) {
209
+ if (options?.onError)
210
+ options.onError(err);
211
+ }
212
+ };
213
+ ws.onerror = (err) => {
214
+ if (options?.onError)
215
+ options.onError(err instanceof Error ? err : new Error(String(err)));
216
+ closed = true;
217
+ if (ws && typeof ws.close === 'function')
218
+ ws.close();
219
+ };
220
+ ws.onclose = () => {
221
+ closed = true;
222
+ ws = null;
223
+ if (pingInterval)
224
+ clearInterval(pingInterval);
225
+ if (pongTimeout)
226
+ clearTimeout(pongTimeout);
227
+ };
228
+ // Start ping interval
229
+ pingInterval = setInterval(sendPing, PING_INTERVAL_MS);
230
+ }
231
+ return {
232
+ close: () => {
233
+ closed = true;
234
+ if (ws && typeof ws.close === 'function')
235
+ ws.close();
236
+ ws = null;
237
+ if (pingInterval)
238
+ clearInterval(pingInterval);
239
+ if (pongTimeout)
240
+ clearTimeout(pongTimeout);
241
+ },
242
+ };
243
+ }
244
+ sseWatch(path, callback, options) {
121
245
  path = this.formatPath(path);
122
246
  let closed = false;
123
247
  let controller = new AbortController();
@@ -4,6 +4,21 @@ import { DeleteProcessByIdentifierKillResponse, DeleteProcessByIdentifierRespons
4
4
  export declare class SandboxProcess extends SandboxAction {
5
5
  constructor(sandbox: Sandbox);
6
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: {
7
22
  onLog?: (log: string) => void;
8
23
  onStdout?: (stdout: string) => void;
9
24
  onStderr?: (stderr: string) => void;
@@ -9,6 +9,119 @@ class SandboxProcess extends action_js_1.SandboxAction {
9
9
  super(sandbox);
10
10
  }
11
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) {
12
125
  const controller = new AbortController();
13
126
  (async () => {
14
127
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blaxel/core",
3
- "version": "0.2.2-preview3",
3
+ "version": "0.2.2",
4
4
  "description": "Blaxel Core SDK for TypeScript",
5
5
  "license": "MIT",
6
6
  "author": "Blaxel, INC (https://blaxel.ai)",
@@ -62,11 +62,13 @@
62
62
  "uuid": "^11.1.0",
63
63
  "ws": "^8.18.2",
64
64
  "yaml": "^2.7.1",
65
+ "yargs": "^17.7.2",
65
66
  "zod": "^3.24.3"
66
67
  },
67
68
  "devDependencies": {
68
69
  "@eslint/js": "^9.26.0",
69
70
  "@types/ws": "^8.18.1",
71
+ "@types/yargs": "^17.0.33",
70
72
  "typescript": "^5.0.0",
71
73
  "typescript-eslint": "^8.31.1"
72
74
  },
@@ -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,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();