@blaxel/core 0.2.2-dev.60 → 0.2.2-preview1

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.
@@ -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;
@@ -0,0 +1,8 @@
1
+ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
2
+ import { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
3
+ export declare class BlaxelHttpMcpServerTransport implements Transport {
4
+ private server;
5
+ constructor(port: number);
6
+ start(): Promise<void>;
7
+ send(msg: JSONRPCMessage): Promise<void>;
8
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BlaxelHttpMcpServerTransport = void 0;
4
+ const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
5
+ class BlaxelHttpMcpServerTransport {
6
+ server;
7
+ constructor(port) {
8
+ this.server = new streamableHttp_js_1.StreamableHTTPServerTransport({ port });
9
+ }
10
+ async start() {
11
+ await this.server.start();
12
+ }
13
+ async send(msg) {
14
+ }
15
+ }
16
+ exports.BlaxelHttpMcpServerTransport = BlaxelHttpMcpServerTransport;
@@ -0,0 +1,4 @@
1
+ export * from "./http.js";
2
+ export * from "./websocket.js";
3
+ import { BlaxelWebsocketMcpServerTransport } from "./websocket.js";
4
+ export declare const BlaxelMcpServerTransport: typeof BlaxelWebsocketMcpServerTransport;
@@ -0,0 +1,21 @@
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
+ exports.BlaxelMcpServerTransport = void 0;
18
+ __exportStar(require("./http.js"), exports);
19
+ __exportStar(require("./websocket.js"), exports);
20
+ const websocket_js_1 = require("./websocket.js");
21
+ exports.BlaxelMcpServerTransport = websocket_js_1.BlaxelWebsocketMcpServerTransport;
@@ -0,0 +1,24 @@
1
+ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
2
+ interface JSONRPCMessage {
3
+ jsonrpc: "2.0";
4
+ id?: string | number;
5
+ method?: string;
6
+ params?: Record<string, unknown>;
7
+ }
8
+ export declare class BlaxelWebsocketMcpServerTransport implements Transport {
9
+ private port;
10
+ private wss;
11
+ private clients;
12
+ onclose?: () => void;
13
+ onerror?: (err: Error) => void;
14
+ private messageHandler?;
15
+ onconnection?: (clientId: string) => void;
16
+ ondisconnection?: (clientId: string) => void;
17
+ set onmessage(handler: ((message: JSONRPCMessage) => void) | undefined);
18
+ constructor(port?: number);
19
+ start(): Promise<void>;
20
+ send(msg: JSONRPCMessage): Promise<void>;
21
+ broadcast(msg: JSONRPCMessage): Promise<void>;
22
+ close(): Promise<void>;
23
+ }
24
+ export {};
@@ -0,0 +1,213 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.BlaxelWebsocketMcpServerTransport = void 0;
37
+ const uuid_1 = require("uuid");
38
+ const ws_1 = __importStar(require("ws"));
39
+ const env_js_1 = require("../../common/env.js");
40
+ const logger_js_1 = require("../../common/logger.js");
41
+ const telemetry_js_1 = require("../../telemetry/telemetry.js");
42
+ const spans = new Map();
43
+ class BlaxelWebsocketMcpServerTransport {
44
+ port;
45
+ wss;
46
+ clients = new Map();
47
+ onclose;
48
+ onerror;
49
+ messageHandler;
50
+ onconnection;
51
+ ondisconnection;
52
+ set onmessage(handler) {
53
+ this.messageHandler = handler
54
+ ? (msg, clientId) => {
55
+ if (!("id" in msg)) {
56
+ return handler(msg);
57
+ }
58
+ return handler({
59
+ ...msg,
60
+ id: clientId + ":" + msg.id,
61
+ });
62
+ }
63
+ : undefined;
64
+ }
65
+ constructor(port) {
66
+ this.port = port ?? parseInt(env_js_1.env.BL_SERVER_PORT ?? "8080", 10);
67
+ this.wss = new ws_1.WebSocketServer({ port: this.port });
68
+ }
69
+ async start() {
70
+ logger_js_1.logger.info("Starting WebSocket Server on port " + this.port);
71
+ this.wss.on("connection", (ws) => {
72
+ const clientId = (0, uuid_1.v4)();
73
+ this.clients.set(clientId, {
74
+ ws,
75
+ });
76
+ this.onconnection?.(clientId);
77
+ ws.on("message", (data) => {
78
+ const span = (0, telemetry_js_1.startSpan)("message", {
79
+ attributes: {
80
+ "mcp.client.id": clientId,
81
+ "span.type": "mcp.message",
82
+ },
83
+ isRoot: false,
84
+ });
85
+ try {
86
+ const msg = JSON.parse(data.toString());
87
+ this.messageHandler?.(msg, clientId);
88
+ if ("method" in msg && "id" in msg && "params" in msg) {
89
+ span.setAttributes({
90
+ "mcp.message.parsed": true,
91
+ "mcp.method": msg.method,
92
+ "mcp.messageId": msg.id,
93
+ "mcp.toolName": msg.params?.name,
94
+ });
95
+ spans.set(clientId + ":" + msg.id, span);
96
+ }
97
+ // Handle msg.id safely
98
+ const msgId = msg.id ? String(msg.id) : "";
99
+ const [cId, parsedMsgId] = msgId.split(":");
100
+ msg.id = parsedMsgId ? parseInt(parsedMsgId) : undefined;
101
+ // Use optional chaining for safe access
102
+ const client = this.clients.get(cId ?? "");
103
+ if (client?.ws?.readyState === ws_1.default.OPEN) {
104
+ const msgSpan = spans.get(cId + ":" + (msg.id ?? ""));
105
+ try {
106
+ client.ws.send(JSON.stringify(msg));
107
+ if (msgSpan) {
108
+ msgSpan.setAttributes({
109
+ "mcp.message.response_sent": true,
110
+ });
111
+ }
112
+ }
113
+ catch (err) {
114
+ if (msgSpan) {
115
+ msgSpan.setStatus("error"); // Error status
116
+ msgSpan.recordException(err);
117
+ }
118
+ throw err;
119
+ }
120
+ finally {
121
+ if (msgSpan) {
122
+ msgSpan.end();
123
+ }
124
+ }
125
+ }
126
+ else {
127
+ this.clients.delete(cId);
128
+ this.ondisconnection?.(cId);
129
+ }
130
+ }
131
+ catch (err) {
132
+ if (err instanceof Error) {
133
+ span.setStatus("error"); // Error status
134
+ span.recordException(err);
135
+ this.onerror?.(err);
136
+ }
137
+ else {
138
+ this.onerror?.(new Error(`Failed to parse message: ${String(err)}`));
139
+ }
140
+ span.end();
141
+ }
142
+ });
143
+ ws.on("close", () => {
144
+ this.clients.delete(clientId);
145
+ this.ondisconnection?.(clientId);
146
+ });
147
+ ws.on("error", (err) => {
148
+ this.onerror?.(err);
149
+ });
150
+ });
151
+ return Promise.resolve();
152
+ }
153
+ async send(msg) {
154
+ const [cId, msgId] = msg.id ? String(msg.id).split(":") : [];
155
+ msg.id = parseInt(msgId);
156
+ const data = JSON.stringify(msg);
157
+ const deadClients = [];
158
+ if (cId) {
159
+ // Send to specific client
160
+ const client = this.clients.get(cId);
161
+ if (client?.ws?.readyState === ws_1.default.OPEN) {
162
+ const msgSpan = spans.get(cId + ":" + msg.id);
163
+ try {
164
+ client.ws.send(data);
165
+ if (msgSpan) {
166
+ msgSpan.setAttributes({
167
+ "mcp.message.response_sent": true,
168
+ });
169
+ }
170
+ }
171
+ catch (err) {
172
+ if (msgSpan) {
173
+ msgSpan.setStatus("error"); // Error status
174
+ msgSpan.recordException(err);
175
+ }
176
+ throw err;
177
+ }
178
+ finally {
179
+ if (msgSpan) {
180
+ msgSpan.end();
181
+ }
182
+ }
183
+ }
184
+ else {
185
+ this.clients.delete(cId);
186
+ this.ondisconnection?.(cId);
187
+ }
188
+ }
189
+ for (const [id, client] of this.clients.entries()) {
190
+ if (client.ws.readyState !== ws_1.default.OPEN) {
191
+ deadClients.push(id);
192
+ }
193
+ }
194
+ // Cleanup dead clients
195
+ deadClients.forEach((id) => {
196
+ this.clients.delete(id);
197
+ this.ondisconnection?.(id);
198
+ });
199
+ return Promise.resolve();
200
+ }
201
+ async broadcast(msg) {
202
+ return this.send(msg);
203
+ }
204
+ async close() {
205
+ return new Promise((resolve) => {
206
+ this.wss.close(() => {
207
+ this.clients.clear();
208
+ resolve();
209
+ });
210
+ });
211
+ }
212
+ }
213
+ exports.BlaxelWebsocketMcpServerTransport = BlaxelWebsocketMcpServerTransport;
@@ -1,4 +1,4 @@
1
- import { Sandbox } from "../client/index.js";
1
+ import { SandboxConfiguration } from "./types.js";
2
2
  export declare class ResponseError extends Error {
3
3
  response: Response;
4
4
  data: unknown;
@@ -6,14 +6,14 @@ export declare class ResponseError extends Error {
6
6
  constructor(response: Response, data: unknown, error: unknown);
7
7
  }
8
8
  export declare class SandboxAction {
9
- private sandbox;
10
- constructor(sandbox: Sandbox);
9
+ protected sandbox: SandboxConfiguration;
10
+ constructor(sandbox: SandboxConfiguration);
11
11
  get name(): string;
12
12
  get fallbackUrl(): string | null;
13
13
  get externalUrl(): string;
14
14
  get internalUrl(): string;
15
+ get client(): import("@hey-api/client-fetch").Client;
15
16
  get forcedUrl(): string | null;
16
17
  get url(): string;
17
18
  handleResponseError(response: Response, data: unknown, error: unknown): void;
18
- websocket(path: string): WebSocket | null;
19
19
  }
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SandboxAction = exports.ResponseError = void 0;
4
+ const client_fetch_1 = require("@hey-api/client-fetch");
4
5
  const process_1 = require("process");
5
6
  const internal_js_1 = require("../common/internal.js");
6
7
  const settings_js_1 = require("../common/settings.js");
8
+ const client_gen_js_1 = require("./client/client.gen.js");
7
9
  class ResponseError extends Error {
8
10
  response;
9
11
  data;
@@ -50,7 +52,18 @@ class SandboxAction {
50
52
  const hash = (0, internal_js_1.getGlobalUniqueHash)(settings_js_1.settings.workspace, "sandbox", this.name);
51
53
  return `${settings_js_1.settings.runInternalProtocol}://bl-${settings_js_1.settings.env}-${hash}.${settings_js_1.settings.runInternalHostname}`;
52
54
  }
55
+ get client() {
56
+ if (this.sandbox.forceUrl) {
57
+ return (0, client_fetch_1.createClient)({
58
+ baseUrl: this.sandbox.forceUrl,
59
+ headers: this.sandbox.headers,
60
+ });
61
+ }
62
+ return client_gen_js_1.client;
63
+ }
53
64
  get forcedUrl() {
65
+ if (this.sandbox.forceUrl)
66
+ return this.sandbox.forceUrl;
54
67
  const envVar = this.name.replace(/-/g, "_").toUpperCase();
55
68
  const envName = `BL_SANDBOX_${envVar}_URL`;
56
69
  if (process_1.env[envName]) {
@@ -71,37 +84,5 @@ class SandboxAction {
71
84
  throw new ResponseError(response, data, error);
72
85
  }
73
86
  }
74
- websocket(path) {
75
- let ws = null;
76
- // Build ws:// or wss:// URL from baseUrl
77
- let baseUrl = this.url.replace(/^http/, 'ws');
78
- if (baseUrl.endsWith('/'))
79
- baseUrl = baseUrl.slice(0, -1);
80
- const wsUrl = `${baseUrl}/ws/${path}?token=${settings_js_1.settings.token}`;
81
- // Use isomorphic WebSocket: browser or Node.js
82
- let WS = undefined;
83
- if (typeof globalThis.WebSocket !== 'undefined') {
84
- WS = globalThis.WebSocket;
85
- }
86
- else {
87
- try {
88
- // eslint-disable-next-line @typescript-eslint/no-var-requires
89
- WS = require('ws');
90
- }
91
- catch {
92
- WS = undefined;
93
- }
94
- }
95
- if (!WS)
96
- throw new Error('WebSocket is not available in this environment');
97
- try {
98
- ws = typeof WS === 'function' ? new WS(wsUrl) : new WS(wsUrl);
99
- }
100
- catch (err) {
101
- console.error('WebSocket connection error:', err);
102
- throw err;
103
- }
104
- return ws;
105
- }
106
87
  }
107
88
  exports.SandboxAction = SandboxAction;
@@ -14,27 +14,7 @@ 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
17
  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
18
  onError?: (error: Error) => void;
39
19
  withContent: boolean;
40
20
  }): {
@@ -13,6 +13,7 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
13
13
  path: { path },
14
14
  body: { isDirectory: true, permissions },
15
15
  baseUrl: this.url,
16
+ client: this.client,
16
17
  });
17
18
  this.handleResponseError(response, data, error);
18
19
  return data;
@@ -23,6 +24,7 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
23
24
  path: { path },
24
25
  body: { content },
25
26
  baseUrl: this.url,
27
+ client: this.client,
26
28
  });
27
29
  this.handleResponseError(response, data, error);
28
30
  return data;
@@ -32,6 +34,7 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
32
34
  const { response, data, error } = await (0, index_js_1.getFilesystemByPath)({
33
35
  path: { path },
34
36
  baseUrl: this.url,
37
+ client: this.client,
35
38
  });
36
39
  this.handleResponseError(response, data, error);
37
40
  if (data && 'content' in data) {
@@ -45,6 +48,7 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
45
48
  path: { path },
46
49
  query: { recursive },
47
50
  baseUrl: this.url,
51
+ client: this.client,
48
52
  });
49
53
  this.handleResponseError(response, data, error);
50
54
  return data;
@@ -54,6 +58,7 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
54
58
  const { response, data, error } = await (0, index_js_1.getFilesystemByPath)({
55
59
  path: { path },
56
60
  baseUrl: this.url,
61
+ client: this.client,
57
62
  });
58
63
  this.handleResponseError(response, data, error);
59
64
  if (!data || !('files' in data || 'subdirectories' in data)) {
@@ -67,6 +72,7 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
67
72
  const { response, data, error } = await (0, index_js_1.getFilesystemByPath)({
68
73
  path: { path: source },
69
74
  baseUrl: this.url,
75
+ client: this.client,
70
76
  });
71
77
  this.handleResponseError(response, data, error);
72
78
  if (data && ('files' in data || 'subdirectories' in data)) {
@@ -111,136 +117,13 @@ class SandboxFileSystem extends action_js_1.SandboxAction {
111
117
  }
112
118
  throw new Error("Unsupported file type");
113
119
  }
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
120
  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
121
  path = this.formatPath(path);
240
122
  let closed = false;
241
123
  let controller = new AbortController();
242
124
  const start = async () => {
243
125
  const { response, data, error } = await (0, index_js_1.getWatchFilesystemByPath)({
126
+ client: this.client,
244
127
  path: { path },
245
128
  baseUrl: this.url,
246
129
  parseAs: 'stream',
@@ -4,21 +4,6 @@ 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: {
22
7
  onLog?: (log: string) => void;
23
8
  onStdout?: (stdout: string) => void;
24
9
  onStderr?: (stderr: string) => void;
@@ -9,126 +9,14 @@ 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) {
125
12
  const controller = new AbortController();
126
13
  (async () => {
127
14
  try {
15
+ const headers = this.sandbox.forceUrl ? this.sandbox.headers : settings_js_1.settings.headers;
128
16
  const stream = await fetch(`${this.url}/process/${identifier}/logs/stream`, {
129
17
  method: 'GET',
130
18
  signal: controller.signal,
131
- headers: settings_js_1.settings.headers,
19
+ headers,
132
20
  });
133
21
  if (stream.status !== 200) {
134
22
  throw new Error(`Failed to stream logs: ${await stream.text()}`);
@@ -175,6 +63,7 @@ class SandboxProcess extends action_js_1.SandboxAction {
175
63
  const { response, data, error } = await (0, index_js_1.postProcess)({
176
64
  body: process,
177
65
  baseUrl: this.url,
66
+ client: this.client,
178
67
  });
179
68
  this.handleResponseError(response, data, error);
180
69
  return data;
@@ -202,6 +91,7 @@ class SandboxProcess extends action_js_1.SandboxAction {
202
91
  const { response, data, error } = await (0, index_js_1.getProcessByIdentifier)({
203
92
  path: { identifier },
204
93
  baseUrl: this.url,
94
+ client: this.client,
205
95
  });
206
96
  this.handleResponseError(response, data, error);
207
97
  return data;
@@ -209,6 +99,7 @@ class SandboxProcess extends action_js_1.SandboxAction {
209
99
  async list() {
210
100
  const { response, data, error } = await (0, index_js_1.getProcess)({
211
101
  baseUrl: this.url,
102
+ client: this.client,
212
103
  });
213
104
  this.handleResponseError(response, data, error);
214
105
  return data;
@@ -217,6 +108,7 @@ class SandboxProcess extends action_js_1.SandboxAction {
217
108
  const { response, data, error } = await (0, index_js_1.deleteProcessByIdentifier)({
218
109
  path: { identifier },
219
110
  baseUrl: this.url,
111
+ client: this.client,
220
112
  });
221
113
  this.handleResponseError(response, data, error);
222
114
  return data;
@@ -225,6 +117,7 @@ class SandboxProcess extends action_js_1.SandboxAction {
225
117
  const { response, data, error } = await (0, index_js_1.deleteProcessByIdentifierKill)({
226
118
  path: { identifier },
227
119
  baseUrl: this.url,
120
+ client: this.client,
228
121
  });
229
122
  this.handleResponseError(response, data, error);
230
123
  return data;
@@ -233,6 +126,7 @@ class SandboxProcess extends action_js_1.SandboxAction {
233
126
  const { response, data, error } = await (0, index_js_1.getProcessByIdentifierLogs)({
234
127
  path: { identifier },
235
128
  baseUrl: this.url,
129
+ client: this.client,
236
130
  });
237
131
  this.handleResponseError(response, data, error);
238
132
  if (data && type in data) {
@@ -3,13 +3,16 @@ import { SandboxFileSystem } from "./filesystem.js";
3
3
  import { SandboxNetwork } from "./network.js";
4
4
  import { SandboxPreviews } from "./preview.js";
5
5
  import { SandboxProcess } from "./process.js";
6
+ import { SandboxSessions } from "./session.js";
7
+ import { SandboxConfiguration, SessionWithToken } from "./types.js";
6
8
  export declare class SandboxInstance {
7
9
  private sandbox;
8
10
  fs: SandboxFileSystem;
9
11
  network: SandboxNetwork;
10
12
  process: SandboxProcess;
11
13
  previews: SandboxPreviews;
12
- constructor(sandbox: SandboxModel);
14
+ sessions: SandboxSessions;
15
+ constructor(sandbox: SandboxConfiguration);
13
16
  get metadata(): import("../client/types.gen.js").Metadata | undefined;
14
17
  get status(): string | undefined;
15
18
  get events(): import("../client/types.gen.js").CoreEvents | undefined;
@@ -22,4 +25,5 @@ export declare class SandboxInstance {
22
25
  static get(sandboxName: string): Promise<SandboxInstance>;
23
26
  static list(): Promise<SandboxInstance[]>;
24
27
  static delete(sandboxName: string): Promise<SandboxModel>;
28
+ static fromSession(session: SessionWithToken): Promise<SandboxInstance>;
25
29
  }
@@ -7,18 +7,21 @@ const filesystem_js_1 = require("./filesystem.js");
7
7
  const network_js_1 = require("./network.js");
8
8
  const preview_js_1 = require("./preview.js");
9
9
  const process_js_1 = require("./process.js");
10
+ const session_js_1 = require("./session.js");
10
11
  class SandboxInstance {
11
12
  sandbox;
12
13
  fs;
13
14
  network;
14
15
  process;
15
16
  previews;
17
+ sessions;
16
18
  constructor(sandbox) {
17
19
  this.sandbox = sandbox;
18
20
  this.fs = new filesystem_js_1.SandboxFileSystem(sandbox);
19
21
  this.network = new network_js_1.SandboxNetwork(sandbox);
20
22
  this.process = new process_js_1.SandboxProcess(sandbox);
21
23
  this.previews = new preview_js_1.SandboxPreviews(sandbox);
24
+ this.sessions = new session_js_1.SandboxSessions(sandbox);
22
25
  }
23
26
  get metadata() {
24
27
  return this.sandbox.metadata;
@@ -91,5 +94,8 @@ class SandboxInstance {
91
94
  });
92
95
  return data;
93
96
  }
97
+ static async fromSession(session) {
98
+ return new SandboxInstance({ forceUrl: session.url, params: { bl_preview_token: session.token }, headers: { "X-Blaxel-Preview-Token": session.token } });
99
+ }
94
100
  }
95
101
  exports.SandboxInstance = SandboxInstance;
@@ -0,0 +1,21 @@
1
+ import { Sandbox } from "../client/index.js";
2
+ import { SessionCreateOptions, SessionWithToken } from "./types.js";
3
+ export declare class SandboxSessions {
4
+ private sandbox;
5
+ constructor(sandbox: Sandbox);
6
+ get sandboxName(): string;
7
+ create(options?: SessionCreateOptions): Promise<SessionWithToken>;
8
+ list(): Promise<{
9
+ name: string;
10
+ url: string;
11
+ token: string;
12
+ expiresAt: string | Date;
13
+ }[]>;
14
+ get(name: string): Promise<{
15
+ url: string;
16
+ token: string;
17
+ expiresAt: string | Date;
18
+ }>;
19
+ delete(name: string): Promise<import("../client/types.gen.js").Preview>;
20
+ getToken(previewName: string): Promise<import("../client/types.gen.js").PreviewToken | null>;
21
+ }
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SandboxSessions = void 0;
4
+ const index_js_1 = require("../client/index.js");
5
+ const preview_js_1 = require("./preview.js");
6
+ class SandboxSessions {
7
+ sandbox;
8
+ constructor(sandbox) {
9
+ this.sandbox = sandbox;
10
+ }
11
+ get sandboxName() {
12
+ return this.sandbox.metadata?.name ?? "";
13
+ }
14
+ async create(options = {}) {
15
+ const expiresAt = options.expiresAt ?? new Date(Date.now() + 24 * 60 * 60 * 1000); // 1 day from now
16
+ const body = {
17
+ metadata: {
18
+ name: "session-" + Date.now(),
19
+ },
20
+ spec: {
21
+ port: 443,
22
+ public: false,
23
+ },
24
+ };
25
+ const { data } = await (0, index_js_1.createSandboxPreview)({
26
+ path: {
27
+ sandboxName: this.sandboxName,
28
+ },
29
+ body,
30
+ throwOnError: true,
31
+ });
32
+ const preview = new preview_js_1.SandboxPreview(data);
33
+ // Create a token for the preview with the given expiresAt
34
+ const tokenObj = await preview.tokens.create(expiresAt);
35
+ return {
36
+ name: body.metadata.name,
37
+ url: preview.spec?.url ?? "",
38
+ token: tokenObj.value,
39
+ expiresAt: typeof tokenObj.expiresAt === 'string' ? new Date(tokenObj.expiresAt) : tokenObj.expiresAt,
40
+ };
41
+ }
42
+ async list() {
43
+ const { data } = await (0, index_js_1.listSandboxPreviews)({
44
+ path: {
45
+ sandboxName: this.sandboxName,
46
+ },
47
+ throwOnError: true,
48
+ });
49
+ return await Promise.all(data.filter((preview) => preview.metadata?.name?.includes("session-")).map(async (preview) => {
50
+ const token = await this.getToken(preview.metadata?.name ?? "");
51
+ return {
52
+ name: preview.metadata?.name ?? "",
53
+ url: preview.spec?.url ?? "",
54
+ token: token?.spec?.token ?? "",
55
+ expiresAt: token?.spec?.expiresAt ?? new Date(),
56
+ };
57
+ }));
58
+ }
59
+ async get(name) {
60
+ const { data } = await (0, index_js_1.getSandboxPreview)({
61
+ path: {
62
+ sandboxName: this.sandboxName,
63
+ previewName: name,
64
+ },
65
+ throwOnError: true,
66
+ });
67
+ const token = await this.getToken(name);
68
+ return {
69
+ url: data.spec?.url ?? "",
70
+ token: token?.spec?.token ?? "",
71
+ expiresAt: token?.spec?.expiresAt ?? new Date(),
72
+ };
73
+ }
74
+ async delete(name) {
75
+ const { data } = await (0, index_js_1.deleteSandboxPreview)({
76
+ path: {
77
+ sandboxName: this.sandboxName,
78
+ previewName: name,
79
+ },
80
+ throwOnError: true,
81
+ });
82
+ return data;
83
+ }
84
+ async getToken(previewName) {
85
+ const { data } = await (0, index_js_1.listSandboxPreviewTokens)({
86
+ path: {
87
+ sandboxName: this.sandboxName,
88
+ previewName,
89
+ },
90
+ throwOnError: true,
91
+ });
92
+ if (data.length === 0)
93
+ return null;
94
+ return data[0];
95
+ }
96
+ }
97
+ exports.SandboxSessions = SandboxSessions;
@@ -0,0 +1,15 @@
1
+ import { Sandbox } from "../client/types.gen";
2
+ export interface SessionCreateOptions {
3
+ expiresAt?: Date;
4
+ }
5
+ export interface SessionWithToken {
6
+ name: string;
7
+ url: string;
8
+ token: string;
9
+ expiresAt: Date;
10
+ }
11
+ export type SandboxConfiguration = {
12
+ forceUrl?: string;
13
+ headers?: Record<string, string>;
14
+ params?: Record<string, string>;
15
+ } & Sandbox;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blaxel/core",
3
- "version": "0.2.2-dev.60",
3
+ "version": "0.2.2-preview1",
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
  },