@delta-infra/cli 1.0.84 → 1.0.86

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
@@ -170,13 +170,19 @@ delta-cli sandbox create \
170
170
  --cpu 4 --memory 16Gi --gpu 1 \
171
171
  --max-life 120
172
172
 
173
+ # 需要长期存活、不被自动清理的 sandbox:加 --no-auto-cleanup(仅显式 kill/finish 可销毁)
174
+ delta-cli sandbox create \
175
+ --image image.yangtzeailab.com/opensandbox/pytorch-cuda13:latest \
176
+ --cpu 4 --memory 16Gi --gpu 1 \
177
+ --no-auto-cleanup
178
+
173
179
  # 运行命令(短任务同步执行,长任务用 run-bg 替代 run)
174
180
  delta-cli sandbox run <sandbox_id> \
175
181
  --command "<命令>" \
176
182
  --timeout 3600
177
183
 
178
- # v1.0.55+ 默认带 --summary,返回 JSON 中 data.result_summary 字段已含 stdout 末尾 JSON 提取结果
179
- # 无需再手动读取 log_file 做二次解析(除非使用了 --no-summary)
184
+ # v1.0.85+ sandbox run 走 SSE 实时流:stdout 为原始 data: 帧(无末尾信封),exit_code/log_file 在 complete 帧中
185
+ # 需要 stdout 末尾 JSON 摘要时:读取 log_file 解析(sandbox read <id> --path <log_file>)
180
186
  delta-cli sandbox run <sandbox_id> --command "echo '{\"status\":\"ok\",\"result\":\"done\"}'" --timeout 60
181
187
 
182
188
  # 读取结果文件
@@ -214,13 +220,13 @@ delta-cli sandbox kill <sandbox_id>
214
220
  | `delta-cli sandbox providers` | 查看可用计算后端 |
215
221
  | `delta-cli sandbox images` | 查看可用镜像列表 |
216
222
  | `delta-cli sandbox recommend --cpu N --memory XGi` | 获取资源配置推荐 |
217
- | `delta-cli sandbox create --image <img>` | 创建 sandbox 容器 |
223
+ | `delta-cli sandbox create --image <img> [--no-auto-cleanup]` | 创建 sandbox 容器(`--no-auto-cleanup` 不被自动清理) |
218
224
  | `delta-cli sandbox connect <id>` | 连接 sandbox |
219
225
  | `delta-cli sandbox status <id>` | 查看 sandbox 状态 |
220
226
  | `delta-cli sandbox finish <id>` | 保存结果并销毁 |
221
227
  | `delta-cli sandbox kill <id>` | 销毁 sandbox |
222
- | `delta-cli sandbox run <id> --command "..." [--timeout <秒>] [--summary/--no-summary] [--artifacts]` | 同步运行命令;默认带 `--summary`,返回 JSON 中 `summary` 字段已含 stdout 末尾 JSON 提取结果;`--artifacts` 附带 workspace 产物清单 |
223
- | `delta-cli sandbox run-bg <id> --command "..." [--timeout <秒>] [--wait] [--summary/--no-summary] [--artifacts]` | 后台运行命令;不加 `--wait` 立即返回 `{execution_id, sandbox_id}`;加 `--wait` 等待完成返回含 `summary` 的完整结果 |
228
+ | `delta-cli sandbox run <id> --command "..." [--timeout <秒>] [--summary/--no-summary] [--artifacts]` | 同步运行命令(SSE 原始 `data:` 帧透传到 stdout,无末尾信封,`exit_code`/`log_file` 在 `complete` 帧);`--summary`/`--artifacts` 仅旧服务器回退路径生效 |
229
+ | `delta-cli sandbox run-bg <id> --command "..." [--timeout <秒>] [--wait] [--summary/--no-summary] [--artifacts]` | 后台运行命令;不加 `--wait` 立即返回 `{execution_id, sandbox_id}`;加 `--wait` 通过 SSE 实时流跟随(原始 `data:` 帧透传到 stdout,无末尾信封) |
224
230
  | `delta-cli sandbox logs <id> --execution-id <eid> [--tail N --grep <pattern> --context N --max-bytes N]` | 查看后台日志;默认返回 `stderr_size + stderr_tail`(避免上下文爆炸);可用 `--tail/--grep` 过滤 |
225
231
  | `delta-cli sandbox cancel <id> --execution-id <eid>` | 中断后台命令 |
226
232
  | `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 |
package/checksums.txt CHANGED
@@ -1,5 +1,5 @@
1
- 667417d5358bf7dacbb8a79ec15e045c79205f48df9f8155b294a387fc5ecfc1 delta-cli-darwin-amd64.tar.gz
2
- 30d4e295329b41fa56e244d74e8a2bfacdcb546486065bc829e4532aa52a1eeb delta-cli-darwin-arm64.tar.gz
3
- bd2922359a6be1289bff1e0c8050ec01a444b376f0681124cbfd5c39e5af7580 delta-cli-linux-amd64.tar.gz
4
- 3ba46f7c5470d92d8b59825e639b66f207482746c45d8cd34a95ba6a89e0b6df delta-cli-linux-arm64.tar.gz
5
- f0426b06da61dfdffc079fa5e5e52437285c840855197240d55a49246d23e835 delta-cli-windows-amd64.zip
1
+ eb73ffbfea1fdbdd770af7e1f6ee270b0ba53751c229521b0c15bae5768e7b2d delta-cli-darwin-amd64.tar.gz
2
+ ab05c3261cfae0f186dfa26d692ebc79724186ae1ad2e24ada3c725628489606 delta-cli-darwin-arm64.tar.gz
3
+ 5ff9b72bd530a5d5974a1ca22735849332548fc785dd20d5619c8b30017a4673 delta-cli-linux-amd64.tar.gz
4
+ 05922276dd519711d23aa9233b3bff560119f6803c9b9db1dcd9ee5d6070bd05 delta-cli-linux-arm64.tar.gz
5
+ b9ee55fe69a421b58f4706ae579b3db663d55625bb4990f444b984dee5ec73a2 delta-cli-windows-amd64.zip
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delta-infra/cli",
3
- "version": "1.0.84",
3
+ "version": "1.0.86",
4
4
  "description": "Delta Infra CLI for sandbox compute and science tools.",
5
5
  "bin": {
6
6
  "delta-cli": "scripts/run.js"
@@ -45,7 +45,67 @@ const PLATFORM_LABELS = {
45
45
  const skillManifest = require("../skills/manifest.json");
46
46
  const SKILL_NAMES = skillManifest.skills;
47
47
  const RETIRED_SKILL_NAMES = skillManifest.retired_skills || [];
48
- const LANG = "zh";
48
+
49
+ // Language for interactive prompts and log text. Detection precedence:
50
+ // DELTA_CLI_LANG (explicit override) > OS locale env (LANG/LC_ALL/LANGUAGE/
51
+ // LC_MESSAGES) > platform probe (Windows UI culture via PowerShell) >
52
+ // Intl default locale. Only zh and en are supported; anything not Chinese
53
+ // falls back to English. Neutral locales (C / POSIX / und) carry no language
54
+ // signal and resolve to English instead of leaking the runtime's default.
55
+ //
56
+ // Windows does not set the POSIX LANG/LC_ALL vars and Node's Intl defaults to
57
+ // en-US there, so a Chinese Windows user would otherwise see English. We probe
58
+ // the real UI culture on that platform instead.
59
+ function detectLang() {
60
+ if (process.env.DELTA_CLI_LANG) {
61
+ return process.env.DELTA_CLI_LANG.toLowerCase().startsWith("zh") ? "zh" : "en";
62
+ }
63
+ const neutral = /^(c|posix|und)(\b|$)/i;
64
+ const candidates = [
65
+ process.env.LC_ALL,
66
+ process.env.LC_MESSAGES,
67
+ process.env.LANG,
68
+ process.env.LANGUAGE,
69
+ ];
70
+ let sawLocale = false;
71
+ for (const value of candidates) {
72
+ if (!value) continue;
73
+ sawLocale = true;
74
+ const lower = value.toLowerCase();
75
+ if (neutral.test(value)) return "en";
76
+ if (lower.startsWith("zh")) return "zh";
77
+ return "en";
78
+ }
79
+ if (sawLocale) return "en";
80
+ if (isWindows) {
81
+ const win = windowsUiLanguage();
82
+ if (win) return win;
83
+ }
84
+ try {
85
+ if (String(Intl.DateTimeFormat().resolvedOptions().locale).toLowerCase().startsWith("zh")) {
86
+ return "zh";
87
+ }
88
+ } catch {}
89
+ return "en";
90
+ }
91
+
92
+ // Returns "zh" or "en" from the Windows UI culture, or "" when it cannot be read.
93
+ function windowsUiLanguage() {
94
+ try {
95
+ const out = execFileSync(
96
+ "powershell.exe",
97
+ ["-NoProfile", "-Command",
98
+ "[System.Globalization.CultureInfo]::CurrentUICulture.Name"],
99
+ { stdio: ["ignore", "pipe", "ignore"], encoding: "utf8", timeout: 8000 }
100
+ );
101
+ const name = (out || "").trim().toLowerCase();
102
+ if (name.startsWith("zh")) return "zh";
103
+ if (name) return "en";
104
+ } catch {}
105
+ return "";
106
+ }
107
+
108
+ const LANG = detectLang();
49
109
 
50
110
  function platformSkillDir(platform) {
51
111
  return path.join(osHomedir(), ...PLATFORM_PATHS[platform]);
@@ -83,6 +143,10 @@ const messages = {
83
143
  done: "安装完成!\n现在可以让你的 AI 工具使用 delta-cli 管理 Delta Sandbox 资源或调用 Science 工具。",
84
144
  cancelled: "安装已取消",
85
145
  nonTtyHint: "要完成配置,请在终端中运行:\n delta-cli config init\n delta-cli auth login",
146
+ uninstallConfirm: "将移除全局包、AI Skills 和配置文件。是否继续?",
147
+ uninstallCancelled: "卸载已取消",
148
+ uninstallDone: "卸载完成",
149
+ uninstallComplete: "卸载完成。",
86
150
  },
87
151
  en: {
88
152
  setup: "Setting up Delta Infra CLI...",
@@ -113,6 +177,10 @@ const messages = {
113
177
  done: "You are all set!\nYour AI tool can now use delta-cli to manage Delta Sandbox resources or invoke Science tools.",
114
178
  cancelled: "Installation cancelled",
115
179
  nonTtyHint: "To complete setup, run interactively:\n delta-cli config init\n delta-cli auth login",
180
+ uninstallConfirm: "This will remove the global package, AI Skills and config. Continue?",
181
+ uninstallCancelled: "Uninstall cancelled",
182
+ uninstallDone: "Uninstall complete",
183
+ uninstallComplete: "Uninstall complete.",
116
184
  },
117
185
  };
118
186
 
@@ -565,33 +633,35 @@ async function stepUninstallPackage(msg) {
565
633
 
566
634
  async function doInstall() {
567
635
  const isInteractive = isInteractiveEnv();
636
+ const msg = messages[LANG];
568
637
 
569
638
  if (isInteractive) {
570
- p.intro(messages.zh.setup);
571
- await stepInstallGlobally(messages.zh);
572
- const platforms = await stepSelectPlatforms(messages.zh);
573
- await stepInstallSkills(messages.zh, platforms);
574
- await stepConfigInit(messages.zh);
575
- await stepAuthLogin(messages.zh);
576
- p.outro(messages.zh.done);
639
+ p.intro(msg.setup);
640
+ await stepInstallGlobally(msg);
641
+ const platforms = await stepSelectPlatforms(msg);
642
+ await stepInstallSkills(msg, platforms);
643
+ await stepConfigInit(msg);
644
+ await stepAuthLogin(msg);
645
+ p.outro(msg.done);
577
646
  } else {
578
- console.log(messages.zh.setup);
579
- await stepInstallGlobally(messages.zh);
647
+ console.log(msg.setup);
648
+ await stepInstallGlobally(msg);
580
649
  const platforms = ["agents"];
581
- await stepInstallSkills(messages.zh, platforms);
582
- console.log(messages.zh.nonTtyHint);
650
+ await stepInstallSkills(msg, platforms);
651
+ console.log(msg.nonTtyHint);
583
652
  }
584
653
  }
585
654
 
586
655
  async function doUninstall() {
587
656
  const isInteractive = isInteractiveEnv();
657
+ const msg = messages[LANG];
588
658
 
589
659
  if (isInteractive) {
590
660
  const ok = await p.confirm({
591
- message: "将移除全局包、AI Skills 和配置文件。是否继续?",
661
+ message: msg.uninstallConfirm,
592
662
  });
593
663
  if (p.isCancel(ok) || !ok) {
594
- p.cancel("卸载已取消");
664
+ p.cancel(msg.uninstallCancelled);
595
665
  process.exit(0);
596
666
  }
597
667
  }
@@ -601,9 +671,9 @@ async function doUninstall() {
601
671
  await stepUninstallPackage();
602
672
 
603
673
  if (isInteractive) {
604
- p.outro("卸载完成");
674
+ p.outro(msg.uninstallDone);
605
675
  } else {
606
- console.log("\nUninstall complete.");
676
+ console.log("\n" + msg.uninstallComplete);
607
677
  }
608
678
  }
609
679
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: delta-sandbox
3
- description: "在 Delta Sandbox Linux 容器中运行任意命令或脚本。适用于训练、推理、编译、数据处理等任意需要 sandbox 的计算任务,支持 Python / Node.js / Go / Java / Rust 等语言。所有任务通用同一套输出约定:命令在 stdout 末尾输出结构化 JSON,CLI 反向扫描 stdout 末尾 JSON 自动提取为 `data.result_summary` 字段。Planner 调用本 skill 时,请在 plan step 的 required_outputs 中声明 [{kind: 'file', extensions: ['.json']}]。请求中用中性动词(运行/执行)描述命令,只有真的会落盘文件时,才使用创建/写入/保存等动词并带上扩展名。认证/配置/权限错误转 delta-shared。"
3
+ description: "在 Delta Sandbox Linux 容器中运行任意命令或脚本。适用于训练、推理、编译、数据处理等任意需要 sandbox 的计算任务,支持 Python / Node.js / Go / Java / Rust 等语言。所有任务通用同一套输出约定:命令在 stdout 末尾输出结构化 JSON,skill 从 log_file 反向扫描提取为 `result_summary`(旧服务器回退路径由 CLI `--summary` 自动提取为 `data.result_summary`)。Planner 调用本 skill 时,请在 plan step 的 required_outputs 中声明 [{kind: 'file', extensions: ['.json']}]。请求中用中性动词(运行/执行)描述命令,只有真的会落盘文件时,才使用创建/写入/保存等动词并带上扩展名。认证/配置/权限错误转 delta-shared。"
4
4
  metadata:
5
5
  requires:
6
6
  bins: ["delta-cli"]
@@ -40,10 +40,10 @@ metadata:
40
40
  ```bash
41
41
  delta-cli sandbox run-bg <id> --command "<命令>" --timeout <秒> --wait
42
42
  ```
43
- CLI 在内部每 5 秒轮询一次 `logs`,直到 `finished=true` 或超时(`--timeout` 同时充当服务端命令超时和客户端轮询 deadline,默认 300s),然后一次性返回 `CommandResult` 信封:`{execution_id, sandbox_id, finished, exit_code, stderr_tail, stderr_size, log_file, result_summary, hints, error}`。**stdout 不直接暴露**——命令的 stdout 在 `finished=true` 时被写入沙箱内的 `log_file`,需要读全文时用 `sandbox read <id> --path <log_file>`;默认开启的 `--summary` 会自动 reverse-scan stdout 末尾 JSON 并填入 `data.result_summary`,常用场景无需手动读 `log_file`。返回的 `execution_id` 可在后续用于 `sandbox logs` / `cancel`。适合只关心最终结果、不需要查看中间进度的场景。
43
+ CLI 通过 SSE 实时流(`/logs/stream`)跟随输出,把原始 SSE 帧(`data: {...}`)**逐帧透传到 stdout**(curl -N 风格),帧类型见 JSON 的 `type` 字段:`init`(含 `execution_id`)/ `stdout` / `stderr` / `error` / `complete`(含 `exit_code`、`log_file`)。收到 `complete` 即命令结束,CLI 直接退出,**不追加末尾信封**——命令成败看 `complete` 帧里的 `exit_code`,`execution_id` 从 `init`/`complete` 帧读取。若在 `--timeout`(默认 300s,同时充当服务端命令超时和客户端等待 deadline)内未收到 `complete`,CLI 会在已透传的帧后**追加一个 `finished=false` 的快照信封**(`CommandLogsResult`),表示命令仍运行。完整 stdout 在 `finished` 后被写入沙箱内的 `log_file`(默认 `/workspace/{user_id}/{sandbox_id}/{execution_id}/delta-result-{execution_id}.json`,以服务端返回的 `log_file` 字段为准),需要读全文或提取末尾 JSON 时用 `sandbox read <id> --path <log_file>`。**`--summary`/`--artifacts` 富化仅在旧服务器回退路径生效**(服务端无 `/logs/stream` 端点时 CLI 自动回退到 5s 轮询并输出 `CommandResult` 信封,此时才返回 `result_summary`/`hints`)。`execution_id` 可用于后续 `sandbox logs` / `cancel`。
44
44
 
45
- **手动轮询(需要查看中间进度时)**:
46
- 如果不加 `--wait` 或需要查看实时输出,用 `sandbox logs <id> --execution-id <exec_id>` 手动轮询:
45
+ **实时跟随 / 手动轮询(需要查看中间进度时)**:
46
+ `sandbox logs <id> --execution-id <exec_id> --stream` 实时跟随(SSE):原始 `data:` 帧逐帧透传到 stdout,直到 `complete` 事件,无末尾信封。`sandbox logs <id> --execution-id <exec_id>`(不加 `--stream`)返回快照信封:
47
47
  - `finished=true` + `exit_code=0` → 完成
48
48
  - `finished=true` + `exit_code!=0` → 失败
49
49
  - `running=true` → 仍运行
@@ -133,7 +133,7 @@ required_outputs:
133
133
  | 获取资源推荐 | `sandbox recommend --cpu N --memory XGi [--gpu N] [--gpu-mem N]` |
134
134
  | 列出当前用户的 sandbox | `sandbox list` |
135
135
  | **生命周期** | |
136
- | 创建 sandbox | `sandbox create --image <image> [--cpu N --memory XGi --gpu N --gpu-mem N --max-life M]` |
136
+ | 创建 sandbox | `sandbox create --image <image> [--cpu N --memory XGi --gpu N --gpu-mem N --max-life M]`;不希望被自动清理时加 `--no-auto-cleanup`(仅显式 kill/finish 可销毁)|
137
137
  | 连接 sandbox | `sandbox connect <id>` |
138
138
  | 查看状态 | `sandbox status <id>` |
139
139
  | 完成 sandbox | `sandbox finish <id> [--results '{...}']` |
@@ -141,7 +141,7 @@ required_outputs:
141
141
  | **命令执行** | |
142
142
  | 同步运行命令(≤60s) | `sandbox run <id> --command "..." [--timeout <秒>] [--summary/--no-summary] [--artifacts]` |
143
143
  | 后台运行命令(>60s) | `sandbox run-bg <id> --command "..." [--timeout <秒>] [--wait] [--summary/--no-summary] [--artifacts]` |
144
- | 查看后台日志 | `sandbox logs <id> --execution-id <exec_id> [--tail N --grep <pattern> --context N]` |
144
+ | 查看后台日志 | `sandbox logs <id> --execution-id <exec_id> [--tail N --grep <pattern> --context N] [--stream]` |
145
145
  | 中断后台命令 | `sandbox cancel <id> --execution-id <exec_id>` |
146
146
  | **文件操作** | |
147
147
  | 读取文件 | `sandbox read <id> --path <path> [--output <本地路径> --tail N --grep <pattern> --offset N --limit N --context N --max-bytes N --parse-json]` |
@@ -170,7 +170,7 @@ required_outputs:
170
170
  - `delta-cli sandbox recommend --cpu N --memory XGi [--gpu N]` — 资源配置推荐
171
171
  2. **创建**:`delta-cli sandbox create --image <img> --cpu 4 --memory 16Gi --gpu 1 --gpu-mem 8000 --max-life 120`。**返回的 JSON 信封中是 `data.sandbox_id`,不是 `data.id`;后续所有命令必须使用这个真实的 `sandbox_id`。**(创建后 sandbox 立即可用,无需额外连接)。**同一次任务若已有 `sandbox_id`,禁止再次 create,必须优先复用。**
172
172
  - --max-life 指定 sandbox 最大存活时间(分钟),默认 30。长任务请调高,确保 sandbox 在命令执行期间不被回收。
173
- - 这是 `sandbox create` 支持的完整资源参数集合,不存在其它“更正确”的资源 flag,不要发明不存在的参数。
173
+ - `--no-auto-cleanup`:加此 flag 后该 sandbox **不会被自动清理**(服务端超时回收 + 本地周期 cleanup_stale 均跳过),只能通过显式 `sandbox kill`/`finish` 销毁。仅当任务确实需要跨越周期清理长期存活时才使用,任务结束后必须主动销毁,避免资源泄漏。
174
174
  - **禁止在 create 成功后反复调用 `sandbox status` 轮询**。`sandbox create` 返回时 sandbox 已经就绪,直接用它返回的 `data.sandbox_id` 执行 `write`/`run` 即可。多余的轮询会增加工具调用次数且没有任何收益。
175
175
  3. **写入代码/数据**:
176
176
  - **单个文件**:`delta-cli sandbox write <id> --path /workspace/<filename> --source <文件名>` — **必须**使用相对路径(如 `--source train.py`),**禁止**使用 `"$WORKSPACE_ROOT/train.py"` 或 `$(pwd)/train.py` 等 Shell 变量路径。`--source` 让 CLI 自行读取本地文件,不会经过 Shell 展开,是最安全的方式。写入后返回的 `size` 字段是实际磁盘字节数(来自 stat 验证),可对比确认写入完整性。少量配置(<20 行)可用 `--data "..."`。文件路径和扩展名由镜像中的运行时决定。
@@ -180,43 +180,19 @@ required_outputs:
180
180
  - 支持嵌套目录,空目录也会被创建。
181
181
  - **写后验证(可选)**:`sandbox stat <id> --path <path>` 确认文件存在且 size 符合预期即可。**不要**用 `sandbox ls` + `sandbox read` 把刚写入的文件读回宿主再逐字对比;无异常时不需要读回。
182
182
  4. **运行命令**:
183
- - **短任务(预计 ≤ 60 秒)**:`delta-cli sandbox run <id> --command "<命令>" --timeout <秒>` 同步执行,返回 `stderr` / `exit_code` / `log_file`,完整 `stdout` 在 run envelope 日志文件中,**不要**再调用 `sandbox logs`。可通过 `--log-file <路径>` 自定义 run envelope 日志文件路径;默认值为 `/tmp/delta-result-{execution_id}.json`。根据镜像中的运行时构造命令,常见示例:
183
+ - **短任务(预计 ≤ 60 秒)**:`delta-cli sandbox run <id> --command "<命令>" --timeout <秒>` 同步执行,命令的 stdout/stderr 通过 SSE **以原始 `data:` 帧逐帧透传到 stdout**(无末尾信封),`exit_code` / `log_file` / `execution_id` 从 `complete` 帧读取。完整 `stdout` 在完成后写入 run envelope 日志文件(`log_file` 字段),需要读全文或提取末尾 JSON 时用 `sandbox read`,**不要**再调用 `sandbox logs`。可通过 `--log-file <路径>` 自定义 run envelope 日志文件路径;默认值为 `/workspace/{user_id}/{sandbox_id}/{execution_id}/delta-result-{execution_id}.json`(以服务端返回的 `log_file` 字段为准)。根据镜像中的运行时构造命令,常见示例:
184
184
  - Python:`python /workspace/train.py`
185
185
  - Node.js:`node /workspace/app.js`
186
186
  - Go:`go run /workspace/main.go`
187
187
  - Shell:`bash /workspace/run.sh`
188
- - **长任务(预计 > 60 秒,如下载模型、训练、编译、大规模数据处理)**:`delta-cli sandbox run-bg <id> --command "<命令>" --timeout <秒> [--log-file <路径>]` 提交后台任务,获得 `execution_id` 后通过以下命令轮询:
189
- - `delta-cli sandbox logs <id> --execution-id <execution_id>` — 返回 `stdout_tail`(末尾 800 字节)、`stderr_tail`(末尾 200 字节)、`stdout_size`、`stderr_size`、`cursor`、`running`、`finished`、`exit_code`、`log_file`(完成后)。当 `finished=true` 时认为完成,完整输出需读取 `log_file`。
188
+ - **长任务(预计 > 60 秒,如下载模型、训练、编译、大规模数据处理)**:`delta-cli sandbox run-bg <id> --command "<命令>" --timeout <秒> [--log-file <路径>]` 提交后台任务,获得 `execution_id` 后通过以下命令查询:
189
+ - `delta-cli sandbox logs <id> --execution-id <execution_id> --stream` — SSE 实时跟随,原始 `data:` 帧逐帧透传到 stdout,直到 `complete` 事件(含 `exit_code`/`log_file`),无末尾信封。
190
+ - `delta-cli sandbox logs <id> --execution-id <execution_id>` — 快照信封,返回 `stdout_tail`(末尾 800 字节)、`stderr_tail`(末尾 200 字节)、`stdout_size`、`stderr_size`、`cursor`、`running`、`finished`、`exit_code`、`log_file`(完成后)。当 `finished=true` 时认为完成,完整输出需读取 `log_file`。
190
191
  禁止对长任务使用同步 `sandbox run`
191
192
 
192
- **推荐实践**:让 sandbox 脚本在 `stdout` 末尾打印一个独立的结构化 JSON 对象(例如 `{"status":"ok", ...}`),CLI 的 `--summary` 默认会自动反向扫描 stdout 末尾 JSON 提取为 `data.result_summary` 字段。这样即使 `stdout` 前面是大量训练/下载日志,skill 也无需让大模型去“读整段日志再摘要”。
193
- 5. **生成 result.json**(默认路径使用 CLI 返回的 `summary` 字段;fallback 路径用 `sandbox read` + Python 解析):
194
- - **5a(默认路径,推荐)**:`run` / `run-bg --wait` 已返回 `result_summary` 字段(CLI 自动 reverse-scan stdout 末尾 JSON)。直接构造 `result.json`:
195
- ```bash
196
- delta-cli sandbox run <id> --command "..." --timeout 60 > /tmp/_run.json
197
- ```
198
- ```python
199
- import json, os
200
- r = json.load(open("/tmp/_run.json"))
201
- data = r.get("data", {})
202
- # CLI 字段名是 result_summary(不是 summary),是一个对象
203
- cli_summary = data.get("result_summary") or {}
204
- result = {
205
- "exit_code": data.get("exit_code"),
206
- "finished": data.get("finished", True),
207
- "summary": cli_summary,
208
- "result_summary": ", ".join(f"{k}={v}" for k,v in cli_summary.items()
209
- if isinstance(v, (str,int,float,bool)) and len(str(v))<200),
210
- "error": data.get("error"),
211
- }
212
- if not result["result_summary"]:
213
- result["result_summary"] = f"exit_code={result['exit_code']}, finished={result['finished']}"
214
- ws = os.environ.get("WORKSPACE_ROOT", ".")
215
- json.dump(result, open(os.path.join(ws, "result.json"), "w", encoding="utf-8"),
216
- indent=2, ensure_ascii=False)
217
- print(result["result_summary"])
218
- ```
219
- - **5b(fallback,仅当 `--no-summary` 或 `result_summary` 为 null)**:从 `sandbox run` / `sandbox run-bg --wait` / `sandbox logs` 返回数据中获得 `log_file` 路径,用 `sandbox read` + Python 解析。
193
+ **推荐实践**:让 sandbox 脚本在 `stdout` 末尾打印一个独立的结构化 JSON 对象(例如 `{"status":"ok", ...}`)。SSE 路径下 CLI 不自动提取 `result_summary`(`--summary` 仅在旧服务器回退路径生效),因此任务结束后用 `sandbox read <id> --path <log_file>` 读取 log_file,再按步骤 5 反向扫描 stdout 末尾 JSON 提取摘要,避免让大模型“读整段日志再摘要”。
194
+ 5. **生成 result.json**(从 `log_file` 读取解析:SSE 路径下 `run`/`run-bg --wait` 不透传 `result_summary` 信封,`--summary` 仅在旧服务器回退路径生效):
195
+ - **获取 `log_file` 路径**:`run` / `run-bg --wait` 的 SSE `complete` 帧中含 `log_file` 字段;`sandbox logs <id> --execution-id <eid>` 快照在 `finished=true` 后同样返回 `log_file`。然后从 log_file 读取解析:
220
196
  - `sandbox read` 返回的是 CLI 信封 `{"ok":true,"data":{"content":"..."}}`,真实结果 JSON 在 `data.content` 字段里。
221
197
  - **不要把 CLI 信封或完整长 `stdout` 原样写入本地 `result.json`**;应该解析后生成一份精简的结构化摘要。
222
198
  - **推荐做法(避免 heredoc 引号失败)**:先用 `bash` 把信封写入临时文件,再用 `python3`(或 `python_repl`)读取该文件并生成 `result.json`。**不要**把 `delta-cli sandbox read` 的输出通过管道直接喂给内联 heredoc,管道+heredoc 的组合极易因 Shell 转义/引号问题失败。
@@ -308,8 +284,8 @@ required_outputs:
308
284
 
309
285
  ## 输出阅读与最终回答格式
310
286
 
311
- 1. **`sandbox run` / `run-bg --wait` 默认带 `--summary`,返回的 JSON 中 `data.result_summary` 字段已包含 stdout 末尾 JSON 提取结果(注意:这里是 `result_summary`,不是 `summary`)。也包含 `data.log_file`**(沙箱内 run envelope 日志文件的路径),作为 fallback 路径。
312
- 2. **默认优先用 `data.result_summary`**;仅当 `result_summary` 字段为 null(使用了 `--no-summary` 或 stdout 无 JSON)时,再用 `sandbox read <id> --path <log_file>` 读取 log_file 并解析 CLI 信封。
287
+ 1. **SSE 路径下 `sandbox run` / `run-bg --wait` 不透传 `result_summary` 信封**(`--summary` 富化仅在旧服务器回退路径生效)。`exit_code` / `log_file` / `execution_id` 从 SSE `complete` 帧读取;`log_file` 路径也可从 `sandbox logs <id> --execution-id <eid>` 快照(`finished=true` 后)获得。
288
+ 2. **统一从 `log_file` 提取摘要**:用 `sandbox read <id> --path <log_file>` 读取 run envelope 日志文件并解析,反向扫描 stdout 末尾 JSON 生成 `result_summary`(见“完整生命周期”步骤 5)。
313
289
  - run envelope 日志文件包含完整 `stdout`、`stderr`、`exit_code`、`finished`、`command`、`error`。
314
290
  3. **生成本地精简 `result.json`**(见“完整生命周期”步骤 5 和“通用结构化输出约定”)。本地副本只应包含关键字段和摘要,**不应包含完整长 `stdout`**。
315
291
  4. **最终回答必须且只能是 `RESULT:` 开头的一行。**
@@ -15,7 +15,7 @@
15
15
 
16
16
  | 命令 | 说明 |
17
17
  |------|------|
18
- | `sandbox create --image <img> [--cpu N --memory XGi --gpu N --gpu-mem N --max-life M --provider P]` | 创建 sandbox 容器 |
18
+ | `sandbox create --image <img> [--cpu N --memory XGi --gpu N --gpu-mem N --max-life M --no-auto-cleanup --provider P]` | 创建 sandbox 容器(`--no-auto-cleanup` 不被自动清理,仅显式 kill/finish 可销毁) |
19
19
  | `sandbox connect <id>` | 连接已有 sandbox |
20
20
  | `sandbox status <id>` | 查看 sandbox 状态(running / done) |
21
21
  | `sandbox finish <id> [--results '{...}']` | 保存结果后自动销毁 |
@@ -25,9 +25,9 @@
25
25
 
26
26
  | 命令 | 说明 |
27
27
  |------|------|
28
- | `sandbox run <id> --command "..." [--timeout N] [--summary/--no-summary] [--artifacts]` | 同步运行命令;默认 `--summary` 开启,返回 JSON 中 `summary` 字段已含 stdout 末尾 JSON 提取结果;`--artifacts` 附带 workspace 产物清单 |
29
- | `sandbox run-bg <id> --command "..." [--timeout N] [--wait] [--summary/--no-summary] [--artifacts]` | 后台运行命令;不加 `--wait` 立即返回 `{execution_id, sandbox_id}`;加 `--wait` 等待完成返回含 `summary` 的完整结果 |
30
- | `sandbox logs <id> --execution-id <eid> [--tail N --grep <pattern> --context N]` | 获取后台命令日志;**默认返回 `stdout_tail`(末尾 800 字节)+ `stderr_tail`(末尾 200 字节)+ `stderr_size` + `cursor`**,总计约 1KB,避免上下文爆炸;`--tail N` 同时作用于 stdout 和 stderr(各取尾 N 行);`--grep <pattern>` 同时过滤 stdout 和 stderr(正则);`--context N` 提供 grep 匹配行前后 N 行上下文(同时用于 stdout 和 stderr);**`cursor` 字段无 `omitempty`,0 也常驻响应**。 |
28
+ | `sandbox run <id> --command "..." [--timeout N] [--summary/--no-summary] [--artifacts]` | 同步运行命令(SSE 实时流,原始 `data:` 帧逐帧透传到 stdout,**无末尾信封**;`exit_code`/`log_file`/`execution_id` 从 `complete` 帧读取);`--summary`/`--artifacts` 仅在旧服务器回退路径(无 SSE 端点时输出 `CommandResult` 信封)生效 |
29
+ | `sandbox run-bg <id> --command "..." [--timeout N] [--wait] [--summary/--no-summary] [--artifacts]` | 后台运行命令;不加 `--wait` 立即返回 `{execution_id, sandbox_id}`;加 `--wait` 通过 SSE 实时流跟随到 `complete`(原始 `data:` 帧透传到 stdout,无末尾信封;超时则追加 `finished=false` 快照信封);`--summary`/`--artifacts` 仅在旧服务器回退路径生效 |
30
+ | `sandbox logs <id> --execution-id <eid> [--tail N --grep <pattern> --context N] [--stream]` | 获取后台命令日志;**默认返回 `stdout_tail`(末尾 800 字节)+ `stderr_tail`(末尾 200 字节)+ `stderr_size` + `cursor`**,总计约 1KB,避免上下文爆炸;`--stream` 走 `/logs/stream` SSE 实时跟随,原始 `data:` 帧透传到 stdout 直到 `complete`(无末尾信封);`--tail N` 同时作用于 stdout 和 stderr(各取尾 N 行);`--grep <pattern>` 同时过滤 stdout 和 stderr(正则);`--context N` 提供 grep 匹配行前后 N 行上下文(同时用于 stdout 和 stderr);**`cursor` 字段无 `omitempty`,0 也常驻响应**。 |
31
31
  | `sandbox cancel <id> --execution-id <eid>` | 中断正在运行的后台命令 |
32
32
 
33
33
  ## 文件操作
@@ -6,10 +6,10 @@
6
6
 
7
7
  1. **查看可用镜像** `delta-cli sandbox images` — 查询服务端支持的镜像列表。镜像决定了容器内的语言运行时(Python / Node.js / Go / Java / Rust 等),根据用户的技术栈和 GPU/CPU 需求匹配。
8
8
  2. **列出现有 sandbox(可选)** `delta-cli sandbox list` — 查看当前用户已创建的活跃 sandbox,避免重复创建
9
- 3. **创建** `delta-cli sandbox create --image <image> --cpu 4 --memory 16Gi --gpu 1 --gpu-mem 8000 --max-life 120`(创建后立即可用,无需连接;--max-life 指定 sandbox 最大存活时间(分钟),默认 30;这些是 `create` 全部资源参数,不要发明其它 flag。`--provider autodl` 可指定使用 AutoDL 后端。)
9
+ 3. **创建** `delta-cli sandbox create --image <image> --cpu 4 --memory 16Gi --gpu 1 --gpu-mem 8000 --max-life 120`(创建后立即可用,无需连接;--max-life 指定 sandbox 最大存活时间(分钟),默认 30。`--no-auto-cleanup` 可让 sandbox 不被自动清理,仅显式 kill/finish 可销毁。`--provider autodl` 可指定使用 AutoDL 后端。)
10
10
  4. **写入代码/数据** `delta-cli sandbox write <id> --path /workspace/<filename> --source <文件名>` — 将本地文件直接写入 sandbox。**必须**使用相对路径(如 `--source infer.py`),禁止使用 `"$WORKSPACE_ROOT/<filename>"`。也可用 `--data "..."` 写入少量内联内容,`--mode 755` 可设置文件权限。写入后返回的 `size` 字段是 stat 验证后的实际磁盘字节数,可确认写入完整。批量写入用 `write-multiple`。文件路径和扩展名由镜像中的运行时决定。
11
11
  5. **运行命令** — 根据镜像中的运行时构造命令,常见示例:`python /workspace/train.py`(Python)、`node /workspace/app.js`(Node.js)、`go run /workspace/main.go`(Go)、`bash /workspace/run.sh`(Shell)
12
- - **短任务 ≤60s**:`delta-cli sandbox run <id> --command "<命令>" --timeout <秒>` 同步执行,返回 `stdout`/`stderr`/`exit_code` (默认带 --summary,返回 JSON data.result_summary 已含 stdout 末尾 JSON 提取结果)
12
+ - **短任务 ≤60s**:`delta-cli sandbox run <id> --command "<命令>" --timeout <秒>` 同步执行,SSE 原始 `data:` 帧透传到 stdout(无末尾信封),`exit_code`/`log_file` 从 `complete` 帧读取,完整 stdout 写入 log_file
13
13
  - **长任务 >60s**:`delta-cli sandbox run-bg <id> --command "<命令>" --timeout <秒> --wait` 后台执行(推荐),或 `run-bg` + `logs` 手动轮询 (run-bg --wait 默认带 --summary;不加 --wait 立即返回 {execution_id, sandbox_id})
14
14
  6. **读取结果** `delta-cli sandbox read <id> --path /workspace/result.json` — 返回 `content`(内容)、`size`(磁盘字节,来自 stat)、`content_length`(字符数)。读不存在的文件返回 error 而非空内容。
15
15
  7. **销毁** `delta-cli sandbox kill <id>`(如需保存结果用 finish,finish 会自动销毁)
@@ -25,11 +25,11 @@
25
25
  ```bash
26
26
  delta-cli sandbox run-bg <id> --command "<命令>" --timeout 7200 --wait
27
27
  ```
28
- CLI 内部每 5 秒轮询一次,`finished=true` 时返回,结果中包含 `execution_id`。只消耗 **1 次 tool call**,适合不关心中间进度的场景。
28
+ CLI 通过 SSE 实时流(`/logs/stream`)跟随输出,把原始 SSE 帧(`data: {...}`)**逐帧透传到 stdout**,收到 `complete` 帧(含 `exit_code`/`log_file`/`execution_id`)即命令结束,CLI 直接退出、不追加末尾信封。只消耗 **1 次 tool call**,适合不关心中间进度的场景。
29
29
 
30
- > `--wait` 默认带 `--summary`,返回的 JSON `data.result_summary` 字段已包含 stdout 末尾结构化 JSON 的提取结果。常用场景无需再调 `sandbox read` 二次解析 log_file。
30
+ > `--wait` 的 SSE 路径不返回 `result_summary` 信封(`--summary` 仅在旧服务器回退路径生效)。需要摘要时,用 `sandbox read <id> --path <log_file>` 读取 log_file,反向扫描 stdout 末尾 JSON 提取 `result_summary`。
31
31
 
32
- > **结果信封统一为 `CommandResult`**:`run-bg --wait` 始终返回同一 `CommandResult` 信封:`{execution_id, sandbox_id, finished, exit_code, stderr_tail, stderr_size, log_file, result_summary, hints, error}`,`finished` 键**始终存在**。`finished=true` 表示执行结束并带富化摘要(`result_summary`/`hints`);`finished=false` 表示命令仍在运行(`exit_code` 与富化字段可能缺失),需用 `sandbox logs` 继续轮询或 `sandbox status` 查看。
32
+ > **输出契约(SSE 路径)**:`run-bg --wait` 成功时 stdout 只有原始 SSE 帧,**没有末尾信封**;`--timeout` 内未结束则追加一个 `finished=false` 的 `CommandLogsResult` 快照信封;旧服务器(无 `/logs/stream` 端点)回退到 5s 轮询并输出 `CommandResult` 信封(`finished` 键始终存在,`finished=true` 时带 `result_summary`/`hints` 富化)。
33
33
 
34
34
  ### 方式二:手动轮询(需要看实时输出时)
35
35
 
@@ -38,7 +38,11 @@ CLI 内部每 5 秒轮询一次,`finished=true` 时返回,结果中包含 `e
38
38
  delta-cli sandbox run-bg <id> --command "<命令>" --timeout 7200
39
39
  # ↑ 返回 execution_id
40
40
 
41
- # 2. 手动轮询进度(可用 --tail N / --grep <pattern> 过滤大输出)
41
+ # 2a. 实时跟随(SSE,推荐):
42
+ delta-cli sandbox logs <id> --execution-id <exec_id> --stream
43
+ # ↑ 原始 data: 帧逐帧透传到 stdout,直到 complete 事件,无末尾信封
44
+
45
+ # 2b. 手动轮询快照(可用 --tail N / --grep <pattern> 过滤大输出)
42
46
  delta-cli sandbox logs <id> --execution-id <exec_id>
43
47
  # ↑ 返回 {sandbox_id, execution_id, stderr_tail, stderr_size, cursor, running, finished, exit_code, log_file?}
44
48
 
@@ -51,7 +55,7 @@ delta-cli sandbox kill <id>
51
55
  `sandbox logs` 返回的数据结构为:
52
56
 
53
57
  ```json
54
- {"ok":true,"data":{"sandbox_id":"...","execution_id":"...","stdout_tail":"...","stderr_tail":"...","stdout_size":1234,"stderr_size":1234,"cursor":5,"running":false,"finished":true,"exit_code":0,"log_file":"/tmp/delta-result-...json"}}
58
+ {"ok":true,"data":{"sandbox_id":"...","execution_id":"...","stdout_tail":"...","stderr_tail":"...","stdout_size":1234,"stderr_size":1234,"cursor":5,"running":false,"finished":true,"exit_code":0,"log_file":"/workspace/{user_id}/{sandbox_id}/{execution_id}/delta-result-{execution_id}.json"}}
55
59
  ```
56
60
 
57
61
  字段说明:
@@ -66,7 +70,7 @@ delta-cli sandbox kill <id>
66
70
  - `exit_code` — 退出码(完成后才有值,运行中字段省略)
67
71
  - `log_file` — 仅 `finished=true` 时出现,是 run envelope 日志文件路径(含完整 stdout + stderr + exit_code + finished + command + error),需读它用 `delta-cli sandbox read <id> --path <log_file>`
68
72
 
69
- **stdout 路径说明**:`sandbox logs` 默认返回 stdout 末尾 800 字节(`stdout_tail`)提供实时中间进度预览,完整 stdout 仍在命令结束时被写入沙箱内的 `log_file`,需全文时用 `sandbox read` 读。CLI `run-bg --wait` 完成后默认开启 `--summary`,会自动 reverse-scan stdout 末尾 JSON 填入 `data.result_summary`,常用场景无需手动读 `log_file`。
73
+ **stdout 路径说明**:`sandbox logs` 默认返回 stdout 末尾 800 字节(`stdout_tail`)提供实时中间进度预览,完整 stdout 仍在命令结束时被写入沙箱内的 `log_file`,需全文时用 `sandbox read` 读。CLI 的 `--summary` 富化仅在旧服务器回退路径生效(SSE 路径不透传 `result_summary`),SSE 路径下需手动 `sandbox read` log_file 反向扫描 stdout 末尾 JSON 提取摘要。
70
74
 
71
75
  要用 `--tail/--grep/--context` 自定义切片:传任意一个 flag 时,CLI 会把过滤后的 stdout 和 stderr 分别写回 `stdout`/`stderr` 字段并更新 `stdout_size`/`stderr_size`;不传则保持 `stdout=""` + `stdout_tail` 末尾 800 字节预览 + `stderr=""` + `stderr_tail` 末尾 200 字节预览(总计约 1KB)。
72
76
 
@@ -88,5 +92,5 @@ delta-cli sandbox logs <id> --execution-id <eid> --grep "ERROR|warning" --contex
88
92
  **注意**:
89
93
  - `sandbox logs` 只应配合 `sandbox run-bg` 使用;同步 `sandbox run` 的结果直接返回,不能也不应该调用 `logs`。
90
94
  - `sandbox run-bg` 不加 `--wait` 时,HTTP 请求本身会快速返回 `{execution_id, sandbox_id}`;命令若超时会以非零退出码结束,需后续通过 `logs` 拿 `exit_code`。
91
- - `sandbox run-bg --wait` 模式下,`--timeout` **同时**充当服务端命令执行超时和客户端轮询 deadline(默认 300s);轮询间隔固定 5 秒;初始 `/run-background` POST 有独立的 60s HTTP timeout。
95
+ - `sandbox run-bg --wait` 模式下,`--timeout` **同时**充当服务端命令执行超时和客户端等待 deadline(默认 300s);CLI 通过 SSE 实时流(`/logs/stream`)跟随,原始 `data:` 帧透传到 stdout,收到 `complete` 即结束(无末尾信封),超时则追加 `finished=false` 快照信封;初始 `/run-background` POST 有独立的 60s HTTP timeout。
92
96
  - 后台命令可跨 tool call 轮次查询:保存 `sandbox_id` 和 `execution_id`,后续轮次通过 `sandbox status` / `sandbox logs` 获取结果。
@@ -22,7 +22,7 @@ required_outputs:
22
22
  {"status":"ok","key_metric_1":"value_1","key_metric_2":123}
23
23
  ```
24
24
 
25
- > `sandbox run` / `run-bg --wait` 默认带 `--summary`,CLI 会自动 reverse-scan stdout 末尾的 JSON 并填入响应 `data.result_summary` 字段。脚本作者只需保证 stdout 最后一行是合法 JSON,skill 端无需再做提取。
25
+ > SSE 路径下 `sandbox run` / `run-bg --wait` 不透传 `data.result_summary`(`--summary` 富化仅在旧服务器回退路径生效)。脚本作者保证 stdout 最后一行是合法 JSON 即可,skill 端用 `sandbox read` 读取 `log_file` 后反向扫描提取。
26
26
 
27
27
  SKILL 会把它提取为 `result.json` 的 `summary`,并生成 `result_summary` 与最终 `RESULT:` 行。
28
28
 
@@ -124,8 +124,8 @@ SKILL 会把它提取为 `result.json` 的 `summary`,并生成 `result_summary
124
124
 
125
125
  ## 本地 `result.json` 的内容约定
126
126
 
127
- - **默认路径**:`run` / `run-bg --wait` 返回 JSON 的 `data.result_summary` 字段已含 CLI 提取结果。写本地 `result.json` 时直接复用 `data.result_summary` 即可,无需再走 `sandbox read` 解析 `log_file`。
128
- - **fallback 路径**(用 `--no-summary` 时或 `summary` 为 null):保留 `delta-cli sandbox read <id> --path <log_file>` + Python 解析的旧路。
127
+ - **默认路径(SSE 下唯一可靠路径)**:`delta-cli sandbox read <id> --path <log_file>` 读取 run envelope 日志文件 + Python 反向扫描 stdout 末尾 JSON 提取 `result_summary`,再写入本地 `result.json`。`log_file` 路径来自 SSE `complete` 帧或 `sandbox logs` 快照(`finished=true` 后)。
128
+ - **旧服务器回退路径**:`run` / `run-bg --wait` 走轮询回退时返回 `CommandResult` 信封,可直接复用 `data.result_summary` 字段。
129
129
  - 本地 `result.json` 是一份**精简摘要**,用于 host deliverable 校验,不是完整日志存档。
130
130
  - 必须包含 `result_summary` 字段,内容与最终 `RESULT:` 行一致,方便 Runner 直接读取结论。
131
131
  - 只写入关键字段(`exit_code`、`finished`、`summary`、`result_summary`、`error`)。**不要**额外添加 `final_answer` 等字段。