deskbuddy 0.2.0-beta.15 → 0.2.0-beta.16

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
@@ -1484,8 +1484,8 @@ var AutoUpdater = class {
1484
1484
 
1485
1485
  // src/daemon/index.ts
1486
1486
  import { randomBytes as randomBytes7 } from "crypto";
1487
- import { existsSync as existsSync14, statSync as statSync11 } from "fs";
1488
- import { basename as basename9, dirname as dirname11, join as join28 } from "path";
1487
+ import { existsSync as existsSync15, statSync as statSync11 } from "fs";
1488
+ import { basename as basename9, dirname as dirname12, join as join29 } from "path";
1489
1489
  import WebSocket2 from "ws";
1490
1490
 
1491
1491
  // src/adapters/ocPlugin.ts
@@ -2613,6 +2613,9 @@ function isNotLoggedIn(r, text = "") {
2613
2613
  const err = redactRaw((r.stderr ?? "").trim(), text);
2614
2614
  return err.split("\n").some((l) => NOT_LOGGED_IN.test(l.trim()));
2615
2615
  }
2616
+ function isCodexWriterBusy(r, text = "") {
2617
+ return redactRaw((r.stderr ?? "").trim(), text).split("\n").some((l) => /thread-store conflict\b.*already has an active writer/i.test(l));
2618
+ }
2616
2619
  function finalResultOf(stdout7) {
2617
2620
  if (!stdout7) return null;
2618
2621
  const pick = (v) => {
@@ -3274,6 +3277,15 @@ var QUESTION_SKIPPED = "\u7528\u6237\u5728\u8BBE\u5907\u4E0A\u9009\u62E9\u4E0D\u
3274
3277
  var QUESTION_UNANSWERABLE = "\u8BBE\u5907\u672A\u7ED9\u51FA\u6709\u6548\u7B54\u6848\uFF08\u8FD9\u4E2A\u95EE\u9898\u6CA1\u6CD5\u5728\u8BBE\u5907\u4E0A\u56DE\u7B54\uFF09\uFF0C\u8BF7\u6309\u4F60\u7684\u5224\u65AD\u7EE7\u7EED\uFF0C\u6216\u5728\u7ED3\u679C\u91CC\u8BF4\u660E\u9700\u8981\u7528\u6237\u51B3\u5B9A\u7684\u4E8B";
3275
3278
  var REMOTE_QUESTION_TOOLS = /* @__PURE__ */ new Set(["workbuddy"]);
3276
3279
  var NO_NOTICE_TOOLS = /* @__PURE__ */ new Set(["qwenwork", "dumate", "workbuddy"]);
3280
+ var TRUST_TOOL_NAME = "DeskBuddyTrustFolder";
3281
+ var TRUST_SESSION_PREFIX = "trust-";
3282
+ function trustSummary(cwd) {
3283
+ const name = cwd.split(/[\\/]+/).filter(Boolean).pop() || cwd;
3284
+ return `\u9996\u6B21\u5728\u300C${name}\u300D\u8FD0\u884C\uFF0C\u9700\u8981\u4FE1\u4EFB\u8FD9\u4E2A\u6587\u4EF6\u5939`;
3285
+ }
3286
+ function isTrustRecord(rec) {
3287
+ return rec.sessionId.startsWith(TRUST_SESSION_PREFIX);
3288
+ }
3277
3289
  function questionNotice(summary2, deviceName = "") {
3278
3290
  const who = deviceName ? `\u300C${deviceName}\u300D` : DEVICE_FALLBACK;
3279
3291
  return `\u2705 \u5DF2\u5728${who}\u4E0A\u56DE\u7B54\uFF1A${summary2}`;
@@ -3438,8 +3450,10 @@ var ApprovalCoordinator = class {
3438
3450
  if (isQuestion && !qv) return none;
3439
3451
  for (const run2 of this.#runs.values()) if (run2.sessionId === sessionId) return none;
3440
3452
  const store = this.#d.store;
3441
- const { kind, summary: summary2 } = qv ? { kind: "question", summary: qv.summary } : describe(toolName, hook.tool_input, cwd ? cwdForms(cwd) : []);
3442
- const remember = !qv && REMEMBER_TOOLS.has(tool) ? pickRememberSuggestion(hook.permission_suggestions, { edit: EDIT_TOOLS2.has(toolName) || kind === "edit" }) : null;
3453
+ const trust = toolName === TRUST_TOOL_NAME;
3454
+ if (trust !== sessionId.startsWith(TRUST_SESSION_PREFIX)) return none;
3455
+ const { kind, summary: summary2 } = qv ? { kind: "question", summary: qv.summary } : trust ? { kind: "other", summary: trustSummary(cwd) } : describe(toolName, hook.tool_input, cwd ? cwdForms(cwd) : []);
3456
+ const remember = !qv && !trust && REMEMBER_TOOLS.has(tool) ? pickRememberSuggestion(hook.permission_suggestions, { edit: EDIT_TOOLS2.has(toolName) || kind === "edit" }) : null;
3443
3457
  const rec = {
3444
3458
  approvalId: randomUUID3(),
3445
3459
  runId: "",
@@ -3490,7 +3504,7 @@ var ApprovalCoordinator = class {
3490
3504
  return new Promise((resolve8) => {
3491
3505
  void decided.then(resolve8);
3492
3506
  const onAbort = () => {
3493
- if (this.#waiters.has(rec.approvalId)) this.#settleWindow(rec, "hook \u5DF2\u7ED3\u675F");
3507
+ if (this.#waiters.has(rec.approvalId)) this.#settleWindow(rec, typeof signal.reason === "string" && signal.reason ? signal.reason : "hook \u5DF2\u7ED3\u675F");
3494
3508
  resolve8(none);
3495
3509
  };
3496
3510
  if (signal.aborted) onAbort();
@@ -3641,7 +3655,7 @@ var ApprovalCoordinator = class {
3641
3655
  this.#d.store.save();
3642
3656
  if (rec.mode === "window") {
3643
3657
  const notice = windowNotice(decision, rec.summary, deviceName) + (always ? "\uFF08\u5DF2\u8BB0\u4F4F\uFF09" : "");
3644
- if (!NO_NOTICE_TOOLS.has(rec.tool)) {
3658
+ if (!NO_NOTICE_TOOLS.has(rec.tool) && !isTrustRecord(rec)) {
3645
3659
  try {
3646
3660
  ;
3647
3661
  (this.#d.writeNotice ?? writeNotice)(rec.sessionId, notice);
@@ -5218,9 +5232,17 @@ function parseAnswer(m) {
5218
5232
  if (a.decision !== "allow" && a.decision !== "deny") return null;
5219
5233
  return { instructionId, sessionId, expireAt, answer: { kind: "permission", toolName, fingerprint: fingerprint2, decision: a.decision } };
5220
5234
  }
5235
+ if (a.kind === "trust") {
5236
+ if (a.decision !== "allow" && a.decision !== "deny") return null;
5237
+ return { instructionId, sessionId, expireAt, answer: { kind: "trust", decision: a.decision } };
5238
+ }
5221
5239
  if (a.kind === "probe") {
5222
5240
  const toolName = toolNameOf(a.toolName);
5223
5241
  if (!toolName) return null;
5242
+ if (a.trust === true) {
5243
+ if (a.fingerprint !== void 0 || a.questions !== void 0) return null;
5244
+ return { instructionId, sessionId, expireAt, answer: { kind: "probe", toolName, trust: true } };
5245
+ }
5224
5246
  const fingerprint2 = a.fingerprint === void 0 ? void 0 : str3(a.fingerprint, 200);
5225
5247
  if (fingerprint2 === null) return null;
5226
5248
  let questions2;
@@ -5823,11 +5845,502 @@ function defaultSend(windowId, rec, req2) {
5823
5845
  return sendAnswer(channelPath(windowId), secret, req2, { finalTimeoutMs: req2.answer.kind === "probe" ? 5e3 : 60 * 1e3 });
5824
5846
  }
5825
5847
 
5848
+ // src/wrap/trustPrompt.ts
5849
+ import { existsSync as existsSync13, realpathSync as realpathSync5 } from "fs";
5850
+ import { homedir as homedir8 } from "os";
5851
+ import { dirname as dirname11, join as join21 } from "path";
5852
+ var TRUST_TOOLS = /* @__PURE__ */ new Set(["claude_code", "workbuddy", "codex"]);
5853
+ var CURSOR_CHARS = "\u276F>\u203A";
5854
+ var BOX = /[─-╿|]/g;
5855
+ function squash2(s2) {
5856
+ return s2.replace(BOX, "").replace(/\s+/g, "");
5857
+ }
5858
+ function squashLines(lines2) {
5859
+ let body = "";
5860
+ const breaks = [];
5861
+ for (const line of lines2) {
5862
+ for (const ch of line) {
5863
+ if (/\s/.test(ch) || /[\u2500-\u257F|]/.test(ch)) {
5864
+ if (breaks.length) breaks[breaks.length - 1] = true;
5865
+ continue;
5866
+ }
5867
+ body += ch;
5868
+ breaks.push(false);
5869
+ }
5870
+ if (breaks.length) breaks[breaks.length - 1] = true;
5871
+ }
5872
+ return { body, breaks };
5873
+ }
5874
+ function inner(line) {
5875
+ return line.replace(/^\s*[│|]/, "").replace(/[│|]\s*$/, "");
5876
+ }
5877
+ var SPECS = {
5878
+ workbuddy: {
5879
+ title: /Do you trust the files in this folder\?/,
5880
+ option: (t) => {
5881
+ const m = t.match(/^\d\.\s+(.*)$/);
5882
+ if (!m) return null;
5883
+ if (/^Trust folder only\b/.test(m[1])) return "folder";
5884
+ if (/^Trust parent folder\b/.test(m[1])) return "parent";
5885
+ if (/^Trust folder and all subdirectories\b/.test(m[1])) return "recursive";
5886
+ if (/^No, exit\b/.test(m[1])) return "no";
5887
+ return null;
5888
+ }
5889
+ },
5890
+ claude_code: {
5891
+ // 新版「Accessing workspace:」;老版「Do you trust the files in this folder?」(screen.ts TRUST_RE 收录的两种)
5892
+ title: /Accessing workspace:|Do you trust the files in this folder\?/,
5893
+ option: (t) => {
5894
+ const s2 = t.replace(/^\d\.\s+/, "");
5895
+ if (/^Yes, (I trust this folder|proceed)\s*$/.test(s2)) return "folder";
5896
+ if (/^No, exit\s*$/.test(s2)) return "no";
5897
+ return null;
5898
+ }
5899
+ },
5900
+ codex: {
5901
+ title: /Trust this folder\?/,
5902
+ option: (t) => {
5903
+ const m = t.match(/^\d\.\s+(.*)$/);
5904
+ if (!m) return null;
5905
+ if (/^Trust and continue\s*$/.test(m[1])) return "folder";
5906
+ if (/^(Back to Agent Command Center|Quit)\s*$/.test(m[1])) return "no";
5907
+ return null;
5908
+ },
5909
+ widened: /subdirectory of a Git project|apply to the repository root/i
5910
+ }
5911
+ };
5912
+ function inspectTrust(lines2, tool) {
5913
+ const spec = SPECS[tool];
5914
+ if (!spec) return null;
5915
+ let start = -1;
5916
+ for (let i = lines2.length - 1; i >= 0; i--) {
5917
+ if (spec.title.test(lines2[i])) {
5918
+ start = i;
5919
+ break;
5920
+ }
5921
+ }
5922
+ if (start < 0) return null;
5923
+ const top = tool === "codex" ? Math.max(0, start - 8) : start;
5924
+ const options = [];
5925
+ let cursor = -1;
5926
+ let cursors = 0;
5927
+ let last = start;
5928
+ for (let i = start + 1; i < lines2.length; i++) {
5929
+ const raw = inner(lines2[i]).trim();
5930
+ let text = raw;
5931
+ let here2 = false;
5932
+ if (text && CURSOR_CHARS.includes(text[0])) {
5933
+ here2 = true;
5934
+ text = text.slice(1).trim();
5935
+ }
5936
+ const role = spec.option(text);
5937
+ if (!role) continue;
5938
+ if (here2) {
5939
+ cursors++;
5940
+ cursor = options.length;
5941
+ }
5942
+ options.push({ line: i, role });
5943
+ last = i;
5944
+ }
5945
+ if (!options.some((o) => o.role === "folder") || !options.some((o) => o.role === "no")) return null;
5946
+ const region = lines2.slice(top, last + 1);
5947
+ const { body, breaks } = squashLines(region);
5948
+ const widened = !!spec.widened && spec.widened.test(region.join("\n"));
5949
+ const widenedRoot = widened ? noteRoot(region) : "";
5950
+ return { options, cursor: cursors === 1 ? cursor : -1, body, breaks, widened, ...widenedRoot ? { widenedRoot } : {} };
5951
+ }
5952
+ function noteRoot(region) {
5953
+ const i = region.findIndex((l) => /repository root:\s*$/i.test(l));
5954
+ if (i < 0) return "";
5955
+ const parts = [];
5956
+ for (let j = i + 1; j < region.length; j++) {
5957
+ const t = region[j].trim();
5958
+ if (!t) break;
5959
+ parts.push(t);
5960
+ }
5961
+ return parts.join("");
5962
+ }
5963
+ function sameDir2(a, b) {
5964
+ const win = (p) => /^[A-Za-z]:[\\/]/.test(p) || p.includes("\\");
5965
+ const norm3 = (p) => {
5966
+ const t = p.replace(/\\/g, "/").replace(/\/+$/, "");
5967
+ return win(a) || win(b) ? t.toLowerCase() : t;
5968
+ };
5969
+ return !!a && !!b && norm3(a) === norm3(b);
5970
+ }
5971
+ function widenedBeyond(v, forms) {
5972
+ if (!v.widened) return false;
5973
+ const root = v.widenedRoot ?? "";
5974
+ return !root || !forms.some((f) => sameDir2(root, f));
5975
+ }
5976
+ function pathShown(v, forms) {
5977
+ const body = v.body.toLowerCase();
5978
+ const breaks = v.breaks ?? [];
5979
+ return forms.some((f) => {
5980
+ const q = squash2(f).toLowerCase();
5981
+ if (!q) return false;
5982
+ for (let i = body.indexOf(q); i >= 0; i = body.indexOf(q, i + 1)) {
5983
+ const end = i + q.length;
5984
+ if (end === body.length || breaks[end - 1] || /[\\/)]/.test(body[end])) return true;
5985
+ }
5986
+ return false;
5987
+ });
5988
+ }
5989
+ function trustCwdForms(cwd, real2 = realpathSync5, home = homedir8()) {
5990
+ const out = /* @__PURE__ */ new Set();
5991
+ const add = (p) => {
5992
+ if (!p) return;
5993
+ const t = p.replace(/[\\/]+$/, "") || p;
5994
+ out.add(t);
5995
+ const h = home.replace(/[\\/]+$/, "");
5996
+ if (h && t.toLowerCase().startsWith(h.toLowerCase() + (t.includes("\\") ? "\\" : "/"))) out.add("~" + t.slice(h.length));
5997
+ };
5998
+ add(cwd);
5999
+ try {
6000
+ add(real2(cwd));
6001
+ } catch {
6002
+ }
6003
+ return [...out];
6004
+ }
6005
+ function trustWidensToRepo(tool, cwd, exists = existsSync13) {
6006
+ if (tool !== "claude_code" && tool !== "codex") return false;
6007
+ if (!cwd || exists(join21(cwd, ".git"))) return false;
6008
+ let d = cwd;
6009
+ for (let i = 0; i < 64; i++) {
6010
+ const up = dirname11(d);
6011
+ if (up === d) return false;
6012
+ d = up;
6013
+ if (exists(join21(d, ".git"))) return true;
6014
+ }
6015
+ return false;
6016
+ }
6017
+ function trustAnswerable(v, forms) {
6018
+ return !!v && !widenedBeyond(v, forms) && pathShown(v, forms);
6019
+ }
6020
+ var SETTLE_MS2 = 800;
6021
+ var CONFIRM_MS2 = 4e3;
6022
+ var POLL_MS4 = 150;
6023
+ var ARROW_GAP_MS2 = 150;
6024
+ async function waitUntil2(io, ok, ms = CONFIRM_MS2) {
6025
+ const loops = Math.max(1, Math.ceil(ms / POLL_MS4));
6026
+ for (let i = 0; i < loops; i++) {
6027
+ if (ok(io.screen())) return true;
6028
+ await io.sleep(POLL_MS4);
6029
+ }
6030
+ return ok(io.screen());
6031
+ }
6032
+ async function answerTrust(io, tool, decision, forms) {
6033
+ const view = (l) => {
6034
+ const v2 = inspectTrust(l, tool);
6035
+ return trustAnswerable(v2, forms) ? v2 : null;
6036
+ };
6037
+ const v0 = view(io.screen());
6038
+ if (!v0) return "not_waiting";
6039
+ await io.sleep(SETTLE_MS2);
6040
+ const v = view(io.screen());
6041
+ if (!v || v.cursor !== 0) return "not_waiting";
6042
+ const role = decision === "allow" ? "folder" : "no";
6043
+ const target = v.options.findIndex((o) => o.role === role);
6044
+ if (target < 0) return "not_waiting";
6045
+ let wrote = false;
6046
+ const key = target > 0 ? KEY_DOWN : KEY_UP;
6047
+ io.hold?.(true);
6048
+ try {
6049
+ for (let i = 0; i < Math.abs(target); i++) {
6050
+ io.write(key);
6051
+ wrote = true;
6052
+ await io.sleep(ARROW_GAP_MS2);
6053
+ }
6054
+ const onTarget = await waitUntil2(io, (l) => {
6055
+ const w = view(l);
6056
+ return !!w && w.cursor >= 0 && w.options[w.cursor].role === role;
6057
+ });
6058
+ if (!onTarget) return wrote ? "partial" : "not_waiting";
6059
+ io.write(KEY_ENTER);
6060
+ } finally {
6061
+ io.hold?.(false);
6062
+ }
6063
+ return await waitUntil2(io, (l) => !inspectTrust(l, tool)) ? "done" : "partial";
6064
+ }
6065
+ function trustWaiting(lines2, tool) {
6066
+ return inspectTrust(lines2, tool) !== null;
6067
+ }
6068
+ var TRUST_STABLE_MS = 500;
6069
+ var TRUST_RENOTIFY_MS = 3e4;
6070
+ var TrustWatcher = class {
6071
+ #d;
6072
+ #forms;
6073
+ #since = null;
6074
+ #reported = false;
6075
+ /** 上次和 daemon 有往来(报给它,或收到它的信任探测 / 答复)的时间;null = 这次出现还没报过(或不能交给设备) */
6076
+ #contactAt = null;
6077
+ constructor(d) {
6078
+ this.#d = d;
6079
+ this.#forms = d.forms ?? trustCwdForms(d.cwd);
6080
+ }
6081
+ get forms() {
6082
+ return this.#forms;
6083
+ }
6084
+ /** 收到 daemon 的信任探测或答复:daemon 还持有这张卡,不用补报 */
6085
+ noteContact() {
6086
+ if (this.#contactAt !== null) this.#contactAt = this.#d.now();
6087
+ }
6088
+ tick(lines2) {
6089
+ const v = lines2 ? inspectTrust(lines2, this.#d.tool) : null;
6090
+ if (!v) {
6091
+ this.#since = null;
6092
+ this.#reported = false;
6093
+ this.#contactAt = null;
6094
+ return;
6095
+ }
6096
+ const now = this.#d.now();
6097
+ if (this.#since === null) this.#since = now;
6098
+ if (this.#reported) {
6099
+ if (this.#contactAt !== null && now - this.#contactAt >= TRUST_RENOTIFY_MS) {
6100
+ this.#contactAt = now;
6101
+ this.#d.log("\u4FE1\u4EFB\u6846\u8FD8\u5728\uFF0Cdaemon 30 \u79D2\u6CA1\u6765\u63A2\u6D4B\uFF0C\u8865\u62A5\u4E00\u6B21");
6102
+ this.#d.notify();
6103
+ }
6104
+ return;
6105
+ }
6106
+ if (now - this.#since < TRUST_STABLE_MS) return;
6107
+ if (this.#d.ready && !this.#d.ready()) return;
6108
+ this.#reported = true;
6109
+ if (widenedBeyond(v, this.#forms) || (this.#d.widensToRepo ?? trustWidensToRepo)(this.#d.tool, this.#d.cwd)) {
6110
+ this.#d.log("\u4FE1\u4EFB\u6846\u4F1A\u628A\u4FE1\u4EFB\u6269\u5927\u5230\u4E0A\u7EA7\uFF08git \u4ED3\u5E93\u6839\uFF09\uFF0C\u4E0D\u63A8\u8BBE\u5907\uFF0C\u8BF7\u5728\u7535\u8111\u4E0A\u786E\u8BA4");
6111
+ return;
6112
+ }
6113
+ if (!pathShown(v, this.#forms)) {
6114
+ this.#d.log("\u4FE1\u4EFB\u6846\u91CC\u7684\u76EE\u5F55\u548C\u7A97\u53E3\u5DE5\u4F5C\u76EE\u5F55\u5BF9\u4E0D\u4E0A\uFF0C\u4E0D\u63A8\u8BBE\u5907");
6115
+ return;
6116
+ }
6117
+ this.#d.log("\u4FE1\u4EFB\u6846\u51FA\u73B0\uFF0C\u63A8\u8BBE\u5907\u786E\u8BA4");
6118
+ this.#contactAt = now;
6119
+ this.#d.notify();
6120
+ }
6121
+ };
6122
+
6123
+ // src/wrap/trustNotify.ts
6124
+ import { readFileSync as readFileSync16 } from "fs";
6125
+ import { request } from "http";
6126
+ var TRUST_EVENT = "DeskBuddyTrustPrompt";
6127
+ var TIMEOUT_MS = 2e3;
6128
+ function trustNotifyBody(tool, windowId) {
6129
+ return JSON.stringify({ mode: "notify", tool, hook: { hook_event_name: TRUST_EVENT, deskbuddy_window: windowId } });
6130
+ }
6131
+ function notifyTrust(tool, windowId, confPath = gateConfPath()) {
6132
+ return new Promise((resolve8) => {
6133
+ let done = false;
6134
+ const finish = (v) => {
6135
+ if (done) return;
6136
+ done = true;
6137
+ resolve8(v);
6138
+ };
6139
+ let conf;
6140
+ try {
6141
+ conf = JSON.parse(readFileSync16(confPath, "utf8"));
6142
+ } catch {
6143
+ return finish(false);
6144
+ }
6145
+ if (!conf || !Number.isInteger(conf.port) || typeof conf.token !== "string") return finish(false);
6146
+ const body = trustNotifyBody(tool, windowId);
6147
+ try {
6148
+ const req2 = request(
6149
+ {
6150
+ host: "127.0.0.1",
6151
+ port: conf.port,
6152
+ path: "/gate",
6153
+ method: "POST",
6154
+ headers: { "content-type": "application/json", "content-length": Buffer.byteLength(body), "x-gate-token": conf.token },
6155
+ timeout: TIMEOUT_MS
6156
+ },
6157
+ (res) => {
6158
+ res.resume();
6159
+ res.on("end", () => finish(res.statusCode === 200));
6160
+ res.on("error", () => finish(false));
6161
+ }
6162
+ );
6163
+ req2.on("timeout", () => req2.destroy());
6164
+ req2.on("error", () => finish(false));
6165
+ req2.on("close", () => finish(false));
6166
+ req2.end(body);
6167
+ } catch {
6168
+ finish(false);
6169
+ }
6170
+ });
6171
+ }
6172
+
6173
+ // src/daemon/trustBridge.ts
6174
+ var POLL_MS5 = 1500;
6175
+ var ANSWER_TTL_MS2 = 60 * 1e3;
6176
+ var WINDOW_ID = /^[a-f0-9]{32}$/;
6177
+ var TRUST_NO_RESPONSE_POLLS = 5;
6178
+ var TRUST_QUIET_MS = 2 * 60 * 1e3;
6179
+ function isTrustNotify(req2) {
6180
+ return !!req2 && !!req2.hook && req2.hook.hook_event_name === TRUST_EVENT;
6181
+ }
6182
+ var TrustBridge = class {
6183
+ #d;
6184
+ #cards = /* @__PURE__ */ new Map();
6185
+ /** 窗口号 → 静默到期时间(期间忽略 notify) */
6186
+ #quiet = /* @__PURE__ */ new Map();
6187
+ #timer = null;
6188
+ #stopped = false;
6189
+ #polling = false;
6190
+ constructor(deps2) {
6191
+ this.#d = deps2;
6192
+ }
6193
+ get pending() {
6194
+ return this.#cards.size;
6195
+ }
6196
+ stop() {
6197
+ this.#stopped = true;
6198
+ if (this.#timer) clearInterval(this.#timer);
6199
+ this.#timer = null;
6200
+ for (const c of this.#cards.values()) c.ac.abort();
6201
+ this.#cards.clear();
6202
+ }
6203
+ #now() {
6204
+ return this.#d.now ? this.#d.now() : Date.now();
6205
+ }
6206
+ #window(id, tool) {
6207
+ let rec;
6208
+ try {
6209
+ rec = (this.#d.readWindow ?? readWindow)(id);
6210
+ } catch {
6211
+ return null;
6212
+ }
6213
+ if (!rec || rec.trust !== true || rec.launched !== true || rec.tool !== tool || !rec.cwd) return null;
6214
+ return (this.#d.alive ?? pidAlive3)(rec.pid) ? rec : null;
6215
+ }
6216
+ onNotify(req2) {
6217
+ void this.#onNotify(req2).catch((e) => this.#d.log(`\u4FE1\u4EFB\u6846\u63A8\u5361\u51FA\u9519\uFF1A${e?.message ?? e}`));
6218
+ }
6219
+ async #onNotify(req2) {
6220
+ if (this.#stopped || !isTrustNotify(req2) || !TRUST_TOOLS.has(req2.tool)) return;
6221
+ if (this.#d.enabled && !this.#d.enabled(req2.tool)) return;
6222
+ const windowId = typeof req2.hook.deskbuddy_window === "string" ? req2.hook.deskbuddy_window : "";
6223
+ if (!WINDOW_ID.test(windowId)) return;
6224
+ const rec = this.#window(windowId, req2.tool);
6225
+ if (!rec) return;
6226
+ if (this.#cards.has(windowId)) return;
6227
+ const until = this.#quiet.get(windowId);
6228
+ if (until !== void 0) {
6229
+ if (this.#now() < until) return;
6230
+ this.#quiet.delete(windowId);
6231
+ }
6232
+ const card = { windowId, tool: req2.tool, ac: new AbortController(), misses: 0, fails: 0 };
6233
+ this.#cards.set(windowId, card);
6234
+ this.#ensurePoll();
6235
+ const short = windowId.slice(0, 8);
6236
+ this.#d.log(`\u4FE1\u4EFB\u6846\uFF1A\u7A97\u53E3 ${short}\uFF08${req2.tool}\uFF09\u63A8\u8BBE\u5907\u786E\u8BA4`);
6237
+ let reply;
6238
+ try {
6239
+ reply = await this.#d.gate(
6240
+ {
6241
+ window: true,
6242
+ tool: req2.tool,
6243
+ hook: {
6244
+ hook_event_name: "PermissionRequest",
6245
+ session_id: TRUST_SESSION_PREFIX + windowId,
6246
+ cwd: rec.cwd,
6247
+ tool_name: TRUST_TOOL_NAME,
6248
+ tool_input: { cwd: rec.cwd }
6249
+ }
6250
+ },
6251
+ card.ac.signal
6252
+ );
6253
+ } catch {
6254
+ if (this.#cards.get(windowId) === card) this.#cards.delete(windowId);
6255
+ return;
6256
+ }
6257
+ if (this.#cards.get(windowId) !== card || card.ac.signal.aborted) return;
6258
+ this.#cards.delete(windowId);
6259
+ if (reply.decision !== "allow" && reply.decision !== "deny") return;
6260
+ this.#quiet.set(windowId, this.#now() + TRUST_QUIET_MS);
6261
+ const now = this.#window(windowId, req2.tool);
6262
+ if (!now) {
6263
+ this.#d.log(`\u4FE1\u4EFB\u6846\uFF1A\u7A97\u53E3 ${short} \u5DF2\u5173\uFF0C\u8BBE\u5907\u7B54\u590D\u4E0D\u518D\u5199\u5165`);
6264
+ return;
6265
+ }
6266
+ const r = await this.#send(windowId, now, {
6267
+ instructionId: `trust-${windowId}`,
6268
+ sessionId: "",
6269
+ expireAt: this.#now() + ANSWER_TTL_MS2,
6270
+ answer: { kind: "trust", decision: reply.decision }
6271
+ });
6272
+ if (!r) this.#d.log(`\u4FE1\u4EFB\u6846\uFF1A\u7A97\u53E3 ${short} \u7684\u901A\u9053\u8FDE\u4E0D\u4E0A\uFF0C\u8BBE\u5907\u7B54\u590D\u6CA1\u5199\u8FDB\u53BB`);
6273
+ else if (r.status === "started") this.#d.log(`\u4FE1\u4EFB\u6846\uFF1A\u7A97\u53E3 ${short} \u5DF2\u6309\u8BBE\u5907\u7B54\u590D\uFF08${reply.decision === "allow" ? "\u53EA\u4FE1\u4EFB\u5F53\u524D\u76EE\u5F55" : "\u4E0D\u4FE1\u4EFB"}\uFF09\u5199\u5165`);
6274
+ else if (r.reason === "not_waiting") this.#d.log(`\u4FE1\u4EFB\u6846\uFF1A\u7A97\u53E3 ${short} \u6846\u5DF2\u4E0D\u5728\u6216\u5BF9\u4E0D\u4E0A\uFF08\u7535\u8111\u4E0A\u5DF2\u5904\u7406 / \u6709\u4EBA\u5728\u52A8\uFF09\uFF0C\u8BBE\u5907\u7B54\u590D\u6CA1\u5199`);
6275
+ else if (r.reason === "partial") this.#d.log(`\u4FE1\u4EFB\u6846\uFF1A\u7A97\u53E3 ${short} \u8BBE\u5907\u7B54\u590D\u53EA\u5199\u4E86\u4E00\u534A\u5C31\u5BF9\u4E0D\u4E0A\u4E86\uFF0C\u5DF2\u505C\u6B62\uFF0C\u8BF7\u5230\u7535\u8111\u4E0A\u786E\u8BA4`);
6276
+ else this.#d.log(`\u4FE1\u4EFB\u6846\uFF1A\u7A97\u53E3 ${short} \u8BBE\u5907\u7B54\u590D\u88AB\u62D2\uFF08${r.reason ?? ""}\uFF09\uFF0C\u8BF7\u5728\u7535\u8111\u4E0A\u786E\u8BA4`);
6277
+ }
6278
+ #send(windowId, rec, req2) {
6279
+ return (this.#d.send ?? defaultSend2)(windowId, rec, req2);
6280
+ }
6281
+ #ensurePoll() {
6282
+ if (this.#timer || this.#stopped) return;
6283
+ this.#timer = setInterval(() => void this.poll(), this.#d.pollMs ?? POLL_MS5);
6284
+ this.#timer.unref?.();
6285
+ }
6286
+ /** 窗口关了 → 撤卡;包装层连续两次报框不在(电脑上先答了)→ 撤卡;通道连续 5 次连不上 → 窗口无响应,撤卡 */
6287
+ async poll() {
6288
+ if (this.#cards.size === 0) {
6289
+ if (this.#timer) clearInterval(this.#timer);
6290
+ this.#timer = null;
6291
+ return;
6292
+ }
6293
+ if (this.#polling) return;
6294
+ this.#polling = true;
6295
+ try {
6296
+ for (const card of [...this.#cards.values()]) {
6297
+ let why = "";
6298
+ const rec = this.#window(card.windowId, card.tool);
6299
+ if (!rec) why = "\u7A97\u53E3\u5DF2\u5173\u95ED";
6300
+ else {
6301
+ const r = await this.#send(card.windowId, rec, {
6302
+ instructionId: `tprobe-${card.windowId}`,
6303
+ sessionId: "",
6304
+ expireAt: 0,
6305
+ answer: { kind: "probe", toolName: card.tool, trust: true }
6306
+ });
6307
+ if (r === null) card.fails++;
6308
+ else card.fails = 0;
6309
+ if (r?.status === "rejected" && r.reason === "not_waiting") card.misses++;
6310
+ else if (r?.status === "started") card.misses = 0;
6311
+ if (card.misses >= 2) why = "\u7535\u8111\u4E0A\u5DF2\u5904\u7406";
6312
+ else if (card.fails >= TRUST_NO_RESPONSE_POLLS) why = "\u7A97\u53E3\u65E0\u54CD\u5E94";
6313
+ }
6314
+ if (!why || this.#cards.get(card.windowId) !== card) continue;
6315
+ this.#cards.delete(card.windowId);
6316
+ if (why === "\u7A97\u53E3\u65E0\u54CD\u5E94") this.#quiet.set(card.windowId, this.#now() + TRUST_QUIET_MS);
6317
+ this.#d.log(`\u4FE1\u4EFB\u6846\uFF1A\u7A97\u53E3 ${card.windowId.slice(0, 8)} ${why}\uFF0C\u64A4\u5361`);
6318
+ card.ac.abort(why);
6319
+ }
6320
+ } finally {
6321
+ this.#polling = false;
6322
+ }
6323
+ }
6324
+ };
6325
+ function pidAlive3(pid) {
6326
+ if (!Number.isInteger(pid) || pid <= 0) return false;
6327
+ try {
6328
+ process.kill(pid, 0);
6329
+ return true;
6330
+ } catch (e) {
6331
+ return e?.code === "EPERM";
6332
+ }
6333
+ }
6334
+ function defaultSend2(windowId, rec, req2) {
6335
+ const secret = typeof rec.secret === "string" && /^[0-9a-f]{64}$/.test(rec.secret) ? Buffer.from(rec.secret, "hex") : null;
6336
+ return sendAnswer(channelPath(windowId), secret, req2, { finalTimeoutMs: req2.answer.kind === "probe" ? 5e3 : 60 * 1e3 });
6337
+ }
6338
+
5826
6339
  // src/daemon/dumateWatch.ts
5827
6340
  import { execFileSync as execFileSync10, spawn as spawn3 } from "child_process";
5828
- import { readdirSync as readdirSync6, realpathSync as realpathSync5, statSync as statSync9 } from "fs";
6341
+ import { readdirSync as readdirSync6, realpathSync as realpathSync6, statSync as statSync9 } from "fs";
5829
6342
  import { request as httpRequest } from "http";
5830
- import { join as join21 } from "path";
6343
+ import { join as join22 } from "path";
5831
6344
 
5832
6345
  // hook/ocTranslate.mjs
5833
6346
  function toCcTool(tool, args) {
@@ -5927,7 +6440,7 @@ function dirKey(p) {
5927
6440
  let r = realCache.get(p);
5928
6441
  if (r === void 0) {
5929
6442
  try {
5930
- r = realpathSync5(p);
6443
+ r = realpathSync6(p);
5931
6444
  } catch {
5932
6445
  r = p;
5933
6446
  }
@@ -6053,7 +6566,7 @@ function scanSessionDirs(root, now, maxAgeMs = ACTIVE_DIR_MS) {
6053
6566
  const out = [];
6054
6567
  const list = (p) => {
6055
6568
  try {
6056
- return readdirSync6(p, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join21(p, d.name));
6569
+ return readdirSync6(p, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join22(p, d.name));
6057
6570
  } catch {
6058
6571
  return [];
6059
6572
  }
@@ -6224,11 +6737,11 @@ var DumateWatch = class {
6224
6737
  }
6225
6738
  /** /global/event 的信封 `{directory, payload}`;按目录的 /event 直接是事件 */
6226
6739
  #onEnvelope(raw, dir) {
6227
- const inner = raw.payload && typeof raw.payload === "object" ? raw.payload : raw;
6740
+ const inner2 = raw.payload && typeof raw.payload === "object" ? raw.payload : raw;
6228
6741
  const d = typeof raw.directory === "string" && raw.directory ? raw.directory : dir;
6229
- if (dir === void 0 && d && inner.type !== "server.connected" && inner.type !== "server.heartbeat") this.#sseVerified = true;
6742
+ if (dir === void 0 && d && inner2.type !== "server.connected" && inner2.type !== "server.heartbeat") this.#sseVerified = true;
6230
6743
  try {
6231
- this.onEvent(inner, d);
6744
+ this.onEvent(inner2, d);
6232
6745
  } catch (e) {
6233
6746
  this.#d.log(`\u5904\u7406\u767E\u5EA6\u642D\u5B50\u4E8B\u4EF6\u51FA\u9519\uFF1A${e?.message ?? e}`);
6234
6747
  }
@@ -6512,7 +7025,7 @@ var DumateWatch = class {
6512
7025
  const now = this.#now();
6513
7026
  if (now - this.#scanAt >= SCAN_DIRS_MS) {
6514
7027
  this.#scanAt = now;
6515
- for (const d of (this.#d.sessionDirs ?? (() => scanSessionDirs(join21(qianfanHome(), "workspace", "sessions"), now)))()) {
7028
+ for (const d of (this.#d.sessionDirs ?? (() => scanSessionDirs(join22(qianfanHome(), "workspace", "sessions"), now)))()) {
6516
7029
  if (!this.#dirs.has(dirKey(d))) this.#touchDir(d, now);
6517
7030
  }
6518
7031
  }
@@ -7565,9 +8078,9 @@ function codexQuestionInput(p) {
7565
8078
 
7566
8079
  // src/core/codexRemote.ts
7567
8080
  import { mkdirSync as mkdirSync16, renameSync as renameSync12, rmSync as rmSync10, writeFileSync as writeFileSync14 } from "fs";
7568
- import { join as join22 } from "path";
8081
+ import { join as join23 } from "path";
7569
8082
  function codexRemotePath() {
7570
- return join22(deskbuddyHome(), "codex-remote.json");
8083
+ return join23(deskbuddyHome(), "codex-remote.json");
7571
8084
  }
7572
8085
  function writeCodexRemote(threads, pid = process.pid, now = Date.now()) {
7573
8086
  try {
@@ -7647,8 +8160,8 @@ function windowForSession(sessionId) {
7647
8160
  }
7648
8161
 
7649
8162
  // src/core/launch.ts
7650
- import { mkdirSync as mkdirSync17, readFileSync as readFileSync16, renameSync as renameSync13, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
7651
- import { join as join23 } from "path";
8163
+ import { mkdirSync as mkdirSync17, readFileSync as readFileSync17, renameSync as renameSync13, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
8164
+ import { join as join24 } from "path";
7652
8165
  var RUN_ID_RE = /^[a-f0-9]{32}$/;
7653
8166
  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}$/;
7654
8167
  var LAUNCH_TOOLS = ["claude_code", "codex", "workbuddy"];
@@ -7658,10 +8171,10 @@ function validPlan(p) {
7658
8171
  return true;
7659
8172
  }
7660
8173
  function launchDir(home = deskbuddyHome()) {
7661
- return join23(home, "launch");
8174
+ return join24(home, "launch");
7662
8175
  }
7663
8176
  function planFile(runId) {
7664
- return join23(launchDir(), `${runId}.json`);
8177
+ return join24(launchDir(), `${runId}.json`);
7665
8178
  }
7666
8179
  function writeLaunch(runId, plan) {
7667
8180
  if (!RUN_ID_RE.test(runId)) throw new Error("bad runId");
@@ -7674,14 +8187,14 @@ function writeLaunch(runId, plan) {
7674
8187
  }
7675
8188
  function claimLaunch(runId) {
7676
8189
  if (!RUN_ID_RE.test(runId)) return null;
7677
- const claimed = join23(launchDir(), `${runId}.${process.pid}.claimed`);
8190
+ const claimed = join24(launchDir(), `${runId}.${process.pid}.claimed`);
7678
8191
  try {
7679
8192
  renameSync13(planFile(runId), claimed);
7680
8193
  } catch {
7681
8194
  return null;
7682
8195
  }
7683
8196
  try {
7684
- const p = JSON.parse(readFileSync16(claimed, "utf8"));
8197
+ const p = JSON.parse(readFileSync17(claimed, "utf8"));
7685
8198
  if (!p || typeof p.cwd !== "string" || typeof p.resume !== "string") return null;
7686
8199
  if (p.tool === void 0) p.tool = "claude_code";
7687
8200
  return validPlan(p) ? p : null;
@@ -7696,7 +8209,7 @@ function claimLaunch(runId) {
7696
8209
  }
7697
8210
  function launchPending(runId) {
7698
8211
  try {
7699
- readFileSync16(planFile(runId));
8212
+ readFileSync17(planFile(runId));
7700
8213
  return true;
7701
8214
  } catch {
7702
8215
  return false;
@@ -7705,7 +8218,7 @@ function launchPending(runId) {
7705
8218
 
7706
8219
  // src/core/interrupt.ts
7707
8220
  import { closeSync as closeSync6, openSync as openSync6, readdirSync as readdirSync7, readSync as readSync5, statSync as statSync10 } from "fs";
7708
- import { join as join24 } from "path";
8221
+ import { join as join25 } from "path";
7709
8222
  var TAIL = 64 * 1024;
7710
8223
  var SID_RE2 = /^[A-Za-z0-9_-]{1,64}$/;
7711
8224
  function userText2(content) {
@@ -7766,17 +8279,17 @@ function claudeTranscriptFile(sessionId, cwd, home = claudeHome()) {
7766
8279
  }
7767
8280
  };
7768
8281
  if (cwd) {
7769
- const f = join24(home, "projects", projectDirName(cwd), name);
8282
+ const f = join25(home, "projects", projectDirName(cwd), name);
7770
8283
  if (exists(f)) return f;
7771
8284
  }
7772
8285
  let dirs = [];
7773
8286
  try {
7774
- dirs = readdirSync7(join24(home, "projects"));
8287
+ dirs = readdirSync7(join25(home, "projects"));
7775
8288
  } catch {
7776
8289
  return null;
7777
8290
  }
7778
8291
  for (const d of dirs) {
7779
- const f = join24(home, "projects", d, name);
8292
+ const f = join25(home, "projects", d, name);
7780
8293
  if (exists(f)) return f;
7781
8294
  }
7782
8295
  return null;
@@ -7797,7 +8310,7 @@ function shouldSettleInterrupt(st, markerAt, now) {
7797
8310
  import { execFile as execFile4, execFileSync as execFileSync12, spawn as spawn5 } from "child_process";
7798
8311
  import { chmodSync as chmodSync6, mkdirSync as mkdirSync18, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "fs";
7799
8312
  import { userInfo } from "os";
7800
- import { join as join25 } from "path";
8313
+ import { join as join26 } from "path";
7801
8314
  function safePath(p) {
7802
8315
  return /^(?:[A-Za-z]:[\\/]|\/)/.test(p) && !/[\r\n\0"'`$%;]/.test(p);
7803
8316
  }
@@ -7897,7 +8410,7 @@ async function openTerminal(t, d) {
7897
8410
  checkTarget(t);
7898
8411
  if (d.platform === "darwin") {
7899
8412
  mkdirSync18(launchDir(t.home), { recursive: true, mode: 448 });
7900
- const file = join25(launchDir(t.home), `${t.runId}.command`);
8413
+ const file = join26(launchDir(t.home), `${t.runId}.command`);
7901
8414
  writeFileSync16(file, macCommandScript(t), { encoding: "utf8", mode: 448 });
7902
8415
  chmodSync6(file, 448);
7903
8416
  await d.run("open", ["-a", "Terminal", file]);
@@ -7917,7 +8430,7 @@ async function openTerminal(t, d) {
7917
8430
  function removeCommandFile(home, runId) {
7918
8431
  if (!RUN_ID_RE.test(runId)) return;
7919
8432
  try {
7920
- rmSync12(join25(launchDir(home), `${runId}.command`), { force: true });
8433
+ rmSync12(join26(launchDir(home), `${runId}.command`), { force: true });
7921
8434
  } catch {
7922
8435
  }
7923
8436
  }
@@ -8321,24 +8834,24 @@ function startWakeServer(path, hub, secret, platform = process.platform) {
8321
8834
  }
8322
8835
 
8323
8836
  // src/core/appended.ts
8324
- import { chmodSync as chmodSync7, mkdirSync as mkdirSync19, readFileSync as readFileSync17, renameSync as renameSync14, rmSync as rmSync14, writeFileSync as writeFileSync17 } from "fs";
8325
- import { join as join26 } from "path";
8837
+ import { chmodSync as chmodSync7, mkdirSync as mkdirSync19, readFileSync as readFileSync18, renameSync as renameSync14, rmSync as rmSync14, writeFileSync as writeFileSync17 } from "fs";
8838
+ import { join as join27 } from "path";
8326
8839
  var APPENDED_MAX_ITEMS = 5;
8327
8840
  var APPENDED_TEXT_MAX = 500;
8328
8841
  var APPENDED_RESULT_MAX = 800;
8329
8842
  var APPENDED_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
8330
8843
  function appendedDir() {
8331
- return join26(deskbuddyHome(), "appended");
8844
+ return join27(deskbuddyHome(), "appended");
8332
8845
  }
8333
8846
  function appendedPath(sessionId) {
8334
8847
  const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
8335
- return safe ? join26(appendedDir(), `${safe}.json`) : null;
8848
+ return safe ? join27(appendedDir(), `${safe}.json`) : null;
8336
8849
  }
8337
8850
  function readAppended(sessionId) {
8338
8851
  const file = appendedPath(sessionId);
8339
8852
  if (!file) return [];
8340
8853
  try {
8341
- const d = JSON.parse(readFileSync17(file, "utf8"));
8854
+ const d = JSON.parse(readFileSync18(file, "utf8"));
8342
8855
  return Array.isArray(d.items) ? d.items : [];
8343
8856
  } catch {
8344
8857
  return [];
@@ -8362,13 +8875,13 @@ function writeAppended(sessionId, item) {
8362
8875
  var RUNNING_TTL_MS = 60 * 60 * 1e3;
8363
8876
  function runningPath(sessionId) {
8364
8877
  const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
8365
- return safe ? join26(deskbuddyHome(), "running", `${safe}.json`) : null;
8878
+ return safe ? join27(deskbuddyHome(), "running", `${safe}.json`) : null;
8366
8879
  }
8367
8880
  function writeRunning(sessionId, now = Date.now()) {
8368
8881
  const file = runningPath(sessionId);
8369
8882
  if (!file) return;
8370
8883
  try {
8371
- mkdirSync19(join26(deskbuddyHome(), "running"), { recursive: true, mode: 448 });
8884
+ mkdirSync19(join27(deskbuddyHome(), "running"), { recursive: true, mode: 448 });
8372
8885
  writeFileSync17(`${file}.tmp`, JSON.stringify({ at: now, pid: process.pid }), { encoding: "utf8", mode: 384 });
8373
8886
  renameSync14(`${file}.tmp`, file);
8374
8887
  } catch {
@@ -8415,8 +8928,8 @@ function resultSummary(tool, stdout7) {
8415
8928
  }
8416
8929
 
8417
8930
  // src/core/dshMailbox.ts
8418
- import { chmodSync as chmodSync8, existsSync as existsSync13, mkdirSync as mkdirSync20, readdirSync as readdirSync8, readFileSync as readFileSync18, renameSync as renameSync15, rmSync as rmSync15, writeFileSync as writeFileSync18 } from "fs";
8419
- import { join as join27 } from "path";
8931
+ import { chmodSync as chmodSync8, existsSync as existsSync14, mkdirSync as mkdirSync20, readdirSync as readdirSync8, readFileSync as readFileSync19, renameSync as renameSync15, rmSync as rmSync15, writeFileSync as writeFileSync18 } from "fs";
8932
+ import { join as join28 } from "path";
8420
8933
  var DSH_HEARTBEAT_FRESH_MS = 5 * 1e3;
8421
8934
  var DSH_ACK_TIMEOUT_MS = 8 * 1e3;
8422
8935
  var ACK_POLL_MS = 200;
@@ -8427,10 +8940,10 @@ var REVOKE_RETRY_MS = 50;
8427
8940
  var SID_RE3 = /^[A-Za-z0-9_-]{1,128}$/;
8428
8941
  var IID_RE = /^[A-Za-z0-9_-]{1,128}$/;
8429
8942
  function dshMailboxDir(home = deskbuddyHome()) {
8430
- return join27(home, "dsh");
8943
+ return join28(home, "dsh");
8431
8944
  }
8432
8945
  function sub(home, name) {
8433
- return join27(dshMailboxDir(home), name);
8946
+ return join28(dshMailboxDir(home), name);
8434
8947
  }
8435
8948
  function ensureDir2(p) {
8436
8949
  mkdirSync20(p, { recursive: true, mode: 448 });
@@ -8468,7 +8981,7 @@ function dshLiveFor(sessionId, d0 = {}) {
8468
8981
  if (!/^\d+\.json$/.test(n)) continue;
8469
8982
  let hb;
8470
8983
  try {
8471
- hb = JSON.parse(readFileSync18(join27(sub(d.home, "alive"), n), "utf8"));
8984
+ hb = JSON.parse(readFileSync19(join28(sub(d.home, "alive"), n), "utf8"));
8472
8985
  } catch {
8473
8986
  continue;
8474
8987
  }
@@ -8488,9 +9001,9 @@ async function dshDeliver(sessionId, msg, d0 = {}) {
8488
9001
  const ackDir = sub(d.home, "ack");
8489
9002
  ensureDir2(inbox);
8490
9003
  ensureDir2(ackDir);
8491
- const letter = join27(inbox, `${sessionId}.${msg.instructionId}.json`);
8492
- const ackPath = join27(ackDir, `${msg.instructionId}.json`);
8493
- if (existsSync13(letter)) return { ok: false, reason: "inbox_busy" };
9004
+ const letter = join28(inbox, `${sessionId}.${msg.instructionId}.json`);
9005
+ const ackPath = join28(ackDir, `${msg.instructionId}.json`);
9006
+ if (existsSync14(letter)) return { ok: false, reason: "inbox_busy" };
8494
9007
  const writtenAt = d.now();
8495
9008
  writeJson(letter, { instructionId: msg.instructionId, text: msg.text, at: writtenAt });
8496
9009
  const sleep3 = d0.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
@@ -8498,10 +9011,10 @@ async function dshDeliver(sessionId, msg, d0 = {}) {
8498
9011
  let claimed = false;
8499
9012
  let revokeFailed = false;
8500
9013
  for (; ; ) {
8501
- if (existsSync13(ackPath)) {
9014
+ if (existsSync14(ackPath)) {
8502
9015
  let ack = { ok: false, reason: "bad_ack" };
8503
9016
  try {
8504
- const v = JSON.parse(readFileSync18(ackPath, "utf8"));
9017
+ const v = JSON.parse(readFileSync19(ackPath, "utf8"));
8505
9018
  if (v && typeof v.ok === "boolean") ack = v;
8506
9019
  } catch {
8507
9020
  }
@@ -8510,7 +9023,7 @@ async function dshDeliver(sessionId, msg, d0 = {}) {
8510
9023
  }
8511
9024
  if (d.now() >= deadline) {
8512
9025
  if (revokeFailed) {
8513
- if (existsSync13(letter)) return { ok: false, reason: "revoke_failed" };
9026
+ if (existsSync14(letter)) return { ok: false, reason: "revoke_failed" };
8514
9027
  revokeFailed = false;
8515
9028
  claimed = true;
8516
9029
  deadline = d.now() + DSH_ACK_GRACE_MS;
@@ -8544,7 +9057,7 @@ async function revoke(letter, sleep3) {
8544
9057
  if (i < REVOKE_RETRIES) await sleep3(REVOKE_RETRY_MS);
8545
9058
  }
8546
9059
  }
8547
- return existsSync13(letter) ? "failed" : "claimed";
9060
+ return existsSync14(letter) ? "failed" : "claimed";
8548
9061
  }
8549
9062
  function dshClearInbox(home = deskbuddyHome()) {
8550
9063
  const inbox = sub(home, "inbox");
@@ -8557,7 +9070,7 @@ function dshClearInbox(home = deskbuddyHome()) {
8557
9070
  let n = 0;
8558
9071
  for (const f of names) {
8559
9072
  try {
8560
- rmSync15(join27(inbox, f), { force: true });
9073
+ rmSync15(join28(inbox, f), { force: true });
8561
9074
  n++;
8562
9075
  } catch {
8563
9076
  }
@@ -8588,7 +9101,7 @@ function defaultCodexAppServers() {
8588
9101
  }
8589
9102
  function opencodeReady() {
8590
9103
  const mod = ocPluginModule(installedHookPath());
8591
- return isOcPluginInstalled(opencodePluginPath()) && existsSync14(mod) && existsSync14(join28(dirname11(mod), "ocTranslate.mjs"));
9104
+ return isOcPluginInstalled(opencodePluginPath()) && existsSync15(mod) && existsSync15(join29(dirname12(mod), "ocTranslate.mjs"));
8592
9105
  }
8593
9106
  var INTERNAL_PROMPT_MARK = "[DeskBuddy]";
8594
9107
  function orphanResumeText(summary2) {
@@ -8664,6 +9177,7 @@ var Daemon = class {
8664
9177
  #qwenwork;
8665
9178
  /** daemon 开窗的 WorkBuddy 窗口:授权框 / 选择题推设备、答复写成按键(Bug 176) */
8666
9179
  #workbuddy;
9180
+ #trust;
8667
9181
  /** 百度搭子(spec 2026-10-03 §3.2):直连 dumate-opencode 的 HTTP,状态上报 + 选择题 / 授权推设备 */
8668
9182
  #dumate;
8669
9183
  #dumateTimer = null;
@@ -8757,6 +9271,11 @@ var Daemon = class {
8757
9271
  log,
8758
9272
  enabled: () => loadConfig().enabledTools.includes("workbuddy")
8759
9273
  });
9274
+ this.#trust = new TrustBridge({
9275
+ gate: (req2, signal) => this.#coord.onGate(req2, signal),
9276
+ log,
9277
+ enabled: (tool) => loadConfig().enabledTools.includes(tool)
9278
+ });
8760
9279
  this.#qwenwork = new QwenWorkBridge({
8761
9280
  gate: (req2, signal) => this.#coord.onGate(req2, signal),
8762
9281
  log,
@@ -8770,7 +9289,7 @@ var Daemon = class {
8770
9289
  });
8771
9290
  this.#gate = new GateServer(
8772
9291
  (req2, signal) => this.#coord.onGate(req2, signal),
8773
- (req2) => req2.tool === "workbuddy" ? this.#workbuddy.onNotify(req2) : this.#qwenwork.onNotify(req2)
9292
+ (req2) => isTrustNotify(req2) ? this.#trust.onNotify(req2) : req2.tool === "workbuddy" ? this.#workbuddy.onNotify(req2) : this.#qwenwork.onNotify(req2)
8774
9293
  );
8775
9294
  }
8776
9295
  /** 起本地授权端点。必须在执行任何指令之前起好 —— 否则 gate 连不上,全部 exit 2 */
@@ -8930,9 +9449,9 @@ var Daemon = class {
8930
9449
  }
8931
9450
  /** 会话的 transcript:hook 记在状态里的优先;没有再按目录找,找不到的一段时间内不再找 */
8932
9451
  #transcriptOf(st, now) {
8933
- if (st.transcriptPath && existsSync14(st.transcriptPath)) return st.transcriptPath;
9452
+ if (st.transcriptPath && existsSync15(st.transcriptPath)) return st.transcriptPath;
8934
9453
  const cached = this.#transcripts.get(st.sessionId);
8935
- if (cached && existsSync14(cached)) return cached;
9454
+ if (cached && existsSync15(cached)) return cached;
8936
9455
  const missAt = this.#transcriptMiss.get(st.sessionId);
8937
9456
  if (missAt !== void 0 && now - missAt < TRANSCRIPT_MISS_TTL_MS) return null;
8938
9457
  const f = claudeTranscriptFile(st.sessionId, st.cwd);
@@ -9095,6 +9614,7 @@ var Daemon = class {
9095
9614
  this.#codex.clear();
9096
9615
  this.#qwenwork.stop();
9097
9616
  this.#workbuddy.stop();
9617
+ this.#trust.stop();
9098
9618
  if (this.#dumateTimer) clearInterval(this.#dumateTimer);
9099
9619
  if (this.#interruptTimer) clearInterval(this.#interruptTimer);
9100
9620
  this.#dumate.stop();
@@ -9716,6 +10236,7 @@ var Daemon = class {
9716
10236
  writeRunning(sid, this.#deps.now());
9717
10237
  try {
9718
10238
  await this.#execute(cmd, sid, runCwd, true, (r) => {
10239
+ if (cmd.tool === "codex" && !r.ok && isCodexWriterBusy(r, cmd.text)) return;
9719
10240
  const now = readState(sid);
9720
10241
  if (!now || now.interactive !== true && now.embedded !== true || !now.pid || !isAlive(now.pid, now.pidName)) return;
9721
10242
  writeAppended(sid, { text: cmd.text, result: resultSummary(cmd.tool, r.stdout), ok: r.ok, at: this.#deps.now() });
@@ -9772,7 +10293,15 @@ var Daemon = class {
9772
10293
  log(
9773
10294
  `\u6307\u4EE4 ${cmd.instructionId} \u6267\u884C\u5931\u8D25\uFF1A${result.error ?? "\u672A\u77E5"}` + (notLoggedIn ? `\uFF08${cmd.tool} \u7684\u547D\u4EE4\u884C\u6CA1\u767B\u5F55\uFF1A\u684C\u9762 App \u7684\u767B\u5F55\u4E0D\u5171\u4EAB\u7ED9\u547D\u4EE4\u884C\uFF0C\u9700\u8981\u5728\u7EC8\u7AEF\u91CC\u767B\u5F55\u4E00\u6B21\uFF09` : "") + (head ? `\uFF1B\u8F93\u51FA\u5F00\u5934\uFF1A${head}` : "") + (tail2 ? `\uFF1B\u8F93\u51FA\u672B\u5C3E\uFF1A${tail2}` : "")
9774
10295
  );
9775
- this.#report(cmd, sessionId || result.sessionId || "", "failed", "headless", notLoggedIn ? AGENT_NOT_LOGGED_IN : "exec_failed");
10296
+ const writerBusy = cmd.tool === "codex" && isCodexWriterBusy(result, cmd.text);
10297
+ if (writerBusy) log(`\u6307\u4EE4 ${cmd.instructionId} Codex \u4F1A\u8BDD\u6B63\u5F00\u5728\u7535\u8111\u7A97\u53E3\u91CC\uFF08\u5360\u7740\u5199\uFF09\uFF0C\u540E\u53F0\u7EED\u4E0D\u4E86`);
10298
+ this.#report(
10299
+ cmd,
10300
+ sessionId || result.sessionId || "",
10301
+ "failed",
10302
+ "headless",
10303
+ notLoggedIn ? AGENT_NOT_LOGGED_IN : writerBusy ? "window_open" : "exec_failed"
10304
+ );
9776
10305
  }
9777
10306
  if (!sessionId) {
9778
10307
  const newId = result.sessionId ?? parseSessionId(cmd.tool, result.stdout);
@@ -9881,7 +10410,7 @@ var Daemon = class {
9881
10410
  * 起不了(关卡不可用 / 找不到可执行文件)返回 {fail};进程起来那一刻调 onStarted
9882
10411
  */
9883
10412
  async #run(plan, tool, runId, sessionId, cwd, onStarted) {
9884
- if (!existsSync14(this.#deps.nodePath) || !existsSync14(this.#deps.gateScript)) {
10413
+ if (!existsSync15(this.#deps.nodePath) || !existsSync15(this.#deps.gateScript)) {
9885
10414
  log(`\u6388\u6743\u5173\u5361\u4E0D\u53EF\u7528\uFF08node \u6216 gate.cjs \u627E\u4E0D\u5230\uFF09\uFF0C\u62D2\u7EDD\u6267\u884C ${tool}`);
9886
10415
  return { fail: "gate_unavailable" };
9887
10416
  }
@@ -10013,8 +10542,8 @@ async function runDaemonCommand(opts = {}) {
10013
10542
  }
10014
10543
 
10015
10544
  // src/core/registry.ts
10016
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync19, writeFileSync as writeFileSync20 } from "fs";
10017
- import { join as join29 } from "path";
10545
+ import { mkdirSync as mkdirSync21, readFileSync as readFileSync20, writeFileSync as writeFileSync20 } from "fs";
10546
+ import { join as join30 } from "path";
10018
10547
  var FALLBACK = {
10019
10548
  version: "builtin",
10020
10549
  tools: {
@@ -10171,7 +10700,7 @@ var FALLBACK = {
10171
10700
  }
10172
10701
  };
10173
10702
  function registryCachePath() {
10174
- return join29(deskbuddyHome(), "tool-registry.json");
10703
+ return join30(deskbuddyHome(), "tool-registry.json");
10175
10704
  }
10176
10705
  function withFallbackTools(registry) {
10177
10706
  if (!registry || typeof registry !== "object" || !registry.tools || typeof registry.tools !== "object") return FALLBACK;
@@ -10179,7 +10708,7 @@ function withFallbackTools(registry) {
10179
10708
  }
10180
10709
  function loadRegistry() {
10181
10710
  try {
10182
- return withFallbackTools(JSON.parse(readFileSync19(registryCachePath(), "utf8")));
10711
+ return withFallbackTools(JSON.parse(readFileSync20(registryCachePath(), "utf8")));
10183
10712
  } catch {
10184
10713
  return FALLBACK;
10185
10714
  }
@@ -10194,8 +10723,8 @@ async function refreshRegistry() {
10194
10723
  }
10195
10724
 
10196
10725
  // src/adapters/ccHook.ts
10197
- import { chmodSync as chmodSync9, existsSync as existsSync15, mkdirSync as mkdirSync22, readFileSync as readFileSync20, renameSync as renameSync16, rmSync as rmSync17, statSync as statSync12, writeFileSync as writeFileSync21 } from "fs";
10198
- import { dirname as dirname12, join as join30 } from "path";
10726
+ import { chmodSync as chmodSync9, existsSync as existsSync16, mkdirSync as mkdirSync22, readFileSync as readFileSync21, renameSync as renameSync16, rmSync as rmSync17, statSync as statSync12, writeFileSync as writeFileSync21 } from "fs";
10727
+ import { dirname as dirname13, join as join31 } from "path";
10199
10728
  var CC_EVENTS = [
10200
10729
  "UserPromptSubmit",
10201
10730
  "PreToolUse",
@@ -10205,8 +10734,8 @@ var CC_EVENTS = [
10205
10734
  "SessionEnd"
10206
10735
  ];
10207
10736
  function readSettings(path) {
10208
- if (!existsSync15(path)) return {};
10209
- const raw = readFileSync20(path, "utf8");
10737
+ if (!existsSync16(path)) return {};
10738
+ const raw = readFileSync21(path, "utf8");
10210
10739
  if (raw.trim() === "") return {};
10211
10740
  try {
10212
10741
  return JSON.parse(raw);
@@ -10215,7 +10744,7 @@ function readSettings(path) {
10215
10744
  }
10216
10745
  }
10217
10746
  function writeSettings(path, settings) {
10218
- mkdirSync22(dirname12(path), { recursive: true });
10747
+ mkdirSync22(dirname13(path), { recursive: true });
10219
10748
  let mode;
10220
10749
  try {
10221
10750
  mode = statSync12(path).mode & 511;
@@ -10326,7 +10855,7 @@ function installCcRewake(settingsPath, wakePath, platform = process.platform) {
10326
10855
  if (changed) writeSettings(settingsPath, settings);
10327
10856
  }
10328
10857
  function retagCcHook(settingsPath, scripts, from, to) {
10329
- if (!existsSync15(settingsPath)) return false;
10858
+ if (!existsSync16(settingsPath)) return false;
10330
10859
  const settings = readSettings(settingsPath);
10331
10860
  if (!settings.hooks) return false;
10332
10861
  const tail2 = new RegExp(`(\\s)${from.replace(/[^a-z0-9_]/gi, "")}$`);
@@ -10347,8 +10876,8 @@ function retagCcHook(settingsPath, scripts, from, to) {
10347
10876
  function uninstallCcHook(settingsPath, hookPath) {
10348
10877
  const settings = readSettings(settingsPath);
10349
10878
  if (!settings.hooks) return;
10350
- const gatePath = join30(dirname12(hookPath), "gate.cjs");
10351
- const wakePath = join30(dirname12(hookPath), "wake.cjs");
10879
+ const gatePath = join31(dirname13(hookPath), "gate.cjs");
10880
+ const wakePath = join31(dirname13(hookPath), "wake.cjs");
10352
10881
  for (const event of Object.keys(settings.hooks)) {
10353
10882
  const groups = settings.hooks[event] ?? [];
10354
10883
  const kept = [];
@@ -10362,14 +10891,14 @@ function uninstallCcHook(settingsPath, hookPath) {
10362
10891
  }
10363
10892
  writeSettings(settingsPath, settings);
10364
10893
  try {
10365
- rmSync17(join30(dirname12(hookPath), "wake", "tokens"), { recursive: true, force: true });
10894
+ rmSync17(join31(dirname13(hookPath), "wake", "tokens"), { recursive: true, force: true });
10366
10895
  } catch {
10367
10896
  }
10368
10897
  }
10369
10898
 
10370
10899
  // src/adapters/codexHook.ts
10371
- import { chmodSync as chmodSync10, existsSync as existsSync16, mkdirSync as mkdirSync23, readFileSync as readFileSync21, renameSync as renameSync17, statSync as statSync13, writeFileSync as writeFileSync22 } from "fs";
10372
- import { dirname as dirname13, join as join31 } from "path";
10900
+ import { chmodSync as chmodSync10, existsSync as existsSync17, mkdirSync as mkdirSync23, readFileSync as readFileSync22, renameSync as renameSync17, statSync as statSync13, writeFileSync as writeFileSync22 } from "fs";
10901
+ import { dirname as dirname14, join as join32 } from "path";
10373
10902
  var CODEX_MARKER_BEGIN = "# >>> deskbuddy >>>";
10374
10903
  var CODEX_MARKER_END = "# <<< deskbuddy <<<";
10375
10904
  var CODEX_EVENTS = [
@@ -10382,7 +10911,7 @@ var CODEX_EVENTS = [
10382
10911
  ];
10383
10912
  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";
10384
10913
  function codexConfigPath() {
10385
- return join31(codexHome(), "config.toml");
10914
+ return join32(codexHome(), "config.toml");
10386
10915
  }
10387
10916
  var CODEX_APPENDED_TIMEOUT_S = 3;
10388
10917
  function renderBlock(hookPath, events, tool, platform) {
@@ -10401,7 +10930,7 @@ function renderBlock(hookPath, events, tool, platform) {
10401
10930
  return lines2.join("\n");
10402
10931
  }
10403
10932
  function writeAtomic(path, content) {
10404
- mkdirSync23(dirname13(path), { recursive: true });
10933
+ mkdirSync23(dirname14(path), { recursive: true });
10405
10934
  let mode;
10406
10935
  try {
10407
10936
  mode = statSync13(path).mode & 511;
@@ -10421,7 +10950,7 @@ function assertSafeToAppend(raw, path) {
10421
10950
  }
10422
10951
  }
10423
10952
  function installCodexHook(configPath2, hookPath, events = CODEX_EVENTS, tool = "codex", platform = process.platform) {
10424
- const raw = existsSync16(configPath2) ? readFileSync21(configPath2, "utf8") : "";
10953
+ const raw = existsSync17(configPath2) ? readFileSync22(configPath2, "utf8") : "";
10425
10954
  const block = renderBlock(hookPath, events, tool, platform);
10426
10955
  if (raw.includes(CODEX_MARKER_BEGIN)) {
10427
10956
  const lines2 = raw.split("\n");
@@ -10438,8 +10967,8 @@ function installCodexHook(configPath2, hookPath, events = CODEX_EVENTS, tool = "
10438
10967
  writeAtomic(configPath2, head + block + "\n");
10439
10968
  }
10440
10969
  function uninstallCodexHook(configPath2) {
10441
- if (!existsSync16(configPath2)) return;
10442
- const raw = readFileSync21(configPath2, "utf8");
10970
+ if (!existsSync17(configPath2)) return;
10971
+ const raw = readFileSync22(configPath2, "utf8");
10443
10972
  const lines2 = raw.split("\n");
10444
10973
  const begin = lines2.findIndex((l) => l.trim() === CODEX_MARKER_BEGIN);
10445
10974
  if (begin === -1) return;
@@ -10453,14 +10982,14 @@ function uninstallCodexHook(configPath2) {
10453
10982
  }
10454
10983
 
10455
10984
  // src/adapters/dshPlugin.ts
10456
- import { copyFileSync as copyFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync24, readFileSync as readFileSync22, renameSync as renameSync18, unlinkSync as unlinkSync2, writeFileSync as writeFileSync23 } from "fs";
10457
- import { dirname as dirname14, join as join32 } from "path";
10985
+ import { copyFileSync as copyFileSync2, existsSync as existsSync18, mkdirSync as mkdirSync24, readFileSync as readFileSync23, renameSync as renameSync18, unlinkSync as unlinkSync2, writeFileSync as writeFileSync23 } from "fs";
10986
+ import { dirname as dirname15, join as join33 } from "path";
10458
10987
  var DSH_MARKER_BEGIN = "# >>> deskbuddy:dsh-plugin";
10459
10988
  var DSH_MARKER_END = "# <<< deskbuddy:dsh-plugin";
10460
10989
  var CREATED_FLAG = "created-file";
10461
10990
  var DSH_ENTRY_ID = "deskbuddy-cli";
10462
10991
  function dshPluginModule(hookPath) {
10463
- return join32(dirname14(hookPath), "deskbuddy-dsh.mjs");
10992
+ return join33(dirname15(hookPath), "deskbuddy-dsh.mjs");
10464
10993
  }
10465
10994
  function dshBlock(pluginModule, nodePath, created) {
10466
10995
  return [
@@ -10499,7 +11028,7 @@ function patchShape(text) {
10499
11028
  return "block";
10500
11029
  }
10501
11030
  function atomicWrite2(path, text) {
10502
- mkdirSync24(dirname14(path), { recursive: true });
11031
+ mkdirSync24(dirname15(path), { recursive: true });
10503
11032
  const tmp = `${path}.deskbuddy.tmp`;
10504
11033
  writeFileSync23(tmp, text, "utf8");
10505
11034
  renameSync18(tmp, path);
@@ -10511,12 +11040,12 @@ function dropFlowEmpty(text) {
10511
11040
  return text.split("\n").filter((l) => l.trim() !== "[]").join("\n");
10512
11041
  }
10513
11042
  function readPatch(path) {
10514
- const t = readFileSync22(path, "utf8");
11043
+ const t = readFileSync23(path, "utf8");
10515
11044
  return t.charCodeAt(0) === 65279 ? t.slice(1) : t;
10516
11045
  }
10517
11046
  function installDshPlugin(patchPath, hookPath, nodePath, now = Date.now()) {
10518
- const exists = existsSync17(patchPath);
10519
- const raw = exists ? readFileSync22(patchPath, "utf8") : "";
11047
+ const exists = existsSync18(patchPath);
11048
+ const raw = exists ? readFileSync23(patchPath, "utf8") : "";
10520
11049
  const original = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
10521
11050
  const { rest, found, created: wasCreated, broken } = splitOurs(original);
10522
11051
  if (broken) {
@@ -10565,11 +11094,11 @@ function isDshPluginInstalled(patchPath) {
10565
11094
  var DSH_NOTICE = " \u5DF2\u5F00\u7740\u7684 DeepSeek Harness\uFF08dsh web / \u684C\u9762\u7AEF\uFF09\u91CD\u542F\u540E\u751F\u6548";
10566
11095
 
10567
11096
  // src/commands/tools.ts
10568
- import { existsSync as existsSync18, readFileSync as readFileSync23 } from "fs";
11097
+ import { existsSync as existsSync19, readFileSync as readFileSync24 } from "fs";
10569
11098
  function fileContains(path, needle) {
10570
11099
  try {
10571
- if (!existsSync18(path)) return false;
10572
- return readFileSync23(path, "utf8").includes(needle);
11100
+ if (!existsSync19(path)) return false;
11101
+ return readFileSync24(path, "utf8").includes(needle);
10573
11102
  } catch {
10574
11103
  return false;
10575
11104
  }
@@ -10648,7 +11177,7 @@ ADAPTERS["dumate-http"] = {
10648
11177
  },
10649
11178
  isInstalled: (p) => {
10650
11179
  try {
10651
- const enabled = JSON.parse(readFileSync23(p, "utf8")).enabledTools;
11180
+ const enabled = JSON.parse(readFileSync24(p, "utf8")).enabledTools;
10652
11181
  return Array.isArray(enabled) && enabled.includes("dumate");
10653
11182
  } catch {
10654
11183
  return false;
@@ -10679,13 +11208,13 @@ function subscribedEvents(registry, tool, fallback) {
10679
11208
  }
10680
11209
 
10681
11210
  // src/commands/enable.ts
10682
- import { dirname as dirname17, join as join35 } from "path";
11211
+ import { dirname as dirname18, join as join36 } from "path";
10683
11212
 
10684
11213
  // src/wrap/shim.ts
10685
11214
  import { execFileSync as execFileSync13 } from "child_process";
10686
- import { chmodSync as chmodSync11, existsSync as existsSync19, mkdirSync as mkdirSync25, readdirSync as readdirSync9, readFileSync as readFileSync24, rmdirSync, rmSync as rmSync18, writeFileSync as writeFileSync24 } from "fs";
10687
- import { homedir as homedir8 } from "os";
10688
- import { delimiter as delimiter2, join as join33, resolve as resolve5 } from "path";
11215
+ import { chmodSync as chmodSync11, existsSync as existsSync20, mkdirSync as mkdirSync25, readdirSync as readdirSync9, readFileSync as readFileSync25, rmdirSync, rmSync as rmSync18, writeFileSync as writeFileSync24 } from "fs";
11216
+ import { homedir as homedir9 } from "os";
11217
+ import { delimiter as delimiter2, join as join34, resolve as resolve5 } from "path";
10689
11218
  var SHIM_FALLBACK_EXIT = 213;
10690
11219
  var WIN_SHIM_ENABLED = false;
10691
11220
  var SHIM_COMMANDS = { claude_code: "claude", codex: "codex" };
@@ -10695,10 +11224,10 @@ var RC_END = "# <<< deskbuddy <<<";
10695
11224
  var RC_FILES = [".zshrc", ".bashrc"];
10696
11225
  var WIN_PENDING = "Windows \u7A97\u53E3\u63A5\u7BA1\u5F85\u9A8C\u8BC1\uFF0C\u6682\u672A\u5F00\u542F";
10697
11226
  function shimDir() {
10698
- return join33(deskbuddyHome(), "bin");
11227
+ return join34(deskbuddyHome(), "bin");
10699
11228
  }
10700
11229
  function shimFile(binDir, command, platform = process.platform) {
10701
- return join33(binDir, platform === "win32" ? `${command}.cmd` : command);
11230
+ return join34(binDir, platform === "win32" ? `${command}.cmd` : command);
10702
11231
  }
10703
11232
  function shQuote3(s2) {
10704
11233
  return `'${s2.replace(/'/g, `'\\''`)}'`;
@@ -10827,7 +11356,7 @@ function removeRcBlock(content) {
10827
11356
  return content.slice(0, start) + content.slice(end);
10828
11357
  }
10829
11358
  function readRc(p) {
10830
- return readFileSync24(p).toString("latin1");
11359
+ return readFileSync25(p).toString("latin1");
10831
11360
  }
10832
11361
  function writeRc(p, latin1) {
10833
11362
  writeFileSync24(p, Buffer.from(latin1, "latin1"));
@@ -10875,7 +11404,7 @@ function psWinPath() {
10875
11404
  function defaultShimEnv(nodePath, cliPath) {
10876
11405
  const env = process.env;
10877
11406
  const isolated = !!env.VITEST || !!env.DESKBUDDY_HOME;
10878
- const rcHome = env.DESKBUDDY_RC_HOME || (isolated ? null : homedir8());
11407
+ const rcHome = env.DESKBUDDY_RC_HOME || (isolated ? null : homedir9());
10879
11408
  return {
10880
11409
  platform: process.platform,
10881
11410
  binDir: shimDir(),
@@ -10918,8 +11447,8 @@ function installShim(tool, e) {
10918
11447
  } else if (e.rcHome) {
10919
11448
  const block = toLatin1(rcBlock(e.binDir, e.rcHome));
10920
11449
  for (const name of RC_FILES) {
10921
- const rc = join33(e.rcHome, name);
10922
- if (!existsSync19(rc)) continue;
11450
+ const rc = join34(e.rcHome, name);
11451
+ if (!existsSync20(rc)) continue;
10923
11452
  const next = addRcBlock(readRc(rc), block);
10924
11453
  if (next !== null) writeRc(rc, next);
10925
11454
  touched.push(`~/${name}`);
@@ -10945,7 +11474,7 @@ function ourShims(binDir) {
10945
11474
  }
10946
11475
  return names.filter((n) => {
10947
11476
  try {
10948
- return readFileSync24(join33(binDir, n), "utf8").includes(SHIM_MARKER);
11477
+ return readFileSync25(join34(binDir, n), "utf8").includes(SHIM_MARKER);
10949
11478
  } catch {
10950
11479
  return false;
10951
11480
  }
@@ -10957,7 +11486,7 @@ function uninstallShim(tool, e) {
10957
11486
  let removed = false;
10958
11487
  const file = shimFile(e.binDir, command, e.platform);
10959
11488
  try {
10960
- if (existsSync19(file) && readFileSync24(file, "utf8").includes(SHIM_MARKER)) {
11489
+ if (existsSync20(file) && readFileSync25(file, "utf8").includes(SHIM_MARKER)) {
10961
11490
  rmSync18(file, { force: true });
10962
11491
  removed = true;
10963
11492
  }
@@ -10976,8 +11505,8 @@ function uninstallShim(tool, e) {
10976
11505
  }
10977
11506
  } else if (e.rcHome) {
10978
11507
  for (const name of RC_FILES) {
10979
- const rc = join33(e.rcHome, name);
10980
- if (!existsSync19(rc)) continue;
11508
+ const rc = join34(e.rcHome, name);
11509
+ if (!existsSync20(rc)) continue;
10981
11510
  const next = removeRcBlock(readRc(rc));
10982
11511
  if (next !== null) {
10983
11512
  writeRc(rc, next);
@@ -10986,7 +11515,7 @@ function uninstallShim(tool, e) {
10986
11515
  }
10987
11516
  }
10988
11517
  try {
10989
- if (existsSync19(e.binDir) && readdirSync9(e.binDir).length === 0) rmdirSync(e.binDir);
11518
+ if (existsSync20(e.binDir) && readdirSync9(e.binDir).length === 0) rmdirSync(e.binDir);
10990
11519
  } catch {
10991
11520
  }
10992
11521
  if (!removed && touched.length === 0) return { ok: true, message: "" };
@@ -10998,7 +11527,7 @@ function shimState(tool, binDir, pathEnv, platform = process.platform) {
10998
11527
  const file = shimFile(binDir, command, platform);
10999
11528
  let installed = false;
11000
11529
  try {
11001
- installed = readFileSync24(file, "utf8").includes(SHIM_MARKER);
11530
+ installed = readFileSync25(file, "utf8").includes(SHIM_MARKER);
11002
11531
  } catch {
11003
11532
  installed = false;
11004
11533
  }
@@ -11009,11 +11538,11 @@ function shimState(tool, binDir, pathEnv, platform = process.platform) {
11009
11538
  }
11010
11539
 
11011
11540
  // src/commands/migrateQoder.ts
11012
- import { dirname as dirname16, join as join34 } from "path";
11541
+ import { dirname as dirname17, join as join35 } from "path";
11013
11542
 
11014
11543
  // src/core/detect.ts
11015
- import { existsSync as existsSync20 } from "fs";
11016
- import { dirname as dirname15 } from "path";
11544
+ import { existsSync as existsSync21 } from "fs";
11545
+ import { dirname as dirname16 } from "path";
11017
11546
  var ALL_TOOLS = ["claude_code", "codex", "workbuddy", "qoder_cn", "qoder", "opencode", "qwenwork", "dumate", "deepseek_harness"];
11018
11547
  function toolConfigDir(tool) {
11019
11548
  switch (tool) {
@@ -11022,13 +11551,13 @@ function toolConfigDir(tool) {
11022
11551
  case "codex":
11023
11552
  return codexHome();
11024
11553
  case "workbuddy":
11025
- return dirname15(workbuddySettingsPath());
11554
+ return dirname16(workbuddySettingsPath());
11026
11555
  case "qoder_cn":
11027
- return dirname15(qoderCnSettingsPath());
11556
+ return dirname16(qoderCnSettingsPath());
11028
11557
  case "qoder":
11029
- return dirname15(qoderSettingsPath());
11558
+ return dirname16(qoderSettingsPath());
11030
11559
  case "opencode":
11031
- return dirname15(dirname15(opencodePluginPath()));
11560
+ return dirname16(dirname16(opencodePluginPath()));
11032
11561
  case "qwenwork":
11033
11562
  return qwenworkHome();
11034
11563
  case "deepseek_harness":
@@ -11043,7 +11572,7 @@ function toolDetectDir(tool) {
11043
11572
  }
11044
11573
  var defaultDetectDeps = () => ({
11045
11574
  located: (tool) => locate(tool) != null,
11046
- exists: existsSync20,
11575
+ exists: existsSync21,
11047
11576
  env: process.env
11048
11577
  });
11049
11578
  function callerTool(env) {
@@ -11065,7 +11594,7 @@ function migrateLegacyQoder(out, detect = () => detectTools()) {
11065
11594
  try {
11066
11595
  const cfg = loadConfig();
11067
11596
  const hookPath = installedHookPath();
11068
- const scripts = [hookPath, join34(dirname16(hookPath), "gate.cjs")];
11597
+ const scripts = [hookPath, join35(dirname17(hookPath), "gate.cjs")];
11069
11598
  if (!retagCcHook(qoderCnSettingsPath(), scripts, "qoder", "qoder_cn")) return false;
11070
11599
  const hadQoder = cfg.enabledTools.includes("qoder");
11071
11600
  let intl = false;
@@ -11158,14 +11687,14 @@ function installTools(tools, out, shim = defaultShimEnvForInstall()) {
11158
11687
  installed.push(tool);
11159
11688
  out(`\u2705 ${tool}\uFF1Ahook \u5DF2\u88C5\u8FDB ${configPath2}\uFF08${events.length} \u4E2A\u4E8B\u4EF6\uFF09`);
11160
11689
  try {
11161
- if (adapter.installWindowGate?.(configPath2, join35(dirname17(hookPath), "gate.cjs"), tool)) {
11690
+ if (adapter.installWindowGate?.(configPath2, join36(dirname18(hookPath), "gate.cjs"), tool)) {
11162
11691
  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`);
11163
11692
  }
11164
11693
  } catch (e) {
11165
11694
  out(`\u26A0\uFE0F ${tool}\uFF1A\u8BBE\u5907\u786E\u8BA4\u6388\u6743\u6CA1\u88C5\u4E0A\uFF1A${e.message}`);
11166
11695
  }
11167
11696
  try {
11168
- if (adapter.installRewake?.(configPath2, join35(dirname17(hookPath), "wake.cjs"), tool)) {
11697
+ if (adapter.installRewake?.(configPath2, join36(dirname18(hookPath), "wake.cjs"), tool)) {
11169
11698
  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`);
11170
11699
  }
11171
11700
  } catch (e) {
@@ -11267,17 +11796,17 @@ async function runDisable(tools, opts = {}) {
11267
11796
 
11268
11797
  // src/commands/install.ts
11269
11798
  import { mkdirSync as mkdirSync26, rmSync as rmSync19, writeFileSync as writeFileSync25 } from "fs";
11270
- import { join as join36 } from "path";
11799
+ import { join as join37 } from "path";
11271
11800
 
11272
11801
  // src/commands/setup.ts
11273
11802
  import qrcode from "qrcode-terminal";
11274
11803
 
11275
11804
  // src/commands/remoteCwd.ts
11276
- import { existsSync as existsSync21, statSync as statSync14 } from "fs";
11805
+ import { existsSync as existsSync22, statSync as statSync14 } from "fs";
11277
11806
  import { createInterface } from "readline";
11278
- import { homedir as homedir9 } from "os";
11807
+ import { homedir as homedir10 } from "os";
11279
11808
  import { isAbsolute as isAbsolute2, resolve as resolve6 } from "path";
11280
- function normalizePath(raw, home = homedir9()) {
11809
+ function normalizePath(raw, home = homedir10()) {
11281
11810
  const s2 = (raw || "").trim();
11282
11811
  if (!s2) return null;
11283
11812
  const expanded = s2 === "~" ? home : s2.startsWith("~/") || process.platform === "win32" && s2.startsWith("~\\") ? resolve6(home, s2.slice(2)) : s2;
@@ -11285,7 +11814,7 @@ function normalizePath(raw, home = homedir9()) {
11285
11814
  return abs;
11286
11815
  }
11287
11816
  function checkPath(abs) {
11288
- if (!existsSync21(abs)) return "missing";
11817
+ if (!existsSync22(abs)) return "missing";
11289
11818
  try {
11290
11819
  return statSync14(abs).isDirectory() ? "ok" : "not_a_dir";
11291
11820
  } catch {
@@ -11495,7 +12024,7 @@ function inSandbox(env) {
11495
12024
  }
11496
12025
  function precheckWritable(dirs) {
11497
12026
  for (const dir of dirs) {
11498
- const probe = join36(dir, `.deskbuddy-probe-${process.pid}`);
12027
+ const probe = join37(dir, `.deskbuddy-probe-${process.pid}`);
11499
12028
  try {
11500
12029
  mkdirSync26(dir, { recursive: true });
11501
12030
  writeFileSync25(probe, "");
@@ -11628,7 +12157,7 @@ async function runInstall(opts, deps2 = defaultInstallDeps()) {
11628
12157
 
11629
12158
  // src/commands/logout.ts
11630
12159
  import { createInterface as createInterface2 } from "readline";
11631
- import { existsSync as existsSync22, rmSync as rmSync20 } from "fs";
12160
+ import { existsSync as existsSync23, rmSync as rmSync20 } from "fs";
11632
12161
  function stdout4(line) {
11633
12162
  process.stdout.write(line + "\n");
11634
12163
  }
@@ -11659,7 +12188,7 @@ async function runLogout(opts = {}) {
11659
12188
  }
11660
12189
  }
11661
12190
  await runDisable([], { out, brief: true, keepAutostart: opts.keepAutostart });
11662
- if (existsSync22(home)) {
12191
+ if (existsSync23(home)) {
11663
12192
  rmSync20(home, { recursive: true, force: true });
11664
12193
  }
11665
12194
  out("");
@@ -11857,7 +12386,7 @@ async function runAutoSwitch(opts) {
11857
12386
  // src/wrap/run.ts
11858
12387
  import { randomBytes as randomBytes10 } from "crypto";
11859
12388
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync28, statSync as statSync16, truncateSync as truncateSync3 } from "fs";
11860
- import { join as join39 } from "path";
12389
+ import { join as join40 } from "path";
11861
12390
  import { StringDecoder as StringDecoder2 } from "string_decoder";
11862
12391
 
11863
12392
  // src/wrap/injector.ts
@@ -12106,9 +12635,9 @@ var Injector = class {
12106
12635
 
12107
12636
  // src/wrap/passthrough.ts
12108
12637
  import { spawn as spawn6 } from "child_process";
12109
- import { realpathSync as realpathSync6 } from "fs";
12638
+ import { realpathSync as realpathSync7 } from "fs";
12110
12639
  import { constants as constants2 } from "os";
12111
- import { dirname as dirname18, resolve as resolve7 } from "path";
12640
+ import { dirname as dirname19, resolve as resolve7 } from "path";
12112
12641
  var CLAUDE_SUBCOMMANDS = /* @__PURE__ */ new Set([
12113
12642
  "agents",
12114
12643
  "attach",
@@ -12164,7 +12693,7 @@ function norm2(p, win) {
12164
12693
  }
12165
12694
  function real(p) {
12166
12695
  try {
12167
- return realpathSync6(p);
12696
+ return realpathSync7(p);
12168
12697
  } catch {
12169
12698
  return p;
12170
12699
  }
@@ -12172,7 +12701,7 @@ function real(p) {
12172
12701
  function findReal(tool, d = defaultLocateDeps(), skip = shimDir()) {
12173
12702
  const win = (d.platform ?? process.platform) === "win32";
12174
12703
  const skipKeys = /* @__PURE__ */ new Set([norm2(skip, win), norm2(real(skip), win)]);
12175
- const inSkip = (file) => skipKeys.has(norm2(dirname18(file), win)) || skipKeys.has(norm2(dirname18(real(file)), win));
12704
+ const inSkip = (file) => skipKeys.has(norm2(dirname19(file), win)) || skipKeys.has(norm2(dirname19(real(file)), win));
12176
12705
  const dd = { ...d, skipDirs: [...d.skipDirs ?? [], skip] };
12177
12706
  const name = SHIM_COMMANDS[tool];
12178
12707
  if (!name) {
@@ -12252,10 +12781,10 @@ function runDirect(real2, args, env = process.env) {
12252
12781
 
12253
12782
  // src/wrap/codexLaunch.ts
12254
12783
  import { spawn as spawn7 } from "child_process";
12255
- import { closeSync as closeSync7, mkdirSync as mkdirSync27, openSync as openSync7, readdirSync as readdirSync10, readFileSync as readFileSync25, rmSync as rmSync21, statSync as statSync15, truncateSync as truncateSync2, writeFileSync as writeFileSync26, renameSync as renameSync19 } from "fs";
12256
- import { request } from "http";
12784
+ import { closeSync as closeSync7, mkdirSync as mkdirSync27, openSync as openSync7, readdirSync as readdirSync10, readFileSync as readFileSync26, rmSync as rmSync21, statSync as statSync15, truncateSync as truncateSync2, writeFileSync as writeFileSync26, renameSync as renameSync19 } from "fs";
12785
+ import { request as request2 } from "http";
12257
12786
  import { createServer as createServer4 } from "net";
12258
- import { join as join37 } from "path";
12787
+ import { join as join38 } from "path";
12259
12788
 
12260
12789
  // src/core/codexServer.ts
12261
12790
  import { execFileSync as execFileSync14 } from "child_process";
@@ -12423,12 +12952,12 @@ async function runCodexWindow(real2, args, d) {
12423
12952
  }
12424
12953
  var MAX_LOG_BYTES2 = 1024 * 1024;
12425
12954
  function appServerLogPath() {
12426
- return join37(deskbuddyHome(), "codex-app-server.log");
12955
+ return join38(deskbuddyHome(), "codex-app-server.log");
12427
12956
  }
12428
12957
  function windowsDir2() {
12429
- return join37(deskbuddyHome(), "codex-windows");
12958
+ return join38(deskbuddyHome(), "codex-windows");
12430
12959
  }
12431
- function pidAlive3(pid) {
12960
+ function pidAlive4(pid) {
12432
12961
  try {
12433
12962
  process.kill(pid, 0);
12434
12963
  return true;
@@ -12439,18 +12968,18 @@ function pidAlive3(pid) {
12439
12968
  function daemonAlive(pidFile = pidFilePath(), args = processArgs) {
12440
12969
  let pid = 0;
12441
12970
  try {
12442
- pid = Number(readFileSync25(pidFile, "utf8").trim());
12971
+ pid = Number(readFileSync26(pidFile, "utf8").trim());
12443
12972
  } catch {
12444
12973
  return false;
12445
12974
  }
12446
- if (!Number.isInteger(pid) || pid <= 0 || !pidAlive3(pid)) return false;
12975
+ if (!Number.isInteger(pid) || pid <= 0 || !pidAlive4(pid)) return false;
12447
12976
  const a = args(pid);
12448
12977
  return !!a && /cli\.js daemon(?:\s|$)/.test(a);
12449
12978
  }
12450
12979
  function probeReady(url) {
12451
12980
  const port = Number(url.slice(url.lastIndexOf(":") + 1));
12452
12981
  return new Promise((resolve8) => {
12453
- const req2 = request({ host: "127.0.0.1", port, path: "/readyz", method: "GET", timeout: 800 }, (res) => {
12982
+ const req2 = request2({ host: "127.0.0.1", port, path: "/readyz", method: "GET", timeout: 800 }, (res) => {
12454
12983
  res.resume();
12455
12984
  resolve8((res.statusCode ?? 0) >= 200 && (res.statusCode ?? 0) < 300);
12456
12985
  });
@@ -12507,7 +13036,7 @@ function killOwnServer(pid, url) {
12507
13036
  if (a && isAppServerArgs(a, url)) killServer(pid);
12508
13037
  }
12509
13038
  function recordPath(rec) {
12510
- return join37(windowsDir2(), `${rec.server}.json`);
13039
+ return join38(windowsDir2(), `${rec.server}.json`);
12511
13040
  }
12512
13041
  function writeRecord(rec) {
12513
13042
  try {
@@ -12524,7 +13053,7 @@ function removeRecord(rec) {
12524
13053
  } catch {
12525
13054
  }
12526
13055
  }
12527
- function reapWindows(dir = windowsDir2(), alive = pidAlive3, args = processArgs, kill = killServer) {
13056
+ function reapWindows(dir = windowsDir2(), alive = pidAlive4, args = processArgs, kill = killServer) {
12528
13057
  let names = [];
12529
13058
  try {
12530
13059
  names = readdirSync10(dir).filter((n) => n.endsWith(".json"));
@@ -12532,10 +13061,10 @@ function reapWindows(dir = windowsDir2(), alive = pidAlive3, args = processArgs,
12532
13061
  return;
12533
13062
  }
12534
13063
  for (const n of names) {
12535
- const p = join37(dir, n);
13064
+ const p = join38(dir, n);
12536
13065
  let rec;
12537
13066
  try {
12538
- rec = JSON.parse(readFileSync25(p, "utf8"));
13067
+ rec = JSON.parse(readFileSync26(p, "utf8"));
12539
13068
  } catch {
12540
13069
  rmSync21(p, { force: true });
12541
13070
  continue;
@@ -12555,7 +13084,7 @@ function reapWindows(dir = windowsDir2(), alive = pidAlive3, args = processArgs,
12555
13084
  function launchLog(line) {
12556
13085
  try {
12557
13086
  mkdirSync27(deskbuddyHome(), { recursive: true });
12558
- writeFileSync26(join37(deskbuddyHome(), "wrap.log"), `${(/* @__PURE__ */ new Date()).toISOString()} [codex ${process.pid}] ${line}
13087
+ writeFileSync26(join38(deskbuddyHome(), "wrap.log"), `${(/* @__PURE__ */ new Date()).toISOString()} [codex ${process.pid}] ${line}
12559
13088
  `, { flag: "a" });
12560
13089
  } catch {
12561
13090
  }
@@ -12579,8 +13108,8 @@ function defaultCodexWindowDeps(base) {
12579
13108
 
12580
13109
  // src/wrap/promptLaunch.ts
12581
13110
  import { randomBytes as randomBytes8 } from "crypto";
12582
- import { homedir as homedir10 } from "os";
12583
- import { join as join38 } from "path";
13111
+ import { homedir as homedir11 } from "os";
13112
+ import { join as join39 } from "path";
12584
13113
  var FIRST_LINE_WAIT_MS2 = 2 * 60 * 1e3;
12585
13114
  function promptArgs(text) {
12586
13115
  return text ? ["--", prepare(text)] : [];
@@ -12588,7 +13117,7 @@ function promptArgs(text) {
12588
13117
  function promptEnv(tool, env, launcherPid = process.pid, answerWindowId = "") {
12589
13118
  const out = { ...env };
12590
13119
  if (tool === "workbuddy") {
12591
- if (!out.CODEBUDDY_CONFIG_DIR) out.CODEBUDDY_CONFIG_DIR = join38(homedir10(), ".workbuddy");
13120
+ if (!out.CODEBUDDY_CONFIG_DIR) out.CODEBUDDY_CONFIG_DIR = join39(homedir11(), ".workbuddy");
12592
13121
  out.DESKBUDDY_LAUNCHER_PID = String(launcherPid);
12593
13122
  out.DESKBUDDY_LAUNCHED_TOOL = tool;
12594
13123
  if (answerWindowId) out.DESKBUDDY_LAUNCHED_WINDOW = answerWindowId;
@@ -12639,7 +13168,7 @@ async function runPromptLaunch(tool, windowId, cwd, d) {
12639
13168
  } else {
12640
13169
  d.log(`[${windowId.slice(0, 8)}] \u6CA1\u7B49\u5230\u9996\u53E5\uFF0C\u8D77\u4E00\u4E2A\u7A7A\u7684 ${tool}`);
12641
13170
  }
12642
- const answerWindowId = tool === "workbuddy" ? randomBytes8(16).toString("hex") : "";
13171
+ const answerWindowId = tool === "workbuddy" || tool === "codex" ? randomBytes8(16).toString("hex") : "";
12643
13172
  return d.run(tool, real2, [...launchArgs(tool, d.platform), ...promptArgs(text)], promptEnv(tool, d.env, process.pid, answerWindowId), answerWindowId);
12644
13173
  }
12645
13174
  function receiveFirstLine(windowId, tool, cwd, opts = {}) {
@@ -12722,18 +13251,24 @@ var FINAL_LINGER_MS = 5e3;
12722
13251
  // src/wrap/answerWindow.ts
12723
13252
  import { randomBytes as randomBytes9 } from "crypto";
12724
13253
  import { StringDecoder } from "string_decoder";
12725
- async function applyAnswer(req2, io) {
13254
+ var TRUST_TICK_MS = 300;
13255
+ async function applyAnswer(req2, io, trust) {
12726
13256
  const id = req2.instructionId;
12727
13257
  if (req2.expireAt > 0 && Date.now() > req2.expireAt) return { instructionId: id, status: "rejected", reason: "expired" };
12728
- if (!io.screen()) return { instructionId: id, status: "rejected", reason: "no_screen" };
12729
13258
  const a = req2.answer;
13259
+ if ((a.kind === "trust" || a.kind === "probe" && a.trust) && !trust) return { instructionId: id, status: "rejected", reason: "unsupported" };
13260
+ if (!io.screen()) return { instructionId: id, status: "rejected", reason: "no_screen" };
12730
13261
  if (a.kind === "probe") {
12731
- return stillWaiting(io.screen() ?? [], a) ? { instructionId: id, status: "started", mode: "window" } : { instructionId: id, status: "rejected", reason: "not_waiting" };
13262
+ const waiting = a.trust ? trustWaiting(io.screen() ?? [], trust?.tool ?? "") : stillWaiting(io.screen() ?? [], a);
13263
+ return waiting ? { instructionId: id, status: "started", mode: "window" } : { instructionId: id, status: "rejected", reason: "not_waiting" };
12732
13264
  }
12733
- const keys = { screen: () => io.screen() ?? [], cursorLine: () => io.cursorLine(), write: io.write, sleep: io.sleep };
13265
+ const keys = { screen: () => io.screen() ?? [], cursorLine: () => io.cursorLine(), write: io.write, sleep: io.sleep, hold: io.hold };
12734
13266
  let r;
12735
13267
  try {
12736
- r = a.kind === "permission" ? await answerPermission(keys, a.toolName, a.fingerprint, a.decision) : await answerQuestions(keys, a.questions, a.selections);
13268
+ if (a.kind === "trust" && trust?.widens) r = "not_waiting";
13269
+ else if (a.kind === "trust") r = await answerTrust(keys, trust?.tool ?? "", a.decision, trust?.forms ?? []);
13270
+ else if (a.kind === "permission") r = await answerPermission(keys, a.toolName, a.fingerprint, a.decision);
13271
+ else r = await answerQuestions(keys, a.questions, a.selections);
12737
13272
  } catch {
12738
13273
  r = "partial";
12739
13274
  }
@@ -12810,8 +13345,24 @@ async function runAnswerWindow(tool, real2, args, env, windowId, d) {
12810
13345
  let server = null;
12811
13346
  let finished = false;
12812
13347
  let chain = Promise.resolve();
13348
+ const trust = new TrustWatcher({
13349
+ tool,
13350
+ cwd: d.cwd,
13351
+ now: () => Date.now(),
13352
+ notify: () => void (d.notifyTrust ?? notifyTrust)(tool, windowId).then((ok) => ok || d.log(`[${windowId.slice(0, 8)}] \u4FE1\u4EFB\u6846\u6CA1\u80FD\u62A5\u7ED9 daemon\uFF08daemon \u4E0D\u5728\uFF1F\uFF09\uFF0C\u8BF7\u5728\u7535\u8111\u4E0A\u786E\u8BA4`)),
13353
+ log: (m) => d.log(`[${windowId.slice(0, 8)}] ${m}`),
13354
+ ready: () => server !== null
13355
+ });
13356
+ const trustCtx = { tool, forms: trust.forms, widens: trustWidensToRepo(tool, d.cwd) };
13357
+ const trustTimer = setInterval(() => {
13358
+ try {
13359
+ trust.tick(term && !finished ? screenLines(term) : null);
13360
+ } catch {
13361
+ }
13362
+ }, TRUST_TICK_MS);
13363
+ trustTimer.unref?.();
12813
13364
  try {
12814
- writeWindow(windowId, { pid: process.pid, childPid: p.pid, sock, tool, cwd: d.cwd, at: Date.now(), secret: secret.toString("hex"), launched: true, answer: true });
13365
+ writeWindow(windowId, { pid: process.pid, childPid: p.pid, sock, tool, cwd: d.cwd, at: Date.now(), secret: secret.toString("hex"), launched: true, answer: true, trust: true });
12815
13366
  } catch (e) {
12816
13367
  d.log(`\u767B\u8BB0\u7A97\u53E3\u5931\u8D25\uFF1A${e.message}`);
12817
13368
  }
@@ -12824,6 +13375,12 @@ async function runAnswerWindow(tool, real2, args, env, windowId, d) {
12824
13375
  d.platform,
12825
13376
  (req2, reply) => {
12826
13377
  reply({ instructionId: req2.instructionId, status: "ack" });
13378
+ const a = req2.answer;
13379
+ if (a.kind === "trust" || a.kind === "probe" && a.trust) trust.noteContact();
13380
+ if (tool !== "workbuddy" && a.kind !== "trust" && !(a.kind === "probe" && a.trust)) {
13381
+ reply({ instructionId: req2.instructionId, status: "rejected", reason: "unsupported" });
13382
+ return;
13383
+ }
12827
13384
  chain = chain.then(async () => {
12828
13385
  const r = await applyAnswer(req2, {
12829
13386
  screen: () => term && !finished ? screenLines(term) : null,
@@ -12836,7 +13393,7 @@ async function runAnswerWindow(tool, real2, args, env, windowId, d) {
12836
13393
  },
12837
13394
  write: toPty,
12838
13395
  sleep: (ms) => new Promise((res) => setTimeout(res, ms))
12839
- });
13396
+ }, trustCtx);
12840
13397
  if (req2.answer.kind !== "probe") d.log(`[${windowId.slice(0, 8)}] \u8BBE\u5907\u7B54\u590D\uFF08${req2.answer.kind}\uFF09\uFF1A${r.status}${r.reason ? " " + r.reason : ""}`);
12841
13398
  reply(r);
12842
13399
  });
@@ -12864,6 +13421,7 @@ async function runAnswerWindow(tool, real2, args, env, windowId, d) {
12864
13421
  return new Promise((resolve8) => {
12865
13422
  p.onExit(({ exitCode, signal }) => {
12866
13423
  finished = true;
13424
+ clearInterval(trustTimer);
12867
13425
  for (const [s2, h] of sigs) process.removeListener(s2, h);
12868
13426
  process.removeListener("SIGINT", ignoreInt);
12869
13427
  closeChannelServer(server, sock, d.platform);
@@ -12901,7 +13459,7 @@ function childEnv(src, windowId) {
12901
13459
  }
12902
13460
  function wrapLog(line) {
12903
13461
  try {
12904
- const p = join39(deskbuddyHome(), "wrap.log");
13462
+ const p = join40(deskbuddyHome(), "wrap.log");
12905
13463
  mkdirSync28(deskbuddyHome(), { recursive: true });
12906
13464
  try {
12907
13465
  if (statSync16(p).size > MAX_LOG_BYTES3) truncateSync3(p, 0);
@@ -12989,6 +13547,18 @@ var defaultLaunchDeps = () => ({
12989
13547
  isDir: isDirSync,
12990
13548
  chdir: (p) => process.chdir(p)
12991
13549
  });
13550
+ function answerDeps(deps2, cwd) {
13551
+ return {
13552
+ platform: deps2.platform,
13553
+ stdin: deps2.stdin,
13554
+ stdout: deps2.stdout,
13555
+ cwd,
13556
+ loadPty: deps2.loadPty,
13557
+ loadHeadless: deps2.loadHeadless,
13558
+ log: deps2.log,
13559
+ ...deps2.notifyTrust ? { notifyTrust: deps2.notifyTrust } : {}
13560
+ };
13561
+ }
12992
13562
  function defaultPromptDeps(deps2, cwd) {
12993
13563
  return {
12994
13564
  platform: deps2.platform,
@@ -12999,19 +13569,18 @@ function defaultPromptDeps(deps2, cwd) {
12999
13569
  receive: (id, tool, c) => receiveFirstLine(id, tool, c, { platform: deps2.platform, log: deps2.log }),
13000
13570
  run: async (tool, real2, args, env, answerWindowId) => {
13001
13571
  if (tool === "workbuddy" && answerWindowId) {
13002
- const r = await runAnswerWindow(tool, real2, args, env, answerWindowId, {
13003
- platform: deps2.platform,
13004
- stdin: deps2.stdin,
13005
- stdout: deps2.stdout,
13006
- cwd,
13007
- loadPty: deps2.loadPty,
13008
- loadHeadless: deps2.loadHeadless,
13009
- log: deps2.log
13010
- });
13572
+ const r = await runAnswerWindow(tool, real2, args, env, answerWindowId, answerDeps(deps2, cwd));
13011
13573
  if (r !== null) return r;
13012
13574
  return runDirect(real2, args, env);
13013
13575
  }
13014
13576
  if (tool === "codex") {
13577
+ const ptyRun = async (r, a) => {
13578
+ if (answerWindowId) {
13579
+ const code = await runAnswerWindow(tool, r, a, env, answerWindowId, answerDeps(deps2, cwd));
13580
+ if (code !== null) return code;
13581
+ }
13582
+ return runDirect(r, a, env);
13583
+ };
13015
13584
  return runCodexWindow(
13016
13585
  real2,
13017
13586
  args,
@@ -13021,7 +13590,7 @@ function defaultPromptDeps(deps2, cwd) {
13021
13590
  stdinTTY: !!deps2.stdin.isTTY,
13022
13591
  stdoutTTY: !!deps2.stdout.isTTY,
13023
13592
  cwd: () => cwd,
13024
- runDirect: (r, a) => runDirect(r, a, env)
13593
+ runDirect: ptyRun
13025
13594
  })
13026
13595
  );
13027
13596
  }
@@ -13121,21 +13690,26 @@ function runAttached(tool, p, term0, windowId, deps2, launched = false) {
13121
13690
  } catch {
13122
13691
  }
13123
13692
  };
13693
+ let holdDepth = 0;
13694
+ const holdKeys = (on) => {
13695
+ if (on) {
13696
+ holdDepth++;
13697
+ held = held ?? [];
13698
+ return;
13699
+ }
13700
+ holdDepth = Math.max(0, holdDepth - 1);
13701
+ if (holdDepth > 0) return;
13702
+ const h = held ?? [];
13703
+ held = null;
13704
+ for (const s2 of h) toPty(s2);
13705
+ };
13124
13706
  const injector = new Injector({
13125
13707
  now: () => Date.now(),
13126
13708
  view: () => term ? inspectLines(screenLines(term), screenLinesUndimmed(term)) : null,
13127
13709
  lastEvent: () => readLastEvent(windowId),
13128
13710
  write: toPty,
13129
13711
  sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
13130
- holdKeys: (on) => {
13131
- if (on) {
13132
- held = held ?? [];
13133
- return;
13134
- }
13135
- const h = held ?? [];
13136
- held = null;
13137
- for (const s2 of h) toPty(s2);
13138
- },
13712
+ holdKeys,
13139
13713
  log: (m) => deps2.log(`[${windowId.slice(0, 8)}] ${m}`),
13140
13714
  startupIdle: launched
13141
13715
  });
@@ -13194,6 +13768,16 @@ function runAttached(tool, p, term0, windowId, deps2, launched = false) {
13194
13768
  } catch (e) {
13195
13769
  deps2.log(`\u63A5\u7BA1\u7EC8\u7AEF\u51FA\u9519\uFF1A${e.message}`);
13196
13770
  }
13771
+ const trust = launched ? new TrustWatcher({
13772
+ tool,
13773
+ cwd: deps2.cwd(),
13774
+ now: () => Date.now(),
13775
+ notify: () => void (deps2.notifyTrust ?? notifyTrust)(tool, windowId).then((ok) => ok || deps2.log(`[${windowId.slice(0, 8)}] \u4FE1\u4EFB\u6846\u6CA1\u80FD\u62A5\u7ED9 daemon\uFF0C\u8BF7\u5728\u7535\u8111\u4E0A\u786E\u8BA4`)),
13776
+ log: (m) => deps2.log(`[${windowId.slice(0, 8)}] ${m}`),
13777
+ ready: () => server !== null
13778
+ }) : null;
13779
+ const trustCtx = trust ? { tool, forms: trust.forms, widens: trustWidensToRepo(tool, deps2.cwd()) } : void 0;
13780
+ let answerChain = Promise.resolve();
13197
13781
  const secret = randomBytes10(32);
13198
13782
  try {
13199
13783
  writeWindow(windowId, {
@@ -13204,7 +13788,7 @@ function runAttached(tool, p, term0, windowId, deps2, launched = false) {
13204
13788
  cwd: deps2.cwd(),
13205
13789
  at: Date.now(),
13206
13790
  secret: secret.toString("hex"),
13207
- ...launched ? { launched: true } : {}
13791
+ ...launched ? { launched: true, trust: true } : {}
13208
13792
  });
13209
13793
  } catch (e) {
13210
13794
  deps2.log(`\u767B\u8BB0\u7A97\u53E3\u5931\u8D25\uFF1A${e.message}`);
@@ -13221,7 +13805,32 @@ function runAttached(tool, p, term0, windowId, deps2, launched = false) {
13221
13805
  deps2.log(`\u6536\u6307\u4EE4\u51FA\u9519\uFF1A${e.message}`);
13222
13806
  }
13223
13807
  },
13224
- deps2.platform
13808
+ deps2.platform,
13809
+ // 设备答复只收信任框(授权框走 hook 的 PermissionRequest,不经这里);用户自己开的窗口一律不收
13810
+ (req2, reply) => {
13811
+ reply({ instructionId: req2.instructionId, status: "ack" });
13812
+ const a = req2.answer;
13813
+ if (trust && (a.kind === "trust" || a.kind === "probe" && a.trust)) trust.noteContact();
13814
+ if (!trustCtx || !(a.kind === "trust" || a.kind === "probe" && a.trust)) {
13815
+ reply({ instructionId: req2.instructionId, status: "rejected", reason: "unsupported" });
13816
+ return;
13817
+ }
13818
+ answerChain = answerChain.then(async () => {
13819
+ const r = await applyAnswer(
13820
+ req2,
13821
+ {
13822
+ screen: () => term && !finished ? screenLines(term) : null,
13823
+ cursorLine: () => null,
13824
+ write: toPty,
13825
+ sleep: (ms) => new Promise((res) => setTimeout(res, ms)),
13826
+ hold: holdKeys
13827
+ },
13828
+ trustCtx
13829
+ );
13830
+ if (a.kind !== "probe") deps2.log(`[${windowId.slice(0, 8)}] \u8BBE\u5907\u4FE1\u4EFB\u7B54\u590D\uFF08${a.decision}\uFF09\uFF1A${r.status}${r.reason ? " " + r.reason : ""}`);
13831
+ reply(r);
13832
+ });
13833
+ }
13225
13834
  ).then((s2) => {
13226
13835
  if (finished) {
13227
13836
  closeChannelServer(s2, sock, deps2.platform);
@@ -13234,6 +13843,12 @@ function runAttached(tool, p, term0, windowId, deps2, launched = false) {
13234
13843
  timer = setInterval(() => {
13235
13844
  injector.tick().catch(() => {
13236
13845
  });
13846
+ if (trust) {
13847
+ try {
13848
+ trust.tick(term ? screenLines(term) : null);
13849
+ } catch {
13850
+ }
13851
+ }
13237
13852
  }, TICK_MS);
13238
13853
  return new Promise((resolve8) => {
13239
13854
  let degraded = false;