@blaxel/core 0.2.33 → 0.2.35-preview.78
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/dist/client/sdk.gen.d.ts +50 -31
- package/dist/client/sdk.gen.js +195 -112
- package/dist/client/types.gen.d.ts +364 -217
- package/dist/common/settings.d.ts +2 -0
- package/dist/common/settings.js +74 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/client.d.ts +8 -1
- package/dist/mcp/client.js +35 -13
- package/dist/sandbox/action.js +4 -2
- package/dist/sandbox/client/types.gen.js +0 -1
- package/dist/sandbox/process/process.js +5 -5
- package/dist/sandbox/sandbox.d.ts +6 -3
- package/dist/sandbox/sandbox.js +52 -50
- package/dist/sandbox/session.js +4 -1
- package/dist/sandbox/types.d.ts +15 -1
- package/dist/sandbox/types.js +29 -0
- package/dist/tools/mcpTool.js +19 -4
- package/dist/volume/index.d.ts +23 -0
- package/dist/volume/index.js +113 -0
- package/package.json +6 -2
|
@@ -7,6 +7,7 @@ export type Config = {
|
|
|
7
7
|
declare class Settings {
|
|
8
8
|
credentials: Credentials;
|
|
9
9
|
config: Config;
|
|
10
|
+
private _version;
|
|
10
11
|
constructor();
|
|
11
12
|
setConfig(config: Config): void;
|
|
12
13
|
get env(): string;
|
|
@@ -15,6 +16,7 @@ declare class Settings {
|
|
|
15
16
|
get workspace(): string;
|
|
16
17
|
get authorization(): string;
|
|
17
18
|
get token(): string;
|
|
19
|
+
get version(): string;
|
|
18
20
|
get headers(): Record<string, string>;
|
|
19
21
|
get name(): string;
|
|
20
22
|
get type(): string;
|
package/dist/common/settings.js
CHANGED
|
@@ -3,9 +3,74 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.settings = void 0;
|
|
4
4
|
const index_js_1 = require("../authentication/index.js");
|
|
5
5
|
const env_js_1 = require("../common/env.js");
|
|
6
|
+
// Function to get package version
|
|
7
|
+
function getPackageVersion() {
|
|
8
|
+
try {
|
|
9
|
+
// Try to require package.json (Node.js only, gracefully fails in browser)
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
11
|
+
const packageJson = {"version":"0.2.35-preview.78","commit":"cb365d439aca12c16cca6b102c2a800d351e6f0c"};
|
|
12
|
+
return packageJson.version || "unknown";
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Fallback for browser environments or if require fails
|
|
16
|
+
return "unknown";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// Function to get OS and architecture
|
|
20
|
+
function getOsArch() {
|
|
21
|
+
try {
|
|
22
|
+
// Node.js environment
|
|
23
|
+
if (typeof process !== 'undefined' && process.platform && process.arch) {
|
|
24
|
+
const platform = process.platform === 'win32' ? 'windows' :
|
|
25
|
+
process.platform === 'darwin' ? 'darwin' :
|
|
26
|
+
process.platform === 'linux' ? 'linux' : process.platform;
|
|
27
|
+
return `${platform}/${process.arch}`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Fall through to browser detection
|
|
32
|
+
}
|
|
33
|
+
// Browser environment - use fixed detection
|
|
34
|
+
try {
|
|
35
|
+
// @ts-ignore - navigator is available in browser environments
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
37
|
+
if (typeof navigator !== 'undefined' && navigator?.platform) {
|
|
38
|
+
// @ts-ignore - navigator.platform is available in browser environments
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
40
|
+
const navPlatform = navigator.platform.toLowerCase();
|
|
41
|
+
const platform = navPlatform.includes('win') ? 'windows' :
|
|
42
|
+
navPlatform.includes('mac') ? 'darwin' :
|
|
43
|
+
navPlatform.includes('linux') ? 'linux' : 'browser';
|
|
44
|
+
const arch = navPlatform.includes('64') ? 'amd64' : 'unknown';
|
|
45
|
+
return `${platform}/${arch}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Ignore errors
|
|
50
|
+
}
|
|
51
|
+
return "browser/unknown";
|
|
52
|
+
}
|
|
53
|
+
// Function to get commit hash
|
|
54
|
+
function getCommitHash() {
|
|
55
|
+
try {
|
|
56
|
+
// Try to require package.json and look for commit field (set during build)
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
58
|
+
const packageJson = {"version":"0.2.35-preview.78","commit":"cb365d439aca12c16cca6b102c2a800d351e6f0c"};
|
|
59
|
+
// Check for commit in various possible locations
|
|
60
|
+
const commit = packageJson.commit || packageJson.buildInfo?.commit;
|
|
61
|
+
if (commit) {
|
|
62
|
+
return commit.length > 7 ? commit.substring(0, 7) : commit;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Fallback for browser environments or if require fails
|
|
67
|
+
}
|
|
68
|
+
return "unknown";
|
|
69
|
+
}
|
|
6
70
|
class Settings {
|
|
7
71
|
credentials;
|
|
8
72
|
config;
|
|
73
|
+
_version = null;
|
|
9
74
|
constructor() {
|
|
10
75
|
this.credentials = (0, index_js_1.authentication)();
|
|
11
76
|
this.config = {
|
|
@@ -59,10 +124,19 @@ class Settings {
|
|
|
59
124
|
}
|
|
60
125
|
return this.credentials.token;
|
|
61
126
|
}
|
|
127
|
+
get version() {
|
|
128
|
+
if (this._version === null) {
|
|
129
|
+
this._version = getPackageVersion();
|
|
130
|
+
}
|
|
131
|
+
return this._version;
|
|
132
|
+
}
|
|
62
133
|
get headers() {
|
|
134
|
+
const osArch = getOsArch();
|
|
135
|
+
const commitHash = getCommitHash();
|
|
63
136
|
return {
|
|
64
137
|
"x-blaxel-authorization": this.authorization,
|
|
65
138
|
"x-blaxel-workspace": this.workspace || "",
|
|
139
|
+
"User-Agent": `blaxel/sdk/typescript/${this.version} (${osArch}) blaxel/${commitHash}`,
|
|
66
140
|
};
|
|
67
141
|
}
|
|
68
142
|
get name() {
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -30,3 +30,4 @@ __exportStar(require("./sandbox/index.js"), exports);
|
|
|
30
30
|
__exportStar(require("./telemetry/telemetry.js"), exports);
|
|
31
31
|
__exportStar(require("./tools/index.js"), exports);
|
|
32
32
|
__exportStar(require("./tools/types.js"), exports);
|
|
33
|
+
__exportStar(require("./volume/index.js"), exports);
|
package/dist/mcp/client.d.ts
CHANGED
|
@@ -9,10 +9,17 @@ export declare class BlaxelMcpClientTransport implements Transport {
|
|
|
9
9
|
private _url;
|
|
10
10
|
private _headers;
|
|
11
11
|
private _isBrowser;
|
|
12
|
+
private _retry_max;
|
|
13
|
+
private _retry_delay;
|
|
12
14
|
onclose?: () => void;
|
|
13
15
|
onerror?: (error: Error) => void;
|
|
14
16
|
onmessage?: (message: JSONRPCMessage) => void;
|
|
15
|
-
constructor(url: string, headers?: Record<string, string
|
|
17
|
+
constructor(url: string, headers?: Record<string, string>, options?: {
|
|
18
|
+
retry: {
|
|
19
|
+
max?: number;
|
|
20
|
+
delay?: number;
|
|
21
|
+
};
|
|
22
|
+
});
|
|
16
23
|
start(): Promise<void>;
|
|
17
24
|
private _connect;
|
|
18
25
|
get isConnected(): boolean;
|
package/dist/mcp/client.js
CHANGED
|
@@ -21,8 +21,6 @@ if (!isBrowser) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
//const SUBPROTOCOL = "mcp";
|
|
24
|
-
const MAX_RETRIES = 3;
|
|
25
|
-
const RETRY_DELAY_MS = 1000;
|
|
26
24
|
// Helper function to wait
|
|
27
25
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
28
26
|
/**
|
|
@@ -34,11 +32,15 @@ class BlaxelMcpClientTransport {
|
|
|
34
32
|
_url;
|
|
35
33
|
_headers;
|
|
36
34
|
_isBrowser;
|
|
35
|
+
_retry_max;
|
|
36
|
+
_retry_delay;
|
|
37
37
|
onclose;
|
|
38
38
|
onerror;
|
|
39
39
|
onmessage;
|
|
40
|
-
constructor(url, headers) {
|
|
40
|
+
constructor(url, headers, options) {
|
|
41
41
|
this._url = new URL(url.replace("http", "ws"));
|
|
42
|
+
this._retry_max = options?.retry?.max ?? 3;
|
|
43
|
+
this._retry_delay = options?.retry?.delay ?? 1000;
|
|
42
44
|
this._headers = headers ?? {};
|
|
43
45
|
this._isBrowser = isBrowser;
|
|
44
46
|
}
|
|
@@ -47,21 +49,22 @@ class BlaxelMcpClientTransport {
|
|
|
47
49
|
throw new Error("Blaxel already started! If using Client class, note that connect() calls start() automatically.");
|
|
48
50
|
}
|
|
49
51
|
let attempts = 0;
|
|
50
|
-
|
|
52
|
+
const maxAttempts = Math.max(1, this._retry_max + 1); // Ensure at least 1 attempt
|
|
53
|
+
while (attempts < maxAttempts) {
|
|
51
54
|
try {
|
|
52
55
|
await this._connect();
|
|
53
56
|
return;
|
|
54
57
|
}
|
|
55
58
|
catch (error) {
|
|
59
|
+
attempts++;
|
|
56
60
|
if (error instanceof Error) {
|
|
57
61
|
logger_js_1.logger.warn(error.stack ?? error.message);
|
|
58
62
|
}
|
|
59
|
-
attempts
|
|
60
|
-
if (attempts === MAX_RETRIES) {
|
|
63
|
+
if (attempts >= maxAttempts) {
|
|
61
64
|
throw error;
|
|
62
65
|
}
|
|
63
|
-
logger_js_1.logger.debug(`WebSocket connection attempt ${attempts} failed, retrying in ${
|
|
64
|
-
await delay(
|
|
66
|
+
logger_js_1.logger.debug(`WebSocket connection attempt ${attempts} failed, retrying in ${this._retry_delay}ms...`);
|
|
67
|
+
await delay(this._retry_delay);
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
70
|
}
|
|
@@ -86,7 +89,25 @@ class BlaxelMcpClientTransport {
|
|
|
86
89
|
});
|
|
87
90
|
}
|
|
88
91
|
this._socket.onerror = (event) => {
|
|
89
|
-
|
|
92
|
+
// Log websocket error with meaningful information instead of raw event
|
|
93
|
+
const errorInfo = {
|
|
94
|
+
type: 'WebSocket Error',
|
|
95
|
+
url: this._url.toString(),
|
|
96
|
+
readyState: this._socket?.readyState,
|
|
97
|
+
browser: this._isBrowser,
|
|
98
|
+
// Extract any available error details from the event
|
|
99
|
+
eventType: event && typeof event === 'object' && 'type' in event
|
|
100
|
+
? String(event.type)
|
|
101
|
+
: 'unknown',
|
|
102
|
+
// Browser events might have different properties than Node.js
|
|
103
|
+
message: this._isBrowser && event && typeof event === 'object' && 'message' in event
|
|
104
|
+
? String(event.message)
|
|
105
|
+
: undefined,
|
|
106
|
+
error: !this._isBrowser && event && typeof event === 'object' && 'error' in event
|
|
107
|
+
? String(event.error)
|
|
108
|
+
: undefined
|
|
109
|
+
};
|
|
110
|
+
logger_js_1.logger.error('WebSocket connection error', errorInfo);
|
|
90
111
|
const error = this._isBrowser
|
|
91
112
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
92
113
|
? new Error(`WebSocket error: ${event.message}`)
|
|
@@ -174,7 +195,8 @@ class BlaxelMcpClientTransport {
|
|
|
174
195
|
}
|
|
175
196
|
async send(message) {
|
|
176
197
|
let attempts = 0;
|
|
177
|
-
|
|
198
|
+
const maxAttempts = Math.max(1, this._retry_max + 1); // Ensure at least 1 attempt
|
|
199
|
+
while (attempts < maxAttempts) {
|
|
178
200
|
try {
|
|
179
201
|
if (!this._socket || !this.isConnected) {
|
|
180
202
|
if (!this._socket) {
|
|
@@ -213,11 +235,11 @@ class BlaxelMcpClientTransport {
|
|
|
213
235
|
}
|
|
214
236
|
catch (error) {
|
|
215
237
|
attempts++;
|
|
216
|
-
if (attempts
|
|
238
|
+
if (attempts >= maxAttempts) {
|
|
217
239
|
throw error;
|
|
218
240
|
}
|
|
219
|
-
logger_js_1.logger.warn(`WebSocket send attempt ${attempts} failed, retrying in ${
|
|
220
|
-
await delay(
|
|
241
|
+
logger_js_1.logger.warn(`WebSocket send attempt ${attempts} failed, retrying in ${this._retry_delay}ms...`);
|
|
242
|
+
await delay(this._retry_delay);
|
|
221
243
|
}
|
|
222
244
|
}
|
|
223
245
|
}
|
package/dist/sandbox/action.js
CHANGED
|
@@ -66,8 +66,10 @@ class SandboxAction {
|
|
|
66
66
|
return (0, internal_js_1.getForcedUrl)('sandbox', this.name);
|
|
67
67
|
}
|
|
68
68
|
get url() {
|
|
69
|
-
if (this.forcedUrl)
|
|
70
|
-
|
|
69
|
+
if (this.forcedUrl) {
|
|
70
|
+
const url = this.forcedUrl.toString();
|
|
71
|
+
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
72
|
+
}
|
|
71
73
|
// Uncomment and use this when agent and mcp are available in mk3
|
|
72
74
|
// Update all requests made in this package to use fallbackUrl when internalUrl is not working
|
|
73
75
|
// if (settings.runInternalHostname) return this.internalUrl;
|
|
@@ -36,6 +36,9 @@ class SandboxProcess extends action_js_1.SandboxAction {
|
|
|
36
36
|
const lines = buffer.split(/\r?\n/);
|
|
37
37
|
buffer = lines.pop();
|
|
38
38
|
for (const line of lines) {
|
|
39
|
+
if (line.startsWith("[keepalive]")) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
39
42
|
if (line.startsWith('stdout:')) {
|
|
40
43
|
options.onStdout?.(line.slice(7));
|
|
41
44
|
options.onLog?.(line.slice(7));
|
|
@@ -81,11 +84,8 @@ class SandboxProcess extends action_js_1.SandboxAction {
|
|
|
81
84
|
this.handleResponseError(response, data, error);
|
|
82
85
|
let result = data;
|
|
83
86
|
// Handle wait_for_completion with parallel log streaming
|
|
84
|
-
if (shouldWaitForCompletion) {
|
|
85
|
-
|
|
86
|
-
if (onLog) {
|
|
87
|
-
streamControl = this.streamLogs(result.pid, { onLog });
|
|
88
|
-
}
|
|
87
|
+
if (shouldWaitForCompletion && onLog) {
|
|
88
|
+
const streamControl = this.streamLogs(result.pid, { onLog });
|
|
89
89
|
try {
|
|
90
90
|
// Wait for process completion
|
|
91
91
|
result = await this.wait(result.pid, { interval: 500, maxWait: 1000 * 60 * 60 });
|
|
@@ -4,7 +4,7 @@ import { SandboxNetwork } from "./network/index.js";
|
|
|
4
4
|
import { SandboxPreviews } from "./preview.js";
|
|
5
5
|
import { SandboxProcess } from "./process/index.js";
|
|
6
6
|
import { SandboxSessions } from "./session.js";
|
|
7
|
-
import { SandboxConfiguration, SandboxCreateConfiguration, SessionWithToken } from "./types.js";
|
|
7
|
+
import { SandboxConfiguration, SandboxCreateConfiguration, SandboxUpdateMetadata, SessionWithToken } from "./types.js";
|
|
8
8
|
export declare class SandboxInstance {
|
|
9
9
|
private sandbox;
|
|
10
10
|
fs: SandboxFileSystem;
|
|
@@ -16,15 +16,18 @@ export declare class SandboxInstance {
|
|
|
16
16
|
get metadata(): import("../client/types.gen.js").Metadata | undefined;
|
|
17
17
|
get status(): string | undefined;
|
|
18
18
|
get events(): import("../client/types.gen.js").CoreEvents | undefined;
|
|
19
|
-
get spec(): import("../client/types.gen.js").
|
|
19
|
+
get spec(): import("../client/types.gen.js").SandboxSpec | undefined;
|
|
20
20
|
wait({ maxWait, interval }?: {
|
|
21
21
|
maxWait?: number;
|
|
22
22
|
interval?: number;
|
|
23
23
|
}): Promise<this>;
|
|
24
|
-
static create(sandbox?: SandboxModel | SandboxCreateConfiguration
|
|
24
|
+
static create(sandbox?: SandboxModel | SandboxCreateConfiguration, { safe }?: {
|
|
25
|
+
safe?: boolean;
|
|
26
|
+
}): Promise<SandboxInstance>;
|
|
25
27
|
static get(sandboxName: string): Promise<SandboxInstance>;
|
|
26
28
|
static list(): Promise<SandboxInstance[]>;
|
|
27
29
|
static delete(sandboxName: string): Promise<SandboxModel>;
|
|
30
|
+
static updateMetadata(sandboxName: string, metadata: SandboxUpdateMetadata): Promise<SandboxInstance>;
|
|
28
31
|
static createIfNotExists(sandbox: SandboxModel | SandboxCreateConfiguration): Promise<SandboxInstance>;
|
|
29
32
|
static fromSession(session: SessionWithToken): Promise<SandboxInstance>;
|
|
30
33
|
}
|
package/dist/sandbox/sandbox.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.SandboxInstance = void 0;
|
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
5
|
const index_js_1 = require("../client/index.js");
|
|
6
6
|
const logger_js_1 = require("../common/logger.js");
|
|
7
|
+
const settings_js_1 = require("../common/settings.js");
|
|
7
8
|
const index_js_2 = require("./filesystem/index.js");
|
|
8
9
|
const index_js_3 = require("./network/index.js");
|
|
9
10
|
const preview_js_1 = require("./preview.js");
|
|
@@ -37,48 +38,18 @@ class SandboxInstance {
|
|
|
37
38
|
get spec() {
|
|
38
39
|
return this.sandbox.spec;
|
|
39
40
|
}
|
|
41
|
+
/* eslint-disable */
|
|
40
42
|
async wait({ maxWait = 60000, interval = 1000 } = {}) {
|
|
41
|
-
|
|
42
|
-
while (this.sandbox.status !== "DEPLOYED") {
|
|
43
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
44
|
-
try {
|
|
45
|
-
const { data } = await (0, index_js_1.getSandbox)({
|
|
46
|
-
path: {
|
|
47
|
-
sandboxName: this.sandbox.metadata?.name ?? "",
|
|
48
|
-
},
|
|
49
|
-
throwOnError: true,
|
|
50
|
-
});
|
|
51
|
-
logger_js_1.logger.debug(`Waiting for sandbox to be deployed, status: ${data.status}`);
|
|
52
|
-
this.sandbox = data;
|
|
53
|
-
}
|
|
54
|
-
catch (e) {
|
|
55
|
-
logger_js_1.logger.error("Could not retrieve sandbox", e);
|
|
56
|
-
}
|
|
57
|
-
if (this.sandbox.status === "FAILED") {
|
|
58
|
-
throw new Error("Sandbox failed to deploy");
|
|
59
|
-
}
|
|
60
|
-
if (Date.now() - startTime > maxWait) {
|
|
61
|
-
throw new Error("Sandbox did not deploy in time");
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (this.sandbox.status === "DEPLOYED") {
|
|
65
|
-
try {
|
|
66
|
-
// This is a hack for sometime receiving a 502,
|
|
67
|
-
// need to remove this once we have a better way to handle this
|
|
68
|
-
await this.fs.ls("/");
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
// pass
|
|
72
|
-
}
|
|
73
|
-
}
|
|
43
|
+
logger_js_1.logger.warn("⚠️ Warning: sandbox.wait() is deprecated. You don't need to wait for the sandbox to be deployed anymore.");
|
|
74
44
|
return this;
|
|
75
45
|
}
|
|
76
|
-
static async create(sandbox) {
|
|
46
|
+
static async create(sandbox, { safe = true } = {}) {
|
|
47
|
+
const env = settings_js_1.settings.env;
|
|
77
48
|
const defaultName = `sandbox-${(0, uuid_1.v4)().replace(/-/g, '').substring(0, 8)}`;
|
|
78
|
-
const defaultImage =
|
|
49
|
+
const defaultImage = `blaxel/${env}-base:latest`;
|
|
79
50
|
const defaultMemory = 4096;
|
|
80
|
-
// Handle SandboxCreateConfiguration or simple dict with name/image/memory/ports/envs keys
|
|
81
|
-
if (!sandbox || 'name' in sandbox || 'image' in sandbox || 'memory' in sandbox || 'ports' in sandbox || 'envs' in sandbox) {
|
|
51
|
+
// Handle SandboxCreateConfiguration or simple dict with name/image/memory/ports/envs/volumes keys
|
|
52
|
+
if (!sandbox || 'name' in sandbox || 'image' in sandbox || 'memory' in sandbox || 'ports' in sandbox || 'envs' in sandbox || 'volumes' in sandbox) {
|
|
82
53
|
if (!sandbox)
|
|
83
54
|
sandbox = {};
|
|
84
55
|
if (!sandbox.name)
|
|
@@ -89,25 +60,37 @@ class SandboxInstance {
|
|
|
89
60
|
sandbox.memory = defaultMemory;
|
|
90
61
|
const ports = (0, types_js_1.normalizePorts)(sandbox.ports);
|
|
91
62
|
const envs = (0, types_js_1.normalizeEnvs)(sandbox.envs);
|
|
63
|
+
const volumes = (0, types_js_1.normalizeVolumes)(sandbox.volumes);
|
|
64
|
+
const ttl = sandbox.ttl;
|
|
65
|
+
const expires = sandbox.expires;
|
|
66
|
+
const region = sandbox.region;
|
|
92
67
|
sandbox = {
|
|
93
68
|
metadata: { name: sandbox.name },
|
|
94
69
|
spec: {
|
|
70
|
+
region: region,
|
|
95
71
|
runtime: {
|
|
96
72
|
image: sandbox.image,
|
|
97
73
|
memory: sandbox.memory,
|
|
98
74
|
ports: ports,
|
|
99
75
|
envs: envs,
|
|
100
|
-
generation: "mk3"
|
|
101
|
-
}
|
|
76
|
+
generation: "mk3",
|
|
77
|
+
},
|
|
78
|
+
volumes: volumes
|
|
102
79
|
}
|
|
103
80
|
};
|
|
81
|
+
if (ttl) {
|
|
82
|
+
sandbox.spec.runtime.ttl = ttl;
|
|
83
|
+
}
|
|
84
|
+
if (expires) {
|
|
85
|
+
sandbox.spec.runtime.expires = expires.toISOString();
|
|
86
|
+
}
|
|
104
87
|
}
|
|
105
88
|
sandbox = sandbox;
|
|
106
89
|
if (!sandbox.metadata) {
|
|
107
|
-
sandbox.metadata = { name:
|
|
90
|
+
sandbox.metadata = { name: defaultName };
|
|
108
91
|
}
|
|
109
92
|
if (!sandbox.spec) {
|
|
110
|
-
sandbox.spec = { runtime: { image:
|
|
93
|
+
sandbox.spec = { runtime: { image: defaultImage, memory: defaultMemory } };
|
|
111
94
|
}
|
|
112
95
|
if (!sandbox.spec.runtime) {
|
|
113
96
|
sandbox.spec.runtime = { image: defaultImage, memory: defaultMemory };
|
|
@@ -119,7 +102,15 @@ class SandboxInstance {
|
|
|
119
102
|
body: sandbox,
|
|
120
103
|
throwOnError: true,
|
|
121
104
|
});
|
|
122
|
-
|
|
105
|
+
const instance = new SandboxInstance(data);
|
|
106
|
+
// TODO remove this part once we have a better way to handle this
|
|
107
|
+
if (safe) {
|
|
108
|
+
try {
|
|
109
|
+
await instance.fs.ls('/');
|
|
110
|
+
}
|
|
111
|
+
catch { }
|
|
112
|
+
}
|
|
113
|
+
return instance;
|
|
123
114
|
}
|
|
124
115
|
static async get(sandboxName) {
|
|
125
116
|
const { data } = await (0, index_js_1.getSandbox)({
|
|
@@ -143,18 +134,29 @@ class SandboxInstance {
|
|
|
143
134
|
});
|
|
144
135
|
return data;
|
|
145
136
|
}
|
|
137
|
+
static async updateMetadata(sandboxName, metadata) {
|
|
138
|
+
const sandbox = await SandboxInstance.get(sandboxName);
|
|
139
|
+
const body = { ...sandbox.sandbox, metadata: { ...sandbox.metadata, ...metadata } };
|
|
140
|
+
const { data } = await (0, index_js_1.updateSandbox)({
|
|
141
|
+
path: { sandboxName },
|
|
142
|
+
body,
|
|
143
|
+
throwOnError: true,
|
|
144
|
+
});
|
|
145
|
+
const instance = new SandboxInstance(data);
|
|
146
|
+
return instance;
|
|
147
|
+
}
|
|
146
148
|
static async createIfNotExists(sandbox) {
|
|
147
149
|
try {
|
|
148
|
-
|
|
149
|
-
if (!name) {
|
|
150
|
-
throw new Error("Sandbox name is required");
|
|
151
|
-
}
|
|
152
|
-
const sandboxInstance = await SandboxInstance.get(name);
|
|
153
|
-
return sandboxInstance;
|
|
150
|
+
return await SandboxInstance.create(sandbox);
|
|
154
151
|
}
|
|
155
152
|
catch (e) {
|
|
156
|
-
if (typeof e === "object" && e !== null && "code" in e && e.code ===
|
|
157
|
-
|
|
153
|
+
if (typeof e === "object" && e !== null && "code" in e && (e.code === 409 || e.code === 'SANDBOX_ALREADY_EXISTS')) {
|
|
154
|
+
const name = 'name' in sandbox ? sandbox.name : sandbox.metadata?.name;
|
|
155
|
+
if (!name) {
|
|
156
|
+
throw new Error("Sandbox name is required");
|
|
157
|
+
}
|
|
158
|
+
const sandboxInstance = await SandboxInstance.get(name);
|
|
159
|
+
return sandboxInstance;
|
|
158
160
|
}
|
|
159
161
|
throw e;
|
|
160
162
|
}
|
package/dist/sandbox/session.js
CHANGED
|
@@ -20,6 +20,7 @@ class SandboxSessions {
|
|
|
20
20
|
spec: {
|
|
21
21
|
port: 443,
|
|
22
22
|
public: false,
|
|
23
|
+
expires: expiresAt.toISOString(),
|
|
23
24
|
requestHeaders: options.requestHeaders,
|
|
24
25
|
responseHeaders: options.responseHeaders,
|
|
25
26
|
},
|
|
@@ -51,7 +52,7 @@ class SandboxSessions {
|
|
|
51
52
|
// If no valid session exists, create a new one
|
|
52
53
|
if (allSessions.length > 0) {
|
|
53
54
|
sessionData = allSessions[0];
|
|
54
|
-
if (sessionData.expiresAt < threshold) {
|
|
55
|
+
if (new Date(sessionData.expiresAt) < threshold) {
|
|
55
56
|
await this.delete(sessionData.name);
|
|
56
57
|
sessionData = await this.create(options);
|
|
57
58
|
}
|
|
@@ -69,6 +70,8 @@ class SandboxSessions {
|
|
|
69
70
|
},
|
|
70
71
|
throwOnError: true,
|
|
71
72
|
});
|
|
73
|
+
if (data === null)
|
|
74
|
+
return [];
|
|
72
75
|
return await Promise.all(data.filter((preview) => preview.metadata?.name?.includes("session-")).map(async (preview) => {
|
|
73
76
|
const token = await this.getToken(preview.metadata?.name ?? "");
|
|
74
77
|
return {
|
package/dist/sandbox/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Port, Sandbox } from "../client/types.gen";
|
|
1
|
+
import { Port, Sandbox, VolumeAttachment } from "../client/types.gen";
|
|
2
2
|
import { PostProcessResponse, ProcessRequest } from "./client";
|
|
3
3
|
export interface SessionCreateOptions {
|
|
4
4
|
expiresAt?: Date;
|
|
@@ -15,20 +15,34 @@ export interface EnvVar {
|
|
|
15
15
|
name: string;
|
|
16
16
|
value: string;
|
|
17
17
|
}
|
|
18
|
+
export interface VolumeBinding {
|
|
19
|
+
name: string;
|
|
20
|
+
mountPath: string;
|
|
21
|
+
readOnly?: boolean;
|
|
22
|
+
}
|
|
18
23
|
export type SandboxConfiguration = {
|
|
19
24
|
forceUrl?: string;
|
|
20
25
|
headers?: Record<string, string>;
|
|
21
26
|
params?: Record<string, string>;
|
|
22
27
|
} & Sandbox;
|
|
28
|
+
export type SandboxUpdateMetadata = {
|
|
29
|
+
labels?: Record<string, string>;
|
|
30
|
+
displayName?: string;
|
|
31
|
+
};
|
|
23
32
|
export type SandboxCreateConfiguration = {
|
|
24
33
|
name?: string;
|
|
25
34
|
image?: string;
|
|
26
35
|
memory?: number;
|
|
27
36
|
ports?: (Port | Record<string, any>)[];
|
|
28
37
|
envs?: EnvVar[];
|
|
38
|
+
volumes?: (VolumeBinding | VolumeAttachment)[];
|
|
39
|
+
ttl?: string;
|
|
40
|
+
expires?: Date;
|
|
41
|
+
region?: string;
|
|
29
42
|
};
|
|
30
43
|
export declare function normalizePorts(ports?: (Port | Record<string, any>)[]): Port[] | undefined;
|
|
31
44
|
export declare function normalizeEnvs(envs?: EnvVar[]): EnvVar[] | undefined;
|
|
45
|
+
export declare function normalizeVolumes(volumes?: (VolumeBinding | VolumeAttachment)[]): VolumeAttachment[] | undefined;
|
|
32
46
|
export type ProcessRequestWithLog = ProcessRequest & {
|
|
33
47
|
onLog?: (log: string) => void;
|
|
34
48
|
};
|
package/dist/sandbox/types.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.normalizePorts = normalizePorts;
|
|
4
4
|
exports.normalizeEnvs = normalizeEnvs;
|
|
5
|
+
exports.normalizeVolumes = normalizeVolumes;
|
|
5
6
|
function normalizePorts(ports) {
|
|
6
7
|
if (!ports || ports.length === 0) {
|
|
7
8
|
return undefined;
|
|
@@ -50,3 +51,31 @@ function normalizeEnvs(envs) {
|
|
|
50
51
|
}
|
|
51
52
|
return envObjects;
|
|
52
53
|
}
|
|
54
|
+
function normalizeVolumes(volumes) {
|
|
55
|
+
if (!volumes || volumes.length === 0) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const volumeObjects = [];
|
|
59
|
+
for (const volume of volumes) {
|
|
60
|
+
if (typeof volume === 'object' && volume !== null) {
|
|
61
|
+
// Validate that the object has the required keys
|
|
62
|
+
if (!('name' in volume) || !('mountPath' in volume)) {
|
|
63
|
+
throw new Error(`Volume binding object must have 'name' and 'mountPath' keys: ${JSON.stringify(volume)}`);
|
|
64
|
+
}
|
|
65
|
+
if (typeof volume.name !== 'string' || typeof volume.mountPath !== 'string') {
|
|
66
|
+
throw new Error(`Volume binding 'name' and 'mountPath' must be strings: ${JSON.stringify(volume)}`);
|
|
67
|
+
}
|
|
68
|
+
// Convert VolumeBinding to VolumeAttachment format
|
|
69
|
+
const volumeAttachment = {
|
|
70
|
+
name: volume.name,
|
|
71
|
+
mountPath: volume.mountPath,
|
|
72
|
+
readOnly: 'readOnly' in volume ? volume.readOnly : false
|
|
73
|
+
};
|
|
74
|
+
volumeObjects.push(volumeAttachment);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw new Error(`Invalid volume type: ${typeof volume}. Expected object with 'name' and 'mountPath' keys.`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return volumeObjects;
|
|
81
|
+
}
|
package/dist/tools/mcpTool.js
CHANGED
|
@@ -74,13 +74,18 @@ class McpTool {
|
|
|
74
74
|
await (0, index_js_2.authenticate)();
|
|
75
75
|
try {
|
|
76
76
|
logger_js_1.logger.debug(`MCP:${this.name}:Connecting::${this.url.toString()}`);
|
|
77
|
-
this.transport = new client_js_1.BlaxelMcpClientTransport(this.url.toString(), settings_js_1.settings.headers);
|
|
77
|
+
this.transport = new client_js_1.BlaxelMcpClientTransport(this.url.toString(), settings_js_1.settings.headers, { retry: { max: 0 } });
|
|
78
78
|
await this.client.connect(this.transport);
|
|
79
79
|
logger_js_1.logger.debug(`MCP:${this.name}:Connected`);
|
|
80
80
|
}
|
|
81
81
|
catch (err) {
|
|
82
82
|
if (err instanceof Error) {
|
|
83
|
-
logger_js_1.logger.error(err.
|
|
83
|
+
logger_js_1.logger.error(`MCP ${this.name} connection failed: ${err.message}`, {
|
|
84
|
+
error: err.message,
|
|
85
|
+
stack: err.stack,
|
|
86
|
+
mcpName: this.name,
|
|
87
|
+
url: this.url
|
|
88
|
+
});
|
|
84
89
|
}
|
|
85
90
|
if (!this.fallbackUrl) {
|
|
86
91
|
throw err;
|
|
@@ -107,7 +112,11 @@ class McpTool {
|
|
|
107
112
|
delete this.startPromise;
|
|
108
113
|
this.client.close().catch((err) => {
|
|
109
114
|
if (err instanceof Error) {
|
|
110
|
-
logger_js_1.logger.error(err.
|
|
115
|
+
logger_js_1.logger.error(`MCP ${this.name} close failed: ${err.message}`, {
|
|
116
|
+
error: err.message,
|
|
117
|
+
stack: err.stack,
|
|
118
|
+
mcpName: this.name
|
|
119
|
+
});
|
|
111
120
|
}
|
|
112
121
|
});
|
|
113
122
|
}, now ? 0 : this.ms);
|
|
@@ -178,7 +187,13 @@ class McpTool {
|
|
|
178
187
|
}
|
|
179
188
|
catch (err) {
|
|
180
189
|
if (err instanceof Error) {
|
|
181
|
-
logger_js_1.logger.error(err.
|
|
190
|
+
logger_js_1.logger.error(`MCP tool call failed: ${err.message}`, {
|
|
191
|
+
error: err.message,
|
|
192
|
+
stack: err.stack,
|
|
193
|
+
mcpName: this.name,
|
|
194
|
+
toolName,
|
|
195
|
+
args: JSON.stringify(args)
|
|
196
|
+
});
|
|
182
197
|
}
|
|
183
198
|
throw err;
|
|
184
199
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Volume } from "../client/index.js";
|
|
2
|
+
export interface VolumeCreateConfiguration {
|
|
3
|
+
name?: string;
|
|
4
|
+
displayName?: string;
|
|
5
|
+
size?: number;
|
|
6
|
+
region?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class VolumeInstance {
|
|
9
|
+
private volume;
|
|
10
|
+
constructor(volume: Volume);
|
|
11
|
+
get metadata(): import("../client/types.gen.js").Metadata | undefined;
|
|
12
|
+
get spec(): import("../client/types.gen.js").VolumeSpec | undefined;
|
|
13
|
+
get status(): string | undefined;
|
|
14
|
+
get name(): string | undefined;
|
|
15
|
+
get displayName(): string | undefined;
|
|
16
|
+
get size(): number | undefined;
|
|
17
|
+
get region(): string | undefined;
|
|
18
|
+
static create(config: VolumeCreateConfiguration | Volume): Promise<VolumeInstance>;
|
|
19
|
+
static get(volumeName: string): Promise<VolumeInstance>;
|
|
20
|
+
static list(): Promise<VolumeInstance[]>;
|
|
21
|
+
static delete(volumeName: string): Promise<Volume>;
|
|
22
|
+
static createIfNotExists(config: VolumeCreateConfiguration | Volume): Promise<VolumeInstance>;
|
|
23
|
+
}
|