Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 26 additions & 11 deletions docs/file-system.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -122,22 +122,13 @@ Batch upload accepts `application/x-tar` and extracts into the destination direc
<CodeGroup>
```ts TypeScript
import { SandboxAgent } from "sandbox-agent";
import fs from "node:fs";
import path from "node:path";
import tar from "tar";

const sdk = await SandboxAgent.connect({
baseUrl: "http://127.0.0.1:2468",
});

const archivePath = path.join(process.cwd(), "skills.tar");
await tar.c({
cwd: "./skills",
file: archivePath,
}, ["."]);

const tarBuffer = await fs.promises.readFile(archivePath);
const result = await sdk.uploadFsBatch(tarBuffer, {
// Requires `tar` to be installed (it's an optional peer dependency).
const result = await sdk.uploadFsBatch({ sourcePath: "./skills" }, {
path: "./skills",
});

Expand All @@ -152,3 +143,27 @@ curl -X POST "http://127.0.0.1:2468/v1/fs/upload-batch?path=./skills" \
--data-binary @skills.tar
```
</CodeGroup>

## Batch download (tar)

Batch download returns `application/x-tar` bytes for a file or directory. If the path is a directory,
the archive contains the directory contents (similar to `tar -C <dir> .`).

<CodeGroup>
```ts TypeScript
import { SandboxAgent } from "sandbox-agent";

const sdk = await SandboxAgent.connect({
baseUrl: "http://127.0.0.1:2468",
});

// Requires `tar` to be installed if you want to extract (it's an optional peer dependency).
await sdk.downloadFsBatch({ path: "./skills" }, { outPath: "./skills.tar" });
await sdk.downloadFsBatch({ path: "./skills" }, { extractTo: "./skills-extracted" });
```

```bash cURL
curl -X GET "http://127.0.0.1:2468/v1/fs/download-batch?path=./skills" \
--output ./skills.tar
```
</CodeGroup>
36 changes: 36 additions & 0 deletions docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,33 @@
}
}
},
"/v1/fs/download-batch": {
"get": {
"tags": [
"v1"
],
"summary": "Download a tar archive of a file or directory.",
"description": "Returns `application/x-tar` bytes containing the requested path. If the path is a directory,\nthe archive contains its contents (similar to `tar -C <dir> .`).",
"operationId": "get_v1_fs_download_batch",
"parameters": [
{
"name": "path",
"in": "query",
"description": "Source path (file or directory)",
"required": false,
"schema": {
"type": "string",
"nullable": true
}
}
],
"responses": {
"200": {
"description": "tar archive bytes"
}
}
}
},
"/v1/fs/entries": {
"get": {
"tags": [
Expand Down Expand Up @@ -1267,6 +1294,15 @@
}
}
},
"FsDownloadBatchQuery": {
"type": "object",
"properties": {
"path": {
"type": "string",
"nullable": true
}
}
},
"FsEntriesQuery": {
"type": "object",
"properties": {
Expand Down
28 changes: 19 additions & 9 deletions examples/file-system/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { SandboxAgent } from "sandbox-agent";
import { detectAgent, buildInspectorUrl } from "@sandbox-agent/example-shared";
import { startDockerSandbox } from "@sandbox-agent/example-shared/docker";
import * as tar from "tar";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
Expand All @@ -23,14 +22,9 @@ console.log(" Created 3 files in my-project/");
console.log("Uploading files via batch tar...");
const client = await SandboxAgent.connect({ baseUrl });

const tarPath = path.join(tmpDir, "upload.tar");
await tar.create(
{ file: tarPath, cwd: tmpDir },
["my-project"],
);
const tarBuffer = await fs.promises.readFile(tarPath);
const uploadResult = await client.uploadFsBatch(tarBuffer, { path: "/opt" });
console.log(` Uploaded ${uploadResult.paths.length} files: ${uploadResult.paths.join(", ")}`);
// Requires `tar` to be installed (optional peer dependency of `sandbox-agent`).
const uploadResult = await client.uploadFsBatch({ sourcePath: projectDir }, { path: "/opt/my-project" });
console.log(` Uploaded ${uploadResult.paths.length} entries: ${uploadResult.paths.join(", ")}`);

// Cleanup temp files
fs.rmSync(tmpDir, { recursive: true, force: true });
Expand All @@ -46,6 +40,22 @@ const readmeBytes = await client.readFsFile({ path: "/opt/my-project/README.md"
const readmeText = new TextDecoder().decode(readmeBytes);
console.log(` README.md content: ${readmeText.trim()}`);

console.log("Downloading the uploaded project via batch tar...");
const downloadTmp = path.resolve(__dirname, "../.tmp-download");
fs.rmSync(downloadTmp, { recursive: true, force: true });
fs.mkdirSync(downloadTmp, { recursive: true });
await client.downloadFsBatch(
{ path: "/opt/my-project" },
{ outPath: path.join(downloadTmp, "my-project.tar"), extractTo: downloadTmp },
);
console.log(` Extracted to: ${downloadTmp}`);
for (const entry of fs.readdirSync(downloadTmp)) {
if (entry.endsWith(".tar")) {
continue;
}
console.log(` ${entry}`);
}

console.log("Creating session...");
const session = await client.createSession({ agent: detectAgent(), sessionInit: { cwd: "/opt/my-project", mcpServers: [] } });
const sessionId = session.id;
Expand Down
Loading
Loading