@clawos-dev/clawd 0.2.456 → 0.2.458

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/dist/cli.cjs CHANGED
@@ -46240,9 +46240,19 @@ function reduceSessionInner(state, input, deps) {
46240
46240
  resetReadyGate(next);
46241
46241
  const queueEffects = [];
46242
46242
  clearQueuedSends(next, queueEffects);
46243
+ const eventEffects = [
46244
+ ...pushEventToBufferInPlace(next, { kind: "error", message: input.message }, deps),
46245
+ ...pushEventToBufferInPlace(next, { kind: "turn_end" }, deps)
46246
+ // 丢掉 turn_end 顺带产出的 append-turn-duration:那条 effect 会往用户真实的 CC
46247
+ // jsonl 追加一行 turn_duration(runner.ts case 'append-turn-duration',resume 场景
46248
+ // toolSessionId 已 stamp 时会真落盘)。它的用途是"SDK 模式 CC 不写 turn 边界、
46249
+ // daemon 补写",而这里 CC 根本没跑过这一轮——补边界等于往历史里插一个空 turn,
46250
+ // 且不可回滚。proc-exit 的 interrupted 注入走同一条路但语义不同:那边 turn 真发生过。
46251
+ ].filter((e) => e.kind !== "append-turn-duration");
46243
46252
  return {
46244
46253
  state: next,
46245
46254
  effects: [
46255
+ ...eventEffects,
46246
46256
  {
46247
46257
  kind: "emit-frame",
46248
46258
  frame: {
@@ -46429,6 +46439,15 @@ init_stdout_splitter();
46429
46439
  init_permission_stdio();
46430
46440
  init_claude_history();
46431
46441
 
46442
+ // src/tools/spawn-error.ts
46443
+ function describeSpawnError(err, fallbackBin, cwd, pathEnv = process.env.PATH) {
46444
+ const message = err instanceof Error ? err.message : String(err);
46445
+ const e = err;
46446
+ if (e?.code !== "ENOENT") return message;
46447
+ const bin = typeof e.path === "string" && e.path ? e.path : fallbackBin;
46448
+ return `\u8D77\u4E0D\u4E86 ${bin}\uFF1APATH \u91CC\u6CA1\u6709\u5B83\uFF0C\u6216\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u5B58\u5728\u3002\u5DE5\u4F5C\u76EE\u5F55\uFF1A${cwd || "(\u672A\u77E5)"}\uFF5Cclawd \u770B\u5230\u7684 PATH\uFF1A${pathEnv || "(\u7A7A)"}`;
46449
+ }
46450
+
46432
46451
  // src/persona/mcp-secret-render.ts
46433
46452
  var import_node_fs7 = __toESM(require("fs"), 1);
46434
46453
  var import_node_path6 = __toESM(require("path"), 1);
@@ -47009,7 +47028,10 @@ var SessionRunner = class {
47009
47028
  if (this.proc !== proc) return;
47010
47029
  const message = err.message;
47011
47030
  this.hooks.logger?.error("claude spawn failed", { message });
47012
- this.input({ kind: "proc-error", message });
47031
+ this.input({
47032
+ kind: "proc-error",
47033
+ message: describeSpawnError(err, this.hooks.adapter.id, this.state.file.cwd)
47034
+ });
47013
47035
  });
47014
47036
  }
47015
47037
  /**
@@ -51770,7 +51792,8 @@ function createCodexSession(ctx, sink, deps = {}) {
51770
51792
  proc.on("error", (err) => {
51771
51793
  const message = err.message;
51772
51794
  deps.logger?.error("codex app-server spawn failed", { message });
51773
- sink.onError(`codex app-server spawn failed: ${message}`);
51795
+ const described = describeSpawnError(err, cmd, ctx.cwd);
51796
+ sink.onError(described === message ? `codex app-server spawn failed: ${message}` : described);
51774
51797
  });
51775
51798
  proc.on("exit", (code) => finish(code));
51776
51799
  const ready = (async () => {
@@ -63908,7 +63931,7 @@ function computeMethodAccess(args) {
63908
63931
  }
63909
63932
 
63910
63933
  // src/version.ts
63911
- var version = "0.2.456".length > 0 ? "0.2.456" : "dev";
63934
+ var version = "0.2.458".length > 0 ? "0.2.458" : "dev";
63912
63935
 
63913
63936
  // src/cli-probe/probe.ts
63914
63937
  var fs70 = __toESM(require("fs"), 1);
@@ -19,17 +19,27 @@
19
19
  S_ACCESS_ALIAS=clawd-fc
20
20
 
21
21
  # 分层加载阿里云 AK 进 env:shared demo(OTA 每次启动覆盖)→ 本机 .local override(永不被覆盖)。
22
+ #
23
+ # 两种来源,**文件优先、env 兜底**:
24
+ # - 本机:`$KIT_DIR/.secrets/aliyun.env`(+ 可选 .local),OTA 刷新;
25
+ # - 云上容器:没有 `.secrets/`(`cloud-host/provision/build-image.mjs` 刻意把它剔出镜像层
26
+ # ——镜像要分发,凭据进层就洗不掉了),AK 由 gateway 经 **MCP server 的 per-server env**
27
+ # 交到调用本脚本的那个进程里(M1b Step 9)。这条分支不落盘是有意的:写成文件的话
28
+ # agent 一个 `cat` 就看见了,而 env 只有这个子进程有。
22
29
  ensure_aliyun_env() {
23
30
  local kit="${KIT_DIR:?ensure_aliyun_env 需要调用方先设 KIT_DIR}"
24
- [ -f "$kit/.secrets/aliyun.env" ] || {
25
- echo "❌ 缺 $kit/.secrets/aliyun.env(clawd ship 的共享 demo 凭证,OTA 自动刷新)。参考 .secrets/aliyun.env.example 填写。" >&2
31
+ if [ -f "$kit/.secrets/aliyun.env" ]; then
32
+ set -a; . "$kit/.secrets/aliyun.env"; set +a
33
+ # aliyun.env.local: 本机个人 override(永远不被 OTA 覆盖,见 refreshDeployKit)。有就后 source 覆盖 env。
34
+ # 写成 if 而不是 `[ -f ] && { }`:后者在函数末尾会把「文件不存在」变成函数返回 1,调用方 set -e 直接中止。
35
+ if [ -f "$kit/.secrets/aliyun.env.local" ]; then
36
+ set -a; . "$kit/.secrets/aliyun.env.local"; set +a
37
+ fi
38
+ elif [ -n "${ALIBABA_CLOUD_ACCESS_KEY_ID:-}" ] && [ -n "${ALIBABA_CLOUD_ACCESS_KEY_SECRET:-}" ]; then
39
+ : # 云上:值已经在本进程 env 里,不落文件
40
+ else
41
+ echo "❌ 缺 $kit/.secrets/aliyun.env(clawd ship 的共享 demo 凭证,OTA 自动刷新),env 里也没有 ALIBABA_CLOUD_ACCESS_KEY_ID/_SECRET。参考 .secrets/aliyun.env.example 填写。" >&2
26
42
  return 1
27
- }
28
- set -a; . "$kit/.secrets/aliyun.env"; set +a
29
- # aliyun.env.local: 本机个人 override(永远不被 OTA 覆盖,见 refreshDeployKit)。有就后 source 覆盖 env。
30
- # 写成 if 而不是 `[ -f ] && { }`:后者在函数末尾会把「文件不存在」变成函数返回 1,调用方 set -e 直接中止。
31
- if [ -f "$kit/.secrets/aliyun.env.local" ]; then
32
- set -a; . "$kit/.secrets/aliyun.env.local"; set +a
33
43
  fi
34
44
  export ALIBABA_CLOUD_REGION_ID="${REGION:?config.env 缺 REGION}"
35
45
  }
@@ -182,11 +182,70 @@ describe('ensure_credentials', () => {
182
182
  expect(kitAccessYaml()).toContain("AccountID: '5566778899001122'")
183
183
  })
184
184
 
185
- it('缺共享凭证文件时 fail-loud', () => {
185
+ it('缺共享凭证文件、env 里也没有 AK 时 fail-loud', () => {
186
186
  rmSync(join(box.kit, '.secrets', 'aliyun.env'))
187
187
  expect(() => runInKit('ensure_credentials')).toThrow()
188
188
  expect(existsSync(join(box.kit, '.s-home', '.s', 'access.yaml'))).toBe(false)
189
189
  })
190
+
191
+ // 云上分支(persona 上云 M1b Step 9):容器里没有 `.secrets/`——`build-image.mjs` 刻意把它
192
+ // 剔出镜像层(镜像要分发,凭据进层洗不掉)。AK 由 gateway 经 **MCP server 的 per-server env**
193
+ // 交到调用本脚本的那个子进程里,不落文件:写成文件的话 agent 一个 `cat` 就看见了。
194
+ it('没有 .secrets/aliyun.env 但 env 里有 AK 时用 env(云上容器那条路)', () => {
195
+ rmSync(join(box.kit, '.secrets', 'aliyun.env'))
196
+ const out = execFileSync(
197
+ 'bash',
198
+ [
199
+ '-c',
200
+ `set -euo pipefail
201
+ KIT_DIR="${box.kit}"
202
+ REGION=cn-hangzhou
203
+ source "$KIT_DIR/scripts/ensure-credentials.sh"
204
+ ensure_credentials
205
+ printf 'ok %s\\n' "$ALIBABA_CLOUD_ACCESS_KEY_ID"`,
206
+ ],
207
+ {
208
+ env: {
209
+ ...process.env,
210
+ HOME: box.home,
211
+ PATH: `${box.bin}:${process.env.PATH}`,
212
+ ALIBABA_CLOUD_ACCESS_KEY_ID: 'LTAI-FROM-ENV',
213
+ ALIBABA_CLOUD_ACCESS_KEY_SECRET: 'secret-from-env',
214
+ },
215
+ encoding: 'utf8',
216
+ },
217
+ )
218
+ expect(out).toContain('ok LTAI-FROM-ENV')
219
+ // 走 env 分支照样把凭证落进 kit 私有 s-home(publish 用的是那份 profile),仍不碰用户全局
220
+ expect(kitAccessYaml()).toContain('LTAI-FROM-ENV')
221
+ expect(userAccessYaml()).toContain('LTAI-USER-OWN-KEY')
222
+ })
223
+
224
+ it('文件在就优先用文件(云上那条只是兜底,不能反过来盖掉本机 shared 凭证)', () => {
225
+ const out = execFileSync(
226
+ 'bash',
227
+ [
228
+ '-c',
229
+ `set -euo pipefail
230
+ KIT_DIR="${box.kit}"
231
+ REGION=cn-hangzhou
232
+ source "$KIT_DIR/scripts/ensure-credentials.sh"
233
+ ensure_aliyun_env
234
+ printf 'id=%s\\n' "$ALIBABA_CLOUD_ACCESS_KEY_ID"`,
235
+ ],
236
+ {
237
+ env: {
238
+ ...process.env,
239
+ HOME: box.home,
240
+ PATH: `${box.bin}:${process.env.PATH}`,
241
+ ALIBABA_CLOUD_ACCESS_KEY_ID: 'LTAI-FROM-ENV',
242
+ ALIBABA_CLOUD_ACCESS_KEY_SECRET: 'secret-from-env',
243
+ },
244
+ encoding: 'utf8',
245
+ },
246
+ )
247
+ expect(out).not.toContain('LTAI-FROM-ENV')
248
+ })
190
249
  })
191
250
 
192
251
  describe('publish.sh', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.456",
3
+ "version": "0.2.458",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",