@dptech-corp/bohr-cli 2.6.103 → 2.7.1

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/package.json +7 -7
  3. package/run.js +54 -14
package/CHANGELOG.md CHANGED
@@ -22,6 +22,59 @@
22
22
 
23
23
  ## [Unreleased]
24
24
 
25
+ ## [2.7.1] - 2026-09-19
26
+
27
+ ### Changed
28
+
29
+ - **中断后的退出码**:自行处理中断的命令(如 structure search 下载)被 Ctrl-C 后返回自身的退出码,不再是 130;未处理中断的命令不变。
30
+
31
+ ### Fixed
32
+
33
+ - **只停启动器也能停下 CLI**:`timeout`、Agent 框架超时只给 `bohr` 进程发信号时,CLI 不再成为孤儿继续运行、占着输出,而是照常处理中断后退出。
34
+ - **结构下载被挂断时不留临时文件**:关掉终端或收到 SIGHUP 时,`structure search --download` 同样清理临时文件并报下载已取消。
35
+
36
+ ## [2.7.0] - 2026-09-18
37
+
38
+ ### Breaking
39
+
40
+ - **`bohr file list -o json` 改为标准列表**:`objects` 改名 `items`,翻页改读 `pagination.has_more`、`next_token`。
41
+ - **`sandbox`/`batchjob machine list -o json` 合成一张表**:`cpu`、`gpu` 并入 `items`,按每行新增的 `class` 区分。
42
+ - **`bohr project list -o json` 去掉顶层 `totalPage`**:判断有无下一页改读 `data.pagination.has_more`。
43
+ - **移除 15 条已过公示期的弃用项**(3 条命令、11 处 flag 旧名、`-o pretty`):此前带告警仍可用,现在直接报错;改法见下表。
44
+
45
+ 本版移除下列已过公示期的弃用项。旧名 → 新名的映射只在这里,运行时的报错不会说新名字。
46
+
47
+ **命令**
48
+
49
+ | 移除 | 改用 |
50
+ |-|-|
51
+ | `bohr database list` | `bohr database polymer list` |
52
+ | `bohr mentor` | `bohr agents mentor` |
53
+ | `bohr sandbox image` | `bohr image` |
54
+
55
+ **flag 与格式常量**
56
+
57
+ | 移除 | 改用 |
58
+ |-|-|
59
+ | `--space` (bohr file list) | pass the space as a path prefix instead: --space personal with <path> becomes personal/<path>, and --space share becomes share/<path> |
60
+ | `--space` (bohr file upload) | pass the space as a path prefix instead: --space personal with <path> becomes personal/<path>, and --space share becomes share/<path> |
61
+ | `--projectId` (bohr dataset list) | `--project-id` |
62
+ | `--project_id` (bohr job submit) | `--project-id` |
63
+ | `--project_id` (bohr job_group create) | `--project-id` |
64
+ | `--projectId` (bohr job_group list) | `--project-id` |
65
+ | `--project_id` (bohr node create) | `--project-id` |
66
+ | `--project_id` (bohr project delete) | `--project-id` |
67
+ | `--datasetId` (bohr dataset delete) | `--dataset-id` |
68
+ | `--imageId` (bohr image delete) | `--image-id` |
69
+ | `--chooseType` (bohr machine list) | `--choose-type` |
70
+ | `-o pretty` | `-o json` |
71
+
72
+ - **`bohr file upload` 的远端路径须带 `personal/` 或 `share/` 前缀**:此前无前缀会被补全并告警,现在报 `VALIDATION_FAILED`。
73
+
74
+ ### Added
75
+
76
+ - **`bohr file list --next-token`**:把上一页的 `data.pagination.next_token` 传回来取下一页;人读输出在还有下一页时直接给出这条命令。
77
+
25
78
  ## [2.6.103] - 2026-09-18
26
79
 
27
80
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dptech-corp/bohr-cli",
3
- "version": "2.6.103",
3
+ "version": "2.7.1",
4
4
  "description": "CLI tool for Bohrium scientific computing platform",
5
5
  "bin": {
6
6
  "bohr": "run.js"
@@ -32,11 +32,11 @@
32
32
  "CHANGELOG.md"
33
33
  ],
34
34
  "optionalDependencies": {
35
- "@dptech-corp/bohr-cli-darwin-arm64": "2.6.103",
36
- "@dptech-corp/bohr-cli-darwin-amd64": "2.6.103",
37
- "@dptech-corp/bohr-cli-linux-amd64": "2.6.103",
38
- "@dptech-corp/bohr-cli-linux-arm64": "2.6.103",
39
- "@dptech-corp/bohr-cli-windows-amd64": "2.6.103",
40
- "@dptech-corp/bohr-cli-windows-arm64": "2.6.103"
35
+ "@dptech-corp/bohr-cli-darwin-arm64": "2.7.1",
36
+ "@dptech-corp/bohr-cli-darwin-amd64": "2.7.1",
37
+ "@dptech-corp/bohr-cli-linux-amd64": "2.7.1",
38
+ "@dptech-corp/bohr-cli-linux-arm64": "2.7.1",
39
+ "@dptech-corp/bohr-cli-windows-amd64": "2.7.1",
40
+ "@dptech-corp/bohr-cli-windows-arm64": "2.7.1"
41
41
  }
42
42
  }
package/run.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const { execFileSync } = require("child_process");
2
+ const { spawn } = require("child_process");
3
3
  const path = require("path");
4
4
  const os = require("os");
5
5
 
@@ -66,18 +66,58 @@ try {
66
66
  );
67
67
  }
68
68
 
69
- try {
70
- execFileSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
71
- } catch (e) {
72
- // e.status is null when the child was killed by a signal; exiting 1 there
73
- // would report an ordinary failure for a SIGINT. Exit codes are how the
74
- // billing gate (10) and every other envelope code reach the caller, so this
75
- // must pass through untouched when there is one.
76
- if (typeof e.status === "number") {
77
- process.exit(e.status);
78
- }
79
- if (e.signal) {
80
- process.exit(128 + (os.constants.signals[e.signal] || 0));
69
+ // Signals sent to this launcher must reach the binary. A caller that stops bohr
70
+ // by pid — `timeout`, Python's Popen.terminate(), an agent framework's step
71
+ // timeout only ever signals this process. It used to run the binary with
72
+ // execFileSync, which blocks the event loop, so node took the default action and
73
+ // died on the spot while the binary kept running as an orphan, still holding the
74
+ // caller's stdout. The caller then waited on a pipe nothing would close.
75
+ //
76
+ // A terminal Ctrl-C reaches both processes (same process group), so the binary
77
+ // sees SIGINT twice. That is the usual trade-off of a forwarding launcher
78
+ // (internal/toolproxy/exec.go makes the same one): a command without a SIGINT
79
+ // handler is already gone when the copy arrives, and a command with one keeps
80
+ // catching until it has written its envelope, so the copy is absorbed.
81
+ //
82
+ // Windows is left alone. Console Ctrl-C already reaches every process on the
83
+ // console, and child.kill() there is TerminateProcess, which would cut off the
84
+ // binary's own cancellation handling. Listening is still worth it: node then
85
+ // waits for the binary instead of exiting first and handing the prompt back
86
+ // while it is still writing.
87
+ const FORWARDED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
88
+ const forward = platform !== "win32";
89
+
90
+ const child = spawn(binaryPath, process.argv.slice(2), { stdio: "inherit" });
91
+
92
+ const listeners = new Map();
93
+ for (const signal of FORWARDED_SIGNALS) {
94
+ const listener = () => {
95
+ if (forward) child.kill(signal);
96
+ };
97
+ listeners.set(signal, listener);
98
+ process.on(signal, listener);
99
+ }
100
+
101
+ child.on("error", () => {
102
+ // Only a spawn failure (missing binary, no exec bit) ends up here with no
103
+ // pid; execFileSync reported those as a bare exit 1 and so does this. A
104
+ // failed kill also emits "error", but the binary is still running then and
105
+ // "exit" will follow.
106
+ if (child.pid === undefined) process.exit(1);
107
+ });
108
+
109
+ child.on("exit", (code, signal) => {
110
+ // Exit codes are how the billing gate (10) and every other envelope code
111
+ // reach the caller, so they pass through untouched.
112
+ if (typeof code === "number") process.exit(code);
113
+ for (const [name, listener] of listeners) process.removeListener(name, listener);
114
+ if (signal) {
115
+ // Die of the same signal instead of exiting 128+n: a shell script only
116
+ // stops at a Ctrl-C when the process it waited on was killed by SIGINT; an
117
+ // ordinary exit 130 means "handled it", and the script carries on. The
118
+ // exit after it is only the fallback for a signal that does not end us.
119
+ process.kill(process.pid, signal);
120
+ process.exit(128 + (os.constants.signals[signal] || 0));
81
121
  }
82
122
  process.exit(1);
83
- }
123
+ });