deskbuddy 0.2.0-beta.11 → 0.2.0-beta.13

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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/commands/daemon.ts
4
- import { writeFileSync as writeFileSync19, rmSync as rmSync15 } from "fs";
4
+ import { writeFileSync as writeFileSync18, rmSync as rmSync15 } from "fs";
5
5
 
6
6
  // src/core/api.ts
7
7
  import { execFileSync } from "child_process";
@@ -1439,6 +1439,7 @@ function writeEnded(s, now = Date.now()) {
1439
1439
  const dir = endedDir();
1440
1440
  mkdirSync7(dir, { recursive: true, mode: 448 });
1441
1441
  const rec = { sessionId: s.sessionId, tool: s.tool, cwd: s.cwd ?? "", interactive: s.interactive === true, endedAt: now };
1442
+ if (Number.isInteger(s.turns) && s.turns >= 0) rec.turns = s.turns;
1442
1443
  const file = join8(dir, `${s.sessionId}.json`);
1443
1444
  const tmp = `${file}.${process.pid}.tmp`;
1444
1445
  writeFileSync6(tmp, JSON.stringify(rec), { encoding: "utf8", mode: 384 });
@@ -1636,19 +1637,27 @@ async function postReport(payload) {
1636
1637
  import { basename as basename3 } from "path";
1637
1638
  var KEEPALIVE_MS = 2 * 60 * 1e3;
1638
1639
  var MAX_SESSIONS = 20;
1639
- var CAPS = ["cmd_result", "window_inject", "resume_ended", "reply_idle_window"];
1640
+ var CAPS = ["cmd_result", "window_inject", "resume_ended", "reply_idle_window", "turns"];
1641
+ function turnsOf(s) {
1642
+ return Number.isInteger(s.turns) && s.turns >= 0 ? s.turns : void 0;
1643
+ }
1640
1644
  function buildSnapshot(states, tool, cfg, nowMs, remote) {
1641
- const sessions = states.filter((s) => s.tool === tool).sort((a, b) => b.updatedAt - a.updatedAt).slice(0, MAX_SESSIONS).map((s) => ({
1642
- sessionId: s.sessionId,
1643
- title: s.title,
1644
- // 隐私:只带目录名,不带全路径——全路径会暴露公司目录结构与用户名
1645
- project: basename3(s.project || ""),
1646
- state: s.state,
1647
- startedAt: s.startedAt,
1648
- updatedAt: s.updatedAt,
1649
- interactive: s.interactive === true,
1650
- remoteWindow: remote ? remote.has(s.sessionId) : s.remoteWindow === true
1651
- }));
1645
+ const sessions = states.filter((s) => s.tool === tool).sort((a, b) => b.updatedAt - a.updatedAt).slice(0, MAX_SESSIONS).map((s) => {
1646
+ const o = {
1647
+ sessionId: s.sessionId,
1648
+ title: s.title,
1649
+ // 隐私:只带目录名,不带全路径——全路径会暴露公司目录结构与用户名
1650
+ project: basename3(s.project || ""),
1651
+ state: s.state,
1652
+ startedAt: s.startedAt,
1653
+ updatedAt: s.updatedAt,
1654
+ interactive: s.interactive === true,
1655
+ remoteWindow: remote ? remote.has(s.sessionId) : s.remoteWindow === true
1656
+ };
1657
+ const turns = turnsOf(s);
1658
+ if (turns !== void 0) o.turns = turns;
1659
+ return o;
1660
+ });
1652
1661
  return { clientId: cfg.clientId, hostName: cfg.hostName, tool, snapshotAt: nowMs, sessions, caps: [...CAPS] };
1653
1662
  }
1654
1663
 
@@ -2491,17 +2500,17 @@ function serverBackend(client = api) {
2491
2500
  const out = [];
2492
2501
  for (const a of res?.approvals ?? []) {
2493
2502
  if (typeof a?.approvalId !== "string") continue;
2494
- const d = mapStatus(a.status);
2503
+ const d = mapStatus(a.status, a.decision);
2495
2504
  if (d) out.push({ approvalId: a.approvalId, decision: d, deviceName: cleanDeviceName(a.deviceName) });
2496
2505
  }
2497
2506
  return out;
2498
2507
  }
2499
2508
  };
2500
2509
  }
2501
- function mapStatus(s) {
2510
+ function mapStatus(s, decision) {
2502
2511
  switch (s) {
2503
2512
  case "APPROVED":
2504
- return "allow";
2513
+ return decision === "allow_always" ? "allow_always" : "allow";
2505
2514
  case "DENIED":
2506
2515
  return "deny";
2507
2516
  case "CANCELLED":
@@ -2514,7 +2523,8 @@ function mapStatus(s) {
2514
2523
  // src/daemon/gate/coordinator.ts
2515
2524
  import { randomUUID as randomUUID3 } from "crypto";
2516
2525
  import { existsSync as existsSync12, realpathSync as realpathSync2 } from "fs";
2517
- import { basename as basename6, dirname as dirname8, join as join14 } from "path";
2526
+ import { homedir as homedir6 } from "os";
2527
+ import { basename as basename6, dirname as dirname8, join as join14, posix as posix3, win32 as win324 } from "path";
2518
2528
 
2519
2529
  // src/daemon/gate/classify.ts
2520
2530
  import { isAbsolute, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
@@ -2712,7 +2722,19 @@ var CMD_MATCH_MIN = 6;
2712
2722
  var CMD_MATCH_LEN = 80;
2713
2723
 
2714
2724
  // src/daemon/gate/coordinator.ts
2715
- function pickRememberSuggestion(suggestions) {
2725
+ var REMEMBER_TOOLS = /* @__PURE__ */ new Set([
2726
+ "claude_code",
2727
+ // Qoder CN / Qoder 的 worker 与 Claude Code 同一套 PermissionUpdate(addRules / setMode / addDirectories),
2728
+ // 输入原样带建议、输出的 updatedPermissions 会被应用(2026-10-02 读 Qoder CN 0.4.1 worker 源码核对)⚠️ 未实机点过
2729
+ "qoder_cn",
2730
+ "qoder",
2731
+ // opencode:插件把授权请求的 always 模式翻成一条 session 级 addRules 建议,设备选「记住」时插件回 always(hook/opencode.mjs)
2732
+ "opencode"
2733
+ ]);
2734
+ function cut100(text) {
2735
+ return text.length > 100 ? text.slice(0, 99) + "\u2026" : text;
2736
+ }
2737
+ function pickRememberSuggestion(suggestions, opts = {}) {
2716
2738
  if (!Array.isArray(suggestions)) return null;
2717
2739
  for (const s of suggestions) {
2718
2740
  if (!s || s.type !== "addRules" || s.behavior !== "allow" || !Array.isArray(s.rules) || s.rules.length === 0) continue;
@@ -2721,10 +2743,41 @@ function pickRememberSuggestion(suggestions) {
2721
2743
  if (raw.some((r) => typeof r?.ruleContent !== "string" || !r.ruleContent)) continue;
2722
2744
  const rules = raw.map((r) => r.ruleContent);
2723
2745
  const scope = s.destination === "session" ? "\u8FD9\u6B21\u4F1A\u8BDD\u91CC" : "\u8FD9\u4E2A\u9879\u76EE\u91CC";
2724
- const text = `\u4EE5\u540E\u5728${scope}\u90FD\u5141\u8BB8\uFF1A${rules.join("\u3001")}`;
2725
- return { suggestion: s, text: text.length > 100 ? text.slice(0, 99) + "\u2026" : text };
2726
- }
2727
- return null;
2746
+ return { updates: [s], text: cut100(`\u4EE5\u540E\u5728${scope}\u90FD\u5141\u8BB8\uFF1A${rules.join("\u3001")}`) };
2747
+ }
2748
+ if (!opts.edit) return null;
2749
+ const all = suggestions;
2750
+ const mode = all.find((s) => s && s.type === "setMode" && s.mode === "acceptEdits" && s.destination === "session");
2751
+ if (!mode) return null;
2752
+ const dirs = all.filter((s) => s && s.type === "addDirectories");
2753
+ if (dirs.length > 1) return null;
2754
+ let text = "\u672C\u4F1A\u8BDD\u81EA\u52A8\u63A5\u53D7\u6587\u4EF6\u7F16\u8F91\uFF08\u542B\u65B0\u5EFA/\u79FB\u52A8\u6587\u4EF6\u7B49\uFF09";
2755
+ if (dirs.length === 1) {
2756
+ const d = dirs[0];
2757
+ const list = Array.isArray(d.directories) ? d.directories : [];
2758
+ if (d.destination !== "session" || list.length !== 1 || !grantableDir(list[0], opts.home ?? homedir6())) return null;
2759
+ text += `\uFF0C\u5E76\u5141\u8BB8\u8BBF\u95EE\u76EE\u5F55 ${list[0]}`;
2760
+ }
2761
+ if (text.length > 100) return null;
2762
+ return { updates: dirs.length === 1 ? [mode, dirs[0]] : [mode], text };
2763
+ }
2764
+ var EDIT_TOOLS2 = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
2765
+ function normDir(p, win) {
2766
+ if (win) return win324.normalize(p).replace(/[\\/]+$/, "").toLowerCase();
2767
+ return posix3.normalize(p).replace(/\/+$/, "");
2768
+ }
2769
+ function grantableDir(dir, home) {
2770
+ if (typeof dir !== "string" || dir === "") return false;
2771
+ const win = /^[A-Za-z]:[\\/]/.test(dir) || dir.startsWith("\\\\");
2772
+ if (!win && !dir.startsWith("/")) return false;
2773
+ const n = normDir(dir, win);
2774
+ if (n === "" || /^[a-z]:$/.test(n) || win && /^\\\\[^\\]+\\[^\\]+$/.test(n)) return false;
2775
+ if (home && n === normDir(home, win)) return false;
2776
+ return true;
2777
+ }
2778
+ function rememberUpdatesOf(rec) {
2779
+ if (Array.isArray(rec.rememberUpdates) && rec.rememberUpdates.length > 0) return rec.rememberUpdates;
2780
+ return rec.rememberSuggestion !== void 0 ? [rec.rememberSuggestion] : null;
2728
2781
  }
2729
2782
  var DEVICE_FALLBACK = "\u684C\u9762\u642D\u5B50";
2730
2783
  function windowNotice(decision, summary2, deviceName = "") {
@@ -2859,7 +2912,7 @@ var ApprovalCoordinator = class {
2859
2912
  for (const run2 of this.#runs.values()) if (run2.sessionId === sessionId) return none;
2860
2913
  const store = this.#d.store;
2861
2914
  const { kind, summary: summary2 } = describe(toolName, hook.tool_input, cwd ? cwdForms(cwd) : []);
2862
- const remember = tool === "claude_code" ? pickRememberSuggestion(hook.permission_suggestions) : null;
2915
+ const remember = REMEMBER_TOOLS.has(tool) ? pickRememberSuggestion(hook.permission_suggestions, { edit: EDIT_TOOLS2.has(toolName) || kind === "edit" }) : null;
2863
2916
  const rec = {
2864
2917
  approvalId: randomUUID3(),
2865
2918
  runId: "",
@@ -2872,7 +2925,7 @@ var ApprovalCoordinator = class {
2872
2925
  mode: "window",
2873
2926
  status: "pending",
2874
2927
  createdAt: this.#now(),
2875
- ...remember ? { remember: remember.text, rememberSuggestion: remember.suggestion } : {}
2928
+ ...remember ? { remember: remember.text, rememberUpdates: remember.updates } : {}
2876
2929
  };
2877
2930
  store.add(rec);
2878
2931
  store.save();
@@ -2988,7 +3041,8 @@ var ApprovalCoordinator = class {
2988
3041
  return;
2989
3042
  }
2990
3043
  if (rec.status !== "pending") return;
2991
- const always = decision === "allow_always" && rec.mode === "window" && rec.rememberSuggestion !== void 0;
3044
+ const updates = rememberUpdatesOf(rec);
3045
+ const always = decision === "allow_always" && rec.mode === "window" && updates !== null;
2992
3046
  if (decision === "allow_always") decision = "allow";
2993
3047
  rec.status = decision === "allow" ? "approved" : "denied";
2994
3048
  rec.decidedAt = this.#now();
@@ -3006,7 +3060,7 @@ var ApprovalCoordinator = class {
3006
3060
  } catch {
3007
3061
  }
3008
3062
  waiter(
3009
- decision === "allow" ? { decision: "allow", notice, ...always ? { updatedPermissions: [rec.rememberSuggestion] } : {} } : (
3063
+ decision === "allow" ? { decision: "allow", notice, ...always && updates ? { updatedPermissions: updates } : {} } : (
3010
3064
  // 🔴 交给模型的原因里不带设备名:名字是家庭成员在 App 里起的,别让它进提示词。设备名只进给人看的回显
3011
3065
  { decision: "deny", reason: "\u7528\u6237\u5728 DeskBuddy \u8BBE\u5907\u4E0A\u62D2\u7EDD\u4E86\u8FD9\u4E00\u6B65\uFF0C\u4E0D\u8981\u91CD\u8BD5\u8FD9\u4E2A\u64CD\u4F5C", notice }
3012
3066
  )
@@ -3430,42 +3484,6 @@ var SessionQueue = class {
3430
3484
  }
3431
3485
  };
3432
3486
 
3433
- // src/daemon/remote.ts
3434
- import { mkdirSync as mkdirSync13, readFileSync as readFileSync12, renameSync as renameSync9, writeFileSync as writeFileSync11 } from "fs";
3435
- import { join as join17 } from "path";
3436
- function handleDir() {
3437
- return join17(deskbuddyHome(), "remote");
3438
- }
3439
- function handlePath(tool) {
3440
- const safe = (tool || "").replace(/[^0-9a-zA-Z_-]/g, "");
3441
- return join17(handleDir(), `${safe || "unknown"}.json`);
3442
- }
3443
- function readHandle(tool) {
3444
- try {
3445
- const h = JSON.parse(readFileSync12(handlePath(tool), "utf8"));
3446
- if (typeof h?.sessionId !== "string" || !h.sessionId) return null;
3447
- if (typeof h?.cwd !== "string" || !h.cwd) return null;
3448
- return h;
3449
- } catch {
3450
- return null;
3451
- }
3452
- }
3453
- function writeHandle(tool, handle) {
3454
- try {
3455
- mkdirSync13(handleDir(), { recursive: true });
3456
- const tmp = handlePath(tool) + ".tmp";
3457
- writeFileSync11(tmp, JSON.stringify(handle), "utf8");
3458
- renameSync9(tmp, handlePath(tool));
3459
- } catch {
3460
- }
3461
- }
3462
- function handleIsUsable(handle) {
3463
- const s = readState(handle.sessionId);
3464
- if (!s) return false;
3465
- if (s.pid && !isAlive(s.pid, s.pidName)) return false;
3466
- return true;
3467
- }
3468
-
3469
3487
  // src/daemon/resolve.ts
3470
3488
  function resolveTarget(cmd, deps) {
3471
3489
  if (cmd.sessionId) {
@@ -3474,23 +3492,16 @@ function resolveTarget(cmd, deps) {
3474
3492
  if (!state) return { ok: false, reason: "session_gone" };
3475
3493
  if (!state.cwd) return { ok: false, reason: "no_cwd" };
3476
3494
  if (deps.windowOpen?.(state)) return { ok: false, reason: "window_open" };
3477
- return { ok: true, sessionId, cwd: state.cwd, handleKey: cmd.tool };
3495
+ return { ok: true, sessionId, cwd: state.cwd };
3478
3496
  }
3479
3497
  let dirCwd = "";
3480
3498
  if (cmd.dirRef) {
3481
3499
  dirCwd = deps.dirOf ? deps.dirOf(cmd.dirRef) : "";
3482
3500
  if (!dirCwd) return { ok: false, reason: "dir_gone" };
3483
3501
  }
3484
- const handleKey = cmd.dirRef ? `${cmd.tool}_${cmd.dirRef}` : cmd.tool;
3485
- const handle = deps.readHandle(handleKey);
3486
- const handleState = handle ? deps.readState(handle.sessionId) : null;
3487
- const handleInWindow = !!(handleState && deps.windowOpen?.(handleState));
3488
- if (handle && !handleInWindow && deps.handleIsUsable(handle)) {
3489
- return { ok: true, sessionId: handle.sessionId, cwd: handle.cwd, handleKey };
3490
- }
3491
3502
  const cwd = dirCwd || deps.remoteCwd();
3492
3503
  if (!cwd) return { ok: false, reason: "no_remote_cwd" };
3493
- return { ok: true, sessionId: "", cwd, handleKey };
3504
+ return { ok: true, sessionId: "", cwd };
3494
3505
  }
3495
3506
  function failureMessage(reason) {
3496
3507
  switch (reason) {
@@ -3508,11 +3519,11 @@ function failureMessage(reason) {
3508
3519
  }
3509
3520
 
3510
3521
  // src/daemon/seen.ts
3511
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync13, renameSync as renameSync10, writeFileSync as writeFileSync12 } from "fs";
3512
- import { join as join18 } from "path";
3522
+ import { mkdirSync as mkdirSync13, readFileSync as readFileSync12, renameSync as renameSync9, writeFileSync as writeFileSync11 } from "fs";
3523
+ import { join as join17 } from "path";
3513
3524
  var SEEN_CAPACITY = 200;
3514
3525
  function seenPath() {
3515
- return join18(deskbuddyHome(), "seen.json");
3526
+ return join17(deskbuddyHome(), "seen.json");
3516
3527
  }
3517
3528
  var SeenIds = class _SeenIds {
3518
3529
  #ids = [];
@@ -3543,7 +3554,7 @@ var SeenIds = class _SeenIds {
3543
3554
  }
3544
3555
  static load() {
3545
3556
  try {
3546
- const raw = JSON.parse(readFileSync13(seenPath(), "utf8"));
3557
+ const raw = JSON.parse(readFileSync12(seenPath(), "utf8"));
3547
3558
  return new _SeenIds(Array.isArray(raw?.ids) ? raw.ids.filter((x) => typeof x === "string") : []);
3548
3559
  } catch {
3549
3560
  return new _SeenIds();
@@ -3551,10 +3562,10 @@ var SeenIds = class _SeenIds {
3551
3562
  }
3552
3563
  save() {
3553
3564
  try {
3554
- mkdirSync14(deskbuddyHome(), { recursive: true });
3565
+ mkdirSync13(deskbuddyHome(), { recursive: true });
3555
3566
  const tmp = seenPath() + ".tmp";
3556
- writeFileSync12(tmp, JSON.stringify({ ids: this.#ids }), "utf8");
3557
- renameSync10(tmp, seenPath());
3567
+ writeFileSync11(tmp, JSON.stringify({ ids: this.#ids }), "utf8");
3568
+ renameSync9(tmp, seenPath());
3558
3569
  } catch {
3559
3570
  }
3560
3571
  }
@@ -4216,17 +4227,17 @@ var CodexWatch = class {
4216
4227
  };
4217
4228
 
4218
4229
  // src/core/codexRemote.ts
4219
- import { mkdirSync as mkdirSync15, renameSync as renameSync11, rmSync as rmSync7, writeFileSync as writeFileSync13 } from "fs";
4220
- import { join as join19 } from "path";
4230
+ import { mkdirSync as mkdirSync14, renameSync as renameSync10, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
4231
+ import { join as join18 } from "path";
4221
4232
  function codexRemotePath() {
4222
- return join19(deskbuddyHome(), "codex-remote.json");
4233
+ return join18(deskbuddyHome(), "codex-remote.json");
4223
4234
  }
4224
4235
  function writeCodexRemote(threads, pid = process.pid, now = Date.now()) {
4225
4236
  try {
4226
- mkdirSync15(deskbuddyHome(), { recursive: true });
4237
+ mkdirSync14(deskbuddyHome(), { recursive: true });
4227
4238
  const p = codexRemotePath();
4228
- writeFileSync13(p + ".tmp", JSON.stringify({ pid, threads: [...threads], at: now }), "utf8");
4229
- renameSync11(p + ".tmp", p);
4239
+ writeFileSync12(p + ".tmp", JSON.stringify({ pid, threads: [...threads], at: now }), "utf8");
4240
+ renameSync10(p + ".tmp", p);
4230
4241
  } catch {
4231
4242
  }
4232
4243
  }
@@ -4245,14 +4256,14 @@ import { connect, createServer as createServer2 } from "net";
4245
4256
  // src/core/wake.ts
4246
4257
  import { execFileSync as execFileSync10 } from "child_process";
4247
4258
  import { createHash as createHash3, createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
4248
- import { chmodSync as chmodSync3, closeSync as closeSync3, mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync4, readFileSync as readFileSync14, readSync as readSync2, renameSync as renameSync12, rmSync as rmSync8, statSync as statSync5, writeFileSync as writeFileSync14 } from "fs";
4259
+ import { chmodSync as chmodSync3, closeSync as closeSync3, mkdirSync as mkdirSync15, openSync as openSync3, readdirSync as readdirSync4, readFileSync as readFileSync13, readSync as readSync2, renameSync as renameSync11, rmSync as rmSync8, statSync as statSync5, writeFileSync as writeFileSync13 } from "fs";
4249
4260
  import { tmpdir as tmpdir2 } from "os";
4250
- import { join as join20 } from "path";
4261
+ import { join as join19 } from "path";
4251
4262
  function wakeDir(home = deskbuddyHome()) {
4252
- return join20(home, "wake");
4263
+ return join19(home, "wake");
4253
4264
  }
4254
4265
  function marksDir(home = deskbuddyHome()) {
4255
- return join20(wakeDir(home), "marks");
4266
+ return join19(wakeDir(home), "marks");
4256
4267
  }
4257
4268
  function safeSid(sessionId) {
4258
4269
  return String(sessionId).replace(/[^A-Za-z0-9_-]/g, "");
@@ -4262,10 +4273,10 @@ function homeTag(home) {
4262
4273
  }
4263
4274
  function wakeChannelPath(platform = process.platform, home = deskbuddyHome()) {
4264
4275
  if (platform === "win32") return `\\\\.\\pipe\\deskbuddy-wake-${homeTag(home)}`;
4265
- const primary = join20(wakeDir(home), "wake.sock");
4276
+ const primary = join19(wakeDir(home), "wake.sock");
4266
4277
  if (Buffer.byteLength(primary) <= 103) return primary;
4267
4278
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
4268
- return join20(tmpdir2(), `deskbuddy-${uid}`, `wake-${homeTag(home)}.sock`);
4279
+ return join19(tmpdir2(), `deskbuddy-${uid}`, `wake-${homeTag(home)}.sock`);
4269
4280
  }
4270
4281
  function pidExists(pid) {
4271
4282
  if (!Number.isInteger(pid) || pid <= 0) return false;
@@ -4280,11 +4291,11 @@ function writeWakeMark(sessionId, agentPid, now = Date.now()) {
4280
4291
  const safe = safeSid(sessionId);
4281
4292
  if (!safe) return;
4282
4293
  try {
4283
- mkdirSync16(marksDir(), { recursive: true, mode: 448 });
4284
- const file = join20(marksDir(), `${safe}.json`);
4294
+ mkdirSync15(marksDir(), { recursive: true, mode: 448 });
4295
+ const file = join19(marksDir(), `${safe}.json`);
4285
4296
  const tmp = `${file}.${process.pid}.tmp`;
4286
- writeFileSync14(tmp, JSON.stringify({ agentPid: agentPid ?? 0, daemonPid: process.pid, at: now }), { encoding: "utf8", mode: 384 });
4287
- renameSync12(tmp, file);
4297
+ writeFileSync13(tmp, JSON.stringify({ agentPid: agentPid ?? 0, daemonPid: process.pid, at: now }), { encoding: "utf8", mode: 384 });
4298
+ renameSync11(tmp, file);
4288
4299
  } catch {
4289
4300
  }
4290
4301
  }
@@ -4292,7 +4303,7 @@ function removeWakeMark(sessionId) {
4292
4303
  const safe = safeSid(sessionId);
4293
4304
  if (!safe) return;
4294
4305
  try {
4295
- rmSync8(join20(marksDir(), `${safe}.json`), { force: true });
4306
+ rmSync8(join19(marksDir(), `${safe}.json`), { force: true });
4296
4307
  } catch {
4297
4308
  }
4298
4309
  }
@@ -4305,7 +4316,7 @@ function clearWakeMarks() {
4305
4316
  }
4306
4317
  for (const n of names) {
4307
4318
  try {
4308
- rmSync8(join20(marksDir(), n), { force: true });
4319
+ rmSync8(join19(marksDir(), n), { force: true });
4309
4320
  } catch {
4310
4321
  }
4311
4322
  }
@@ -4314,7 +4325,7 @@ function isWakeMarked(sessionId, alive = pidExists) {
4314
4325
  const safe = safeSid(sessionId);
4315
4326
  if (!safe) return false;
4316
4327
  try {
4317
- const m = JSON.parse(readFileSync14(join20(marksDir(), `${safe}.json`), "utf8"));
4328
+ const m = JSON.parse(readFileSync13(join19(marksDir(), `${safe}.json`), "utf8"));
4318
4329
  const daemonPid = Number(m?.daemonPid) || 0;
4319
4330
  if (!daemonPid || !alive(daemonPid)) return false;
4320
4331
  const pid = Number(m?.agentPid) || 0;
@@ -4327,24 +4338,24 @@ function withWake(states, alive) {
4327
4338
  return states.map((s) => s.remoteWindow === true || !isWakeMarked(s.sessionId, alive) ? s : { ...s, remoteWindow: true });
4328
4339
  }
4329
4340
  function wakeSecretPath(home = deskbuddyHome()) {
4330
- return join20(wakeDir(home), "secret");
4341
+ return join19(wakeDir(home), "secret");
4331
4342
  }
4332
4343
  function writeWakeSecret(hex, platform = process.platform) {
4333
4344
  const file = wakeSecretPath();
4334
- mkdirSync16(wakeDir(), { recursive: true, mode: 448 });
4345
+ mkdirSync15(wakeDir(), { recursive: true, mode: 448 });
4335
4346
  const tmp = `${file}.${process.pid}.tmp`;
4336
- writeFileSync14(tmp, hex, { encoding: "utf8", mode: 384 });
4347
+ writeFileSync13(tmp, hex, { encoding: "utf8", mode: 384 });
4337
4348
  if (platform === "win32") {
4338
4349
  try {
4339
4350
  execFileSync10("icacls", [tmp, "/inheritance:r", "/grant:r", `${currentUserSid()}:F`], { stdio: "ignore", windowsHide: true });
4340
4351
  } catch {
4341
- renameSync12(tmp, file);
4352
+ renameSync11(tmp, file);
4342
4353
  return "\u7A97\u53E3\u5524\u9192\u5BC6\u94A5\u6587\u4EF6\u7684\u6743\u9650\u6CA1\u80FD\u6536\u7D27\uFF08icacls \u5931\u8D25\uFF09";
4343
4354
  }
4344
4355
  } else {
4345
4356
  chmodSync3(tmp, 384);
4346
4357
  }
4347
- renameSync12(tmp, file);
4358
+ renameSync11(tmp, file);
4348
4359
  return "";
4349
4360
  }
4350
4361
  function restrictToUser(file, platform = process.platform) {
@@ -4723,29 +4734,29 @@ function boxMatches(view, body) {
4723
4734
  }
4724
4735
 
4725
4736
  // src/core/wrapWindows.ts
4726
- import { chmodSync as chmodSync5, mkdirSync as mkdirSync17, readdirSync as readdirSync5, readFileSync as readFileSync15, renameSync as renameSync13, rmSync as rmSync10, statSync as statSync6, writeFileSync as writeFileSync15 } from "fs";
4737
+ import { chmodSync as chmodSync5, mkdirSync as mkdirSync16, readdirSync as readdirSync5, readFileSync as readFileSync14, renameSync as renameSync12, rmSync as rmSync10, statSync as statSync6, writeFileSync as writeFileSync14 } from "fs";
4727
4738
  import { tmpdir as tmpdir3 } from "os";
4728
- import { join as join21 } from "path";
4739
+ import { join as join20 } from "path";
4729
4740
  var WINDOW_ID_RE = /^[a-f0-9]{32}$/;
4730
4741
  function windowsDir() {
4731
- return join21(deskbuddyHome(), "windows");
4742
+ return join20(deskbuddyHome(), "windows");
4732
4743
  }
4733
4744
  function bySessionDir() {
4734
- return join21(windowsDir(), "by-session");
4745
+ return join20(windowsDir(), "by-session");
4735
4746
  }
4736
4747
  function safeSid2(sessionId) {
4737
4748
  return String(sessionId).replace(/[^A-Za-z0-9_-]/g, "");
4738
4749
  }
4739
4750
  function ensureDir(dir) {
4740
- mkdirSync17(dir, { recursive: true, mode: 448 });
4751
+ mkdirSync16(dir, { recursive: true, mode: 448 });
4741
4752
  }
4742
4753
  function atomicWrite(file, text) {
4743
4754
  const tmp = `${file}.${process.pid}.tmp`;
4744
- writeFileSync15(tmp, text, { encoding: "utf8", mode: 384 });
4745
- renameSync13(tmp, file);
4755
+ writeFileSync14(tmp, text, { encoding: "utf8", mode: 384 });
4756
+ renameSync12(tmp, file);
4746
4757
  }
4747
4758
  function windowFile(windowId) {
4748
- return join21(windowsDir(), `${windowId}.json`);
4759
+ return join20(windowsDir(), `${windowId}.json`);
4749
4760
  }
4750
4761
  function writeWindow(windowId, rec) {
4751
4762
  if (!WINDOW_ID_RE.test(windowId)) return;
@@ -4760,7 +4771,7 @@ function windowSecret(windowId) {
4760
4771
  function readWindow(windowId) {
4761
4772
  if (!WINDOW_ID_RE.test(windowId)) return null;
4762
4773
  try {
4763
- const r = JSON.parse(readFileSync15(windowFile(windowId), "utf8"));
4774
+ const r = JSON.parse(readFileSync14(windowFile(windowId), "utf8"));
4764
4775
  return r && typeof r.pid === "number" ? r : null;
4765
4776
  } catch {
4766
4777
  return null;
@@ -4781,9 +4792,9 @@ function removeWindow(windowId) {
4781
4792
  return;
4782
4793
  }
4783
4794
  for (const n of names) {
4784
- const f = join21(bySessionDir(), n);
4795
+ const f = join20(bySessionDir(), n);
4785
4796
  try {
4786
- if (readFileSync15(f, "utf8").trim() === windowId) rmSync10(f, { force: true });
4797
+ if (readFileSync14(f, "utf8").trim() === windowId) rmSync10(f, { force: true });
4787
4798
  } catch {
4788
4799
  }
4789
4800
  }
@@ -4792,19 +4803,19 @@ function readBySession(sessionId) {
4792
4803
  const safe = safeSid2(sessionId);
4793
4804
  if (!safe) return null;
4794
4805
  try {
4795
- const id = readFileSync15(join21(bySessionDir(), safe), "utf8").trim();
4806
+ const id = readFileSync14(join20(bySessionDir(), safe), "utf8").trim();
4796
4807
  return WINDOW_ID_RE.test(id) ? id : null;
4797
4808
  } catch {
4798
4809
  return null;
4799
4810
  }
4800
4811
  }
4801
4812
  function lastEventFile(windowId) {
4802
- return join21(windowsDir(), `${windowId}.last`);
4813
+ return join20(windowsDir(), `${windowId}.last`);
4803
4814
  }
4804
4815
  function readLastEvent(windowId) {
4805
4816
  if (!WINDOW_ID_RE.test(windowId)) return null;
4806
4817
  try {
4807
- const e = JSON.parse(readFileSync15(lastEventFile(windowId), "utf8"));
4818
+ const e = JSON.parse(readFileSync14(lastEventFile(windowId), "utf8"));
4808
4819
  return e && typeof e.ev === "string" && typeof e.at === "number" ? { ev: e.ev, sid: String(e.sid ?? ""), at: e.at } : null;
4809
4820
  } catch {
4810
4821
  return null;
@@ -4813,15 +4824,15 @@ function readLastEvent(windowId) {
4813
4824
  function channelPath(windowId, platform = process.platform, home = deskbuddyHome()) {
4814
4825
  if (platform === "win32") return `\\\\.\\pipe\\deskbuddy-${windowId}`;
4815
4826
  const name = `${windowId.slice(0, 16)}.sock`;
4816
- const primary = join21(home, "win", name);
4827
+ const primary = join20(home, "win", name);
4817
4828
  if (Buffer.byteLength(primary) <= 103) return primary;
4818
4829
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
4819
- return join21(tmpdir3(), `deskbuddy-${uid}`, name);
4830
+ return join20(tmpdir3(), `deskbuddy-${uid}`, name);
4820
4831
  }
4821
4832
  function ensureChannelDir(sockPath) {
4822
- const dir = join21(sockPath, "..");
4833
+ const dir = join20(sockPath, "..");
4823
4834
  try {
4824
- mkdirSync17(dir, { recursive: true, mode: 448 });
4835
+ mkdirSync16(dir, { recursive: true, mode: 448 });
4825
4836
  const st = statSync6(dir);
4826
4837
  if (typeof process.getuid === "function" && st.uid !== process.getuid()) return false;
4827
4838
  chmodSync5(dir, 448);
@@ -4841,38 +4852,44 @@ function windowForSession(sessionId) {
4841
4852
  }
4842
4853
 
4843
4854
  // src/core/launch.ts
4844
- import { mkdirSync as mkdirSync18, readFileSync as readFileSync16, renameSync as renameSync14, rmSync as rmSync11, writeFileSync as writeFileSync16 } from "fs";
4845
- import { join as join22 } from "path";
4855
+ import { mkdirSync as mkdirSync17, readFileSync as readFileSync15, renameSync as renameSync13, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
4856
+ import { join as join21 } from "path";
4846
4857
  var RUN_ID_RE = /^[a-f0-9]{32}$/;
4847
4858
  var SESSION_UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
4859
+ var LAUNCH_TOOLS = ["claude_code", "codex", "workbuddy"];
4860
+ function validPlan(p) {
4861
+ if (!LAUNCH_TOOLS.includes(p.tool)) return false;
4862
+ if (p.resume && (p.tool !== "claude_code" || !SESSION_UUID_RE.test(p.resume))) return false;
4863
+ return true;
4864
+ }
4848
4865
  function launchDir(home = deskbuddyHome()) {
4849
- return join22(home, "launch");
4866
+ return join21(home, "launch");
4850
4867
  }
4851
4868
  function planFile(runId) {
4852
- return join22(launchDir(), `${runId}.json`);
4869
+ return join21(launchDir(), `${runId}.json`);
4853
4870
  }
4854
4871
  function writeLaunch(runId, plan) {
4855
4872
  if (!RUN_ID_RE.test(runId)) throw new Error("bad runId");
4856
- if (plan.resume && !SESSION_UUID_RE.test(plan.resume)) throw new Error("bad session id");
4857
- mkdirSync18(launchDir(), { recursive: true, mode: 448 });
4873
+ if (!validPlan(plan)) throw new Error("bad launch plan");
4874
+ mkdirSync17(launchDir(), { recursive: true, mode: 448 });
4858
4875
  const tmp = `${planFile(runId)}.${process.pid}.tmp`;
4859
- writeFileSync16(tmp, JSON.stringify(plan), { encoding: "utf8", mode: 384 });
4876
+ writeFileSync15(tmp, JSON.stringify(plan), { encoding: "utf8", mode: 384 });
4860
4877
  restrictToUser(tmp);
4861
- renameSync14(tmp, planFile(runId));
4878
+ renameSync13(tmp, planFile(runId));
4862
4879
  }
4863
4880
  function claimLaunch(runId) {
4864
4881
  if (!RUN_ID_RE.test(runId)) return null;
4865
- const claimed = join22(launchDir(), `${runId}.${process.pid}.claimed`);
4882
+ const claimed = join21(launchDir(), `${runId}.${process.pid}.claimed`);
4866
4883
  try {
4867
- renameSync14(planFile(runId), claimed);
4884
+ renameSync13(planFile(runId), claimed);
4868
4885
  } catch {
4869
4886
  return null;
4870
4887
  }
4871
4888
  try {
4872
- const p = JSON.parse(readFileSync16(claimed, "utf8"));
4889
+ const p = JSON.parse(readFileSync15(claimed, "utf8"));
4873
4890
  if (!p || typeof p.cwd !== "string" || typeof p.resume !== "string") return null;
4874
- if (p.resume && !SESSION_UUID_RE.test(p.resume)) return null;
4875
- return p;
4891
+ if (p.tool === void 0) p.tool = "claude_code";
4892
+ return validPlan(p) ? p : null;
4876
4893
  } catch {
4877
4894
  return null;
4878
4895
  } finally {
@@ -4884,7 +4901,7 @@ function claimLaunch(runId) {
4884
4901
  }
4885
4902
  function launchPending(runId) {
4886
4903
  try {
4887
- readFileSync16(planFile(runId));
4904
+ readFileSync15(planFile(runId));
4888
4905
  return true;
4889
4906
  } catch {
4890
4907
  return false;
@@ -4893,9 +4910,9 @@ function launchPending(runId) {
4893
4910
 
4894
4911
  // src/daemon/openWindow.ts
4895
4912
  import { execFile as execFile4, execFileSync as execFileSync11, spawn as spawn3 } from "child_process";
4896
- import { chmodSync as chmodSync6, mkdirSync as mkdirSync19, rmSync as rmSync12, writeFileSync as writeFileSync17 } from "fs";
4913
+ import { chmodSync as chmodSync6, mkdirSync as mkdirSync18, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "fs";
4897
4914
  import { userInfo } from "os";
4898
- import { join as join23 } from "path";
4915
+ import { join as join22 } from "path";
4899
4916
  function safePath(p) {
4900
4917
  return /^(?:[A-Za-z]:[\\/]|\/)/.test(p) && !/[\r\n\0"'`$%;]/.test(p);
4901
4918
  }
@@ -4994,9 +5011,9 @@ var defaultOpenerDeps = (findWt2) => ({
4994
5011
  async function openTerminal(t, d) {
4995
5012
  checkTarget(t);
4996
5013
  if (d.platform === "darwin") {
4997
- mkdirSync19(launchDir(t.home), { recursive: true, mode: 448 });
4998
- const file = join23(launchDir(t.home), `${t.runId}.command`);
4999
- writeFileSync17(file, macCommandScript(t), { encoding: "utf8", mode: 448 });
5014
+ mkdirSync18(launchDir(t.home), { recursive: true, mode: 448 });
5015
+ const file = join22(launchDir(t.home), `${t.runId}.command`);
5016
+ writeFileSync16(file, macCommandScript(t), { encoding: "utf8", mode: 448 });
5000
5017
  chmodSync6(file, 448);
5001
5018
  await d.run("open", ["-a", "Terminal", file]);
5002
5019
  return "terminal";
@@ -5015,7 +5032,7 @@ async function openTerminal(t, d) {
5015
5032
  function removeCommandFile(home, runId) {
5016
5033
  if (!RUN_ID_RE.test(runId)) return;
5017
5034
  try {
5018
- rmSync12(join23(launchDir(home), `${runId}.command`), { force: true });
5035
+ rmSync12(join22(launchDir(home), `${runId}.command`), { force: true });
5019
5036
  } catch {
5020
5037
  }
5021
5038
  }
@@ -5419,24 +5436,24 @@ function startWakeServer(path, hub, secret, platform = process.platform) {
5419
5436
  }
5420
5437
 
5421
5438
  // src/core/appended.ts
5422
- import { chmodSync as chmodSync7, mkdirSync as mkdirSync20, readFileSync as readFileSync17, renameSync as renameSync15, rmSync as rmSync14, writeFileSync as writeFileSync18 } from "fs";
5423
- import { join as join24 } from "path";
5439
+ import { chmodSync as chmodSync7, mkdirSync as mkdirSync19, readFileSync as readFileSync16, renameSync as renameSync14, rmSync as rmSync14, writeFileSync as writeFileSync17 } from "fs";
5440
+ import { join as join23 } from "path";
5424
5441
  var APPENDED_MAX_ITEMS = 5;
5425
5442
  var APPENDED_TEXT_MAX = 500;
5426
5443
  var APPENDED_RESULT_MAX = 800;
5427
5444
  var APPENDED_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
5428
5445
  function appendedDir() {
5429
- return join24(deskbuddyHome(), "appended");
5446
+ return join23(deskbuddyHome(), "appended");
5430
5447
  }
5431
5448
  function appendedPath(sessionId) {
5432
5449
  const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
5433
- return safe ? join24(appendedDir(), `${safe}.json`) : null;
5450
+ return safe ? join23(appendedDir(), `${safe}.json`) : null;
5434
5451
  }
5435
5452
  function readAppended(sessionId) {
5436
5453
  const file = appendedPath(sessionId);
5437
5454
  if (!file) return [];
5438
5455
  try {
5439
- const d = JSON.parse(readFileSync17(file, "utf8"));
5456
+ const d = JSON.parse(readFileSync16(file, "utf8"));
5440
5457
  return Array.isArray(d.items) ? d.items : [];
5441
5458
  } catch {
5442
5459
  return [];
@@ -5449,26 +5466,26 @@ function writeAppended(sessionId, item) {
5449
5466
  const items = readAppended(sessionId).concat([
5450
5467
  { text: item.text.slice(0, APPENDED_TEXT_MAX), result: item.result.slice(0, APPENDED_RESULT_MAX), ok: item.ok, at: item.at }
5451
5468
  ]);
5452
- mkdirSync20(appendedDir(), { recursive: true, mode: 448 });
5469
+ mkdirSync19(appendedDir(), { recursive: true, mode: 448 });
5453
5470
  const tmp = `${file}.${process.pid}.tmp`;
5454
- writeFileSync18(tmp, JSON.stringify({ items: items.slice(-APPENDED_MAX_ITEMS) }), { encoding: "utf8", mode: 384 });
5471
+ writeFileSync17(tmp, JSON.stringify({ items: items.slice(-APPENDED_MAX_ITEMS) }), { encoding: "utf8", mode: 384 });
5455
5472
  chmodSync7(tmp, 384);
5456
- renameSync15(tmp, file);
5473
+ renameSync14(tmp, file);
5457
5474
  } catch {
5458
5475
  }
5459
5476
  }
5460
5477
  var RUNNING_TTL_MS = 60 * 60 * 1e3;
5461
5478
  function runningPath(sessionId) {
5462
5479
  const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
5463
- return safe ? join24(deskbuddyHome(), "running", `${safe}.json`) : null;
5480
+ return safe ? join23(deskbuddyHome(), "running", `${safe}.json`) : null;
5464
5481
  }
5465
5482
  function writeRunning(sessionId, now = Date.now()) {
5466
5483
  const file = runningPath(sessionId);
5467
5484
  if (!file) return;
5468
5485
  try {
5469
- mkdirSync20(join24(deskbuddyHome(), "running"), { recursive: true, mode: 448 });
5470
- writeFileSync18(`${file}.tmp`, JSON.stringify({ at: now, pid: process.pid }), { encoding: "utf8", mode: 384 });
5471
- renameSync15(`${file}.tmp`, file);
5486
+ mkdirSync19(join23(deskbuddyHome(), "running"), { recursive: true, mode: 448 });
5487
+ writeFileSync17(`${file}.tmp`, JSON.stringify({ at: now, pid: process.pid }), { encoding: "utf8", mode: 384 });
5488
+ renameSync14(`${file}.tmp`, file);
5472
5489
  } catch {
5473
5490
  }
5474
5491
  }
@@ -5590,6 +5607,13 @@ var Daemon = class {
5590
5607
  sendToWindow: deps.sendToWindow ?? ((id, req2, onReply) => sendToWindow(channelPath(id), windowSecret(id), req2, onReply)),
5591
5608
  gui: deps.gui ?? (() => guiAvailable(defaultGuiDeps())),
5592
5609
  openWindow: deps.openWindow ?? ((runId) => this.#openTerminal(runId)),
5610
+ launchable: deps.launchable ?? ((tool) => {
5611
+ try {
5612
+ return locate(tool) != null;
5613
+ } catch {
5614
+ return false;
5615
+ }
5616
+ }),
5593
5617
  endedCwd: deps.endedCwd ?? ((sid, tool, stateCwd) => resolveEndedCwd(sid, tool, {
5594
5618
  readEnded: (s) => readEnded(s) ?? (stateCwd ? { sessionId: s, tool, cwd: stateCwd, interactive: false, endedAt: 0 } : null),
5595
5619
  transcriptCwd: (s) => claudeTranscriptCwd(s),
@@ -5966,6 +5990,7 @@ var Daemon = class {
5966
5990
  }
5967
5991
  this.#seen.save();
5968
5992
  cmd.receivedAt = this.#deps.now();
5993
+ log(`\u6536\u5230\u6307\u4EE4 ${cmd.instructionId} tool=${cmd.tool} ${cmd.sessionId ? `\u56DE\u590D ${cmd.sessionId.slice(0, 8)}` : "\u65B0\u5EFA"}`);
5969
5994
  this.#route(cmd);
5970
5995
  }
5971
5996
  /**
@@ -6023,7 +6048,8 @@ var Daemon = class {
6023
6048
  const sid = resolveAlias(cmd.sessionId);
6024
6049
  if (this.#wake.submit(cmd, sid)) return;
6025
6050
  }
6026
- if (cmd.tool === "claude_code" && !isExpired(cmd, this.#deps.now())) {
6051
+ const opensWindow = (cmd.tool === "claude_code" || !cmd.sessionId && LAUNCH_TOOLS.includes(cmd.tool)) && this.#launchable(cmd.tool);
6052
+ if (opensWindow && !isExpired(cmd, this.#deps.now())) {
6027
6053
  void this.#track(this.#openOrDispatch(cmd)).catch((e) => {
6028
6054
  log(`\u6307\u4EE4 ${cmd.instructionId} \u5F00\u7A97\u6D41\u7A0B\u51FA\u9519\uFF1A${e?.stack ?? String(e)}`);
6029
6055
  this.#report(cmd, cmd.sessionId, "failed", "", "window_failed");
@@ -6046,11 +6072,22 @@ var Daemon = class {
6046
6072
  }
6047
6073
  this.#dispatch(cmd);
6048
6074
  }
6075
+ /** 开窗前预检:找不到可执行文件就走原后台路径(它按原方式快速回报找不到),不开一个立刻 127 的窗口 */
6076
+ #launchable(tool) {
6077
+ let ok = false;
6078
+ try {
6079
+ ok = this.#deps.launchable(tool);
6080
+ } catch {
6081
+ ok = false;
6082
+ }
6083
+ if (!ok) log(`\u672C\u673A\u627E\u4E0D\u5230 ${tool} \u7684\u53EF\u6267\u884C\u6587\u4EF6\uFF0C\u4E0D\u5F00\u7A97\uFF0C\u6309\u540E\u53F0\u8DEF\u5F84\u5904\u7406`);
6084
+ return ok;
6085
+ }
6049
6086
  /**
6050
6087
  * 新建任务、会话已结束 → 开窗;会话还活着(后台 -p 在跑、或在用户自己开的窗口里)→ 原来的路径(排队 / window_open)
6051
6088
  */
6052
6089
  async #openOrDispatch(cmd) {
6053
- if (tooLongToInject(cmd.text)) {
6090
+ if (cmd.tool === "claude_code" && this.#guiAvailable() && tooLongToInject(cmd.text)) {
6054
6091
  log(`\u6307\u4EE4 ${cmd.instructionId} \u592A\u957F\uFF08${cmd.text.length} \u5B57\uFF09\uFF0C\u4E0D\u6267\u884C`);
6055
6092
  this.#report(cmd, cmd.sessionId ? resolveAlias(cmd.sessionId) : "", "rejected", "", TEXT_TOO_LONG);
6056
6093
  return;
@@ -6101,6 +6138,13 @@ var Daemon = class {
6101
6138
  release2();
6102
6139
  }
6103
6140
  }
6141
+ #guiAvailable() {
6142
+ try {
6143
+ return this.#deps.gui();
6144
+ } catch {
6145
+ return false;
6146
+ }
6147
+ }
6104
6148
  /**
6105
6149
  * 开窗 → 等认领 → 首句经通道交给窗口;任何一步起不来 → 后台 -p(回报 started/headless)。
6106
6150
  * 返回时:窗口已收下首句 / 已按失败回报 / 降级的后台那次已跑完
@@ -6110,16 +6154,10 @@ var Daemon = class {
6110
6154
  log(`\u6307\u4EE4 ${cmd.instructionId} ${why}\uFF0C\u6539\u540E\u53F0\u6267\u884C`);
6111
6155
  return this.#headless(c, sid, cwd);
6112
6156
  };
6113
- let gui = false;
6114
- try {
6115
- gui = this.#deps.gui();
6116
- } catch {
6117
- gui = false;
6118
- }
6119
- if (!gui) return headless("\u6CA1\u6709\u56FE\u5F62\u4F1A\u8BDD\uFF08SSH / \u9501\u5B9A\u7684\u670D\u52A1\u4F1A\u8BDD / Linux\uFF09");
6157
+ if (!this.#guiAvailable()) return headless("\u6CA1\u6709\u56FE\u5F62\u4F1A\u8BDD\uFF08SSH / \u9501\u5B9A\u7684\u670D\u52A1\u4F1A\u8BDD / Linux\uFF09");
6120
6158
  const runId = randomBytes7(16).toString("hex");
6121
6159
  try {
6122
- writeLaunch(runId, { tool: "claude_code", cwd, resume: sid, at: this.#deps.now() });
6160
+ writeLaunch(runId, { tool: cmd.tool, cwd, resume: sid, at: this.#deps.now() });
6123
6161
  } catch (e) {
6124
6162
  return headless(`\u5199\u4EA4\u63A5\u5355\u5931\u8D25\uFF1A${e.message}`);
6125
6163
  }
@@ -6201,7 +6239,7 @@ var Daemon = class {
6201
6239
  }
6202
6240
  return this.#queue.enqueue(
6203
6241
  sid,
6204
- () => this.#execute(cmd, sid, cwd, cmd.tool, true),
6242
+ () => this.#execute(cmd, sid, cwd, true),
6205
6243
  (e) => {
6206
6244
  log(`\u6307\u4EE4 ${cmd.instructionId} \u6267\u884C\u65F6\u51FA\u9519\uFF1A${e?.stack ?? String(e)}`);
6207
6245
  this.#report(cmd, sid, "failed", "", "exec_failed");
@@ -6330,8 +6368,6 @@ var Daemon = class {
6330
6368
  }
6331
6369
  const target = resolveTarget(cmd, {
6332
6370
  readState,
6333
- readHandle,
6334
- handleIsUsable,
6335
6371
  remoteCwd: () => loadConfig().remoteCwd || "",
6336
6372
  alias: resolveAlias,
6337
6373
  dirOf: (ref) => dirOf(ref),
@@ -6343,9 +6379,14 @@ var Daemon = class {
6343
6379
  return;
6344
6380
  }
6345
6381
  if (target.sessionId) this.#coord.cancelSession(target.sessionId);
6382
+ const key = target.sessionId || `new:${cmd.instructionId}`;
6383
+ if (this.#queue.busy(key)) {
6384
+ log(`\u6307\u4EE4 ${cmd.instructionId} \u4F1A\u8BDD ${key.slice(0, 8)} \u524D\u9762\u8FD8\u6709\u4EFB\u52A1\uFF0C\u6392\u961F`);
6385
+ this.#report(cmd, target.sessionId, "queued", "headless");
6386
+ }
6346
6387
  this.#queue.enqueue(
6347
- target.sessionId,
6348
- () => this.#execute(cmd, target.sessionId, target.cwd, target.handleKey),
6388
+ key,
6389
+ () => this.#execute(cmd, target.sessionId, target.cwd),
6349
6390
  (e) => {
6350
6391
  log(`\u6307\u4EE4 ${cmd.instructionId} \u6267\u884C\u65F6\u51FA\u9519\uFF1A${e?.stack ?? String(e)}`);
6351
6392
  this.#report(cmd, target.sessionId, "failed", "", "exec_failed");
@@ -6390,7 +6431,7 @@ var Daemon = class {
6390
6431
  log(`\u6307\u4EE4 ${cmd.instructionId} \u4F1A\u8BDD\u5728\u7535\u8111\u7A97\u53E3\u91CC\u7A7A\u95F2\uFF0C\u540E\u53F0\u7EED\u8DD1`);
6391
6432
  writeRunning(sid, this.#deps.now());
6392
6433
  try {
6393
- await this.#execute(cmd, sid, runCwd, cmd.tool, true, (r) => {
6434
+ await this.#execute(cmd, sid, runCwd, true, (r) => {
6394
6435
  const now = readState(sid);
6395
6436
  if (!now || now.interactive !== true && now.embedded !== true || !now.pid || !isAlive(now.pid, now.pidName)) return;
6396
6437
  writeAppended(sid, { text: cmd.text, result: resultSummary(cmd.tool, r.stdout), ok: r.ok, at: this.#deps.now() });
@@ -6404,7 +6445,12 @@ var Daemon = class {
6404
6445
  this.#report(cmd, sid, "failed", "", "exec_failed");
6405
6446
  });
6406
6447
  }
6407
- async #execute(cmd, sessionId, cwd, handleKey = cmd.tool, ended = false, onResult) {
6448
+ async #execute(cmd, sessionId, cwd, ended = false, onResult) {
6449
+ if (isExpired(cmd, this.#deps.now())) {
6450
+ log(`\u6307\u4EE4 ${cmd.instructionId} \u6392\u961F\u5230\u6267\u884C\u65F6\u5DF2\u8FC7\u671F\uFF0C\u4E0D\u6267\u884C`);
6451
+ this.#report(cmd, sessionId, "rejected", "", "expired");
6452
+ return;
6453
+ }
6408
6454
  if (sessionId && !ended) {
6409
6455
  const fresh = readState(sessionId);
6410
6456
  if (!fresh) {
@@ -6437,12 +6483,7 @@ var Daemon = class {
6437
6483
  }
6438
6484
  if (!sessionId) {
6439
6485
  const newId = result.sessionId ?? parseSessionId(cmd.tool, result.stdout);
6440
- if (newId) {
6441
- writeHandle(handleKey, { sessionId: newId, cwd, createdAt: this.#deps.now() });
6442
- log(`\u6307\u4EE4 ${cmd.instructionId} \u5EFA\u51FA\u4F1A\u8BDD ${newId}\uFF0C\u5DF2\u8BB0\u4E3A ${cmd.tool} \u7684\u8FDC\u7A0B\u4F1A\u8BDD`);
6443
- } else {
6444
- log(`\u6307\u4EE4 ${cmd.instructionId} \u672A\u80FD\u89E3\u6790\u51FA\u4F1A\u8BDD id\uFF0C\u4E0B\u6B21\u4ECD\u4F1A\u65B0\u5EFA`);
6445
- }
6486
+ log(`\u6307\u4EE4 ${cmd.instructionId} ${newId ? `\u5EFA\u51FA\u4F1A\u8BDD ${newId}` : "\u672A\u80FD\u89E3\u6790\u51FA\u4F1A\u8BDD id"}`);
6446
6487
  }
6447
6488
  }
6448
6489
  #gateTarget(runId) {
@@ -6565,7 +6606,7 @@ async function runDaemonCommand(opts = {}) {
6565
6606
  }
6566
6607
  if (opts.foreground) process.env.DESKBUDDY_DAEMON_FOREGROUND = "1";
6567
6608
  try {
6568
- writeFileSync19(pidFilePath(), String(process.pid), "utf8");
6609
+ writeFileSync18(pidFilePath(), String(process.pid), "utf8");
6569
6610
  } catch {
6570
6611
  }
6571
6612
  let daemon;
@@ -6612,15 +6653,16 @@ async function runDaemonCommand(opts = {}) {
6612
6653
  }
6613
6654
 
6614
6655
  // src/core/registry.ts
6615
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync18, writeFileSync as writeFileSync20 } from "fs";
6616
- import { join as join25 } from "path";
6656
+ import { mkdirSync as mkdirSync20, readFileSync as readFileSync17, writeFileSync as writeFileSync19 } from "fs";
6657
+ import { join as join24 } from "path";
6617
6658
  var FALLBACK = {
6618
6659
  version: "builtin",
6619
6660
  tools: {
6620
6661
  claude_code: {
6621
6662
  format: "cc-hook",
6622
6663
  processNames: ["claude"],
6623
- titleSources: ["cc-ai-title", "git-branch", "project"],
6664
+ titleSources: ["cc-ai-title", "first-prompt", "git-branch", "project"],
6665
+ turnEvents: ["UserPromptSubmit"],
6624
6666
  events: {
6625
6667
  UserPromptSubmit: "watch",
6626
6668
  PreToolUse: "watch",
@@ -6639,6 +6681,7 @@ var FALLBACK = {
6639
6681
  // codex-thread-name 是机会主义的一级:session_index.jsonl 里的 thread_name
6640
6682
  // 只有 /rename 或 VS Code 扩展起过名的才有,多数是空的,取不到用首条指令
6641
6683
  titleSources: ["codex-thread-name", "first-prompt", "git-branch", "project"],
6684
+ turnEvents: ["UserPromptSubmit"],
6642
6685
  events: {
6643
6686
  UserPromptSubmit: "watch",
6644
6687
  PreToolUse: "watch",
@@ -6654,6 +6697,7 @@ var FALLBACK = {
6654
6697
  // WorkBuddy 的 transcript 与 Claude Code 同格式,也写 ai-title(2026-10-01 WorkBuddy 5.6.2 实测);
6655
6698
  // 工作目录是按时间自动建的,目录名不能当标题
6656
6699
  titleSources: ["cc-ai-title", "first-prompt", "git-branch", "project"],
6700
+ turnEvents: ["UserPromptSubmit"],
6657
6701
  events: {
6658
6702
  UserPromptSubmit: "watch",
6659
6703
  PreToolUse: "watch",
@@ -6670,6 +6714,7 @@ var FALLBACK = {
6670
6714
  // 桌面端会话库里的标题(先是首条指令,后被换成 AI 摘要);命令行跑的会话不在库里,落到首条指令
6671
6715
  // (CLI 1.1.61 实测 transcript 里没有 ai-title,UserPromptSubmit 带 prompt)
6672
6716
  titleSources: ["qoder-session-title", "first-prompt", "git-branch", "project"],
6717
+ turnEvents: ["UserPromptSubmit"],
6673
6718
  events: {
6674
6719
  UserPromptSubmit: "watch",
6675
6720
  PreToolUse: "watch",
@@ -6684,6 +6729,7 @@ var FALLBACK = {
6684
6729
  format: "cc-hook",
6685
6730
  processNames: ["Qoder"],
6686
6731
  titleSources: ["qoder-session-title", "first-prompt", "git-branch", "project"],
6732
+ turnEvents: ["UserPromptSubmit"],
6687
6733
  events: {
6688
6734
  UserPromptSubmit: "watch",
6689
6735
  PreToolUse: "watch",
@@ -6698,6 +6744,7 @@ var FALLBACK = {
6698
6744
  processNames: ["opencode"],
6699
6745
  // opencode 自己生成的会话标题(插件从 session.updated 取,随事件传 oc_session_title),没有就用首条指令
6700
6746
  titleSources: ["oc-session-title", "first-prompt", "git-branch", "project"],
6747
+ turnEvents: ["UserPromptSubmit"],
6701
6748
  events: {
6702
6749
  UserPromptSubmit: "watch",
6703
6750
  PreToolUse: "watch",
@@ -6710,7 +6757,7 @@ var FALLBACK = {
6710
6757
  }
6711
6758
  };
6712
6759
  function registryCachePath() {
6713
- return join25(deskbuddyHome(), "tool-registry.json");
6760
+ return join24(deskbuddyHome(), "tool-registry.json");
6714
6761
  }
6715
6762
  function withFallbackTools(registry) {
6716
6763
  if (!registry || typeof registry !== "object" || !registry.tools || typeof registry.tools !== "object") return FALLBACK;
@@ -6718,7 +6765,7 @@ function withFallbackTools(registry) {
6718
6765
  }
6719
6766
  function loadRegistry() {
6720
6767
  try {
6721
- return withFallbackTools(JSON.parse(readFileSync18(registryCachePath(), "utf8")));
6768
+ return withFallbackTools(JSON.parse(readFileSync17(registryCachePath(), "utf8")));
6722
6769
  } catch {
6723
6770
  return FALLBACK;
6724
6771
  }
@@ -6726,15 +6773,15 @@ function loadRegistry() {
6726
6773
  async function refreshRegistry() {
6727
6774
  try {
6728
6775
  const registry = await api.toolRegistry();
6729
- mkdirSync21(deskbuddyHome(), { recursive: true });
6730
- writeFileSync20(registryCachePath(), JSON.stringify(registry, null, 2), "utf8");
6776
+ mkdirSync20(deskbuddyHome(), { recursive: true });
6777
+ writeFileSync19(registryCachePath(), JSON.stringify(registry, null, 2), "utf8");
6731
6778
  } catch {
6732
6779
  }
6733
6780
  }
6734
6781
 
6735
6782
  // src/adapters/ccHook.ts
6736
- import { chmodSync as chmodSync8, existsSync as existsSync14, mkdirSync as mkdirSync22, readFileSync as readFileSync19, renameSync as renameSync16, rmSync as rmSync16, statSync as statSync7, writeFileSync as writeFileSync21 } from "fs";
6737
- import { dirname as dirname9, join as join26 } from "path";
6783
+ import { chmodSync as chmodSync8, existsSync as existsSync14, mkdirSync as mkdirSync21, readFileSync as readFileSync18, renameSync as renameSync15, rmSync as rmSync16, statSync as statSync7, writeFileSync as writeFileSync20 } from "fs";
6784
+ import { dirname as dirname9, join as join25 } from "path";
6738
6785
  var CC_EVENTS = [
6739
6786
  "UserPromptSubmit",
6740
6787
  "PreToolUse",
@@ -6745,7 +6792,7 @@ var CC_EVENTS = [
6745
6792
  ];
6746
6793
  function readSettings(path) {
6747
6794
  if (!existsSync14(path)) return {};
6748
- const raw = readFileSync19(path, "utf8");
6795
+ const raw = readFileSync18(path, "utf8");
6749
6796
  if (raw.trim() === "") return {};
6750
6797
  try {
6751
6798
  return JSON.parse(raw);
@@ -6754,7 +6801,7 @@ function readSettings(path) {
6754
6801
  }
6755
6802
  }
6756
6803
  function writeSettings(path, settings) {
6757
- mkdirSync22(dirname9(path), { recursive: true });
6804
+ mkdirSync21(dirname9(path), { recursive: true });
6758
6805
  let mode;
6759
6806
  try {
6760
6807
  mode = statSync7(path).mode & 511;
@@ -6762,9 +6809,9 @@ function writeSettings(path, settings) {
6762
6809
  mode = void 0;
6763
6810
  }
6764
6811
  const tmp = path + ".deskbuddy.tmp";
6765
- writeFileSync21(tmp, JSON.stringify(settings, null, 2), "utf8");
6812
+ writeFileSync20(tmp, JSON.stringify(settings, null, 2), "utf8");
6766
6813
  if (mode !== void 0) chmodSync8(tmp, mode);
6767
- renameSync16(tmp, path);
6814
+ renameSync15(tmp, path);
6768
6815
  }
6769
6816
  function mentions(command, script) {
6770
6817
  return !!command && (command.includes(script) || command.includes(fwdSlash(script)));
@@ -6886,8 +6933,8 @@ function retagCcHook(settingsPath, scripts, from, to) {
6886
6933
  function uninstallCcHook(settingsPath, hookPath) {
6887
6934
  const settings = readSettings(settingsPath);
6888
6935
  if (!settings.hooks) return;
6889
- const gatePath = join26(dirname9(hookPath), "gate.cjs");
6890
- const wakePath = join26(dirname9(hookPath), "wake.cjs");
6936
+ const gatePath = join25(dirname9(hookPath), "gate.cjs");
6937
+ const wakePath = join25(dirname9(hookPath), "wake.cjs");
6891
6938
  for (const event of Object.keys(settings.hooks)) {
6892
6939
  const groups = settings.hooks[event] ?? [];
6893
6940
  const kept = [];
@@ -6901,14 +6948,14 @@ function uninstallCcHook(settingsPath, hookPath) {
6901
6948
  }
6902
6949
  writeSettings(settingsPath, settings);
6903
6950
  try {
6904
- rmSync16(join26(dirname9(hookPath), "wake", "tokens"), { recursive: true, force: true });
6951
+ rmSync16(join25(dirname9(hookPath), "wake", "tokens"), { recursive: true, force: true });
6905
6952
  } catch {
6906
6953
  }
6907
6954
  }
6908
6955
 
6909
6956
  // src/adapters/codexHook.ts
6910
- import { chmodSync as chmodSync9, existsSync as existsSync15, mkdirSync as mkdirSync23, readFileSync as readFileSync20, renameSync as renameSync17, statSync as statSync8, writeFileSync as writeFileSync22 } from "fs";
6911
- import { dirname as dirname10, join as join27 } from "path";
6957
+ import { chmodSync as chmodSync9, existsSync as existsSync15, mkdirSync as mkdirSync22, readFileSync as readFileSync19, renameSync as renameSync16, statSync as statSync8, writeFileSync as writeFileSync21 } from "fs";
6958
+ import { dirname as dirname10, join as join26 } from "path";
6912
6959
  var CODEX_MARKER_BEGIN = "# >>> deskbuddy >>>";
6913
6960
  var CODEX_MARKER_END = "# <<< deskbuddy <<<";
6914
6961
  var CODEX_EVENTS = [
@@ -6921,7 +6968,7 @@ var CODEX_EVENTS = [
6921
6968
  ];
6922
6969
  var CODEX_TRUST_NOTICE = "\u26A0\uFE0F \u4E0B\u6B21\u542F\u52A8 Codex \u65F6\u4F1A\u8981\u6C42\u4F60\u786E\u8BA4\u4FE1\u4EFB\u8FD9\u4E2A hook\uFF0C\u8BF7\u9009\u62E9\u540C\u610F\u2014\u2014\u4E0D\u786E\u8BA4\u7684\u8BDD Codex \u4E0D\u4F1A\u6267\u884C\u5B83\u3002\n DeskBuddy \u5347\u7EA7\uFF08\u542B\u81EA\u52A8\u5347\u7EA7\uFF09\u65B0\u589E hook \u6761\u76EE\u65F6\uFF0CCodex \u53EA\u4F1A\u5C31\u65B0\u6761\u76EE\u518D\u95EE\u4E00\u6B21\uFF0C\u540C\u6837\u9009\u540C\u610F\uFF1B\u539F\u6709\u6761\u76EE\u4E0D\u53D7\u5F71\u54CD\u3001\u72B6\u6001\u7167\u5E38\u4E0A\u62A5\u3002\n Codex \u7ECF `--remote` \u8FDE\u5E38\u9A7B app-server \u4F7F\u7528\u65F6\uFF0C\u7A97\u53E3\u91CC\u7684\u6388\u6743\u6846\u4E5F\u80FD\u5728\u8BBE\u5907\u4E0A\u786E\u8BA4\uFF08\u5B88\u62A4\u8FDB\u7A0B\u5728\u8DD1\u65F6\u751F\u6548\uFF09";
6923
6970
  function codexConfigPath() {
6924
- return join27(codexHome(), "config.toml");
6971
+ return join26(codexHome(), "config.toml");
6925
6972
  }
6926
6973
  var CODEX_APPENDED_TIMEOUT_S = 3;
6927
6974
  function renderBlock(hookPath, events, tool, platform) {
@@ -6940,7 +6987,7 @@ function renderBlock(hookPath, events, tool, platform) {
6940
6987
  return lines2.join("\n");
6941
6988
  }
6942
6989
  function writeAtomic(path, content) {
6943
- mkdirSync23(dirname10(path), { recursive: true });
6990
+ mkdirSync22(dirname10(path), { recursive: true });
6944
6991
  let mode;
6945
6992
  try {
6946
6993
  mode = statSync8(path).mode & 511;
@@ -6948,9 +6995,9 @@ function writeAtomic(path, content) {
6948
6995
  mode = void 0;
6949
6996
  }
6950
6997
  const tmp = path + ".deskbuddy.tmp";
6951
- writeFileSync22(tmp, content, "utf8");
6998
+ writeFileSync21(tmp, content, "utf8");
6952
6999
  if (mode !== void 0) chmodSync9(tmp, mode);
6953
- renameSync17(tmp, path);
7000
+ renameSync16(tmp, path);
6954
7001
  }
6955
7002
  function assertSafeToAppend(raw, path) {
6956
7003
  for (const quote of ['"""', "'''"]) {
@@ -6960,7 +7007,7 @@ function assertSafeToAppend(raw, path) {
6960
7007
  }
6961
7008
  }
6962
7009
  function installCodexHook(configPath2, hookPath, events = CODEX_EVENTS, tool = "codex", platform = process.platform) {
6963
- const raw = existsSync15(configPath2) ? readFileSync20(configPath2, "utf8") : "";
7010
+ const raw = existsSync15(configPath2) ? readFileSync19(configPath2, "utf8") : "";
6964
7011
  const block = renderBlock(hookPath, events, tool, platform);
6965
7012
  if (raw.includes(CODEX_MARKER_BEGIN)) {
6966
7013
  const lines2 = raw.split("\n");
@@ -6978,7 +7025,7 @@ function installCodexHook(configPath2, hookPath, events = CODEX_EVENTS, tool = "
6978
7025
  }
6979
7026
  function uninstallCodexHook(configPath2) {
6980
7027
  if (!existsSync15(configPath2)) return;
6981
- const raw = readFileSync20(configPath2, "utf8");
7028
+ const raw = readFileSync19(configPath2, "utf8");
6982
7029
  const lines2 = raw.split("\n");
6983
7030
  const begin = lines2.findIndex((l) => l.trim() === CODEX_MARKER_BEGIN);
6984
7031
  if (begin === -1) return;
@@ -6992,11 +7039,11 @@ function uninstallCodexHook(configPath2) {
6992
7039
  }
6993
7040
 
6994
7041
  // src/commands/tools.ts
6995
- import { existsSync as existsSync16, readFileSync as readFileSync21 } from "fs";
7042
+ import { existsSync as existsSync16, readFileSync as readFileSync20 } from "fs";
6996
7043
  function fileContains(path, needle) {
6997
7044
  try {
6998
7045
  if (!existsSync16(path)) return false;
6999
- return readFileSync21(path, "utf8").includes(needle);
7046
+ return readFileSync20(path, "utf8").includes(needle);
7000
7047
  } catch {
7001
7048
  return false;
7002
7049
  }
@@ -7073,13 +7120,13 @@ function subscribedEvents(registry, tool, fallback) {
7073
7120
  }
7074
7121
 
7075
7122
  // src/commands/enable.ts
7076
- import { dirname as dirname13, join as join30 } from "path";
7123
+ import { dirname as dirname13, join as join29 } from "path";
7077
7124
 
7078
7125
  // src/wrap/shim.ts
7079
7126
  import { execFileSync as execFileSync12 } from "child_process";
7080
- import { chmodSync as chmodSync10, existsSync as existsSync17, mkdirSync as mkdirSync24, readdirSync as readdirSync6, readFileSync as readFileSync22, rmdirSync, rmSync as rmSync17, writeFileSync as writeFileSync23 } from "fs";
7081
- import { homedir as homedir6 } from "os";
7082
- import { delimiter as delimiter2, join as join28, resolve as resolve3 } from "path";
7127
+ import { chmodSync as chmodSync10, existsSync as existsSync17, mkdirSync as mkdirSync23, readdirSync as readdirSync6, readFileSync as readFileSync21, rmdirSync, rmSync as rmSync17, writeFileSync as writeFileSync22 } from "fs";
7128
+ import { homedir as homedir7 } from "os";
7129
+ import { delimiter as delimiter2, join as join27, resolve as resolve3 } from "path";
7083
7130
  var SHIM_FALLBACK_EXIT = 213;
7084
7131
  var WIN_SHIM_ENABLED = false;
7085
7132
  var SHIM_COMMANDS = { claude_code: "claude", codex: "codex" };
@@ -7089,10 +7136,10 @@ var RC_END = "# <<< deskbuddy <<<";
7089
7136
  var RC_FILES = [".zshrc", ".bashrc"];
7090
7137
  var WIN_PENDING = "Windows \u7A97\u53E3\u63A5\u7BA1\u5F85\u9A8C\u8BC1\uFF0C\u6682\u672A\u5F00\u542F";
7091
7138
  function shimDir() {
7092
- return join28(deskbuddyHome(), "bin");
7139
+ return join27(deskbuddyHome(), "bin");
7093
7140
  }
7094
7141
  function shimFile(binDir, command, platform = process.platform) {
7095
- return join28(binDir, platform === "win32" ? `${command}.cmd` : command);
7142
+ return join27(binDir, platform === "win32" ? `${command}.cmd` : command);
7096
7143
  }
7097
7144
  function shQuote3(s) {
7098
7145
  return `'${s.replace(/'/g, `'\\''`)}'`;
@@ -7221,10 +7268,10 @@ function removeRcBlock(content) {
7221
7268
  return content.slice(0, start) + content.slice(end);
7222
7269
  }
7223
7270
  function readRc(p) {
7224
- return readFileSync22(p).toString("latin1");
7271
+ return readFileSync21(p).toString("latin1");
7225
7272
  }
7226
7273
  function writeRc(p, latin1) {
7227
- writeFileSync23(p, Buffer.from(latin1, "latin1"));
7274
+ writeFileSync22(p, Buffer.from(latin1, "latin1"));
7228
7275
  }
7229
7276
  function toLatin1(utf8) {
7230
7277
  return Buffer.from(utf8, "utf8").toString("latin1");
@@ -7269,7 +7316,7 @@ function psWinPath() {
7269
7316
  function defaultShimEnv(nodePath, cliPath) {
7270
7317
  const env = process.env;
7271
7318
  const isolated = !!env.VITEST || !!env.DESKBUDDY_HOME;
7272
- const rcHome = env.DESKBUDDY_RC_HOME || (isolated ? null : homedir6());
7319
+ const rcHome = env.DESKBUDDY_RC_HOME || (isolated ? null : homedir7());
7273
7320
  return {
7274
7321
  platform: process.platform,
7275
7322
  binDir: shimDir(),
@@ -7293,11 +7340,11 @@ function installShim(tool, e) {
7293
7340
  if (!node || !cli || !bin) {
7294
7341
  return { ok: false, message: "\u7A97\u53E3\u63A5\u7BA1\u6CA1\u88C5\uFF1A\u5B89\u88C5\u8DEF\u5F84\u91CC\u6709\u975E ASCII \u5B57\u7B26\uFF0CWindows \u7684 .cmd \u8BFB\u4E0D\u4E86\uFF08\u4E0D\u5F71\u54CD\u5176\u5B83\u529F\u80FD\uFF09" };
7295
7342
  }
7296
- mkdirSync24(e.binDir, { recursive: true });
7297
- writeFileSync23(file, cmdScript(tool, command, node, cli, bin), "utf8");
7343
+ mkdirSync23(e.binDir, { recursive: true });
7344
+ writeFileSync22(file, cmdScript(tool, command, node, cli, bin), "utf8");
7298
7345
  } else {
7299
- mkdirSync24(e.binDir, { recursive: true, mode: 448 });
7300
- writeFileSync23(file, shScript(tool, command, e.nodePath, e.cliPath, e.binDir), { encoding: "utf8", mode: 493 });
7346
+ mkdirSync23(e.binDir, { recursive: true, mode: 448 });
7347
+ writeFileSync22(file, shScript(tool, command, e.nodePath, e.cliPath, e.binDir), { encoding: "utf8", mode: 493 });
7301
7348
  chmodSync10(file, 493);
7302
7349
  }
7303
7350
  const touched = [];
@@ -7312,7 +7359,7 @@ function installShim(tool, e) {
7312
7359
  } else if (e.rcHome) {
7313
7360
  const block = toLatin1(rcBlock(e.binDir, e.rcHome));
7314
7361
  for (const name of RC_FILES) {
7315
- const rc = join28(e.rcHome, name);
7362
+ const rc = join27(e.rcHome, name);
7316
7363
  if (!existsSync17(rc)) continue;
7317
7364
  const next = addRcBlock(readRc(rc), block);
7318
7365
  if (next !== null) writeRc(rc, next);
@@ -7339,7 +7386,7 @@ function ourShims(binDir) {
7339
7386
  }
7340
7387
  return names.filter((n) => {
7341
7388
  try {
7342
- return readFileSync22(join28(binDir, n), "utf8").includes(SHIM_MARKER);
7389
+ return readFileSync21(join27(binDir, n), "utf8").includes(SHIM_MARKER);
7343
7390
  } catch {
7344
7391
  return false;
7345
7392
  }
@@ -7351,7 +7398,7 @@ function uninstallShim(tool, e) {
7351
7398
  let removed = false;
7352
7399
  const file = shimFile(e.binDir, command, e.platform);
7353
7400
  try {
7354
- if (existsSync17(file) && readFileSync22(file, "utf8").includes(SHIM_MARKER)) {
7401
+ if (existsSync17(file) && readFileSync21(file, "utf8").includes(SHIM_MARKER)) {
7355
7402
  rmSync17(file, { force: true });
7356
7403
  removed = true;
7357
7404
  }
@@ -7370,7 +7417,7 @@ function uninstallShim(tool, e) {
7370
7417
  }
7371
7418
  } else if (e.rcHome) {
7372
7419
  for (const name of RC_FILES) {
7373
- const rc = join28(e.rcHome, name);
7420
+ const rc = join27(e.rcHome, name);
7374
7421
  if (!existsSync17(rc)) continue;
7375
7422
  const next = removeRcBlock(readRc(rc));
7376
7423
  if (next !== null) {
@@ -7392,7 +7439,7 @@ function shimState(tool, binDir, pathEnv, platform = process.platform) {
7392
7439
  const file = shimFile(binDir, command, platform);
7393
7440
  let installed = false;
7394
7441
  try {
7395
- installed = readFileSync22(file, "utf8").includes(SHIM_MARKER);
7442
+ installed = readFileSync21(file, "utf8").includes(SHIM_MARKER);
7396
7443
  } catch {
7397
7444
  installed = false;
7398
7445
  }
@@ -7403,7 +7450,7 @@ function shimState(tool, binDir, pathEnv, platform = process.platform) {
7403
7450
  }
7404
7451
 
7405
7452
  // src/commands/migrateQoder.ts
7406
- import { dirname as dirname12, join as join29 } from "path";
7453
+ import { dirname as dirname12, join as join28 } from "path";
7407
7454
 
7408
7455
  // src/core/detect.ts
7409
7456
  import { existsSync as existsSync18 } from "fs";
@@ -7451,7 +7498,7 @@ function migrateLegacyQoder(out, detect = () => detectTools()) {
7451
7498
  try {
7452
7499
  const cfg = loadConfig();
7453
7500
  const hookPath = installedHookPath();
7454
- const scripts = [hookPath, join29(dirname12(hookPath), "gate.cjs")];
7501
+ const scripts = [hookPath, join28(dirname12(hookPath), "gate.cjs")];
7455
7502
  if (!retagCcHook(qoderCnSettingsPath(), scripts, "qoder", "qoder_cn")) return false;
7456
7503
  const hadQoder = cfg.enabledTools.includes("qoder");
7457
7504
  let intl = false;
@@ -7543,14 +7590,14 @@ function installTools(tools, out, shim = defaultShimEnvForInstall()) {
7543
7590
  installed.push(tool);
7544
7591
  out(`\u2705 ${tool}\uFF1Ahook \u5DF2\u88C5\u8FDB ${configPath2}\uFF08${events.length} \u4E2A\u4E8B\u4EF6\uFF09`);
7545
7592
  try {
7546
- if (adapter.installWindowGate?.(configPath2, join30(dirname13(hookPath), "gate.cjs"), tool)) {
7593
+ if (adapter.installWindowGate?.(configPath2, join29(dirname13(hookPath), "gate.cjs"), tool)) {
7547
7594
  out(` \u7A97\u53E3\u91CC\u5F39\u6388\u6743\u6846\u65F6\uFF0C\u8BBE\u5907\u4E0A\u4E5F\u80FD\u786E\u8BA4\uFF08\u5B88\u62A4\u8FDB\u7A0B\u5728\u8DD1\u65F6\u751F\u6548\uFF09`);
7548
7595
  }
7549
7596
  } catch (e) {
7550
7597
  out(`\u26A0\uFE0F ${tool}\uFF1A\u8BBE\u5907\u786E\u8BA4\u6388\u6743\u6CA1\u88C5\u4E0A\uFF1A${e.message}`);
7551
7598
  }
7552
7599
  try {
7553
- if (adapter.installRewake?.(configPath2, join30(dirname13(hookPath), "wake.cjs"), tool)) {
7600
+ if (adapter.installRewake?.(configPath2, join29(dirname13(hookPath), "wake.cjs"), tool)) {
7554
7601
  out(` \u7A97\u53E3\u5F00\u7740\u65F6\uFF0C\u8BBE\u5907\u6307\u4EE4\u76F4\u63A5\u5728\u8FD9\u4E2A\u7A97\u53E3\u91CC\u6267\u884C\uFF08\u5B88\u62A4\u8FDB\u7A0B\u5728\u8DD1\u65F6\u751F\u6548\uFF09`);
7555
7602
  }
7556
7603
  } catch (e) {
@@ -7651,8 +7698,8 @@ async function runDisable(tools, opts = {}) {
7651
7698
  }
7652
7699
 
7653
7700
  // src/commands/install.ts
7654
- import { mkdirSync as mkdirSync25, rmSync as rmSync18, writeFileSync as writeFileSync24 } from "fs";
7655
- import { join as join31 } from "path";
7701
+ import { mkdirSync as mkdirSync24, rmSync as rmSync18, writeFileSync as writeFileSync23 } from "fs";
7702
+ import { join as join30 } from "path";
7656
7703
 
7657
7704
  // src/commands/setup.ts
7658
7705
  import qrcode from "qrcode-terminal";
@@ -7660,9 +7707,9 @@ import qrcode from "qrcode-terminal";
7660
7707
  // src/commands/remoteCwd.ts
7661
7708
  import { existsSync as existsSync19, statSync as statSync9 } from "fs";
7662
7709
  import { createInterface } from "readline";
7663
- import { homedir as homedir7 } from "os";
7710
+ import { homedir as homedir8 } from "os";
7664
7711
  import { isAbsolute as isAbsolute2, resolve as resolve4 } from "path";
7665
- function normalizePath(raw, home = homedir7()) {
7712
+ function normalizePath(raw, home = homedir8()) {
7666
7713
  const s = (raw || "").trim();
7667
7714
  if (!s) return null;
7668
7715
  const expanded = s === "~" ? home : s.startsWith("~/") || process.platform === "win32" && s.startsWith("~\\") ? resolve4(home, s.slice(2)) : s;
@@ -7877,10 +7924,10 @@ function inSandbox(env) {
7877
7924
  }
7878
7925
  function precheckWritable(dirs) {
7879
7926
  for (const dir of dirs) {
7880
- const probe = join31(dir, `.deskbuddy-probe-${process.pid}`);
7927
+ const probe = join30(dir, `.deskbuddy-probe-${process.pid}`);
7881
7928
  try {
7882
- mkdirSync25(dir, { recursive: true });
7883
- writeFileSync24(probe, "");
7929
+ mkdirSync24(dir, { recursive: true });
7930
+ writeFileSync23(probe, "");
7884
7931
  rmSync18(probe, { force: true });
7885
7932
  } catch (e) {
7886
7933
  return { ok: false, dir, error: e.message };
@@ -8229,8 +8276,8 @@ async function runAutoSwitch(opts) {
8229
8276
  }
8230
8277
 
8231
8278
  // src/wrap/run.ts
8232
- import { randomBytes as randomBytes8 } from "crypto";
8233
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync27, statSync as statSync11, truncateSync as truncateSync3 } from "fs";
8279
+ import { randomBytes as randomBytes9 } from "crypto";
8280
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync26, statSync as statSync11, truncateSync as truncateSync3 } from "fs";
8234
8281
  import { join as join33 } from "path";
8235
8282
  import { StringDecoder } from "string_decoder";
8236
8283
 
@@ -8626,10 +8673,10 @@ function runDirect(real2, args, env = process.env) {
8626
8673
 
8627
8674
  // src/wrap/codexLaunch.ts
8628
8675
  import { spawn as spawn5 } from "child_process";
8629
- import { closeSync as closeSync4, mkdirSync as mkdirSync26, openSync as openSync4, readdirSync as readdirSync7, readFileSync as readFileSync23, rmSync as rmSync20, statSync as statSync10, truncateSync as truncateSync2, writeFileSync as writeFileSync25, renameSync as renameSync18 } from "fs";
8676
+ import { closeSync as closeSync4, mkdirSync as mkdirSync25, openSync as openSync4, readdirSync as readdirSync7, readFileSync as readFileSync22, rmSync as rmSync20, statSync as statSync10, truncateSync as truncateSync2, writeFileSync as writeFileSync24, renameSync as renameSync17 } from "fs";
8630
8677
  import { request } from "http";
8631
8678
  import { createServer as createServer4 } from "net";
8632
- import { join as join32 } from "path";
8679
+ import { join as join31 } from "path";
8633
8680
 
8634
8681
  // src/core/codexServer.ts
8635
8682
  import { execFileSync as execFileSync13 } from "child_process";
@@ -8797,10 +8844,10 @@ async function runCodexWindow(real2, args, d) {
8797
8844
  }
8798
8845
  var MAX_LOG_BYTES2 = 1024 * 1024;
8799
8846
  function appServerLogPath() {
8800
- return join32(deskbuddyHome(), "codex-app-server.log");
8847
+ return join31(deskbuddyHome(), "codex-app-server.log");
8801
8848
  }
8802
8849
  function windowsDir2() {
8803
- return join32(deskbuddyHome(), "codex-windows");
8850
+ return join31(deskbuddyHome(), "codex-windows");
8804
8851
  }
8805
8852
  function pidAlive2(pid) {
8806
8853
  try {
@@ -8813,7 +8860,7 @@ function pidAlive2(pid) {
8813
8860
  function daemonAlive(pidFile = pidFilePath(), args = processArgs) {
8814
8861
  let pid = 0;
8815
8862
  try {
8816
- pid = Number(readFileSync23(pidFile, "utf8").trim());
8863
+ pid = Number(readFileSync22(pidFile, "utf8").trim());
8817
8864
  } catch {
8818
8865
  return false;
8819
8866
  }
@@ -8847,7 +8894,7 @@ function freePort() {
8847
8894
  }
8848
8895
  function startServer(real2, url, env, cwd) {
8849
8896
  const logPath2 = appServerLogPath();
8850
- mkdirSync26(deskbuddyHome(), { recursive: true });
8897
+ mkdirSync25(deskbuddyHome(), { recursive: true });
8851
8898
  try {
8852
8899
  if (statSync10(logPath2).size > MAX_LOG_BYTES2) truncateSync2(logPath2, 0);
8853
8900
  } catch {
@@ -8881,14 +8928,14 @@ function killOwnServer(pid, url) {
8881
8928
  if (a && isAppServerArgs(a, url)) killServer(pid);
8882
8929
  }
8883
8930
  function recordPath(rec) {
8884
- return join32(windowsDir2(), `${rec.server}.json`);
8931
+ return join31(windowsDir2(), `${rec.server}.json`);
8885
8932
  }
8886
8933
  function writeRecord(rec) {
8887
8934
  try {
8888
- mkdirSync26(windowsDir2(), { recursive: true, mode: 448 });
8935
+ mkdirSync25(windowsDir2(), { recursive: true, mode: 448 });
8889
8936
  const p = recordPath(rec);
8890
- writeFileSync25(p + ".tmp", JSON.stringify(rec), { encoding: "utf8", mode: 384 });
8891
- renameSync18(p + ".tmp", p);
8937
+ writeFileSync24(p + ".tmp", JSON.stringify(rec), { encoding: "utf8", mode: 384 });
8938
+ renameSync17(p + ".tmp", p);
8892
8939
  } catch {
8893
8940
  }
8894
8941
  }
@@ -8906,10 +8953,10 @@ function reapWindows(dir = windowsDir2(), alive = pidAlive2, args = processArgs,
8906
8953
  return;
8907
8954
  }
8908
8955
  for (const n of names) {
8909
- const p = join32(dir, n);
8956
+ const p = join31(dir, n);
8910
8957
  let rec;
8911
8958
  try {
8912
- rec = JSON.parse(readFileSync23(p, "utf8"));
8959
+ rec = JSON.parse(readFileSync22(p, "utf8"));
8913
8960
  } catch {
8914
8961
  rmSync20(p, { force: true });
8915
8962
  continue;
@@ -8928,8 +8975,8 @@ function reapWindows(dir = windowsDir2(), alive = pidAlive2, args = processArgs,
8928
8975
  }
8929
8976
  function launchLog(line) {
8930
8977
  try {
8931
- mkdirSync26(deskbuddyHome(), { recursive: true });
8932
- writeFileSync25(join32(deskbuddyHome(), "wrap.log"), `${(/* @__PURE__ */ new Date()).toISOString()} [codex ${process.pid}] ${line}
8978
+ mkdirSync25(deskbuddyHome(), { recursive: true });
8979
+ writeFileSync24(join31(deskbuddyHome(), "wrap.log"), `${(/* @__PURE__ */ new Date()).toISOString()} [codex ${process.pid}] ${line}
8933
8980
  `, { flag: "a" });
8934
8981
  } catch {
8935
8982
  }
@@ -8951,6 +8998,129 @@ function defaultCodexWindowDeps(base) {
8951
8998
  };
8952
8999
  }
8953
9000
 
9001
+ // src/wrap/promptLaunch.ts
9002
+ import { randomBytes as randomBytes8 } from "crypto";
9003
+ import { homedir as homedir9 } from "os";
9004
+ import { join as join32 } from "path";
9005
+ var FIRST_LINE_WAIT_MS2 = 2 * 60 * 1e3;
9006
+ function promptArgs(text) {
9007
+ return text ? ["--", prepare(text)] : [];
9008
+ }
9009
+ function promptEnv(tool, env) {
9010
+ const out = { ...env };
9011
+ if (tool === "workbuddy" && !out.CODEBUDDY_CONFIG_DIR) out.CODEBUDDY_CONFIG_DIR = join32(homedir9(), ".workbuddy");
9012
+ return out;
9013
+ }
9014
+ async function runPromptLaunch(tool, windowId, cwd, d) {
9015
+ let real2 = null;
9016
+ try {
9017
+ real2 = d.findReal(tool);
9018
+ } catch {
9019
+ real2 = null;
9020
+ }
9021
+ if (!real2) {
9022
+ d.stderr.write(`deskbuddy: \u627E\u4E0D\u5230 ${tool} \u7684\u53EF\u6267\u884C\u6587\u4EF6
9023
+ `);
9024
+ return 127;
9025
+ }
9026
+ let first = null;
9027
+ try {
9028
+ first = await d.receive(windowId, tool, cwd);
9029
+ } catch (e) {
9030
+ d.log(`\u7B49\u9996\u53E5\u51FA\u9519\uFF1A${e.message}`);
9031
+ first = null;
9032
+ }
9033
+ let text = null;
9034
+ if (first) {
9035
+ const { req: req2 } = first;
9036
+ if (req2.expireAt > 0 && d.now() > req2.expireAt) {
9037
+ first.reply({ instructionId: req2.instructionId, status: "rejected", reason: "expired" });
9038
+ } else {
9039
+ text = req2.text;
9040
+ first.reply({ instructionId: req2.instructionId, status: "started", mode: "window" });
9041
+ }
9042
+ } else {
9043
+ d.log(`[${windowId.slice(0, 8)}] \u6CA1\u7B49\u5230\u9996\u53E5\uFF0C\u8D77\u4E00\u4E2A\u7A7A\u7684 ${tool}`);
9044
+ }
9045
+ return d.run(tool, real2, promptArgs(text), promptEnv(tool, d.env));
9046
+ }
9047
+ function receiveFirstLine(windowId, tool, cwd, opts = {}) {
9048
+ const platform = opts.platform ?? process.platform;
9049
+ const sock = channelPath(windowId, platform);
9050
+ const secret = randomBytes8(32);
9051
+ const cleanup = (server) => {
9052
+ closeChannelServer(server, sock, platform);
9053
+ try {
9054
+ removeWindow(windowId);
9055
+ } catch {
9056
+ }
9057
+ };
9058
+ try {
9059
+ writeWindow(windowId, { pid: process.pid, sock, tool, cwd, at: Date.now(), secret: secret.toString("hex"), launched: true });
9060
+ } catch (e) {
9061
+ opts.log?.(`\u767B\u8BB0\u7A97\u53E3\u5931\u8D25\uFF1A${e.message}`);
9062
+ return Promise.resolve(null);
9063
+ }
9064
+ if (platform !== "win32" && !ensureChannelDir(sock)) {
9065
+ cleanup(null);
9066
+ return Promise.resolve(null);
9067
+ }
9068
+ return new Promise((resolve6) => {
9069
+ let server = null;
9070
+ let done = false;
9071
+ let firstId = "";
9072
+ let final = null;
9073
+ const replies = [];
9074
+ const finish = (v) => {
9075
+ if (done) return;
9076
+ done = true;
9077
+ clearTimeout(timer);
9078
+ if (!v) cleanup(server);
9079
+ resolve6(v);
9080
+ };
9081
+ const timer = setTimeout(() => finish(null), opts.waitMs ?? FIRST_LINE_WAIT_MS2);
9082
+ void startChannelServer(
9083
+ sock,
9084
+ secret,
9085
+ (req2, reply) => {
9086
+ if (firstId && req2.instructionId === firstId) {
9087
+ reply({ instructionId: req2.instructionId, status: "ack" });
9088
+ if (final) reply(final);
9089
+ else replies.push(reply);
9090
+ return;
9091
+ }
9092
+ if (done) {
9093
+ reply({ instructionId: req2.instructionId, status: "not_here" });
9094
+ return;
9095
+ }
9096
+ firstId = req2.instructionId;
9097
+ replies.push(reply);
9098
+ reply({ instructionId: req2.instructionId, status: "ack" });
9099
+ finish({
9100
+ req: req2,
9101
+ reply: (r) => {
9102
+ if (final) return;
9103
+ final = r;
9104
+ for (const f of replies.splice(0)) {
9105
+ try {
9106
+ f(r);
9107
+ } catch {
9108
+ }
9109
+ }
9110
+ setTimeout(() => cleanup(server), opts.lingerMs ?? FINAL_LINGER_MS);
9111
+ }
9112
+ });
9113
+ },
9114
+ platform
9115
+ ).then((s) => {
9116
+ server = s;
9117
+ if (!s) finish(null);
9118
+ else if (done && !firstId) cleanup(s);
9119
+ });
9120
+ });
9121
+ }
9122
+ var FINAL_LINGER_MS = 5e3;
9123
+
8954
9124
  // src/wrap/run.ts
8955
9125
  var DEFAULT_TTL_MS = 10 * 60 * 1e3;
8956
9126
  var TICK_MS = 300;
@@ -8970,7 +9140,7 @@ function childEnv(src, windowId) {
8970
9140
  function wrapLog(line) {
8971
9141
  try {
8972
9142
  const p = join33(deskbuddyHome(), "wrap.log");
8973
- mkdirSync27(deskbuddyHome(), { recursive: true });
9143
+ mkdirSync26(deskbuddyHome(), { recursive: true });
8974
9144
  try {
8975
9145
  if (statSync11(p).size > MAX_LOG_BYTES3) truncateSync3(p, 0);
8976
9146
  } catch {
@@ -9057,6 +9227,34 @@ var defaultLaunchDeps = () => ({
9057
9227
  isDir: isDirSync,
9058
9228
  chdir: (p) => process.chdir(p)
9059
9229
  });
9230
+ function defaultPromptDeps(deps, cwd) {
9231
+ return {
9232
+ platform: deps.platform,
9233
+ now: () => Date.now(),
9234
+ stderr: deps.stderr,
9235
+ findReal: deps.findReal,
9236
+ receive: (id, tool, c) => receiveFirstLine(id, tool, c, { platform: deps.platform, log: deps.log }),
9237
+ run: (tool, real2, args, env) => {
9238
+ if (tool === "codex") {
9239
+ return runCodexWindow(
9240
+ real2,
9241
+ args,
9242
+ defaultCodexWindowDeps({
9243
+ env,
9244
+ platform: deps.platform,
9245
+ stdinTTY: !!deps.stdin.isTTY,
9246
+ stdoutTTY: !!deps.stdout.isTTY,
9247
+ cwd: () => cwd,
9248
+ runDirect: (r, a) => runDirect(r, a, env)
9249
+ })
9250
+ );
9251
+ }
9252
+ return runDirect(real2, args, env);
9253
+ },
9254
+ env: deps.env,
9255
+ log: deps.log
9256
+ };
9257
+ }
9060
9258
  async function runLaunch(argv, deps = defaultWrapDeps(), ld = defaultLaunchDeps()) {
9061
9259
  const runId = argv[0] ?? "";
9062
9260
  const hi = argv.indexOf("--home");
@@ -9083,6 +9281,7 @@ async function runLaunch(argv, deps = defaultWrapDeps(), ld = defaultLaunchDeps(
9083
9281
  return 1;
9084
9282
  }
9085
9283
  ld.chdir(plan.cwd);
9284
+ if (plan.tool !== "claude_code") return runPromptLaunch(plan.tool, runId, plan.cwd, ld.prompt ?? defaultPromptDeps(deps, plan.cwd));
9086
9285
  const args = plan.resume ? ["--resume", plan.resume] : [];
9087
9286
  let real2 = null;
9088
9287
  try {
@@ -9112,7 +9311,7 @@ async function wrapInPty(tool, real2, args, deps, opts = {}) {
9112
9311
  return null;
9113
9312
  }
9114
9313
  const { stdout: stdout7 } = deps;
9115
- const windowId = opts.windowId ?? randomBytes8(16).toString("hex");
9314
+ const windowId = opts.windowId ?? randomBytes9(16).toString("hex");
9116
9315
  const cols = stdout7.columns || 80;
9117
9316
  const rows = stdout7.rows || 24;
9118
9317
  const term = new Headless({ cols, rows, allowProposedApi: true });
@@ -9219,7 +9418,7 @@ function runAttached(tool, p, term0, windowId, deps, launched = false) {
9219
9418
  } catch (e) {
9220
9419
  deps.log(`\u63A5\u7BA1\u7EC8\u7AEF\u51FA\u9519\uFF1A${e.message}`);
9221
9420
  }
9222
- const secret = randomBytes8(32);
9421
+ const secret = randomBytes9(32);
9223
9422
  try {
9224
9423
  writeWindow(windowId, {
9225
9424
  pid: process.pid,