@blaxel/core 0.2.36-dev.185 → 0.2.36-dev.190
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/cjs/.tsbuildinfo +1 -0
- package/dist/{common → cjs/common}/settings.js +2 -2
- package/dist/{tools → cjs/types/tools}/index.d.ts +1 -0
- package/dist/esm/.tsbuildinfo +1 -0
- package/dist/esm/agents/index.js +104 -0
- package/dist/esm/authentication/apikey.js +20 -0
- package/dist/esm/authentication/clientcredentials.js +81 -0
- package/dist/esm/authentication/credentials.js +13 -0
- package/dist/esm/authentication/deviceMode.js +66 -0
- package/dist/esm/authentication/index.js +56 -0
- package/dist/esm/authentication/types.js +1 -0
- package/dist/esm/cache/index.js +20 -0
- package/dist/esm/client/authentication.js +11 -0
- package/dist/esm/client/client.gen.js +5 -0
- package/dist/esm/client/client.js +1 -0
- package/dist/esm/client/index.js +3 -0
- package/dist/esm/client/interceptors.js +14 -0
- package/dist/esm/client/sdk.gen.js +1649 -0
- package/dist/esm/client/types.gen.js +3 -0
- package/dist/esm/common/autoload.js +23 -0
- package/dist/esm/common/env.js +36 -0
- package/dist/esm/common/errors.js +14 -0
- package/dist/esm/common/internal.js +182 -0
- package/dist/esm/common/internal.test.js +37 -0
- package/dist/esm/common/logger.js +65 -0
- package/dist/esm/common/node.js +20 -0
- package/dist/esm/common/settings.js +167 -0
- package/dist/esm/index.browser.test.js +10 -0
- package/dist/esm/index.js +17 -0
- package/dist/esm/jobs/index.js +3 -0
- package/dist/esm/jobs/jobs.js +86 -0
- package/dist/esm/jobs/start.js +62 -0
- package/dist/esm/jobs/types.js +1 -0
- package/dist/esm/mcp/client.js +243 -0
- package/dist/esm/mcp/index.js +2 -0
- package/dist/esm/mcp/server.js +176 -0
- package/dist/esm/models/index.js +25 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/sandbox/action.js +79 -0
- package/dist/esm/sandbox/client/client.gen.js +3 -0
- package/dist/esm/sandbox/client/index.js +3 -0
- package/dist/esm/sandbox/client/sdk.gen.js +270 -0
- package/dist/esm/sandbox/client/types.gen.js +2 -0
- package/dist/esm/sandbox/filesystem/filesystem.js +272 -0
- package/dist/esm/sandbox/filesystem/index.js +2 -0
- package/dist/esm/sandbox/filesystem/types.js +1 -0
- package/dist/esm/sandbox/index.js +7 -0
- package/dist/esm/sandbox/network/index.js +1 -0
- package/dist/esm/sandbox/network/network.js +6 -0
- package/dist/esm/sandbox/preview.js +141 -0
- package/dist/esm/sandbox/process/index.js +1 -0
- package/dist/esm/sandbox/process/process.js +185 -0
- package/dist/esm/sandbox/sandbox.js +174 -0
- package/dist/esm/sandbox/session.js +119 -0
- package/dist/esm/sandbox/types.js +76 -0
- package/dist/esm/telemetry/telemetry.js +74 -0
- package/dist/esm/tools/index.js +44 -0
- package/dist/esm/tools/mcpTool.js +213 -0
- package/dist/esm/tools/types.js +1 -0
- package/dist/esm/tools/zodSchema.js +43 -0
- package/dist/esm/volume/index.js +109 -0
- package/package.json +17 -30
- /package/dist/{agents → cjs/agents}/index.js +0 -0
- /package/dist/{authentication → cjs/authentication}/apikey.js +0 -0
- /package/dist/{authentication → cjs/authentication}/clientcredentials.js +0 -0
- /package/dist/{authentication → cjs/authentication}/credentials.js +0 -0
- /package/dist/{authentication → cjs/authentication}/deviceMode.js +0 -0
- /package/dist/{authentication → cjs/authentication}/index.js +0 -0
- /package/dist/{authentication → cjs/authentication}/types.js +0 -0
- /package/dist/{cache → cjs/cache}/index.js +0 -0
- /package/dist/{client → cjs/client}/authentication.js +0 -0
- /package/dist/{client → cjs/client}/client.gen.js +0 -0
- /package/dist/{client → cjs/client}/client.js +0 -0
- /package/dist/{client → cjs/client}/index.js +0 -0
- /package/dist/{client → cjs/client}/interceptors.js +0 -0
- /package/dist/{client → cjs/client}/sdk.gen.js +0 -0
- /package/dist/{client → cjs/client}/types.gen.js +0 -0
- /package/dist/{common → cjs/common}/autoload.js +0 -0
- /package/dist/{common → cjs/common}/env.js +0 -0
- /package/dist/{common → cjs/common}/errors.js +0 -0
- /package/dist/{common → cjs/common}/internal.js +0 -0
- /package/dist/{common → cjs/common}/internal.test.js +0 -0
- /package/dist/{common → cjs/common}/logger.js +0 -0
- /package/dist/{common → cjs/common}/node.js +0 -0
- /package/dist/{index.browser.test.js → cjs/index.browser.test.js} +0 -0
- /package/dist/{index.js → cjs/index.js} +0 -0
- /package/dist/{jobs → cjs/jobs}/index.js +0 -0
- /package/dist/{jobs → cjs/jobs}/jobs.js +0 -0
- /package/dist/{jobs → cjs/jobs}/start.js +0 -0
- /package/dist/{jobs → cjs/jobs}/types.js +0 -0
- /package/dist/{mcp → cjs/mcp}/client.js +0 -0
- /package/dist/{mcp → cjs/mcp}/index.js +0 -0
- /package/dist/{mcp → cjs/mcp}/server.js +0 -0
- /package/dist/{models → cjs/models}/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/action.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/client/client.gen.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/client/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/client/sdk.gen.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/client/types.gen.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/filesystem/filesystem.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/filesystem/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/filesystem/types.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/network/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/network/network.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/preview.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/process/index.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/process/process.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/sandbox.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/session.js +0 -0
- /package/dist/{sandbox → cjs/sandbox}/types.js +0 -0
- /package/dist/{telemetry → cjs/telemetry}/telemetry.js +0 -0
- /package/dist/{tools → cjs/tools}/index.js +0 -0
- /package/dist/{tools → cjs/tools}/mcpTool.js +0 -0
- /package/dist/{tools → cjs/tools}/types.js +0 -0
- /package/dist/{tools → cjs/tools}/zodSchema.js +0 -0
- /package/dist/{agents → cjs/types/agents}/index.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/apikey.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/clientcredentials.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/credentials.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/deviceMode.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/index.d.ts +0 -0
- /package/dist/{authentication → cjs/types/authentication}/types.d.ts +0 -0
- /package/dist/{cache → cjs/types/cache}/index.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/authentication.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/client.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/client.gen.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/index.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/interceptors.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/sdk.gen.d.ts +0 -0
- /package/dist/{client → cjs/types/client}/types.gen.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/autoload.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/env.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/errors.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/internal.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/internal.test.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/logger.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/node.d.ts +0 -0
- /package/dist/{common → cjs/types/common}/settings.d.ts +0 -0
- /package/dist/{index.browser.test.d.ts → cjs/types/index.browser.test.d.ts} +0 -0
- /package/dist/{index.d.ts → cjs/types/index.d.ts} +0 -0
- /package/dist/{jobs → cjs/types/jobs}/index.d.ts +0 -0
- /package/dist/{jobs → cjs/types/jobs}/jobs.d.ts +0 -0
- /package/dist/{jobs → cjs/types/jobs}/start.d.ts +0 -0
- /package/dist/{jobs → cjs/types/jobs}/types.d.ts +0 -0
- /package/dist/{mcp → cjs/types/mcp}/client.d.ts +0 -0
- /package/dist/{mcp → cjs/types/mcp}/index.d.ts +0 -0
- /package/dist/{mcp → cjs/types/mcp}/server.d.ts +0 -0
- /package/dist/{models → cjs/types/models}/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/action.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/client/client.gen.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/client/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/client/sdk.gen.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/client/types.gen.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/filesystem/filesystem.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/filesystem/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/filesystem/types.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/network/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/network/network.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/preview.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/process/index.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/process/process.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/sandbox.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/session.d.ts +0 -0
- /package/dist/{sandbox → cjs/types/sandbox}/types.d.ts +0 -0
- /package/dist/{telemetry → cjs/types/telemetry}/telemetry.d.ts +0 -0
- /package/dist/{tools → cjs/types/tools}/mcpTool.d.ts +0 -0
- /package/dist/{tools → cjs/types/tools}/types.d.ts +0 -0
- /package/dist/{tools → cjs/types/tools}/zodSchema.d.ts +0 -0
- /package/dist/{volume → cjs/types/volume}/index.d.ts +0 -0
- /package/dist/{volume → cjs/volume}/index.js +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { createSandboxPreview, createSandboxPreviewToken, deleteSandboxPreview, deleteSandboxPreviewToken, getSandboxPreview, listSandboxPreviews, listSandboxPreviewTokens } from "../client/index.js";
|
|
2
|
+
export class SandboxPreviewToken {
|
|
3
|
+
previewToken;
|
|
4
|
+
constructor(previewToken) {
|
|
5
|
+
this.previewToken = previewToken;
|
|
6
|
+
}
|
|
7
|
+
get value() {
|
|
8
|
+
return this.previewToken.spec?.token ?? "";
|
|
9
|
+
}
|
|
10
|
+
get expiresAt() {
|
|
11
|
+
return this.previewToken.spec?.expiresAt ?? new Date();
|
|
12
|
+
}
|
|
13
|
+
get expired() {
|
|
14
|
+
return this.previewToken.spec?.expired ?? false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class SandboxPreviewTokens {
|
|
18
|
+
preview;
|
|
19
|
+
constructor(preview) {
|
|
20
|
+
this.preview = preview;
|
|
21
|
+
}
|
|
22
|
+
get previewName() {
|
|
23
|
+
return this.preview.metadata?.name ?? "";
|
|
24
|
+
}
|
|
25
|
+
get resourceName() {
|
|
26
|
+
return this.preview.metadata?.resourceName ?? "";
|
|
27
|
+
}
|
|
28
|
+
async create(expiresAt) {
|
|
29
|
+
const { data } = await createSandboxPreviewToken({
|
|
30
|
+
path: {
|
|
31
|
+
sandboxName: this.resourceName,
|
|
32
|
+
previewName: this.previewName,
|
|
33
|
+
},
|
|
34
|
+
body: {
|
|
35
|
+
spec: {
|
|
36
|
+
expiresAt: expiresAt.toISOString(),
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
throwOnError: true,
|
|
40
|
+
});
|
|
41
|
+
return new SandboxPreviewToken(data);
|
|
42
|
+
}
|
|
43
|
+
async list() {
|
|
44
|
+
const { data } = await listSandboxPreviewTokens({
|
|
45
|
+
path: {
|
|
46
|
+
sandboxName: this.resourceName,
|
|
47
|
+
previewName: this.previewName,
|
|
48
|
+
},
|
|
49
|
+
throwOnError: true,
|
|
50
|
+
});
|
|
51
|
+
return data.map((token) => new SandboxPreviewToken(token));
|
|
52
|
+
}
|
|
53
|
+
async delete(tokenName) {
|
|
54
|
+
const { data } = await deleteSandboxPreviewToken({
|
|
55
|
+
path: {
|
|
56
|
+
sandboxName: this.resourceName,
|
|
57
|
+
previewName: this.previewName,
|
|
58
|
+
tokenName,
|
|
59
|
+
},
|
|
60
|
+
throwOnError: true,
|
|
61
|
+
});
|
|
62
|
+
return data;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export class SandboxPreview {
|
|
66
|
+
preview;
|
|
67
|
+
tokens;
|
|
68
|
+
constructor(preview) {
|
|
69
|
+
this.preview = preview;
|
|
70
|
+
this.tokens = new SandboxPreviewTokens(this);
|
|
71
|
+
}
|
|
72
|
+
get name() {
|
|
73
|
+
return this.preview.metadata?.name ?? "";
|
|
74
|
+
}
|
|
75
|
+
get metadata() {
|
|
76
|
+
return this.preview.metadata;
|
|
77
|
+
}
|
|
78
|
+
get spec() {
|
|
79
|
+
return this.preview.spec;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export class SandboxPreviews {
|
|
83
|
+
sandbox;
|
|
84
|
+
constructor(sandbox) {
|
|
85
|
+
this.sandbox = sandbox;
|
|
86
|
+
}
|
|
87
|
+
get sandboxName() {
|
|
88
|
+
return this.sandbox.metadata?.name ?? "";
|
|
89
|
+
}
|
|
90
|
+
async list() {
|
|
91
|
+
const { data } = await listSandboxPreviews({
|
|
92
|
+
path: {
|
|
93
|
+
sandboxName: this.sandboxName,
|
|
94
|
+
},
|
|
95
|
+
throwOnError: true,
|
|
96
|
+
});
|
|
97
|
+
return data.map((preview) => new SandboxPreview(preview));
|
|
98
|
+
}
|
|
99
|
+
async create(preview) {
|
|
100
|
+
const { data } = await createSandboxPreview({
|
|
101
|
+
path: {
|
|
102
|
+
sandboxName: this.sandboxName,
|
|
103
|
+
},
|
|
104
|
+
body: preview,
|
|
105
|
+
throwOnError: true,
|
|
106
|
+
});
|
|
107
|
+
return new SandboxPreview(data);
|
|
108
|
+
}
|
|
109
|
+
async createIfNotExists(preview) {
|
|
110
|
+
try {
|
|
111
|
+
const previewInstance = await this.get(preview.metadata?.name ?? "");
|
|
112
|
+
return previewInstance;
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
if (typeof e === "object" && e !== null && "code" in e && e.code === 404) {
|
|
116
|
+
return this.create(preview);
|
|
117
|
+
}
|
|
118
|
+
throw e;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async get(previewName) {
|
|
122
|
+
const { data } = await getSandboxPreview({
|
|
123
|
+
path: {
|
|
124
|
+
sandboxName: this.sandboxName,
|
|
125
|
+
previewName,
|
|
126
|
+
},
|
|
127
|
+
throwOnError: true,
|
|
128
|
+
});
|
|
129
|
+
return new SandboxPreview(data);
|
|
130
|
+
}
|
|
131
|
+
async delete(previewName) {
|
|
132
|
+
const { data } = await deleteSandboxPreview({
|
|
133
|
+
path: {
|
|
134
|
+
sandboxName: this.sandboxName,
|
|
135
|
+
previewName,
|
|
136
|
+
},
|
|
137
|
+
throwOnError: true,
|
|
138
|
+
});
|
|
139
|
+
return data;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./process.js";
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { settings } from "../../common/settings.js";
|
|
2
|
+
import { SandboxAction } from "../action.js";
|
|
3
|
+
import { deleteProcessByIdentifier, deleteProcessByIdentifierKill, getProcess, getProcessByIdentifier, getProcessByIdentifierLogs, postProcess } from "../client/index.js";
|
|
4
|
+
export class SandboxProcess extends SandboxAction {
|
|
5
|
+
constructor(sandbox) {
|
|
6
|
+
super(sandbox);
|
|
7
|
+
}
|
|
8
|
+
streamLogs(identifier, options) {
|
|
9
|
+
const controller = new AbortController();
|
|
10
|
+
void (async () => {
|
|
11
|
+
try {
|
|
12
|
+
const headers = this.sandbox.forceUrl ? this.sandbox.headers : settings.headers;
|
|
13
|
+
const stream = await fetch(`${this.url}/process/${identifier}/logs/stream`, {
|
|
14
|
+
method: 'GET',
|
|
15
|
+
signal: controller.signal,
|
|
16
|
+
headers,
|
|
17
|
+
});
|
|
18
|
+
if (stream.status !== 200) {
|
|
19
|
+
throw new Error(`Failed to stream logs: ${await stream.text()}`);
|
|
20
|
+
}
|
|
21
|
+
if (!stream.body)
|
|
22
|
+
throw new Error('No stream body');
|
|
23
|
+
const reader = stream.body.getReader();
|
|
24
|
+
const decoder = new TextDecoder();
|
|
25
|
+
let buffer = '';
|
|
26
|
+
while (true) {
|
|
27
|
+
const result = await reader.read();
|
|
28
|
+
if (result.done)
|
|
29
|
+
break;
|
|
30
|
+
if (result.value && result.value instanceof Uint8Array) {
|
|
31
|
+
buffer += decoder.decode(result.value, { stream: true });
|
|
32
|
+
}
|
|
33
|
+
const lines = buffer.split(/\r?\n/);
|
|
34
|
+
buffer = lines.pop();
|
|
35
|
+
for (const line of lines) {
|
|
36
|
+
if (line.startsWith("[keepalive]")) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (line.startsWith('stdout:')) {
|
|
40
|
+
options.onStdout?.(line.slice(7));
|
|
41
|
+
options.onLog?.(line.slice(7));
|
|
42
|
+
}
|
|
43
|
+
else if (line.startsWith('stderr:')) {
|
|
44
|
+
options.onStderr?.(line.slice(7));
|
|
45
|
+
options.onLog?.(line.slice(7));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
options.onLog?.(line);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err && typeof err === 'object' && 'name' in err && err.name !== 'AbortError') {
|
|
55
|
+
console.error("Stream error:", err);
|
|
56
|
+
throw new Error(err instanceof Error ? err.message : 'Unknown stream error');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
})();
|
|
60
|
+
return {
|
|
61
|
+
close: () => controller.abort(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async exec(process) {
|
|
65
|
+
let onLog;
|
|
66
|
+
if ('onLog' in process && process.onLog) {
|
|
67
|
+
onLog = process.onLog;
|
|
68
|
+
delete process.onLog;
|
|
69
|
+
}
|
|
70
|
+
// Store original wait_for_completion setting
|
|
71
|
+
const shouldWaitForCompletion = process.waitForCompletion;
|
|
72
|
+
// Always start process without wait_for_completion to avoid server-side blocking
|
|
73
|
+
if (shouldWaitForCompletion && onLog) {
|
|
74
|
+
process.waitForCompletion = false;
|
|
75
|
+
}
|
|
76
|
+
const { response, data, error } = await postProcess({
|
|
77
|
+
body: process,
|
|
78
|
+
baseUrl: this.url,
|
|
79
|
+
client: this.client,
|
|
80
|
+
});
|
|
81
|
+
this.handleResponseError(response, data, error);
|
|
82
|
+
let result = data;
|
|
83
|
+
// Handle wait_for_completion with parallel log streaming
|
|
84
|
+
if (shouldWaitForCompletion && onLog) {
|
|
85
|
+
const streamControl = this.streamLogs(result.pid, { onLog });
|
|
86
|
+
try {
|
|
87
|
+
// Wait for process completion
|
|
88
|
+
result = await this.wait(result.pid, { interval: 500, maxWait: 1000 * 60 * 60 });
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
// Clean up log streaming
|
|
92
|
+
if (streamControl) {
|
|
93
|
+
streamControl.close();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// For non-blocking execution, set up log streaming immediately if requested
|
|
99
|
+
if (onLog) {
|
|
100
|
+
const streamControl = this.streamLogs(result.pid, { onLog });
|
|
101
|
+
return {
|
|
102
|
+
...result,
|
|
103
|
+
close() {
|
|
104
|
+
if (streamControl) {
|
|
105
|
+
streamControl.close();
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { ...result, close: () => { } };
|
|
112
|
+
}
|
|
113
|
+
async wait(identifier, { maxWait = 60000, interval = 1000 } = {}) {
|
|
114
|
+
const startTime = Date.now();
|
|
115
|
+
let status = "running";
|
|
116
|
+
let data = await this.get(identifier);
|
|
117
|
+
while (status === "running") {
|
|
118
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
119
|
+
try {
|
|
120
|
+
data = await this.get(identifier);
|
|
121
|
+
status = data.status ?? "running";
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
if (Date.now() - startTime > maxWait) {
|
|
127
|
+
throw new Error("Process did not finish in time");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return data;
|
|
131
|
+
}
|
|
132
|
+
async get(identifier) {
|
|
133
|
+
const { response, data, error } = await getProcessByIdentifier({
|
|
134
|
+
path: { identifier },
|
|
135
|
+
baseUrl: this.url,
|
|
136
|
+
client: this.client,
|
|
137
|
+
});
|
|
138
|
+
this.handleResponseError(response, data, error);
|
|
139
|
+
return data;
|
|
140
|
+
}
|
|
141
|
+
async list() {
|
|
142
|
+
const { response, data, error } = await getProcess({
|
|
143
|
+
baseUrl: this.url,
|
|
144
|
+
client: this.client,
|
|
145
|
+
});
|
|
146
|
+
this.handleResponseError(response, data, error);
|
|
147
|
+
return data;
|
|
148
|
+
}
|
|
149
|
+
async stop(identifier) {
|
|
150
|
+
const { response, data, error } = await deleteProcessByIdentifier({
|
|
151
|
+
path: { identifier },
|
|
152
|
+
baseUrl: this.url,
|
|
153
|
+
client: this.client,
|
|
154
|
+
});
|
|
155
|
+
this.handleResponseError(response, data, error);
|
|
156
|
+
return data;
|
|
157
|
+
}
|
|
158
|
+
async kill(identifier) {
|
|
159
|
+
const { response, data, error } = await deleteProcessByIdentifierKill({
|
|
160
|
+
path: { identifier },
|
|
161
|
+
baseUrl: this.url,
|
|
162
|
+
client: this.client,
|
|
163
|
+
});
|
|
164
|
+
this.handleResponseError(response, data, error);
|
|
165
|
+
return data;
|
|
166
|
+
}
|
|
167
|
+
async logs(identifier, type = "all") {
|
|
168
|
+
const { response, data, error } = await getProcessByIdentifierLogs({
|
|
169
|
+
path: { identifier },
|
|
170
|
+
baseUrl: this.url,
|
|
171
|
+
client: this.client,
|
|
172
|
+
});
|
|
173
|
+
this.handleResponseError(response, data, error);
|
|
174
|
+
if (type === "all") {
|
|
175
|
+
return data?.logs || "";
|
|
176
|
+
}
|
|
177
|
+
else if (type === "stdout") {
|
|
178
|
+
return data?.stdout || "";
|
|
179
|
+
}
|
|
180
|
+
else if (type === "stderr") {
|
|
181
|
+
return data?.stderr || "";
|
|
182
|
+
}
|
|
183
|
+
throw new Error("Unsupported log type");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
import { createSandbox, deleteSandbox, getSandbox, listSandboxes, updateSandbox } from "../client/index.js";
|
|
3
|
+
import { logger } from "../common/logger.js";
|
|
4
|
+
import { settings } from "../common/settings.js";
|
|
5
|
+
import { SandboxFileSystem } from "./filesystem/index.js";
|
|
6
|
+
import { SandboxNetwork } from "./network/index.js";
|
|
7
|
+
import { SandboxPreviews } from "./preview.js";
|
|
8
|
+
import { SandboxProcess } from "./process/index.js";
|
|
9
|
+
import { SandboxSessions } from "./session.js";
|
|
10
|
+
import { normalizeEnvs, normalizePorts, normalizeVolumes } from "./types.js";
|
|
11
|
+
export class SandboxInstance {
|
|
12
|
+
sandbox;
|
|
13
|
+
fs;
|
|
14
|
+
network;
|
|
15
|
+
process;
|
|
16
|
+
previews;
|
|
17
|
+
sessions;
|
|
18
|
+
constructor(sandbox) {
|
|
19
|
+
this.sandbox = sandbox;
|
|
20
|
+
this.fs = new SandboxFileSystem(sandbox);
|
|
21
|
+
this.network = new SandboxNetwork(sandbox);
|
|
22
|
+
this.process = new SandboxProcess(sandbox);
|
|
23
|
+
this.previews = new SandboxPreviews(sandbox);
|
|
24
|
+
this.sessions = new SandboxSessions(sandbox);
|
|
25
|
+
}
|
|
26
|
+
get metadata() {
|
|
27
|
+
return this.sandbox.metadata;
|
|
28
|
+
}
|
|
29
|
+
get status() {
|
|
30
|
+
return this.sandbox.status;
|
|
31
|
+
}
|
|
32
|
+
get events() {
|
|
33
|
+
return this.sandbox.events;
|
|
34
|
+
}
|
|
35
|
+
get spec() {
|
|
36
|
+
return this.sandbox.spec;
|
|
37
|
+
}
|
|
38
|
+
/* eslint-disable */
|
|
39
|
+
async wait({ maxWait = 60000, interval = 1000 } = {}) {
|
|
40
|
+
logger.warn("⚠️ Warning: sandbox.wait() is deprecated. You don't need to wait for the sandbox to be deployed anymore.");
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
static async create(sandbox, { safe = true } = {}) {
|
|
44
|
+
const env = settings.env;
|
|
45
|
+
const defaultName = `sandbox-${uuidv4().replace(/-/g, '').substring(0, 8)}`;
|
|
46
|
+
const defaultImage = `blaxel/${env}-base:latest`;
|
|
47
|
+
const defaultMemory = 4096;
|
|
48
|
+
// Handle SandboxCreateConfiguration or simple dict with name/image/memory/ports/envs/volumes keys
|
|
49
|
+
if (!sandbox || 'name' in sandbox || 'image' in sandbox || 'memory' in sandbox || 'ports' in sandbox || 'envs' in sandbox || 'volumes' in sandbox) {
|
|
50
|
+
if (!sandbox)
|
|
51
|
+
sandbox = {};
|
|
52
|
+
if (!sandbox.name)
|
|
53
|
+
sandbox.name = defaultName;
|
|
54
|
+
if (!sandbox.image)
|
|
55
|
+
sandbox.image = defaultImage;
|
|
56
|
+
if (!sandbox.memory)
|
|
57
|
+
sandbox.memory = defaultMemory;
|
|
58
|
+
const ports = normalizePorts(sandbox.ports);
|
|
59
|
+
const envs = normalizeEnvs(sandbox.envs);
|
|
60
|
+
const volumes = normalizeVolumes(sandbox.volumes);
|
|
61
|
+
const ttl = sandbox.ttl;
|
|
62
|
+
const expires = sandbox.expires;
|
|
63
|
+
const region = sandbox.region;
|
|
64
|
+
sandbox = {
|
|
65
|
+
metadata: { name: sandbox.name },
|
|
66
|
+
spec: {
|
|
67
|
+
region: region,
|
|
68
|
+
runtime: {
|
|
69
|
+
image: sandbox.image,
|
|
70
|
+
memory: sandbox.memory,
|
|
71
|
+
ports: ports,
|
|
72
|
+
envs: envs,
|
|
73
|
+
generation: "mk3",
|
|
74
|
+
},
|
|
75
|
+
volumes: volumes
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
if (ttl) {
|
|
79
|
+
sandbox.spec.runtime.ttl = ttl;
|
|
80
|
+
}
|
|
81
|
+
if (expires) {
|
|
82
|
+
sandbox.spec.runtime.expires = expires.toISOString();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
sandbox = sandbox;
|
|
86
|
+
if (!sandbox.metadata) {
|
|
87
|
+
sandbox.metadata = { name: defaultName };
|
|
88
|
+
}
|
|
89
|
+
if (!sandbox.spec) {
|
|
90
|
+
sandbox.spec = { runtime: { image: defaultImage, memory: defaultMemory } };
|
|
91
|
+
}
|
|
92
|
+
if (!sandbox.spec.runtime) {
|
|
93
|
+
sandbox.spec.runtime = { image: defaultImage, memory: defaultMemory };
|
|
94
|
+
}
|
|
95
|
+
sandbox.spec.runtime.image = sandbox.spec.runtime.image || defaultImage;
|
|
96
|
+
sandbox.spec.runtime.memory = sandbox.spec.runtime.memory || defaultMemory;
|
|
97
|
+
sandbox.spec.runtime.generation = sandbox.spec.runtime.generation || "mk3";
|
|
98
|
+
const { data } = await createSandbox({
|
|
99
|
+
body: sandbox,
|
|
100
|
+
throwOnError: true,
|
|
101
|
+
});
|
|
102
|
+
const instance = new SandboxInstance(data);
|
|
103
|
+
// TODO remove this part once we have a better way to handle this
|
|
104
|
+
if (safe) {
|
|
105
|
+
try {
|
|
106
|
+
await instance.fs.ls('/');
|
|
107
|
+
}
|
|
108
|
+
catch { }
|
|
109
|
+
}
|
|
110
|
+
return instance;
|
|
111
|
+
}
|
|
112
|
+
static async get(sandboxName) {
|
|
113
|
+
const { data } = await getSandbox({
|
|
114
|
+
path: {
|
|
115
|
+
sandboxName,
|
|
116
|
+
},
|
|
117
|
+
throwOnError: true,
|
|
118
|
+
});
|
|
119
|
+
return new SandboxInstance(data);
|
|
120
|
+
}
|
|
121
|
+
static async list() {
|
|
122
|
+
const { data } = await listSandboxes({ throwOnError: true });
|
|
123
|
+
return data.map((sandbox) => new SandboxInstance(sandbox));
|
|
124
|
+
}
|
|
125
|
+
static async delete(sandboxName) {
|
|
126
|
+
const { data } = await deleteSandbox({
|
|
127
|
+
path: {
|
|
128
|
+
sandboxName,
|
|
129
|
+
},
|
|
130
|
+
throwOnError: true,
|
|
131
|
+
});
|
|
132
|
+
return data;
|
|
133
|
+
}
|
|
134
|
+
static async updateMetadata(sandboxName, metadata) {
|
|
135
|
+
const sandbox = await SandboxInstance.get(sandboxName);
|
|
136
|
+
const body = { ...sandbox.sandbox, metadata: { ...sandbox.metadata, ...metadata } };
|
|
137
|
+
const { data } = await updateSandbox({
|
|
138
|
+
path: { sandboxName },
|
|
139
|
+
body,
|
|
140
|
+
throwOnError: true,
|
|
141
|
+
});
|
|
142
|
+
const instance = new SandboxInstance(data);
|
|
143
|
+
return instance;
|
|
144
|
+
}
|
|
145
|
+
static async createIfNotExists(sandbox) {
|
|
146
|
+
try {
|
|
147
|
+
return await SandboxInstance.create(sandbox);
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
if (typeof e === "object" && e !== null && "code" in e && (e.code === 409 || e.code === 'SANDBOX_ALREADY_EXISTS')) {
|
|
151
|
+
const name = 'name' in sandbox ? sandbox.name : sandbox.metadata?.name;
|
|
152
|
+
if (!name) {
|
|
153
|
+
throw new Error("Sandbox name is required");
|
|
154
|
+
}
|
|
155
|
+
const sandboxInstance = await SandboxInstance.get(name);
|
|
156
|
+
return sandboxInstance;
|
|
157
|
+
}
|
|
158
|
+
throw e;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/* eslint-disable */
|
|
162
|
+
static async fromSession(session) {
|
|
163
|
+
// Create a minimal sandbox configuration for session-based access
|
|
164
|
+
const sandboxName = session.name.includes("-") ? session.name.split("-")[0] : session.name;
|
|
165
|
+
const sandbox = {
|
|
166
|
+
metadata: { name: sandboxName },
|
|
167
|
+
forceUrl: session.url,
|
|
168
|
+
headers: { "X-Blaxel-Preview-Token": session.token },
|
|
169
|
+
params: { bl_preview_token: session.token }
|
|
170
|
+
};
|
|
171
|
+
// Create instance using constructor instead of direct property assignment
|
|
172
|
+
return new SandboxInstance(sandbox);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createSandboxPreview, deleteSandboxPreview, getSandboxPreview, listSandboxPreviews, listSandboxPreviewTokens } from "../client/index.js";
|
|
2
|
+
import { SandboxPreview } from "./preview.js";
|
|
3
|
+
export class SandboxSessions {
|
|
4
|
+
sandbox;
|
|
5
|
+
constructor(sandbox) {
|
|
6
|
+
this.sandbox = sandbox;
|
|
7
|
+
}
|
|
8
|
+
get sandboxName() {
|
|
9
|
+
return this.sandbox.metadata?.name ?? "";
|
|
10
|
+
}
|
|
11
|
+
async create(options = {}) {
|
|
12
|
+
const expiresAt = options.expiresAt ?? new Date(Date.now() + 24 * 60 * 60 * 1000); // 1 day from now
|
|
13
|
+
const body = {
|
|
14
|
+
metadata: {
|
|
15
|
+
name: "session-" + Date.now(),
|
|
16
|
+
},
|
|
17
|
+
spec: {
|
|
18
|
+
port: 443,
|
|
19
|
+
public: false,
|
|
20
|
+
expires: expiresAt.toISOString(),
|
|
21
|
+
requestHeaders: options.requestHeaders,
|
|
22
|
+
responseHeaders: options.responseHeaders,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
const { data } = await createSandboxPreview({
|
|
26
|
+
path: {
|
|
27
|
+
sandboxName: this.sandboxName,
|
|
28
|
+
},
|
|
29
|
+
body,
|
|
30
|
+
throwOnError: true,
|
|
31
|
+
});
|
|
32
|
+
const preview = new 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 createIfExpired(options = {}, delta = 1000 * 60 * 60) {
|
|
43
|
+
// First, list all sessions
|
|
44
|
+
const allSessions = await this.list();
|
|
45
|
+
// Variable to hold our final session
|
|
46
|
+
let sessionData;
|
|
47
|
+
const now = new Date();
|
|
48
|
+
const threshold = new Date(now.getTime() + delta);
|
|
49
|
+
// If no valid session exists, create a new one
|
|
50
|
+
if (allSessions.length > 0) {
|
|
51
|
+
sessionData = allSessions[0];
|
|
52
|
+
if (new Date(sessionData.expiresAt) < threshold) {
|
|
53
|
+
await this.delete(sessionData.name);
|
|
54
|
+
sessionData = await this.create(options);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Create a new session
|
|
59
|
+
sessionData = await this.create(options);
|
|
60
|
+
}
|
|
61
|
+
return sessionData;
|
|
62
|
+
}
|
|
63
|
+
async list() {
|
|
64
|
+
const { data } = await listSandboxPreviews({
|
|
65
|
+
path: {
|
|
66
|
+
sandboxName: this.sandboxName,
|
|
67
|
+
},
|
|
68
|
+
throwOnError: true,
|
|
69
|
+
});
|
|
70
|
+
if (data === null)
|
|
71
|
+
return [];
|
|
72
|
+
return await Promise.all(data.filter((preview) => preview.metadata?.name?.includes("session-")).map(async (preview) => {
|
|
73
|
+
const token = await this.getToken(preview.metadata?.name ?? "");
|
|
74
|
+
return {
|
|
75
|
+
name: preview.metadata?.name ?? "",
|
|
76
|
+
url: preview.spec?.url ?? "",
|
|
77
|
+
token: token?.spec?.token ?? "",
|
|
78
|
+
expiresAt: token?.spec?.expiresAt ?? new Date(),
|
|
79
|
+
};
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
async get(name) {
|
|
83
|
+
const { data } = await getSandboxPreview({
|
|
84
|
+
path: {
|
|
85
|
+
sandboxName: this.sandboxName,
|
|
86
|
+
previewName: name,
|
|
87
|
+
},
|
|
88
|
+
throwOnError: true,
|
|
89
|
+
});
|
|
90
|
+
const token = await this.getToken(name);
|
|
91
|
+
return {
|
|
92
|
+
url: data.spec?.url ?? "",
|
|
93
|
+
token: token?.spec?.token ?? "",
|
|
94
|
+
expiresAt: token?.spec?.expiresAt ?? new Date(),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async delete(name) {
|
|
98
|
+
const { data } = await deleteSandboxPreview({
|
|
99
|
+
path: {
|
|
100
|
+
sandboxName: this.sandboxName,
|
|
101
|
+
previewName: name,
|
|
102
|
+
},
|
|
103
|
+
throwOnError: true,
|
|
104
|
+
});
|
|
105
|
+
return data;
|
|
106
|
+
}
|
|
107
|
+
async getToken(previewName) {
|
|
108
|
+
const { data } = await listSandboxPreviewTokens({
|
|
109
|
+
path: {
|
|
110
|
+
sandboxName: this.sandboxName,
|
|
111
|
+
previewName,
|
|
112
|
+
},
|
|
113
|
+
throwOnError: true,
|
|
114
|
+
});
|
|
115
|
+
if (data.length === 0)
|
|
116
|
+
return null;
|
|
117
|
+
return data[0];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export function normalizePorts(ports) {
|
|
2
|
+
if (!ports || ports.length === 0) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
const portObjects = [];
|
|
6
|
+
for (const port of ports) {
|
|
7
|
+
if (typeof port === 'object' && port !== null) {
|
|
8
|
+
if ('name' in port || 'target' in port || 'protocol' in port) {
|
|
9
|
+
// It's a Port-like object, ensure protocol defaults to HTTP
|
|
10
|
+
const normalizedPort = {
|
|
11
|
+
name: typeof port.name === 'string' ? port.name : undefined,
|
|
12
|
+
target: typeof port.target === 'number' ? port.target : undefined,
|
|
13
|
+
protocol: typeof port.protocol === 'string' ? port.protocol : "HTTP"
|
|
14
|
+
};
|
|
15
|
+
portObjects.push(normalizedPort);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
throw new Error(`Invalid port type: ${typeof port}. Expected Port object or object with port properties.`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
throw new Error(`Invalid port type: ${typeof port}. Expected Port object or object with port properties.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return portObjects;
|
|
26
|
+
}
|
|
27
|
+
export function normalizeEnvs(envs) {
|
|
28
|
+
if (!envs || envs.length === 0) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
const envObjects = [];
|
|
32
|
+
for (const env of envs) {
|
|
33
|
+
if (typeof env === 'object' && env !== null) {
|
|
34
|
+
// Validate that the object has the required keys
|
|
35
|
+
if (!('name' in env) || !('value' in env)) {
|
|
36
|
+
throw new Error(`Environment variable object must have 'name' and 'value' keys: ${JSON.stringify(env)}`);
|
|
37
|
+
}
|
|
38
|
+
if (typeof env.name !== 'string' || typeof env.value !== 'string') {
|
|
39
|
+
throw new Error(`Environment variable 'name' and 'value' must be strings: ${JSON.stringify(env)}`);
|
|
40
|
+
}
|
|
41
|
+
envObjects.push({ name: env.name, value: env.value });
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
throw new Error(`Invalid env type: ${typeof env}. Expected object with 'name' and 'value' keys.`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return envObjects;
|
|
48
|
+
}
|
|
49
|
+
export function normalizeVolumes(volumes) {
|
|
50
|
+
if (!volumes || volumes.length === 0) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
const volumeObjects = [];
|
|
54
|
+
for (const volume of volumes) {
|
|
55
|
+
if (typeof volume === 'object' && volume !== null) {
|
|
56
|
+
// Validate that the object has the required keys
|
|
57
|
+
if (!('name' in volume) || !('mountPath' in volume)) {
|
|
58
|
+
throw new Error(`Volume binding object must have 'name' and 'mountPath' keys: ${JSON.stringify(volume)}`);
|
|
59
|
+
}
|
|
60
|
+
if (typeof volume.name !== 'string' || typeof volume.mountPath !== 'string') {
|
|
61
|
+
throw new Error(`Volume binding 'name' and 'mountPath' must be strings: ${JSON.stringify(volume)}`);
|
|
62
|
+
}
|
|
63
|
+
// Convert VolumeBinding to VolumeAttachment format
|
|
64
|
+
const volumeAttachment = {
|
|
65
|
+
name: volume.name,
|
|
66
|
+
mountPath: volume.mountPath,
|
|
67
|
+
readOnly: 'readOnly' in volume ? volume.readOnly : false
|
|
68
|
+
};
|
|
69
|
+
volumeObjects.push(volumeAttachment);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new Error(`Invalid volume type: ${typeof volume}. Expected object with 'name' and 'mountPath' keys.`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return volumeObjects;
|
|
76
|
+
}
|