fix(e2b): harden remote adapter boundaries

This commit is contained in:
Tianyi Cui
2026-07-28 18:31:37 +08:00
parent dc44f93c39
commit 3e343b4477
33 changed files with 939 additions and 146 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/e2b/fs-e2b/README.md
README.md: d8f3915fa281e8b9c062e3412c92bc6358616284
README.zh.md: 93d27bcdd514ccbf87e07d0eff5958edf3c726c1
README.md: a505703fc764f8441fa54d2b1d922762eb3fafdf
README.zh.md: 626fdb52979d29f12d1bc1b67b0ea13730003af7

View File

@@ -8,7 +8,7 @@ E2B implementation of the [`@deepseek-ai/dsh-fs`](../../fs/fs/README.md) provide
- **Remote identity and metadata** — relative paths resolve as POSIX paths against the caller cwd or `ctx.e2b.cwd`; `realpath -m` supplies canonical target identity without requiring the final file to exist. `stat`, no-follow `lstat`, and stable one-level directory listings project E2B metadata into the filesystem seam. Versions are opaque hashes of E2B metadata plus a per-write extended attribute.
- **UTF-8 reads** — whole reads and streamed reads preserve cross-chunk decoding, reject invalid UTF-8, and use the seam's 8192-byte NUL sample for binary detection. The model-facing tool still owns size selection and line windowing.
- **Atomic mutations** — writes upload a mode-`0600` temporary sibling, preserve an existing file's POSIX mode, and publish through same-directory Linux `mv -f`. E2B creates missing parent directories. Literal edits LF-normalize for matching, restore dominant CRLF storage, and serialize mutations per canonical target within the host process. Optional create/version guards keep the base seam's observed-state semantics.
- **Atomic mutations** — writes upload a mode-`0600` temporary sibling, preserve an existing file's POSIX mode, and publish through E2B's same-directory atomic rename. The rename response supplies the committed version, so no fallible metadata request follows the commit point. E2B creates missing parent directories. Literal edits LF-normalize for matching, restore dominant CRLF storage, and serialize mutations per canonical target within the host process. Optional create/version guards keep the base seam's observed-state semantics.
- **Failures and cancellation** — E2B not-found, permission, abort, and other controller failures map to the existing `FsError` vocabulary. Cancellation is best-effort at SDK request boundaries; a successful rename is the commit point.
The provider does not copy, mount, or reconcile the host workspace. Giving it a host path as `cwd` creates a remote directory with the same spelling only.

View File

@@ -8,7 +8,7 @@
- **远程身份与元数据**:相对路径以调用方 cwd 或 `ctx.e2b.cwd` 为基准,按照 POSIX 路径解析;`realpath -m` 提供规范化目标身份,且不要求最终文件存在。`stat`、不跟随链接的 `lstat` 和稳定的单层目录列表会把 E2B 元数据投影到文件系统 seam。版本是 E2B 元数据与每次写入设置的扩展属性所组成的不透明哈希。
- **UTF-8 读取**:完整读取和流式读取会保留跨分片解码、拒绝无效 UTF-8并使用 seam 的 8192 字节 NUL 样本检测二进制内容。面向模型的工具仍负责选择大小和行窗口。
- **原子变更**:写入会上传 mode 为 `0600` 的同级临时文件,保留现有文件的 POSIX mode并通过同目录 Linux `mv -f` 发布。E2B 会创建缺失的父目录。字面量编辑匹配时会规范化为 LF存储时恢复占主导的 CRLF并在宿主进程内按规范化目标串行执行变更。可选的创建版本防护会保留基础 seam 的已观察状态语义。
- **原子变更**:写入会上传 mode 为 `0600` 的同级临时文件,保留现有文件的 POSIX mode并通过 E2B 的同目录原子重命名发布。重命名响应会提供已提交的版本,因此提交点之后不会再进行可能失败的元数据请求。E2B 会创建缺失的父目录。字面量编辑匹配时会规范化为 LF存储时恢复占主导的 CRLF并在宿主进程内按规范化目标串行执行变更。可选的创建版本防护会保留基础 seam 的已观察状态语义。
- **失败与取消**E2B 的未找到、权限、中止及其他控制器故障会映射到现有 `FsError` 词汇。取消在 SDK 请求边界上采用尽力而为语义;成功 rename 是提交点。
该提供方不会复制、挂载或协调宿主工作区。把宿主路径用作 `cwd`,只会在远程创建一个拼写相同的目录。

View File

@@ -412,11 +412,7 @@ export class E2BFileSystem extends FileSystem {
signalOpts(signal),
)
assertNotAborted(signal, 'write')
await sandbox.commands.run(
`mv -f -- ${quoteE2BShellArg(temporary)} ${quoteE2BShellArg(targetPath)}`,
signalOpts(signal),
)
const committed = await sandbox.files.getInfo(targetPath)
const committed = await sandbox.files.rename(temporary, targetPath, signalOpts(signal))
return entryVersion(committed)
} catch (error: unknown) {
try {

View File

@@ -453,6 +453,17 @@ describe('E2BFileSystem atomic writes and edits', () => {
expect(controller.signal.aborted).toBe(true)
})
it('returns committed rename metadata without a fallible post-commit lookup', async () => {
const remote = new FakeRemote()
const getInfo = vi.spyOn(remote.sandbox.files, 'getInfo')
const { fs } = await setup(remote)
await expect(fs.writeText(await fs.resolve('committed'), 'yes'))
.resolves.toMatchObject({ operation: 'create' })
expect(getInfo).toHaveBeenCalledTimes(1)
expect(remote.renames).toHaveLength(1)
})
it('cleans staging files and maps command, permission, and abort failures', async () => {
const remote = new FakeRemote()
const { fs } = await setup(remote)