deskbuddy 0.2.0-beta.17 → 0.2.0-beta.18
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 +437 -111
- package/dist/gate.cjs +74 -25
- package/dist/hook.cjs +241 -7
- package/hook/gate.cjs +74 -25
- package/hook/hook-debug.log +3 -0
- package/hook/hook.cjs +241 -7
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -731,7 +731,13 @@ function winNormalizedArgs(name, cmdline) {
|
|
|
731
731
|
}
|
|
732
732
|
return argv.map((a) => a.replace(/\s+/g, "_").replace(/\\/g, "/")).join(" ");
|
|
733
733
|
}
|
|
734
|
-
var WIN_PS_SCRIPT = "[Console]::OutputEncoding=[Text.Encoding]::UTF8;Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine | ConvertTo-Json -Compress";
|
|
734
|
+
var WIN_PS_SCRIPT = "[Console]::OutputEncoding=[Text.Encoding]::UTF8;Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine,CreationDate | ConvertTo-Json -Compress";
|
|
735
|
+
function winCreationMs(v) {
|
|
736
|
+
if (typeof v !== "string" || !v) return void 0;
|
|
737
|
+
const m = /^\/?Date\((\d+)/.exec(v);
|
|
738
|
+
const ms = m ? Number(m[1]) : Date.parse(v);
|
|
739
|
+
return Number.isFinite(ms) && ms > 0 ? ms : void 0;
|
|
740
|
+
}
|
|
735
741
|
function parseWinProcJson(json) {
|
|
736
742
|
let d;
|
|
737
743
|
try {
|
|
@@ -746,12 +752,14 @@ function parseWinProcJson(json) {
|
|
|
746
752
|
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
747
753
|
const rawName = typeof r.Name === "string" ? r.Name : "";
|
|
748
754
|
const cmdline = typeof r.CommandLine === "string" ? r.CommandLine.replace(/[\r\n]+/g, " ") : "";
|
|
755
|
+
const start = winCreationMs(r.CreationDate);
|
|
749
756
|
out.push({
|
|
750
757
|
pid,
|
|
751
758
|
ppid: Number(r.ParentProcessId) || 0,
|
|
752
759
|
name: winEffectiveName(rawName, cmdline),
|
|
753
760
|
args: winNormalizedArgs(rawName, cmdline),
|
|
754
|
-
cmdline
|
|
761
|
+
cmdline,
|
|
762
|
+
...start !== void 0 ? { start } : {}
|
|
755
763
|
});
|
|
756
764
|
}
|
|
757
765
|
return out;
|
|
@@ -1499,10 +1507,27 @@ import { existsSync as existsSync8 } from "fs";
|
|
|
1499
1507
|
import { dirname as dirname7 } from "path";
|
|
1500
1508
|
|
|
1501
1509
|
// src/daemon/locate.ts
|
|
1502
|
-
import { execFileSync as execFileSync5 } from "child_process";
|
|
1503
|
-
import { accessSync, constants, existsSync as existsSync7, lstatSync, readFileSync as readFileSync6, realpathSync, statSync as statSync2 } from "fs";
|
|
1510
|
+
import { execFile as execFile3, execFileSync as execFileSync5 } from "child_process";
|
|
1511
|
+
import { accessSync, closeSync as closeSync2, constants, existsSync as existsSync7, lstatSync, openSync as openSync2, readFileSync as readFileSync6, readSync, realpathSync, statSync as statSync2 } from "fs";
|
|
1504
1512
|
import { homedir as homedir3 } from "os";
|
|
1505
1513
|
import { delimiter, dirname as dirname6, join as join7, win32 as win322 } from "path";
|
|
1514
|
+
|
|
1515
|
+
// src/core/agentEnv.ts
|
|
1516
|
+
var BUN_HIJACK_ENV = ["BUN_BE_BUN", "BUN_OPTIONS"];
|
|
1517
|
+
function scrubAgentEnv(env) {
|
|
1518
|
+
for (const k of Object.keys(env)) {
|
|
1519
|
+
if (BUN_HIJACK_ENV.includes(k.toUpperCase())) delete env[k];
|
|
1520
|
+
}
|
|
1521
|
+
return env;
|
|
1522
|
+
}
|
|
1523
|
+
var AGENT_SPAWNING_COMMANDS = ["daemon", "wrap"];
|
|
1524
|
+
function scrubForCommand(cmd, env = process.env) {
|
|
1525
|
+
if (!cmd || !AGENT_SPAWNING_COMMANDS.includes(cmd)) return false;
|
|
1526
|
+
scrubAgentEnv(env);
|
|
1527
|
+
return true;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
// src/daemon/locate.ts
|
|
1506
1531
|
function qoderWorker(d, worker, appBin) {
|
|
1507
1532
|
if (d.isExecutable(appBin)) return { file: appBin, prefixArgs: [worker], env: { ELECTRON_RUN_AS_NODE: "1" } };
|
|
1508
1533
|
return { file: d.nodePath, prefixArgs: [worker] };
|
|
@@ -1541,8 +1566,124 @@ var defaultLocateDeps = () => ({
|
|
|
1541
1566
|
},
|
|
1542
1567
|
// 🔴 daemon 自己也不能找到垫片:远程任务要的是真程序(窗口接管 spec 2026-09-30 §3.1)
|
|
1543
1568
|
skipDirs: [join7(deskbuddyHome(), "bin")],
|
|
1544
|
-
winAppHints: (product, exe) => winInstallDirs(product, exe)
|
|
1569
|
+
winAppHints: (product, exe) => winInstallDirs(product, exe),
|
|
1570
|
+
isPe: (p) => {
|
|
1571
|
+
let fd = null;
|
|
1572
|
+
try {
|
|
1573
|
+
fd = openSync2(p, "r");
|
|
1574
|
+
const b = Buffer.alloc(2);
|
|
1575
|
+
return readSync(fd, b, 0, 2, 0) === 2 && b.toString("latin1") === "MZ";
|
|
1576
|
+
} catch {
|
|
1577
|
+
return false;
|
|
1578
|
+
} finally {
|
|
1579
|
+
if (fd !== null) closeSync2(fd);
|
|
1580
|
+
}
|
|
1581
|
+
},
|
|
1582
|
+
versionOf: (loc) => probeVersion(loc)
|
|
1583
|
+
});
|
|
1584
|
+
var PROBE_TIMEOUT_MS = 3e3;
|
|
1585
|
+
var PROBE_SHORT_TTL_MS = 5 * 60 * 1e3;
|
|
1586
|
+
function createVersionProbe(d) {
|
|
1587
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
1588
|
+
const keyOf = (loc) => {
|
|
1589
|
+
let key = [loc.file, ...loc.prefixArgs].join("\0");
|
|
1590
|
+
for (const p of [loc.file, ...loc.prefixArgs.slice(0, 1)]) {
|
|
1591
|
+
const st = d.stat(p);
|
|
1592
|
+
if (st) key += `\0${st.size}:${st.mtimeMs}`;
|
|
1593
|
+
}
|
|
1594
|
+
return key;
|
|
1595
|
+
};
|
|
1596
|
+
const cached = (key) => {
|
|
1597
|
+
const hit = cache2.get(key);
|
|
1598
|
+
if (!hit) return null;
|
|
1599
|
+
if (hit.until > d.now()) return hit;
|
|
1600
|
+
cache2.delete(key);
|
|
1601
|
+
return null;
|
|
1602
|
+
};
|
|
1603
|
+
const record = (key, r) => {
|
|
1604
|
+
if (r.kind === "spawn_error") return null;
|
|
1605
|
+
if (r.kind === "timeout") {
|
|
1606
|
+
cache2.set(key, { out: null, until: d.now() + PROBE_SHORT_TTL_MS });
|
|
1607
|
+
return null;
|
|
1608
|
+
}
|
|
1609
|
+
const ok = /Claude Code/i.test(r.out);
|
|
1610
|
+
cache2.set(key, { out: r.out, until: ok ? Number.POSITIVE_INFINITY : d.now() + PROBE_SHORT_TTL_MS });
|
|
1611
|
+
return r.out;
|
|
1612
|
+
};
|
|
1613
|
+
return {
|
|
1614
|
+
probe(loc) {
|
|
1615
|
+
const key = keyOf(loc);
|
|
1616
|
+
const hit = cached(key);
|
|
1617
|
+
if (hit) return hit.out;
|
|
1618
|
+
return record(key, d.runSync(loc));
|
|
1619
|
+
},
|
|
1620
|
+
async warm(loc) {
|
|
1621
|
+
const key = keyOf(loc);
|
|
1622
|
+
if (cached(key)) return;
|
|
1623
|
+
record(key, await d.runAsync(loc));
|
|
1624
|
+
}
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
var probeEnv = (loc) => ({ ...scrubAgentEnv({ ...process.env }), ...loc.env ?? {} });
|
|
1628
|
+
function classifySyncError(e) {
|
|
1629
|
+
const err = e;
|
|
1630
|
+
if (err.code === "ETIMEDOUT" || err.signal) return { kind: "timeout" };
|
|
1631
|
+
if (typeof err.status === "number") return { kind: "done", out: `${err.stdout ?? ""}${err.stderr ?? ""}` };
|
|
1632
|
+
return { kind: "spawn_error" };
|
|
1633
|
+
}
|
|
1634
|
+
function classifyAsyncError(e, stdout7, stderr) {
|
|
1635
|
+
const err = e;
|
|
1636
|
+
if (typeof err.code === "string") return { kind: "spawn_error" };
|
|
1637
|
+
if (err.killed || err.signal) return { kind: "timeout" };
|
|
1638
|
+
return { kind: "done", out: `${stdout7}${stderr}` };
|
|
1639
|
+
}
|
|
1640
|
+
var defaultProbe = createVersionProbe({
|
|
1641
|
+
stat: (p) => {
|
|
1642
|
+
try {
|
|
1643
|
+
const st = statSync2(p);
|
|
1644
|
+
return { size: st.size, mtimeMs: st.mtimeMs };
|
|
1645
|
+
} catch {
|
|
1646
|
+
return null;
|
|
1647
|
+
}
|
|
1648
|
+
},
|
|
1649
|
+
runSync: (loc) => {
|
|
1650
|
+
try {
|
|
1651
|
+
const out = execFileSync5(loc.file, [...loc.prefixArgs, "--version"], {
|
|
1652
|
+
encoding: "utf8",
|
|
1653
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1654
|
+
windowsHide: true,
|
|
1655
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
1656
|
+
env: probeEnv(loc)
|
|
1657
|
+
});
|
|
1658
|
+
return { kind: "done", out };
|
|
1659
|
+
} catch (e) {
|
|
1660
|
+
return classifySyncError(e);
|
|
1661
|
+
}
|
|
1662
|
+
},
|
|
1663
|
+
runAsync: (loc) => new Promise((resolve10) => {
|
|
1664
|
+
try {
|
|
1665
|
+
execFile3(
|
|
1666
|
+
loc.file,
|
|
1667
|
+
[...loc.prefixArgs, "--version"],
|
|
1668
|
+
{ encoding: "utf8", windowsHide: true, timeout: PROBE_TIMEOUT_MS, env: probeEnv(loc) },
|
|
1669
|
+
(err, stdout7, stderr) => resolve10(err ? classifyAsyncError(err, String(stdout7 ?? ""), String(stderr ?? "")) : { kind: "done", out: `${stdout7}${stderr}` })
|
|
1670
|
+
);
|
|
1671
|
+
} catch {
|
|
1672
|
+
resolve10({ kind: "spawn_error" });
|
|
1673
|
+
}
|
|
1674
|
+
}),
|
|
1675
|
+
now: () => Date.now()
|
|
1545
1676
|
});
|
|
1677
|
+
function probeVersion(loc) {
|
|
1678
|
+
return defaultProbe.probe(loc);
|
|
1679
|
+
}
|
|
1680
|
+
async function warmClaudeProbe(d = defaultLocateDeps()) {
|
|
1681
|
+
if (!isWin(d)) return;
|
|
1682
|
+
try {
|
|
1683
|
+
for (const { loc } of winClaudeLaunches(d)) await defaultProbe.warm(loc);
|
|
1684
|
+
} catch {
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1546
1687
|
function installDirsFromProcs(procs, exe) {
|
|
1547
1688
|
const out = [];
|
|
1548
1689
|
for (const p of procs ?? []) {
|
|
@@ -1639,6 +1780,7 @@ function parseNpmCmdShim(shimPath, text) {
|
|
|
1639
1780
|
if (!m) return null;
|
|
1640
1781
|
return join7(dirname6(shimPath), m[1].replace(/^\\/, ""));
|
|
1641
1782
|
}
|
|
1783
|
+
var JS_RUNTIME_EXE = /^(?:node|bun|bunx|deno)(?:\.exe)?$/i;
|
|
1642
1784
|
function resolveWinLaunch(file, d) {
|
|
1643
1785
|
const lower = file.toLowerCase();
|
|
1644
1786
|
if (lower.endsWith(".exe") || lower.endsWith(".com")) return { file, prefixArgs: [] };
|
|
@@ -1647,6 +1789,7 @@ function resolveWinLaunch(file, d) {
|
|
|
1647
1789
|
const target = text ? parseNpmCmdShim(file, text) : null;
|
|
1648
1790
|
if (!target || !d.exists(target)) return null;
|
|
1649
1791
|
if (/\.(?:cmd|bat)$/i.test(target)) return null;
|
|
1792
|
+
if (JS_RUNTIME_EXE.test(win322.basename(target.replace(/\//g, "\\")))) return null;
|
|
1650
1793
|
return resolveWinLaunch(target, d);
|
|
1651
1794
|
}
|
|
1652
1795
|
if (/\.(?:c|m)?js$/.test(lower)) return { file: d.nodePath, prefixArgs: [file] };
|
|
@@ -1673,6 +1816,32 @@ function winClaudeExeCandidates(d) {
|
|
|
1673
1816
|
...d.appData ? [join7(d.appData, "npm", "node_modules", "@anthropic-ai", "claude-code", "bin", "claude.exe")] : []
|
|
1674
1817
|
];
|
|
1675
1818
|
}
|
|
1819
|
+
function winClaudeLaunches(d) {
|
|
1820
|
+
const out = [];
|
|
1821
|
+
for (const p of winClaudeExeCandidates(d)) if (d.exists(p)) out.push({ src: p, loc: { file: p, prefixArgs: [] } });
|
|
1822
|
+
for (const f of whichAllIn("claude", d)) {
|
|
1823
|
+
const loc = resolveWinLaunch(f, d);
|
|
1824
|
+
if (loc) out.push({ src: f, loc });
|
|
1825
|
+
}
|
|
1826
|
+
return out;
|
|
1827
|
+
}
|
|
1828
|
+
function isClaudeCode(loc, d) {
|
|
1829
|
+
if (loc.prefixArgs.length === 0 && /\.exe$/i.test(loc.file) && d.isPe && !d.isPe(loc.file)) return false;
|
|
1830
|
+
const out = d.versionOf?.(loc);
|
|
1831
|
+
if (out === void 0 || out === null) return true;
|
|
1832
|
+
return /Claude Code/i.test(out);
|
|
1833
|
+
}
|
|
1834
|
+
function pickWinClaude(d, accept = () => true) {
|
|
1835
|
+
const tried = /* @__PURE__ */ new Set();
|
|
1836
|
+
for (const { src, loc } of winClaudeLaunches(d)) {
|
|
1837
|
+
if (!accept(src, loc)) continue;
|
|
1838
|
+
const key = [loc.file, ...loc.prefixArgs].join("\0").toLowerCase();
|
|
1839
|
+
if (tried.has(key)) continue;
|
|
1840
|
+
tried.add(key);
|
|
1841
|
+
if (isClaudeCode(loc, d)) return loc;
|
|
1842
|
+
}
|
|
1843
|
+
return null;
|
|
1844
|
+
}
|
|
1676
1845
|
function locateWin(tool, d) {
|
|
1677
1846
|
const plain = (name) => {
|
|
1678
1847
|
for (const f of whichAllIn(name, d)) {
|
|
@@ -1683,8 +1852,7 @@ function locateWin(tool, d) {
|
|
|
1683
1852
|
};
|
|
1684
1853
|
switch (tool) {
|
|
1685
1854
|
case "claude_code":
|
|
1686
|
-
|
|
1687
|
-
return plain("claude");
|
|
1855
|
+
return pickWinClaude(d);
|
|
1688
1856
|
case "codex":
|
|
1689
1857
|
return plain("codex");
|
|
1690
1858
|
case "workbuddy": {
|
|
@@ -1917,7 +2085,9 @@ var FALLBACK = {
|
|
|
1917
2085
|
// Qoder 国内版(Qoder CN.app,配置 ~/.qoder-cn)
|
|
1918
2086
|
qoder_cn: {
|
|
1919
2087
|
format: "cc-hook",
|
|
1920
|
-
|
|
2088
|
+
// qoderclicn:终端里的命令行窗口(Bug 204)。认不出它的话会话没有进程号、interactive=false,
|
|
2089
|
+
// 打断补落与窗口授权都不管(2026-10-03 qoderclicn 1.1.59 实测)
|
|
2090
|
+
processNames: ["Qoder CN", "qoderclicn"],
|
|
1921
2091
|
// 桌面端会话库里的标题(先是首条指令,后被换成 AI 摘要);命令行跑的会话不在库里,落到首条指令
|
|
1922
2092
|
// (CLI 1.1.61 实测 transcript 里没有 ai-title,UserPromptSubmit 带 prompt)
|
|
1923
2093
|
titleSources: ["qoder-session-title", "first-prompt", "git-branch", "project"],
|
|
@@ -3211,7 +3381,7 @@ var AutoEnabler = class {
|
|
|
3211
3381
|
// src/daemon/index.ts
|
|
3212
3382
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
3213
3383
|
import { existsSync as existsSync20, statSync as statSync14 } from "fs";
|
|
3214
|
-
import { basename as basename9, dirname as
|
|
3384
|
+
import { basename as basename9, dirname as dirname19, join as join36 } from "path";
|
|
3215
3385
|
import WebSocket2 from "ws";
|
|
3216
3386
|
|
|
3217
3387
|
// src/core/state.ts
|
|
@@ -3277,8 +3447,55 @@ function isAlive(pid, expectName) {
|
|
|
3277
3447
|
return name != null && name.toLowerCase().includes(expectName.toLowerCase());
|
|
3278
3448
|
}
|
|
3279
3449
|
|
|
3450
|
+
// src/core/codexServer.ts
|
|
3451
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
3452
|
+
function isAppServerArgs(args, url) {
|
|
3453
|
+
return /\bapp-server\b/.test(args) && new RegExp(`--listen[= ]${url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|$)`).test(args);
|
|
3454
|
+
}
|
|
3455
|
+
function processArgs(pid, platform = process.platform, procs, fresh = false) {
|
|
3456
|
+
if (platform === "win32") {
|
|
3457
|
+
const list = procs ? procs() : winProcs(Date.now(), { force: fresh });
|
|
3458
|
+
const p = (list ?? []).find((x) => x.pid === pid);
|
|
3459
|
+
return p?.args || null;
|
|
3460
|
+
}
|
|
3461
|
+
try {
|
|
3462
|
+
const out = execFileSync8("ps", ["-ww", "-o", "args=", "-p", String(pid)], {
|
|
3463
|
+
encoding: "utf8",
|
|
3464
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
3465
|
+
});
|
|
3466
|
+
return out.trim() || null;
|
|
3467
|
+
} catch {
|
|
3468
|
+
return null;
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
function isCodexManagedDaemonArgs(args) {
|
|
3472
|
+
const s2 = String(args || "").replace(/\\/g, "/");
|
|
3473
|
+
return /(?:^|\s)--managed-daemon(?:[=\s]|$)/.test(s2) || /\/app-server-daemon\//i.test(programOf(s2)) || /(?:^|\s)app-server\s+daemon(?:\s|$)/.test(s2);
|
|
3474
|
+
}
|
|
3475
|
+
var SCRIPT_HOST_RE = /^(?:node|bun|deno)(?:\.exe)?$/i;
|
|
3476
|
+
function baseName(p) {
|
|
3477
|
+
return p.replace(/\\/g, "/").split("/").pop().toLowerCase();
|
|
3478
|
+
}
|
|
3479
|
+
function progAt(all, i, re) {
|
|
3480
|
+
for (let j = i; j < Math.min(all.length, i + 8); j++) {
|
|
3481
|
+
const cand = all.slice(i, j + 1).join(" ");
|
|
3482
|
+
if (re.test(baseName(cand))) return { prog: cand, next: j + 1 };
|
|
3483
|
+
if (!/^(?:\/|[A-Za-z]:\/)/.test(all[i].replace(/\\/g, "/"))) break;
|
|
3484
|
+
}
|
|
3485
|
+
return null;
|
|
3486
|
+
}
|
|
3487
|
+
function programOf(args) {
|
|
3488
|
+
const all = String(args || "").trim().split(/\s+/).filter(Boolean);
|
|
3489
|
+
const h = progAt(all, 0, SCRIPT_HOST_RE);
|
|
3490
|
+
if (h) {
|
|
3491
|
+
const k = all.findIndex((a, n) => n >= h.next && !a.startsWith("-"));
|
|
3492
|
+
if (k >= 0) return all[k];
|
|
3493
|
+
}
|
|
3494
|
+
return all[0] || "";
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3280
3497
|
// src/core/ended.ts
|
|
3281
|
-
import { closeSync as
|
|
3498
|
+
import { closeSync as closeSync3, mkdirSync as mkdirSync12, openSync as openSync3, readdirSync as readdirSync4, readFileSync as readFileSync14, readSync as readSync2, realpathSync as realpathSync2, renameSync as renameSync8, rmSync as rmSync6, statSync as statSync5, writeFileSync as writeFileSync11 } from "fs";
|
|
3282
3499
|
import { join as join16 } from "path";
|
|
3283
3500
|
var ENDED_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3284
3501
|
var SID_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
@@ -3328,11 +3545,11 @@ function readChunk(file, fromEnd, max) {
|
|
|
3328
3545
|
const size = statSync5(file).size;
|
|
3329
3546
|
const len = Math.min(size, max);
|
|
3330
3547
|
const buf = Buffer.alloc(len);
|
|
3331
|
-
fd =
|
|
3332
|
-
|
|
3548
|
+
fd = openSync3(file, "r");
|
|
3549
|
+
readSync2(fd, buf, 0, len, fromEnd ? size - len : 0);
|
|
3333
3550
|
return buf.toString("utf8");
|
|
3334
3551
|
} finally {
|
|
3335
|
-
if (fd !== void 0)
|
|
3552
|
+
if (fd !== void 0) closeSync3(fd);
|
|
3336
3553
|
}
|
|
3337
3554
|
}
|
|
3338
3555
|
function cwdsIn(text) {
|
|
@@ -3446,6 +3663,13 @@ function isStaleNoPid(s2, now, pending) {
|
|
|
3446
3663
|
if (s2.pid || !STALE_STATES.has(s2.state) || !(now - s2.updatedAt > STALE_NO_PID_MS)) return false;
|
|
3447
3664
|
return !pending().has(s2.sessionId);
|
|
3448
3665
|
}
|
|
3666
|
+
function dropManagedDaemonPid(s2, args = (pid) => processArgs(pid)) {
|
|
3667
|
+
if (s2.tool !== "codex" || !s2.pid || s2.pidChecked === true || !isCodexManagedDaemonArgs(args(s2.pid) ?? "")) return s2;
|
|
3668
|
+
const next = { ...s2 };
|
|
3669
|
+
delete next.pid;
|
|
3670
|
+
delete next.pidName;
|
|
3671
|
+
return next;
|
|
3672
|
+
}
|
|
3449
3673
|
function readAliveStates(tool, opts = {}) {
|
|
3450
3674
|
if (!existsSync16(stateDir())) return [];
|
|
3451
3675
|
const now = opts.now ?? Date.now();
|
|
@@ -3466,6 +3690,7 @@ function readAliveStates(tool, opts = {}) {
|
|
|
3466
3690
|
retireState(s2);
|
|
3467
3691
|
continue;
|
|
3468
3692
|
}
|
|
3693
|
+
s2 = dropManagedDaemonPid(s2, opts.args);
|
|
3469
3694
|
if (isStaleNoPid(s2, now, pending)) {
|
|
3470
3695
|
try {
|
|
3471
3696
|
opts.onStale?.(s2);
|
|
@@ -3713,7 +3938,7 @@ function isExpired(cmd, now = Date.now()) {
|
|
|
3713
3938
|
}
|
|
3714
3939
|
|
|
3715
3940
|
// src/daemon/executor.ts
|
|
3716
|
-
import { execFile as
|
|
3941
|
+
import { execFile as execFile4, execFileSync as execFileSync9 } from "child_process";
|
|
3717
3942
|
import { homedir as homedir6 } from "os";
|
|
3718
3943
|
import { join as join19 } from "path";
|
|
3719
3944
|
var EXEC_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
@@ -3991,7 +4216,7 @@ function startCommand(plan, tool, limitMs = EXEC_TIMEOUT_MS) {
|
|
|
3991
4216
|
};
|
|
3992
4217
|
let timedOut = false;
|
|
3993
4218
|
const done = new Promise((resolve10) => {
|
|
3994
|
-
child =
|
|
4219
|
+
child = execFile4(
|
|
3995
4220
|
plan.file,
|
|
3996
4221
|
plan.args,
|
|
3997
4222
|
{
|
|
@@ -4039,7 +4264,7 @@ function startCommand(plan, tool, limitMs = EXEC_TIMEOUT_MS) {
|
|
|
4039
4264
|
};
|
|
4040
4265
|
}
|
|
4041
4266
|
function mergeEnv(base, extra, platform = process.platform) {
|
|
4042
|
-
const env = { ...base };
|
|
4267
|
+
const env = scrubAgentEnv({ ...base });
|
|
4043
4268
|
for (const [k, v] of Object.entries(extra ?? {})) {
|
|
4044
4269
|
if (platform === "win32") {
|
|
4045
4270
|
for (const old of Object.keys(env)) if (old !== k && old.toUpperCase() === k.toUpperCase()) delete env[old];
|
|
@@ -4052,7 +4277,7 @@ function killTree(child) {
|
|
|
4052
4277
|
if (!child) return;
|
|
4053
4278
|
if (process.platform === "win32" && child.pid) {
|
|
4054
4279
|
try {
|
|
4055
|
-
|
|
4280
|
+
execFileSync9("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
4056
4281
|
return;
|
|
4057
4282
|
} catch {
|
|
4058
4283
|
}
|
|
@@ -4345,7 +4570,7 @@ function editInProject(toolName, toolInput, cwd, opts) {
|
|
|
4345
4570
|
}
|
|
4346
4571
|
|
|
4347
4572
|
// src/daemon/gate/question.ts
|
|
4348
|
-
import { closeSync as
|
|
4573
|
+
import { closeSync as closeSync4, openSync as openSync4, readSync as readSync3, statSync as statSync6 } from "fs";
|
|
4349
4574
|
var MAX_QUESTIONS = 4;
|
|
4350
4575
|
var MAX_OPTIONS = 4;
|
|
4351
4576
|
var HEADER_MAX = 12;
|
|
@@ -4455,11 +4680,11 @@ function lastAssistantText(transcriptPath) {
|
|
|
4455
4680
|
const size = statSync6(transcriptPath).size;
|
|
4456
4681
|
const len = Math.min(size, TAIL_BYTES);
|
|
4457
4682
|
const buf = Buffer.alloc(len);
|
|
4458
|
-
const fd =
|
|
4683
|
+
const fd = openSync4(transcriptPath, "r");
|
|
4459
4684
|
try {
|
|
4460
|
-
|
|
4685
|
+
readSync3(fd, buf, 0, len, size - len);
|
|
4461
4686
|
} finally {
|
|
4462
|
-
|
|
4687
|
+
closeSync4(fd);
|
|
4463
4688
|
}
|
|
4464
4689
|
text = buf.toString("utf8");
|
|
4465
4690
|
} catch {
|
|
@@ -4575,14 +4800,14 @@ function pcAnswers(toolInput, rec) {
|
|
|
4575
4800
|
}
|
|
4576
4801
|
|
|
4577
4802
|
// src/daemon/gate/procs.ts
|
|
4578
|
-
import { execFileSync as
|
|
4803
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
4579
4804
|
function listDescendants(rootPid) {
|
|
4580
4805
|
if (process.platform === "win32") {
|
|
4581
4806
|
return descendantsOf(rootPid, (winProcs() ?? []).map((p) => ({ pid: p.pid, ppid: p.ppid, args: p.cmdline })));
|
|
4582
4807
|
}
|
|
4583
4808
|
let out = "";
|
|
4584
4809
|
try {
|
|
4585
|
-
out =
|
|
4810
|
+
out = execFileSync10("ps", ["-A", "-ww", "-o", "pid=,ppid=,args="], {
|
|
4586
4811
|
encoding: "utf8",
|
|
4587
4812
|
stdio: ["ignore", "pipe", "ignore"],
|
|
4588
4813
|
maxBuffer: 16 * 1024 * 1024
|
|
@@ -6241,7 +6466,7 @@ function sleep(ms) {
|
|
|
6241
6466
|
}
|
|
6242
6467
|
|
|
6243
6468
|
// src/daemon/workbuddyBridge.ts
|
|
6244
|
-
import { closeSync as
|
|
6469
|
+
import { closeSync as closeSync6, openSync as openSync6, readSync as readSync5, realpathSync as realpathSync4, statSync as statSync10 } from "fs";
|
|
6245
6470
|
import { dirname as dirname16, sep as sep3 } from "path";
|
|
6246
6471
|
|
|
6247
6472
|
// src/core/wrapWindows.ts
|
|
@@ -6250,9 +6475,9 @@ import { tmpdir as tmpdir3 } from "os";
|
|
|
6250
6475
|
import { join as join27 } from "path";
|
|
6251
6476
|
|
|
6252
6477
|
// src/core/wake.ts
|
|
6253
|
-
import { execFileSync as
|
|
6478
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
6254
6479
|
import { createHash as createHash3, createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
6255
|
-
import { chmodSync as chmodSync6, closeSync as
|
|
6480
|
+
import { chmodSync as chmodSync6, closeSync as closeSync5, mkdirSync as mkdirSync19, openSync as openSync5, readdirSync as readdirSync6, readFileSync as readFileSync20, readSync as readSync4, renameSync as renameSync13, rmSync as rmSync9, statSync as statSync8, writeFileSync as writeFileSync17 } from "fs";
|
|
6256
6481
|
import { tmpdir as tmpdir2 } from "os";
|
|
6257
6482
|
import { join as join26 } from "path";
|
|
6258
6483
|
function wakeDir(home = deskbuddyHome()) {
|
|
@@ -6343,7 +6568,7 @@ function writeWakeSecret(hex, platform = process.platform) {
|
|
|
6343
6568
|
writeFileSync17(tmp, hex, { encoding: "utf8", mode: 384 });
|
|
6344
6569
|
if (platform === "win32") {
|
|
6345
6570
|
try {
|
|
6346
|
-
|
|
6571
|
+
execFileSync11("icacls", [tmp, "/inheritance:r", "/grant:r", `${currentUserSid()}:F`], { stdio: "ignore", windowsHide: true });
|
|
6347
6572
|
} catch {
|
|
6348
6573
|
renameSync13(tmp, file);
|
|
6349
6574
|
return "\u7A97\u53E3\u5524\u9192\u5BC6\u94A5\u6587\u4EF6\u7684\u6743\u9650\u6CA1\u80FD\u6536\u7D27\uFF08icacls \u5931\u8D25\uFF09";
|
|
@@ -6357,7 +6582,7 @@ function writeWakeSecret(hex, platform = process.platform) {
|
|
|
6357
6582
|
function restrictToUser(file, platform = process.platform) {
|
|
6358
6583
|
if (platform !== "win32") return true;
|
|
6359
6584
|
try {
|
|
6360
|
-
|
|
6585
|
+
execFileSync11("icacls", [file, "/inheritance:r", "/grant:r", `${currentUserSid()}:F`], { stdio: "ignore", windowsHide: true });
|
|
6361
6586
|
return true;
|
|
6362
6587
|
} catch {
|
|
6363
6588
|
return false;
|
|
@@ -6365,7 +6590,7 @@ function restrictToUser(file, platform = process.platform) {
|
|
|
6365
6590
|
}
|
|
6366
6591
|
function currentUserSid() {
|
|
6367
6592
|
try {
|
|
6368
|
-
const out =
|
|
6593
|
+
const out = execFileSync11("whoami", ["/user", "/fo", "csv", "/nh"], { encoding: "utf8", windowsHide: true });
|
|
6369
6594
|
const m = /"(S-1-[0-9-]+)"/.exec(out);
|
|
6370
6595
|
if (m) return `*${m[1]}`;
|
|
6371
6596
|
} catch {
|
|
@@ -6403,8 +6628,8 @@ function analyzeTranscript(file) {
|
|
|
6403
6628
|
const size = statSync8(file).size;
|
|
6404
6629
|
const len = Math.min(size, TRANSCRIPT_TAIL2);
|
|
6405
6630
|
const buf = Buffer.alloc(len);
|
|
6406
|
-
fd =
|
|
6407
|
-
|
|
6631
|
+
fd = openSync5(file, "r");
|
|
6632
|
+
readSync4(fd, buf, 0, len, size - len);
|
|
6408
6633
|
const lines2 = buf.toString("utf8").split("\n");
|
|
6409
6634
|
let seg = false;
|
|
6410
6635
|
let reply = null;
|
|
@@ -6445,7 +6670,7 @@ function analyzeTranscript(file) {
|
|
|
6445
6670
|
} finally {
|
|
6446
6671
|
if (fd !== void 0) {
|
|
6447
6672
|
try {
|
|
6448
|
-
|
|
6673
|
+
closeSync5(fd);
|
|
6449
6674
|
} catch {
|
|
6450
6675
|
}
|
|
6451
6676
|
}
|
|
@@ -6820,6 +7045,12 @@ function parseAnswer(m) {
|
|
|
6820
7045
|
const toolName = toolNameOf(a.toolName);
|
|
6821
7046
|
if (!toolName) return null;
|
|
6822
7047
|
if (a.trust !== true && a.prompt !== void 0) return null;
|
|
7048
|
+
if (a.login !== void 0) {
|
|
7049
|
+
if (a.login !== true || a.trust !== void 0 || a.questions !== void 0) return null;
|
|
7050
|
+
const fp = a.fingerprint === void 0 ? void 0 : str3(a.fingerprint, 200);
|
|
7051
|
+
if (fp === null) return null;
|
|
7052
|
+
return { instructionId, sessionId, expireAt, answer: { kind: "probe", toolName, login: true, ...fp ? { fingerprint: fp } : {} } };
|
|
7053
|
+
}
|
|
6823
7054
|
if (a.trust === true) {
|
|
6824
7055
|
if (a.fingerprint !== void 0 || a.questions !== void 0) return null;
|
|
6825
7056
|
return { instructionId, sessionId, expireAt, answer: { kind: "probe", toolName, trust: true, ...hooks } };
|
|
@@ -7141,11 +7372,11 @@ function tail(path) {
|
|
|
7141
7372
|
const size = statSync10(path).size;
|
|
7142
7373
|
const len = Math.min(size, TAIL_BYTES2);
|
|
7143
7374
|
const buf = Buffer.alloc(len);
|
|
7144
|
-
const fd =
|
|
7375
|
+
const fd = openSync6(path, "r");
|
|
7145
7376
|
try {
|
|
7146
|
-
|
|
7377
|
+
readSync5(fd, buf, 0, len, size - len);
|
|
7147
7378
|
} finally {
|
|
7148
|
-
|
|
7379
|
+
closeSync6(fd);
|
|
7149
7380
|
}
|
|
7150
7381
|
return buf.toString("utf8");
|
|
7151
7382
|
} catch {
|
|
@@ -7291,6 +7522,16 @@ var WorkbuddyBridge = class {
|
|
|
7291
7522
|
this.#prune();
|
|
7292
7523
|
if (this.#seen.has(key)) return;
|
|
7293
7524
|
this.#seen.set(key, this.#now());
|
|
7525
|
+
if (await this.#onLoginPage(windowId, rec, call2.callId, toolName, fingerprint2)) {
|
|
7526
|
+
this.#seen.delete(key);
|
|
7527
|
+
this.#d.log(`WorkBuddy\uFF1A\u4F1A\u8BDD ${sessionId.slice(0, 8)} \u7684 ${toolName} \u6388\u6743\u4E0D\u63A8\u8BBE\u5907\u3001\u64A4\u5361\uFF08\u9700\u8981\u767B\u5F55\uFF1A\u7A97\u53E3\u8FD8\u505C\u5728\u767B\u5F55\u9875\uFF09`);
|
|
7528
|
+
try {
|
|
7529
|
+
this.#d.loginBlocked?.(windowId, sessionId);
|
|
7530
|
+
} catch {
|
|
7531
|
+
}
|
|
7532
|
+
return;
|
|
7533
|
+
}
|
|
7534
|
+
if (this.#stopped) return;
|
|
7294
7535
|
const old = this.#cards.get(sessionId);
|
|
7295
7536
|
if (old) {
|
|
7296
7537
|
this.#cards.delete(sessionId);
|
|
@@ -7322,6 +7563,23 @@ var WorkbuddyBridge = class {
|
|
|
7322
7563
|
this.#cards.delete(sessionId);
|
|
7323
7564
|
await this.#answer(card, reply);
|
|
7324
7565
|
}
|
|
7566
|
+
/**
|
|
7567
|
+
* 窗口此刻还在登录页?只问认出过登录页的窗口;🔴 拿不准(通道不通、老包装层不认、超时)一律按不在 —— 宁可照常推卡,不误撤
|
|
7568
|
+
*/
|
|
7569
|
+
async #onLoginPage(windowId, rec, callId, toolName, fingerprint2) {
|
|
7570
|
+
try {
|
|
7571
|
+
if (!this.#d.loginWindow?.(windowId)) return false;
|
|
7572
|
+
const r = await this.#send(windowId, rec, {
|
|
7573
|
+
instructionId: `login-${callId}`.replace(/[^0-9a-zA-Z_-]/g, "").slice(0, 64) || "login",
|
|
7574
|
+
sessionId: "",
|
|
7575
|
+
expireAt: 0,
|
|
7576
|
+
answer: { kind: "probe", toolName, login: true, ...fingerprint2 ? { fingerprint: fingerprint2 } : {} }
|
|
7577
|
+
});
|
|
7578
|
+
return r?.status === "started";
|
|
7579
|
+
} catch {
|
|
7580
|
+
return false;
|
|
7581
|
+
}
|
|
7582
|
+
}
|
|
7325
7583
|
async #answer(card, reply) {
|
|
7326
7584
|
let spec;
|
|
7327
7585
|
if (card.toolName === "AskUserQuestion") {
|
|
@@ -7374,6 +7632,10 @@ var WorkbuddyBridge = class {
|
|
|
7374
7632
|
}
|
|
7375
7633
|
return n;
|
|
7376
7634
|
}
|
|
7635
|
+
/** 这个会话手上有没有在等的卡 */
|
|
7636
|
+
hasCard(sessionId) {
|
|
7637
|
+
return this.#cards.has(sessionId);
|
|
7638
|
+
}
|
|
7377
7639
|
/** 这个窗口里在等的卡所属的会话号(没登录回报时带上) */
|
|
7378
7640
|
sessionOfWindow(windowId) {
|
|
7379
7641
|
for (const card of this.#cards.values()) if (card.windowId === windowId) return card.sessionId;
|
|
@@ -7783,6 +8045,12 @@ var TrustWatcher = class {
|
|
|
7783
8045
|
#notify() {
|
|
7784
8046
|
this.#d.notify({ kind: this.#kind ?? "trust", ...this.#count ? { count: this.#count } : {}, ...this.#repoRoot ? { repoRoot: this.#repoRoot } : {} });
|
|
7785
8047
|
}
|
|
8048
|
+
#mismatchLogged = false;
|
|
8049
|
+
#mismatch(msg) {
|
|
8050
|
+
if (this.#mismatchLogged) return;
|
|
8051
|
+
this.#mismatchLogged = true;
|
|
8052
|
+
this.#d.log(msg);
|
|
8053
|
+
}
|
|
7786
8054
|
tick(lines2) {
|
|
7787
8055
|
const v = lines2 ? inspectTrust(lines2, this.#d.tool) : null;
|
|
7788
8056
|
if (!v || v.kind !== this.#kind) {
|
|
@@ -7791,6 +8059,7 @@ var TrustWatcher = class {
|
|
|
7791
8059
|
this.#contactAt = null;
|
|
7792
8060
|
this.#kind = v ? v.kind : null;
|
|
7793
8061
|
this.#repoRoot = void 0;
|
|
8062
|
+
this.#mismatchLogged = false;
|
|
7794
8063
|
if (!v) return;
|
|
7795
8064
|
}
|
|
7796
8065
|
this.#count = v.count;
|
|
@@ -7815,8 +8084,10 @@ var TrustWatcher = class {
|
|
|
7815
8084
|
}
|
|
7816
8085
|
const scope = this.#scope();
|
|
7817
8086
|
if (scope?.kind === "repo") {
|
|
8087
|
+
const shownRoot = v.widenedRoot ?? "";
|
|
7818
8088
|
if (!repoAnswerable(v, this.#d.tool, this.#forms, scope.root)) {
|
|
7819
|
-
this.#
|
|
8089
|
+
this.#reported = false;
|
|
8090
|
+
this.#mismatch(`\u4FE1\u4EFB\u6846\u4F1A\u843D\u5230 git \u4ED3\u5E93\u6839\uFF0C\u4F46\u6846\u91CC\u7684\u76EE\u5F55\u548C\u6211\u4EEC\u627E\u5230\u7684\u4ED3\u5E93\u6839\u5BF9\u4E0D\u4E0A\uFF0C\u6682\u4E0D\u63A8\u8BBE\u5907\uFF08\u6846\u91CC\u4ED3\u5E93\u6839\u300C${shownRoot}\u300D\uFF0C\u6211\u4EEC\u627E\u5230\u300C${scope.root}\u300D\uFF09`);
|
|
7820
8091
|
return;
|
|
7821
8092
|
}
|
|
7822
8093
|
this.#d.log("\u4FE1\u4EFB\u6846\u4F1A\u843D\u5230 git \u4ED3\u5E93\u6839\uFF0C\u63A8\u8BBE\u5907\u786E\u8BA4\uFF08\u4ED3\u5E93\u6839\u5361\u7247\uFF09");
|
|
@@ -7836,7 +8107,8 @@ var TrustWatcher = class {
|
|
|
7836
8107
|
return;
|
|
7837
8108
|
}
|
|
7838
8109
|
if (!pathShown(v, this.#forms)) {
|
|
7839
|
-
this.#
|
|
8110
|
+
this.#reported = false;
|
|
8111
|
+
this.#mismatch("\u4FE1\u4EFB\u6846\u91CC\u7684\u76EE\u5F55\u548C\u7A97\u53E3\u5DE5\u4F5C\u76EE\u5F55\u5BF9\u4E0D\u4E0A\uFF0C\u6682\u4E0D\u63A8\u8BBE\u5907");
|
|
7840
8112
|
return;
|
|
7841
8113
|
}
|
|
7842
8114
|
this.#d.log("\u4FE1\u4EFB\u6846\u51FA\u73B0\uFF0C\u63A8\u8BBE\u5907\u786E\u8BA4");
|
|
@@ -8114,6 +8386,11 @@ function defaultSend2(windowId, rec, req2) {
|
|
|
8114
8386
|
// src/daemon/loginRequired.ts
|
|
8115
8387
|
var LOGIN_REASON = "agent_not_logged_in";
|
|
8116
8388
|
var LOGIN_CANCEL_REASON = "\u9700\u8981\u767B\u5F55\uFF1AWorkBuddy \u547D\u4EE4\u884C\u6CA1\u767B\u5F55\uFF0C\u8BF7\u5728\u7535\u8111\u7EC8\u7AEF\u91CC\u767B\u5F55\u4E00\u6B21";
|
|
8389
|
+
function loginBlockedState(st, notAfter = Infinity) {
|
|
8390
|
+
if (!st || st.tool !== "workbuddy" || st.state !== "ask_approval") return null;
|
|
8391
|
+
if (st.updatedAt > notAfter) return null;
|
|
8392
|
+
return { ...st, state: "failed", updatedAt: st.updatedAt + 1 };
|
|
8393
|
+
}
|
|
8117
8394
|
function windowReplyAction(launch, status) {
|
|
8118
8395
|
if (status === "ack") return "skip";
|
|
8119
8396
|
if (launch.ended) return "ended";
|
|
@@ -8123,6 +8400,28 @@ function windowReplyAction(launch, status) {
|
|
|
8123
8400
|
}
|
|
8124
8401
|
return status === "not_here" ? "fallback" : "report";
|
|
8125
8402
|
}
|
|
8403
|
+
var LOGIN_BLOCKED_RETRY_MS = [2e3, 6e3];
|
|
8404
|
+
function markLoginBlocked(sid, d) {
|
|
8405
|
+
let baseline;
|
|
8406
|
+
try {
|
|
8407
|
+
baseline = Math.max(d.now(), d.readState(sid)?.updatedAt ?? 0);
|
|
8408
|
+
} catch {
|
|
8409
|
+
baseline = d.now();
|
|
8410
|
+
}
|
|
8411
|
+
const tryOnce = () => {
|
|
8412
|
+
if (d.stopped() || d.hasCard(sid)) return;
|
|
8413
|
+
try {
|
|
8414
|
+
const next = loginBlockedState(d.readState(sid), baseline);
|
|
8415
|
+
if (!next) return;
|
|
8416
|
+
d.writeState(next);
|
|
8417
|
+
d.log(`\u4F1A\u8BDD ${sid.slice(0, 8)} \u6388\u6743\u88AB\u767B\u5F55\u9875\u6321\u4F4F\uFF0C\u72B6\u6001 ask_approval \u6539\u6210 failed\uFF08\u670D\u52A1\u7AEF\u6CA1\u6709\u300C\u9700\u8981\u767B\u5F55\u300D\u72B6\u6001\u4E5F\u6CA1\u6709 reason \u5B57\u6BB5\uFF0Cfailed \u8BA9\u8BBE\u5907\u63D0\u793A\u53BB\u7535\u8111\u4E0A\u770B\uFF09`);
|
|
8418
|
+
d.report();
|
|
8419
|
+
} catch {
|
|
8420
|
+
}
|
|
8421
|
+
};
|
|
8422
|
+
tryOnce();
|
|
8423
|
+
for (const ms of LOGIN_BLOCKED_RETRY_MS) d.later(tryOnce, ms);
|
|
8424
|
+
}
|
|
8126
8425
|
function isLoginNotify(req2) {
|
|
8127
8426
|
return !!req2 && !!req2.hook && req2.hook.hook_event_name === LOGIN_EVENT;
|
|
8128
8427
|
}
|
|
@@ -8132,6 +8431,10 @@ var LoginRequired = class {
|
|
|
8132
8431
|
constructor(d) {
|
|
8133
8432
|
this.#d = d;
|
|
8134
8433
|
}
|
|
8434
|
+
/** 这个窗口认出过登录页(WorkbuddyBridge 据此对新授权卡先问包装层登录页还在不在) */
|
|
8435
|
+
isLoginWindow(windowId) {
|
|
8436
|
+
return this.#done.has(windowId);
|
|
8437
|
+
}
|
|
8135
8438
|
onNotify(req2) {
|
|
8136
8439
|
try {
|
|
8137
8440
|
this.#onNotify(req2);
|
|
@@ -8155,6 +8458,7 @@ var LoginRequired = class {
|
|
|
8155
8458
|
const short = windowId.slice(0, 8);
|
|
8156
8459
|
const sid = this.#d.sessionOfWindow(windowId);
|
|
8157
8460
|
const n = this.#d.cancelCards(windowId, LOGIN_CANCEL_REASON);
|
|
8461
|
+
if (n > 0 && sid) this.#d.markBlocked?.(sid);
|
|
8158
8462
|
this.#d.log(`WorkBuddy \u7A97\u53E3 ${short} \u505C\u5728\u767B\u5F55\u9875\uFF1A\u547D\u4EE4\u884C\u6CA1\u767B\u5F55\uFF08\u684C\u9762 App \u7684\u767B\u5F55\u4E0D\u5171\u4EAB\u7ED9\u547D\u4EE4\u884C\uFF09\uFF0C\u9700\u8981\u5728\u7EC8\u7AEF\u91CC\u767B\u5F55\u4E00\u6B21\uFF1B\u64A4\u5361 ${n} \u5F20`);
|
|
8159
8463
|
const entry = typeof rec.launchId === "string" ? this.#d.launchOf(rec.launchId) : void 0;
|
|
8160
8464
|
if (!entry) {
|
|
@@ -8185,7 +8489,7 @@ function pidAlive4(pid) {
|
|
|
8185
8489
|
}
|
|
8186
8490
|
|
|
8187
8491
|
// src/daemon/dumateWatch.ts
|
|
8188
|
-
import { execFileSync as
|
|
8492
|
+
import { execFileSync as execFileSync12, spawn as spawn3 } from "child_process";
|
|
8189
8493
|
import { readdirSync as readdirSync8, realpathSync as realpathSync6, statSync as statSync12 } from "fs";
|
|
8190
8494
|
import { request as httpRequest } from "http";
|
|
8191
8495
|
import { join as join29 } from "path";
|
|
@@ -8323,11 +8627,11 @@ var keyCache = null;
|
|
|
8323
8627
|
function defaultDiscover() {
|
|
8324
8628
|
if (process.platform === "win32" || typeof process.getuid !== "function") return null;
|
|
8325
8629
|
try {
|
|
8326
|
-
const ps =
|
|
8630
|
+
const ps = execFileSync12("ps", ["-A", "-ww", "-o", "pid=,uid=,args="], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
8327
8631
|
const hit = parseDumateServe(ps, process.getuid());
|
|
8328
8632
|
if (!hit) return null;
|
|
8329
8633
|
if (keyCache && keyCache.pid === hit.pid && keyCache.port === hit.port) return keyCache;
|
|
8330
|
-
const env =
|
|
8634
|
+
const env = execFileSync12("ps", ["eww", "-p", String(hit.pid), "-o", "command="], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
8331
8635
|
const key = parseInappKey(env);
|
|
8332
8636
|
keyCache = key ? { ...hit, key } : null;
|
|
8333
8637
|
return keyCache;
|
|
@@ -8969,7 +9273,7 @@ function runHook(node, input, agentPid) {
|
|
|
8969
9273
|
}
|
|
8970
9274
|
|
|
8971
9275
|
// src/daemon/codexWatch.ts
|
|
8972
|
-
import { execFileSync as
|
|
9276
|
+
import { execFileSync as execFileSync13, spawn as spawn4 } from "child_process";
|
|
8973
9277
|
import WebSocket from "ws";
|
|
8974
9278
|
var TURN_LIMIT_MS = 10 * 60 * 1e3;
|
|
8975
9279
|
var QUEUE_MAX_MS = 10 * 60 * 1e3;
|
|
@@ -9014,7 +9318,7 @@ function discoverAppServers(configured) {
|
|
|
9014
9318
|
return parseAppServerUrls((winProcs() ?? []).map((p) => p.cmdline).join("\n"));
|
|
9015
9319
|
}
|
|
9016
9320
|
try {
|
|
9017
|
-
const out =
|
|
9321
|
+
const out = execFileSync13("ps", ["-A", "-ww", "-o", "args="], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
9018
9322
|
return parseAppServerUrls(out);
|
|
9019
9323
|
} catch {
|
|
9020
9324
|
return [];
|
|
@@ -10079,8 +10383,15 @@ function launchPending(runId) {
|
|
|
10079
10383
|
}
|
|
10080
10384
|
|
|
10081
10385
|
// src/core/interrupt.ts
|
|
10082
|
-
import { closeSync as
|
|
10083
|
-
import { join as join32 } from "path";
|
|
10386
|
+
import { closeSync as closeSync7, openSync as openSync7, readdirSync as readdirSync9, readSync as readSync6, statSync as statSync13 } from "fs";
|
|
10387
|
+
import { dirname as dirname18, join as join32 } from "path";
|
|
10388
|
+
var INTERRUPT_TOOLS = ["claude_code", "qoder_cn", "qoder"];
|
|
10389
|
+
function transcriptHome(tool) {
|
|
10390
|
+
if (tool === "claude_code") return claudeHome();
|
|
10391
|
+
if (tool === "qoder_cn") return dirname18(qoderCnSettingsPath());
|
|
10392
|
+
if (tool === "qoder") return dirname18(qoderSettingsPath());
|
|
10393
|
+
return null;
|
|
10394
|
+
}
|
|
10084
10395
|
var TAIL = 64 * 1024;
|
|
10085
10396
|
var SID_RE2 = /^[A-Za-z0-9_-]{1,64}$/;
|
|
10086
10397
|
function userText2(content) {
|
|
@@ -10094,8 +10405,8 @@ function interruptedAt(file) {
|
|
|
10094
10405
|
const st = statSync13(file);
|
|
10095
10406
|
const len = Math.min(st.size, TAIL);
|
|
10096
10407
|
const buf = Buffer.alloc(len);
|
|
10097
|
-
fd =
|
|
10098
|
-
|
|
10408
|
+
fd = openSync7(file, "r");
|
|
10409
|
+
readSync6(fd, buf, 0, len, st.size - len);
|
|
10099
10410
|
const lines2 = buf.toString("utf8").split("\n");
|
|
10100
10411
|
for (let i = lines2.length - 1; i >= 0; i--) {
|
|
10101
10412
|
const line = lines2[i];
|
|
@@ -10124,7 +10435,7 @@ function interruptedAt(file) {
|
|
|
10124
10435
|
} finally {
|
|
10125
10436
|
if (fd !== void 0) {
|
|
10126
10437
|
try {
|
|
10127
|
-
|
|
10438
|
+
closeSync7(fd);
|
|
10128
10439
|
} catch {
|
|
10129
10440
|
}
|
|
10130
10441
|
}
|
|
@@ -10159,7 +10470,7 @@ function claudeTranscriptFile(sessionId, cwd, home = claudeHome()) {
|
|
|
10159
10470
|
var INTERRUPT_SETTLE_MS = 2e3;
|
|
10160
10471
|
var INTERRUPT_TOLERANCE_MS = 5e3;
|
|
10161
10472
|
function shouldSettleInterrupt(st, markerAt, now) {
|
|
10162
|
-
if (markerAt === null || st.tool
|
|
10473
|
+
if (markerAt === null || !INTERRUPT_TOOLS.includes(st.tool)) return false;
|
|
10163
10474
|
if (st.state !== "watch" && st.state !== "ask_approval") return false;
|
|
10164
10475
|
if (st.interactive !== true && st.embedded !== true) return false;
|
|
10165
10476
|
if (now - markerAt < INTERRUPT_SETTLE_MS) return false;
|
|
@@ -10169,7 +10480,7 @@ function shouldSettleInterrupt(st, markerAt, now) {
|
|
|
10169
10480
|
}
|
|
10170
10481
|
|
|
10171
10482
|
// src/daemon/openWindow.ts
|
|
10172
|
-
import { execFile as
|
|
10483
|
+
import { execFile as execFile5, execFileSync as execFileSync14, spawn as spawn5 } from "child_process";
|
|
10173
10484
|
import { chmodSync as chmodSync9, mkdirSync as mkdirSync23, rmSync as rmSync14, writeFileSync as writeFileSync21 } from "fs";
|
|
10174
10485
|
import { userInfo } from "os";
|
|
10175
10486
|
import { join as join33 } from "path";
|
|
@@ -10220,14 +10531,14 @@ var defaultGuiDeps = () => ({
|
|
|
10220
10531
|
env: process.env,
|
|
10221
10532
|
macManager: () => {
|
|
10222
10533
|
try {
|
|
10223
|
-
return
|
|
10534
|
+
return execFileSync14("launchctl", ["managername"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 3e3 });
|
|
10224
10535
|
} catch {
|
|
10225
10536
|
return "";
|
|
10226
10537
|
}
|
|
10227
10538
|
},
|
|
10228
10539
|
macConsoleUser: () => {
|
|
10229
10540
|
try {
|
|
10230
|
-
return
|
|
10541
|
+
return execFileSync14("stat", ["-f", "%Su", "/dev/console"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 3e3 });
|
|
10231
10542
|
} catch {
|
|
10232
10543
|
return "";
|
|
10233
10544
|
}
|
|
@@ -10241,7 +10552,7 @@ var defaultGuiDeps = () => ({
|
|
|
10241
10552
|
},
|
|
10242
10553
|
winSessionId: () => {
|
|
10243
10554
|
try {
|
|
10244
|
-
const out =
|
|
10555
|
+
const out = execFileSync14("tasklist", ["/FI", `PID eq ${process.pid}`, "/FO", "CSV", "/NH"], {
|
|
10245
10556
|
encoding: "utf8",
|
|
10246
10557
|
stdio: ["ignore", "pipe", "ignore"],
|
|
10247
10558
|
windowsHide: true,
|
|
@@ -10259,7 +10570,7 @@ var defaultOpenerDeps = (findWt2) => ({
|
|
|
10259
10570
|
platform: process.platform,
|
|
10260
10571
|
findWt: findWt2,
|
|
10261
10572
|
run: (file, args) => new Promise((resolve10, reject) => {
|
|
10262
|
-
|
|
10573
|
+
execFile5(file, args, { windowsHide: true, timeout: 15e3 }, (err) => err ? reject(err) : resolve10());
|
|
10263
10574
|
}),
|
|
10264
10575
|
spawnDetached: (file, args) => {
|
|
10265
10576
|
const c = spawn5(file, args, { detached: true, stdio: "ignore", shell: false, windowsHide: false });
|
|
@@ -10964,7 +11275,7 @@ function defaultCodexAppServers() {
|
|
|
10964
11275
|
}
|
|
10965
11276
|
function opencodeReady() {
|
|
10966
11277
|
const mod = ocPluginModule(installedHookPath());
|
|
10967
|
-
return isOcPluginInstalled(opencodePluginPath()) && existsSync20(mod) && existsSync20(join36(
|
|
11278
|
+
return isOcPluginInstalled(opencodePluginPath()) && existsSync20(mod) && existsSync20(join36(dirname19(mod), "ocTranslate.mjs"));
|
|
10968
11279
|
}
|
|
10969
11280
|
var INTERNAL_PROMPT_MARK = "[DeskBuddy]";
|
|
10970
11281
|
function orphanResumeText(summary2) {
|
|
@@ -11135,7 +11446,9 @@ var Daemon = class {
|
|
|
11135
11446
|
this.#workbuddy = new WorkbuddyBridge({
|
|
11136
11447
|
gate: (req2, signal) => this.#coord.onGate(req2, signal),
|
|
11137
11448
|
log,
|
|
11138
|
-
enabled: () => loadConfig().enabledTools.includes("workbuddy")
|
|
11449
|
+
enabled: () => loadConfig().enabledTools.includes("workbuddy"),
|
|
11450
|
+
loginWindow: (windowId) => this.#login.isLoginWindow(windowId),
|
|
11451
|
+
loginBlocked: (_windowId, sid) => this.#markLoginBlocked(sid)
|
|
11139
11452
|
});
|
|
11140
11453
|
this.#trust = new TrustBridge({
|
|
11141
11454
|
gate: (req2, signal) => this.#coord.onGate(req2, signal),
|
|
@@ -11147,7 +11460,8 @@ var Daemon = class {
|
|
|
11147
11460
|
launchOf: (id) => this.#launches.get(id),
|
|
11148
11461
|
report: (cmd, sid, status, reason) => this.#report(cmd, sid, status, "window", reason),
|
|
11149
11462
|
cancelCards: (windowId, reason) => this.#workbuddy.cancelWindow(windowId, reason) + this.#trust.cancelWindow(windowId, reason),
|
|
11150
|
-
sessionOfWindow: (windowId) => this.#workbuddy.sessionOfWindow(windowId)
|
|
11463
|
+
sessionOfWindow: (windowId) => this.#workbuddy.sessionOfWindow(windowId),
|
|
11464
|
+
markBlocked: (sid) => this.#markLoginBlocked(sid)
|
|
11151
11465
|
});
|
|
11152
11466
|
this.#qwenwork = new QwenWorkBridge({
|
|
11153
11467
|
gate: (req2, signal) => this.#coord.onGate(req2, signal),
|
|
@@ -11278,6 +11592,22 @@ var Daemon = class {
|
|
|
11278
11592
|
}, CODEX_IDLE_SETTLE_MS);
|
|
11279
11593
|
t.unref?.();
|
|
11280
11594
|
}
|
|
11595
|
+
/**
|
|
11596
|
+
* WorkBuddy 窗口停在登录页、授权卡没推 / 被撤(loginRequired.ts loginBlockedState):会话状态从 ask_approval 改成 failed 并上报。
|
|
11597
|
+
* 推卡的通知与写 ask_approval 的 hook 是同一事件的两次独立调用,谁先到不一定:现在改一次,隔几秒再各看一次
|
|
11598
|
+
*/
|
|
11599
|
+
#markLoginBlocked(sid) {
|
|
11600
|
+
markLoginBlocked(sid, {
|
|
11601
|
+
now: this.#deps.now,
|
|
11602
|
+
readState,
|
|
11603
|
+
writeState,
|
|
11604
|
+
hasCard: (id) => this.#workbuddy.hasCard(id),
|
|
11605
|
+
stopped: () => this.#stopped,
|
|
11606
|
+
log,
|
|
11607
|
+
report: () => void this.#reportAlive().catch(() => void 0),
|
|
11608
|
+
later: (fn, ms) => void setTimeout(fn, ms).unref?.()
|
|
11609
|
+
});
|
|
11610
|
+
}
|
|
11281
11611
|
/** 状态还停在忙且 since 之后没有新事件 → 改成 done。改了返回 true */
|
|
11282
11612
|
#settleBusy(sessionId, tool, since, why) {
|
|
11283
11613
|
try {
|
|
@@ -11291,34 +11621,39 @@ var Daemon = class {
|
|
|
11291
11621
|
}
|
|
11292
11622
|
}
|
|
11293
11623
|
/**
|
|
11294
|
-
* 扫一遍 Claude Code 窗口里还在忙的会话:transcript 显示已被打断就补落 done(Bug 表 160);
|
|
11624
|
+
* 扫一遍 Claude Code / Qoder 窗口里还在忙的会话:transcript 显示已被打断就补落 done(Bug 表 160 / 204);
|
|
11295
11625
|
* 会话开在 DeskBuddy 窗口里的,同时把窗口的 .last 补成 Stop(包装层据此判空闲,否则设备回复一直排队到过期)
|
|
11296
11626
|
*/
|
|
11297
11627
|
#settleInterrupted() {
|
|
11298
|
-
if (this.#stopped
|
|
11628
|
+
if (this.#stopped) return;
|
|
11629
|
+
const enabled = loadConfig().enabledTools;
|
|
11630
|
+
const tools = INTERRUPT_TOOLS.filter((t) => enabled.includes(t));
|
|
11631
|
+
if (tools.length === 0) return;
|
|
11299
11632
|
const now = this.#deps.now();
|
|
11300
|
-
let changed = false;
|
|
11301
11633
|
const alive = /* @__PURE__ */ new Set();
|
|
11302
|
-
for (const
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
this.#
|
|
11634
|
+
for (const tool of tools) {
|
|
11635
|
+
let changed = false;
|
|
11636
|
+
for (const st of this.#aliveStates(tool)) {
|
|
11637
|
+
alive.add(st.sessionId);
|
|
11638
|
+
if (!BUSY_STATES2.has(st.state) || st.interactive !== true && st.embedded !== true) {
|
|
11639
|
+
this.#transcriptSeen.delete(st.sessionId);
|
|
11640
|
+
continue;
|
|
11641
|
+
}
|
|
11642
|
+
const file = this.#transcriptOf(st, now);
|
|
11643
|
+
if (!file) continue;
|
|
11644
|
+
const sig = transcriptSig(file);
|
|
11645
|
+
if (sig && this.#transcriptSeen.get(st.sessionId) === sig) continue;
|
|
11646
|
+
const at = interruptedAt(file);
|
|
11647
|
+
if (sig && (at === null || now - at >= INTERRUPT_SETTLE_MS)) this.#transcriptSeen.set(st.sessionId, sig);
|
|
11648
|
+
if (at === null || !shouldSettleInterrupt(st, at, now)) continue;
|
|
11649
|
+
if (this.#settleBusy(st.sessionId, tool, st.updatedAt, `\u5728\u7A97\u53E3\u91CC\u88AB\u6253\u65AD\uFF08${tool} \u4E0D\u53D1 Stop\uFF09`)) {
|
|
11650
|
+
changed = true;
|
|
11651
|
+
this.#idleWindowAfterInterrupt(st.sessionId, at);
|
|
11652
|
+
}
|
|
11318
11653
|
}
|
|
11654
|
+
if (changed) void this.#reportTool(tool);
|
|
11319
11655
|
}
|
|
11320
11656
|
for (const m of [this.#transcripts, this.#transcriptMiss, this.#transcriptSeen]) for (const k of [...m.keys()]) if (!alive.has(k)) m.delete(k);
|
|
11321
|
-
if (changed) void this.#reportTool("claude_code");
|
|
11322
11657
|
}
|
|
11323
11658
|
/** 会话的 transcript:hook 记在状态里的优先;没有再按目录找,找不到的一段时间内不再找 */
|
|
11324
11659
|
#transcriptOf(st, now) {
|
|
@@ -11327,7 +11662,8 @@ var Daemon = class {
|
|
|
11327
11662
|
if (cached && existsSync20(cached)) return cached;
|
|
11328
11663
|
const missAt = this.#transcriptMiss.get(st.sessionId);
|
|
11329
11664
|
if (missAt !== void 0 && now - missAt < TRANSCRIPT_MISS_TTL_MS) return null;
|
|
11330
|
-
const
|
|
11665
|
+
const home = transcriptHome(st.tool);
|
|
11666
|
+
const f = home ? claudeTranscriptFile(st.sessionId, st.cwd, home) : null;
|
|
11331
11667
|
if (f) {
|
|
11332
11668
|
this.#transcripts.set(st.sessionId, f);
|
|
11333
11669
|
this.#transcriptMiss.delete(st.sessionId);
|
|
@@ -12396,6 +12732,7 @@ async function runDaemonCommand(opts = {}) {
|
|
|
12396
12732
|
`);
|
|
12397
12733
|
return 1;
|
|
12398
12734
|
}
|
|
12735
|
+
void warmClaudeProbe();
|
|
12399
12736
|
const d = daemon;
|
|
12400
12737
|
const updater = opts.foreground ? null : new AutoUpdater({
|
|
12401
12738
|
currentVersion: packageVersion(),
|
|
@@ -13340,7 +13677,7 @@ var Injector = class {
|
|
|
13340
13677
|
import { spawn as spawn6 } from "child_process";
|
|
13341
13678
|
import { realpathSync as realpathSync7 } from "fs";
|
|
13342
13679
|
import { constants as constants2 } from "os";
|
|
13343
|
-
import { dirname as
|
|
13680
|
+
import { dirname as dirname20, resolve as resolve9 } from "path";
|
|
13344
13681
|
var CLAUDE_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
13345
13682
|
"agents",
|
|
13346
13683
|
"attach",
|
|
@@ -13404,7 +13741,7 @@ function real(p) {
|
|
|
13404
13741
|
function findReal(tool, d = defaultLocateDeps(), skip = shimDir()) {
|
|
13405
13742
|
const win = (d.platform ?? process.platform) === "win32";
|
|
13406
13743
|
const skipKeys = /* @__PURE__ */ new Set([norm2(skip, win), norm2(real(skip), win)]);
|
|
13407
|
-
const inSkip = (file) => skipKeys.has(norm2(
|
|
13744
|
+
const inSkip = (file) => skipKeys.has(norm2(dirname20(file), win)) || skipKeys.has(norm2(dirname20(real(file)), win));
|
|
13408
13745
|
const dd = { ...d, skipDirs: [...d.skipDirs ?? [], skip] };
|
|
13409
13746
|
const name = SHIM_COMMANDS[tool];
|
|
13410
13747
|
if (!name) {
|
|
@@ -13412,7 +13749,11 @@ function findReal(tool, d = defaultLocateDeps(), skip = shimDir()) {
|
|
|
13412
13749
|
return loc && !inSkip(loc.prefixArgs[0] && /\.(?:c|m)?js$/i.test(loc.prefixArgs[0]) ? loc.prefixArgs[0] : loc.file) ? loc : null;
|
|
13413
13750
|
}
|
|
13414
13751
|
if (win && tool === "claude_code") {
|
|
13415
|
-
|
|
13752
|
+
return pickWinClaude(dd, (src, loc) => {
|
|
13753
|
+
if (inSkip(src)) return false;
|
|
13754
|
+
const target = loc.prefixArgs[0] && /\.(?:c|m)?js$/i.test(loc.prefixArgs[0]) ? loc.prefixArgs[0] : loc.file;
|
|
13755
|
+
return !inSkip(target);
|
|
13756
|
+
});
|
|
13416
13757
|
}
|
|
13417
13758
|
for (const f of whichAllIn(name, dd)) {
|
|
13418
13759
|
if (inSkip(f)) continue;
|
|
@@ -13427,7 +13768,7 @@ function findReal(tool, d = defaultLocateDeps(), skip = shimDir()) {
|
|
|
13427
13768
|
function withoutShimFlag(env) {
|
|
13428
13769
|
const out = { ...env };
|
|
13429
13770
|
delete out.DESKBUDDY_SHIM;
|
|
13430
|
-
return out;
|
|
13771
|
+
return scrubAgentEnv(out);
|
|
13431
13772
|
}
|
|
13432
13773
|
function runDirect(real2, args, env = process.env) {
|
|
13433
13774
|
return new Promise((done) => {
|
|
@@ -13484,34 +13825,10 @@ function runDirect(real2, args, env = process.env) {
|
|
|
13484
13825
|
|
|
13485
13826
|
// src/wrap/codexLaunch.ts
|
|
13486
13827
|
import { execFileSync as execFileSync15, spawn as spawn7 } from "child_process";
|
|
13487
|
-
import { closeSync as
|
|
13828
|
+
import { closeSync as closeSync8, mkdirSync as mkdirSync27, openSync as openSync8, readdirSync as readdirSync11, readFileSync as readFileSync26, rmSync as rmSync21, statSync as statSync16, truncateSync as truncateSync2, writeFileSync as writeFileSync26, renameSync as renameSync19 } from "fs";
|
|
13488
13829
|
import { request as request2 } from "http";
|
|
13489
13830
|
import { createServer as createServer4 } from "net";
|
|
13490
13831
|
import { join as join38 } from "path";
|
|
13491
|
-
|
|
13492
|
-
// src/core/codexServer.ts
|
|
13493
|
-
import { execFileSync as execFileSync14 } from "child_process";
|
|
13494
|
-
function isAppServerArgs(args, url) {
|
|
13495
|
-
return /\bapp-server\b/.test(args) && new RegExp(`--listen[= ]${url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|$)`).test(args);
|
|
13496
|
-
}
|
|
13497
|
-
function processArgs(pid, platform = process.platform, procs, fresh = false) {
|
|
13498
|
-
if (platform === "win32") {
|
|
13499
|
-
const list = procs ? procs() : winProcs(Date.now(), { force: fresh });
|
|
13500
|
-
const p = (list ?? []).find((x) => x.pid === pid);
|
|
13501
|
-
return p?.args || null;
|
|
13502
|
-
}
|
|
13503
|
-
try {
|
|
13504
|
-
const out = execFileSync14("ps", ["-ww", "-o", "args=", "-p", String(pid)], {
|
|
13505
|
-
encoding: "utf8",
|
|
13506
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
13507
|
-
});
|
|
13508
|
-
return out.trim() || null;
|
|
13509
|
-
} catch {
|
|
13510
|
-
return null;
|
|
13511
|
-
}
|
|
13512
|
-
}
|
|
13513
|
-
|
|
13514
|
-
// src/wrap/codexLaunch.ts
|
|
13515
13832
|
var TAKEOVER_VALUE_OPTS = /* @__PURE__ */ new Set(["-c", "--config", "-m", "--model", "-s", "--sandbox", "-a", "--ask-for-approval", "-C", "--cd", "--enable", "--disable"]);
|
|
13516
13833
|
var SHORT_VALUE_OPTS = /* @__PURE__ */ new Set(["-c", "-m", "-s", "-a", "-C"]);
|
|
13517
13834
|
var TAKEOVER_FLAGS = /* @__PURE__ */ new Set(["--search", "--no-alt-screen", "--dangerously-bypass-approvals-and-sandbox"]);
|
|
@@ -13738,7 +14055,7 @@ function startServer(real2, url, env, cwd, platform = process.platform) {
|
|
|
13738
14055
|
if (statSync16(logPath2).size > MAX_LOG_BYTES2) truncateSync2(logPath2, 0);
|
|
13739
14056
|
} catch {
|
|
13740
14057
|
}
|
|
13741
|
-
const fd =
|
|
14058
|
+
const fd = openSync8(logPath2, "a");
|
|
13742
14059
|
try {
|
|
13743
14060
|
const childEnv2 = { ...env, DESKBUDDY_WRAPPED: "1" };
|
|
13744
14061
|
delete childEnv2.DESKBUDDY_SHIM;
|
|
@@ -13748,7 +14065,7 @@ function startServer(real2, url, env, cwd, platform = process.platform) {
|
|
|
13748
14065
|
child.unref();
|
|
13749
14066
|
return child.pid ?? null;
|
|
13750
14067
|
} finally {
|
|
13751
|
-
|
|
14068
|
+
closeSync8(fd);
|
|
13752
14069
|
}
|
|
13753
14070
|
}
|
|
13754
14071
|
function killServer(pid, platform = process.platform, run2 = taskkill) {
|
|
@@ -14004,8 +14321,10 @@ function isLoginPage(lines2) {
|
|
|
14004
14321
|
if (title < 0) return false;
|
|
14005
14322
|
const below = lines2.slice(title + 1);
|
|
14006
14323
|
const options = below.filter((l) => OPTION.test(l)).length;
|
|
14007
|
-
|
|
14324
|
+
const tail2 = lines2.filter((l) => l.trim() !== "").slice(-HINT_TAIL_LINES);
|
|
14325
|
+
return options >= 2 && tail2.some((l) => HINT.test(l));
|
|
14008
14326
|
}
|
|
14327
|
+
var HINT_TAIL_LINES = 3;
|
|
14009
14328
|
var LOGIN_STABLE_MS = 500;
|
|
14010
14329
|
var LoginWatcher = class {
|
|
14011
14330
|
constructor(d) {
|
|
@@ -14046,6 +14365,11 @@ async function applyAnswer(req2, io, trust) {
|
|
|
14046
14365
|
if (req2.expireAt > 0 && Date.now() > req2.expireAt) return { instructionId: id, status: "rejected", reason: "expired" };
|
|
14047
14366
|
const a = req2.answer;
|
|
14048
14367
|
if ((a.kind === "trust" || a.kind === "probe" && a.trust) && !trust) return { instructionId: id, status: "rejected", reason: "unsupported" };
|
|
14368
|
+
if (a.kind === "probe" && a.login) {
|
|
14369
|
+
if (!io.loginOnScreen) return { instructionId: id, status: "rejected", reason: "unsupported" };
|
|
14370
|
+
const onLogin = io.loginOnScreen() && !(a.fingerprint && isPermissionDialog(io.screen() ?? [], a.toolName, a.fingerprint));
|
|
14371
|
+
return onLogin ? { instructionId: id, status: "started", mode: "window" } : { instructionId: id, status: "rejected", reason: "not_waiting" };
|
|
14372
|
+
}
|
|
14049
14373
|
if (!io.screen()) return { instructionId: id, status: "rejected", reason: "no_screen" };
|
|
14050
14374
|
if (a.kind === "probe") {
|
|
14051
14375
|
const waiting = a.trust ? trustWaiting(io.screen() ?? [], trust?.tool ?? "", a.prompt ?? "trust") : stillWaiting(io.screen() ?? [], a);
|
|
@@ -14210,7 +14534,8 @@ async function runAnswerWindow(tool, real2, args, env, windowId, d) {
|
|
|
14210
14534
|
}
|
|
14211
14535
|
},
|
|
14212
14536
|
write: toPty,
|
|
14213
|
-
sleep: (ms) => new Promise((res) => setTimeout(res, ms))
|
|
14537
|
+
sleep: (ms) => new Promise((res) => setTimeout(res, ms)),
|
|
14538
|
+
...login ? { loginOnScreen: () => login.onScreen } : {}
|
|
14214
14539
|
}, trustCtx);
|
|
14215
14540
|
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 : ""}`);
|
|
14216
14541
|
reply(r);
|
|
@@ -14273,7 +14598,7 @@ function childEnv(src, windowId) {
|
|
|
14273
14598
|
}
|
|
14274
14599
|
env.DESKBUDDY_WINDOW = windowId;
|
|
14275
14600
|
env.DESKBUDDY_WRAPPED = "1";
|
|
14276
|
-
return env;
|
|
14601
|
+
return scrubAgentEnv(env);
|
|
14277
14602
|
}
|
|
14278
14603
|
function wrapLog(line) {
|
|
14279
14604
|
try {
|
|
@@ -14771,6 +15096,7 @@ async function main(argv) {
|
|
|
14771
15096
|
} catch {
|
|
14772
15097
|
}
|
|
14773
15098
|
}
|
|
15099
|
+
scrubForCommand(cmd);
|
|
14774
15100
|
switch (cmd) {
|
|
14775
15101
|
case void 0:
|
|
14776
15102
|
case "-h":
|