@mastra/platform-workspace 1.8.2-alpha.0 → 1.9.0-alpha.1

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.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Shell scripts for the file operations a sandbox can only do atomically (or
3
+ * without moving file bytes over the network) by running a command inside the
4
+ * VM. Each builder takes absolute paths and returns one POSIX `sh` script; the
5
+ * caller runs it with `sh -c` and maps the exit codes below to typed errors.
6
+ */
7
+ /** Exit codes the scripts use for outcomes the caller turns into typed errors. Any other non-zero code is a plain failure. */
8
+ export declare const FILE_COMMAND_EXIT: {
9
+ readonly notFound: 20;
10
+ readonly isDirectory: 21;
11
+ readonly exists: 22;
12
+ readonly notDirectory: 23;
13
+ readonly destIsDirectory: 24;
14
+ };
15
+ /** Single-quote a string for safe POSIX shell interpolation. */
16
+ export declare function shellQuote(value: string): string;
17
+ /**
18
+ * A sibling temp path for `path`, so a later `ln` / `mv` onto `path` stays on
19
+ * one filesystem. Hidden, and unique per call.
20
+ */
21
+ export declare function siblingTempPath(path: string): string;
22
+ export interface CopyCommandOptions {
23
+ /** Copy a directory and everything under it. Without it a directory source fails with `isDirectory`. */
24
+ recursive?: boolean;
25
+ /** `false` fails with `exists` when the destination is taken, atomically. Default `true`. */
26
+ overwrite?: boolean;
27
+ }
28
+ /**
29
+ * Copy `src` to `dest` inside the sandbox, creating `dest`'s parent.
30
+ *
31
+ * A file source copies its content (a symlinked source is followed). A
32
+ * directory source with `recursive` copies its contents into `dest`, keeping
33
+ * symlinks inside the tree as links. With `overwrite: false` the destination
34
+ * is claimed atomically: a directory with an exclusive `mkdir`, a file by
35
+ * copying to a temp name and hard-linking it into place (`link(2)` fails when
36
+ * the destination exists).
37
+ *
38
+ * Exits: `notFound` (no source), `isDirectory` (directory source without
39
+ * `recursive`), `destIsDirectory` (a file onto an existing directory or a link
40
+ * to one), `exists`.
41
+ */
42
+ export declare function copyCommand(src: string, dest: string, options?: CopyCommandOptions): string;
43
+ /**
44
+ * Move `src` to `dest` without replacing anything at `dest`, creating `dest`'s
45
+ * parent. A skipped `mv -n` is detected by the source surviving, whatever
46
+ * its exit code. If a directory appears at `dest` between the check and the
47
+ * move, `mv` moves `src` into it (BSD `mv` has no `-T`), and that is reported
48
+ * as success; the no-clobber rename itself is atomic.
49
+ *
50
+ * Exits: `notFound` (no source), `exists`.
51
+ */
52
+ export declare function moveNoClobberCommand(src: string, dest: string): string;
53
+ /**
54
+ * Append the bytes already written to `tmp` onto `dest`, then delete `tmp`.
55
+ * The content travels as a file rather than a command argument, so it has no
56
+ * size limit. Creates `dest` (and its parent) when missing.
57
+ *
58
+ * Exits: `isDirectory` (dest is a directory).
59
+ */
60
+ export declare function appendFromCommand(tmp: string, dest: string): string;
61
+ /**
62
+ * Publish the file already written to `tmp` at `dest` only if nothing is
63
+ * there, then delete `tmp`. `ln` (`link(2)`) is the atomic exclusivity check;
64
+ * `tmp` must be on the same filesystem, e.g. from {@link siblingTempPath}.
65
+ *
66
+ * Exits: `exists`, `notDirectory` (dest's parent is not a directory).
67
+ */
68
+ export declare function linkNoClobberCommand(tmp: string, dest: string): string;
69
+ //# sourceMappingURL=file-commands.d.ts.map
@@ -0,0 +1,85 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import type { CopyOptions, FileContent, FileEntry, FileStat, ListOptions, ReadOptions, RemoveOptions, WriteOptions } from '@mastra/core/workspace';
3
+ import { MastraFilesystem } from '@mastra/core/workspace';
4
+ /** One entry as E2B's envd files API reports it (`EntryInfo`), reduced to the fields used here. */
5
+ export interface FilesApiEntry {
6
+ name: string;
7
+ /** `'file'` or `'dir'`, resolved through a symlink. Absent for a symlink whose target is missing. */
8
+ type?: string;
9
+ path: string;
10
+ /** The entry's own size: a symlink's is the link's, not its target's. */
11
+ size: number;
12
+ modifiedTime?: Date;
13
+ /** Set only on symlinks. */
14
+ symlinkTarget?: string;
15
+ }
16
+ /** The envd files calls the filesystem makes (E2B's `sandbox.files`). */
17
+ export interface FilesApi {
18
+ read(path: string, opts: {
19
+ format: 'bytes';
20
+ }): Promise<Uint8Array>;
21
+ /** Creates missing parent directories. */
22
+ write(path: string, data: string | ArrayBuffer): Promise<unknown>;
23
+ list(path: string, opts?: {
24
+ depth?: number;
25
+ }): Promise<FilesApiEntry[]>;
26
+ /** Recursive; resolves `false` when the directory already exists. */
27
+ makeDir(path: string): Promise<boolean>;
28
+ /** Creates missing destination parents and replaces an existing file. */
29
+ rename(oldPath: string, newPath: string): Promise<unknown>;
30
+ /** Recursive; a symlink is unlinked, never followed. */
31
+ remove(path: string): Promise<void>;
32
+ /** Does not follow the entry itself. */
33
+ getInfo(path: string): Promise<FilesApiEntry>;
34
+ }
35
+ /**
36
+ * The file-level answers a provider recognizes in its SDK's errors.
37
+ * `'not-found'` must not include "the sandbox itself is gone".
38
+ */
39
+ export type FilesApiErrorKind = 'not-found' | 'invalid-argument';
40
+ export interface FilesCommandResult {
41
+ exitCode: number;
42
+ stderr: string;
43
+ }
44
+ /**
45
+ * A sandbox's disk, read and written through E2B's envd files API, with the
46
+ * operations the files API can't do in one call (copy, append, no-clobber
47
+ * writes and moves) run as one shell command inside the VM so they are atomic
48
+ * and file bytes never round-trip through the host. Paths are absolute inside
49
+ * the sandbox; this class does not confine paths or symlinks to any root.
50
+ *
51
+ * Providers supply how to reach the files API and the shell, and how to read
52
+ * their SDK's errors.
53
+ *
54
+ * If a provider's `runCommand` re-runs a command whose first run already
55
+ * finished (e.g. a retry after a dropped connection), the second run sees the
56
+ * first's result: a no-clobber copy, write or move reports `FileExistsError`
57
+ * or `FileNotFoundError`, and an append fails, even though the work was done.
58
+ */
59
+ export declare abstract class FilesApiFilesystem extends MastraFilesystem {
60
+ #private;
61
+ /** Run `fn` with the provider's files API (starting the sandbox, retrying, … as the provider needs). */
62
+ protected abstract runFiles<T>(operation: string, fn: (files: FilesApi) => Promise<T>): Promise<T>;
63
+ /** Run a POSIX `sh` script inside the sandbox. */
64
+ protected abstract runCommand(operation: string, script: string): Promise<FilesCommandResult>;
65
+ /** Which file-level answer, if any, a files API error is. Anything else propagates unchanged. */
66
+ protected abstract errorKind(error: unknown): FilesApiErrorKind | undefined;
67
+ readFile(path: string, options?: ReadOptions): Promise<string | Buffer>;
68
+ writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void>;
69
+ appendFile(path: string, content: FileContent): Promise<void>;
70
+ deleteFile(path: string, options?: RemoveOptions): Promise<void>;
71
+ /**
72
+ * One `cp` inside the VM. A recursive copy keeps symlinks inside the tree as
73
+ * links; `overwrite: false` claims the destination atomically.
74
+ */
75
+ copyFile(src: string, dest: string, options?: CopyOptions): Promise<void>;
76
+ moveFile(src: string, dest: string, options?: CopyOptions): Promise<void>;
77
+ mkdir(path: string, options?: {
78
+ recursive?: boolean;
79
+ }): Promise<void>;
80
+ rmdir(path: string, options?: RemoveOptions): Promise<void>;
81
+ readdir(path: string, options?: ListOptions): Promise<FileEntry[]>;
82
+ exists(path: string): Promise<boolean>;
83
+ stat(path: string): Promise<FileStat>;
84
+ }
85
+ //# sourceMappingURL=files-api-filesystem.d.ts.map
@@ -0,0 +1,5 @@
1
+ export { SETUP_MARKER_DIR, SETUP_MARKER_PATH, WORKSPACE_SETUP_MARKER_PATH, SETUP_FAILED_MARKER_PATH, repoSetupMarkerPath, normalizeSetupCommands, setupMarkerContent, setupMarkerCommand, guardedSetupCommand, type GuardedSetupCommandOptions, } from './setup-marker.js';
2
+ export { repoCloneCommand, type RepoCloneCommandOptions } from './repo-clone.js';
3
+ export { FILE_COMMAND_EXIT, shellQuote, siblingTempPath, copyCommand, moveNoClobberCommand, appendFromCommand, linkNoClobberCommand, type CopyCommandOptions, } from './file-commands.js';
4
+ export { FilesApiFilesystem, type FilesApi, type FilesApiEntry, type FilesApiErrorKind, type FilesCommandResult, } from './files-api-filesystem.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The one way a repository is cloned into a sandbox, shared by the repo
3
+ * templates (which bake the clone into an image) and by Factory (which clones
4
+ * at session start when no image provided one), so both produce the same
5
+ * checkout: shallow, single branch, `origin` pointing at the plain URL.
6
+ *
7
+ * Credentials never enter the URL or `.git/config`. When `tokenEnv` is set,
8
+ * the token is read from that environment variable at run time and sent as a
9
+ * per-invocation `Authorization` header, so nothing persists in the checkout.
10
+ */
11
+ export interface RepoCloneCommandOptions {
12
+ /** Plain https clone URL, without credentials. */
13
+ cloneUrl: string;
14
+ /** Directory to clone into; relative paths resolve against the cwd. */
15
+ destination: string;
16
+ /** Branch to check out. Omit for the remote's default branch. */
17
+ branch?: string;
18
+ /** Name of the environment variable holding a GitHub installation token. */
19
+ tokenEnv?: string;
20
+ }
21
+ export declare function repoCloneCommand({ cloneUrl, destination, branch, tokenEnv }: RepoCloneCommandOptions): string;
22
+ //# sourceMappingURL=repo-clone.d.ts.map
@@ -0,0 +1,56 @@
1
+ /** Directory, relative to the template's build cwd, that holds every marker below. */
2
+ export declare const SETUP_MARKER_DIR = ".mastra-sandbox";
3
+ /**
4
+ * Setup completion marker shared by repo templates and their consumers.
5
+ *
6
+ * A repo template writes this file beside the checkout as its last build
7
+ * step, so it exists only in images where every setup command succeeded. Its
8
+ * content is a digest of the setup commands the image ran, letting a sandbox
9
+ * booted from the image tell whether the setup it is about to run already
10
+ * happened. Relative to the template's build cwd, which is also the runtime
11
+ * working directory the repo was cloned into.
12
+ *
13
+ * This is the single-repository marker. Templates built from a repository
14
+ * list write one marker per repository instead ({@link repoSetupMarkerPath}).
15
+ */
16
+ export declare const SETUP_MARKER_PATH = ".mastra-sandbox/setup";
17
+ /**
18
+ * Marker for the workspace-level setup of a multi-repository template: the
19
+ * commands that ran at the working directory after every repository was set
20
+ * up. Written even when there were no such commands, so a consumer can tell
21
+ * "ran nothing" from "never built".
22
+ */
23
+ export declare const WORKSPACE_SETUP_MARKER_PATH = ".mastra-sandbox/workspace-setup";
24
+ /**
25
+ * One line per repository directory whose setup failed while the build was
26
+ * told to continue. Absent when nothing failed. Consumers check this before
27
+ * trusting a per-repository marker: that marker means every step ran, not
28
+ * that every step succeeded.
29
+ */
30
+ export declare const SETUP_FAILED_MARKER_PATH = ".mastra-sandbox/setup-failed";
31
+ /** Per-repository marker of a multi-repository template, keyed by the clone directory name. */
32
+ export declare function repoSetupMarkerPath(repoDir: string): string;
33
+ /** Blank entries never become build steps, so they never count toward the digest either. */
34
+ export declare function normalizeSetupCommands(setupCommand: string | readonly string[] | undefined): string[];
35
+ /** The marker content for a setup command list: `sha256:<hex>` over the commands joined by newlines. */
36
+ export declare function setupMarkerContent(setupCommand: string | readonly string[] | undefined): string;
37
+ /** Shell step that writes the marker relative to the cwd. `content` is a digest, so it is shell-safe. */
38
+ export declare function setupMarkerCommand(content: string, markerPath?: string): string;
39
+ export interface GuardedSetupCommandOptions {
40
+ /** Clone directory the command runs in, relative to the build cwd. Shell-safe (`[\w.-]` only). */
41
+ repoDir: string;
42
+ /** The setup command, run verbatim inside `repoDir`. */
43
+ command: string;
44
+ /** When true a failure appends `repoDir` to {@link SETUP_FAILED_MARKER_PATH} instead of failing the step. */
45
+ continueOnFailure: boolean;
46
+ }
47
+ /**
48
+ * Shell step that runs one setup command inside a repository clone.
49
+ *
50
+ * Without the guard this is the plain `cd "<dir>" && <cmd>` step. With it,
51
+ * the `cd` sits inside the parenthesized group so that the failure branch
52
+ * runs back at the build cwd and the failure list lands at the workspace
53
+ * level, never inside the repository.
54
+ */
55
+ export declare function guardedSetupCommand({ repoDir, command, continueOnFailure }: GuardedSetupCommandOptions): string;
56
+ //# sourceMappingURL=setup-marker.d.ts.map
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -1,7 +1,28 @@
1
+ import type { EntryInfo } from 'e2b';
1
2
  import type { DirectExecOptions, DirectExecResult, ExecLease } from './direct-exec.js';
2
3
  export interface E2BExecLease extends ExecLease {
3
4
  sandboxId: string;
4
5
  }
5
6
  export type E2BExecRunner = (lease: E2BExecLease, options: DirectExecOptions) => Promise<DirectExecResult>;
6
7
  export declare const execViaE2BLease: E2BExecRunner;
8
+ /**
9
+ * The subset of E2B's `sandbox.files` API that {@link PlatformSandboxFilesystem}
10
+ * uses. Tests substitute a fake through `PlatformSandboxOptions.e2bFilesFactory`.
11
+ */
12
+ export interface E2BFiles {
13
+ read(path: string, opts: {
14
+ format: 'bytes';
15
+ }): Promise<Uint8Array>;
16
+ write(path: string, data: string | ArrayBuffer): Promise<unknown>;
17
+ list(path: string, opts?: {
18
+ depth?: number;
19
+ }): Promise<EntryInfo[]>;
20
+ makeDir(path: string): Promise<boolean>;
21
+ rename(oldPath: string, newPath: string): Promise<unknown>;
22
+ remove(path: string): Promise<void>;
23
+ getInfo(path: string): Promise<EntryInfo>;
24
+ }
25
+ export type E2BFilesFactory = (lease: E2BExecLease) => E2BFiles;
26
+ /** Opens E2B's files API directly against the VM, authenticated by the exec lease. */
27
+ export declare const e2bFilesForLease: E2BFilesFactory;
7
28
  //# sourceMappingURL=e2b-exec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"e2b-exec.d.ts","sourceRoot":"","sources":["../src/e2b-exec.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAIvF,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAE3G,eAAO,MAAM,eAAe,EAAE,aAwD7B,CAAC"}
1
+ {"version":3,"file":"e2b-exec.d.ts","sourceRoot":"","sources":["../src/e2b-exec.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAErC,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAIvF,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAE3G,eAAO,MAAM,eAAe,EAAE,aAwD7B,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACnE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACpE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CAC3C;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,QAAQ,CAAC;AAEhE,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,EAAE,eAOrB,CAAC"}