@mastra/e2b 0.14.2-alpha.0 → 0.15.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.
- package/dist/_types/@internal_workspace/dist/file-commands.d.ts +69 -0
- package/dist/_types/@internal_workspace/dist/files-api-filesystem.d.ts +85 -0
- package/dist/_types/@internal_workspace/dist/index.d.ts +5 -0
- package/dist/_types/@internal_workspace/dist/repo-clone.d.ts +22 -0
- package/dist/_types/@internal_workspace/dist/setup-marker.d.ts +56 -0
- package/dist/_types/@internal_workspace/package.json +1 -0
- package/dist/index.cjs +535 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +537 -93
- package/dist/index.js.map +1 -1
- package/dist/sandbox/filesystem.d.ts +31 -0
- package/dist/sandbox/filesystem.d.ts.map +1 -0
- package/dist/sandbox/index.d.ts +8 -0
- package/dist/sandbox/index.d.ts.map +1 -1
- package/package.json +7 -8
|
@@ -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"}
|