@mastra/e2b-desktop 0.0.0 → 0.1.0-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/LICENSE.md +30 -0
- package/dist/index.cjs +285 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +283 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +32 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/sandbox/index.d.ts +129 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/package.json +23 -23
package/LICENSE.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Portions of this software are licensed as follows:
|
|
2
|
+
|
|
3
|
+
- All content that resides under any directory named "ee/" within this
|
|
4
|
+
repository, including but not limited to:
|
|
5
|
+
- `packages/core/src/auth/ee/`
|
|
6
|
+
- `packages/server/src/server/auth/ee/`
|
|
7
|
+
is licensed under the license defined in `ee/LICENSE`.
|
|
8
|
+
|
|
9
|
+
- All third-party components incorporated into the Mastra Software are
|
|
10
|
+
licensed under the original license provided by the owner of the
|
|
11
|
+
applicable component.
|
|
12
|
+
|
|
13
|
+
- Content outside of the above-mentioned directories or restrictions is
|
|
14
|
+
available under the "Apache License 2.0" as defined below.
|
|
15
|
+
|
|
16
|
+
# Apache License 2.0
|
|
17
|
+
|
|
18
|
+
Copyright (c) 2025 Kepler Software, Inc.
|
|
19
|
+
|
|
20
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
21
|
+
you may not use this file except in compliance with the License.
|
|
22
|
+
You may obtain a copy of the License at
|
|
23
|
+
|
|
24
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
25
|
+
|
|
26
|
+
Unless required by applicable law or agreed to in writing, software
|
|
27
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
28
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
29
|
+
See the License for the specific language governing permissions and
|
|
30
|
+
limitations under the License.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _e2b_desktop = require("@e2b/desktop");
|
|
3
|
+
let _mastra_core_workspace = require("@mastra/core/workspace");
|
|
4
|
+
let _mastra_e2b = require("@mastra/e2b");
|
|
5
|
+
//#region src/sandbox/index.ts
|
|
6
|
+
/**
|
|
7
|
+
* E2B Desktop Sandbox Provider
|
|
8
|
+
*
|
|
9
|
+
* An E2B sandbox running a full Linux desktop environment with computer-use
|
|
10
|
+
* (screenshot, mouse, keyboard) control. Extends `@mastra/e2b`'s `E2BSandbox`
|
|
11
|
+
* the same way `@e2b/desktop`'s SDK `Sandbox` extends `e2b`'s: everything the
|
|
12
|
+
* base provider supports (command execution, processes, file upload,
|
|
13
|
+
* reconnection) works against the same desktop VM.
|
|
14
|
+
*
|
|
15
|
+
* @see https://github.com/e2b-dev/desktop
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* E2B-hosted desktop template (`@e2b/desktop`'s default). Used when no
|
|
19
|
+
* explicit `template` option is provided — unlike the base provider, no
|
|
20
|
+
* template build is required.
|
|
21
|
+
*/
|
|
22
|
+
const DEFAULT_DESKTOP_TEMPLATE = "desktop";
|
|
23
|
+
/**
|
|
24
|
+
* E2B Desktop sandbox provider for Mastra workspaces.
|
|
25
|
+
*
|
|
26
|
+
* Features:
|
|
27
|
+
* - Everything from `E2BSandbox` (commands, processes, file upload, pause/resume)
|
|
28
|
+
* - `computer` capability: screenshot, mouse, and keyboard control — when
|
|
29
|
+
* used in a `Workspace`, agents automatically get the
|
|
30
|
+
* `mastra_workspace_computer_*` tools
|
|
31
|
+
* - Live desktop view via an authenticated noVNC stream URL
|
|
32
|
+
*
|
|
33
|
+
* @example Basic usage
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import { Agent } from '@mastra/core/agent';
|
|
36
|
+
* import { Workspace } from '@mastra/core/workspace';
|
|
37
|
+
* import { E2BDesktopSandbox } from '@mastra/e2b-desktop';
|
|
38
|
+
*
|
|
39
|
+
* const sandbox = new E2BDesktopSandbox({ resolution: [1280, 720] });
|
|
40
|
+
* const agent = new Agent({
|
|
41
|
+
* // file + shell + computer tools are all emitted automatically
|
|
42
|
+
* workspace: new Workspace({ sandbox }),
|
|
43
|
+
* // ...
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @example Direct desktop control
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const sandbox = new E2BDesktopSandbox();
|
|
50
|
+
* await sandbox.start();
|
|
51
|
+
* await sandbox.computer.leftClick(100, 200);
|
|
52
|
+
* const { data } = await sandbox.computer.screenshot();
|
|
53
|
+
* const viewerUrl = await sandbox.computer.streamUrl();
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
var E2BDesktopSandbox = class E2BDesktopSandbox extends _mastra_e2b.E2BSandbox {
|
|
57
|
+
name = "E2BDesktopSandbox";
|
|
58
|
+
provider = "e2b-desktop";
|
|
59
|
+
resolution;
|
|
60
|
+
dpi;
|
|
61
|
+
_desktopConstructorOptions;
|
|
62
|
+
/** Stream startup memo, keyed by SDK sandbox ID so a fresh VM restarts it. */
|
|
63
|
+
_streamStarted = null;
|
|
64
|
+
constructor(options = {}) {
|
|
65
|
+
super(options);
|
|
66
|
+
this.resolution = options.resolution;
|
|
67
|
+
this.dpi = options.dpi;
|
|
68
|
+
this._desktopConstructorOptions = { ...options };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Computer-use (desktop) capability: screenshot, mouse, and keyboard
|
|
72
|
+
* control of the sandbox's desktop, backed by `@e2b/desktop`.
|
|
73
|
+
* Operations start the sandbox automatically if it is not running.
|
|
74
|
+
*/
|
|
75
|
+
computer = {
|
|
76
|
+
screenshot: async () => {
|
|
77
|
+
return {
|
|
78
|
+
data: await this.withDesktop((desktop) => desktop.screenshot()),
|
|
79
|
+
mediaType: "image/png"
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
leftClick: async (x, y) => {
|
|
83
|
+
await this.withDesktop((desktop) => desktop.leftClick(x, y));
|
|
84
|
+
},
|
|
85
|
+
rightClick: async (x, y) => {
|
|
86
|
+
await this.withDesktop((desktop) => desktop.rightClick(x, y));
|
|
87
|
+
},
|
|
88
|
+
doubleClick: async (x, y) => {
|
|
89
|
+
await this.withDesktop((desktop) => desktop.doubleClick(x, y));
|
|
90
|
+
},
|
|
91
|
+
moveMouse: async (x, y) => {
|
|
92
|
+
await this.withDesktop((desktop) => desktop.moveMouse(x, y));
|
|
93
|
+
},
|
|
94
|
+
drag: async (from, to) => {
|
|
95
|
+
await this.withDesktop((desktop) => desktop.drag([from.x, from.y], [to.x, to.y]));
|
|
96
|
+
},
|
|
97
|
+
scroll: async (direction, amount) => {
|
|
98
|
+
await this.withDesktop((desktop) => desktop.scroll(direction, amount));
|
|
99
|
+
},
|
|
100
|
+
type: async (text) => {
|
|
101
|
+
await this.withDesktop((desktop) => desktop.write(text));
|
|
102
|
+
},
|
|
103
|
+
press: async (key) => {
|
|
104
|
+
await this.withDesktop((desktop) => desktop.press(key));
|
|
105
|
+
},
|
|
106
|
+
getScreenSize: async () => {
|
|
107
|
+
return this.withDesktop((desktop) => desktop.getScreenSize());
|
|
108
|
+
},
|
|
109
|
+
getCursorPosition: async () => {
|
|
110
|
+
return this.withDesktop((desktop) => desktop.getCursorPosition());
|
|
111
|
+
},
|
|
112
|
+
streamUrl: async () => {
|
|
113
|
+
try {
|
|
114
|
+
return await this.withDesktop(async (desktop) => {
|
|
115
|
+
await this.ensureStreamStarted(desktop);
|
|
116
|
+
try {
|
|
117
|
+
return desktop.stream.getUrl({ authKey: desktop.stream.getAuthKey() });
|
|
118
|
+
} catch {
|
|
119
|
+
return desktop.stream.getUrl();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Get the underlying `@e2b/desktop` Sandbox instance for direct access to
|
|
129
|
+
* desktop APIs not exposed through the abstraction (e.g. `launch()`,
|
|
130
|
+
* `open()`, window helpers, custom stream control).
|
|
131
|
+
*
|
|
132
|
+
* @throws {SandboxNotReadyError} If the sandbox has not been started
|
|
133
|
+
*
|
|
134
|
+
* @example Launch an application
|
|
135
|
+
* ```typescript
|
|
136
|
+
* await sandbox.start();
|
|
137
|
+
* await sandbox.desktop.launch('xfce4-terminal');
|
|
138
|
+
* await sandbox.desktop.open('https://mastra.ai');
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
get desktop() {
|
|
142
|
+
if (!this._sandbox) throw new _mastra_core_workspace.SandboxNotReadyError(this.id);
|
|
143
|
+
return this._sandbox;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Construct a sibling `E2BDesktopSandbox` that inherits this sandbox's
|
|
147
|
+
* configuration with per-instance overrides. See `E2BSandbox.clone`.
|
|
148
|
+
*/
|
|
149
|
+
clone(options = {}) {
|
|
150
|
+
const { id: _id, ...base } = this._desktopConstructorOptions;
|
|
151
|
+
return new E2BDesktopSandbox({
|
|
152
|
+
...base,
|
|
153
|
+
...options.id !== void 0 && { id: options.id },
|
|
154
|
+
...options.env !== void 0 && { env: options.env },
|
|
155
|
+
...options.idleTimeoutMinutes !== void 0 && { timeout: options.idleTimeoutMinutes * 6e4 }
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
/** Create a `@e2b/desktop` SDK sandbox (with desktop display options). */
|
|
159
|
+
async createSdkSandbox(templateId, opts) {
|
|
160
|
+
return _e2b_desktop.Sandbox.create(templateId, {
|
|
161
|
+
...opts,
|
|
162
|
+
...this.resolution && { resolution: this.resolution },
|
|
163
|
+
...this.dpi !== void 0 && { dpi: this.dpi }
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/** Connect to an existing `@e2b/desktop` SDK sandbox. */
|
|
167
|
+
async connectSdkSandbox(sandboxId, opts) {
|
|
168
|
+
return _e2b_desktop.Sandbox.connect(sandboxId, opts);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Resolve the template: without an explicit `template` option, use the
|
|
172
|
+
* E2B-hosted desktop template (no build needed). Explicit templates go
|
|
173
|
+
* through the base provider's resolution (ID, builder, or customizer).
|
|
174
|
+
*/
|
|
175
|
+
async resolveTemplate() {
|
|
176
|
+
if (this._resolvedTemplateId) return this._resolvedTemplateId;
|
|
177
|
+
if (!this.templateSpec) {
|
|
178
|
+
this._resolvedTemplateId = DEFAULT_DESKTOP_TEMPLATE;
|
|
179
|
+
return this._resolvedTemplateId;
|
|
180
|
+
}
|
|
181
|
+
return super.resolveTemplate();
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The default desktop template is E2B-hosted — there is nothing to build.
|
|
185
|
+
* (Only reached from the template-not-found retry path in `start()`.)
|
|
186
|
+
*/
|
|
187
|
+
async buildDefaultTemplate() {
|
|
188
|
+
this._resolvedTemplateId = DEFAULT_DESKTOP_TEMPLATE;
|
|
189
|
+
return this._resolvedTemplateId;
|
|
190
|
+
}
|
|
191
|
+
/** Ensure the sandbox is running, then run a desktop SDK operation. */
|
|
192
|
+
async withDesktop(fn) {
|
|
193
|
+
await this.ensureRunning();
|
|
194
|
+
return this.retryOnDead(() => fn(this.desktop));
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Ensure the VNC stream is running (authenticated), memoized per SDK
|
|
198
|
+
* sandbox so a resumed/recreated VM restarts it. A stream already started
|
|
199
|
+
* externally (via the `desktop` escape hatch) is left untouched.
|
|
200
|
+
*/
|
|
201
|
+
async ensureStreamStarted(desktop) {
|
|
202
|
+
if (this._streamStarted?.sandboxId !== desktop.sandboxId) {
|
|
203
|
+
const promise = desktop.stream.start({ requireAuth: true }).catch((error) => {
|
|
204
|
+
if (String(error).includes("already running")) return;
|
|
205
|
+
this._streamStarted = null;
|
|
206
|
+
throw error;
|
|
207
|
+
});
|
|
208
|
+
this._streamStarted = {
|
|
209
|
+
sandboxId: desktop.sandboxId,
|
|
210
|
+
promise
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return this._streamStarted.promise;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/provider.ts
|
|
218
|
+
const e2bDesktopSandboxProvider = {
|
|
219
|
+
id: "e2b-desktop",
|
|
220
|
+
name: "E2B Desktop Sandbox",
|
|
221
|
+
description: "Cloud desktop (computer-use) sandbox powered by E2B",
|
|
222
|
+
configSchema: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {
|
|
225
|
+
template: {
|
|
226
|
+
type: "string",
|
|
227
|
+
description: "Sandbox template ID (defaults to the E2B desktop template)"
|
|
228
|
+
},
|
|
229
|
+
timeout: {
|
|
230
|
+
type: "number",
|
|
231
|
+
description: "Execution timeout in milliseconds",
|
|
232
|
+
default: 3e5
|
|
233
|
+
},
|
|
234
|
+
env: {
|
|
235
|
+
type: "object",
|
|
236
|
+
description: "Environment variables",
|
|
237
|
+
additionalProperties: { type: "string" }
|
|
238
|
+
},
|
|
239
|
+
metadata: {
|
|
240
|
+
type: "object",
|
|
241
|
+
description: "Custom metadata",
|
|
242
|
+
additionalProperties: true
|
|
243
|
+
},
|
|
244
|
+
resolution: {
|
|
245
|
+
type: "array",
|
|
246
|
+
description: "Desktop resolution as [width, height] in pixels",
|
|
247
|
+
items: { type: "number" },
|
|
248
|
+
minItems: 2,
|
|
249
|
+
maxItems: 2
|
|
250
|
+
},
|
|
251
|
+
dpi: {
|
|
252
|
+
type: "number",
|
|
253
|
+
description: "Desktop display DPI"
|
|
254
|
+
},
|
|
255
|
+
domain: {
|
|
256
|
+
type: "string",
|
|
257
|
+
description: "Domain for self-hosted E2B"
|
|
258
|
+
},
|
|
259
|
+
apiUrl: {
|
|
260
|
+
type: "string",
|
|
261
|
+
description: "API URL for self-hosted E2B"
|
|
262
|
+
},
|
|
263
|
+
apiKey: {
|
|
264
|
+
type: "string",
|
|
265
|
+
description: "E2B API key"
|
|
266
|
+
},
|
|
267
|
+
accessToken: {
|
|
268
|
+
type: "string",
|
|
269
|
+
description: "E2B access token"
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
createSandbox: (config) => {
|
|
274
|
+
const { resolution, ...rest } = config;
|
|
275
|
+
return new E2BDesktopSandbox({
|
|
276
|
+
...rest,
|
|
277
|
+
...resolution && { resolution: [resolution[0], resolution[1]] }
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
//#endregion
|
|
282
|
+
exports.E2BDesktopSandbox = E2BDesktopSandbox;
|
|
283
|
+
exports.e2bDesktopSandboxProvider = e2bDesktopSandboxProvider;
|
|
284
|
+
|
|
285
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["E2BSandbox","SandboxNotReadyError","E2BDesktopSdkSandbox"],"sources":["../src/sandbox/index.ts","../src/provider.ts"],"sourcesContent":["/**\n * E2B Desktop Sandbox Provider\n *\n * An E2B sandbox running a full Linux desktop environment with computer-use\n * (screenshot, mouse, keyboard) control. Extends `@mastra/e2b`'s `E2BSandbox`\n * the same way `@e2b/desktop`'s SDK `Sandbox` extends `e2b`'s: everything the\n * base provider supports (command execution, processes, file upload,\n * reconnection) works against the same desktop VM.\n *\n * @see https://github.com/e2b-dev/desktop\n */\n\nimport { Sandbox as E2BDesktopSdkSandbox } from '@e2b/desktop';\nimport type { SandboxCloneOptions, SandboxComputer } from '@mastra/core/workspace';\nimport { SandboxNotReadyError } from '@mastra/core/workspace';\nimport { E2BSandbox } from '@mastra/e2b';\nimport type { E2BSandboxOptions } from '@mastra/e2b';\nimport type { Sandbox, SandboxConnectOpts, SandboxOpts } from 'e2b';\n\n/**\n * E2B-hosted desktop template (`@e2b/desktop`'s default). Used when no\n * explicit `template` option is provided — unlike the base provider, no\n * template build is required.\n */\nconst DEFAULT_DESKTOP_TEMPLATE = 'desktop';\n\n// =============================================================================\n// E2B Desktop Sandbox Options\n// =============================================================================\n\n/**\n * E2B Desktop sandbox provider configuration.\n *\n * Inherits all `E2BSandboxOptions` (credentials, timeout, env, metadata,\n * network, template, instructions). When no `template` is provided, the\n * E2B-hosted `desktop` template is used instead of the base provider's\n * mountable template.\n */\nexport interface E2BDesktopSandboxOptions extends E2BSandboxOptions {\n /**\n * Desktop display resolution as `[width, height]` in pixels.\n * Applies to newly created sandboxes only.\n */\n resolution?: [number, number];\n /**\n * Desktop display DPI.\n * Applies to newly created sandboxes only.\n */\n dpi?: number;\n}\n\n// =============================================================================\n// E2B Desktop Sandbox Implementation\n// =============================================================================\n\n/**\n * E2B Desktop sandbox provider for Mastra workspaces.\n *\n * Features:\n * - Everything from `E2BSandbox` (commands, processes, file upload, pause/resume)\n * - `computer` capability: screenshot, mouse, and keyboard control — when\n * used in a `Workspace`, agents automatically get the\n * `mastra_workspace_computer_*` tools\n * - Live desktop view via an authenticated noVNC stream URL\n *\n * @example Basic usage\n * ```typescript\n * import { Agent } from '@mastra/core/agent';\n * import { Workspace } from '@mastra/core/workspace';\n * import { E2BDesktopSandbox } from '@mastra/e2b-desktop';\n *\n * const sandbox = new E2BDesktopSandbox({ resolution: [1280, 720] });\n * const agent = new Agent({\n * // file + shell + computer tools are all emitted automatically\n * workspace: new Workspace({ sandbox }),\n * // ...\n * });\n * ```\n *\n * @example Direct desktop control\n * ```typescript\n * const sandbox = new E2BDesktopSandbox();\n * await sandbox.start();\n * await sandbox.computer.leftClick(100, 200);\n * const { data } = await sandbox.computer.screenshot();\n * const viewerUrl = await sandbox.computer.streamUrl();\n * ```\n */\nexport class E2BDesktopSandbox extends E2BSandbox {\n override readonly name: string = 'E2BDesktopSandbox';\n override readonly provider: string = 'e2b-desktop';\n\n private readonly resolution?: [number, number];\n private readonly dpi?: number;\n private readonly _desktopConstructorOptions: E2BDesktopSandboxOptions;\n\n /** Stream startup memo, keyed by SDK sandbox ID so a fresh VM restarts it. */\n private _streamStarted: { sandboxId: string; promise: Promise<void> } | null = null;\n\n constructor(options: E2BDesktopSandboxOptions = {}) {\n super(options);\n this.resolution = options.resolution;\n this.dpi = options.dpi;\n this._desktopConstructorOptions = { ...options };\n }\n\n /**\n * Computer-use (desktop) capability: screenshot, mouse, and keyboard\n * control of the sandbox's desktop, backed by `@e2b/desktop`.\n * Operations start the sandbox automatically if it is not running.\n */\n override readonly computer: SandboxComputer = {\n screenshot: async () => {\n const data = await this.withDesktop(desktop => desktop.screenshot());\n return { data, mediaType: 'image/png' as const };\n },\n leftClick: async (x, y) => {\n await this.withDesktop(desktop => desktop.leftClick(x, y));\n },\n rightClick: async (x, y) => {\n await this.withDesktop(desktop => desktop.rightClick(x, y));\n },\n doubleClick: async (x, y) => {\n await this.withDesktop(desktop => desktop.doubleClick(x, y));\n },\n moveMouse: async (x, y) => {\n await this.withDesktop(desktop => desktop.moveMouse(x, y));\n },\n drag: async (from, to) => {\n await this.withDesktop(desktop => desktop.drag([from.x, from.y], [to.x, to.y]));\n },\n scroll: async (direction, amount) => {\n await this.withDesktop(desktop => desktop.scroll(direction, amount));\n },\n type: async text => {\n await this.withDesktop(desktop => desktop.write(text));\n },\n press: async key => {\n await this.withDesktop(desktop => desktop.press(key));\n },\n getScreenSize: async () => {\n return this.withDesktop(desktop => desktop.getScreenSize());\n },\n getCursorPosition: async () => {\n return this.withDesktop(desktop => desktop.getCursorPosition());\n },\n streamUrl: async () => {\n try {\n return await this.withDesktop(async desktop => {\n await this.ensureStreamStarted(desktop);\n try {\n return desktop.stream.getUrl({ authKey: desktop.stream.getAuthKey() });\n } catch {\n // Stream was started externally without auth — return the plain URL.\n return desktop.stream.getUrl();\n }\n });\n } catch {\n return null;\n }\n },\n };\n\n /**\n * Get the underlying `@e2b/desktop` Sandbox instance for direct access to\n * desktop APIs not exposed through the abstraction (e.g. `launch()`,\n * `open()`, window helpers, custom stream control).\n *\n * @throws {SandboxNotReadyError} If the sandbox has not been started\n *\n * @example Launch an application\n * ```typescript\n * await sandbox.start();\n * await sandbox.desktop.launch('xfce4-terminal');\n * await sandbox.desktop.open('https://mastra.ai');\n * ```\n */\n get desktop(): E2BDesktopSdkSandbox {\n if (!this._sandbox) {\n throw new SandboxNotReadyError(this.id);\n }\n // Safe: the factory hooks below guarantee the SDK sandbox is a desktop sandbox.\n return this._sandbox as E2BDesktopSdkSandbox;\n }\n\n /**\n * Construct a sibling `E2BDesktopSandbox` that inherits this sandbox's\n * configuration with per-instance overrides. See `E2BSandbox.clone`.\n */\n override clone(options: SandboxCloneOptions = {}): E2BDesktopSandbox {\n const { id: _id, ...base } = this._desktopConstructorOptions;\n return new E2BDesktopSandbox({\n ...base,\n ...(options.id !== undefined && { id: options.id }),\n ...(options.env !== undefined && { env: options.env }),\n ...(options.idleTimeoutMinutes !== undefined && { timeout: options.idleTimeoutMinutes * 60_000 }),\n });\n }\n\n // ---------------------------------------------------------------------------\n // SDK Factory Hooks\n // ---------------------------------------------------------------------------\n\n /** Create a `@e2b/desktop` SDK sandbox (with desktop display options). */\n protected override async createSdkSandbox(templateId: string, opts: SandboxOpts): Promise<Sandbox> {\n return E2BDesktopSdkSandbox.create(templateId, {\n ...opts,\n ...(this.resolution && { resolution: this.resolution }),\n ...(this.dpi !== undefined && { dpi: this.dpi }),\n });\n }\n\n /** Connect to an existing `@e2b/desktop` SDK sandbox. */\n protected override async connectSdkSandbox(sandboxId: string, opts: SandboxConnectOpts): Promise<Sandbox> {\n return E2BDesktopSdkSandbox.connect(sandboxId, opts);\n }\n\n // ---------------------------------------------------------------------------\n // Template Resolution\n // ---------------------------------------------------------------------------\n\n /**\n * Resolve the template: without an explicit `template` option, use the\n * E2B-hosted desktop template (no build needed). Explicit templates go\n * through the base provider's resolution (ID, builder, or customizer).\n */\n protected override async resolveTemplate(): Promise<string> {\n if (this._resolvedTemplateId) {\n return this._resolvedTemplateId;\n }\n if (!this.templateSpec) {\n this._resolvedTemplateId = DEFAULT_DESKTOP_TEMPLATE;\n return this._resolvedTemplateId;\n }\n return super.resolveTemplate();\n }\n\n /**\n * The default desktop template is E2B-hosted — there is nothing to build.\n * (Only reached from the template-not-found retry path in `start()`.)\n */\n protected override async buildDefaultTemplate(): Promise<string> {\n this._resolvedTemplateId = DEFAULT_DESKTOP_TEMPLATE;\n return this._resolvedTemplateId;\n }\n\n // ---------------------------------------------------------------------------\n // Internal Helpers\n // ---------------------------------------------------------------------------\n\n /** Ensure the sandbox is running, then run a desktop SDK operation. */\n private async withDesktop<T>(fn: (desktop: E2BDesktopSdkSandbox) => Promise<T>): Promise<T> {\n await this.ensureRunning();\n return this.retryOnDead(() => fn(this.desktop));\n }\n\n /**\n * Ensure the VNC stream is running (authenticated), memoized per SDK\n * sandbox so a resumed/recreated VM restarts it. A stream already started\n * externally (via the `desktop` escape hatch) is left untouched.\n */\n private async ensureStreamStarted(desktop: E2BDesktopSdkSandbox): Promise<void> {\n if (this._streamStarted?.sandboxId !== desktop.sandboxId) {\n const promise = desktop.stream.start({ requireAuth: true }).catch((error: unknown) => {\n if (String(error).includes('already running')) {\n return;\n }\n this._streamStarted = null;\n throw error;\n });\n this._streamStarted = { sandboxId: desktop.sandboxId, promise };\n }\n return this._streamStarted.promise;\n }\n}\n","/**\n * E2B Desktop sandbox provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { e2bDesktopSandboxProvider } from '@mastra/e2b-desktop';\n *\n * const editor = new MastraEditor({\n * sandboxes: [e2bDesktopSandboxProvider],\n * });\n * ```\n */\nimport type { SandboxProvider } from '@mastra/core/editor';\nimport { E2BDesktopSandbox } from './sandbox';\n\n/**\n * Serializable subset of E2BDesktopSandboxOptions for editor storage.\n * Non-serializable options (TemplateBuilder callbacks, runtime objects) are excluded.\n */\ninterface E2BDesktopProviderConfig {\n template?: string;\n timeout?: number;\n env?: Record<string, string>;\n metadata?: Record<string, unknown>;\n resolution?: [number, number];\n dpi?: number;\n domain?: string;\n apiUrl?: string;\n apiKey?: string;\n accessToken?: string;\n}\n\nexport const e2bDesktopSandboxProvider: SandboxProvider<E2BDesktopProviderConfig> = {\n id: 'e2b-desktop',\n name: 'E2B Desktop Sandbox',\n description: 'Cloud desktop (computer-use) sandbox powered by E2B',\n configSchema: {\n type: 'object',\n properties: {\n template: { type: 'string', description: 'Sandbox template ID (defaults to the E2B desktop template)' },\n timeout: { type: 'number', description: 'Execution timeout in milliseconds', default: 300000 },\n env: {\n type: 'object',\n description: 'Environment variables',\n additionalProperties: { type: 'string' },\n },\n metadata: {\n type: 'object',\n description: 'Custom metadata',\n additionalProperties: true,\n },\n resolution: {\n type: 'array',\n description: 'Desktop resolution as [width, height] in pixels',\n items: { type: 'number' },\n minItems: 2,\n maxItems: 2,\n },\n dpi: { type: 'number', description: 'Desktop display DPI' },\n domain: { type: 'string', description: 'Domain for self-hosted E2B' },\n apiUrl: { type: 'string', description: 'API URL for self-hosted E2B' },\n apiKey: { type: 'string', description: 'E2B API key' },\n accessToken: { type: 'string', description: 'E2B access token' },\n },\n },\n createSandbox: config => {\n const { resolution, ...rest } = config;\n return new E2BDesktopSandbox({\n ...rest,\n ...(resolution && { resolution: [resolution[0]!, resolution[1]!] }),\n });\n },\n};\n"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAC7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { Sandbox } from "@e2b/desktop";
|
|
2
|
+
import { SandboxNotReadyError } from "@mastra/core/workspace";
|
|
3
|
+
import { E2BSandbox } from "@mastra/e2b";
|
|
4
|
+
//#region src/sandbox/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* E2B Desktop Sandbox Provider
|
|
7
|
+
*
|
|
8
|
+
* An E2B sandbox running a full Linux desktop environment with computer-use
|
|
9
|
+
* (screenshot, mouse, keyboard) control. Extends `@mastra/e2b`'s `E2BSandbox`
|
|
10
|
+
* the same way `@e2b/desktop`'s SDK `Sandbox` extends `e2b`'s: everything the
|
|
11
|
+
* base provider supports (command execution, processes, file upload,
|
|
12
|
+
* reconnection) works against the same desktop VM.
|
|
13
|
+
*
|
|
14
|
+
* @see https://github.com/e2b-dev/desktop
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* E2B-hosted desktop template (`@e2b/desktop`'s default). Used when no
|
|
18
|
+
* explicit `template` option is provided — unlike the base provider, no
|
|
19
|
+
* template build is required.
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_DESKTOP_TEMPLATE = "desktop";
|
|
22
|
+
/**
|
|
23
|
+
* E2B Desktop sandbox provider for Mastra workspaces.
|
|
24
|
+
*
|
|
25
|
+
* Features:
|
|
26
|
+
* - Everything from `E2BSandbox` (commands, processes, file upload, pause/resume)
|
|
27
|
+
* - `computer` capability: screenshot, mouse, and keyboard control — when
|
|
28
|
+
* used in a `Workspace`, agents automatically get the
|
|
29
|
+
* `mastra_workspace_computer_*` tools
|
|
30
|
+
* - Live desktop view via an authenticated noVNC stream URL
|
|
31
|
+
*
|
|
32
|
+
* @example Basic usage
|
|
33
|
+
* ```typescript
|
|
34
|
+
* import { Agent } from '@mastra/core/agent';
|
|
35
|
+
* import { Workspace } from '@mastra/core/workspace';
|
|
36
|
+
* import { E2BDesktopSandbox } from '@mastra/e2b-desktop';
|
|
37
|
+
*
|
|
38
|
+
* const sandbox = new E2BDesktopSandbox({ resolution: [1280, 720] });
|
|
39
|
+
* const agent = new Agent({
|
|
40
|
+
* // file + shell + computer tools are all emitted automatically
|
|
41
|
+
* workspace: new Workspace({ sandbox }),
|
|
42
|
+
* // ...
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @example Direct desktop control
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const sandbox = new E2BDesktopSandbox();
|
|
49
|
+
* await sandbox.start();
|
|
50
|
+
* await sandbox.computer.leftClick(100, 200);
|
|
51
|
+
* const { data } = await sandbox.computer.screenshot();
|
|
52
|
+
* const viewerUrl = await sandbox.computer.streamUrl();
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
var E2BDesktopSandbox = class E2BDesktopSandbox extends E2BSandbox {
|
|
56
|
+
name = "E2BDesktopSandbox";
|
|
57
|
+
provider = "e2b-desktop";
|
|
58
|
+
resolution;
|
|
59
|
+
dpi;
|
|
60
|
+
_desktopConstructorOptions;
|
|
61
|
+
/** Stream startup memo, keyed by SDK sandbox ID so a fresh VM restarts it. */
|
|
62
|
+
_streamStarted = null;
|
|
63
|
+
constructor(options = {}) {
|
|
64
|
+
super(options);
|
|
65
|
+
this.resolution = options.resolution;
|
|
66
|
+
this.dpi = options.dpi;
|
|
67
|
+
this._desktopConstructorOptions = { ...options };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Computer-use (desktop) capability: screenshot, mouse, and keyboard
|
|
71
|
+
* control of the sandbox's desktop, backed by `@e2b/desktop`.
|
|
72
|
+
* Operations start the sandbox automatically if it is not running.
|
|
73
|
+
*/
|
|
74
|
+
computer = {
|
|
75
|
+
screenshot: async () => {
|
|
76
|
+
return {
|
|
77
|
+
data: await this.withDesktop((desktop) => desktop.screenshot()),
|
|
78
|
+
mediaType: "image/png"
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
leftClick: async (x, y) => {
|
|
82
|
+
await this.withDesktop((desktop) => desktop.leftClick(x, y));
|
|
83
|
+
},
|
|
84
|
+
rightClick: async (x, y) => {
|
|
85
|
+
await this.withDesktop((desktop) => desktop.rightClick(x, y));
|
|
86
|
+
},
|
|
87
|
+
doubleClick: async (x, y) => {
|
|
88
|
+
await this.withDesktop((desktop) => desktop.doubleClick(x, y));
|
|
89
|
+
},
|
|
90
|
+
moveMouse: async (x, y) => {
|
|
91
|
+
await this.withDesktop((desktop) => desktop.moveMouse(x, y));
|
|
92
|
+
},
|
|
93
|
+
drag: async (from, to) => {
|
|
94
|
+
await this.withDesktop((desktop) => desktop.drag([from.x, from.y], [to.x, to.y]));
|
|
95
|
+
},
|
|
96
|
+
scroll: async (direction, amount) => {
|
|
97
|
+
await this.withDesktop((desktop) => desktop.scroll(direction, amount));
|
|
98
|
+
},
|
|
99
|
+
type: async (text) => {
|
|
100
|
+
await this.withDesktop((desktop) => desktop.write(text));
|
|
101
|
+
},
|
|
102
|
+
press: async (key) => {
|
|
103
|
+
await this.withDesktop((desktop) => desktop.press(key));
|
|
104
|
+
},
|
|
105
|
+
getScreenSize: async () => {
|
|
106
|
+
return this.withDesktop((desktop) => desktop.getScreenSize());
|
|
107
|
+
},
|
|
108
|
+
getCursorPosition: async () => {
|
|
109
|
+
return this.withDesktop((desktop) => desktop.getCursorPosition());
|
|
110
|
+
},
|
|
111
|
+
streamUrl: async () => {
|
|
112
|
+
try {
|
|
113
|
+
return await this.withDesktop(async (desktop) => {
|
|
114
|
+
await this.ensureStreamStarted(desktop);
|
|
115
|
+
try {
|
|
116
|
+
return desktop.stream.getUrl({ authKey: desktop.stream.getAuthKey() });
|
|
117
|
+
} catch {
|
|
118
|
+
return desktop.stream.getUrl();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Get the underlying `@e2b/desktop` Sandbox instance for direct access to
|
|
128
|
+
* desktop APIs not exposed through the abstraction (e.g. `launch()`,
|
|
129
|
+
* `open()`, window helpers, custom stream control).
|
|
130
|
+
*
|
|
131
|
+
* @throws {SandboxNotReadyError} If the sandbox has not been started
|
|
132
|
+
*
|
|
133
|
+
* @example Launch an application
|
|
134
|
+
* ```typescript
|
|
135
|
+
* await sandbox.start();
|
|
136
|
+
* await sandbox.desktop.launch('xfce4-terminal');
|
|
137
|
+
* await sandbox.desktop.open('https://mastra.ai');
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
get desktop() {
|
|
141
|
+
if (!this._sandbox) throw new SandboxNotReadyError(this.id);
|
|
142
|
+
return this._sandbox;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Construct a sibling `E2BDesktopSandbox` that inherits this sandbox's
|
|
146
|
+
* configuration with per-instance overrides. See `E2BSandbox.clone`.
|
|
147
|
+
*/
|
|
148
|
+
clone(options = {}) {
|
|
149
|
+
const { id: _id, ...base } = this._desktopConstructorOptions;
|
|
150
|
+
return new E2BDesktopSandbox({
|
|
151
|
+
...base,
|
|
152
|
+
...options.id !== void 0 && { id: options.id },
|
|
153
|
+
...options.env !== void 0 && { env: options.env },
|
|
154
|
+
...options.idleTimeoutMinutes !== void 0 && { timeout: options.idleTimeoutMinutes * 6e4 }
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/** Create a `@e2b/desktop` SDK sandbox (with desktop display options). */
|
|
158
|
+
async createSdkSandbox(templateId, opts) {
|
|
159
|
+
return Sandbox.create(templateId, {
|
|
160
|
+
...opts,
|
|
161
|
+
...this.resolution && { resolution: this.resolution },
|
|
162
|
+
...this.dpi !== void 0 && { dpi: this.dpi }
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/** Connect to an existing `@e2b/desktop` SDK sandbox. */
|
|
166
|
+
async connectSdkSandbox(sandboxId, opts) {
|
|
167
|
+
return Sandbox.connect(sandboxId, opts);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Resolve the template: without an explicit `template` option, use the
|
|
171
|
+
* E2B-hosted desktop template (no build needed). Explicit templates go
|
|
172
|
+
* through the base provider's resolution (ID, builder, or customizer).
|
|
173
|
+
*/
|
|
174
|
+
async resolveTemplate() {
|
|
175
|
+
if (this._resolvedTemplateId) return this._resolvedTemplateId;
|
|
176
|
+
if (!this.templateSpec) {
|
|
177
|
+
this._resolvedTemplateId = DEFAULT_DESKTOP_TEMPLATE;
|
|
178
|
+
return this._resolvedTemplateId;
|
|
179
|
+
}
|
|
180
|
+
return super.resolveTemplate();
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* The default desktop template is E2B-hosted — there is nothing to build.
|
|
184
|
+
* (Only reached from the template-not-found retry path in `start()`.)
|
|
185
|
+
*/
|
|
186
|
+
async buildDefaultTemplate() {
|
|
187
|
+
this._resolvedTemplateId = DEFAULT_DESKTOP_TEMPLATE;
|
|
188
|
+
return this._resolvedTemplateId;
|
|
189
|
+
}
|
|
190
|
+
/** Ensure the sandbox is running, then run a desktop SDK operation. */
|
|
191
|
+
async withDesktop(fn) {
|
|
192
|
+
await this.ensureRunning();
|
|
193
|
+
return this.retryOnDead(() => fn(this.desktop));
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Ensure the VNC stream is running (authenticated), memoized per SDK
|
|
197
|
+
* sandbox so a resumed/recreated VM restarts it. A stream already started
|
|
198
|
+
* externally (via the `desktop` escape hatch) is left untouched.
|
|
199
|
+
*/
|
|
200
|
+
async ensureStreamStarted(desktop) {
|
|
201
|
+
if (this._streamStarted?.sandboxId !== desktop.sandboxId) {
|
|
202
|
+
const promise = desktop.stream.start({ requireAuth: true }).catch((error) => {
|
|
203
|
+
if (String(error).includes("already running")) return;
|
|
204
|
+
this._streamStarted = null;
|
|
205
|
+
throw error;
|
|
206
|
+
});
|
|
207
|
+
this._streamStarted = {
|
|
208
|
+
sandboxId: desktop.sandboxId,
|
|
209
|
+
promise
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return this._streamStarted.promise;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/provider.ts
|
|
217
|
+
const e2bDesktopSandboxProvider = {
|
|
218
|
+
id: "e2b-desktop",
|
|
219
|
+
name: "E2B Desktop Sandbox",
|
|
220
|
+
description: "Cloud desktop (computer-use) sandbox powered by E2B",
|
|
221
|
+
configSchema: {
|
|
222
|
+
type: "object",
|
|
223
|
+
properties: {
|
|
224
|
+
template: {
|
|
225
|
+
type: "string",
|
|
226
|
+
description: "Sandbox template ID (defaults to the E2B desktop template)"
|
|
227
|
+
},
|
|
228
|
+
timeout: {
|
|
229
|
+
type: "number",
|
|
230
|
+
description: "Execution timeout in milliseconds",
|
|
231
|
+
default: 3e5
|
|
232
|
+
},
|
|
233
|
+
env: {
|
|
234
|
+
type: "object",
|
|
235
|
+
description: "Environment variables",
|
|
236
|
+
additionalProperties: { type: "string" }
|
|
237
|
+
},
|
|
238
|
+
metadata: {
|
|
239
|
+
type: "object",
|
|
240
|
+
description: "Custom metadata",
|
|
241
|
+
additionalProperties: true
|
|
242
|
+
},
|
|
243
|
+
resolution: {
|
|
244
|
+
type: "array",
|
|
245
|
+
description: "Desktop resolution as [width, height] in pixels",
|
|
246
|
+
items: { type: "number" },
|
|
247
|
+
minItems: 2,
|
|
248
|
+
maxItems: 2
|
|
249
|
+
},
|
|
250
|
+
dpi: {
|
|
251
|
+
type: "number",
|
|
252
|
+
description: "Desktop display DPI"
|
|
253
|
+
},
|
|
254
|
+
domain: {
|
|
255
|
+
type: "string",
|
|
256
|
+
description: "Domain for self-hosted E2B"
|
|
257
|
+
},
|
|
258
|
+
apiUrl: {
|
|
259
|
+
type: "string",
|
|
260
|
+
description: "API URL for self-hosted E2B"
|
|
261
|
+
},
|
|
262
|
+
apiKey: {
|
|
263
|
+
type: "string",
|
|
264
|
+
description: "E2B API key"
|
|
265
|
+
},
|
|
266
|
+
accessToken: {
|
|
267
|
+
type: "string",
|
|
268
|
+
description: "E2B access token"
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
createSandbox: (config) => {
|
|
273
|
+
const { resolution, ...rest } = config;
|
|
274
|
+
return new E2BDesktopSandbox({
|
|
275
|
+
...rest,
|
|
276
|
+
...resolution && { resolution: [resolution[0], resolution[1]] }
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
//#endregion
|
|
281
|
+
export { E2BDesktopSandbox, e2bDesktopSandboxProvider };
|
|
282
|
+
|
|
283
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["E2BDesktopSdkSandbox"],"sources":["../src/sandbox/index.ts","../src/provider.ts"],"sourcesContent":["/**\n * E2B Desktop Sandbox Provider\n *\n * An E2B sandbox running a full Linux desktop environment with computer-use\n * (screenshot, mouse, keyboard) control. Extends `@mastra/e2b`'s `E2BSandbox`\n * the same way `@e2b/desktop`'s SDK `Sandbox` extends `e2b`'s: everything the\n * base provider supports (command execution, processes, file upload,\n * reconnection) works against the same desktop VM.\n *\n * @see https://github.com/e2b-dev/desktop\n */\n\nimport { Sandbox as E2BDesktopSdkSandbox } from '@e2b/desktop';\nimport type { SandboxCloneOptions, SandboxComputer } from '@mastra/core/workspace';\nimport { SandboxNotReadyError } from '@mastra/core/workspace';\nimport { E2BSandbox } from '@mastra/e2b';\nimport type { E2BSandboxOptions } from '@mastra/e2b';\nimport type { Sandbox, SandboxConnectOpts, SandboxOpts } from 'e2b';\n\n/**\n * E2B-hosted desktop template (`@e2b/desktop`'s default). Used when no\n * explicit `template` option is provided — unlike the base provider, no\n * template build is required.\n */\nconst DEFAULT_DESKTOP_TEMPLATE = 'desktop';\n\n// =============================================================================\n// E2B Desktop Sandbox Options\n// =============================================================================\n\n/**\n * E2B Desktop sandbox provider configuration.\n *\n * Inherits all `E2BSandboxOptions` (credentials, timeout, env, metadata,\n * network, template, instructions). When no `template` is provided, the\n * E2B-hosted `desktop` template is used instead of the base provider's\n * mountable template.\n */\nexport interface E2BDesktopSandboxOptions extends E2BSandboxOptions {\n /**\n * Desktop display resolution as `[width, height]` in pixels.\n * Applies to newly created sandboxes only.\n */\n resolution?: [number, number];\n /**\n * Desktop display DPI.\n * Applies to newly created sandboxes only.\n */\n dpi?: number;\n}\n\n// =============================================================================\n// E2B Desktop Sandbox Implementation\n// =============================================================================\n\n/**\n * E2B Desktop sandbox provider for Mastra workspaces.\n *\n * Features:\n * - Everything from `E2BSandbox` (commands, processes, file upload, pause/resume)\n * - `computer` capability: screenshot, mouse, and keyboard control — when\n * used in a `Workspace`, agents automatically get the\n * `mastra_workspace_computer_*` tools\n * - Live desktop view via an authenticated noVNC stream URL\n *\n * @example Basic usage\n * ```typescript\n * import { Agent } from '@mastra/core/agent';\n * import { Workspace } from '@mastra/core/workspace';\n * import { E2BDesktopSandbox } from '@mastra/e2b-desktop';\n *\n * const sandbox = new E2BDesktopSandbox({ resolution: [1280, 720] });\n * const agent = new Agent({\n * // file + shell + computer tools are all emitted automatically\n * workspace: new Workspace({ sandbox }),\n * // ...\n * });\n * ```\n *\n * @example Direct desktop control\n * ```typescript\n * const sandbox = new E2BDesktopSandbox();\n * await sandbox.start();\n * await sandbox.computer.leftClick(100, 200);\n * const { data } = await sandbox.computer.screenshot();\n * const viewerUrl = await sandbox.computer.streamUrl();\n * ```\n */\nexport class E2BDesktopSandbox extends E2BSandbox {\n override readonly name: string = 'E2BDesktopSandbox';\n override readonly provider: string = 'e2b-desktop';\n\n private readonly resolution?: [number, number];\n private readonly dpi?: number;\n private readonly _desktopConstructorOptions: E2BDesktopSandboxOptions;\n\n /** Stream startup memo, keyed by SDK sandbox ID so a fresh VM restarts it. */\n private _streamStarted: { sandboxId: string; promise: Promise<void> } | null = null;\n\n constructor(options: E2BDesktopSandboxOptions = {}) {\n super(options);\n this.resolution = options.resolution;\n this.dpi = options.dpi;\n this._desktopConstructorOptions = { ...options };\n }\n\n /**\n * Computer-use (desktop) capability: screenshot, mouse, and keyboard\n * control of the sandbox's desktop, backed by `@e2b/desktop`.\n * Operations start the sandbox automatically if it is not running.\n */\n override readonly computer: SandboxComputer = {\n screenshot: async () => {\n const data = await this.withDesktop(desktop => desktop.screenshot());\n return { data, mediaType: 'image/png' as const };\n },\n leftClick: async (x, y) => {\n await this.withDesktop(desktop => desktop.leftClick(x, y));\n },\n rightClick: async (x, y) => {\n await this.withDesktop(desktop => desktop.rightClick(x, y));\n },\n doubleClick: async (x, y) => {\n await this.withDesktop(desktop => desktop.doubleClick(x, y));\n },\n moveMouse: async (x, y) => {\n await this.withDesktop(desktop => desktop.moveMouse(x, y));\n },\n drag: async (from, to) => {\n await this.withDesktop(desktop => desktop.drag([from.x, from.y], [to.x, to.y]));\n },\n scroll: async (direction, amount) => {\n await this.withDesktop(desktop => desktop.scroll(direction, amount));\n },\n type: async text => {\n await this.withDesktop(desktop => desktop.write(text));\n },\n press: async key => {\n await this.withDesktop(desktop => desktop.press(key));\n },\n getScreenSize: async () => {\n return this.withDesktop(desktop => desktop.getScreenSize());\n },\n getCursorPosition: async () => {\n return this.withDesktop(desktop => desktop.getCursorPosition());\n },\n streamUrl: async () => {\n try {\n return await this.withDesktop(async desktop => {\n await this.ensureStreamStarted(desktop);\n try {\n return desktop.stream.getUrl({ authKey: desktop.stream.getAuthKey() });\n } catch {\n // Stream was started externally without auth — return the plain URL.\n return desktop.stream.getUrl();\n }\n });\n } catch {\n return null;\n }\n },\n };\n\n /**\n * Get the underlying `@e2b/desktop` Sandbox instance for direct access to\n * desktop APIs not exposed through the abstraction (e.g. `launch()`,\n * `open()`, window helpers, custom stream control).\n *\n * @throws {SandboxNotReadyError} If the sandbox has not been started\n *\n * @example Launch an application\n * ```typescript\n * await sandbox.start();\n * await sandbox.desktop.launch('xfce4-terminal');\n * await sandbox.desktop.open('https://mastra.ai');\n * ```\n */\n get desktop(): E2BDesktopSdkSandbox {\n if (!this._sandbox) {\n throw new SandboxNotReadyError(this.id);\n }\n // Safe: the factory hooks below guarantee the SDK sandbox is a desktop sandbox.\n return this._sandbox as E2BDesktopSdkSandbox;\n }\n\n /**\n * Construct a sibling `E2BDesktopSandbox` that inherits this sandbox's\n * configuration with per-instance overrides. See `E2BSandbox.clone`.\n */\n override clone(options: SandboxCloneOptions = {}): E2BDesktopSandbox {\n const { id: _id, ...base } = this._desktopConstructorOptions;\n return new E2BDesktopSandbox({\n ...base,\n ...(options.id !== undefined && { id: options.id }),\n ...(options.env !== undefined && { env: options.env }),\n ...(options.idleTimeoutMinutes !== undefined && { timeout: options.idleTimeoutMinutes * 60_000 }),\n });\n }\n\n // ---------------------------------------------------------------------------\n // SDK Factory Hooks\n // ---------------------------------------------------------------------------\n\n /** Create a `@e2b/desktop` SDK sandbox (with desktop display options). */\n protected override async createSdkSandbox(templateId: string, opts: SandboxOpts): Promise<Sandbox> {\n return E2BDesktopSdkSandbox.create(templateId, {\n ...opts,\n ...(this.resolution && { resolution: this.resolution }),\n ...(this.dpi !== undefined && { dpi: this.dpi }),\n });\n }\n\n /** Connect to an existing `@e2b/desktop` SDK sandbox. */\n protected override async connectSdkSandbox(sandboxId: string, opts: SandboxConnectOpts): Promise<Sandbox> {\n return E2BDesktopSdkSandbox.connect(sandboxId, opts);\n }\n\n // ---------------------------------------------------------------------------\n // Template Resolution\n // ---------------------------------------------------------------------------\n\n /**\n * Resolve the template: without an explicit `template` option, use the\n * E2B-hosted desktop template (no build needed). Explicit templates go\n * through the base provider's resolution (ID, builder, or customizer).\n */\n protected override async resolveTemplate(): Promise<string> {\n if (this._resolvedTemplateId) {\n return this._resolvedTemplateId;\n }\n if (!this.templateSpec) {\n this._resolvedTemplateId = DEFAULT_DESKTOP_TEMPLATE;\n return this._resolvedTemplateId;\n }\n return super.resolveTemplate();\n }\n\n /**\n * The default desktop template is E2B-hosted — there is nothing to build.\n * (Only reached from the template-not-found retry path in `start()`.)\n */\n protected override async buildDefaultTemplate(): Promise<string> {\n this._resolvedTemplateId = DEFAULT_DESKTOP_TEMPLATE;\n return this._resolvedTemplateId;\n }\n\n // ---------------------------------------------------------------------------\n // Internal Helpers\n // ---------------------------------------------------------------------------\n\n /** Ensure the sandbox is running, then run a desktop SDK operation. */\n private async withDesktop<T>(fn: (desktop: E2BDesktopSdkSandbox) => Promise<T>): Promise<T> {\n await this.ensureRunning();\n return this.retryOnDead(() => fn(this.desktop));\n }\n\n /**\n * Ensure the VNC stream is running (authenticated), memoized per SDK\n * sandbox so a resumed/recreated VM restarts it. A stream already started\n * externally (via the `desktop` escape hatch) is left untouched.\n */\n private async ensureStreamStarted(desktop: E2BDesktopSdkSandbox): Promise<void> {\n if (this._streamStarted?.sandboxId !== desktop.sandboxId) {\n const promise = desktop.stream.start({ requireAuth: true }).catch((error: unknown) => {\n if (String(error).includes('already running')) {\n return;\n }\n this._streamStarted = null;\n throw error;\n });\n this._streamStarted = { sandboxId: desktop.sandboxId, promise };\n }\n return this._streamStarted.promise;\n }\n}\n","/**\n * E2B Desktop sandbox provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { e2bDesktopSandboxProvider } from '@mastra/e2b-desktop';\n *\n * const editor = new MastraEditor({\n * sandboxes: [e2bDesktopSandboxProvider],\n * });\n * ```\n */\nimport type { SandboxProvider } from '@mastra/core/editor';\nimport { E2BDesktopSandbox } from './sandbox';\n\n/**\n * Serializable subset of E2BDesktopSandboxOptions for editor storage.\n * Non-serializable options (TemplateBuilder callbacks, runtime objects) are excluded.\n */\ninterface E2BDesktopProviderConfig {\n template?: string;\n timeout?: number;\n env?: Record<string, string>;\n metadata?: Record<string, unknown>;\n resolution?: [number, number];\n dpi?: number;\n domain?: string;\n apiUrl?: string;\n apiKey?: string;\n accessToken?: string;\n}\n\nexport const e2bDesktopSandboxProvider: SandboxProvider<E2BDesktopProviderConfig> = {\n id: 'e2b-desktop',\n name: 'E2B Desktop Sandbox',\n description: 'Cloud desktop (computer-use) sandbox powered by E2B',\n configSchema: {\n type: 'object',\n properties: {\n template: { type: 'string', description: 'Sandbox template ID (defaults to the E2B desktop template)' },\n timeout: { type: 'number', description: 'Execution timeout in milliseconds', default: 300000 },\n env: {\n type: 'object',\n description: 'Environment variables',\n additionalProperties: { type: 'string' },\n },\n metadata: {\n type: 'object',\n description: 'Custom metadata',\n additionalProperties: true,\n },\n resolution: {\n type: 'array',\n description: 'Desktop resolution as [width, height] in pixels',\n items: { type: 'number' },\n minItems: 2,\n maxItems: 2,\n },\n dpi: { type: 'number', description: 'Desktop display DPI' },\n domain: { type: 'string', description: 'Domain for self-hosted E2B' },\n apiUrl: { type: 'string', description: 'API URL for self-hosted E2B' },\n apiKey: { type: 'string', description: 'E2B API key' },\n accessToken: { type: 'string', description: 'E2B access token' },\n },\n },\n createSandbox: config => {\n const { resolution, ...rest } = config;\n return new E2BDesktopSandbox({\n ...rest,\n ...(resolution && { resolution: [resolution[0]!, resolution[1]!] }),\n });\n },\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2B Desktop sandbox provider descriptor for MastraEditor.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { e2bDesktopSandboxProvider } from '@mastra/e2b-desktop';
|
|
7
|
+
*
|
|
8
|
+
* const editor = new MastraEditor({
|
|
9
|
+
* sandboxes: [e2bDesktopSandboxProvider],
|
|
10
|
+
* });
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
import type { SandboxProvider } from '@mastra/core/editor';
|
|
14
|
+
/**
|
|
15
|
+
* Serializable subset of E2BDesktopSandboxOptions for editor storage.
|
|
16
|
+
* Non-serializable options (TemplateBuilder callbacks, runtime objects) are excluded.
|
|
17
|
+
*/
|
|
18
|
+
interface E2BDesktopProviderConfig {
|
|
19
|
+
template?: string;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
env?: Record<string, string>;
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
resolution?: [number, number];
|
|
24
|
+
dpi?: number;
|
|
25
|
+
domain?: string;
|
|
26
|
+
apiUrl?: string;
|
|
27
|
+
apiKey?: string;
|
|
28
|
+
accessToken?: string;
|
|
29
|
+
}
|
|
30
|
+
export declare const e2bDesktopSandboxProvider: SandboxProvider<E2BDesktopProviderConfig>;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG3D;;;GAGG;AACH,UAAU,wBAAwB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,yBAAyB,EAAE,eAAe,CAAC,wBAAwB,CAwC/E,CAAC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2B Desktop Sandbox Provider
|
|
3
|
+
*
|
|
4
|
+
* An E2B sandbox running a full Linux desktop environment with computer-use
|
|
5
|
+
* (screenshot, mouse, keyboard) control. Extends `@mastra/e2b`'s `E2BSandbox`
|
|
6
|
+
* the same way `@e2b/desktop`'s SDK `Sandbox` extends `e2b`'s: everything the
|
|
7
|
+
* base provider supports (command execution, processes, file upload,
|
|
8
|
+
* reconnection) works against the same desktop VM.
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/e2b-dev/desktop
|
|
11
|
+
*/
|
|
12
|
+
import { Sandbox as E2BDesktopSdkSandbox } from '@e2b/desktop';
|
|
13
|
+
import type { SandboxCloneOptions, SandboxComputer } from '@mastra/core/workspace';
|
|
14
|
+
import { E2BSandbox } from '@mastra/e2b';
|
|
15
|
+
import type { E2BSandboxOptions } from '@mastra/e2b';
|
|
16
|
+
import type { Sandbox, SandboxConnectOpts, SandboxOpts } from 'e2b';
|
|
17
|
+
/**
|
|
18
|
+
* E2B Desktop sandbox provider configuration.
|
|
19
|
+
*
|
|
20
|
+
* Inherits all `E2BSandboxOptions` (credentials, timeout, env, metadata,
|
|
21
|
+
* network, template, instructions). When no `template` is provided, the
|
|
22
|
+
* E2B-hosted `desktop` template is used instead of the base provider's
|
|
23
|
+
* mountable template.
|
|
24
|
+
*/
|
|
25
|
+
export interface E2BDesktopSandboxOptions extends E2BSandboxOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Desktop display resolution as `[width, height]` in pixels.
|
|
28
|
+
* Applies to newly created sandboxes only.
|
|
29
|
+
*/
|
|
30
|
+
resolution?: [number, number];
|
|
31
|
+
/**
|
|
32
|
+
* Desktop display DPI.
|
|
33
|
+
* Applies to newly created sandboxes only.
|
|
34
|
+
*/
|
|
35
|
+
dpi?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* E2B Desktop sandbox provider for Mastra workspaces.
|
|
39
|
+
*
|
|
40
|
+
* Features:
|
|
41
|
+
* - Everything from `E2BSandbox` (commands, processes, file upload, pause/resume)
|
|
42
|
+
* - `computer` capability: screenshot, mouse, and keyboard control — when
|
|
43
|
+
* used in a `Workspace`, agents automatically get the
|
|
44
|
+
* `mastra_workspace_computer_*` tools
|
|
45
|
+
* - Live desktop view via an authenticated noVNC stream URL
|
|
46
|
+
*
|
|
47
|
+
* @example Basic usage
|
|
48
|
+
* ```typescript
|
|
49
|
+
* import { Agent } from '@mastra/core/agent';
|
|
50
|
+
* import { Workspace } from '@mastra/core/workspace';
|
|
51
|
+
* import { E2BDesktopSandbox } from '@mastra/e2b-desktop';
|
|
52
|
+
*
|
|
53
|
+
* const sandbox = new E2BDesktopSandbox({ resolution: [1280, 720] });
|
|
54
|
+
* const agent = new Agent({
|
|
55
|
+
* // file + shell + computer tools are all emitted automatically
|
|
56
|
+
* workspace: new Workspace({ sandbox }),
|
|
57
|
+
* // ...
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example Direct desktop control
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const sandbox = new E2BDesktopSandbox();
|
|
64
|
+
* await sandbox.start();
|
|
65
|
+
* await sandbox.computer.leftClick(100, 200);
|
|
66
|
+
* const { data } = await sandbox.computer.screenshot();
|
|
67
|
+
* const viewerUrl = await sandbox.computer.streamUrl();
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare class E2BDesktopSandbox extends E2BSandbox {
|
|
71
|
+
readonly name: string;
|
|
72
|
+
readonly provider: string;
|
|
73
|
+
private readonly resolution?;
|
|
74
|
+
private readonly dpi?;
|
|
75
|
+
private readonly _desktopConstructorOptions;
|
|
76
|
+
/** Stream startup memo, keyed by SDK sandbox ID so a fresh VM restarts it. */
|
|
77
|
+
private _streamStarted;
|
|
78
|
+
constructor(options?: E2BDesktopSandboxOptions);
|
|
79
|
+
/**
|
|
80
|
+
* Computer-use (desktop) capability: screenshot, mouse, and keyboard
|
|
81
|
+
* control of the sandbox's desktop, backed by `@e2b/desktop`.
|
|
82
|
+
* Operations start the sandbox automatically if it is not running.
|
|
83
|
+
*/
|
|
84
|
+
readonly computer: SandboxComputer;
|
|
85
|
+
/**
|
|
86
|
+
* Get the underlying `@e2b/desktop` Sandbox instance for direct access to
|
|
87
|
+
* desktop APIs not exposed through the abstraction (e.g. `launch()`,
|
|
88
|
+
* `open()`, window helpers, custom stream control).
|
|
89
|
+
*
|
|
90
|
+
* @throws {SandboxNotReadyError} If the sandbox has not been started
|
|
91
|
+
*
|
|
92
|
+
* @example Launch an application
|
|
93
|
+
* ```typescript
|
|
94
|
+
* await sandbox.start();
|
|
95
|
+
* await sandbox.desktop.launch('xfce4-terminal');
|
|
96
|
+
* await sandbox.desktop.open('https://mastra.ai');
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
get desktop(): E2BDesktopSdkSandbox;
|
|
100
|
+
/**
|
|
101
|
+
* Construct a sibling `E2BDesktopSandbox` that inherits this sandbox's
|
|
102
|
+
* configuration with per-instance overrides. See `E2BSandbox.clone`.
|
|
103
|
+
*/
|
|
104
|
+
clone(options?: SandboxCloneOptions): E2BDesktopSandbox;
|
|
105
|
+
/** Create a `@e2b/desktop` SDK sandbox (with desktop display options). */
|
|
106
|
+
protected createSdkSandbox(templateId: string, opts: SandboxOpts): Promise<Sandbox>;
|
|
107
|
+
/** Connect to an existing `@e2b/desktop` SDK sandbox. */
|
|
108
|
+
protected connectSdkSandbox(sandboxId: string, opts: SandboxConnectOpts): Promise<Sandbox>;
|
|
109
|
+
/**
|
|
110
|
+
* Resolve the template: without an explicit `template` option, use the
|
|
111
|
+
* E2B-hosted desktop template (no build needed). Explicit templates go
|
|
112
|
+
* through the base provider's resolution (ID, builder, or customizer).
|
|
113
|
+
*/
|
|
114
|
+
protected resolveTemplate(): Promise<string>;
|
|
115
|
+
/**
|
|
116
|
+
* The default desktop template is E2B-hosted — there is nothing to build.
|
|
117
|
+
* (Only reached from the template-not-found retry path in `start()`.)
|
|
118
|
+
*/
|
|
119
|
+
protected buildDefaultTemplate(): Promise<string>;
|
|
120
|
+
/** Ensure the sandbox is running, then run a desktop SDK operation. */
|
|
121
|
+
private withDesktop;
|
|
122
|
+
/**
|
|
123
|
+
* Ensure the VNC stream is running (authenticated), memoized per SDK
|
|
124
|
+
* sandbox so a resumed/recreated VM restarts it. A stream already started
|
|
125
|
+
* externally (via the `desktop` escape hatch) is left untouched.
|
|
126
|
+
*/
|
|
127
|
+
private ensureStreamStarted;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEnF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAapE;;;;;;;GAOG;AACH,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,SAAkB,IAAI,EAAE,MAAM,CAAuB;IACrD,SAAkB,QAAQ,EAAE,MAAM,CAAiB;IAEnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAmB;IAC/C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAA2B;IAEtE,8EAA8E;IAC9E,OAAO,CAAC,cAAc,CAA8D;gBAExE,OAAO,GAAE,wBAA6B;IAOlD;;;;OAIG;IACH,SAAkB,QAAQ,EAAE,eAAe,CAkDzC;IAEF;;;;;;;;;;;;;OAaG;IACH,IAAI,OAAO,IAAI,oBAAoB,CAMlC;IAED;;;OAGG;IACM,KAAK,CAAC,OAAO,GAAE,mBAAwB,GAAG,iBAAiB;IAcpE,0EAA0E;cACjD,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAQlG,yDAAyD;cAChC,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAQzG;;;;OAIG;cACsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IAW3D;;;OAGG;cACsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAShE,uEAAuE;YACzD,WAAW;IAKzB;;;;OAIG;YACW,mBAAmB;CAalC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/e2b-desktop",
|
|
3
|
-
"version": "0.0.0",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
4
|
"description": "E2B Desktop (computer-use) sandbox provider for Mastra workspaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,36 +18,25 @@
|
|
|
18
18
|
},
|
|
19
19
|
"./package.json": "./package.json"
|
|
20
20
|
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsdown --silent --config tsdown.config.ts",
|
|
23
|
-
"build:lib": "pnpm build",
|
|
24
|
-
"build:watch": "pnpm build --watch",
|
|
25
|
-
"test:unit": "vitest run --exclude '**/*.integration.test.ts'",
|
|
26
|
-
"test:watch": "vitest watch",
|
|
27
|
-
"test": "vitest run ./src/**/*.integration.test.ts",
|
|
28
|
-
"test:cloud": "pnpm test",
|
|
29
|
-
"lint": "oxlint . && eslint .",
|
|
30
|
-
"lint:fix": "oxlint --fix . && eslint --fix ."
|
|
31
|
-
},
|
|
32
21
|
"license": "Apache-2.0",
|
|
33
22
|
"dependencies": {
|
|
34
23
|
"@e2b/desktop": "^2.3.1",
|
|
35
|
-
"
|
|
36
|
-
"e2b": "^
|
|
24
|
+
"e2b": "^2.36.0",
|
|
25
|
+
"@mastra/e2b": "^0.10.0-alpha.5"
|
|
37
26
|
},
|
|
38
27
|
"devDependencies": {
|
|
39
|
-
"@internal/lint": "workspace:*",
|
|
40
|
-
"@internal/types-builder": "workspace:*",
|
|
41
|
-
"@internal/workspace-test-utils": "workspace:*",
|
|
42
|
-
"@mastra/core": "workspace:*",
|
|
43
28
|
"@types/node": "22.20.1",
|
|
44
|
-
"@vitest/coverage-v8": "
|
|
45
|
-
"@vitest/ui": "
|
|
29
|
+
"@vitest/coverage-v8": "4.1.10",
|
|
30
|
+
"@vitest/ui": "4.1.10",
|
|
46
31
|
"dotenv": "^17.4.2",
|
|
47
32
|
"eslint": "^10.7.0",
|
|
48
33
|
"tsdown": "0.22.9",
|
|
49
|
-
"typescript": "
|
|
50
|
-
"vitest": "
|
|
34
|
+
"typescript": "^6.0.3",
|
|
35
|
+
"vitest": "4.1.10",
|
|
36
|
+
"@internal/types-builder": "0.0.100",
|
|
37
|
+
"@internal/lint": "0.0.125",
|
|
38
|
+
"@internal/workspace-test-utils": "0.0.69",
|
|
39
|
+
"@mastra/core": "1.62.0-alpha.12"
|
|
51
40
|
},
|
|
52
41
|
"peerDependencies": {
|
|
53
42
|
"@mastra/core": ">=1.60.0-0 <2.0.0-0"
|
|
@@ -67,5 +56,16 @@
|
|
|
67
56
|
},
|
|
68
57
|
"engines": {
|
|
69
58
|
"node": ">=22.13.0"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsdown --silent --config tsdown.config.ts",
|
|
62
|
+
"build:lib": "pnpm build",
|
|
63
|
+
"build:watch": "pnpm build --watch",
|
|
64
|
+
"test:unit": "vitest run --exclude '**/*.integration.test.ts'",
|
|
65
|
+
"test:watch": "vitest watch",
|
|
66
|
+
"test": "vitest run ./src/**/*.integration.test.ts",
|
|
67
|
+
"test:cloud": "pnpm test",
|
|
68
|
+
"lint": "oxlint . && eslint .",
|
|
69
|
+
"lint:fix": "oxlint --fix . && eslint --fix ."
|
|
70
70
|
}
|
|
71
|
-
}
|
|
71
|
+
}
|