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