@delta-infra/cli 1.1.25 → 1.1.27

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 CHANGED
@@ -278,13 +278,13 @@ delta-cli sandbox operation <request_id>
278
278
  | `delta-cli sandbox finish <id> [--no-wait]` | 保存结果并销毁;销毁是**服务端任务**,响应含 `request_id`/`operation_status`(`--no-wait` 立即返回 `pending`,断线后任务继续,可用 `sandbox operation <request_id>` 对账) |
279
279
  | `delta-cli sandbox kill <id> [--no-wait]` | 销毁 sandbox(不保存结果);响应含 `request_id`/`operation_status`,中断或网络报错后可用 `sandbox operation` 查询真实终态 |
280
280
  | `delta-cli sandbox operation [request_id] [--sandbox-id <id>] [--operation create\|destroy] [--limit N]` | 查询服务端操作账本:带 `request_id` 查单条,否则按条件列出最近的创建/销毁记录(终态、时间、原因),用于计费与资源对账 |
281
- | `delta-cli sandbox run <id> --command "..." [--timeout <秒>] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` | 同步运行命令。默认(`--format sse`)把 SSE 原始 `data:` 帧透传到 stdout,无末尾信封,`exit_code`/`log_file`/`stdout_tail`/`duration_seconds` 在 `complete` 帧;`--format json`(或 `--json-result`)只输出**一个** JSON 信封,`data` 含 `execution_id`/`finished`/`exit_code`/`stdout_tail`/`stderr_tail`/`stdout_size`/`stderr_size`/`log_file`/`duration_seconds`/`result_summary`,失败时为 `{ok:false,error,data}` 且进程非零退出;`--summary`/`--artifacts` 仅旧服务器回退路径生效 |
282
- | `delta-cli sandbox run-bg <id> --command "..." [--timeout <秒>] [--wait] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` | 后台运行命令;不加 `--wait` 立即返回 `{execution_id, sandbox_id}`(`--format json` 时为统一结果对象,`finished=false`);加 `--wait` 通过 SSE 实时流跟随(默认透传原始 `data:` 帧);`--format json` 时只输出一个与 `sandbox run --format json` 相同结构的信封 |
281
+ | `delta-cli sandbox run <id> --command "..." [--timeout <秒>] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` | 同步运行命令。默认(`--format sse`)把 SSE 原始 `data:` 帧透传到 stdout,无末尾信封,`exit_code`/`log_file`/`stdout_tail`/`duration_seconds` 在 `complete` 帧;`--format json`(或 `--json-result`)只输出**一个** JSON 信封,`data` 含 `execution_id`/`finished`/`exit_code`/`stdout_tail`/`stderr_tail`/`stdout_size`/`stderr_size`/`log_file`/`duration_seconds`/`result_summary`,失败时为 `{ok:false,error,data}` 且进程非零退出;`--summary`/`--artifacts` 仅旧服务器回退路径生效。**退出码语义**:命令非零退出 → `error.type=command_failed`,进程退出码 **8**;命令已结束但**退出状态不可知**(流在 `complete` 帧前中断、或只有 error 帧)→ `error.type=unknown_execution_state`,进程退出码 1,**绝不会合成成功** |
282
+ | `delta-cli sandbox run-bg <id> --command "..." [--timeout <秒>] [--wait] [--cancel-on-timeout] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` | 后台运行命令;不加 `--wait` 立即返回 `{execution_id, sandbox_id}`(`--format json` 时为统一结果对象,`finished=false`);加 `--wait` 通过 SSE 实时流跟随(默认透传原始 `data:` 帧);`--format json` 时只输出一个与 `sandbox run --format json` 相同结构的信封。**等待超时** → `error.type=timeout`、进程**非零退出**,`error.details` 保留 `sandbox_id`/`execution_id`/`remote_state`(`not_cancelled`/`cancelled`/`cancel_failed`)以便事后查询或取消;`remote_state=not_cancelled` 表示**只是本地停止等待,远端命令可能仍在运行**。默认不取消远端命令,加 `--cancel-on-timeout` 才会下发取消(取消失败时报 `cancel_failed`) |
283
283
  | `delta-cli sandbox logs <id> --execution-id <eid> [--tail N --grep <pattern> --context N --max-bytes N]` | 查看后台日志;默认返回 `stderr_size + stderr_tail`(避免上下文爆炸);可用 `--tail/--grep` 过滤 |
284
284
  | `delta-cli sandbox cancel <id> --execution-id <eid>` | 中断后台命令 |
285
- | `delta-cli sandbox read <id> --path <path> [--output <本地路径>] [--tail N] [--grep <pattern>] [--offset N] [--limit N] [--context N] [--max-bytes N] [--parse-json]` | 读取容器内文件;`--output <path>` 保存到本地;`--tail/--grep` 过滤;非 UTF-8 文件 CLI 自动走 base64 fallback |
286
- | `delta-cli sandbox pull <id> --source <沙箱路径> --target <本地路径> [--recursive] [--pattern <glob>]` | 从沙箱拉取文件/目录到本地(mirror of `upload`,flag 方向相反:source=远程,target=本地);单文件或递归目录;CLI 端 + 服务端双向 sha1 完整性校验 |
287
- | `delta-cli sandbox write <id> --path <path> --source <本地路径>` | 写入文件 |
285
+ | `delta-cli sandbox read <id> --path <path> [--output <本地路径>] [--tail N] [--grep <pattern>] [--offset N] [--limit N] [--context N] [--max-bytes N] [--parse-json]` | 读取容器内文件;`--output <path>` 保存到本地;`--tail/--grep` 过滤。**整份拷贝(不带 `--tail/--grep/--offset/--limit/--max-bytes`)走分块下载通道,字节精确**(二进制安全,响应含 `byte_exact:true`/`integrity:"sha256"`/`digest_verified`);带上述过滤参数时走文本通道(服务端按 UTF-8 解码,非法字节会被替换为 U+FFFD),此时响应含 `byte_exact:false`/`integrity:"text_decoded"` 且 stderr 给出警告——**二进制文件请用 `sandbox pull` 拷贝**。**直接输出到 stdout 时**,若文件不是合法 UTF-8,`content` 是有损文本,精确原始字节放在 `content_base64`(base64 解码即为原文件),并标记 `byte_exact:true`;纯文本文件不返回该字段 |
286
+ | `delta-cli sandbox pull <id> --source <沙箱路径> --target <本地路径> [--pattern <glob>] [--concurrency N] [--no-resume] [--no-verify]` | 从沙箱拉取文件/目录到本地(mirror of `upload`,flag 方向相反:source=远程,target=本地)。**目录始终递归**(`--recursive` 已废弃,无需再加);走**分块下载**通道:二进制安全、端到端 **sha256** 校验(响应含 `digest_verified`)、**可断点续传**(`.delta.part`)、多文件/多块**并行**传输(`--concurrency`,默认 4),因此不受单条命令 300s 超时限制。服务端公开的上限随每次响应返回(`limits`):块大小 4 MiB(最大 16 MiB)、单文件最大 100 GiB、每块超时 120s、整文件摘要超时 600s。`--no-resume` 忽略残留的 `.delta.part`,`--no-verify` 跳过沙箱侧 sha256 比对 |
287
+ | `delta-cli sandbox write <id> --path <path> --source <本地路径>` | 写入文件。**二进制安全**:内容按 base64 传输,CLI 同时声明 `size`/`sha256`,服务端在落盘前核对,不匹配直接拒绝;响应含 `server_sha256`/`sha256`/`size`/`size_verified`。超过 32 MiB 的 `--source` 走流式上传通道,不整份读入内存 |
288
288
  | `delta-cli sandbox write-multiple <id> --entry <远程路径>=<本地路径>` | 批量写入 |
289
289
  | `delta-cli sandbox ls <id> --path <path>` | 列出目录 |
290
290
  | `delta-cli sandbox stat <id> --path <path>` | 文件元数据 |
package/checksums.txt CHANGED
@@ -1,5 +1,5 @@
1
- c5f6872c10ed7c875b9a9bdad77ba52a1a25b0c5696311e7eac4b02ec1f473a9 delta-cli-darwin-amd64.tar.gz
2
- 9553a81ec84f8a48e6f0963b294bc6da7630a20f9da01acb89eb151f8009003d delta-cli-darwin-arm64.tar.gz
3
- 37193622609e8175418565168f22014d876d91fe26d20e1cfa2f9c012ef6595d delta-cli-linux-amd64.tar.gz
4
- 0fe68346288688c10a7be32e84243bb18fccfcd5766f359388590373a016664f delta-cli-linux-arm64.tar.gz
5
- f059b6c9dd9c58afc38c3c52421d9ce039e786d4be26a6fc605dade7787ff0bc delta-cli-windows-amd64.zip
1
+ 7ea661fb3ded3ea488d109627f3ea77fc66338dbf3afea0d26e057dbb2c1fee9 delta-cli-darwin-amd64.tar.gz
2
+ d77d563f5afd01f9ba455eadae9364088593c9da85409a14478778683386c5d2 delta-cli-darwin-arm64.tar.gz
3
+ 6cb42b875bb8ca6fccc0c644a734a07fb70f638946f5011c7be3f14e373eee80 delta-cli-linux-amd64.tar.gz
4
+ 6e5cab67adedc7626f8580bf73d1368141165ae5aee6d475c601004ebdddc2c2 delta-cli-linux-arm64.tar.gz
5
+ 7ee96731fd4d367e9fe83616295459b461a1885a34a8fc5744b100c1d815d9a0 delta-cli-windows-amd64.zip
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delta-infra/cli",
3
- "version": "1.1.25",
3
+ "version": "1.1.27",
4
4
  "description": "Delta Agent Cloud CLI for sandbox compute and science tools.",
5
5
  "bin": {
6
6
  "delta-cli": "scripts/run.js"
@@ -101,14 +101,14 @@ metadata:
101
101
 
102
102
  13. **`result.json` is required before completion**
103
103
  - Before calling `step_finish`, confirm that:
104
- 1. `delta-cli sandbox run` / `run-bg --wait` has returned with `finished=true`;
104
+ 1. `delta-cli sandbox run` / `run-bg --wait` has returned with `finished=true` **and `exit_code` = 0**. `finished=true` alone is not a success signal — a command that fails still finishes. A non-zero exit is reported as `error.type: command_failed` (process exit code 8), an **unknown** exit status as `error.type: unknown_execution_state` (exit code 1), and a wait timeout as `error.type: timeout` (non-zero exit, with `sandbox_id`/`execution_id` in `error.details`); in all three cases the task has **not** succeeded — report the failure instead of claiming a result;
105
105
  2. (By default) the `result_summary` in the SSE `complete` frame from `sandbox run` / `run-bg --wait` has been written to `result.json`; if it is missing (fallback), read `log_file` with `sandbox read` and parse it with Python.
106
106
  3. `sandbox kill <id>` has been run to destroy the sandbox;
107
107
  4. `final_response` consists of the single line `RESULT: <result_summary>`.
108
108
 
109
109
  14. **Avoid redundant checks and validation**
110
110
  - If `skill_request` or the user's request already specifies the image and resources, **do not** call `sandbox images` or `sandbox recommend` before `sandbox create`.
111
- - After writing a file to the sandbox, it is enough to use `sandbox stat` (optionally) to confirm the size. **Do not** use `sandbox ls` plus `sandbox read` to read the file back to the host and verify its contents; that is an unnecessary round trip.
111
+ - After writing a file to the sandbox, it is enough to use `sandbox stat` (optionally) to confirm the size. **Do not** use `sandbox ls` plus `sandbox read` to read the file back to the host and verify its contents; that is an unnecessary round trip. (The `sandbox write` response already carries `server_sha256` and `size_verified`, so a local digest comparison is enough if the bytes matter.)
112
112
  - Do not repeatedly poll with `sandbox status` after a successful `sandbox create`; the sandbox is ready as soon as create returns.
113
113
 
114
114
  ## Request wording conventions
@@ -165,8 +165,8 @@ The reason is that, after running a command, this skill reads `log_file` from th
165
165
  | Finish a sandbox | `sandbox finish <id> [--results '{...}']` |
166
166
  | Destroy a sandbox | `sandbox kill <id>` |
167
167
  | **Command execution** | |
168
- | Run a command synchronously (≤60s) | `sandbox run <id> --command "..." [--timeout <seconds>] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` (`--format json` = a single unified result envelope, recommended for automation) |
169
- | Run a command in the background (>60s) | `sandbox run-bg <id> --command "..." [--timeout <seconds>] [--wait] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` (`--wait --format json` = a single unified result envelope) |
168
+ | Run a command synchronously (≤60s) | `sandbox run <id> --command "..." [--timeout <seconds>] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` (`--format json` = a single unified result envelope, recommended for automation). **A non-zero exit is reported as `error.type: command_failed` with process exit code 8; an unknown exit status is reported as `error.type: unknown_execution_state` with exit code 1 — the CLI never reports an unknown outcome as success** |
169
+ | Run a command in the background (>60s) | `sandbox run-bg <id> --command "..." [--timeout <seconds>] [--wait] [--cancel-on-timeout] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` (`--wait --format json` = a single unified result envelope). **If the wait times out the CLI exits non-zero with `error.type: timeout` and keeps `sandbox_id`/`execution_id`/`remote_state` in `error.details`; `remote_state: not_cancelled` means only the local wait stopped and the remote command may still be running — add `--cancel-on-timeout` to cancel it** |
170
170
  | View background logs | `sandbox logs <id> [--execution-id <exec_id>] [--tail N --grep <pattern> --context N] [--stream]` (`--execution-id` is optional; if omitted, query ES by sandbox only; only `--stream` requires it) |
171
171
  | Interrupt a background command | `sandbox cancel <id> --execution-id <exec_id>` |
172
172
  | **Tunnelling (port exposure)** | |
@@ -174,10 +174,10 @@ The reason is that, after running a command, this skill reads `log_file` from th
174
174
  | List active tunnels | `sandbox exposes <id>` — list the public tunnels currently active for this sandbox |
175
175
  | Reclaim a tunnel | `sandbox dispose <id> --port N` — explicitly reclaim a single tunnel (stop frpc and release the public port); killing the sandbox also reclaims all its tunnels |
176
176
  | **File operations** | |
177
- | Read a file | `sandbox read <id> --path <path> [--output <local-path> --tail N --grep <pattern> --offset N --limit N --context N --max-bytes N --parse-json]` |
178
- | Pull a file/directory | `sandbox pull <id> --source <sandbox-path> --target <local-path> [--recursive] [--pattern <glob>]` — pull a single file or recursively pull a directory, with sha1 verification (the reverse of upload: source=remote, target=local) |
177
+ | Read a file | `sandbox read <id> --path <path> [--output <local-path> --tail N --grep <pattern> --offset N --limit N --context N --max-bytes N --parse-json]` — **a full copy is byte-exact** (chunked download, sha256-verified); with `--tail`/`--grep`/`--offset`/`--limit` it uses the text channel and the envelope reports `byte_exact:false` — use `sandbox pull` for binary files. Printed inline, a non-UTF-8 file puts the **exact bytes** in `content_base64` (`content` is the lossy text) |
178
+ | Pull a file/directory | `sandbox pull <id> --source <sandbox-path> --target <local-path> [--pattern <glob>] [--concurrency N] [--no-resume] [--no-verify]` — pull a single file or **recursively** pull a directory (directories are always recursive), over the **chunked** path: binary-safe, sha256-verified, resumable and parallel (the reverse of upload: source=remote, target=local) |
179
179
  | View the working directory | `sandbox working-directory <id>` (returns `data.path`, such as `/workspace/{user_id}/{sandbox_id}`) |
180
- | Write a file | `sandbox write <id> --source <filename>` (without `--path`, writes to the working directory by default); **every sandbox-side path** — `--path`, the remote part of `write-multiple --entry`, `upload --target`, `pull --source`, `run`/`run-bg --log-file` — is prefixed with the working directory when relative, while an absolute path is used as given |
180
+ | Write a file | `sandbox write <id> --source <filename>` (without `--path`, writes to the working directory by default); **every sandbox-side path** — `--path`, the remote part of `write-multiple --entry`, `upload --target`, `pull --source`, `run`/`run-bg --log-file` — is prefixed with the working directory when relative, while an absolute path is used as given. The content is **binary-safe** (base64) and the server verifies the declared `size`/`sha256` before writing, so the response's `server_sha256`/`size_verified` can be compared with the local file |
181
181
  | Write multiple files | `sandbox write-multiple <id> --entry <remote-path>=<local-path> [--entry ...]` |
182
182
  | Upload a directory | `sandbox upload <id> --source <local-directory> --target <sandbox-path>` |
183
183
  | List a directory | `sandbox ls <id> --path <path>` (defaults to `.`) |
@@ -29,8 +29,8 @@
29
29
 
30
30
  | Command | Description |
31
31
  |------|------|
32
- | `sandbox run <id> --command "..." [--timeout N] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` | Run a command synchronously (live SSE stream; raw `data:` frames are passed through to stdout one by one; **there is no trailing envelope on success**; the CLI fills in `exit_code`/`execution_id`/`log_file`/`stdout`/`stderr`/`stdout_tail`/`stderr_tail`/`stdout_size`/`stderr_size`/`duration_seconds`/`result_summary` in the `complete` frame; **if the command exits non-zero or errors, an `error.type: command_failed` envelope is appended and the process exits non-zero**; the envelope's `message` includes the stderr tail, as does the message in a `type:"error"` frame on a best-effort basis). `--format json` (same as `--json-result`) emits no frames, only one envelope whose `data` is the unified result object above; on failure it is `{ok:false,error,data}`. `--summary`/`--artifacts` take effect only on the legacy-server fallback path (when there is no SSE endpoint and a `CommandResult` envelope is returned). |
33
- | `sandbox run-bg <id> --command "..." [--timeout N] [--wait] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` | Run a command in the background. Without `--wait`, return `{execution_id, sandbox_id}` immediately (with `--format json`, the unified result object with `finished=false`). With `--wait`, follow the live SSE stream until `complete` (raw `data:` frames are passed through to stdout; the CLI fills in `log_file`/`stdout`/`stderr`/`result_summary` in the `complete` frame; **on command failure, a `command_failed` envelope is appended and the process exits non-zero**; the envelope's `message` includes the stderr tail; on timeout, a `finished=false` snapshot envelope is appended). With `--format json`, emit only one envelope with the same structure as `sandbox run --format json`. `--summary`/`--artifacts` take effect only on the legacy-server fallback path. |
32
+ | `sandbox run <id> --command "..." [--timeout N] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` | Run a command synchronously (live SSE stream; raw `data:` frames are passed through to stdout one by one; **there is no trailing envelope on success**; the CLI fills in `exit_code`/`execution_id`/`log_file`/`stdout`/`stderr`/`stdout_tail`/`stderr_tail`/`stdout_size`/`stderr_size`/`duration_seconds`/`result_summary` in the `complete` frame; **if the command exits non-zero or errors, an `error.type: command_failed` envelope is appended and the process exits non-zero (exit code 8)**; if the command finished but its **exit status is unknown** (the stream ended before the `complete` frame, or only an error frame arrived), the envelope is `error.type: unknown_execution_state` with exit code 1 — the CLI **never synthesizes a success** in that case; the envelope's `message` includes the stderr tail, as does the message in a `type:"error"` frame on a best-effort basis). `--format json` (same as `--json-result`) emits no frames, only one envelope whose `data` is the unified result object above; on failure it is `{ok:false,error,data}`. `--summary`/`--artifacts` take effect only on the legacy-server fallback path (when there is no SSE endpoint and a `CommandResult` envelope is returned). |
33
+ | `sandbox run-bg <id> --command "..." [--timeout N] [--wait] [--cancel-on-timeout] [--format sse\|json] [--json-result] [--summary/--no-summary] [--artifacts]` | Run a command in the background. Without `--wait`, return `{execution_id, sandbox_id}` immediately (with `--format json`, the unified result object with `finished=false`). With `--wait`, follow the live SSE stream until `complete` (raw `data:` frames are passed through to stdout; the CLI fills in `log_file`/`stdout`/`stderr`/`result_summary` in the `complete` frame; **on command failure, a `command_failed` envelope is appended and the process exits non-zero (exit code 8)**; the envelope's `message` includes the stderr tail). **When the wait times out, the CLI exits non-zero with `error.type: timeout`** and `error.details` carrying `sandbox_id`, `execution_id` and `remote_state` (`not_cancelled` / `cancelled` / `cancel_failed`) so the command can be queried or cancelled afterwards; `remote_state: not_cancelled` means only the local wait stopped and **the remote command may still be running**. The remote command is left running by default — pass `--cancel-on-timeout` to cancel it (a failed cancel is reported as `cancel_failed`). With `--format json`, emit only one envelope with the same structure as `sandbox run --format json`. `--summary`/`--artifacts` take effect only on the legacy-server fallback path. |
34
34
  | `sandbox logs <id> --execution-id <eid> [--tail N --grep <pattern> --context N] [--stream]` | Retrieve background-command logs. **By default, returns `stdout_tail` (last 800 bytes) + `stderr_tail` (last 200 bytes) + `stderr_size` + `cursor`**, about 1 KB in total, to keep context small. `--stream` follows the live `/logs/stream` SSE endpoint, passing raw `data:` frames through to stdout until `complete` (the CLI fills in `stdout`/`stderr`/`log_file`/`result_summary` in the complete frame; there is no trailing envelope). `--tail N` applies to both stdout and stderr (last N lines from each); `--grep <pattern>` filters both stdout and stderr (regular expression); `--context N` includes N lines before and after each grep match (for both stdout and stderr). The `cursor` field has no `omitempty` and is always present, even when 0. Snapshots include `last_updated` (filled by the CLI on the SSE path; the server snapshot path is awaiting a server-side update). **If `--execution-id` is omitted, query by sandbox** (the server uses ES); the response is an **array** grouped by execution_id (each item contains `execution_id`/`stdout`/`stderr`/`exit_code`/`command`/`finished`/`error`/`log_file`), and the CLI applies the same tail/grep trimming to each item. |
35
35
  | `sandbox cancel <id> --execution-id <eid>` | Interrupt a background command that is running. |
36
36
 
@@ -38,12 +38,12 @@
38
38
 
39
39
  | Command | Description |
40
40
  |------|------|
41
- | `sandbox read <id> --path <path> [--output <local-path>] [--tail N] [--grep <pattern>] [--offset N] [--limit N] [--context N] [--max-bytes N] [--parse-json]` | Read a file; returns `content` + `size` + `content_length`. `--output <path>` saves it locally; `--tail`/`--grep` filter the output. For non-UTF-8 files, the CLI automatically falls back to base64. |
41
+ | `sandbox read <id> --path <path> [--output <local-path>] [--tail N] [--grep <pattern>] [--offset N] [--limit N] [--context N] [--max-bytes N] [--parse-json]` | Read a file; returns `content` + `size` + `content_length`. `--output <path>` saves it locally; `--tail`/`--grep` filter the output. **A full copy (no `--tail`/`--grep`/`--offset`/`--limit`/`--max-bytes`) is byte-exact**: it goes through the chunked download path (binary-safe, sha256-verified — the envelope reports `byte_exact:true`, `integrity:"sha256"`, `digest_verified`). With any range filter it falls back to the text channel, where the server decodes as UTF-8 and replaces invalid bytes with U+FFFD; the envelope then reports `byte_exact:false` / `integrity:"text_decoded"` and a warning is printed to stderr. **Use `sandbox pull` to copy a binary file.** When the result is printed inline and the file is not valid UTF-8, `content` is the lossy text and the **exact original bytes** are in `content_base64` (base64-decode it); the envelope is marked `byte_exact:true`. Text files carry no `content_base64`. |
42
42
  | `sandbox working-directory <id>` | Get the sandbox's current working directory (absolute path `/workspace/{user_id}/{sandbox_id}`). |
43
- | `sandbox write <id> [--path <path>] --source <filename>` | Write a file (recommended). If `--path` is omitted, write to the working directory (`<working-directory>/<filename>`); a relative `--path` is also resolved under the working directory. An absolute `--path` is used as-is (but the `/workspace/<filename>` root path is outside the OSS sync scope and is not recommended). |
43
+ | `sandbox write <id> [--path <path>] --source <filename>` | Write a file (recommended). If `--path` is omitted, write to the working directory (`<working-directory>/<filename>`); a relative `--path` is also resolved under the working directory. An absolute `--path` is used as-is (but the `/workspace/<filename>` root path is outside the OSS sync scope and is not recommended). **Binary-safe**: the content travels as base64 and the CLI declares its `size`/`sha256`, which the server verifies before writing (a mismatch is rejected). The response carries `server_sha256`/`sha256`/`size`/`size_verified`. |
44
44
  | `sandbox write <id> --path <absolute-path> --data "..."` | Write a small amount of inline content (`--data` has no source filename, so `--path` must be specified explicitly). |
45
45
  | `sandbox write-multiple <id> --entry <remote-path>=<local-path> [--entry ...]` | Write multiple files in one operation (a relative target `path` is also resolved under the working directory). |
46
- | `sandbox pull <id> --source <sandbox-path> --target <local-path> [--recursive] [--pattern <glob>]` | Pull files/directories to the local machine (the reverse of `upload`; flag directions are reversed: `--source`=remote sandbox path, `--target`=local path). Single files are detected automatically; directories are traversed recursively by default; SHA-1 integrity is checked by both the CLI and server. |
46
+ | `sandbox pull <id> --source <sandbox-path> --target <local-path> [--pattern <glob>] [--concurrency N] [--no-resume] [--no-verify]` | Pull files/directories to the local machine (the reverse of `upload`; flag directions are reversed: `--source`=remote sandbox path, `--target`=local path). Single files are detected automatically; **directories are always traversed recursively** (`--recursive` is deprecated and no longer needed). The transfer uses the **chunked download** path: binary-safe, end-to-end **sha256** verified (`digest_verified`), **resumable** (a partial `<target>.delta.part` is reused), and **parallel** across files and chunks (`--concurrency`, default 4) — so a large file is not bound by a single command timeout. The server publishes its limits with every response (`limits`): 4 MiB chunks (16 MiB max), 100 GiB max per file, 120 s per chunk, 600 s for the whole-file digest. `--no-resume` ignores a leftover `.delta.part`; `--no-verify` skips the sandbox-side sha256 comparison. |
47
47
  | `sandbox upload <id> --source <local-directory> --target <sandbox-path>` | Upload a local directory (packaged as tar.gz, extracted server-side) and verify the resulting file list against the local manifest. `--source` is local, `--target` is inside the sandbox. |
48
48
  | `sandbox ls <id> --path <path>` | List directory contents (defaults to `.`). |
49
49
  | `sandbox stat <id> --path <path>` | Get file metadata (size / mode / owner / group). |
@@ -35,7 +35,7 @@ The CLI follows the live SSE stream (`/logs/stream`) and passes raw SSE frames (
35
35
 
36
36
  > On the SSE path, `--wait` puts `result_summary` directly in the **complete frame** (if trailing JSON is detected), so there is no need to scan backwards with `sandbox read <id> --path <log_file>`. Read the log_file manually only if no valid JSON is found at the end. `--summary`/`--artifacts` enrichment (including hints) still takes effect only on the legacy-server fallback path.
37
37
 
38
- > **Output contract (SSE path):** On success, `run-bg --wait` writes only raw SSE frames to stdout, with **no trailing envelope**. On command failure, it appends an `{"ok":false,"error":{"type":"command_failed",...}}` envelope after the complete frame. If the command has not finished within `--timeout`, it appends a `finished=false` `CommandLogsResult` snapshot envelope. For an older server (without the `/logs/stream` endpoint), the CLI falls back to polling every 5s and outputs a `CommandResult` envelope (the `finished` key is always present; when `finished=true`, it includes `result_summary`/`hints` enrichment). **For automation, add `--format json` (same as `--json-result`)**: it emits no frames, only one envelope whose `data` contains `execution_id`/`finished`/`exit_code`/`stdout_tail`/`stderr_tail`/`stdout_size`/`stderr_size`/`log_file`/`duration_seconds`/`result_summary`; on failure it is `{ok:false,error,data}` and the process exits non-zero. When those fields are missing from the `complete` frame, the CLI fills them from the accumulated stream, so the field semantics match `--format json` exactly.
38
+ > **Output contract (SSE path):** On success, `run-bg --wait` writes only raw SSE frames to stdout, with **no trailing envelope**. On command failure, it appends an `{"ok":false,"error":{"type":"command_failed",...}}` envelope after the complete frame and exits non-zero (process exit code 8). If the command has not finished within `--timeout`, the CLI exits **non-zero** with `{"ok":false,"error":{"type":"timeout",...}}`; `error.details` carries `sandbox_id`, `execution_id` and `remote_state` so the command can be queried or cancelled afterwards (`remote_state: not_cancelled` means only the local wait stopped and the remote command may still be running — pass `--cancel-on-timeout` to cancel it). For an older server (without the `/logs/stream` endpoint), the CLI falls back to polling every 5s and outputs a `CommandResult` envelope (the `finished` key is always present; when `finished=true`, it includes `result_summary`/`hints` enrichment). **For automation, add `--format json` (same as `--json-result`)**: it emits no frames, only one envelope whose `data` contains `execution_id`/`finished`/`exit_code`/`stdout_tail`/`stderr_tail`/`stdout_size`/`stderr_size`/`log_file`/`duration_seconds`/`result_summary`; on failure it is `{ok:false,error,data}` and the process exits non-zero. When those fields are missing from the `complete` frame, the CLI fills them from the accumulated stream, so the field semantics match `--format json` exactly.
39
39
 
40
40
  ### Method 2: poll manually (when you need to see live output)
41
41
 
@@ -107,5 +107,5 @@ Interpret task status using these rules, in order:
107
107
  **Notes:**
108
108
  - Use `sandbox logs` only with `sandbox run-bg`; synchronous `sandbox run` returns its result directly, so `logs` cannot and should not be called for it.
109
109
  - Without `--wait`, the `sandbox run-bg` HTTP request itself returns quickly with `{execution_id, sandbox_id}`. If the command times out, it exits with a non-zero code; retrieve `exit_code` later with `logs`.
110
- - In `sandbox run-bg --wait` mode, `--timeout` serves **both** as the server-side command timeout and the client-side waiting deadline (default 300s). The CLI follows the live SSE stream (`/logs/stream`), passes raw `data:` frames through to stdout and ends when `complete` arrives (with no trailing envelope); on timeout, it appends a `finished=false` snapshot envelope. The initial `/run-background` POST has its own 60s HTTP timeout.
110
+ - In `sandbox run-bg --wait` mode, `--timeout` serves **both** as the server-side command timeout and the client-side waiting deadline (default 300s). The CLI follows the live SSE stream (`/logs/stream`), passes raw `data:` frames through to stdout and ends when `complete` arrives (with no trailing envelope); on timeout it exits **non-zero** with `error.type: timeout` and keeps `sandbox_id`/`execution_id`/`remote_state` in `error.details` (add `--cancel-on-timeout` to also cancel the remote command). The initial `/run-background` POST has its own 60s HTTP timeout.
111
111
  - Background commands can be queried across tool-call turns: save `sandbox_id` and `execution_id`, then retrieve results in later turns with `sandbox status` / `sandbox logs`.
@@ -76,7 +76,7 @@ This returns the current user's information and remaining credits (through the g
76
76
 
77
77
  AI agent parsing rules:
78
78
  - Always check `ok`; do not rely on the exit code alone.
79
- - `error.type` is the stable field for branching on: `auth`, `permission`, `not_found`, `validation`, `network`, `api`, `command_failed`, `internal`.
79
+ - `error.type` is the stable field for branching on: `auth`, `permission`, `not_found`, `validation`, `network`, `api`, `command_failed`, `timeout`, `unknown_execution_state`, `internal`.
80
80
  - `error.hint` may be shown to the user. **Act on it rather than retrying blindly**: for a failed `sandbox create` the hint names the concrete cause and the next step (for example, the image was not cached on the target node — retry, or pick a GPU type whose node already has the image).
81
81
  - `error.request_id` identifies the same failure in the server log; quote it when reporting the failure.
82
82
 
@@ -91,6 +91,8 @@ AI agent parsing rules:
91
91
  | network | 6 | DNS, timeout or connection failure |
92
92
  | api | 7 | Server returned a non-2xx response |
93
93
  | command_failed | 8 | Command failed inside the sandbox (non-zero `exit_code` or an error); on the SSE path, an error envelope is appended after the `complete` frame, with the stderr tail in `message` for quick reference |
94
+ | timeout | 1 | A local wait deadline elapsed (`sandbox run-bg --wait`): the remote command may still be running — `error.details` keeps `sandbox_id`, `execution_id` and `remote_state` (`not_cancelled` / `cancelled` / `cancel_failed`) so it can be queried or cancelled |
95
+ | unknown_execution_state | 1 | The command's outcome is **unknown** (the stream ended before the `complete` frame, or only an error frame arrived). Never treat this as success and never guess the exit code |
94
96
  | internal | 10 | Internal client error |
95
97
 
96
98
  ## Upgrade check
@@ -111,5 +113,7 @@ delta-cli upgrade
111
113
  | `error.type: network` | Check `base_url` and network connectivity |
112
114
  | `error.type: validation` | Check required flags and argument formats |
113
115
  | `error.type: command_failed` | Command failed inside the sandbox: the envelope's `message` already includes the stderr tail to help locate the issue quickly; the full `stderr` / `result_summary` are in the `complete` frame. Retry or correct the command |
116
+ | `error.type: timeout` | A local wait deadline elapsed, **not** a command failure. Read `error.details.execution_id` and query it with `sandbox logs <id> --execution-id <exec_id>`; cancel the still-running command with `sandbox cancel <id> --execution-id <exec_id>` (or re-run with `--cancel-on-timeout` so the CLI cancels it for you) |
117
+ | `error.type: unknown_execution_state` | The remote outcome is unknown — the CLI will not guess. Re-check the command with `sandbox logs <id> --execution-id <exec_id>` (the execution id is in the error details) before deciding whether it succeeded |
114
118
  | `sandbox create` failed (`error.type: api` with a specific `hint`) | Follow the hint — it names the cause and the next step. "the image may not be cached on the target node" → retry once (the pull continues in the background, so later creates succeed) or pick a GPU type whose node already has the image. "check the target node's free disk space" → a retry will not help until an operator frees the node. A failed create leaves no sandbox behind, so retrying is safe. Quote `error.request_id` when reporting it. |
115
119
  | Configuration file parsing failed | Check that `~/.delta-infra/config.json` contains valid JSON |