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 +789 -174
- package/dist/hook.cjs +15 -3
- package/hook/hook-debug.log +1 -0
- package/hook/hook.cjs +15 -3
- package/package.json +1 -1
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
|
|
1488
|
-
import { basename as basename9, dirname as
|
|
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
|
|
3442
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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) =>
|
|
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
|
|
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 &&
|
|
6742
|
+
if (dir === void 0 && d && inner2.type !== "server.connected" && inner2.type !== "server.heartbeat") this.#sseVerified = true;
|
|
6230
6743
|
try {
|
|
6231
|
-
this.onEvent(
|
|
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(
|
|
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
|
|
8081
|
+
import { join as join23 } from "path";
|
|
7569
8082
|
function codexRemotePath() {
|
|
7570
|
-
return
|
|
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
|
|
7651
|
-
import { join as
|
|
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
|
|
8174
|
+
return join24(home, "launch");
|
|
7662
8175
|
}
|
|
7663
8176
|
function planFile(runId) {
|
|
7664
|
-
return
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
8325
|
-
import { join as
|
|
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
|
|
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 ?
|
|
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(
|
|
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 ?
|
|
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(
|
|
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
|
|
8419
|
-
import { join as
|
|
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
|
|
8943
|
+
return join28(home, "dsh");
|
|
8431
8944
|
}
|
|
8432
8945
|
function sub(home, name) {
|
|
8433
|
-
return
|
|
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(
|
|
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 =
|
|
8492
|
-
const ackPath =
|
|
8493
|
-
if (
|
|
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 (
|
|
9014
|
+
if (existsSync14(ackPath)) {
|
|
8502
9015
|
let ack = { ok: false, reason: "bad_ack" };
|
|
8503
9016
|
try {
|
|
8504
|
-
const v = JSON.parse(
|
|
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 (
|
|
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
|
|
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(
|
|
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()) &&
|
|
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 &&
|
|
9452
|
+
if (st.transcriptPath && existsSync15(st.transcriptPath)) return st.transcriptPath;
|
|
8934
9453
|
const cached = this.#transcripts.get(st.sessionId);
|
|
8935
|
-
if (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
|
-
|
|
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 (!
|
|
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
|
|
10017
|
-
import { join as
|
|
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
|
|
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(
|
|
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
|
|
10198
|
-
import { dirname as
|
|
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 (!
|
|
10209
|
-
const raw =
|
|
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(
|
|
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 (!
|
|
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 =
|
|
10351
|
-
const wakePath =
|
|
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(
|
|
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
|
|
10372
|
-
import { dirname as
|
|
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
|
|
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(
|
|
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 =
|
|
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 (!
|
|
10442
|
-
const raw =
|
|
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
|
|
10457
|
-
import { dirname as
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
10519
|
-
const raw = exists ?
|
|
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
|
|
11097
|
+
import { existsSync as existsSync19, readFileSync as readFileSync24 } from "fs";
|
|
10569
11098
|
function fileContains(path, needle) {
|
|
10570
11099
|
try {
|
|
10571
|
-
if (!
|
|
10572
|
-
return
|
|
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(
|
|
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
|
|
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
|
|
10687
|
-
import { homedir as
|
|
10688
|
-
import { delimiter as delimiter2, join as
|
|
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
|
|
11227
|
+
return join34(deskbuddyHome(), "bin");
|
|
10699
11228
|
}
|
|
10700
11229
|
function shimFile(binDir, command, platform = process.platform) {
|
|
10701
|
-
return
|
|
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
|
|
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 :
|
|
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 =
|
|
10922
|
-
if (!
|
|
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
|
|
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 (
|
|
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 =
|
|
10980
|
-
if (!
|
|
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 (
|
|
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 =
|
|
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
|
|
11541
|
+
import { dirname as dirname17, join as join35 } from "path";
|
|
11013
11542
|
|
|
11014
11543
|
// src/core/detect.ts
|
|
11015
|
-
import { existsSync as
|
|
11016
|
-
import { dirname as
|
|
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
|
|
11554
|
+
return dirname16(workbuddySettingsPath());
|
|
11026
11555
|
case "qoder_cn":
|
|
11027
|
-
return
|
|
11556
|
+
return dirname16(qoderCnSettingsPath());
|
|
11028
11557
|
case "qoder":
|
|
11029
|
-
return
|
|
11558
|
+
return dirname16(qoderSettingsPath());
|
|
11030
11559
|
case "opencode":
|
|
11031
|
-
return
|
|
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:
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
11805
|
+
import { existsSync as existsSync22, statSync as statSync14 } from "fs";
|
|
11277
11806
|
import { createInterface } from "readline";
|
|
11278
|
-
import { homedir as
|
|
11807
|
+
import { homedir as homedir10 } from "os";
|
|
11279
11808
|
import { isAbsolute as isAbsolute2, resolve as resolve6 } from "path";
|
|
11280
|
-
function normalizePath(raw, home =
|
|
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 (!
|
|
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 =
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
12638
|
+
import { realpathSync as realpathSync7 } from "fs";
|
|
12110
12639
|
import { constants as constants2 } from "os";
|
|
12111
|
-
import { dirname as
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
12955
|
+
return join38(deskbuddyHome(), "codex-app-server.log");
|
|
12427
12956
|
}
|
|
12428
12957
|
function windowsDir2() {
|
|
12429
|
-
return
|
|
12958
|
+
return join38(deskbuddyHome(), "codex-windows");
|
|
12430
12959
|
}
|
|
12431
|
-
function
|
|
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(
|
|
12971
|
+
pid = Number(readFileSync26(pidFile, "utf8").trim());
|
|
12443
12972
|
} catch {
|
|
12444
12973
|
return false;
|
|
12445
12974
|
}
|
|
12446
|
-
if (!Number.isInteger(pid) || pid <= 0 || !
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
13064
|
+
const p = join38(dir, n);
|
|
12536
13065
|
let rec;
|
|
12537
13066
|
try {
|
|
12538
|
-
rec = JSON.parse(
|
|
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(
|
|
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
|
|
12583
|
-
import { join as
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
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
|
|
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;
|