@mastra/platform-workspace 0.2.1 → 0.2.2-alpha.0
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/CHANGELOG.md +12 -0
- package/dist/index.cjs +663 -600
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +634 -589
- package/dist/index.js.map +1 -1
- package/dist/sandbox.d.ts.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -1,643 +1,706 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let buffer = require("buffer");
|
|
25
|
+
let path = require("path");
|
|
26
|
+
path = __toESM(path, 1);
|
|
27
|
+
let _mastra_core_workspace = require("@mastra/core/workspace");
|
|
28
|
+
//#region src/client.ts
|
|
29
|
+
const DEFAULT_PROXY_URL = "https://workspaces.mastra.ai";
|
|
30
|
+
/**
|
|
31
|
+
* Default per-request timeout for calls to the workspace proxy. Applied only
|
|
32
|
+
* when the caller doesn't already pass an `AbortSignal`. Long-running routes
|
|
33
|
+
* (e.g. `POST /sandbox/:id/exec`) pass their own longer signal.
|
|
34
|
+
*/
|
|
35
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
14
36
|
function requireOption(value, name) {
|
|
15
|
-
|
|
16
|
-
|
|
37
|
+
if (!value) throw new Error(`${name} is required`);
|
|
38
|
+
return value;
|
|
17
39
|
}
|
|
18
40
|
function resolvePlatformOptions(options) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
projectId: requireOption(options.projectId ?? process.env.MASTRA_PROJECT_ID, "projectId"),
|
|
26
|
-
proxyUrl: (process.env.MASTRA_WORKSPACE_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\/$/, ""),
|
|
27
|
-
fetch: options.fetch ?? fetch
|
|
28
|
-
};
|
|
41
|
+
return {
|
|
42
|
+
accessToken: requireOption(options.accessToken ?? process.env.MASTRA_PLATFORM_SECRET_KEY ?? process.env.MASTRA_PLATFORM_ACCESS_TOKEN, "accessToken"),
|
|
43
|
+
projectId: requireOption(options.projectId ?? process.env.MASTRA_PROJECT_ID, "projectId"),
|
|
44
|
+
proxyUrl: (process.env.MASTRA_WORKSPACE_PROXY_URL ?? DEFAULT_PROXY_URL).replace(/\/$/, ""),
|
|
45
|
+
fetch: options.fetch ?? fetch
|
|
46
|
+
};
|
|
29
47
|
}
|
|
30
48
|
function parseProxyError(body) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
if (!body) return void 0;
|
|
50
|
+
let parsed;
|
|
51
|
+
try {
|
|
52
|
+
parsed = JSON.parse(body);
|
|
53
|
+
} catch {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
57
|
+
const err = parsed.error;
|
|
58
|
+
if (typeof err !== "object" || err === null) return void 0;
|
|
59
|
+
const { message, type } = err;
|
|
60
|
+
if (typeof message !== "string" || typeof type !== "string") return void 0;
|
|
61
|
+
return {
|
|
62
|
+
message,
|
|
63
|
+
type
|
|
64
|
+
};
|
|
44
65
|
}
|
|
45
66
|
var PlatformApiError = class extends Error {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
status;
|
|
68
|
+
body;
|
|
69
|
+
/** Machine-readable proxy error kind (e.g. `not_found`), when the response body matches `{ error: { message, type } }`. */
|
|
70
|
+
code;
|
|
71
|
+
/** Human-readable proxy error message, when the response body matches `{ error: { message, type } }`. */
|
|
72
|
+
proxyMessage;
|
|
73
|
+
constructor(status, body) {
|
|
74
|
+
const parsed = parseProxyError(body);
|
|
75
|
+
const summary = parsed ? `${parsed.type}: ${parsed.message}` : body;
|
|
76
|
+
super(`Platform proxy request failed with ${status}${summary ? `: ${summary}` : ""}`);
|
|
77
|
+
this.name = "PlatformApiError";
|
|
78
|
+
this.status = status;
|
|
79
|
+
this.body = body;
|
|
80
|
+
this.code = parsed?.type;
|
|
81
|
+
this.proxyMessage = parsed?.message;
|
|
82
|
+
}
|
|
62
83
|
};
|
|
63
84
|
var PlatformClient = class {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
accessToken;
|
|
86
|
+
projectId;
|
|
87
|
+
proxyUrl;
|
|
88
|
+
fetch;
|
|
89
|
+
constructor(options) {
|
|
90
|
+
const resolved = resolvePlatformOptions(options);
|
|
91
|
+
this.accessToken = resolved.accessToken;
|
|
92
|
+
this.projectId = resolved.projectId;
|
|
93
|
+
this.proxyUrl = resolved.proxyUrl;
|
|
94
|
+
this.fetch = resolved.fetch;
|
|
95
|
+
}
|
|
96
|
+
async request(path, options = {}) {
|
|
97
|
+
const url = new URL(`${this.proxyUrl}/v1/projects/${encodeURIComponent(this.projectId)}${path}`);
|
|
98
|
+
for (const [key, value] of Object.entries(options.query ?? {})) if (value !== void 0) url.searchParams.set(key, String(value));
|
|
99
|
+
const headers = new Headers(options.headers);
|
|
100
|
+
headers.set("authorization", `Bearer ${this.accessToken}`);
|
|
101
|
+
const { query: _query, ...fetchOptions } = options;
|
|
102
|
+
const signal = fetchOptions.signal ?? AbortSignal.timeout(DEFAULT_REQUEST_TIMEOUT_MS);
|
|
103
|
+
const response = await this.fetch(url, {
|
|
104
|
+
...fetchOptions,
|
|
105
|
+
headers,
|
|
106
|
+
signal
|
|
107
|
+
});
|
|
108
|
+
if (!response.ok) throw new PlatformApiError(response.status, await response.text());
|
|
109
|
+
return response;
|
|
110
|
+
}
|
|
90
111
|
};
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region src/filesystem.ts
|
|
91
114
|
function normalizePath(input) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
115
|
+
if (!input || input === ".") return "/";
|
|
116
|
+
let normalized = input.startsWith("/") ? input : `/${input}`;
|
|
117
|
+
normalized = path.default.posix.normalize(normalized);
|
|
118
|
+
return normalized === "." ? "/" : normalized;
|
|
96
119
|
}
|
|
97
|
-
function keyFromPath(path) {
|
|
98
|
-
|
|
99
|
-
|
|
120
|
+
function keyFromPath(path$1) {
|
|
121
|
+
const normalized = normalizePath(path$1);
|
|
122
|
+
return normalized === "/" ? "" : normalized.slice(1);
|
|
100
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Encode each `/`-delimited segment of an object key with `encodeURIComponent`
|
|
126
|
+
* so reserved URL characters (`?`, `#`, `%`, `&`, `+`, spaces, etc.) are
|
|
127
|
+
* treated as part of the key instead of URL syntax. Kept segment-aware so
|
|
128
|
+
* `/` continues to act as a path separator on the wire.
|
|
129
|
+
*/
|
|
101
130
|
function encodeKeyPath(key) {
|
|
102
|
-
|
|
131
|
+
return key.split("/").map(encodeURIComponent).join("/");
|
|
103
132
|
}
|
|
104
|
-
function nameFromPath(path) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
133
|
+
function nameFromPath(path$2) {
|
|
134
|
+
const normalized = normalizePath(path$2);
|
|
135
|
+
if (normalized === "/") return "";
|
|
136
|
+
return normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
108
137
|
}
|
|
109
138
|
function contentToBody(content) {
|
|
110
|
-
|
|
111
|
-
|
|
139
|
+
if (typeof content === "string") return content;
|
|
140
|
+
return buffer.Buffer.from(content);
|
|
112
141
|
}
|
|
113
142
|
function headerDate(headers, name) {
|
|
114
|
-
|
|
115
|
-
|
|
143
|
+
const value = headers.get(name);
|
|
144
|
+
return value ? new Date(value) : /* @__PURE__ */ new Date(0);
|
|
116
145
|
}
|
|
117
146
|
function headerSize(headers) {
|
|
118
|
-
|
|
119
|
-
|
|
147
|
+
const value = headers.get("content-length");
|
|
148
|
+
return value ? Number(value) : 0;
|
|
120
149
|
}
|
|
121
150
|
function isNotFound(error) {
|
|
122
|
-
|
|
151
|
+
return typeof error === "object" && error !== null && "status" in error && error.status === 404;
|
|
123
152
|
}
|
|
124
|
-
var PlatformFilesystem = class extends
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
getInfo() {
|
|
328
|
-
return {
|
|
329
|
-
id: this.id,
|
|
330
|
-
name: this.name,
|
|
331
|
-
provider: this.provider,
|
|
332
|
-
status: this.status,
|
|
333
|
-
readOnly: this.readOnly,
|
|
334
|
-
icon: this.icon,
|
|
335
|
-
metadata: {
|
|
336
|
-
bucketName: this._bucketName,
|
|
337
|
-
...this.displayName && { displayName: this.displayName },
|
|
338
|
-
...this.description && { description: this.description }
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
}
|
|
153
|
+
var PlatformFilesystem = class extends _mastra_core_workspace.MastraFilesystem {
|
|
154
|
+
id;
|
|
155
|
+
name = "PlatformFilesystem";
|
|
156
|
+
provider = "platform";
|
|
157
|
+
readOnly;
|
|
158
|
+
displayName;
|
|
159
|
+
icon;
|
|
160
|
+
description;
|
|
161
|
+
status = "pending";
|
|
162
|
+
_client;
|
|
163
|
+
_bucketName;
|
|
164
|
+
_instructionsOverride;
|
|
165
|
+
constructor(options = {}) {
|
|
166
|
+
super({
|
|
167
|
+
...options,
|
|
168
|
+
name: "PlatformFilesystem"
|
|
169
|
+
});
|
|
170
|
+
this.id = options.id ?? this.generateId();
|
|
171
|
+
this._bucketName = options.bucketName ?? process.env.MASTRA_PLATFORM_BUCKET_NAME ?? "";
|
|
172
|
+
if (!this._bucketName) throw new Error("bucketName is required");
|
|
173
|
+
this.readOnly = options.readOnly;
|
|
174
|
+
this.displayName = options.displayName;
|
|
175
|
+
this.icon = options.icon ?? "cloud";
|
|
176
|
+
this.description = options.description;
|
|
177
|
+
this._instructionsOverride = options.instructions;
|
|
178
|
+
this._client = new PlatformClient(options);
|
|
179
|
+
}
|
|
180
|
+
generateId() {
|
|
181
|
+
return `platform-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
182
|
+
}
|
|
183
|
+
async readFile(path$3, options) {
|
|
184
|
+
await this.ensureReady();
|
|
185
|
+
let response;
|
|
186
|
+
try {
|
|
187
|
+
response = await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path$3))}`);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (isNotFound(error)) throw new _mastra_core_workspace.FileNotFoundError(path$3);
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
const buffer$1 = buffer.Buffer.from(await response.arrayBuffer());
|
|
193
|
+
return options?.encoding ? buffer$1.toString(options.encoding) : buffer$1;
|
|
194
|
+
}
|
|
195
|
+
async writeFile(path$4, content, options) {
|
|
196
|
+
await this.ensureReady();
|
|
197
|
+
if (this.readOnly) throw new _mastra_core_workspace.WorkspaceReadOnlyError("writeFile");
|
|
198
|
+
const headers = {};
|
|
199
|
+
if (options?.mimeType) headers["content-type"] = options.mimeType;
|
|
200
|
+
if (options?.overwrite === false) headers["if-none-match"] = "*";
|
|
201
|
+
try {
|
|
202
|
+
await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path$4))}`, {
|
|
203
|
+
method: "PUT",
|
|
204
|
+
headers,
|
|
205
|
+
body: contentToBody(content)
|
|
206
|
+
});
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (typeof error === "object" && error !== null && "status" in error && error.status === 412) throw new _mastra_core_workspace.FileExistsError(path$4);
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Append bytes to a file.
|
|
214
|
+
*
|
|
215
|
+
* **Not atomic.** Object storage behind the workspace proxy has no native
|
|
216
|
+
* append or compare-and-swap primitive, so this implementation is a
|
|
217
|
+
* read-modify-write: it reads the current contents, concatenates the new
|
|
218
|
+
* bytes, and PUTs the whole object back. Concurrent `appendFile` calls to
|
|
219
|
+
* the same path can overwrite each other's writes ("last write wins").
|
|
220
|
+
* Use `writeFile` with distinct keys for concurrent writers.
|
|
221
|
+
*/
|
|
222
|
+
async appendFile(path$5, content) {
|
|
223
|
+
const existing = await this.exists(path$5) ? await this.readFile(path$5) : buffer.Buffer.alloc(0);
|
|
224
|
+
await this.writeFile(path$5, buffer.Buffer.concat([buffer.Buffer.isBuffer(existing) ? existing : buffer.Buffer.from(existing), buffer.Buffer.from(content)]));
|
|
225
|
+
}
|
|
226
|
+
async deleteFile(path$6, options) {
|
|
227
|
+
await this.ensureReady();
|
|
228
|
+
if (this.readOnly) throw new _mastra_core_workspace.WorkspaceReadOnlyError("deleteFile");
|
|
229
|
+
try {
|
|
230
|
+
await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path$6))}`, {
|
|
231
|
+
method: "DELETE",
|
|
232
|
+
query: { recursive: options?.recursive }
|
|
233
|
+
});
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (isNotFound(error) && options?.force) return;
|
|
236
|
+
if (isNotFound(error)) throw new _mastra_core_workspace.FileNotFoundError(path$6);
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async copyFile(src, dest, options) {
|
|
241
|
+
await this.ensureReady();
|
|
242
|
+
if (this.readOnly) throw new _mastra_core_workspace.WorkspaceReadOnlyError("copyFile");
|
|
243
|
+
if (options?.overwrite === false) throw new Error("PlatformFilesystem.copyFile does not support overwrite: false — the proxy always overwrites.");
|
|
244
|
+
await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(src))}`, {
|
|
245
|
+
method: "POST",
|
|
246
|
+
query: { op: "copy" },
|
|
247
|
+
headers: { "content-type": "application/json" },
|
|
248
|
+
body: JSON.stringify({ destination: keyFromPath(dest) })
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
async moveFile(src, dest, options) {
|
|
252
|
+
await this.ensureReady();
|
|
253
|
+
if (this.readOnly) throw new _mastra_core_workspace.WorkspaceReadOnlyError("moveFile");
|
|
254
|
+
if (options?.overwrite === false) throw new Error("PlatformFilesystem.moveFile does not support overwrite: false — the proxy always overwrites.");
|
|
255
|
+
await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(src))}`, {
|
|
256
|
+
method: "POST",
|
|
257
|
+
query: { op: "rename" },
|
|
258
|
+
headers: { "content-type": "application/json" },
|
|
259
|
+
body: JSON.stringify({ destination: keyFromPath(dest) })
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
async mkdir(path$7, _options) {
|
|
263
|
+
await this.ensureReady();
|
|
264
|
+
if (this.readOnly) throw new _mastra_core_workspace.WorkspaceReadOnlyError("mkdir");
|
|
265
|
+
await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path$7))}`, {
|
|
266
|
+
method: "POST",
|
|
267
|
+
query: { op: "mkdir" }
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
async rmdir(path$8, options) {
|
|
271
|
+
await this.deleteFile(path$8.endsWith("/") ? path$8 : `${path$8}/`, {
|
|
272
|
+
recursive: true,
|
|
273
|
+
force: options?.force
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
async readdir(path$9, options) {
|
|
277
|
+
await this.ensureReady();
|
|
278
|
+
const prefix = keyFromPath(path$9);
|
|
279
|
+
const json = await (await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(prefix)}`, { query: {
|
|
280
|
+
delimiter: options?.recursive ? void 0 : "/",
|
|
281
|
+
prefix: prefix ? `${prefix.replace(/\/$/, "")}/` : void 0
|
|
282
|
+
} })).json();
|
|
283
|
+
return [...(json.commonPrefixes ?? []).map((prefix) => ({
|
|
284
|
+
name: nameFromPath(prefix.replace(/\/$/, "")),
|
|
285
|
+
type: "directory"
|
|
286
|
+
})), ...(json.contents ?? []).filter((object) => object.key && !object.key.endsWith("/")).map((object) => ({
|
|
287
|
+
name: nameFromPath(object.key),
|
|
288
|
+
type: "file",
|
|
289
|
+
size: object.size
|
|
290
|
+
}))].filter((entry) => !options?.extension || entry.type === "directory" || matchesExtension(entry.name, options.extension));
|
|
291
|
+
}
|
|
292
|
+
async exists(path$10) {
|
|
293
|
+
try {
|
|
294
|
+
await this.stat(path$10);
|
|
295
|
+
return true;
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (isNotFound(error) || error instanceof _mastra_core_workspace.FileNotFoundError) return false;
|
|
298
|
+
throw error;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async stat(path$11) {
|
|
302
|
+
await this.ensureReady();
|
|
303
|
+
const normalized = normalizePath(path$11);
|
|
304
|
+
if (normalized === "/") return {
|
|
305
|
+
name: "",
|
|
306
|
+
path: "/",
|
|
307
|
+
type: "directory",
|
|
308
|
+
size: 0,
|
|
309
|
+
createdAt: /* @__PURE__ */ new Date(0),
|
|
310
|
+
modifiedAt: /* @__PURE__ */ new Date(0)
|
|
311
|
+
};
|
|
312
|
+
let response;
|
|
313
|
+
try {
|
|
314
|
+
response = await this._client.request(`/fs/${encodeURIComponent(this._bucketName)}/${encodeKeyPath(keyFromPath(path$11))}`, { method: "HEAD" });
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (isNotFound(error)) throw new _mastra_core_workspace.FileNotFoundError(path$11);
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
name: nameFromPath(path$11),
|
|
321
|
+
path: normalized,
|
|
322
|
+
type: normalized.endsWith("/") ? "directory" : "file",
|
|
323
|
+
size: headerSize(response.headers),
|
|
324
|
+
createdAt: headerDate(response.headers, "last-modified"),
|
|
325
|
+
modifiedAt: headerDate(response.headers, "last-modified"),
|
|
326
|
+
mimeType: response.headers.get("content-type") ?? void 0
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
realpath(path$12) {
|
|
330
|
+
return Promise.resolve(normalizePath(path$12));
|
|
331
|
+
}
|
|
332
|
+
getInstructions(opts) {
|
|
333
|
+
const defaultInstructions = `Platform filesystem backed by Mastra Platform bucket ${this._bucketName}. Use absolute workspace paths.`;
|
|
334
|
+
if (typeof this._instructionsOverride === "function") return this._instructionsOverride({
|
|
335
|
+
defaultInstructions,
|
|
336
|
+
requestContext: opts?.requestContext
|
|
337
|
+
});
|
|
338
|
+
if (typeof this._instructionsOverride === "string") return this._instructionsOverride;
|
|
339
|
+
return defaultInstructions;
|
|
340
|
+
}
|
|
341
|
+
getInfo() {
|
|
342
|
+
return {
|
|
343
|
+
id: this.id,
|
|
344
|
+
name: this.name,
|
|
345
|
+
provider: this.provider,
|
|
346
|
+
status: this.status,
|
|
347
|
+
readOnly: this.readOnly,
|
|
348
|
+
icon: this.icon,
|
|
349
|
+
metadata: {
|
|
350
|
+
bucketName: this._bucketName,
|
|
351
|
+
...this.displayName && { displayName: this.displayName },
|
|
352
|
+
...this.description && { description: this.description }
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
342
356
|
};
|
|
343
357
|
function matchesExtension(name, extension) {
|
|
344
|
-
|
|
345
|
-
return extensions.some((ext) => name.endsWith(ext));
|
|
358
|
+
return (Array.isArray(extension) ? extension : [extension]).some((ext) => name.endsWith(ext));
|
|
346
359
|
}
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/sandbox.ts
|
|
362
|
+
/** Max attempts for `POST /sandbox` when the proxy returns transient 5xx errors. */
|
|
363
|
+
const CREATE_MAX_ATTEMPTS = 3;
|
|
364
|
+
/** Base delay between create retries; multiplied by the attempt number. */
|
|
365
|
+
const CREATE_RETRY_BASE_DELAY_MS = 2e3;
|
|
366
|
+
/**
|
|
367
|
+
* Compose a shell command line from a `command` string and optional `args`.
|
|
368
|
+
*
|
|
369
|
+
* IMPORTANT: `command` is treated as a **shell string** and passed to the
|
|
370
|
+
* remote shell verbatim so callers can use pipes, redirects, and chaining
|
|
371
|
+
* (`ls -la | grep foo`). This matches the contract of {@link MastraSandbox}
|
|
372
|
+
* and the local sandbox implementation. `args` are always shell-quoted so
|
|
373
|
+
* they cannot inject syntax.
|
|
374
|
+
*
|
|
375
|
+
* Callers MUST NOT pass untrusted input as `command`. Untrusted values must
|
|
376
|
+
* be passed via `args`, where they are safely quoted. Passing untrusted
|
|
377
|
+
* input as `command` allows arbitrary shell syntax execution on the remote
|
|
378
|
+
* sandbox.
|
|
379
|
+
*/
|
|
347
380
|
function buildCommand(command, args) {
|
|
348
|
-
|
|
381
|
+
return args?.length ? `${command} ${args.map(shellQuote).join(" ")}` : command;
|
|
349
382
|
}
|
|
350
383
|
function shellQuote(arg) {
|
|
351
|
-
|
|
352
|
-
|
|
384
|
+
if (/^[a-zA-Z0-9._\-/=:@]+$/.test(arg)) return arg;
|
|
385
|
+
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
353
386
|
}
|
|
354
|
-
var PlatformProcessHandle = class extends
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
387
|
+
var PlatformProcessHandle = class extends _mastra_core_workspace.ProcessHandle {
|
|
388
|
+
pid;
|
|
389
|
+
resultPromise;
|
|
390
|
+
exitCodeValue;
|
|
391
|
+
constructor(pid, resultPromise, options) {
|
|
392
|
+
super(options);
|
|
393
|
+
this.pid = pid;
|
|
394
|
+
this.resultPromise = resultPromise.then((result) => {
|
|
395
|
+
this.exitCodeValue = result.exitCode;
|
|
396
|
+
if (result.stdout) this.emitStdout(result.stdout);
|
|
397
|
+
if (result.stderr) this.emitStderr(result.stderr);
|
|
398
|
+
return result;
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
get exitCode() {
|
|
402
|
+
return this.exitCodeValue;
|
|
403
|
+
}
|
|
404
|
+
async wait() {
|
|
405
|
+
return this.resultPromise;
|
|
406
|
+
}
|
|
407
|
+
async kill() {
|
|
408
|
+
throw new Error("Platform sandbox command execution does not support killing individual processes");
|
|
409
|
+
}
|
|
410
|
+
async sendStdin() {
|
|
411
|
+
throw new Error("Platform sandbox command execution does not support stdin");
|
|
412
|
+
}
|
|
380
413
|
};
|
|
381
|
-
var PlatformProcessManager = class extends
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}));
|
|
404
|
-
}
|
|
414
|
+
var PlatformProcessManager = class extends _mastra_core_workspace.SandboxProcessManager {
|
|
415
|
+
spawnCounter = 0;
|
|
416
|
+
/**
|
|
417
|
+
* Spawn a process on the remote sandbox.
|
|
418
|
+
*
|
|
419
|
+
* `command` is interpreted as a shell string by the remote shell, matching
|
|
420
|
+
* the {@link MastraSandbox} contract. See {@link PlatformSandbox.executeCommand}
|
|
421
|
+
* for the untrusted-input caveat: never pass untrusted values as `command`.
|
|
422
|
+
*/
|
|
423
|
+
async spawn(command, options = {}) {
|
|
424
|
+
const handle = new PlatformProcessHandle(`platform-proc-${Date.now().toString(36)}-${(this.spawnCounter++).toString(36)}`, this.sandbox.executeCommand(command, void 0, options), options);
|
|
425
|
+
this._tracked.set(handle.pid, handle);
|
|
426
|
+
return handle;
|
|
427
|
+
}
|
|
428
|
+
async list() {
|
|
429
|
+
return Array.from(this._tracked.values()).map((handle) => ({
|
|
430
|
+
pid: handle.pid,
|
|
431
|
+
command: handle.command,
|
|
432
|
+
running: handle.exitCode === void 0,
|
|
433
|
+
...handle.exitCode !== void 0 && { exitCode: handle.exitCode }
|
|
434
|
+
}));
|
|
435
|
+
}
|
|
405
436
|
};
|
|
406
|
-
var PlatformSandbox = class
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
437
|
+
var PlatformSandbox = class PlatformSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
438
|
+
id;
|
|
439
|
+
name = "PlatformSandbox";
|
|
440
|
+
provider = "platform";
|
|
441
|
+
status = "pending";
|
|
442
|
+
_client;
|
|
443
|
+
_environmentId;
|
|
444
|
+
_sandboxId;
|
|
445
|
+
_idleTimeoutMinutes;
|
|
446
|
+
_networkIsolation;
|
|
447
|
+
_env;
|
|
448
|
+
_timeout;
|
|
449
|
+
_instructionsOverride;
|
|
450
|
+
_createdAt = null;
|
|
451
|
+
constructor(options = {}) {
|
|
452
|
+
super({
|
|
453
|
+
...options,
|
|
454
|
+
name: "PlatformSandbox",
|
|
455
|
+
processes: new PlatformProcessManager()
|
|
456
|
+
});
|
|
457
|
+
this.id = options.id ?? this.generateId();
|
|
458
|
+
this._client = new PlatformClient(options);
|
|
459
|
+
this._environmentId = options.environmentId ?? process.env.MASTRA_ENVIRONMENT_ID ?? "";
|
|
460
|
+
if (!this._environmentId && !options.sandboxId) throw new Error("environmentId is required");
|
|
461
|
+
this._sandboxId = options.sandboxId;
|
|
462
|
+
this._idleTimeoutMinutes = options.idleTimeoutMinutes;
|
|
463
|
+
this._networkIsolation = options.networkIsolation;
|
|
464
|
+
this._env = options.env ?? {};
|
|
465
|
+
this._timeout = options.timeout;
|
|
466
|
+
this._instructionsOverride = options.instructions;
|
|
467
|
+
}
|
|
468
|
+
generateId() {
|
|
469
|
+
return `platform-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Construct a sibling {@link PlatformSandbox} that inherits this sandbox's
|
|
473
|
+
* credentials and defaults (access token, project, environment, network
|
|
474
|
+
* isolation, timeout, instructions, env, idle timeout) with per-instance
|
|
475
|
+
* overrides from `options`.
|
|
476
|
+
*
|
|
477
|
+
* Performs no I/O and does not require this sandbox to be started — the
|
|
478
|
+
* returned sandbox is not started and provisions (or reattaches, when
|
|
479
|
+
* `sandboxId` is set) on its own `start()`. Use it when one configured
|
|
480
|
+
* sandbox acts as the template for a fleet of independent sandboxes
|
|
481
|
+
* (e.g. one per project).
|
|
482
|
+
*/
|
|
483
|
+
clone(options = {}) {
|
|
484
|
+
return new PlatformSandbox({
|
|
485
|
+
...options.id !== void 0 && { id: options.id },
|
|
486
|
+
accessToken: this._client.accessToken,
|
|
487
|
+
projectId: this._client.projectId,
|
|
488
|
+
fetch: this._client.fetch,
|
|
489
|
+
environmentId: this._environmentId,
|
|
490
|
+
...options.sandboxId !== void 0 && { sandboxId: options.sandboxId },
|
|
491
|
+
idleTimeoutMinutes: options.idleTimeoutMinutes ?? this._idleTimeoutMinutes,
|
|
492
|
+
...this._networkIsolation !== void 0 && { networkIsolation: this._networkIsolation },
|
|
493
|
+
env: options.env ?? this._env,
|
|
494
|
+
...this._timeout !== void 0 && { timeout: this._timeout },
|
|
495
|
+
...this._instructionsOverride !== void 0 && { instructions: this._instructionsOverride }
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
async start() {
|
|
499
|
+
if (this._sandboxId) try {
|
|
500
|
+
const json = await (await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`)).json();
|
|
501
|
+
if (!json.destroyedAt) {
|
|
502
|
+
this._createdAt = json.createdAt ? new Date(json.createdAt) : /* @__PURE__ */ new Date();
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
this._sandboxId = void 0;
|
|
506
|
+
} catch (error) {
|
|
507
|
+
if (!(error instanceof PlatformApiError) || error.status !== 404) throw error;
|
|
508
|
+
this._sandboxId = void 0;
|
|
509
|
+
}
|
|
510
|
+
if (!this._environmentId) throw new Error("environmentId is required");
|
|
511
|
+
const body = JSON.stringify({
|
|
512
|
+
id: this.id,
|
|
513
|
+
environmentId: this._environmentId,
|
|
514
|
+
idleTimeoutMinutes: this._idleTimeoutMinutes,
|
|
515
|
+
networkIsolation: this._networkIsolation,
|
|
516
|
+
env: this._env
|
|
517
|
+
});
|
|
518
|
+
let response;
|
|
519
|
+
for (let attempt = 1;; attempt++) try {
|
|
520
|
+
response = await this._client.request("/sandbox", {
|
|
521
|
+
method: "POST",
|
|
522
|
+
headers: { "content-type": "application/json" },
|
|
523
|
+
body
|
|
524
|
+
});
|
|
525
|
+
break;
|
|
526
|
+
} catch (error) {
|
|
527
|
+
if (!(error instanceof PlatformApiError && error.status >= 500) || attempt >= CREATE_MAX_ATTEMPTS) throw error;
|
|
528
|
+
await new Promise((resolve) => setTimeout(resolve, CREATE_RETRY_BASE_DELAY_MS * attempt));
|
|
529
|
+
}
|
|
530
|
+
const json = await response.json();
|
|
531
|
+
this._sandboxId = json.id;
|
|
532
|
+
this._createdAt = json.createdAt ? new Date(json.createdAt) : /* @__PURE__ */ new Date();
|
|
533
|
+
}
|
|
534
|
+
async stop() {
|
|
535
|
+
await this.destroy();
|
|
536
|
+
}
|
|
537
|
+
async destroy() {
|
|
538
|
+
if (!this._sandboxId) return;
|
|
539
|
+
await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`, { method: "DELETE" });
|
|
540
|
+
this._sandboxId = void 0;
|
|
541
|
+
this._createdAt = null;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Execute a command on the remote sandbox.
|
|
545
|
+
*
|
|
546
|
+
* `command` is a **shell string**: it is concatenated verbatim into the
|
|
547
|
+
* command line sent to the remote shell, which lets callers use pipes,
|
|
548
|
+
* redirects, and chaining (`ls -la | grep foo`). This matches the contract
|
|
549
|
+
* of {@link MastraSandbox} and the local sandbox implementation.
|
|
550
|
+
*
|
|
551
|
+
* `args`, when provided, are always shell-quoted so they cannot inject
|
|
552
|
+
* additional shell syntax.
|
|
553
|
+
*
|
|
554
|
+
* Security: callers MUST NOT pass untrusted input as `command`. If any part
|
|
555
|
+
* of the invocation is derived from an untrusted source, pass it through
|
|
556
|
+
* `args` (which is safely quoted) or shell-quote it yourself before
|
|
557
|
+
* inclusion. Untrusted `command` values allow arbitrary shell syntax
|
|
558
|
+
* execution on the remote sandbox.
|
|
559
|
+
*/
|
|
560
|
+
async executeCommand(command, args, options) {
|
|
561
|
+
await this.ensureRunning();
|
|
562
|
+
if (!this._sandboxId) throw new _mastra_core_workspace.SandboxNotReadyError(this.id);
|
|
563
|
+
const started = Date.now();
|
|
564
|
+
const fullCommand = buildCommand(command, args);
|
|
565
|
+
const effectiveTimeout = options?.timeout ?? this._timeout;
|
|
566
|
+
const timeoutSec = effectiveTimeout != null ? Math.ceil(effectiveTimeout / 1e3) : void 0;
|
|
567
|
+
const clientSignal = effectiveTimeout != null && effectiveTimeout > 0 ? AbortSignal.timeout(effectiveTimeout + 3e4) : void 0;
|
|
568
|
+
const json = await (await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}/exec`, {
|
|
569
|
+
method: "POST",
|
|
570
|
+
headers: { "content-type": "application/json" },
|
|
571
|
+
body: JSON.stringify({
|
|
572
|
+
command: fullCommand,
|
|
573
|
+
timeoutSec,
|
|
574
|
+
cwd: options?.cwd,
|
|
575
|
+
env: options?.env
|
|
576
|
+
}),
|
|
577
|
+
signal: clientSignal
|
|
578
|
+
})).json();
|
|
579
|
+
const exitCode = json.exitCode ?? (json.timedOut ? 124 : 1);
|
|
580
|
+
return {
|
|
581
|
+
success: exitCode === 0,
|
|
582
|
+
exitCode,
|
|
583
|
+
stdout: json.stdout,
|
|
584
|
+
stderr: json.stderr,
|
|
585
|
+
executionTimeMs: Date.now() - started,
|
|
586
|
+
timedOut: json.timedOut,
|
|
587
|
+
command: fullCommand
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
async getInfo() {
|
|
591
|
+
if (!this._sandboxId) return {
|
|
592
|
+
id: this.id,
|
|
593
|
+
name: this.name,
|
|
594
|
+
provider: this.provider,
|
|
595
|
+
status: this.status,
|
|
596
|
+
createdAt: this._createdAt ?? /* @__PURE__ */ new Date()
|
|
597
|
+
};
|
|
598
|
+
const json = await (await this._client.request(`/sandbox/${encodeURIComponent(this._sandboxId)}`)).json();
|
|
599
|
+
return {
|
|
600
|
+
id: json.id,
|
|
601
|
+
name: this.name,
|
|
602
|
+
provider: this.provider,
|
|
603
|
+
status: this.status,
|
|
604
|
+
createdAt: json.createdAt ? new Date(json.createdAt) : this._createdAt ?? /* @__PURE__ */ new Date(),
|
|
605
|
+
metadata: {
|
|
606
|
+
sandboxId: json.id,
|
|
607
|
+
providerResourceId: json.providerResourceId ?? void 0,
|
|
608
|
+
platformStatus: json.status
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
getInstructions(opts) {
|
|
613
|
+
const defaultInstructions = `Platform sandbox${this._sandboxId ? ` ${this._sandboxId}` : ""}. Execute commands with the sandbox command APIs.`;
|
|
614
|
+
if (typeof this._instructionsOverride === "function") return this._instructionsOverride({
|
|
615
|
+
defaultInstructions,
|
|
616
|
+
requestContext: opts?.requestContext
|
|
617
|
+
});
|
|
618
|
+
if (typeof this._instructionsOverride === "string") return this._instructionsOverride;
|
|
619
|
+
return defaultInstructions;
|
|
620
|
+
}
|
|
584
621
|
};
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
622
|
+
//#endregion
|
|
623
|
+
//#region src/provider.ts
|
|
624
|
+
const platformSandboxProvider = {
|
|
625
|
+
id: "platform",
|
|
626
|
+
name: "Mastra Platform Sandbox",
|
|
627
|
+
description: "Environment-scoped sandbox execution through Mastra Platform workspace proxy",
|
|
628
|
+
configSchema: {
|
|
629
|
+
type: "object",
|
|
630
|
+
properties: {
|
|
631
|
+
accessToken: {
|
|
632
|
+
type: "string",
|
|
633
|
+
description: "Mastra Platform secret key (falls back to MASTRA_PLATFORM_SECRET_KEY)"
|
|
634
|
+
},
|
|
635
|
+
projectId: {
|
|
636
|
+
type: "string",
|
|
637
|
+
description: "Platform project ID (falls back to MASTRA_PROJECT_ID)"
|
|
638
|
+
},
|
|
639
|
+
environmentId: {
|
|
640
|
+
type: "string",
|
|
641
|
+
description: "Platform environment ID (falls back to MASTRA_ENVIRONMENT_ID)"
|
|
642
|
+
},
|
|
643
|
+
sandboxId: {
|
|
644
|
+
type: "string",
|
|
645
|
+
description: "Reattach to an existing Platform sandbox by ID"
|
|
646
|
+
},
|
|
647
|
+
idleTimeoutMinutes: {
|
|
648
|
+
type: "number",
|
|
649
|
+
description: "Minutes before the sandbox can be destroyed while idle"
|
|
650
|
+
},
|
|
651
|
+
networkIsolation: {
|
|
652
|
+
type: "string",
|
|
653
|
+
description: "Network isolation mode",
|
|
654
|
+
enum: ["ISOLATED", "PRIVATE"],
|
|
655
|
+
default: "ISOLATED"
|
|
656
|
+
},
|
|
657
|
+
env: {
|
|
658
|
+
type: "object",
|
|
659
|
+
description: "Environment variables",
|
|
660
|
+
additionalProperties: { type: "string" }
|
|
661
|
+
},
|
|
662
|
+
timeout: {
|
|
663
|
+
type: "number",
|
|
664
|
+
description: "Default command timeout in ms"
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
createSandbox: (config) => new PlatformSandbox(config)
|
|
613
669
|
};
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
670
|
+
const platformFilesystemProvider = {
|
|
671
|
+
id: "platform",
|
|
672
|
+
name: "Mastra Platform Filesystem",
|
|
673
|
+
description: "Bucket-backed filesystem access through Mastra Platform workspace proxy",
|
|
674
|
+
configSchema: {
|
|
675
|
+
type: "object",
|
|
676
|
+
properties: {
|
|
677
|
+
accessToken: {
|
|
678
|
+
type: "string",
|
|
679
|
+
description: "Mastra Platform secret key (falls back to MASTRA_PLATFORM_SECRET_KEY)"
|
|
680
|
+
},
|
|
681
|
+
projectId: {
|
|
682
|
+
type: "string",
|
|
683
|
+
description: "Platform project ID (falls back to MASTRA_PROJECT_ID)"
|
|
684
|
+
},
|
|
685
|
+
bucketName: {
|
|
686
|
+
type: "string",
|
|
687
|
+
description: "Platform workspace bucket name (falls back to MASTRA_PLATFORM_BUCKET_NAME)"
|
|
688
|
+
},
|
|
689
|
+
readOnly: {
|
|
690
|
+
type: "boolean",
|
|
691
|
+
description: "Mount as read-only",
|
|
692
|
+
default: false
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
},
|
|
696
|
+
createFilesystem: (config) => new PlatformFilesystem(config)
|
|
634
697
|
};
|
|
635
|
-
|
|
698
|
+
//#endregion
|
|
636
699
|
exports.PlatformApiError = PlatformApiError;
|
|
637
700
|
exports.PlatformClient = PlatformClient;
|
|
638
701
|
exports.PlatformFilesystem = PlatformFilesystem;
|
|
639
702
|
exports.PlatformSandbox = PlatformSandbox;
|
|
640
703
|
exports.platformFilesystemProvider = platformFilesystemProvider;
|
|
641
704
|
exports.platformSandboxProvider = platformSandboxProvider;
|
|
642
|
-
|
|
705
|
+
|
|
643
706
|
//# sourceMappingURL=index.cjs.map
|