@hasna/sandboxes 0.1.26 → 0.1.28
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 +23 -0
- package/dist/cli/index.js +29 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +77 -1
- package/dist/lib/archive.d.ts +2 -0
- package/dist/mcp/index.js +29 -1
- package/dist/sdk.d.ts +33 -0
- package/dist/server/index.js +29 -1
- package/dist/types/index.d.ts +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,29 @@ npm install -g @hasna/sandboxes
|
|
|
17
17
|
sandboxes --help
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
## SDK One-shot Commands
|
|
21
|
+
|
|
22
|
+
Use the SDK to create a sandbox, upload a local project, run a command, and clean up:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { createSandboxesSDK } from "@hasna/sandboxes";
|
|
26
|
+
|
|
27
|
+
const sandboxes = createSandboxesSDK();
|
|
28
|
+
|
|
29
|
+
await sandboxes.runCommandInSandbox({
|
|
30
|
+
provider: "e2b",
|
|
31
|
+
command: "bun test",
|
|
32
|
+
upload: {
|
|
33
|
+
localDir: process.cwd(),
|
|
34
|
+
remoteDir: "/workspace/app",
|
|
35
|
+
syncStrategy: "rsync",
|
|
36
|
+
},
|
|
37
|
+
cleanup: "delete",
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Set `E2B_API_KEY` for E2B-backed runs. `syncStrategy: "rsync"` mirrors the local directory into a temporary staging tree with `rsync` before uploading it through the provider file APIs.
|
|
42
|
+
|
|
20
43
|
## MCP Server
|
|
21
44
|
|
|
22
45
|
```bash
|
package/dist/cli/index.js
CHANGED
|
@@ -12060,7 +12060,9 @@ var init_config2 = __esm(() => {
|
|
|
12060
12060
|
});
|
|
12061
12061
|
|
|
12062
12062
|
// src/lib/archive.ts
|
|
12063
|
-
import { existsSync as existsSync7, statSync } from "fs";
|
|
12063
|
+
import { existsSync as existsSync7, mkdtempSync, rmSync, statSync } from "fs";
|
|
12064
|
+
import { tmpdir } from "os";
|
|
12065
|
+
import { join as join8 } from "path";
|
|
12064
12066
|
function shellQuote(value) {
|
|
12065
12067
|
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
12066
12068
|
}
|
|
@@ -12068,6 +12070,15 @@ async function tarDirectory(localDir, opts) {
|
|
|
12068
12070
|
if (!existsSync7(localDir) || !statSync(localDir).isDirectory()) {
|
|
12069
12071
|
throw new Error(`tarDirectory: not a directory: ${localDir}`);
|
|
12070
12072
|
}
|
|
12073
|
+
if (opts?.syncStrategy === "rsync") {
|
|
12074
|
+
const stagingDir = mkdtempSync(join8(tmpdir(), "sandboxes-rsync-"));
|
|
12075
|
+
try {
|
|
12076
|
+
await rsyncDirectory(localDir, stagingDir, opts.exclude ?? DEFAULT_UPLOAD_EXCLUDES);
|
|
12077
|
+
return await tarDirectory(stagingDir, { exclude: [], syncStrategy: "archive" });
|
|
12078
|
+
} finally {
|
|
12079
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
12080
|
+
}
|
|
12081
|
+
}
|
|
12071
12082
|
const excludes = opts?.exclude ?? DEFAULT_UPLOAD_EXCLUDES;
|
|
12072
12083
|
const args = ["-czf", "-"];
|
|
12073
12084
|
for (const ex of excludes)
|
|
@@ -12084,6 +12095,23 @@ async function tarDirectory(localDir, opts) {
|
|
|
12084
12095
|
}
|
|
12085
12096
|
return Buffer.from(buf);
|
|
12086
12097
|
}
|
|
12098
|
+
async function rsyncDirectory(localDir, stagingDir, excludes) {
|
|
12099
|
+
const args = [
|
|
12100
|
+
"-a",
|
|
12101
|
+
"--delete",
|
|
12102
|
+
...excludes.flatMap((ex) => ["--exclude", ex]),
|
|
12103
|
+
`${localDir.replace(/\/+$/, "")}/`,
|
|
12104
|
+
`${stagingDir.replace(/\/+$/, "")}/`
|
|
12105
|
+
];
|
|
12106
|
+
const proc = Bun.spawn(["rsync", ...args], { stdout: "pipe", stderr: "pipe" });
|
|
12107
|
+
const [stderr, exitCode] = await Promise.all([
|
|
12108
|
+
new Response(proc.stderr).text(),
|
|
12109
|
+
proc.exited
|
|
12110
|
+
]);
|
|
12111
|
+
if (exitCode !== 0) {
|
|
12112
|
+
throw new Error(`rsyncDirectory: rsync exited ${exitCode}: ${stderr.trim()}`);
|
|
12113
|
+
}
|
|
12114
|
+
}
|
|
12087
12115
|
function buildUntarCommand(remoteTarPath, remoteDir) {
|
|
12088
12116
|
const tar = shellQuote(remoteTarPath);
|
|
12089
12117
|
const dir = shellQuote(remoteDir);
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export { BUILTIN_IMAGES, resolveImage, getBuiltinImageSetupScript } from "./lib/
|
|
|
15
15
|
export { getProvider } from "./providers/index.js";
|
|
16
16
|
export type { SandboxProvider, ProviderSandbox, CreateSandboxOpts, ExecOptions } from "./providers/types.js";
|
|
17
17
|
export { SandboxesSDK, createSandboxesSDK, } from "./sdk.js";
|
|
18
|
-
export type { ExecCommandResult, ProviderFactory, RunAgentOptions, SandboxesSDKOptions, WaitForSessionOptions, } from "./sdk.js";
|
|
18
|
+
export type { ExecCommandResult, ProviderFactory, RunAgentOptions, RunCommandInSandboxOptions, RunCommandInSandboxResult, RunCommandInSandboxUploadOptions, SandboxesSDKOptions, OneShotSandboxCleanup, WaitForSessionOptions, } from "./sdk.js";
|
|
19
19
|
export { createStreamCollector, addStreamListener, emitLifecycleEvent } from "./lib/stream.js";
|
|
20
20
|
export { getAgentDriver, listAgentDrivers } from "./lib/agents/index.js";
|
|
21
21
|
export type { AgentDriver } from "./lib/agents/types.js";
|
package/dist/index.js
CHANGED
|
@@ -88,7 +88,9 @@ var init_types = __esm(() => {
|
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
// src/lib/archive.ts
|
|
91
|
-
import { existsSync as existsSync7, statSync } from "fs";
|
|
91
|
+
import { existsSync as existsSync7, mkdtempSync, rmSync, statSync } from "fs";
|
|
92
|
+
import { tmpdir } from "os";
|
|
93
|
+
import { join as join8 } from "path";
|
|
92
94
|
function shellQuote(value) {
|
|
93
95
|
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
94
96
|
}
|
|
@@ -96,6 +98,15 @@ async function tarDirectory(localDir, opts) {
|
|
|
96
98
|
if (!existsSync7(localDir) || !statSync(localDir).isDirectory()) {
|
|
97
99
|
throw new Error(`tarDirectory: not a directory: ${localDir}`);
|
|
98
100
|
}
|
|
101
|
+
if (opts?.syncStrategy === "rsync") {
|
|
102
|
+
const stagingDir = mkdtempSync(join8(tmpdir(), "sandboxes-rsync-"));
|
|
103
|
+
try {
|
|
104
|
+
await rsyncDirectory(localDir, stagingDir, opts.exclude ?? DEFAULT_UPLOAD_EXCLUDES);
|
|
105
|
+
return await tarDirectory(stagingDir, { exclude: [], syncStrategy: "archive" });
|
|
106
|
+
} finally {
|
|
107
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
99
110
|
const excludes = opts?.exclude ?? DEFAULT_UPLOAD_EXCLUDES;
|
|
100
111
|
const args = ["-czf", "-"];
|
|
101
112
|
for (const ex of excludes)
|
|
@@ -112,6 +123,23 @@ async function tarDirectory(localDir, opts) {
|
|
|
112
123
|
}
|
|
113
124
|
return Buffer.from(buf);
|
|
114
125
|
}
|
|
126
|
+
async function rsyncDirectory(localDir, stagingDir, excludes) {
|
|
127
|
+
const args = [
|
|
128
|
+
"-a",
|
|
129
|
+
"--delete",
|
|
130
|
+
...excludes.flatMap((ex) => ["--exclude", ex]),
|
|
131
|
+
`${localDir.replace(/\/+$/, "")}/`,
|
|
132
|
+
`${stagingDir.replace(/\/+$/, "")}/`
|
|
133
|
+
];
|
|
134
|
+
const proc = Bun.spawn(["rsync", ...args], { stdout: "pipe", stderr: "pipe" });
|
|
135
|
+
const [stderr, exitCode] = await Promise.all([
|
|
136
|
+
new Response(proc.stderr).text(),
|
|
137
|
+
proc.exited
|
|
138
|
+
]);
|
|
139
|
+
if (exitCode !== 0) {
|
|
140
|
+
throw new Error(`rsyncDirectory: rsync exited ${exitCode}: ${stderr.trim()}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
115
143
|
function buildUntarCommand(remoteTarPath, remoteDir) {
|
|
116
144
|
const tar = shellQuote(remoteTarPath);
|
|
117
145
|
const dir = shellQuote(remoteDir);
|
|
@@ -11643,6 +11671,54 @@ class SandboxesSDK {
|
|
|
11643
11671
|
emitLifecycleEvent(sandbox.id, `uploaded ${localDir} -> ${remoteDir} (${result.bytes} bytes)`);
|
|
11644
11672
|
return result;
|
|
11645
11673
|
}
|
|
11674
|
+
async runCommandInSandbox(input) {
|
|
11675
|
+
const cleanupMode = input.cleanup ?? "delete";
|
|
11676
|
+
const sandbox = await this.createSandbox({
|
|
11677
|
+
provider: input.provider,
|
|
11678
|
+
name: input.name,
|
|
11679
|
+
image: input.image,
|
|
11680
|
+
timeout: input.sandboxTimeout,
|
|
11681
|
+
env_vars: input.sandboxEnvVars,
|
|
11682
|
+
config: input.config,
|
|
11683
|
+
project_id: input.projectId
|
|
11684
|
+
});
|
|
11685
|
+
let upload;
|
|
11686
|
+
let exec;
|
|
11687
|
+
try {
|
|
11688
|
+
if (input.upload) {
|
|
11689
|
+
const uploadOptions = {};
|
|
11690
|
+
if (input.upload.exclude !== undefined)
|
|
11691
|
+
uploadOptions.exclude = input.upload.exclude;
|
|
11692
|
+
if (input.upload.syncStrategy !== undefined)
|
|
11693
|
+
uploadOptions.syncStrategy = input.upload.syncStrategy;
|
|
11694
|
+
upload = await this.uploadDir(sandbox.id, input.upload.localDir, input.upload.remoteDir, uploadOptions);
|
|
11695
|
+
}
|
|
11696
|
+
exec = await this.execCommand(sandbox.id, input.command, {
|
|
11697
|
+
cwd: input.cwd ?? input.upload?.remoteDir,
|
|
11698
|
+
env: input.callEnvVars,
|
|
11699
|
+
timeout: input.commandTimeoutMs,
|
|
11700
|
+
onStdout: input.onStdout,
|
|
11701
|
+
onStderr: input.onStderr
|
|
11702
|
+
});
|
|
11703
|
+
} finally {
|
|
11704
|
+
if (cleanupMode === "delete") {
|
|
11705
|
+
await this.deleteSandbox(sandbox.id);
|
|
11706
|
+
} else if (cleanupMode === "stop") {
|
|
11707
|
+
await this.stopSandbox(sandbox.id);
|
|
11708
|
+
}
|
|
11709
|
+
}
|
|
11710
|
+
if (!exec) {
|
|
11711
|
+
throw new Error("Sandbox command did not produce an execution result");
|
|
11712
|
+
}
|
|
11713
|
+
return {
|
|
11714
|
+
sandbox,
|
|
11715
|
+
session: exec.session,
|
|
11716
|
+
result: exec.result,
|
|
11717
|
+
upload,
|
|
11718
|
+
remoteDir: input.upload?.remoteDir,
|
|
11719
|
+
cleanup: cleanupMode === "delete" ? "deleted" : cleanupMode === "stop" ? "stopped" : "kept"
|
|
11720
|
+
};
|
|
11721
|
+
}
|
|
11646
11722
|
async runAgent(sandboxId, opts) {
|
|
11647
11723
|
const sandbox = this.requireProviderSandbox(sandboxId);
|
|
11648
11724
|
const provider = await this.getProvider(sandbox.provider);
|
package/dist/lib/archive.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface TarDirectoryOptions {
|
|
|
11
11
|
* {@link DEFAULT_UPLOAD_EXCLUDES}; pass `[]` to include everything.
|
|
12
12
|
*/
|
|
13
13
|
exclude?: string[];
|
|
14
|
+
/** Prepare a temporary upload tree with rsync before creating the archive. */
|
|
15
|
+
syncStrategy?: "archive" | "rsync";
|
|
14
16
|
}
|
|
15
17
|
/** Single-quote a value for safe POSIX shell interpolation. */
|
|
16
18
|
export declare function shellQuote(value: string): string;
|
package/dist/mcp/index.js
CHANGED
|
@@ -61,7 +61,9 @@ var init_types2 = __esm(() => {
|
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
// src/lib/archive.ts
|
|
64
|
-
import { existsSync as existsSync7, statSync } from "fs";
|
|
64
|
+
import { existsSync as existsSync7, mkdtempSync, rmSync, statSync } from "fs";
|
|
65
|
+
import { tmpdir } from "os";
|
|
66
|
+
import { join as join8 } from "path";
|
|
65
67
|
function shellQuote(value) {
|
|
66
68
|
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
67
69
|
}
|
|
@@ -69,6 +71,15 @@ async function tarDirectory(localDir, opts) {
|
|
|
69
71
|
if (!existsSync7(localDir) || !statSync(localDir).isDirectory()) {
|
|
70
72
|
throw new Error(`tarDirectory: not a directory: ${localDir}`);
|
|
71
73
|
}
|
|
74
|
+
if (opts?.syncStrategy === "rsync") {
|
|
75
|
+
const stagingDir = mkdtempSync(join8(tmpdir(), "sandboxes-rsync-"));
|
|
76
|
+
try {
|
|
77
|
+
await rsyncDirectory(localDir, stagingDir, opts.exclude ?? DEFAULT_UPLOAD_EXCLUDES);
|
|
78
|
+
return await tarDirectory(stagingDir, { exclude: [], syncStrategy: "archive" });
|
|
79
|
+
} finally {
|
|
80
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
72
83
|
const excludes = opts?.exclude ?? DEFAULT_UPLOAD_EXCLUDES;
|
|
73
84
|
const args = ["-czf", "-"];
|
|
74
85
|
for (const ex of excludes)
|
|
@@ -85,6 +96,23 @@ async function tarDirectory(localDir, opts) {
|
|
|
85
96
|
}
|
|
86
97
|
return Buffer.from(buf);
|
|
87
98
|
}
|
|
99
|
+
async function rsyncDirectory(localDir, stagingDir, excludes) {
|
|
100
|
+
const args = [
|
|
101
|
+
"-a",
|
|
102
|
+
"--delete",
|
|
103
|
+
...excludes.flatMap((ex) => ["--exclude", ex]),
|
|
104
|
+
`${localDir.replace(/\/+$/, "")}/`,
|
|
105
|
+
`${stagingDir.replace(/\/+$/, "")}/`
|
|
106
|
+
];
|
|
107
|
+
const proc = Bun.spawn(["rsync", ...args], { stdout: "pipe", stderr: "pipe" });
|
|
108
|
+
const [stderr, exitCode] = await Promise.all([
|
|
109
|
+
new Response(proc.stderr).text(),
|
|
110
|
+
proc.exited
|
|
111
|
+
]);
|
|
112
|
+
if (exitCode !== 0) {
|
|
113
|
+
throw new Error(`rsyncDirectory: rsync exited ${exitCode}: ${stderr.trim()}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
88
116
|
function buildUntarCommand(remoteTarPath, remoteDir) {
|
|
89
117
|
const tar = shellQuote(remoteTarPath);
|
|
90
118
|
const dir = shellQuote(remoteDir);
|
package/dist/sdk.d.ts
CHANGED
|
@@ -15,6 +15,38 @@ export interface ExecCommandResult {
|
|
|
15
15
|
session: SandboxSession;
|
|
16
16
|
result: ExecResult;
|
|
17
17
|
}
|
|
18
|
+
export type OneShotSandboxCleanup = "delete" | "stop" | "keep";
|
|
19
|
+
export interface RunCommandInSandboxUploadOptions {
|
|
20
|
+
localDir: string;
|
|
21
|
+
remoteDir: string;
|
|
22
|
+
exclude?: string[];
|
|
23
|
+
syncStrategy?: "archive" | "rsync";
|
|
24
|
+
}
|
|
25
|
+
export interface RunCommandInSandboxOptions {
|
|
26
|
+
command: string;
|
|
27
|
+
provider?: SandboxProviderName;
|
|
28
|
+
name?: string;
|
|
29
|
+
image?: string;
|
|
30
|
+
sandboxTimeout?: number;
|
|
31
|
+
commandTimeoutMs?: number;
|
|
32
|
+
projectId?: string;
|
|
33
|
+
config?: Record<string, unknown>;
|
|
34
|
+
sandboxEnvVars?: Record<string, string>;
|
|
35
|
+
callEnvVars?: Record<string, string>;
|
|
36
|
+
cwd?: string;
|
|
37
|
+
upload?: RunCommandInSandboxUploadOptions;
|
|
38
|
+
cleanup?: OneShotSandboxCleanup;
|
|
39
|
+
onStdout?: (data: string) => void;
|
|
40
|
+
onStderr?: (data: string) => void;
|
|
41
|
+
}
|
|
42
|
+
export interface RunCommandInSandboxResult {
|
|
43
|
+
sandbox: Sandbox;
|
|
44
|
+
session: SandboxSession;
|
|
45
|
+
result: ExecResult;
|
|
46
|
+
upload?: UploadDirResult;
|
|
47
|
+
remoteDir?: string;
|
|
48
|
+
cleanup: "deleted" | "stopped" | "kept";
|
|
49
|
+
}
|
|
18
50
|
export interface RunAgentOptions {
|
|
19
51
|
agentType: AgentType;
|
|
20
52
|
prompt: string;
|
|
@@ -54,6 +86,7 @@ export declare class SandboxesSDK {
|
|
|
54
86
|
glob?: string;
|
|
55
87
|
}): Promise<FileInfo[]>;
|
|
56
88
|
uploadDir(sandboxId: string, localDir: string, remoteDir: string, opts?: UploadDirOptions): Promise<UploadDirResult>;
|
|
89
|
+
runCommandInSandbox(input: RunCommandInSandboxOptions): Promise<RunCommandInSandboxResult>;
|
|
57
90
|
runAgent(sandboxId: string, opts: RunAgentOptions): Promise<SandboxSession>;
|
|
58
91
|
getSession(sessionId: string): SandboxSession;
|
|
59
92
|
waitForSession(sessionId: string, opts?: WaitForSessionOptions): Promise<SandboxSession>;
|
package/dist/server/index.js
CHANGED
|
@@ -97,7 +97,9 @@ var init_types2 = __esm(() => {
|
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
// src/lib/archive.ts
|
|
100
|
-
import { existsSync as existsSync7, statSync } from "fs";
|
|
100
|
+
import { existsSync as existsSync7, mkdtempSync, rmSync, statSync } from "fs";
|
|
101
|
+
import { tmpdir } from "os";
|
|
102
|
+
import { join as join8 } from "path";
|
|
101
103
|
function shellQuote(value) {
|
|
102
104
|
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
103
105
|
}
|
|
@@ -105,6 +107,15 @@ async function tarDirectory(localDir, opts) {
|
|
|
105
107
|
if (!existsSync7(localDir) || !statSync(localDir).isDirectory()) {
|
|
106
108
|
throw new Error(`tarDirectory: not a directory: ${localDir}`);
|
|
107
109
|
}
|
|
110
|
+
if (opts?.syncStrategy === "rsync") {
|
|
111
|
+
const stagingDir = mkdtempSync(join8(tmpdir(), "sandboxes-rsync-"));
|
|
112
|
+
try {
|
|
113
|
+
await rsyncDirectory(localDir, stagingDir, opts.exclude ?? DEFAULT_UPLOAD_EXCLUDES);
|
|
114
|
+
return await tarDirectory(stagingDir, { exclude: [], syncStrategy: "archive" });
|
|
115
|
+
} finally {
|
|
116
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
108
119
|
const excludes = opts?.exclude ?? DEFAULT_UPLOAD_EXCLUDES;
|
|
109
120
|
const args = ["-czf", "-"];
|
|
110
121
|
for (const ex of excludes)
|
|
@@ -121,6 +132,23 @@ async function tarDirectory(localDir, opts) {
|
|
|
121
132
|
}
|
|
122
133
|
return Buffer.from(buf);
|
|
123
134
|
}
|
|
135
|
+
async function rsyncDirectory(localDir, stagingDir, excludes) {
|
|
136
|
+
const args = [
|
|
137
|
+
"-a",
|
|
138
|
+
"--delete",
|
|
139
|
+
...excludes.flatMap((ex) => ["--exclude", ex]),
|
|
140
|
+
`${localDir.replace(/\/+$/, "")}/`,
|
|
141
|
+
`${stagingDir.replace(/\/+$/, "")}/`
|
|
142
|
+
];
|
|
143
|
+
const proc = Bun.spawn(["rsync", ...args], { stdout: "pipe", stderr: "pipe" });
|
|
144
|
+
const [stderr, exitCode] = await Promise.all([
|
|
145
|
+
new Response(proc.stderr).text(),
|
|
146
|
+
proc.exited
|
|
147
|
+
]);
|
|
148
|
+
if (exitCode !== 0) {
|
|
149
|
+
throw new Error(`rsyncDirectory: rsync exited ${exitCode}: ${stderr.trim()}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
124
152
|
function buildUntarCommand(remoteTarPath, remoteDir) {
|
|
125
153
|
const tar = shellQuote(remoteTarPath);
|
|
126
154
|
const dir = shellQuote(remoteDir);
|
package/dist/types/index.d.ts
CHANGED
|
@@ -179,6 +179,12 @@ export interface FileInfo {
|
|
|
179
179
|
export interface UploadDirOptions {
|
|
180
180
|
/** Patterns to exclude (passed to `tar --exclude`); defaults applied by the archiver. */
|
|
181
181
|
exclude?: string[];
|
|
182
|
+
/**
|
|
183
|
+
* How to prepare the upload payload. `archive` tars the source directory
|
|
184
|
+
* directly; `rsync` first mirrors the source into a temporary staging
|
|
185
|
+
* directory with rsync, then uploads the staged tree.
|
|
186
|
+
*/
|
|
187
|
+
syncStrategy?: "archive" | "rsync";
|
|
182
188
|
}
|
|
183
189
|
export interface UploadDirResult {
|
|
184
190
|
/** Number of bytes uploaded (compressed archive size). */
|