@mastra/platform-workspace 1.5.0-alpha.3 → 1.5.0-alpha.5
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/README.md +13 -107
- package/dist/index.cjs +27 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -1
- package/dist/repo-template.d.ts.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @mastra/platform-workspace
|
|
2
2
|
|
|
3
|
-
Mastra Platform workspace provider.
|
|
3
|
+
Mastra Platform workspace provider. It gives agents environment-scoped sandbox execution and bucket-backed filesystem access through the Mastra Platform workspace proxy.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,23 +8,6 @@ Mastra Platform workspace provider. Gives agents environment-scoped sandbox exec
|
|
|
8
8
|
npm install @mastra/platform-workspace
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Configuration
|
|
12
|
-
|
|
13
|
-
All options can be passed to the constructor or read from environment variables:
|
|
14
|
-
|
|
15
|
-
| Option | Env var | Required |
|
|
16
|
-
| ----------------- | ------------------------------ | ---------------- |
|
|
17
|
-
| `accessToken` | `MASTRA_PLATFORM_ACCESS_TOKEN` | Yes |
|
|
18
|
-
| `projectId` | `MASTRA_PROJECT_ID` | Yes |
|
|
19
|
-
| `environmentId` | `MASTRA_ENVIRONMENT_ID` | Yes (sandbox) |
|
|
20
|
-
| `actingUserId` | — | No (sandbox) |
|
|
21
|
-
| `sandboxProvider` | `SANDBOX_PROVIDER` | No (sandbox) |
|
|
22
|
-
| `bucketName` | `MASTRA_PLATFORM_BUCKET_NAME` | Yes (filesystem) |
|
|
23
|
-
|
|
24
|
-
The sandbox provider resolves from the explicit `sandboxProvider` option, then `SANDBOX_PROVIDER`, then defaults to `e2b`. Set either option to `railway` to use Railway sandboxes. The proxy URL defaults to `https://workspaces.mastra.ai` and can be overridden with the `MASTRA_WORKSPACE_PROXY_URL` env var (useful for staging).
|
|
25
|
-
|
|
26
|
-
Requests to the proxy are authenticated with `Authorization: Bearer <accessToken>`. For sandbox requests authenticated with a project access token, set `actingUserId` to the stable opaque user subject from your authentication system. It is sent as `x-acting-user-id` for token partitioning and attribution; it is not an authorization claim.
|
|
27
|
-
|
|
28
11
|
## Usage
|
|
29
12
|
|
|
30
13
|
```typescript
|
|
@@ -33,11 +16,8 @@ import { Workspace } from '@mastra/core/workspace';
|
|
|
33
16
|
import { PlatformFilesystem, PlatformSandbox } from '@mastra/platform-workspace';
|
|
34
17
|
|
|
35
18
|
const workspace = new Workspace({
|
|
36
|
-
filesystem: new PlatformFilesystem({
|
|
37
|
-
// accessToken, projectId, bucketName all fall back to env
|
|
38
|
-
}),
|
|
19
|
+
filesystem: new PlatformFilesystem({}),
|
|
39
20
|
sandbox: new PlatformSandbox({
|
|
40
|
-
// accessToken, projectId, environmentId all fall back to env
|
|
41
21
|
idleTimeoutMinutes: 30,
|
|
42
22
|
networkIsolation: 'ISOLATED',
|
|
43
23
|
}),
|
|
@@ -50,98 +30,24 @@ const agent = new Agent({
|
|
|
50
30
|
});
|
|
51
31
|
```
|
|
52
32
|
|
|
53
|
-
##
|
|
54
|
-
|
|
55
|
-
`PlatformFilesystem` implements the Mastra filesystem interface against a workspace bucket. Object keys are percent-encoded per segment, so filenames with `?`, `#`, `%`, `&`, `+`, or spaces are preserved end-to-end.
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
const fs = new PlatformFilesystem({ bucketName: 'reports' });
|
|
59
|
-
|
|
60
|
-
await fs.writeFile('/analyses/repo.md', markdown);
|
|
61
|
-
const content = await fs.readFile('/analyses/repo.md');
|
|
62
|
-
const entries = await fs.readdir('/analyses');
|
|
63
|
-
await fs.moveFile('/analyses/repo.md', '/analyses/repo-final.md');
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Pass `readOnly: true` to mount the bucket read-only. Mutating calls will throw `WorkspaceReadOnlyError`.
|
|
67
|
-
|
|
68
|
-
## Sandbox
|
|
69
|
-
|
|
70
|
-
`PlatformSandbox` executes commands inside a provider-backed sandbox tied to a Platform environment. Sessions boot from the configured provider template or checkpoint.
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
const sandbox = new PlatformSandbox({ environmentId: 'env_abc' });
|
|
74
|
-
|
|
75
|
-
const result = await sandbox.executeCommand('python', ['analyze.py'], {
|
|
76
|
-
timeout: 30_000,
|
|
77
|
-
env: { INPUT: 'repo' },
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
console.log(result.stdout);
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Pass an existing `sandboxId` to reattach to a live sandbox instead of creating a new one.
|
|
33
|
+
## Documentation
|
|
84
34
|
|
|
85
|
-
|
|
35
|
+
Both providers authenticate through the workspace proxy with `MASTRA_PLATFORM_ACCESS_TOKEN` and `MASTRA_PROJECT_ID`. `PlatformSandbox` also requires `MASTRA_ENVIRONMENT_ID`; `PlatformFilesystem` requires `MASTRA_PLATFORM_BUCKET_NAME`. Constructor values override environment variables, and `MASTRA_WORKSPACE_PROXY_URL` can point requests at a non-production proxy.
|
|
86
36
|
|
|
87
|
-
|
|
37
|
+
`PlatformFilesystem` implements the Mastra filesystem interface against a Platform bucket. It supports reading, writing, listing, moving, and deleting files, preserves reserved characters in object names, and can be mounted with `readOnly: true` to reject mutations.
|
|
88
38
|
|
|
89
|
-
|
|
90
|
-
import { PlatformSandbox, Template } from '@mastra/platform-workspace';
|
|
91
|
-
|
|
92
|
-
const commitSha = process.env.REPOSITORY_COMMIT_SHA!;
|
|
93
|
-
const template = Template()
|
|
94
|
-
.cpuCount(4)
|
|
95
|
-
.memoryMB(8_192)
|
|
96
|
-
.setWorkdir('/workspace/repo')
|
|
97
|
-
.setEnvs({ BUILD_CONFIG_MARKER: 'template-v1' })
|
|
98
|
-
.aptInstall(['git', 'jq'])
|
|
99
|
-
.runCmd('git clone https://github.com/mastra-ai/mastra.git /workspace/repo')
|
|
100
|
-
.runCmd(`git checkout ${commitSha}`)
|
|
101
|
-
.runCmd('pnpm install --frozen-lockfile');
|
|
102
|
-
|
|
103
|
-
const sandbox = new PlatformSandbox({
|
|
104
|
-
environmentId: 'env_abc',
|
|
105
|
-
sandboxProvider: 'e2b',
|
|
106
|
-
template,
|
|
107
|
-
});
|
|
108
|
-
await sandbox.start();
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
Platform serializes the builder and stores build state under a server-derived content hash within the selected environment and provider. Passing the same definition to another sandbox reuses that build. Call `await template.build(options)` to start or reuse the provider build without provisioning a sandbox; it returns `ready`, `pending`, or `failed`. For E2B templates, `cpuCount()` and `memoryMB()` set the resources inherited by sandboxes created from the exact build or a resource-matched stale build. They default to 2 CPUs and 1,024 MB. Effective resource values participate in the template identity, so changing either value creates a distinct template while explicit defaults reuse the omitted-default build. If a pending build falls back to the provider base, that sandbox may use provider-default resources; check `templatePending` to detect this case. Railway currently ignores these two methods because its sandbox template API doesn't expose matching resource settings.
|
|
112
|
-
|
|
113
|
-
By default, operation arguments are serialized and sent to Platform. Use `setEnvs(values, { ephemeral: true })` for short-lived build credentials: these values are sent separately, excluded from content identity and persistence, unavailable at runtime, and take precedence over serialized values with the same key. Supply them on every build or fresh provision that may need to build. Railway's provider cache includes transient build variables, so rotating a value may trigger another provider build even though the Platform template ID stays stable.
|
|
114
|
-
|
|
115
|
-
## Errors
|
|
39
|
+
`PlatformSandbox` starts or reconnects to an environment-scoped provider sandbox and implements command execution, lifecycle, and networking operations. The provider defaults to E2B and can be changed to Railway through `sandboxProvider` or `SANDBOX_PROVIDER`. Pass an existing `sandboxId` to reattach to a live sandbox, and `actingUserId` to partition and attribute project-token requests to a stable application user.
|
|
116
40
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
import { PlatformApiError } from '@mastra/platform-workspace';
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
await fs.readFile('/missing.txt');
|
|
124
|
-
} catch (err) {
|
|
125
|
-
if (err instanceof PlatformApiError) {
|
|
126
|
-
if (err.code === 'not_found') {
|
|
127
|
-
// handle missing file
|
|
128
|
-
} else if (err.code === 'authentication_error') {
|
|
129
|
-
// refresh token
|
|
130
|
-
}
|
|
131
|
-
console.error(err.status, err.code, err.proxyMessage, err.body);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
```
|
|
41
|
+
The exported `Template()` builder creates reusable sandbox images from commands, packages, environment values, repository checkouts, CPU, memory, and working-directory settings. Platform derives a content identity from the serialized template so matching definitions can reuse previous builds. Ephemeral environment values are excluded from that identity and are not persisted into the runtime image.
|
|
135
42
|
|
|
136
|
-
|
|
43
|
+
Proxy failures throw `PlatformApiError`, which includes the HTTP status, parsed machine-readable error code, proxy message, and raw response body. Use these fields to distinguish missing resources, authentication failures, and provider errors.
|
|
137
44
|
|
|
138
|
-
|
|
45
|
+
- [Mastra Platform workspaces](https://mastra.ai/docs/mastra-platform/workspaces)
|
|
139
46
|
|
|
140
|
-
|
|
47
|
+
## Changelog
|
|
141
48
|
|
|
142
|
-
|
|
49
|
+
See the [package changelog](https://github.com/mastra-ai/mastra/blob/main/workspaces/platform-workspace/CHANGELOG.md) for version history and release notes.
|
|
143
50
|
|
|
144
|
-
|
|
145
|
-
- `SandboxExecTransportError` — both the initial WebSocket attempt and the built-in retry closed without an `exit` frame against a live sandbox. Carries `{ opened, closeCode, closeReason, wsEndpoint }` diagnostics plus `sandboxId`, `command`, and `attempts` so upstream logs / alerts can distinguish "the Railway data plane is broken" from "your command failed".
|
|
51
|
+
## Support
|
|
146
52
|
|
|
147
|
-
|
|
53
|
+
We have an [open community Discord](https://discord.gg/mastra-ai). Come and say hello and let us know if you have any questions or need any help getting things running.
|
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,7 @@ let _mastra_core_workspace = require("@mastra/core/workspace");
|
|
|
28
28
|
let e2b = require("e2b");
|
|
29
29
|
let child_process = require("child_process");
|
|
30
30
|
let util = require("util");
|
|
31
|
+
let crypto = require("crypto");
|
|
31
32
|
//#region src/client.ts
|
|
32
33
|
const DEFAULT_PROXY_URL = "https://workspaces.mastra.ai";
|
|
33
34
|
/**
|
|
@@ -1964,6 +1965,31 @@ var PlatformSandbox = class PlatformSandbox extends _mastra_core_workspace.Mastr
|
|
|
1964
1965
|
}
|
|
1965
1966
|
};
|
|
1966
1967
|
//#endregion
|
|
1968
|
+
//#region ../../packages/_internals/workspace/dist/index.js
|
|
1969
|
+
/**
|
|
1970
|
+
* Setup completion marker shared by repo templates and their consumers.
|
|
1971
|
+
*
|
|
1972
|
+
* A repo template writes this file beside the checkout as its last build
|
|
1973
|
+
* step, so it exists only in images where every setup command succeeded. Its
|
|
1974
|
+
* content is a digest of the setup commands the image ran, letting a sandbox
|
|
1975
|
+
* booted from the image tell whether the setup it is about to run already
|
|
1976
|
+
* happened. Relative to the template's build cwd, which is also the runtime
|
|
1977
|
+
* working directory the repo was cloned into.
|
|
1978
|
+
*/
|
|
1979
|
+
const SETUP_MARKER_PATH = ".mastra-sandbox/setup";
|
|
1980
|
+
/** Blank entries never become build steps, so they never count toward the digest either. */
|
|
1981
|
+
function normalizeSetupCommands(setupCommand) {
|
|
1982
|
+
return (setupCommand === void 0 ? [] : Array.isArray(setupCommand) ? setupCommand : [setupCommand]).filter((command) => command.trim() !== "");
|
|
1983
|
+
}
|
|
1984
|
+
/** The marker content for a setup command list: `sha256:<hex>` over the commands joined by newlines. */
|
|
1985
|
+
function setupMarkerContent(setupCommand) {
|
|
1986
|
+
return `sha256:${(0, crypto.createHash)("sha256").update(normalizeSetupCommands(setupCommand).join("\n")).digest("hex")}`;
|
|
1987
|
+
}
|
|
1988
|
+
/** Shell step that writes the marker relative to the cwd. `content` is a digest, so it is shell-safe. */
|
|
1989
|
+
function setupMarkerCommand(content) {
|
|
1990
|
+
return `mkdir -p "$(dirname "${SETUP_MARKER_PATH}")" && printf '%s' '${content}' > "${SETUP_MARKER_PATH}"`;
|
|
1991
|
+
}
|
|
1992
|
+
//#endregion
|
|
1967
1993
|
//#region src/repo-template.ts
|
|
1968
1994
|
const execFileAsync = (0, util.promisify)(child_process.execFile);
|
|
1969
1995
|
const SHA_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
@@ -2046,6 +2072,7 @@ function createRepoTemplate(options) {
|
|
|
2046
2072
|
if (workingDirectory) template = template.runCmd(`mkdir -p "${workingDirectory}"`).setWorkdir(workingDirectory);
|
|
2047
2073
|
template = template.runCmd(`git ${auth}clone ${cloneUrl} "${repoDir}"`).runCmd(`git -C "${repoDir}" ${auth}fetch origin ${sha}`).runCmd(`git -C "${repoDir}" checkout ${sha}`);
|
|
2048
2074
|
for (const command of setupCommands) template = template.runCmd(`cd "${repoDir}" && ${command}`);
|
|
2075
|
+
template = template.runCmd(setupMarkerCommand(setupMarkerContent(setupCommands)));
|
|
2049
2076
|
return template.withFamily(family);
|
|
2050
2077
|
};
|
|
2051
2078
|
}
|