deskbuddy 0.2.0-beta.19 → 0.2.0-beta.20
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 +1910 -506
- package/dist/gate.cjs +21 -4
- package/dist/hook.cjs +18 -1
- package/hook/gate.cjs +21 -4
- package/hook/hook-debug.log +10 -0
- package/hook/hook.cjs +18 -1
- 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 writeFileSync27, rmSync as rmSync20 } from "fs";
|
|
5
5
|
|
|
6
6
|
// src/core/api.ts
|
|
7
7
|
import { execFileSync } from "child_process";
|
|
@@ -13,7 +13,7 @@ import { release } from "os";
|
|
|
13
13
|
import { randomUUID } from "crypto";
|
|
14
14
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
15
15
|
import { homedir, hostname } from "os";
|
|
16
|
-
import { join } from "path";
|
|
16
|
+
import { dirname, join } from "path";
|
|
17
17
|
function defaultRemoteCwd(home = homedir(), platform = process.platform, exists = existsSync) {
|
|
18
18
|
const desktop = join(home, "Desktop");
|
|
19
19
|
if (platform === "win32" && !exists(desktop)) {
|
|
@@ -58,6 +58,9 @@ function claudeSettingsPath() {
|
|
|
58
58
|
function workbuddySettingsPath() {
|
|
59
59
|
return join(process.env.CODEBUDDY_CONFIG_DIR || join(homedir(), ".workbuddy"), "settings.json");
|
|
60
60
|
}
|
|
61
|
+
function workbuddyDbPath() {
|
|
62
|
+
return join(dirname(workbuddySettingsPath()), "workbuddy.db");
|
|
63
|
+
}
|
|
61
64
|
function qoderCnSettingsPath() {
|
|
62
65
|
return join(process.env.DESKBUDDY_QODER_CN_DIR || join(homedir(), ".qoder-cn"), "settings.json");
|
|
63
66
|
}
|
|
@@ -89,6 +92,14 @@ function codexHome() {
|
|
|
89
92
|
function installedHookPath() {
|
|
90
93
|
return join(deskbuddyHome(), "hook.cjs");
|
|
91
94
|
}
|
|
95
|
+
function parseWorkbuddySetup(v) {
|
|
96
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return void 0;
|
|
97
|
+
const o = v;
|
|
98
|
+
const out = {};
|
|
99
|
+
if (o.declined === true) out.declined = true;
|
|
100
|
+
if (typeof o.askedAt === "number" && Number.isFinite(o.askedAt)) out.askedAt = o.askedAt;
|
|
101
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
102
|
+
}
|
|
92
103
|
function loadConfig() {
|
|
93
104
|
const fallback = {
|
|
94
105
|
clientId: randomUUID(),
|
|
@@ -115,7 +126,8 @@ function loadConfig() {
|
|
|
115
126
|
approvalPolicy: parsed.approvalPolicy === "all" ? "all" : parsed.approvalPolicy === "commands" ? "commands" : void 0,
|
|
116
127
|
codexAppServer: typeof parsed.codexAppServer === "string" ? parsed.codexAppServer : void 0,
|
|
117
128
|
autoUpdate: typeof parsed.autoUpdate === "boolean" ? parsed.autoUpdate : void 0,
|
|
118
|
-
disabledTools: Array.isArray(parsed.disabledTools) ? parsed.disabledTools.filter((t) => typeof t === "string") : void 0
|
|
129
|
+
disabledTools: Array.isArray(parsed.disabledTools) ? parsed.disabledTools.filter((t) => typeof t === "string") : void 0,
|
|
130
|
+
workbuddySetup: parseWorkbuddySetup(parsed.workbuddySetup)
|
|
119
131
|
};
|
|
120
132
|
} catch {
|
|
121
133
|
return fallback;
|
|
@@ -139,7 +151,7 @@ function ensureClientId() {
|
|
|
139
151
|
// src/core/hookFile.ts
|
|
140
152
|
import { copyFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
141
153
|
import { createRequire } from "module";
|
|
142
|
-
import { dirname, join as join2 } from "path";
|
|
154
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
143
155
|
import { fileURLToPath } from "url";
|
|
144
156
|
|
|
145
157
|
// src/core/semver.ts
|
|
@@ -185,7 +197,7 @@ function semverLt(a, b) {
|
|
|
185
197
|
}
|
|
186
198
|
|
|
187
199
|
// src/core/hookFile.ts
|
|
188
|
-
var here =
|
|
200
|
+
var here = dirname2(fileURLToPath(import.meta.url));
|
|
189
201
|
function packagedHookPath() {
|
|
190
202
|
const candidates = [
|
|
191
203
|
join2(here, "hook.cjs"),
|
|
@@ -212,7 +224,7 @@ function packageVersion() {
|
|
|
212
224
|
return "0.0.0-dev";
|
|
213
225
|
}
|
|
214
226
|
function hookVersionPath(target = installedHookPath()) {
|
|
215
|
-
return join2(
|
|
227
|
+
return join2(dirname2(target), "hook-version");
|
|
216
228
|
}
|
|
217
229
|
function installedHookVersion(target = installedHookPath()) {
|
|
218
230
|
try {
|
|
@@ -235,20 +247,20 @@ function syncHookFile(opts = {}) {
|
|
|
235
247
|
}
|
|
236
248
|
const SIBLINGS = ["gate.cjs", "ocTranslate.mjs", "opencode.mjs", "deskbuddy-dsh.mjs", "wake.cjs", "launcher.cjs"];
|
|
237
249
|
const siblingMissing = () => SIBLINGS.some((name) => {
|
|
238
|
-
if (existsSync2(join2(
|
|
250
|
+
if (existsSync2(join2(dirname2(target), name))) return false;
|
|
239
251
|
try {
|
|
240
|
-
return existsSync2(join2(
|
|
252
|
+
return existsSync2(join2(dirname2(opts.source ?? packagedHookPath()), name));
|
|
241
253
|
} catch {
|
|
242
254
|
return false;
|
|
243
255
|
}
|
|
244
256
|
});
|
|
245
257
|
if (installedHookVersion(target) === version && existsSync2(target) && !siblingMissing()) return false;
|
|
246
258
|
const source = opts.source ?? packagedHookPath();
|
|
247
|
-
mkdirSync2(
|
|
259
|
+
mkdirSync2(dirname2(target), { recursive: true });
|
|
248
260
|
atomicCopy(source, target);
|
|
249
261
|
for (const name of SIBLINGS) {
|
|
250
|
-
const from = join2(
|
|
251
|
-
if (existsSync2(from)) atomicCopy(from, join2(
|
|
262
|
+
const from = join2(dirname2(source), name);
|
|
263
|
+
if (existsSync2(from)) atomicCopy(from, join2(dirname2(target), name));
|
|
252
264
|
}
|
|
253
265
|
writeFileSync2(hookVersionPath(target), version, "utf8");
|
|
254
266
|
return true;
|
|
@@ -416,7 +428,7 @@ var api = {
|
|
|
416
428
|
// src/core/versions.ts
|
|
417
429
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
418
430
|
import { cpSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync4, renameSync as renameSync3, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
419
|
-
import { basename, dirname as
|
|
431
|
+
import { basename, dirname as dirname3, join as join3, relative, resolve, sep } from "path";
|
|
420
432
|
function versionsDir(home = deskbuddyHome()) {
|
|
421
433
|
return join3(home, "versions");
|
|
422
434
|
}
|
|
@@ -440,7 +452,7 @@ function versionCliPath(version, home) {
|
|
|
440
452
|
return join3(versionDir(version, home), "node_modules", "deskbuddy", "dist", "cli.js");
|
|
441
453
|
}
|
|
442
454
|
function atomicWriteJson(path, value) {
|
|
443
|
-
mkdirSync3(
|
|
455
|
+
mkdirSync3(dirname3(path), { recursive: true });
|
|
444
456
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
445
457
|
writeFileSync3(tmp, JSON.stringify(value, null, 2), "utf8");
|
|
446
458
|
renameSync3(tmp, path);
|
|
@@ -560,7 +572,7 @@ function findDepDir(name, fromDir) {
|
|
|
560
572
|
for (; ; ) {
|
|
561
573
|
const c = join3(d, "node_modules", name);
|
|
562
574
|
if (existsSync3(join3(c, "package.json"))) return c;
|
|
563
|
-
const up =
|
|
575
|
+
const up = dirname3(d);
|
|
564
576
|
if (up === d) return null;
|
|
565
577
|
d = up;
|
|
566
578
|
}
|
|
@@ -578,7 +590,7 @@ function copyRunningCli(pkgRoot, version, home) {
|
|
|
578
590
|
recursive: true,
|
|
579
591
|
dereference: true,
|
|
580
592
|
// 包自己的嵌套 node_modules 不整个搬:依赖由下面的递归平铺
|
|
581
|
-
filter: (src) => !(src !== from && basename(src) === "node_modules" &&
|
|
593
|
+
filter: (src) => !(src !== from && basename(src) === "node_modules" && dirname3(src) === from)
|
|
582
594
|
});
|
|
583
595
|
};
|
|
584
596
|
copyPkg("deskbuddy", pkgRoot);
|
|
@@ -617,8 +629,8 @@ function registerRunningCli(running, deps2 = {}) {
|
|
|
617
629
|
let how = "already";
|
|
618
630
|
let note;
|
|
619
631
|
if (versionDirOf(cliPath, home) !== running.version) {
|
|
620
|
-
const distDir =
|
|
621
|
-
const pkgRoot =
|
|
632
|
+
const distDir = dirname3(cliPath);
|
|
633
|
+
const pkgRoot = dirname3(distDir);
|
|
622
634
|
const pkg = readPkg(pkgRoot);
|
|
623
635
|
const copyable = basename(distDir) === "dist" && pkg?.name === "deskbuddy" && pkg.version === running.version && !existsSync3(join3(pkgRoot, "src"));
|
|
624
636
|
how = "inPlace";
|
|
@@ -664,7 +676,7 @@ import { execFile, spawn } from "child_process";
|
|
|
664
676
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
665
677
|
import { homedir as homedir2 } from "os";
|
|
666
678
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
667
|
-
import { basename as basename2, dirname as
|
|
679
|
+
import { basename as basename2, dirname as dirname4, join as join4, resolve as resolve2 } from "path";
|
|
668
680
|
import { promisify } from "util";
|
|
669
681
|
|
|
670
682
|
// src/core/winproc.ts
|
|
@@ -891,7 +903,7 @@ function systemdUnitPath(home = homedir2()) {
|
|
|
891
903
|
return join4(home, ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
892
904
|
}
|
|
893
905
|
function write(path, content) {
|
|
894
|
-
mkdirSync4(
|
|
906
|
+
mkdirSync4(dirname4(path), { recursive: true });
|
|
895
907
|
writeFileSync4(path, content, "utf8");
|
|
896
908
|
}
|
|
897
909
|
var BOOTSTRAP_RETRIES = 10;
|
|
@@ -963,7 +975,7 @@ async function installAutostart(t, platform = process.platform, opts = {}) {
|
|
|
963
975
|
}
|
|
964
976
|
if (platform === "win32") {
|
|
965
977
|
const p = winStartupVbsPath();
|
|
966
|
-
mkdirSync4(
|
|
978
|
+
mkdirSync4(dirname4(p), { recursive: true });
|
|
967
979
|
writeFileSync4(p, "\uFEFF" + winStartupVbs(t), "utf16le");
|
|
968
980
|
await run("schtasks", ["/delete", "/f", "/tn", SCHTASK_NAME], { windowsHide: true }).catch(() => {
|
|
969
981
|
});
|
|
@@ -1083,24 +1095,24 @@ function autostartTarget(out = () => {
|
|
|
1083
1095
|
// src/daemon/autoUpdate.ts
|
|
1084
1096
|
import { execFile as execFile2, execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
1085
1097
|
import { closeSync, existsSync as existsSync6, mkdirSync as mkdirSync5, openSync, rmSync as rmSync3 } from "fs";
|
|
1086
|
-
import { dirname as
|
|
1098
|
+
import { dirname as dirname6, join as join6, posix, win32 } from "path";
|
|
1087
1099
|
|
|
1088
1100
|
// src/wrap/pty.ts
|
|
1089
1101
|
import { chmodSync, existsSync as existsSync5, statSync } from "fs";
|
|
1090
1102
|
import { createRequire as createRequire2 } from "module";
|
|
1091
|
-
import { dirname as
|
|
1103
|
+
import { dirname as dirname5, join as join5 } from "path";
|
|
1092
1104
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1093
1105
|
var req = createRequire2(import.meta.url);
|
|
1094
1106
|
var VENDORED_PTY_PLATFORMS = ["darwin-arm64", "darwin-x64", "win32-arm64", "win32-x64"];
|
|
1095
1107
|
function hasVendoredPty(platform = process.platform, arch = process.arch) {
|
|
1096
1108
|
return VENDORED_PTY_PLATFORMS.includes(`${platform}-${arch}`);
|
|
1097
1109
|
}
|
|
1098
|
-
function vendoredPtyDir(from =
|
|
1110
|
+
function vendoredPtyDir(from = dirname5(fileURLToPath3(import.meta.url))) {
|
|
1099
1111
|
let dir = from;
|
|
1100
1112
|
for (let i = 0; i < 4; i++) {
|
|
1101
1113
|
const cand = join5(dir, "vendor", "node-pty");
|
|
1102
1114
|
if (existsSync5(join5(cand, "package.json"))) return cand;
|
|
1103
|
-
const up =
|
|
1115
|
+
const up = dirname5(dir);
|
|
1104
1116
|
if (up === dir) break;
|
|
1105
1117
|
dir = up;
|
|
1106
1118
|
}
|
|
@@ -1130,7 +1142,7 @@ function loadPty() {
|
|
|
1130
1142
|
}
|
|
1131
1143
|
}
|
|
1132
1144
|
try {
|
|
1133
|
-
const dir =
|
|
1145
|
+
const dir = dirname5(req.resolve("node-pty/package.json"));
|
|
1134
1146
|
fixSpawnHelper(dir);
|
|
1135
1147
|
return asPty(req(dir));
|
|
1136
1148
|
} catch {
|
|
@@ -1365,7 +1377,7 @@ function switchFromEnv(env = process.env) {
|
|
|
1365
1377
|
function spawnSwitch(cliPath, logFile, from, onExit, execPath = process.execPath) {
|
|
1366
1378
|
let fd = "ignore";
|
|
1367
1379
|
try {
|
|
1368
|
-
mkdirSync5(
|
|
1380
|
+
mkdirSync5(dirname6(logFile), { recursive: true });
|
|
1369
1381
|
fd = openSync(logFile, "a");
|
|
1370
1382
|
} catch {
|
|
1371
1383
|
fd = "ignore";
|
|
@@ -1504,13 +1516,13 @@ import { readdirSync as readdirSync4 } from "fs";
|
|
|
1504
1516
|
|
|
1505
1517
|
// src/core/detect.ts
|
|
1506
1518
|
import { existsSync as existsSync8 } from "fs";
|
|
1507
|
-
import { dirname as
|
|
1519
|
+
import { dirname as dirname8 } from "path";
|
|
1508
1520
|
|
|
1509
1521
|
// src/daemon/locate.ts
|
|
1510
1522
|
import { execFile as execFile3, execFileSync as execFileSync5 } from "child_process";
|
|
1511
1523
|
import { accessSync, closeSync as closeSync2, constants, existsSync as existsSync7, lstatSync, openSync as openSync2, readFileSync as readFileSync6, readSync, realpathSync, statSync as statSync2 } from "fs";
|
|
1512
1524
|
import { homedir as homedir3 } from "os";
|
|
1513
|
-
import { delimiter, dirname as
|
|
1525
|
+
import { delimiter, dirname as dirname7, join as join7, win32 as win322 } from "path";
|
|
1514
1526
|
|
|
1515
1527
|
// src/core/agentEnv.ts
|
|
1516
1528
|
var BUN_HIJACK_ENV = ["BUN_BE_BUN", "BUN_OPTIONS"];
|
|
@@ -1579,7 +1591,8 @@ var defaultLocateDeps = () => ({
|
|
|
1579
1591
|
if (fd !== null) closeSync2(fd);
|
|
1580
1592
|
}
|
|
1581
1593
|
},
|
|
1582
|
-
versionOf: (loc) => probeVersion(loc)
|
|
1594
|
+
versionOf: (loc) => probeVersion(loc),
|
|
1595
|
+
realpath: (p) => realpathSync(p)
|
|
1583
1596
|
});
|
|
1584
1597
|
var PROBE_TIMEOUT_MS = 3e3;
|
|
1585
1598
|
var PROBE_SHORT_TTL_MS = 5 * 60 * 1e3;
|
|
@@ -1732,7 +1745,7 @@ function extraBinDirs(home, nodePath = "", win) {
|
|
|
1732
1745
|
join7(home, ".bun", "bin"),
|
|
1733
1746
|
...win.localAppData ? [join7(win.localAppData, "Volta", "bin")] : [],
|
|
1734
1747
|
join7(home, "scoop", "shims"),
|
|
1735
|
-
...nodePath ? [
|
|
1748
|
+
...nodePath ? [dirname7(nodePath)] : []
|
|
1736
1749
|
];
|
|
1737
1750
|
}
|
|
1738
1751
|
return [
|
|
@@ -1743,7 +1756,7 @@ function extraBinDirs(home, nodePath = "", win) {
|
|
|
1743
1756
|
join7(home, ".volta", "bin"),
|
|
1744
1757
|
"/opt/homebrew/bin",
|
|
1745
1758
|
"/usr/local/bin",
|
|
1746
|
-
...nodePath ? [
|
|
1759
|
+
...nodePath ? [dirname7(nodePath)] : []
|
|
1747
1760
|
];
|
|
1748
1761
|
}
|
|
1749
1762
|
function whichIn(name, d) {
|
|
@@ -1778,7 +1791,7 @@ function binDirs(d) {
|
|
|
1778
1791
|
function parseNpmCmdShim(shimPath, text) {
|
|
1779
1792
|
const m = text.match(/"%~?dp0%?\\?([^"%]+)"\s+%\*/i);
|
|
1780
1793
|
if (!m) return null;
|
|
1781
|
-
return join7(
|
|
1794
|
+
return join7(dirname7(shimPath), m[1].replace(/^\\/, ""));
|
|
1782
1795
|
}
|
|
1783
1796
|
var JS_RUNTIME_EXE = /^(?:node|bun|bunx|deno)(?:\.exe)?$/i;
|
|
1784
1797
|
function resolveWinLaunch(file, d) {
|
|
@@ -1855,21 +1868,8 @@ function locateWin(tool, d) {
|
|
|
1855
1868
|
return pickWinClaude(d);
|
|
1856
1869
|
case "codex":
|
|
1857
1870
|
return plain("codex");
|
|
1858
|
-
case "workbuddy":
|
|
1859
|
-
|
|
1860
|
-
for (const dir of dirs) {
|
|
1861
|
-
for (const ext of [".exe", ".cmd", ""]) {
|
|
1862
|
-
const p = join7(dir, WORKBUDDY_CLI_IN_WIN_APP + ext);
|
|
1863
|
-
if (d.exists(p)) {
|
|
1864
|
-
const r = resolveWinLaunch(p, d);
|
|
1865
|
-
if (r) return r;
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
|
-
return null;
|
|
1870
|
-
};
|
|
1871
|
-
return inApp(winAppDirs(d, "WorkBuddy")) ?? plain("codebuddy") ?? inApp(d.winAppHints?.("WorkBuddy", "WorkBuddy.exe") ?? []);
|
|
1872
|
-
}
|
|
1871
|
+
case "workbuddy":
|
|
1872
|
+
return locateWorkbuddy(d)?.loc ?? null;
|
|
1873
1873
|
case "qoder_cn": {
|
|
1874
1874
|
for (const dir of winAppDirs(d, "Qoder CN")) {
|
|
1875
1875
|
const p = join7(dir, QODER_CN_WORKER_IN_WIN_APP);
|
|
@@ -1907,20 +1907,70 @@ function workbuddyHasTui(loc, exists = existsSync7, realpath = realpathSync) {
|
|
|
1907
1907
|
const cands = [...loc.prefixArgs, loc.file].map(real2);
|
|
1908
1908
|
const script = cands.find((a) => BIN.test(a));
|
|
1909
1909
|
if (!script) return true;
|
|
1910
|
-
const dist = join7(
|
|
1910
|
+
const dist = join7(dirname7(dirname7(script)), "dist");
|
|
1911
1911
|
return exists(join7(dist, "codebuddy.js"));
|
|
1912
1912
|
}
|
|
1913
1913
|
function locateWorkbuddyTui(d = defaultLocateDeps(), exists, realpath) {
|
|
1914
|
-
const cands = [];
|
|
1915
|
-
const app = locate("workbuddy", d);
|
|
1916
|
-
if (app) cands.push(app);
|
|
1917
|
-
const onPath = whichIn("codebuddy", d);
|
|
1918
|
-
if (onPath) {
|
|
1919
|
-
const loc = isWin(d) ? resolveWinLaunch(onPath, d) : { file: onPath, prefixArgs: [] };
|
|
1920
|
-
if (loc && !cands.some((c) => c.file === loc.file && c.prefixArgs.join("\0") === loc.prefixArgs.join("\0"))) cands.push(loc);
|
|
1921
|
-
}
|
|
1914
|
+
const cands = [locateWorkbuddyStandalone(d), locateWorkbuddyApp(d)].filter((c) => c !== null);
|
|
1922
1915
|
return cands.find((c) => workbuddyHasTui(c, exists, realpath)) ?? null;
|
|
1923
1916
|
}
|
|
1917
|
+
function locateQoderCnTui(d = defaultLocateDeps()) {
|
|
1918
|
+
if (isWin(d)) return locate("qoder_cn", d);
|
|
1919
|
+
const onPath = whichIn("qoderclicn", d);
|
|
1920
|
+
if (onPath) return { file: onPath, prefixArgs: [] };
|
|
1921
|
+
return locate("qoder_cn", d);
|
|
1922
|
+
}
|
|
1923
|
+
var WORKBUDDY_STANDALONE_PKG = "@tencent-ai/codebuddy-code";
|
|
1924
|
+
function isAppBundledPath(p) {
|
|
1925
|
+
return /app\.asar(?:\.unpacked)?[\\/]/i.test(p) || /\.app[\\/]Contents[\\/]/.test(p);
|
|
1926
|
+
}
|
|
1927
|
+
function locateWorkbuddyStandalone(d = defaultLocateDeps()) {
|
|
1928
|
+
const real2 = (p) => {
|
|
1929
|
+
try {
|
|
1930
|
+
return d.realpath ? d.realpath(p) : p;
|
|
1931
|
+
} catch {
|
|
1932
|
+
return p;
|
|
1933
|
+
}
|
|
1934
|
+
};
|
|
1935
|
+
for (const f of whichAllIn("codebuddy", d)) {
|
|
1936
|
+
if (isWin(d)) {
|
|
1937
|
+
const loc = resolveWinLaunch(f, d);
|
|
1938
|
+
if (!loc || [loc.file, ...loc.prefixArgs].some(isAppBundledPath)) continue;
|
|
1939
|
+
return loc;
|
|
1940
|
+
}
|
|
1941
|
+
if (isAppBundledPath(f) || isAppBundledPath(real2(f))) continue;
|
|
1942
|
+
return { file: f, prefixArgs: [] };
|
|
1943
|
+
}
|
|
1944
|
+
return null;
|
|
1945
|
+
}
|
|
1946
|
+
function locateWorkbuddyApp(d = defaultLocateDeps()) {
|
|
1947
|
+
if (!isWin(d)) {
|
|
1948
|
+
for (const dir of appDirs(d.home)) {
|
|
1949
|
+
const p = join7(dir, WORKBUDDY_CLI_IN_APP);
|
|
1950
|
+
if (d.isExecutable(p)) return { file: p, prefixArgs: [] };
|
|
1951
|
+
}
|
|
1952
|
+
return null;
|
|
1953
|
+
}
|
|
1954
|
+
const inApp = (dirs) => {
|
|
1955
|
+
for (const dir of dirs) {
|
|
1956
|
+
for (const ext of [".exe", ".cmd", ""]) {
|
|
1957
|
+
const p = join7(dir, WORKBUDDY_CLI_IN_WIN_APP + ext);
|
|
1958
|
+
if (d.exists(p)) {
|
|
1959
|
+
const r = resolveWinLaunch(p, d);
|
|
1960
|
+
if (r) return r;
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
return null;
|
|
1965
|
+
};
|
|
1966
|
+
return inApp(winAppDirs(d, "WorkBuddy")) ?? inApp(d.winAppHints?.("WorkBuddy", "WorkBuddy.exe") ?? []);
|
|
1967
|
+
}
|
|
1968
|
+
function locateWorkbuddy(d = defaultLocateDeps()) {
|
|
1969
|
+
const s2 = locateWorkbuddyStandalone(d);
|
|
1970
|
+
if (s2) return { loc: s2, kind: "standalone" };
|
|
1971
|
+
const a = locateWorkbuddyApp(d);
|
|
1972
|
+
return a ? { loc: a, kind: "app" } : null;
|
|
1973
|
+
}
|
|
1924
1974
|
var WORKBUDDY_CLI_IN_APP = "WorkBuddy.app/Contents/Resources/app.asar.unpacked/cli/bin/codebuddy";
|
|
1925
1975
|
var QODER_CN_WORKER_IN_APP = "Qoder CN.app/Contents/Resources/app.asar.unpacked/node_modules/@qoder-ai/qoder-cn-agent-sdk/dist/_worker/qoder-worker-runtime.obf.mjs";
|
|
1926
1976
|
var QODER_WORKER_IN_APP = "Qoder.app/Contents/Resources/app.asar.unpacked/node_modules/@qoder-ai/qoder-agent-sdk/dist/_worker/qoder-worker-runtime.obf.mjs";
|
|
@@ -1935,13 +1985,8 @@ function locate(tool, d = defaultLocateDeps()) {
|
|
|
1935
1985
|
return plain("claude");
|
|
1936
1986
|
case "codex":
|
|
1937
1987
|
return plain("codex");
|
|
1938
|
-
case "workbuddy":
|
|
1939
|
-
|
|
1940
|
-
const p = join7(dir, WORKBUDDY_CLI_IN_APP);
|
|
1941
|
-
if (d.isExecutable(p)) return { file: p, prefixArgs: [] };
|
|
1942
|
-
}
|
|
1943
|
-
return plain("codebuddy");
|
|
1944
|
-
}
|
|
1988
|
+
case "workbuddy":
|
|
1989
|
+
return locateWorkbuddy(d)?.loc ?? null;
|
|
1945
1990
|
case "qoder_cn": {
|
|
1946
1991
|
for (const dir of appDirs(d.home)) {
|
|
1947
1992
|
const p = join7(dir, QODER_CN_WORKER_IN_APP);
|
|
@@ -2000,13 +2045,13 @@ function toolConfigDir(tool) {
|
|
|
2000
2045
|
case "codex":
|
|
2001
2046
|
return codexHome();
|
|
2002
2047
|
case "workbuddy":
|
|
2003
|
-
return
|
|
2048
|
+
return dirname8(workbuddySettingsPath());
|
|
2004
2049
|
case "qoder_cn":
|
|
2005
|
-
return
|
|
2050
|
+
return dirname8(qoderCnSettingsPath());
|
|
2006
2051
|
case "qoder":
|
|
2007
|
-
return
|
|
2052
|
+
return dirname8(qoderSettingsPath());
|
|
2008
2053
|
case "opencode":
|
|
2009
|
-
return
|
|
2054
|
+
return dirname8(dirname8(opencodePluginPath()));
|
|
2010
2055
|
case "qwenwork":
|
|
2011
2056
|
return qwenworkHome();
|
|
2012
2057
|
case "deepseek_harness":
|
|
@@ -2222,11 +2267,11 @@ async function refreshRegistry() {
|
|
|
2222
2267
|
}
|
|
2223
2268
|
|
|
2224
2269
|
// src/commands/enable.ts
|
|
2225
|
-
import { dirname as
|
|
2270
|
+
import { dirname as dirname15, join as join21 } from "path";
|
|
2226
2271
|
|
|
2227
2272
|
// src/adapters/ccHook.ts
|
|
2228
2273
|
import { chmodSync as chmodSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync8, renameSync as renameSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2229
|
-
import { dirname as
|
|
2274
|
+
import { dirname as dirname9, join as join9 } from "path";
|
|
2230
2275
|
|
|
2231
2276
|
// src/daemon/gate/settings.ts
|
|
2232
2277
|
import { existsSync as existsSync9 } from "fs";
|
|
@@ -2316,7 +2361,7 @@ function readSettings(path) {
|
|
|
2316
2361
|
}
|
|
2317
2362
|
}
|
|
2318
2363
|
function writeSettings(path, settings) {
|
|
2319
|
-
mkdirSync7(
|
|
2364
|
+
mkdirSync7(dirname9(path), { recursive: true });
|
|
2320
2365
|
let mode;
|
|
2321
2366
|
try {
|
|
2322
2367
|
mode = statSync3(path).mode & 511;
|
|
@@ -2448,8 +2493,8 @@ function retagCcHook(settingsPath, scripts, from, to) {
|
|
|
2448
2493
|
function uninstallCcHook(settingsPath, hookPath) {
|
|
2449
2494
|
const settings = readSettings(settingsPath);
|
|
2450
2495
|
if (!settings.hooks) return;
|
|
2451
|
-
const gatePath = join9(
|
|
2452
|
-
const wakePath = join9(
|
|
2496
|
+
const gatePath = join9(dirname9(hookPath), "gate.cjs");
|
|
2497
|
+
const wakePath = join9(dirname9(hookPath), "wake.cjs");
|
|
2453
2498
|
for (const event of Object.keys(settings.hooks)) {
|
|
2454
2499
|
const groups = settings.hooks[event] ?? [];
|
|
2455
2500
|
const kept = [];
|
|
@@ -2463,14 +2508,14 @@ function uninstallCcHook(settingsPath, hookPath) {
|
|
|
2463
2508
|
}
|
|
2464
2509
|
writeSettings(settingsPath, settings);
|
|
2465
2510
|
try {
|
|
2466
|
-
rmSync4(join9(
|
|
2511
|
+
rmSync4(join9(dirname9(hookPath), "wake", "tokens"), { recursive: true, force: true });
|
|
2467
2512
|
} catch {
|
|
2468
2513
|
}
|
|
2469
2514
|
}
|
|
2470
2515
|
|
|
2471
2516
|
// src/adapters/codexHook.ts
|
|
2472
2517
|
import { chmodSync as chmodSync3, existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync9, renameSync as renameSync5, statSync as statSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
2473
|
-
import { dirname as
|
|
2518
|
+
import { dirname as dirname10, join as join10 } from "path";
|
|
2474
2519
|
var CODEX_MARKER_BEGIN = "# >>> deskbuddy >>>";
|
|
2475
2520
|
var CODEX_MARKER_END = "# <<< deskbuddy <<<";
|
|
2476
2521
|
var CODEX_EVENTS = [
|
|
@@ -2490,19 +2535,19 @@ function renderBlock(hookPath, events, tool, platform) {
|
|
|
2490
2535
|
const script = platform === "win32" ? winPsPath(hookPath) : hookPath;
|
|
2491
2536
|
const command = JSON.stringify(`node ${script} ${tool}`);
|
|
2492
2537
|
const appended = JSON.stringify(`node ${script} ${tool} --appended`);
|
|
2493
|
-
const
|
|
2538
|
+
const lines3 = [CODEX_MARKER_BEGIN];
|
|
2494
2539
|
for (const event of events) {
|
|
2495
|
-
|
|
2540
|
+
lines3.push(`[[hooks.${event}]]`, 'matcher = ""', "hooks = [", ` { type = "command", command = ${command}, async = true, timeoutSec = 5 }`);
|
|
2496
2541
|
if (event === "UserPromptSubmit") {
|
|
2497
|
-
|
|
2542
|
+
lines3.push(` , { type = "command", command = ${appended}, async = false, timeout = ${CODEX_APPENDED_TIMEOUT_S} }`);
|
|
2498
2543
|
}
|
|
2499
|
-
|
|
2544
|
+
lines3.push("]", "");
|
|
2500
2545
|
}
|
|
2501
|
-
|
|
2502
|
-
return
|
|
2546
|
+
lines3.push(CODEX_MARKER_END);
|
|
2547
|
+
return lines3.join("\n");
|
|
2503
2548
|
}
|
|
2504
2549
|
function writeAtomic(path, content) {
|
|
2505
|
-
mkdirSync8(
|
|
2550
|
+
mkdirSync8(dirname10(path), { recursive: true });
|
|
2506
2551
|
let mode;
|
|
2507
2552
|
try {
|
|
2508
2553
|
mode = statSync4(path).mode & 511;
|
|
@@ -2525,13 +2570,13 @@ function installCodexHook(configPath2, hookPath, events = CODEX_EVENTS, tool = "
|
|
|
2525
2570
|
const raw = existsSync11(configPath2) ? readFileSync9(configPath2, "utf8") : "";
|
|
2526
2571
|
const block = renderBlock(hookPath, events, tool, platform);
|
|
2527
2572
|
if (raw.includes(CODEX_MARKER_BEGIN)) {
|
|
2528
|
-
const
|
|
2529
|
-
const begin =
|
|
2530
|
-
const end =
|
|
2573
|
+
const lines3 = raw.split("\n");
|
|
2574
|
+
const begin = lines3.findIndex((l) => l.trim() === CODEX_MARKER_BEGIN);
|
|
2575
|
+
const end = lines3.findIndex((l, i) => i > begin && l.trim() === CODEX_MARKER_END);
|
|
2531
2576
|
if (begin === -1 || end === -1) return;
|
|
2532
|
-
if (
|
|
2533
|
-
|
|
2534
|
-
writeAtomic(configPath2,
|
|
2577
|
+
if (lines3.slice(begin, end + 1).join("\n") === block) return;
|
|
2578
|
+
lines3.splice(begin, end - begin + 1, ...block.split("\n"));
|
|
2579
|
+
writeAtomic(configPath2, lines3.join("\n"));
|
|
2535
2580
|
return;
|
|
2536
2581
|
}
|
|
2537
2582
|
assertSafeToAppend(raw, configPath2);
|
|
@@ -2541,21 +2586,21 @@ function installCodexHook(configPath2, hookPath, events = CODEX_EVENTS, tool = "
|
|
|
2541
2586
|
function uninstallCodexHook(configPath2) {
|
|
2542
2587
|
if (!existsSync11(configPath2)) return;
|
|
2543
2588
|
const raw = readFileSync9(configPath2, "utf8");
|
|
2544
|
-
const
|
|
2545
|
-
const begin =
|
|
2589
|
+
const lines3 = raw.split("\n");
|
|
2590
|
+
const begin = lines3.findIndex((l) => l.trim() === CODEX_MARKER_BEGIN);
|
|
2546
2591
|
if (begin === -1) return;
|
|
2547
|
-
const end =
|
|
2592
|
+
const end = lines3.findIndex((l, i) => i > begin && l.trim() === CODEX_MARKER_END);
|
|
2548
2593
|
if (end === -1) {
|
|
2549
2594
|
throw new Error(`${configPath2} \u91CC\u53EA\u627E\u5230\u5F00\u59CB\u6807\u8BB0\u3001\u6CA1\u6709\u7ED3\u675F\u6807\u8BB0\uFF0C\u4E3A\u514D\u8BEF\u5220\u5DF2\u4E2D\u6B62\uFF0C\u8BF7\u624B\u5DE5\u68C0\u67E5`);
|
|
2550
2595
|
}
|
|
2551
|
-
const start = begin > 0 &&
|
|
2552
|
-
|
|
2553
|
-
writeAtomic(configPath2,
|
|
2596
|
+
const start = begin > 0 && lines3[begin - 1].trim() === "" ? begin - 1 : begin;
|
|
2597
|
+
lines3.splice(start, end - start + 1);
|
|
2598
|
+
writeAtomic(configPath2, lines3.join("\n"));
|
|
2554
2599
|
}
|
|
2555
2600
|
|
|
2556
2601
|
// src/adapters/ocPlugin.ts
|
|
2557
2602
|
import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync10, renameSync as renameSync6, unlinkSync, writeFileSync as writeFileSync8 } from "fs";
|
|
2558
|
-
import { dirname as
|
|
2603
|
+
import { dirname as dirname11, join as join11 } from "path";
|
|
2559
2604
|
import { pathToFileURL } from "url";
|
|
2560
2605
|
var OC_MARKER = "// deskbuddy:opencode-plugin";
|
|
2561
2606
|
function ocPluginSource(pluginModule, nodePath, platform = process.platform) {
|
|
@@ -2569,13 +2614,13 @@ function ocPluginSource(pluginModule, nodePath, platform = process.platform) {
|
|
|
2569
2614
|
].join("\n");
|
|
2570
2615
|
}
|
|
2571
2616
|
function ocPluginModule(hookPath) {
|
|
2572
|
-
return join11(
|
|
2617
|
+
return join11(dirname11(hookPath), "opencode.mjs");
|
|
2573
2618
|
}
|
|
2574
2619
|
function installOcPlugin(pluginPath, hookPath, nodePath) {
|
|
2575
2620
|
if (existsSync12(pluginPath) && !readFileSync10(pluginPath, "utf8").includes(OC_MARKER)) {
|
|
2576
2621
|
throw new Error(`${pluginPath} \u5DF2\u5B58\u5728\u4E14\u4E0D\u662F DeskBuddy \u751F\u6210\u7684\uFF0C\u8BF7\u5148\u632A\u8D70\u518D\u91CD\u8BD5`);
|
|
2577
2622
|
}
|
|
2578
|
-
mkdirSync9(
|
|
2623
|
+
mkdirSync9(dirname11(pluginPath), { recursive: true });
|
|
2579
2624
|
const tmp = pluginPath + ".deskbuddy.tmp";
|
|
2580
2625
|
writeFileSync8(tmp, ocPluginSource(ocPluginModule(hookPath), nodePath), "utf8");
|
|
2581
2626
|
renameSync6(tmp, pluginPath);
|
|
@@ -2597,13 +2642,13 @@ var OC_NOTICE = " \u5DF2\u5F00\u7740\u7684 opencode \u7A97\u53E3\u91CD\u5F00\u
|
|
|
2597
2642
|
|
|
2598
2643
|
// src/adapters/dshPlugin.ts
|
|
2599
2644
|
import { copyFileSync as copyFileSync2, existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync11, renameSync as renameSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
2600
|
-
import { dirname as
|
|
2645
|
+
import { dirname as dirname12, join as join12 } from "path";
|
|
2601
2646
|
var DSH_MARKER_BEGIN = "# >>> deskbuddy:dsh-plugin";
|
|
2602
2647
|
var DSH_MARKER_END = "# <<< deskbuddy:dsh-plugin";
|
|
2603
2648
|
var CREATED_FLAG = "created-file";
|
|
2604
2649
|
var DSH_ENTRY_ID = "deskbuddy-cli";
|
|
2605
2650
|
function dshPluginModule(hookPath) {
|
|
2606
|
-
return join12(
|
|
2651
|
+
return join12(dirname12(hookPath), "deskbuddy-dsh.mjs");
|
|
2607
2652
|
}
|
|
2608
2653
|
function dshBlock(pluginModule, nodePath, created) {
|
|
2609
2654
|
return [
|
|
@@ -2618,13 +2663,13 @@ function dshBlock(pluginModule, nodePath, created) {
|
|
|
2618
2663
|
].join("\n");
|
|
2619
2664
|
}
|
|
2620
2665
|
function splitOurs(text) {
|
|
2621
|
-
const
|
|
2622
|
-
const b =
|
|
2666
|
+
const lines3 = text.split("\n");
|
|
2667
|
+
const b = lines3.findIndex((l) => l.startsWith(DSH_MARKER_BEGIN));
|
|
2623
2668
|
if (b < 0) return { rest: text, found: false, created: false };
|
|
2624
|
-
const e =
|
|
2669
|
+
const e = lines3.findIndex((l, i) => i > b && l.startsWith(DSH_MARKER_END));
|
|
2625
2670
|
if (e < 0) return { rest: text, found: false, created: false, broken: true };
|
|
2626
|
-
const created =
|
|
2627
|
-
return { rest: [...
|
|
2671
|
+
const created = lines3[b].slice(DSH_MARKER_BEGIN.length).includes(CREATED_FLAG);
|
|
2672
|
+
return { rest: [...lines3.slice(0, b), ...lines3.slice(e + 1)].join("\n"), found: true, created };
|
|
2628
2673
|
}
|
|
2629
2674
|
function significant(text) {
|
|
2630
2675
|
return text.split("\n").filter((l) => l.trim() !== "" && !l.trimStart().startsWith("#"));
|
|
@@ -2642,7 +2687,7 @@ function patchShape(text) {
|
|
|
2642
2687
|
return "block";
|
|
2643
2688
|
}
|
|
2644
2689
|
function atomicWrite(path, text) {
|
|
2645
|
-
mkdirSync10(
|
|
2690
|
+
mkdirSync10(dirname12(path), { recursive: true });
|
|
2646
2691
|
const tmp = `${path}.deskbuddy.tmp`;
|
|
2647
2692
|
writeFileSync9(tmp, text, "utf8");
|
|
2648
2693
|
renameSync7(tmp, path);
|
|
@@ -3149,12 +3194,12 @@ function shimState(tool, binDir, pathEnv, platform = process.platform) {
|
|
|
3149
3194
|
}
|
|
3150
3195
|
|
|
3151
3196
|
// src/commands/migrateQoder.ts
|
|
3152
|
-
import { dirname as
|
|
3197
|
+
import { dirname as dirname13, join as join14 } from "path";
|
|
3153
3198
|
function migrateLegacyQoder(out, detect = () => detectTools()) {
|
|
3154
3199
|
try {
|
|
3155
3200
|
const cfg = loadConfig();
|
|
3156
3201
|
const hookPath = installedHookPath();
|
|
3157
|
-
const scripts = [hookPath, join14(
|
|
3202
|
+
const scripts = [hookPath, join14(dirname13(hookPath), "gate.cjs")];
|
|
3158
3203
|
if (!retagCcHook(qoderCnSettingsPath(), scripts, "qoder", "qoder_cn")) return false;
|
|
3159
3204
|
const hadQoder = cfg.enabledTools.includes("qoder");
|
|
3160
3205
|
let intl = false;
|
|
@@ -3183,7 +3228,7 @@ function migrateLegacyQoder(out, detect = () => detectTools()) {
|
|
|
3183
3228
|
|
|
3184
3229
|
// src/daemon/loginGuide.ts
|
|
3185
3230
|
import { execFile as execFile6, execFileSync as execFileSync10 } from "child_process";
|
|
3186
|
-
import { chmodSync as chmodSync7, mkdirSync as mkdirSync15, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
3231
|
+
import { chmodSync as chmodSync7, existsSync as existsSync16, mkdirSync as mkdirSync15, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
3187
3232
|
import { homedir as homedir6 } from "os";
|
|
3188
3233
|
import { join as join19 } from "path";
|
|
3189
3234
|
|
|
@@ -3347,13 +3392,13 @@ function analyzeTranscript(file) {
|
|
|
3347
3392
|
const buf = Buffer.alloc(len);
|
|
3348
3393
|
fd = openSync3(file, "r");
|
|
3349
3394
|
readSync2(fd, buf, 0, len, size - len);
|
|
3350
|
-
const
|
|
3395
|
+
const lines3 = buf.toString("utf8").split("\n");
|
|
3351
3396
|
let seg = false;
|
|
3352
3397
|
let reply = null;
|
|
3353
3398
|
let humanAfter = false;
|
|
3354
3399
|
let interrupted = null;
|
|
3355
|
-
for (let i =
|
|
3356
|
-
const line =
|
|
3400
|
+
for (let i = lines3.length - 1; i >= 0; i--) {
|
|
3401
|
+
const line = lines3[i];
|
|
3357
3402
|
if (line.indexOf('"assistant"') === -1 && line.indexOf('"user"') === -1) continue;
|
|
3358
3403
|
let o;
|
|
3359
3404
|
try {
|
|
@@ -3397,7 +3442,7 @@ function analyzeTranscript(file) {
|
|
|
3397
3442
|
// src/core/launch.ts
|
|
3398
3443
|
var RUN_ID_RE = /^[a-f0-9]{32}$/;
|
|
3399
3444
|
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}$/;
|
|
3400
|
-
var LAUNCH_TOOLS = ["claude_code", "codex", "workbuddy"];
|
|
3445
|
+
var LAUNCH_TOOLS = ["claude_code", "codex", "workbuddy", "qoder_cn"];
|
|
3401
3446
|
function validPlan(p) {
|
|
3402
3447
|
if (!LAUNCH_TOOLS.includes(p.tool)) return false;
|
|
3403
3448
|
if (p.resume && (p.tool !== "claude_code" || !SESSION_UUID_RE.test(p.resume))) return false;
|
|
@@ -3538,6 +3583,7 @@ function buildSpawnPlan(cmd, cwd, gate, home = homedir5()) {
|
|
|
3538
3583
|
return { file, args: workbuddyPlan(cmd, gate), cwd, env: { CODEBUDDY_CONFIG_DIR: join17(home, ".workbuddy") } };
|
|
3539
3584
|
case "qoder_cn":
|
|
3540
3585
|
case "qoder":
|
|
3586
|
+
if (cmd.sessionId && !SESSION_UUID_RE.test(cmd.sessionId)) return null;
|
|
3541
3587
|
return gate ? { file, args: qoderPlan(cmd, gate), cwd } : null;
|
|
3542
3588
|
case "opencode":
|
|
3543
3589
|
return gate ? {
|
|
@@ -3688,9 +3734,9 @@ function finalResultOf(stdout7) {
|
|
|
3688
3734
|
return pick(JSON.parse(stdout7));
|
|
3689
3735
|
} catch {
|
|
3690
3736
|
}
|
|
3691
|
-
const
|
|
3692
|
-
for (let i =
|
|
3693
|
-
const l =
|
|
3737
|
+
const lines3 = stdout7.split("\n");
|
|
3738
|
+
for (let i = lines3.length - 1; i >= 0; i--) {
|
|
3739
|
+
const l = lines3[i].trim();
|
|
3694
3740
|
if (!l.startsWith("{")) continue;
|
|
3695
3741
|
try {
|
|
3696
3742
|
const o = pick(JSON.parse(l));
|
|
@@ -4001,16 +4047,30 @@ ${r.stderr}`;
|
|
|
4001
4047
|
return { tool, state: "unknown", note: "\u8BA4\u4E0D\u51FA codex login status \u7684\u8F93\u51FA" };
|
|
4002
4048
|
}
|
|
4003
4049
|
var WORKBUDDY_PROBE_ARGS = ["-p", PROBE_TEXT, "--output-format", "json", "--no-session-persistence", "--max-turns", "1"];
|
|
4050
|
+
var WORKBUDDY_AUTH_FILE = "Tencent-Cloud.coding-copilot.info";
|
|
4051
|
+
function workbuddyAuthFile(home, platform, localAppData = "") {
|
|
4052
|
+
const tail2 = ["CodeBuddyExtension", "Data", "Public", "auth", WORKBUDDY_AUTH_FILE];
|
|
4053
|
+
if (platform === "darwin") return join19(home, "Library", "Application Support", ...tail2);
|
|
4054
|
+
if (platform === "win32") return localAppData ? join19(localAppData, ...tail2) : null;
|
|
4055
|
+
return null;
|
|
4056
|
+
}
|
|
4004
4057
|
async function checkWorkbuddy(d) {
|
|
4005
4058
|
const tool = "workbuddy";
|
|
4006
4059
|
const loc = d.locate(tool);
|
|
4007
4060
|
if (!loc) return { tool, state: "unknown", note: "\u627E\u4E0D\u5230\u53EF\u6267\u884C\u6587\u4EF6" };
|
|
4061
|
+
const kind = d.workbuddyKind?.() ?? null;
|
|
4062
|
+
if (kind === "standalone") {
|
|
4063
|
+
const auth = d.workbuddyAuthFile?.() ?? null;
|
|
4064
|
+
if (auth && d.exists?.(auth)) return { tool, state: "logged_in", note: "\u72EC\u7ACB\u7248\u51ED\u636E\u6587\u4EF6\u5728" };
|
|
4065
|
+
}
|
|
4008
4066
|
const env = envFor(d, loc, { CODEBUDDY_CONFIG_DIR: join19(d.home, ".workbuddy"), DESKBUDDY_NO_WAKE: "1", [PROBE_ENV]: "1" });
|
|
4009
4067
|
const r = await d.run(loc.file, [...loc.prefixArgs, ...WORKBUDDY_PROBE_ARGS], env, WORKBUDDY_PROBE_TIMEOUT_MS);
|
|
4010
4068
|
const bad = failedRun(r);
|
|
4011
4069
|
if (bad) return { tool, state: "unknown", note: bad };
|
|
4012
4070
|
const out = { stdout: r.stdout, stderr: r.stderr };
|
|
4013
|
-
if (isNotLoggedIn(out, PROBE_TEXT) || r.code === 0 && isNotLoggedInOk(out, PROBE_TEXT, tool))
|
|
4071
|
+
if (isNotLoggedIn(out, PROBE_TEXT) || r.code === 0 && isNotLoggedInOk(out, PROBE_TEXT, tool)) {
|
|
4072
|
+
return kind === "app" ? { tool, state: "not_logged_in", note: "\u53EA\u88C5\u4E86 WorkBuddy App\uFF0C\u6CA1\u6709\u72EC\u7ACB\u7248\u547D\u4EE4\u884C" } : { tool, state: "not_logged_in" };
|
|
4073
|
+
}
|
|
4014
4074
|
if (r.code === 0 && r.stdout.trim()) return { tool, state: "logged_in" };
|
|
4015
4075
|
return { tool, state: "unknown", note: `\u63A2\u6D4B\u6CA1\u6210\u529F\uFF08\u9000\u51FA\u7801 ${r.code ?? "\u65E0"}\uFF09` };
|
|
4016
4076
|
}
|
|
@@ -4093,7 +4153,10 @@ var defaultCheckDeps = () => ({
|
|
|
4093
4153
|
return null;
|
|
4094
4154
|
}
|
|
4095
4155
|
},
|
|
4096
|
-
platform: process.platform
|
|
4156
|
+
platform: process.platform,
|
|
4157
|
+
workbuddyKind: () => locateWorkbuddy()?.kind ?? null,
|
|
4158
|
+
workbuddyAuthFile: () => workbuddyAuthFile(homedir6(), process.platform, process.env.LOCALAPPDATA ?? ""),
|
|
4159
|
+
exists: (p) => existsSync16(p)
|
|
4097
4160
|
});
|
|
4098
4161
|
var LOGIN_ARGS = {
|
|
4099
4162
|
claude_code: ["auth", "login"],
|
|
@@ -4108,6 +4171,8 @@ var defaultCommandDeps = () => ({
|
|
|
4108
4171
|
locateWorkbuddyTui: () => locateWorkbuddyTui(),
|
|
4109
4172
|
home: homedir6()
|
|
4110
4173
|
});
|
|
4174
|
+
var WORKBUDDY_LOGIN_HINT = "\uFF08\u8F93\u5165 /login \u56DE\u8F66\uFF0C\u9009\u56FD\u5185\u7248\uFF09";
|
|
4175
|
+
var WORKBUDDY_NEED_STANDALONE = "\u53EA\u88C5\u4E86 WorkBuddy App\uFF0C\u5B83\u81EA\u5E26\u7684\u547D\u4EE4\u884C\u7528\u4E0D\u4E86 App \u7684\u767B\u5F55\uFF1B\u8BBE\u5907\u4E0A\u65B0\u5EFA / \u56DE\u590D\u4EFB\u52A1\u9700\u8981\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF08\u8FD0\u884C deskbuddy workbuddy-setup \u5B89\u88C5\uFF09";
|
|
4111
4176
|
function loginCommandFor(tool, d) {
|
|
4112
4177
|
const extra = LOGIN_ARGS[tool];
|
|
4113
4178
|
if (!extra) return { error: "unsupported" };
|
|
@@ -4118,7 +4183,7 @@ function loginCommandFor(tool, d) {
|
|
|
4118
4183
|
loc = d.locateWorkbuddyTui();
|
|
4119
4184
|
if (!loc) return d.locate(tool) ? { error: "no_tui" } : { error: "not_found" };
|
|
4120
4185
|
env = { CODEBUDDY_CONFIG_DIR: join19(d.home, ".workbuddy") };
|
|
4121
|
-
hint =
|
|
4186
|
+
hint = WORKBUDDY_LOGIN_HINT;
|
|
4122
4187
|
} else {
|
|
4123
4188
|
loc = d.locate(tool);
|
|
4124
4189
|
if (!loc) return { error: "not_found" };
|
|
@@ -4156,21 +4221,21 @@ var shq2 = (s2) => `'${s2}'`;
|
|
|
4156
4221
|
var psq2 = (s2) => `'${s2.replace(/'/g, "''")}'`;
|
|
4157
4222
|
function macLoginScript(cmd, pathDirs = []) {
|
|
4158
4223
|
checkLoginCommand(cmd);
|
|
4159
|
-
const
|
|
4160
|
-
for (const [k, v] of Object.entries(cmd.env))
|
|
4224
|
+
const lines3 = ["#!/bin/sh", `printf '%s\\n' ${shq2(bannerLine(cmd))}`, `printf '\\n'`];
|
|
4225
|
+
for (const [k, v] of Object.entries(cmd.env)) lines3.push(`export ${k}=${shq2(v)}`);
|
|
4161
4226
|
const dirs = pathDirs.filter((p) => p.startsWith("/") && safePath(p) && !p.includes(":"));
|
|
4162
|
-
if (dirs.length > 0)
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
return
|
|
4227
|
+
if (dirs.length > 0) lines3.push(`export PATH=${shq2(dirs.join(":"))}":$PATH"`);
|
|
4228
|
+
lines3.push([cmd.file, ...cmd.args].map(shq2).join(" "));
|
|
4229
|
+
lines3.push(`printf '\\n%s\\n' ${shq2(DONE_LINE)}`);
|
|
4230
|
+
return lines3.join("\n") + "\n";
|
|
4166
4231
|
}
|
|
4167
4232
|
function winLoginScript(cmd) {
|
|
4168
4233
|
checkLoginCommand(cmd);
|
|
4169
|
-
const
|
|
4170
|
-
for (const [k, v] of Object.entries(cmd.env))
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
return
|
|
4234
|
+
const lines3 = [`$host.UI.RawUI.WindowTitle = ${psq2(`DeskBuddy - ${cmd.label} \u767B\u5F55`)}`, `Write-Host ${psq2(bannerLine(cmd))}`, `Write-Host ''`];
|
|
4235
|
+
for (const [k, v] of Object.entries(cmd.env)) lines3.push(`$env:${k} = ${psq2(v)}`);
|
|
4236
|
+
lines3.push(`& ${[cmd.file, ...cmd.args].map(psq2).join(" ")}`);
|
|
4237
|
+
lines3.push(`Write-Host ''`, `Write-Host ${psq2(DONE_LINE)}`);
|
|
4238
|
+
return lines3.join("\r\n") + "\r\n";
|
|
4174
4239
|
}
|
|
4175
4240
|
function loginScriptPath(home, tool, platform) {
|
|
4176
4241
|
return join19(launchDir(home), `login-${tool}.${platform === "win32" ? "ps1" : "command"}`);
|
|
@@ -4231,7 +4296,7 @@ async function openGuide(tool, d) {
|
|
|
4231
4296
|
if (!gui) return { tool, action: "no_gui", manual };
|
|
4232
4297
|
try {
|
|
4233
4298
|
await d.open(cmd);
|
|
4234
|
-
return { tool, action: "opened", note: cmd.hint ? `\u5728\u7A97\u53E3\u91CC${cmd.hint.replace(/[()]/g, "")}\u5B8C\u6210\u767B\u5F55` : "" };
|
|
4299
|
+
return { tool, action: "opened", note: cmd.hint ? `\u5728\u7A97\u53E3\u91CC${cmd.hint.replace(/[()]/g, "")}\uFF0C\u5B8C\u6210\u767B\u5F55` : "" };
|
|
4235
4300
|
} catch (e) {
|
|
4236
4301
|
return { tool, action: "open_failed", note: e?.message ?? String(e), manual };
|
|
4237
4302
|
}
|
|
@@ -4261,7 +4326,7 @@ function outcomeLine(o) {
|
|
|
4261
4326
|
case "open_failed":
|
|
4262
4327
|
return `\u26A0\uFE0F ${name} \u672A\u767B\u5F55\uFF0C\u6CA1\u80FD\u6253\u5F00\u767B\u5F55\u7A97\u53E3\uFF08${o.note}\uFF09\uFF1A\u8BF7\u5728\u7535\u8111\u7EC8\u7AEF\u8FD0\u884C ${o.manual}`;
|
|
4263
4328
|
case "no_tui":
|
|
4264
|
-
return `\u26A0\uFE0F ${name} \u672A\u767B\u5F55\uFF1A\u672C\u673A ${name} \u81EA\u5E26\u7684\u547D\u4EE4\u884C\u6CA1\u6709\u4EA4\u4E92\u754C\u9762\uFF0C\u6CA1\u6CD5\u5728\u7EC8\u7AEF\u91CC\u767B\u5F55\uFF0C\u8BF7\u8054\u7CFB\u6211\u4EEC`;
|
|
4329
|
+
return o.tool === "workbuddy" ? `\u26A0\uFE0F ${name} \u547D\u4EE4\u884C\u4E0D\u53EF\u7528\uFF1A${WORKBUDDY_NEED_STANDALONE}` : `\u26A0\uFE0F ${name} \u672A\u767B\u5F55\uFF1A\u672C\u673A ${name} \u81EA\u5E26\u7684\u547D\u4EE4\u884C\u6CA1\u6709\u4EA4\u4E92\u754C\u9762\uFF0C\u6CA1\u6CD5\u5728\u7EC8\u7AEF\u91CC\u767B\u5F55\uFF0C\u8BF7\u8054\u7CFB\u6211\u4EEC`;
|
|
4265
4330
|
default:
|
|
4266
4331
|
return `\u26A0\uFE0F ${name} \u672A\u767B\u5F55\uFF08\u627E\u4E0D\u5230\u5B83\u7684\u547D\u4EE4\u884C\uFF09`;
|
|
4267
4332
|
}
|
|
@@ -4273,6 +4338,7 @@ function summaryLine(outcomes) {
|
|
|
4273
4338
|
if (o.state === "logged_in") return `${name} \u5DF2\u767B\u5F55`;
|
|
4274
4339
|
if (o.state === "unknown") return `${name} \u672A\u77E5`;
|
|
4275
4340
|
if (o.action === "opened") return `${name} \u672A\u767B\u5F55\uFF0C\u5DF2\u6253\u5F00\u767B\u5F55\u7A97\u53E3`;
|
|
4341
|
+
if (o.action === "no_tui" && o.tool === "workbuddy") return `${name} \u9700\u8981\u5B89\u88C5\u72EC\u7ACB\u7248\u547D\u4EE4\u884C`;
|
|
4276
4342
|
return `${name} \u672A\u767B\u5F55\uFF0C\u8BF7\u5728\u7535\u8111\u7EC8\u7AEF\u91CC\u767B\u5F55`;
|
|
4277
4343
|
};
|
|
4278
4344
|
return `\u767B\u5F55\u68C0\u67E5: ${outcomes.map(part).join("\uFF1B")}`;
|
|
@@ -4336,7 +4402,16 @@ var LoginGuide = class {
|
|
|
4336
4402
|
this.#d.log(`${name} \u547D\u4EE4\u884C\u6CA1\u767B\u5F55\uFF0C\u6253\u5F00\u767B\u5F55\u7A97\u53E3\u5931\u8D25\uFF08${r.note}\uFF09\uFF1A\u8BF7\u5728\u7535\u8111\u7EC8\u7AEF\u8FD0\u884C ${r.manual}`);
|
|
4337
4403
|
break;
|
|
4338
4404
|
case "no_tui":
|
|
4339
|
-
|
|
4405
|
+
if (tool === "workbuddy") {
|
|
4406
|
+
this.#d.log(`${name} \u547D\u4EE4\u884C\u8DD1\u4E0D\u8D77\u6765\uFF1A${WORKBUDDY_NEED_STANDALONE}`);
|
|
4407
|
+
try {
|
|
4408
|
+
this.#d.onNeedStandalone?.(tool);
|
|
4409
|
+
} catch (e) {
|
|
4410
|
+
this.#d.log(`${name} \u72EC\u7ACB\u7248\u8BE2\u95EE\u51FA\u9519\uFF1A${e?.message ?? e}`);
|
|
4411
|
+
}
|
|
4412
|
+
} else {
|
|
4413
|
+
this.#d.log(`${name} \u547D\u4EE4\u884C\u6CA1\u767B\u5F55\uFF0C\u672C\u673A ${name} \u81EA\u5E26\u7684\u547D\u4EE4\u884C\u6CA1\u6709\u4EA4\u4E92\u754C\u9762\uFF0C\u6CA1\u6CD5\u6253\u5F00\u767B\u5F55\u7A97\u53E3`);
|
|
4414
|
+
}
|
|
4340
4415
|
break;
|
|
4341
4416
|
default:
|
|
4342
4417
|
this.#d.log(`${name} \u547D\u4EE4\u884C\u6CA1\u767B\u5F55\uFF0C\u627E\u4E0D\u5230\u5B83\u7684\u547D\u4EE4\u884C\uFF0C\u6CA1\u6253\u5F00\u767B\u5F55\u7A97\u53E3`);
|
|
@@ -4344,14 +4419,15 @@ var LoginGuide = class {
|
|
|
4344
4419
|
return r.action;
|
|
4345
4420
|
}
|
|
4346
4421
|
};
|
|
4347
|
-
function defaultLoginGuide(log2, now = () => Date.now()) {
|
|
4422
|
+
function defaultLoginGuide(log2, now = () => Date.now(), onNeedStandalone) {
|
|
4348
4423
|
if (process.env.VITEST) return { onNotLoggedIn: async () => "unsupported" };
|
|
4349
4424
|
return new LoginGuide({
|
|
4350
4425
|
now,
|
|
4351
4426
|
command: (tool) => loginCommandFor(tool, defaultCommandDeps()),
|
|
4352
4427
|
gui: () => guiAvailable(defaultGuiDeps()),
|
|
4353
4428
|
open: (cmd) => openLoginWindow(cmd, defaultLoginOpenDeps()),
|
|
4354
|
-
log: log2
|
|
4429
|
+
log: log2,
|
|
4430
|
+
onNeedStandalone
|
|
4355
4431
|
});
|
|
4356
4432
|
}
|
|
4357
4433
|
async function autoEnableLoginLog(tools, check, log2) {
|
|
@@ -4368,6 +4444,487 @@ async function autoEnableLoginLog(tools, check, log2) {
|
|
|
4368
4444
|
}
|
|
4369
4445
|
}
|
|
4370
4446
|
|
|
4447
|
+
// src/daemon/workbuddySetup.ts
|
|
4448
|
+
import { execFile as execFile7, spawn as spawn4 } from "child_process";
|
|
4449
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync16, readFileSync as readFileSync17, rmSync as rmSync9, statSync as statSync6, writeFileSync as writeFileSync15 } from "fs";
|
|
4450
|
+
import { dirname as dirname14, join as join20 } from "path";
|
|
4451
|
+
var DIALOG_TITLE = "DeskBuddy";
|
|
4452
|
+
var BTN_INSTALL = "\u5B89\u88C5";
|
|
4453
|
+
var BTN_LATER = "\u6682\u4E0D";
|
|
4454
|
+
var DIALOG_TIMEOUT_S = 120;
|
|
4455
|
+
var DIALOG_PROC_SLACK_MS = 15 * 1e3;
|
|
4456
|
+
var NPM_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4457
|
+
var ASK_THROTTLE_MS = 24 * 60 * 60 * 1e3;
|
|
4458
|
+
var NPM_INSTALL_ARGS = ["i", "-g", WORKBUDDY_STANDALONE_PKG];
|
|
4459
|
+
var DIALOG_TEXT = [
|
|
4460
|
+
"\u68C0\u6D4B\u5230\u8FD9\u53F0\u7535\u8111\u88C5\u4E86 WorkBuddy\uFF0C\u4F46\u6CA1\u6709\u5B83\u7684\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF08CodeBuddy Code\uFF09\u3002",
|
|
4461
|
+
"",
|
|
4462
|
+
"\u88C5\u4E86\u72EC\u7ACB\u7248\uFF0C\u53EF\u4EE5\u5728\u8BBE\u5907\u4E0A\u76F4\u63A5\u7ED9 WorkBuddy \u65B0\u5EFA\u4EFB\u52A1\u3001\u56DE\u590D\u5DF2\u6709\u4EFB\u52A1\uFF1B\u4E0D\u88C5\u7684\u8BDD\uFF0C\u8BBE\u5907\u4E0A\u53EA\u80FD\u770B App \u91CC\u4EFB\u52A1\u7684\u72B6\u6001\u3001\u56DE\u7B54\u6388\u6743\u3002",
|
|
4463
|
+
"",
|
|
4464
|
+
`\u73B0\u5728\u5B89\u88C5\u5417\uFF1F\uFF08\u4F1A\u6267\u884C npm i -g ${WORKBUDDY_STANDALONE_PKG}\uFF0C\u88C5\u5B8C\u6253\u5F00\u4E00\u4E2A\u7EC8\u7AEF\u7A97\u53E3\uFF0C\u8F93\u5165 /login \u9009\u56FD\u5185\u7248\u767B\u5F55\uFF09`
|
|
4465
|
+
].join("\n");
|
|
4466
|
+
var LIMITED_NOTE = "\u8BBE\u5907\u4E0A\u53EA\u80FD\u770B\u4EFB\u52A1\u72B6\u6001\u3001\u56DE\u7B54\u6388\u6743";
|
|
4467
|
+
var defaultRunner = (file, args, opts) => new Promise((resolve10) => {
|
|
4468
|
+
try {
|
|
4469
|
+
const child = execFile7(
|
|
4470
|
+
file,
|
|
4471
|
+
args,
|
|
4472
|
+
{ env: opts.env, timeout: opts.timeoutMs, windowsHide: true, maxBuffer: 8 * 1024 * 1024, encoding: "utf8" },
|
|
4473
|
+
(err, stdout7, stderr) => {
|
|
4474
|
+
const e = err;
|
|
4475
|
+
resolve10({
|
|
4476
|
+
code: e ? typeof e.code === "number" ? e.code : null : 0,
|
|
4477
|
+
stdout: String(stdout7 ?? ""),
|
|
4478
|
+
stderr: String(stderr ?? ""),
|
|
4479
|
+
timedOut: !!e && (e.killed === true || !!e.signal) && typeof e.code !== "string",
|
|
4480
|
+
spawnError: !!e && typeof e.code === "string"
|
|
4481
|
+
});
|
|
4482
|
+
}
|
|
4483
|
+
);
|
|
4484
|
+
child.stdin?.end();
|
|
4485
|
+
} catch {
|
|
4486
|
+
resolve10({ code: null, stdout: "", stderr: "", timedOut: false, spawnError: true });
|
|
4487
|
+
}
|
|
4488
|
+
});
|
|
4489
|
+
function checkTimeout(s2) {
|
|
4490
|
+
if (!Number.isInteger(s2) || s2 < 1 || s2 > 3600) throw new Error("\u5F39\u6846\u8D85\u65F6\u4E0D\u5408\u6CD5");
|
|
4491
|
+
}
|
|
4492
|
+
function macDialogArgs(text, title, later, install, timeoutS = DIALOG_TIMEOUT_S) {
|
|
4493
|
+
checkTimeout(timeoutS);
|
|
4494
|
+
for (const a of [text, title, later, install]) if (a.includes("\0")) throw new Error("\u5F39\u6846\u6587\u5B57\u4E0D\u5408\u6CD5");
|
|
4495
|
+
return [
|
|
4496
|
+
"-e",
|
|
4497
|
+
"on run argv",
|
|
4498
|
+
"-e",
|
|
4499
|
+
`display dialog (item 1 of argv) with title (item 2 of argv) buttons {(item 3 of argv), (item 4 of argv)} giving up after ${timeoutS}`,
|
|
4500
|
+
"-e",
|
|
4501
|
+
"end run",
|
|
4502
|
+
text,
|
|
4503
|
+
title,
|
|
4504
|
+
later,
|
|
4505
|
+
install
|
|
4506
|
+
];
|
|
4507
|
+
}
|
|
4508
|
+
function parseMacDialog(stdout7, later = BTN_LATER, install = BTN_INSTALL) {
|
|
4509
|
+
if (/gave up:\s*true/i.test(stdout7)) return "timeout";
|
|
4510
|
+
const m = /button returned:([^,\r\n]*)/.exec(stdout7);
|
|
4511
|
+
if (!m) return "error";
|
|
4512
|
+
const b = m[1].trim();
|
|
4513
|
+
if (b === install) return "install";
|
|
4514
|
+
if (b === later) return "later";
|
|
4515
|
+
return "error";
|
|
4516
|
+
}
|
|
4517
|
+
function psLiteral(s2) {
|
|
4518
|
+
if (/[\0\r\n]/.test(s2)) throw new Error("\u5F39\u6846\u6587\u5B57\u4E0D\u5408\u6CD5");
|
|
4519
|
+
return `'${s2.replace(/['‘-‛]/g, (c) => c + c)}'`;
|
|
4520
|
+
}
|
|
4521
|
+
function psMultiline(s2) {
|
|
4522
|
+
return s2.split("\n").map((l) => psLiteral(l)).join(" + [char]10 + ");
|
|
4523
|
+
}
|
|
4524
|
+
function winDialogScript(text, title, later, install, timeoutS = DIALOG_TIMEOUT_S) {
|
|
4525
|
+
checkTimeout(timeoutS);
|
|
4526
|
+
return [
|
|
4527
|
+
"$ErrorActionPreference = 'Stop'",
|
|
4528
|
+
"[Console]::OutputEncoding = [Text.Encoding]::UTF8",
|
|
4529
|
+
"Add-Type -AssemblyName System.Windows.Forms",
|
|
4530
|
+
"Add-Type -AssemblyName System.Drawing",
|
|
4531
|
+
"[System.Windows.Forms.Application]::EnableVisualStyles()",
|
|
4532
|
+
"$f = New-Object System.Windows.Forms.Form",
|
|
4533
|
+
`$f.Text = ${psLiteral(title)}`,
|
|
4534
|
+
"$f.TopMost = $true",
|
|
4535
|
+
"$f.StartPosition = 'CenterScreen'",
|
|
4536
|
+
"$f.FormBorderStyle = 'FixedDialog'",
|
|
4537
|
+
"$f.MaximizeBox = $false",
|
|
4538
|
+
"$f.MinimizeBox = $false",
|
|
4539
|
+
"$f.ClientSize = New-Object System.Drawing.Size(480, 230)",
|
|
4540
|
+
"$l = New-Object System.Windows.Forms.Label",
|
|
4541
|
+
`$l.Text = ${psMultiline(text)}`,
|
|
4542
|
+
"$l.SetBounds(16, 14, 448, 160)",
|
|
4543
|
+
"$f.Controls.Add($l)",
|
|
4544
|
+
"$no = New-Object System.Windows.Forms.Button",
|
|
4545
|
+
`$no.Text = ${psLiteral(later)}`,
|
|
4546
|
+
"$no.SetBounds(270, 186, 90, 30)",
|
|
4547
|
+
"$no.DialogResult = 'No'",
|
|
4548
|
+
"$yes = New-Object System.Windows.Forms.Button",
|
|
4549
|
+
`$yes.Text = ${psLiteral(install)}`,
|
|
4550
|
+
"$yes.SetBounds(374, 186, 90, 30)",
|
|
4551
|
+
"$yes.DialogResult = 'Yes'",
|
|
4552
|
+
"$f.Controls.Add($no)",
|
|
4553
|
+
"$f.Controls.Add($yes)",
|
|
4554
|
+
"$t = New-Object System.Windows.Forms.Timer",
|
|
4555
|
+
`$t.Interval = ${timeoutS * 1e3}`,
|
|
4556
|
+
"$t.Add_Tick({ $t.Stop(); $f.Tag = 'timeout'; $f.Close() })",
|
|
4557
|
+
"$t.Start()",
|
|
4558
|
+
"$r = $f.ShowDialog()",
|
|
4559
|
+
"$t.Stop()",
|
|
4560
|
+
"if ($f.Tag -eq 'timeout') { 'timeout' } elseif ($r -eq 'Yes') { 'install' } elseif ($r -eq 'No') { 'later' } else { 'timeout' }"
|
|
4561
|
+
].join("\r\n");
|
|
4562
|
+
}
|
|
4563
|
+
function winDialogArgs(script) {
|
|
4564
|
+
return ["-NoProfile", "-NonInteractive", "-Sta", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encodePs(script)];
|
|
4565
|
+
}
|
|
4566
|
+
function parseWinDialog(stdout7) {
|
|
4567
|
+
const last = stdout7.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).pop();
|
|
4568
|
+
return last === "install" || last === "later" || last === "timeout" ? last : "error";
|
|
4569
|
+
}
|
|
4570
|
+
async function askDialog(platform, run2, timeoutS = DIALOG_TIMEOUT_S) {
|
|
4571
|
+
const timeoutMs = timeoutS * 1e3 + DIALOG_PROC_SLACK_MS;
|
|
4572
|
+
if (platform === "darwin") {
|
|
4573
|
+
const r = await run2("osascript", macDialogArgs(DIALOG_TEXT, DIALOG_TITLE, BTN_LATER, BTN_INSTALL, timeoutS), { timeoutMs });
|
|
4574
|
+
if (r.timedOut) return "timeout";
|
|
4575
|
+
return parseMacDialog(r.stdout);
|
|
4576
|
+
}
|
|
4577
|
+
if (platform === "win32") {
|
|
4578
|
+
const r = await run2("powershell.exe", winDialogArgs(winDialogScript(DIALOG_TEXT, DIALOG_TITLE, BTN_LATER, BTN_INSTALL, timeoutS)), { timeoutMs });
|
|
4579
|
+
if (r.timedOut) return "timeout";
|
|
4580
|
+
return parseWinDialog(r.stdout);
|
|
4581
|
+
}
|
|
4582
|
+
return "error";
|
|
4583
|
+
}
|
|
4584
|
+
function npmPlan(d) {
|
|
4585
|
+
if ((d.platform ?? process.platform) === "win32") {
|
|
4586
|
+
for (const f of whichAllIn("npm", d)) {
|
|
4587
|
+
if (!/\.cmd$/i.test(f)) continue;
|
|
4588
|
+
const dir = dirname14(f);
|
|
4589
|
+
const cli = join20(dir, "node_modules", "npm", "bin", "npm-cli.js");
|
|
4590
|
+
if (!d.exists(cli)) continue;
|
|
4591
|
+
const node = join20(dir, "node.exe");
|
|
4592
|
+
return { file: d.exists(node) ? node : d.nodePath, args: [cli, ...NPM_INSTALL_ARGS] };
|
|
4593
|
+
}
|
|
4594
|
+
return null;
|
|
4595
|
+
}
|
|
4596
|
+
const npm = whichIn("npm", d);
|
|
4597
|
+
return npm ? { file: npm, args: [...NPM_INSTALL_ARGS] } : null;
|
|
4598
|
+
}
|
|
4599
|
+
function manualLoginCommand(platform) {
|
|
4600
|
+
return platform === "win32" ? '$env:CODEBUDDY_CONFIG_DIR="$HOME\\.workbuddy"; codebuddy' : "CODEBUDDY_CONFIG_DIR=~/.workbuddy codebuddy";
|
|
4601
|
+
}
|
|
4602
|
+
function manualHint(platform) {
|
|
4603
|
+
return `\u8BF7\u5728\u7535\u8111\u7EC8\u7AEF\u8FD0\u884C npm i -g ${WORKBUDDY_STANDALONE_PKG}\uFF0C\u88C5\u597D\u540E\u8FD0\u884C ${manualLoginCommand(platform)}\uFF0C\u8F93\u5165 /login \u9009\u56FD\u5185\u7248\u767B\u5F55`;
|
|
4604
|
+
}
|
|
4605
|
+
function tailOf(s2) {
|
|
4606
|
+
const t = s2.replace(/\s+/g, " ").trim();
|
|
4607
|
+
return t.length > 200 ? "\u2026" + t.slice(-200) : t;
|
|
4608
|
+
}
|
|
4609
|
+
async function installStandalone(d) {
|
|
4610
|
+
const plan = d.plan();
|
|
4611
|
+
if (!plan) return { ok: false, reason: "\u627E\u4E0D\u5230 npm\uFF08\u6CA1\u88C5 Node.js\uFF0C\u6216\u88C5\u6CD5\u8BA4\u4E0D\u51FA\uFF09" };
|
|
4612
|
+
const r = await d.run(plan.file, plan.args, { env: d.env(), timeoutMs: NPM_TIMEOUT_MS });
|
|
4613
|
+
if (r.spawnError) return { ok: false, reason: "npm \u8D77\u4E0D\u6765" };
|
|
4614
|
+
if (r.timedOut) return { ok: false, reason: `npm \u5B89\u88C5\u8D85\u8FC7 ${Math.round(NPM_TIMEOUT_MS / 6e4)} \u5206\u949F\u6CA1\u5B8C\u6210` };
|
|
4615
|
+
if (r.code !== 0) {
|
|
4616
|
+
const text = `${r.stderr}
|
|
4617
|
+
${r.stdout}`;
|
|
4618
|
+
if (/\bEACCES\b|permission denied/i.test(text)) {
|
|
4619
|
+
return { ok: false, reason: "npm \u5168\u5C40\u76EE\u5F55\u6CA1\u6709\u5199\u6743\u9650\uFF08\u6309 npm \u6587\u6863\u6539 prefix\uFF0C\u6216\u5728\u7EC8\u7AEF\u91CC\u7528 sudo \u88C5\uFF09" };
|
|
4620
|
+
}
|
|
4621
|
+
return { ok: false, reason: `npm \u9000\u51FA\u7801 ${r.code ?? "\u65E0"}${tailOf(r.stderr) ? `\uFF1A${tailOf(r.stderr)}` : ""}` };
|
|
4622
|
+
}
|
|
4623
|
+
if (!d.located()) return { ok: false, reason: "npm \u8BF4\u88C5\u597D\u4E86\uFF0C\u4F46\u5728\u5E38\u89C1\u76EE\u5F55\u91CC\u627E\u4E0D\u5230 codebuddy\uFF08npm \u5168\u5C40\u76EE\u5F55\u4E0D\u5728 PATH \u4E0A\uFF09" };
|
|
4624
|
+
return { ok: true };
|
|
4625
|
+
}
|
|
4626
|
+
var isDaemonSource = (s2) => s2 === "daemon" || s2 === "runtime";
|
|
4627
|
+
async function runOffer(source, d, opts = {}) {
|
|
4628
|
+
if (isDaemonSource(source)) {
|
|
4629
|
+
const t = d.tool();
|
|
4630
|
+
if (!t.enabled || t.disabled) return "no_app";
|
|
4631
|
+
}
|
|
4632
|
+
const cfg = d.setup();
|
|
4633
|
+
if (source !== "manual" && cfg.declined) return "declined";
|
|
4634
|
+
if (isDaemonSource(source) && cfg.askedAt !== void 0) {
|
|
4635
|
+
const since = d.now() - cfg.askedAt;
|
|
4636
|
+
if (since >= 0 && since < (d.throttleMs ?? ASK_THROTTLE_MS)) return "throttled";
|
|
4637
|
+
}
|
|
4638
|
+
const st = d.state();
|
|
4639
|
+
if (st.standalone) return "has_standalone";
|
|
4640
|
+
if (!st.app && source !== "manual") return "no_app";
|
|
4641
|
+
if (!opts.skipAsk) {
|
|
4642
|
+
let gui = false;
|
|
4643
|
+
try {
|
|
4644
|
+
gui = d.gui();
|
|
4645
|
+
} catch {
|
|
4646
|
+
gui = false;
|
|
4647
|
+
}
|
|
4648
|
+
if (!gui) {
|
|
4649
|
+
d.saveSetup({ askedAt: d.now() });
|
|
4650
|
+
d.log(`WorkBuddy \u6CA1\u6709\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF08${LIMITED_NOTE}\uFF09\uFF0C\u6CA1\u6709\u56FE\u5F62\u4F1A\u8BDD\u3001\u4E0D\u5F39\u6846\uFF1A${manualHint(d.platform)}`);
|
|
4651
|
+
return "no_gui";
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
const release2 = d.lock();
|
|
4655
|
+
if (!release2) return "busy";
|
|
4656
|
+
try {
|
|
4657
|
+
if (!opts.skipAsk) {
|
|
4658
|
+
d.saveSetup({ askedAt: d.now() });
|
|
4659
|
+
let ans;
|
|
4660
|
+
try {
|
|
4661
|
+
ans = await d.ask();
|
|
4662
|
+
} catch {
|
|
4663
|
+
ans = "error";
|
|
4664
|
+
}
|
|
4665
|
+
if (ans === "later") {
|
|
4666
|
+
d.saveSetup({ declined: true });
|
|
4667
|
+
d.log("\u7528\u6237\u9009\u4E86\u300C\u6682\u4E0D\u300D\u5B89\u88C5 WorkBuddy \u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF0C\u4EE5\u540E\u4E0D\u518D\u95EE\uFF08deskbuddy workbuddy-setup \u53EF\u518D\u88C5\uFF09");
|
|
4668
|
+
return "later";
|
|
4669
|
+
}
|
|
4670
|
+
if (ans !== "install") {
|
|
4671
|
+
d.log(`WorkBuddy \u72EC\u7ACB\u7248\u8BE2\u95EE${ans === "timeout" ? "\u8D85\u65F6\u6CA1\u4EBA\u70B9" : "\u6CA1\u5F39\u51FA\u6765"}\uFF0C\u6309\u300C\u6682\u4E0D\u300D\u5904\u7406\u4F46\u4E0D\u8BB0\u4F4F\uFF0C\u4E4B\u540E\u8FD8\u4F1A\u518D\u95EE`);
|
|
4672
|
+
return "timeout";
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
d.log(`\u5F00\u59CB\u5B89\u88C5 WorkBuddy \u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF08npm i -g ${WORKBUDDY_STANDALONE_PKG}\uFF09`);
|
|
4676
|
+
let r;
|
|
4677
|
+
try {
|
|
4678
|
+
r = await d.install();
|
|
4679
|
+
} catch (e) {
|
|
4680
|
+
r = { ok: false, reason: e?.message ?? String(e) };
|
|
4681
|
+
}
|
|
4682
|
+
if (!r.ok) {
|
|
4683
|
+
d.log(`WorkBuddy \u72EC\u7ACB\u7248\u547D\u4EE4\u884C\u6CA1\u88C5\u4E0A\uFF1A${r.reason}\u3002${manualHint(d.platform)}`);
|
|
4684
|
+
return "install_failed";
|
|
4685
|
+
}
|
|
4686
|
+
d.saveSetup({ declined: null });
|
|
4687
|
+
d.log("WorkBuddy \u72EC\u7ACB\u7248\u547D\u4EE4\u884C\u5DF2\u88C5\u597D");
|
|
4688
|
+
try {
|
|
4689
|
+
d.log(await d.openLogin());
|
|
4690
|
+
} catch (e) {
|
|
4691
|
+
d.log(`\u6CA1\u80FD\u6253\u5F00\u767B\u5F55\u7A97\u53E3\uFF08${e?.message ?? e}\uFF09\uFF1A\u8BF7\u5728\u7535\u8111\u7EC8\u7AEF\u8FD0\u884C ${manualLoginCommand(d.platform)}\uFF0C\u8F93\u5165 /login \u9009\u56FD\u5185\u7248`);
|
|
4692
|
+
}
|
|
4693
|
+
return "installed";
|
|
4694
|
+
} finally {
|
|
4695
|
+
try {
|
|
4696
|
+
release2();
|
|
4697
|
+
} catch {
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
var LOCK_TTL_MS = 15 * 60 * 1e3;
|
|
4702
|
+
function lockPath() {
|
|
4703
|
+
return join20(deskbuddyHome(), "workbuddy-setup.lock");
|
|
4704
|
+
}
|
|
4705
|
+
function readLock(path) {
|
|
4706
|
+
try {
|
|
4707
|
+
const o = JSON.parse(readFileSync17(path, "utf8"));
|
|
4708
|
+
return typeof o.pid === "number" && typeof o.at === "number" ? { pid: o.pid, at: o.at } : null;
|
|
4709
|
+
} catch {
|
|
4710
|
+
return null;
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
function lockHeld(d) {
|
|
4714
|
+
if (!existsSync17(d.path)) return false;
|
|
4715
|
+
const cur = readLock(d.path);
|
|
4716
|
+
if (!cur) return true;
|
|
4717
|
+
if (d.now() - cur.at > LOCK_TTL_MS || cur.at - d.now() > LOCK_TTL_MS) return false;
|
|
4718
|
+
return d.alive ? d.alive(cur.pid) : true;
|
|
4719
|
+
}
|
|
4720
|
+
function acquireLock(d) {
|
|
4721
|
+
for (let i = 0; i < 2; i++) {
|
|
4722
|
+
const mine = { pid: d.pid, at: d.now() };
|
|
4723
|
+
try {
|
|
4724
|
+
mkdirSync16(dirname14(d.path), { recursive: true });
|
|
4725
|
+
writeFileSync15(d.path, JSON.stringify(mine), { flag: "wx", mode: 384 });
|
|
4726
|
+
return () => {
|
|
4727
|
+
const cur = readLock(d.path);
|
|
4728
|
+
if (cur && cur.pid === mine.pid && cur.at === mine.at) rmSync9(d.path, { force: true });
|
|
4729
|
+
};
|
|
4730
|
+
} catch (e) {
|
|
4731
|
+
if (e?.code !== "EEXIST") return null;
|
|
4732
|
+
if (i > 0) return null;
|
|
4733
|
+
let stale = !lockHeld(d);
|
|
4734
|
+
if (!stale && !readLock(d.path)) {
|
|
4735
|
+
try {
|
|
4736
|
+
stale = d.now() - statSync6(d.path).mtimeMs > LOCK_TTL_MS;
|
|
4737
|
+
} catch {
|
|
4738
|
+
stale = true;
|
|
4739
|
+
}
|
|
4740
|
+
}
|
|
4741
|
+
if (!stale) return null;
|
|
4742
|
+
try {
|
|
4743
|
+
rmSync9(d.path, { force: true });
|
|
4744
|
+
} catch {
|
|
4745
|
+
return null;
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
return null;
|
|
4750
|
+
}
|
|
4751
|
+
function pidAlive2(pid) {
|
|
4752
|
+
try {
|
|
4753
|
+
process.kill(pid, 0);
|
|
4754
|
+
return true;
|
|
4755
|
+
} catch (e) {
|
|
4756
|
+
return e?.code === "EPERM";
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4759
|
+
var defaultLockDeps = () => ({ path: lockPath(), pid: process.pid, now: () => Date.now(), alive: pidAlive2 });
|
|
4760
|
+
var WorkbuddyOffer = class {
|
|
4761
|
+
#d;
|
|
4762
|
+
#busy = false;
|
|
4763
|
+
constructor(d) {
|
|
4764
|
+
this.#d = d;
|
|
4765
|
+
}
|
|
4766
|
+
async maybeAsk(source) {
|
|
4767
|
+
if (this.#busy) return "busy";
|
|
4768
|
+
this.#busy = true;
|
|
4769
|
+
try {
|
|
4770
|
+
return await runOffer(source, this.#d);
|
|
4771
|
+
} catch (e) {
|
|
4772
|
+
this.#d.log(`WorkBuddy \u72EC\u7ACB\u7248\u8BE2\u95EE\u51FA\u9519\uFF1A${e?.message ?? e}`);
|
|
4773
|
+
return "timeout";
|
|
4774
|
+
} finally {
|
|
4775
|
+
this.#busy = false;
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4778
|
+
};
|
|
4779
|
+
function workbuddyState(d = defaultLocateDeps()) {
|
|
4780
|
+
if (locateWorkbuddyStandalone(d) != null) return { standalone: true, app: false };
|
|
4781
|
+
return { standalone: false, app: locateWorkbuddyApp(d) != null };
|
|
4782
|
+
}
|
|
4783
|
+
function daemonLocateDeps() {
|
|
4784
|
+
return { ...defaultLocateDeps(), winAppHints: void 0 };
|
|
4785
|
+
}
|
|
4786
|
+
function readTool() {
|
|
4787
|
+
const cfg = loadConfig();
|
|
4788
|
+
return { enabled: cfg.enabledTools.includes("workbuddy"), disabled: (cfg.disabledTools ?? []).includes("workbuddy") };
|
|
4789
|
+
}
|
|
4790
|
+
function readSetup() {
|
|
4791
|
+
return { ...loadConfig().workbuddySetup ?? {} };
|
|
4792
|
+
}
|
|
4793
|
+
function writeSetup(patch) {
|
|
4794
|
+
const cfg = loadConfig();
|
|
4795
|
+
const next = { ...cfg.workbuddySetup ?? {} };
|
|
4796
|
+
if (patch.declined === null) delete next.declined;
|
|
4797
|
+
else if (patch.declined !== void 0) next.declined = patch.declined;
|
|
4798
|
+
if (patch.askedAt !== void 0) next.askedAt = patch.askedAt;
|
|
4799
|
+
cfg.workbuddySetup = Object.keys(next).length > 0 ? next : void 0;
|
|
4800
|
+
saveConfig(cfg);
|
|
4801
|
+
}
|
|
4802
|
+
async function defaultOpenLogin(platform = process.platform) {
|
|
4803
|
+
const cmd = loginCommandFor("workbuddy", defaultCommandDeps());
|
|
4804
|
+
if ("error" in cmd) return `\u72EC\u7ACB\u7248\u88C5\u597D\u4E86\uFF0C\u4F46\u6CA1\u627E\u5230\u80FD\u5F00\u767B\u5F55\u7A97\u53E3\u7684\u547D\u4EE4\u884C\uFF1A\u8BF7\u5728\u7535\u8111\u7EC8\u7AEF\u8FD0\u884C ${manualLoginCommand(platform)}\uFF0C\u8F93\u5165 /login \u9009\u56FD\u5185\u7248`;
|
|
4805
|
+
if (!guiAvailable(defaultGuiDeps())) return `\u6CA1\u6709\u56FE\u5F62\u4F1A\u8BDD\uFF0C\u6CA1\u6253\u5F00\u767B\u5F55\u7A97\u53E3\uFF1A\u8BF7\u5728\u7535\u8111\u7EC8\u7AEF\u8FD0\u884C ${manualCommand(cmd, platform)}\uFF0C\u8F93\u5165 /login \u9009\u56FD\u5185\u7248`;
|
|
4806
|
+
await openLoginWindow(cmd, defaultLoginOpenDeps());
|
|
4807
|
+
return `\u5DF2\u6253\u5F00 WorkBuddy \u767B\u5F55\u7A97\u53E3${WORKBUDDY_LOGIN_HINT}`;
|
|
4808
|
+
}
|
|
4809
|
+
function defaultOfferDeps(log2) {
|
|
4810
|
+
const platform = process.platform;
|
|
4811
|
+
return {
|
|
4812
|
+
state: () => workbuddyState(daemonLocateDeps()),
|
|
4813
|
+
tool: readTool,
|
|
4814
|
+
setup: readSetup,
|
|
4815
|
+
saveSetup: writeSetup,
|
|
4816
|
+
lock: () => acquireLock(defaultLockDeps()),
|
|
4817
|
+
gui: () => guiAvailable(defaultGuiDeps()),
|
|
4818
|
+
ask: () => askDialog(platform, defaultRunner),
|
|
4819
|
+
install: () => installStandalone({
|
|
4820
|
+
plan: () => npmPlan(defaultLocateDeps()),
|
|
4821
|
+
run: defaultRunner,
|
|
4822
|
+
env: () => mergeEnv(process.env, { PATH: childPath() }, platform),
|
|
4823
|
+
located: () => locateWorkbuddyStandalone() != null
|
|
4824
|
+
}),
|
|
4825
|
+
openLogin: () => defaultOpenLogin(platform),
|
|
4826
|
+
log: (l) => log2(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`),
|
|
4827
|
+
now: () => Date.now(),
|
|
4828
|
+
platform
|
|
4829
|
+
};
|
|
4830
|
+
}
|
|
4831
|
+
function offerDisabled(env) {
|
|
4832
|
+
return !!env.VITEST || env.DESKBUDDY_SKIP_WORKBUDDY_SETUP === "1";
|
|
4833
|
+
}
|
|
4834
|
+
var offerSingleton = null;
|
|
4835
|
+
function defaultWorkbuddyOffer(log2, env = process.env) {
|
|
4836
|
+
if (offerDisabled(env)) return null;
|
|
4837
|
+
if (!offerSingleton) offerSingleton = new WorkbuddyOffer(defaultOfferDeps(log2));
|
|
4838
|
+
return offerSingleton;
|
|
4839
|
+
}
|
|
4840
|
+
var PROMPT_DEDUP_MS = DIALOG_TIMEOUT_S * 1e3 + 60 * 1e3;
|
|
4841
|
+
function startStandalonePrompt(source, out, d) {
|
|
4842
|
+
let st;
|
|
4843
|
+
try {
|
|
4844
|
+
st = d.state();
|
|
4845
|
+
} catch {
|
|
4846
|
+
return "none";
|
|
4847
|
+
}
|
|
4848
|
+
if (st.standalone || !st.app) return "none";
|
|
4849
|
+
out("");
|
|
4850
|
+
if (d.setup().declined) {
|
|
4851
|
+
out(`\xB7 WorkBuddy\uFF1A\u53EA\u88C5\u4E86 App\uFF0C\u4E4B\u524D\u9009\u8FC7\u6682\u4E0D\u5B89\u88C5\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF08${LIMITED_NOTE}\uFF09\uFF1B\u9700\u8981\u65F6\u8FD0\u884C deskbuddy workbuddy-setup`);
|
|
4852
|
+
return "declined";
|
|
4853
|
+
}
|
|
4854
|
+
const askedAt = d.setup().askedAt;
|
|
4855
|
+
const recent = askedAt !== void 0 && d.now() - askedAt >= 0 && d.now() - askedAt < PROMPT_DEDUP_MS;
|
|
4856
|
+
let held = false;
|
|
4857
|
+
try {
|
|
4858
|
+
held = d.locked();
|
|
4859
|
+
} catch {
|
|
4860
|
+
held = false;
|
|
4861
|
+
}
|
|
4862
|
+
if (held || recent) {
|
|
4863
|
+
out(`WorkBuddy\uFF1A\u53EA\u88C5\u4E86 App\uFF0C\u6CA1\u6709\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF1B\u7535\u8111\u4E0A\u5DF2\u7ECF\u5728\u95EE\u8981\u4E0D\u8981\u5B89\u88C5\uFF08\u4E0D\u88C5${LIMITED_NOTE}\uFF09`);
|
|
4864
|
+
return "pending";
|
|
4865
|
+
}
|
|
4866
|
+
let gui = false;
|
|
4867
|
+
try {
|
|
4868
|
+
gui = d.gui();
|
|
4869
|
+
} catch {
|
|
4870
|
+
gui = false;
|
|
4871
|
+
}
|
|
4872
|
+
if (!gui) {
|
|
4873
|
+
out(`\u26A0\uFE0F WorkBuddy\uFF1A\u53EA\u88C5\u4E86 App\uFF0C\u6CA1\u6709\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF08${LIMITED_NOTE}\uFF09\u3002\u6CA1\u6709\u56FE\u5F62\u4F1A\u8BDD\uFF0C\u6CA1\u5F39\u8BE2\u95EE\u6846\uFF1A${manualHint(d.platform)}`);
|
|
4874
|
+
return "no_gui";
|
|
4875
|
+
}
|
|
4876
|
+
try {
|
|
4877
|
+
d.saveSetup({ askedAt: d.now() });
|
|
4878
|
+
d.spawnChild(source);
|
|
4879
|
+
} catch (e) {
|
|
4880
|
+
out(`\u26A0\uFE0F WorkBuddy\uFF1A\u53EA\u88C5\u4E86 App\uFF0C\u6CA1\u6709\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF08${LIMITED_NOTE}\uFF09\u3002\u6CA1\u80FD\u5F39\u51FA\u8BE2\u95EE\u6846\uFF08${e?.message ?? e}\uFF09\uFF1A${manualHint(d.platform)}`);
|
|
4881
|
+
return "spawn_failed";
|
|
4882
|
+
}
|
|
4883
|
+
out(
|
|
4884
|
+
`WorkBuddy\uFF1A\u53EA\u88C5\u4E86 App\uFF0C\u6CA1\u6709\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\u3002\u5DF2\u5728\u7535\u8111\u4E0A\u5F39\u51FA\u8BE2\u95EE\u6846\uFF08${Math.round(DIALOG_TIMEOUT_S / 60)} \u5206\u949F\u5185\u9009\u62E9\uFF09\uFF1A\u88C5\u4E86\u53EF\u4EE5\u5728\u8BBE\u5907\u4E0A\u76F4\u63A5\u65B0\u5EFA / \u56DE\u590D WorkBuddy \u4EFB\u52A1\uFF0C\u4E0D\u88C5${LIMITED_NOTE}`
|
|
4885
|
+
);
|
|
4886
|
+
return "asked";
|
|
4887
|
+
}
|
|
4888
|
+
function promptSummaryLine(r, platform = process.platform) {
|
|
4889
|
+
switch (r) {
|
|
4890
|
+
case "asked":
|
|
4891
|
+
case "pending":
|
|
4892
|
+
return "WorkBuddy \u72EC\u7ACB\u7248\u547D\u4EE4\u884C: \u672A\u5B89\u88C5\uFF0C\u5DF2\u5728\u7535\u8111\u4E0A\u5F39\u7A97\u8BE2\u95EE\u662F\u5426\u5B89\u88C5\uFF08\u88C5\u4E86\u624D\u80FD\u5728\u8BBE\u5907\u4E0A\u65B0\u5EFA / \u56DE\u590D WorkBuddy \u4EFB\u52A1\uFF09";
|
|
4893
|
+
case "declined":
|
|
4894
|
+
return `WorkBuddy \u72EC\u7ACB\u7248\u547D\u4EE4\u884C: \u672A\u5B89\u88C5\uFF08\u4E4B\u524D\u9009\u8FC7\u6682\u4E0D\uFF0C${LIMITED_NOTE}\uFF1B\u8FD0\u884C deskbuddy workbuddy-setup \u53EF\u518D\u88C5\uFF09`;
|
|
4895
|
+
case "no_gui":
|
|
4896
|
+
case "spawn_failed":
|
|
4897
|
+
return `WorkBuddy \u72EC\u7ACB\u7248\u547D\u4EE4\u884C: \u672A\u5B89\u88C5\uFF08${LIMITED_NOTE}\uFF09\u3002${manualHint(platform)}`;
|
|
4898
|
+
default:
|
|
4899
|
+
return "";
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
function defaultPromptStartDeps(env = process.env) {
|
|
4903
|
+
if (offerDisabled(env)) return null;
|
|
4904
|
+
return {
|
|
4905
|
+
state: () => workbuddyState(),
|
|
4906
|
+
setup: readSetup,
|
|
4907
|
+
saveSetup: writeSetup,
|
|
4908
|
+
locked: () => lockHeld(defaultLockDeps()),
|
|
4909
|
+
gui: () => guiAvailable(defaultGuiDeps()),
|
|
4910
|
+
spawnChild: (source) => {
|
|
4911
|
+
const cli = process.argv[1];
|
|
4912
|
+
if (!cli || !existsSync17(cli)) throw new Error("\u627E\u4E0D\u5230 deskbuddy \u81EA\u5DF1\u7684\u5165\u53E3\u6587\u4EF6");
|
|
4913
|
+
const c = spawn4(process.execPath, [cli, "workbuddy-setup", "--from", source], {
|
|
4914
|
+
detached: true,
|
|
4915
|
+
stdio: "ignore",
|
|
4916
|
+
windowsHide: true,
|
|
4917
|
+
env: process.env
|
|
4918
|
+
});
|
|
4919
|
+
c.on("error", () => {
|
|
4920
|
+
});
|
|
4921
|
+
c.unref();
|
|
4922
|
+
},
|
|
4923
|
+
now: () => Date.now(),
|
|
4924
|
+
platform: process.platform
|
|
4925
|
+
};
|
|
4926
|
+
}
|
|
4927
|
+
|
|
4371
4928
|
// src/commands/enable.ts
|
|
4372
4929
|
function stdout(line) {
|
|
4373
4930
|
process.stdout.write(line + "\n");
|
|
@@ -4432,14 +4989,14 @@ function installTools(tools, out, shim = defaultShimEnvForInstall(), opts = {})
|
|
|
4432
4989
|
installed.push(tool);
|
|
4433
4990
|
out(`\u2705 ${tool}\uFF1Ahook \u5DF2\u88C5\u8FDB ${configPath2}\uFF08${events.length} \u4E2A\u4E8B\u4EF6\uFF09`);
|
|
4434
4991
|
try {
|
|
4435
|
-
if (adapter.installWindowGate?.(configPath2,
|
|
4992
|
+
if (adapter.installWindowGate?.(configPath2, join21(dirname15(hookPath), "gate.cjs"), tool)) {
|
|
4436
4993
|
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`);
|
|
4437
4994
|
}
|
|
4438
4995
|
} catch (e) {
|
|
4439
4996
|
out(`\u26A0\uFE0F ${tool}\uFF1A\u8BBE\u5907\u786E\u8BA4\u6388\u6743\u6CA1\u88C5\u4E0A\uFF1A${e.message}`);
|
|
4440
4997
|
}
|
|
4441
4998
|
try {
|
|
4442
|
-
if (adapter.installRewake?.(configPath2,
|
|
4999
|
+
if (adapter.installRewake?.(configPath2, join21(dirname15(hookPath), "wake.cjs"), tool)) {
|
|
4443
5000
|
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`);
|
|
4444
5001
|
}
|
|
4445
5002
|
} catch (e) {
|
|
@@ -4469,6 +5026,14 @@ async function runEnable(tools, opts = {}) {
|
|
|
4469
5026
|
ensureClientId();
|
|
4470
5027
|
if (!opts.skipRefresh) await refreshRegistry();
|
|
4471
5028
|
const { failed } = installTools(tools, out, opts.shim === void 0 ? defaultShimEnvForInstall() : opts.shim);
|
|
5029
|
+
if (!opts.skipLoginCheck && loadConfig().enabledTools.includes("workbuddy")) {
|
|
5030
|
+
const wb = opts.workbuddyPrompt === void 0 ? defaultPromptStartDeps() : opts.workbuddyPrompt;
|
|
5031
|
+
try {
|
|
5032
|
+
if (wb) startStandalonePrompt("enable", out, wb);
|
|
5033
|
+
} catch (e) {
|
|
5034
|
+
out(`\xB7 WorkBuddy \u72EC\u7ACB\u7248\u68C0\u67E5\u51FA\u9519\uFF1A${e?.message ?? e}\uFF08\u4E0D\u5F71\u54CD\u5B89\u88C5\uFF09`);
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
4472
5037
|
if (!opts.skipAutostart) {
|
|
4473
5038
|
out("");
|
|
4474
5039
|
const target = autostartTarget(out);
|
|
@@ -4547,15 +5112,29 @@ var AutoEnabler = class {
|
|
|
4547
5112
|
}
|
|
4548
5113
|
/** 跑一轮。返回这轮新接入的工具(测试用) */
|
|
4549
5114
|
tick() {
|
|
5115
|
+
let bound = false;
|
|
5116
|
+
try {
|
|
5117
|
+
return this.#tick((b) => bound = b);
|
|
5118
|
+
} finally {
|
|
5119
|
+
if (bound) {
|
|
5120
|
+
try {
|
|
5121
|
+
this.#d.onTick?.();
|
|
5122
|
+
} catch {
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
#tick(setBound) {
|
|
4550
5128
|
try {
|
|
4551
5129
|
const cfg = this.#d.config();
|
|
4552
5130
|
if (!cfg.boundAt) return [];
|
|
5131
|
+
setBound(true);
|
|
4553
5132
|
const ok = this.#d.installable ?? (() => true);
|
|
4554
5133
|
const candidates = autoEnableCandidates(this.#d.detect(), cfg).filter((t) => ok(t));
|
|
4555
5134
|
for (const t of [...this.#failed.keys()]) if (!candidates.includes(t)) this.#failed.delete(t);
|
|
4556
5135
|
if (candidates.length === 0) return [];
|
|
4557
|
-
const
|
|
4558
|
-
const r = this.#d.install(candidates, (l) =>
|
|
5136
|
+
const lines3 = [];
|
|
5137
|
+
const r = this.#d.install(candidates, (l) => lines3.push(l));
|
|
4559
5138
|
this.#lastError = "";
|
|
4560
5139
|
if (r.installed.length > 0) {
|
|
4561
5140
|
this.#d.log(`[\u81EA\u52A8\u63A5\u5165] \u53D1\u73B0\u65B0\u88C5\u7684\u667A\u80FD\u4F53\u5E76\u5DF2\u63A5\u5165\uFF1A${r.installed.join(", ")}`);
|
|
@@ -4582,13 +5161,13 @@ var AutoEnabler = class {
|
|
|
4582
5161
|
|
|
4583
5162
|
// src/daemon/index.ts
|
|
4584
5163
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
4585
|
-
import { existsSync as
|
|
4586
|
-
import { basename as
|
|
5164
|
+
import { existsSync as existsSync25, statSync as statSync17 } from "fs";
|
|
5165
|
+
import { basename as basename12, dirname as dirname24, join as join41 } from "path";
|
|
4587
5166
|
import WebSocket2 from "ws";
|
|
4588
5167
|
|
|
4589
5168
|
// src/core/state.ts
|
|
4590
|
-
import { existsSync as
|
|
4591
|
-
import { join as
|
|
5169
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync18, readdirSync as readdirSync6, readFileSync as readFileSync19, renameSync as renameSync11, rmSync as rmSync11, writeFileSync as writeFileSync17 } from "fs";
|
|
5170
|
+
import { join as join23 } from "path";
|
|
4592
5171
|
|
|
4593
5172
|
// src/core/process.ts
|
|
4594
5173
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
@@ -4697,29 +5276,29 @@ function programOf(args) {
|
|
|
4697
5276
|
}
|
|
4698
5277
|
|
|
4699
5278
|
// src/core/ended.ts
|
|
4700
|
-
import { closeSync as closeSync4, mkdirSync as
|
|
4701
|
-
import { join as
|
|
5279
|
+
import { closeSync as closeSync4, mkdirSync as mkdirSync17, openSync as openSync4, readdirSync as readdirSync5, readFileSync as readFileSync18, readSync as readSync3, realpathSync as realpathSync2, renameSync as renameSync10, rmSync as rmSync10, statSync as statSync7, writeFileSync as writeFileSync16 } from "fs";
|
|
5280
|
+
import { join as join22 } from "path";
|
|
4702
5281
|
var ENDED_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
4703
5282
|
var SID_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
4704
5283
|
function endedDir(home = deskbuddyHome()) {
|
|
4705
|
-
return
|
|
5284
|
+
return join22(home, "ended");
|
|
4706
5285
|
}
|
|
4707
5286
|
function writeEnded(s2, now = Date.now()) {
|
|
4708
5287
|
if (!SID_RE.test(s2.sessionId)) return;
|
|
4709
5288
|
try {
|
|
4710
5289
|
const dir = endedDir();
|
|
4711
|
-
|
|
5290
|
+
mkdirSync17(dir, { recursive: true, mode: 448 });
|
|
4712
5291
|
const rec = { sessionId: s2.sessionId, tool: s2.tool, cwd: s2.cwd ?? "", interactive: s2.interactive === true, endedAt: now };
|
|
4713
5292
|
if (Number.isInteger(s2.turns) && s2.turns >= 0) rec.turns = s2.turns;
|
|
4714
5293
|
if (typeof s2.firstPrompt === "string" && s2.firstPrompt) rec.firstPrompt = s2.firstPrompt;
|
|
4715
|
-
const file =
|
|
5294
|
+
const file = join22(dir, `${s2.sessionId}.json`);
|
|
4716
5295
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
4717
|
-
|
|
5296
|
+
writeFileSync16(tmp, JSON.stringify(rec), { encoding: "utf8", mode: 384 });
|
|
4718
5297
|
renameSync10(tmp, file);
|
|
4719
5298
|
for (const n of readdirSync5(dir)) {
|
|
4720
|
-
const f =
|
|
5299
|
+
const f = join22(dir, n);
|
|
4721
5300
|
try {
|
|
4722
|
-
if (now -
|
|
5301
|
+
if (now - statSync7(f).mtimeMs > ENDED_TTL_MS) rmSync10(f, { force: true });
|
|
4723
5302
|
} catch {
|
|
4724
5303
|
}
|
|
4725
5304
|
}
|
|
@@ -4729,7 +5308,7 @@ function writeEnded(s2, now = Date.now()) {
|
|
|
4729
5308
|
function readEnded(sessionId, now = Date.now()) {
|
|
4730
5309
|
if (!SID_RE.test(sessionId)) return null;
|
|
4731
5310
|
try {
|
|
4732
|
-
const r = JSON.parse(
|
|
5311
|
+
const r = JSON.parse(readFileSync18(join22(endedDir(), `${sessionId}.json`), "utf8"));
|
|
4733
5312
|
if (!r || r.sessionId !== sessionId || typeof r.endedAt !== "number" || now - r.endedAt > ENDED_TTL_MS) return null;
|
|
4734
5313
|
return r;
|
|
4735
5314
|
} catch {
|
|
@@ -4744,7 +5323,7 @@ function projectDirName(cwd) {
|
|
|
4744
5323
|
function readChunk(file, fromEnd, max) {
|
|
4745
5324
|
let fd;
|
|
4746
5325
|
try {
|
|
4747
|
-
const size =
|
|
5326
|
+
const size = statSync7(file).size;
|
|
4748
5327
|
const len = Math.min(size, max);
|
|
4749
5328
|
const buf = Buffer.alloc(len);
|
|
4750
5329
|
fd = openSync4(file, "r");
|
|
@@ -4770,14 +5349,14 @@ function claudeTranscriptCwd(sessionId, home = claudeHome()) {
|
|
|
4770
5349
|
if (!SID_RE.test(sessionId)) return "";
|
|
4771
5350
|
let dirs = [];
|
|
4772
5351
|
try {
|
|
4773
|
-
dirs = readdirSync5(
|
|
5352
|
+
dirs = readdirSync5(join22(home, "projects"));
|
|
4774
5353
|
} catch {
|
|
4775
5354
|
return "";
|
|
4776
5355
|
}
|
|
4777
5356
|
for (const d of dirs) {
|
|
4778
|
-
const file =
|
|
5357
|
+
const file = join22(home, "projects", d, `${sessionId}.jsonl`);
|
|
4779
5358
|
try {
|
|
4780
|
-
|
|
5359
|
+
statSync7(file);
|
|
4781
5360
|
} catch {
|
|
4782
5361
|
continue;
|
|
4783
5362
|
}
|
|
@@ -4813,7 +5392,7 @@ function resolveEndedCwd(sessionId, tool, d) {
|
|
|
4813
5392
|
}
|
|
4814
5393
|
function isDirSync(p) {
|
|
4815
5394
|
try {
|
|
4816
|
-
return
|
|
5395
|
+
return statSync7(p).isDirectory();
|
|
4817
5396
|
} catch {
|
|
4818
5397
|
return false;
|
|
4819
5398
|
}
|
|
@@ -4821,23 +5400,23 @@ function isDirSync(p) {
|
|
|
4821
5400
|
|
|
4822
5401
|
// src/core/state.ts
|
|
4823
5402
|
function stateDir() {
|
|
4824
|
-
return
|
|
5403
|
+
return join23(deskbuddyHome(), "state");
|
|
4825
5404
|
}
|
|
4826
5405
|
function statePath(sessionId) {
|
|
4827
5406
|
const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
|
|
4828
|
-
return
|
|
5407
|
+
return join23(stateDir(), `${safe || "unknown"}.json`);
|
|
4829
5408
|
}
|
|
4830
5409
|
function readState(sessionId) {
|
|
4831
5410
|
try {
|
|
4832
|
-
return JSON.parse(
|
|
5411
|
+
return JSON.parse(readFileSync19(statePath(sessionId), "utf8"));
|
|
4833
5412
|
} catch {
|
|
4834
5413
|
return null;
|
|
4835
5414
|
}
|
|
4836
5415
|
}
|
|
4837
5416
|
function writeState(s2) {
|
|
4838
|
-
|
|
5417
|
+
mkdirSync18(stateDir(), { recursive: true });
|
|
4839
5418
|
const tmp = statePath(s2.sessionId) + ".tmp";
|
|
4840
|
-
|
|
5419
|
+
writeFileSync17(tmp, JSON.stringify(s2), "utf8");
|
|
4841
5420
|
renameSync11(tmp, statePath(s2.sessionId));
|
|
4842
5421
|
}
|
|
4843
5422
|
function retireState(s2, now = Date.now()) {
|
|
@@ -4846,7 +5425,7 @@ function retireState(s2, now = Date.now()) {
|
|
|
4846
5425
|
}
|
|
4847
5426
|
function removeState(sessionId) {
|
|
4848
5427
|
try {
|
|
4849
|
-
|
|
5428
|
+
rmSync11(statePath(sessionId), { force: true });
|
|
4850
5429
|
} catch {
|
|
4851
5430
|
}
|
|
4852
5431
|
}
|
|
@@ -4855,7 +5434,7 @@ var STALE_STATES = /* @__PURE__ */ new Set(["watch", "ask_approval"]);
|
|
|
4855
5434
|
var STALE_NO_PID_LOG = "\u65E0\u8FDB\u7A0B\u53F7\u4E14 2 \u5C0F\u65F6\u65E0\u4E8B\u4EF6\uFF0C\u6309\u5DF2\u7ED3\u675F\u5904\u7406";
|
|
4856
5435
|
function pendingApprovalSessions() {
|
|
4857
5436
|
try {
|
|
4858
|
-
const recs = JSON.parse(
|
|
5437
|
+
const recs = JSON.parse(readFileSync19(join23(deskbuddyHome(), "approvals.json"), "utf8"));
|
|
4859
5438
|
return new Set(Array.isArray(recs) ? recs.filter((r) => r && r.status === "pending" && typeof r.sessionId === "string").map((r) => r.sessionId) : []);
|
|
4860
5439
|
} catch {
|
|
4861
5440
|
return /* @__PURE__ */ new Set();
|
|
@@ -4873,7 +5452,7 @@ function dropManagedDaemonPid(s2, args = (pid) => processArgs(pid)) {
|
|
|
4873
5452
|
return next;
|
|
4874
5453
|
}
|
|
4875
5454
|
function readAliveStates(tool, opts = {}) {
|
|
4876
|
-
if (!
|
|
5455
|
+
if (!existsSync18(stateDir())) return [];
|
|
4877
5456
|
const now = opts.now ?? Date.now();
|
|
4878
5457
|
let pend = null;
|
|
4879
5458
|
const pending = () => pend ??= pendingApprovalSessions();
|
|
@@ -4882,9 +5461,9 @@ function readAliveStates(tool, opts = {}) {
|
|
|
4882
5461
|
if (!file.endsWith(".json")) continue;
|
|
4883
5462
|
let s2;
|
|
4884
5463
|
try {
|
|
4885
|
-
s2 = JSON.parse(
|
|
5464
|
+
s2 = JSON.parse(readFileSync19(join23(stateDir(), file), "utf8"));
|
|
4886
5465
|
} catch {
|
|
4887
|
-
|
|
5466
|
+
rmSync11(join23(stateDir(), file), { force: true });
|
|
4888
5467
|
continue;
|
|
4889
5468
|
}
|
|
4890
5469
|
if (s2.tool !== tool) continue;
|
|
@@ -4906,15 +5485,15 @@ function readAliveStates(tool, opts = {}) {
|
|
|
4906
5485
|
return alive;
|
|
4907
5486
|
}
|
|
4908
5487
|
function noticeDir() {
|
|
4909
|
-
return
|
|
5488
|
+
return join23(deskbuddyHome(), "notice");
|
|
4910
5489
|
}
|
|
4911
5490
|
function writeNotice(sessionId, text, now = Date.now()) {
|
|
4912
5491
|
try {
|
|
4913
5492
|
const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
|
|
4914
5493
|
if (!safe) return;
|
|
4915
|
-
|
|
4916
|
-
const file =
|
|
4917
|
-
|
|
5494
|
+
mkdirSync18(noticeDir(), { recursive: true });
|
|
5495
|
+
const file = join23(noticeDir(), `${safe}.json`);
|
|
5496
|
+
writeFileSync17(file + ".tmp", JSON.stringify({ text, at: now }), "utf8");
|
|
4918
5497
|
renameSync11(file + ".tmp", file);
|
|
4919
5498
|
} catch {
|
|
4920
5499
|
}
|
|
@@ -4924,7 +5503,7 @@ var ALIAS_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
4924
5503
|
function resolveAlias(sessionId, now = Date.now()) {
|
|
4925
5504
|
let map = {};
|
|
4926
5505
|
try {
|
|
4927
|
-
map = JSON.parse(
|
|
5506
|
+
map = JSON.parse(readFileSync19(join23(deskbuddyHome(), "alias.json"), "utf8")) || {};
|
|
4928
5507
|
} catch {
|
|
4929
5508
|
return sessionId;
|
|
4930
5509
|
}
|
|
@@ -4975,8 +5554,8 @@ function buildSnapshot(states, tool, cfg, nowMs, remote) {
|
|
|
4975
5554
|
|
|
4976
5555
|
// src/core/dirs.ts
|
|
4977
5556
|
import { createHmac as createHmac2, randomBytes as randomBytes2 } from "crypto";
|
|
4978
|
-
import { chmodSync as chmodSync8, existsSync as
|
|
4979
|
-
import { basename as basename4, join as
|
|
5557
|
+
import { chmodSync as chmodSync8, existsSync as existsSync19, mkdirSync as mkdirSync19, readFileSync as readFileSync20, writeFileSync as writeFileSync18 } from "fs";
|
|
5558
|
+
import { basename as basename4, join as join24 } from "path";
|
|
4980
5559
|
|
|
4981
5560
|
// src/core/dirFilter.ts
|
|
4982
5561
|
import { homedir as homedir7, tmpdir as tmpdir2 } from "os";
|
|
@@ -5015,19 +5594,19 @@ function isReportableDir(p, opts = {}) {
|
|
|
5015
5594
|
// src/core/dirs.ts
|
|
5016
5595
|
var DIRS_REPORT_MAX = 10;
|
|
5017
5596
|
function dirsPath() {
|
|
5018
|
-
return
|
|
5597
|
+
return join24(deskbuddyHome(), "dirs.json");
|
|
5019
5598
|
}
|
|
5020
5599
|
function dirSecret() {
|
|
5021
|
-
const file =
|
|
5600
|
+
const file = join24(deskbuddyHome(), "dir-secret");
|
|
5022
5601
|
try {
|
|
5023
|
-
const s3 =
|
|
5602
|
+
const s3 = readFileSync20(file, "utf8").trim();
|
|
5024
5603
|
if (s3) return s3;
|
|
5025
5604
|
} catch {
|
|
5026
5605
|
}
|
|
5027
5606
|
const s2 = randomBytes2(32).toString("hex");
|
|
5028
5607
|
try {
|
|
5029
|
-
|
|
5030
|
-
|
|
5608
|
+
mkdirSync19(deskbuddyHome(), { recursive: true });
|
|
5609
|
+
writeFileSync18(file, s2, { encoding: "utf8", mode: 384 });
|
|
5031
5610
|
chmodSync8(file, 384);
|
|
5032
5611
|
} catch {
|
|
5033
5612
|
}
|
|
@@ -5038,7 +5617,7 @@ function dirRef(path) {
|
|
|
5038
5617
|
}
|
|
5039
5618
|
function readMap() {
|
|
5040
5619
|
try {
|
|
5041
|
-
const m = JSON.parse(
|
|
5620
|
+
const m = JSON.parse(readFileSync20(dirsPath(), "utf8"));
|
|
5042
5621
|
const out = {};
|
|
5043
5622
|
for (const [k, v] of Object.entries(m ?? {})) if (typeof v === "number") out[k] = v;
|
|
5044
5623
|
return out;
|
|
@@ -5046,7 +5625,7 @@ function readMap() {
|
|
|
5046
5625
|
return {};
|
|
5047
5626
|
}
|
|
5048
5627
|
}
|
|
5049
|
-
function recentDirs(limit = DIRS_REPORT_MAX, exists =
|
|
5628
|
+
function recentDirs(limit = DIRS_REPORT_MAX, exists = existsSync19, reportable = (p) => isReportableDir(p)) {
|
|
5050
5629
|
const list = Object.entries(readMap()).filter(([p]) => p && reportable(p) && exists(p)).sort((a, b) => b[1] - a[1]).slice(0, limit);
|
|
5051
5630
|
const counts = /* @__PURE__ */ new Map();
|
|
5052
5631
|
for (const [p] of list) counts.set(basename4(p), (counts.get(basename4(p)) ?? 0) + 1);
|
|
@@ -5056,7 +5635,7 @@ function recentDirs(limit = DIRS_REPORT_MAX, exists = existsSync17, reportable =
|
|
|
5056
5635
|
return { path: p, ref, name: (counts.get(basename4(p)) ?? 0) > 1 ? `${name} \xB7 ${ref.slice(0, 4)}` : name, lastUsedAt: at };
|
|
5057
5636
|
});
|
|
5058
5637
|
}
|
|
5059
|
-
function dirOf(ref, exists =
|
|
5638
|
+
function dirOf(ref, exists = existsSync19) {
|
|
5060
5639
|
for (const p of Object.keys(readMap())) if (dirRef(p) === ref && exists(p)) return p;
|
|
5061
5640
|
return "";
|
|
5062
5641
|
}
|
|
@@ -5159,8 +5738,8 @@ function isExpired(cmd, now = Date.now()) {
|
|
|
5159
5738
|
|
|
5160
5739
|
// src/daemon/gate/approvals.ts
|
|
5161
5740
|
import { createHash as createHash3 } from "crypto";
|
|
5162
|
-
import { mkdirSync as
|
|
5163
|
-
import { join as
|
|
5741
|
+
import { mkdirSync as mkdirSync20, readFileSync as readFileSync21, renameSync as renameSync12, writeFileSync as writeFileSync19 } from "fs";
|
|
5742
|
+
import { join as join25 } from "path";
|
|
5164
5743
|
var RETAIN_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
5165
5744
|
function fingerprint(toolName, toolInput) {
|
|
5166
5745
|
let input = "";
|
|
@@ -5172,7 +5751,7 @@ function fingerprint(toolName, toolInput) {
|
|
|
5172
5751
|
return createHash3("sha256").update(toolName).update("\0").update(input).digest("hex");
|
|
5173
5752
|
}
|
|
5174
5753
|
function storePath() {
|
|
5175
|
-
return
|
|
5754
|
+
return join25(deskbuddyHome(), "approvals.json");
|
|
5176
5755
|
}
|
|
5177
5756
|
var ApprovalStore = class _ApprovalStore {
|
|
5178
5757
|
#records;
|
|
@@ -5181,7 +5760,7 @@ var ApprovalStore = class _ApprovalStore {
|
|
|
5181
5760
|
}
|
|
5182
5761
|
static load() {
|
|
5183
5762
|
try {
|
|
5184
|
-
const raw = JSON.parse(
|
|
5763
|
+
const raw = JSON.parse(readFileSync21(storePath(), "utf8"));
|
|
5185
5764
|
return new _ApprovalStore(Array.isArray(raw) ? raw : []);
|
|
5186
5765
|
} catch {
|
|
5187
5766
|
return new _ApprovalStore();
|
|
@@ -5190,9 +5769,9 @@ var ApprovalStore = class _ApprovalStore {
|
|
|
5190
5769
|
save() {
|
|
5191
5770
|
for (const r of this.#records) if (r.status !== "pending" && r.toolInput !== void 0) delete r.toolInput;
|
|
5192
5771
|
try {
|
|
5193
|
-
|
|
5772
|
+
mkdirSync20(deskbuddyHome(), { recursive: true });
|
|
5194
5773
|
const tmp = storePath() + ".tmp";
|
|
5195
|
-
|
|
5774
|
+
writeFileSync19(tmp, JSON.stringify(this.#records), { encoding: "utf8", mode: 384 });
|
|
5196
5775
|
renameSync12(tmp, storePath());
|
|
5197
5776
|
} catch {
|
|
5198
5777
|
}
|
|
@@ -5311,7 +5890,7 @@ function mapStatus(s2, decision) {
|
|
|
5311
5890
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5312
5891
|
import { lstatSync as lstatSync2, readlinkSync, realpathSync as realpathSync3 } from "fs";
|
|
5313
5892
|
import { homedir as homedir8 } from "os";
|
|
5314
|
-
import { basename as basename6, dirname as
|
|
5893
|
+
import { basename as basename6, dirname as dirname16, join as join26, posix as posix3, resolve as resolve5, win32 as win324 } from "path";
|
|
5315
5894
|
|
|
5316
5895
|
// src/daemon/gate/classify.ts
|
|
5317
5896
|
import { isAbsolute, relative as relative2, resolve as resolve4, sep as sep2 } from "path";
|
|
@@ -5466,7 +6045,7 @@ function editInProject(toolName, toolInput, cwd, opts) {
|
|
|
5466
6045
|
}
|
|
5467
6046
|
|
|
5468
6047
|
// src/daemon/gate/question.ts
|
|
5469
|
-
import { closeSync as closeSync5, openSync as openSync5, readSync as readSync4, statSync as
|
|
6048
|
+
import { closeSync as closeSync5, openSync as openSync5, readSync as readSync4, statSync as statSync8 } from "fs";
|
|
5470
6049
|
var MAX_QUESTIONS = 4;
|
|
5471
6050
|
var MAX_OPTIONS = 4;
|
|
5472
6051
|
var HEADER_MAX = 12;
|
|
@@ -5573,7 +6152,7 @@ function lastAssistantText(transcriptPath) {
|
|
|
5573
6152
|
if (typeof transcriptPath !== "string" || !transcriptPath) return "";
|
|
5574
6153
|
let text = "";
|
|
5575
6154
|
try {
|
|
5576
|
-
const size =
|
|
6155
|
+
const size = statSync8(transcriptPath).size;
|
|
5577
6156
|
const len = Math.min(size, TAIL_BYTES);
|
|
5578
6157
|
const buf = Buffer.alloc(len);
|
|
5579
6158
|
const fd = openSync5(transcriptPath, "r");
|
|
@@ -5586,9 +6165,9 @@ function lastAssistantText(transcriptPath) {
|
|
|
5586
6165
|
} catch {
|
|
5587
6166
|
return "";
|
|
5588
6167
|
}
|
|
5589
|
-
const
|
|
5590
|
-
for (let i =
|
|
5591
|
-
const line =
|
|
6168
|
+
const lines3 = text.split("\n");
|
|
6169
|
+
for (let i = lines3.length - 1; i >= 0; i--) {
|
|
6170
|
+
const line = lines3[i].trim();
|
|
5592
6171
|
if (!line.startsWith("{")) continue;
|
|
5593
6172
|
try {
|
|
5594
6173
|
const m = JSON.parse(line);
|
|
@@ -5847,6 +6426,8 @@ var CODEX_TEXT = {
|
|
|
5847
6426
|
yes: "Yes, proceed",
|
|
5848
6427
|
cmdSession: "Yes, and don't ask again for this command in this session",
|
|
5849
6428
|
cmdPrefix: "Yes, and don't ask again for commands that start with `",
|
|
6429
|
+
/** 前缀放不进一行(Windows PowerShell 带路径等)时的选项名:TUI 原文的前半句,前缀进 description */
|
|
6430
|
+
cmdPrefixHead: "Yes, and don't ask again for commands that start with",
|
|
5850
6431
|
fileSession: "Yes, and don't ask again for these files",
|
|
5851
6432
|
decline: "No, continue without running it",
|
|
5852
6433
|
cancel: "No, and tell Codex what to do differently"
|
|
@@ -5874,6 +6455,15 @@ function renderAmendment(v) {
|
|
|
5874
6455
|
if (a.length === 3 && /(^|\/)(ba|z)?sh$/.test(a[0]) && (a[1] === "-lc" || a[1] === "-c")) return a[2];
|
|
5875
6456
|
return a.every((x) => /^[A-Za-z0-9_@%+=:,./-]+$/.test(x)) ? a.join(" ") : null;
|
|
5876
6457
|
}
|
|
6458
|
+
function renderAmendmentQuoted(v) {
|
|
6459
|
+
if (!Array.isArray(v) || v.length === 0 || v.some((x) => typeof x !== "string" || x === "")) return null;
|
|
6460
|
+
const out = [];
|
|
6461
|
+
for (const x of v) {
|
|
6462
|
+
if (/["\u0000-\u001f\u007f-\u009f---]/.test(x)) return null;
|
|
6463
|
+
out.push(/^[A-Za-z0-9_@%+=,./-]+$/.test(x) ? x : `"${x}"`);
|
|
6464
|
+
}
|
|
6465
|
+
return out.join(" ");
|
|
6466
|
+
}
|
|
5877
6467
|
function codexCommandOptions(p) {
|
|
5878
6468
|
if (p.kind !== void 0 && p.kind !== null && p.kind !== "command") return null;
|
|
5879
6469
|
if (p.networkApprovalContext) return null;
|
|
@@ -5894,9 +6484,17 @@ function codexCommandOptions(p) {
|
|
|
5894
6484
|
else if (d && typeof d === "object" && !Array.isArray(d) && Object.keys(d).length === 1 && "acceptWithExecpolicyAmendment" in d) {
|
|
5895
6485
|
const amend = d.acceptWithExecpolicyAmendment?.execpolicy_amendment;
|
|
5896
6486
|
const shown = renderAmendment(amend);
|
|
5897
|
-
|
|
6487
|
+
const whole = shown === null ? null : `${CODEX_TEXT.cmdPrefix}${shown}\``;
|
|
6488
|
+
let opt;
|
|
6489
|
+
if (whole !== null && whole.length <= OPTION_LABEL_MAX) {
|
|
6490
|
+
opt = { id: "always", label: whole, kind: "allow_always" };
|
|
6491
|
+
} else {
|
|
6492
|
+
const desc = renderAmendmentQuoted(amend);
|
|
6493
|
+
if (desc === null || desc.length > OPTION_DESC_MAX) return null;
|
|
6494
|
+
opt = { id: "always", label: CODEX_TEXT.cmdPrefixHead, description: desc, kind: "allow_always" };
|
|
6495
|
+
}
|
|
5898
6496
|
add(
|
|
5899
|
-
|
|
6497
|
+
opt,
|
|
5900
6498
|
{ decision: "allow", native: { decision: { acceptWithExecpolicyAmendment: { execpolicy_amendment: [...amend] } } } }
|
|
5901
6499
|
);
|
|
5902
6500
|
} else return null;
|
|
@@ -5991,7 +6589,9 @@ function qoderCnTuiOptions(toolName, toolInput, suggestions) {
|
|
|
5991
6589
|
const desc = `\u89C4\u5219\uFF1ABash(${rc}) \u2192 localSettings`;
|
|
5992
6590
|
if (rc === cmd && /&&|\|\||;|\|/.test(cmd)) return qoderSet(...remember("always", QODER_TEXT.exact, "allow_always", desc));
|
|
5993
6591
|
const root = cmd.split(/\s+/)[0];
|
|
5994
|
-
|
|
6592
|
+
const spaced = rc.endsWith(" *") && (rc === `${root} *` || rc.startsWith(`${root} `));
|
|
6593
|
+
const colon = rc === `${root}:*`;
|
|
6594
|
+
if (!/^[A-Za-z0-9_.+-]+$/.test(root) || !(spaced || colon)) return null;
|
|
5995
6595
|
return qoderSet(...remember("always", alwaysFor(root), "allow_always", desc));
|
|
5996
6596
|
}
|
|
5997
6597
|
if (toolName === "Edit" || toolName === "Write" || toolName === "MultiEdit") {
|
|
@@ -6966,18 +7566,18 @@ function realpathNearest(p) {
|
|
|
6966
7566
|
}
|
|
6967
7567
|
if (exists) {
|
|
6968
7568
|
try {
|
|
6969
|
-
return
|
|
7569
|
+
return join26(realpathSync3(cur), ...[...tail2].reverse());
|
|
6970
7570
|
} catch {
|
|
6971
7571
|
if (!isLink) return "";
|
|
6972
7572
|
try {
|
|
6973
|
-
cur = resolve5(
|
|
7573
|
+
cur = resolve5(dirname16(cur), readlinkSync(cur));
|
|
6974
7574
|
} catch {
|
|
6975
7575
|
return "";
|
|
6976
7576
|
}
|
|
6977
7577
|
continue;
|
|
6978
7578
|
}
|
|
6979
7579
|
}
|
|
6980
|
-
const parent =
|
|
7580
|
+
const parent = dirname16(cur);
|
|
6981
7581
|
if (parent === cur) return "";
|
|
6982
7582
|
tail2.push(basename6(cur));
|
|
6983
7583
|
cur = parent;
|
|
@@ -6996,11 +7596,11 @@ function cwdForms(cwd) {
|
|
|
6996
7596
|
// src/daemon/gate/server.ts
|
|
6997
7597
|
import { randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
6998
7598
|
import { createServer } from "http";
|
|
6999
|
-
import { mkdirSync as
|
|
7000
|
-
import { join as
|
|
7599
|
+
import { mkdirSync as mkdirSync21, renameSync as renameSync13, rmSync as rmSync12, writeFileSync as writeFileSync20 } from "fs";
|
|
7600
|
+
import { join as join27 } from "path";
|
|
7001
7601
|
var MAX_BODY_BYTES = 8 * 1024 * 1024;
|
|
7002
7602
|
function gateConfPath() {
|
|
7003
|
-
return
|
|
7603
|
+
return join27(deskbuddyHome(), "gate.json");
|
|
7004
7604
|
}
|
|
7005
7605
|
function tokenEquals(a, b) {
|
|
7006
7606
|
const x = Buffer.from(a);
|
|
@@ -7036,9 +7636,9 @@ var GateServer = class {
|
|
|
7036
7636
|
this.#server = server;
|
|
7037
7637
|
const addr = server.address();
|
|
7038
7638
|
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
7039
|
-
|
|
7639
|
+
mkdirSync21(deskbuddyHome(), { recursive: true });
|
|
7040
7640
|
const tmp = gateConfPath() + ".tmp";
|
|
7041
|
-
|
|
7641
|
+
writeFileSync20(tmp, JSON.stringify({ port, token: this.#token, pid: process.pid }), {
|
|
7042
7642
|
encoding: "utf8",
|
|
7043
7643
|
mode: 384
|
|
7044
7644
|
});
|
|
@@ -7047,7 +7647,7 @@ var GateServer = class {
|
|
|
7047
7647
|
}
|
|
7048
7648
|
async stop() {
|
|
7049
7649
|
try {
|
|
7050
|
-
|
|
7650
|
+
rmSync12(gateConfPath(), { force: true });
|
|
7051
7651
|
} catch {
|
|
7052
7652
|
}
|
|
7053
7653
|
const s2 = this.#server;
|
|
@@ -7176,15 +7776,15 @@ function buildHandshakeUrl(cfg, opts = {}) {
|
|
|
7176
7776
|
}
|
|
7177
7777
|
|
|
7178
7778
|
// src/daemon/log.ts
|
|
7179
|
-
import { appendFileSync, mkdirSync as
|
|
7180
|
-
import { join as
|
|
7779
|
+
import { appendFileSync, mkdirSync as mkdirSync22, statSync as statSync9, truncateSync } from "fs";
|
|
7780
|
+
import { join as join28 } from "path";
|
|
7181
7781
|
var MAX_LOG_BYTES = 2 * 1024 * 1024;
|
|
7182
7782
|
function logPath() {
|
|
7183
|
-
return
|
|
7783
|
+
return join28(deskbuddyHome(), "daemon.log");
|
|
7184
7784
|
}
|
|
7185
7785
|
function rotateIfNeeded(path) {
|
|
7186
7786
|
try {
|
|
7187
|
-
if (
|
|
7787
|
+
if (statSync9(path).size > MAX_LOG_BYTES) truncateSync(path, 0);
|
|
7188
7788
|
} catch {
|
|
7189
7789
|
}
|
|
7190
7790
|
}
|
|
@@ -7192,7 +7792,7 @@ function log(line) {
|
|
|
7192
7792
|
const stamped = `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
7193
7793
|
`;
|
|
7194
7794
|
try {
|
|
7195
|
-
|
|
7795
|
+
mkdirSync22(deskbuddyHome(), { recursive: true });
|
|
7196
7796
|
const p = logPath();
|
|
7197
7797
|
rotateIfNeeded(p);
|
|
7198
7798
|
appendFileSync(p, stamped, "utf8");
|
|
@@ -7284,11 +7884,11 @@ function failureMessage(reason) {
|
|
|
7284
7884
|
}
|
|
7285
7885
|
|
|
7286
7886
|
// src/daemon/seen.ts
|
|
7287
|
-
import { mkdirSync as
|
|
7288
|
-
import { join as
|
|
7887
|
+
import { mkdirSync as mkdirSync23, readFileSync as readFileSync22, renameSync as renameSync14, writeFileSync as writeFileSync21 } from "fs";
|
|
7888
|
+
import { join as join29 } from "path";
|
|
7289
7889
|
var SEEN_CAPACITY = 200;
|
|
7290
7890
|
function seenPath() {
|
|
7291
|
-
return
|
|
7891
|
+
return join29(deskbuddyHome(), "seen.json");
|
|
7292
7892
|
}
|
|
7293
7893
|
var SeenIds = class _SeenIds {
|
|
7294
7894
|
#ids = [];
|
|
@@ -7319,7 +7919,7 @@ var SeenIds = class _SeenIds {
|
|
|
7319
7919
|
}
|
|
7320
7920
|
static load() {
|
|
7321
7921
|
try {
|
|
7322
|
-
const raw = JSON.parse(
|
|
7922
|
+
const raw = JSON.parse(readFileSync22(seenPath(), "utf8"));
|
|
7323
7923
|
return new _SeenIds(Array.isArray(raw?.ids) ? raw.ids.filter((x) => typeof x === "string") : []);
|
|
7324
7924
|
} catch {
|
|
7325
7925
|
return new _SeenIds();
|
|
@@ -7327,9 +7927,9 @@ var SeenIds = class _SeenIds {
|
|
|
7327
7927
|
}
|
|
7328
7928
|
save() {
|
|
7329
7929
|
try {
|
|
7330
|
-
|
|
7930
|
+
mkdirSync23(deskbuddyHome(), { recursive: true });
|
|
7331
7931
|
const tmp = seenPath() + ".tmp";
|
|
7332
|
-
|
|
7932
|
+
writeFileSync21(tmp, JSON.stringify({ ids: this.#ids }), "utf8");
|
|
7333
7933
|
renameSync14(tmp, seenPath());
|
|
7334
7934
|
} catch {
|
|
7335
7935
|
}
|
|
@@ -7337,9 +7937,9 @@ var SeenIds = class _SeenIds {
|
|
|
7337
7937
|
};
|
|
7338
7938
|
|
|
7339
7939
|
// src/daemon/qwenworkBridge.ts
|
|
7340
|
-
import { readFileSync as
|
|
7940
|
+
import { readFileSync as readFileSync23 } from "fs";
|
|
7341
7941
|
import { homedir as homedir9 } from "os";
|
|
7342
|
-
import { basename as basename7, dirname as
|
|
7942
|
+
import { basename as basename7, dirname as dirname17, join as join30, resolve as resolve6 } from "path";
|
|
7343
7943
|
var SUBMIT_TOOL = "qwenwork_task_submit_response";
|
|
7344
7944
|
var DETAIL_TOOL = "qwenwork_task_get_detail";
|
|
7345
7945
|
var POLL_MS = 2e3;
|
|
@@ -7386,7 +7986,7 @@ function sameDir(a, b) {
|
|
|
7386
7986
|
function chatIdOf(cwd, root) {
|
|
7387
7987
|
if (typeof cwd !== "string" || !cwd) return null;
|
|
7388
7988
|
const dir = resolve6(cwd);
|
|
7389
|
-
if (!sameDir(
|
|
7989
|
+
if (!sameDir(dirname17(dir), resolve6(root))) return null;
|
|
7390
7990
|
const id = basename7(dir);
|
|
7391
7991
|
return /^[A-Za-z0-9_-]{1,64}$/.test(id) ? id : null;
|
|
7392
7992
|
}
|
|
@@ -7537,7 +8137,7 @@ var QwenWorkBridge = class {
|
|
|
7537
8137
|
#stopped = false;
|
|
7538
8138
|
constructor(deps2) {
|
|
7539
8139
|
this.#d = deps2;
|
|
7540
|
-
const read = deps2.readConfig ?? (() => readConfigFile(
|
|
8140
|
+
const read = deps2.readConfig ?? (() => readConfigFile(join30(qwenworkHome(), "mcp-adaptor.config")));
|
|
7541
8141
|
const f = deps2.fetch ?? globalThis.fetch;
|
|
7542
8142
|
this.#mcp = new QwenWorkMcp(read, f, deps2.log);
|
|
7543
8143
|
}
|
|
@@ -7586,7 +8186,7 @@ var QwenWorkBridge = class {
|
|
|
7586
8186
|
toolUseId = typeof det.toolUseId === "string" ? det.toolUseId : "";
|
|
7587
8187
|
if (!toolName) return;
|
|
7588
8188
|
} else return;
|
|
7589
|
-
const root = this.#d.workspaceRoot ??
|
|
8189
|
+
const root = this.#d.workspaceRoot ?? join30(homedir9(), ".qwenworkcn", "workspace");
|
|
7590
8190
|
const chatId = chatIdOf(h.cwd, root);
|
|
7591
8191
|
if (!chatId) {
|
|
7592
8192
|
this.#d.log("\u5343\u95EE\u529E\u516C\uFF1A\u8FD9\u9053\u9898 / \u6388\u6743\u4E0D\u5728\u4EFB\u52A1\u5DE5\u4F5C\u76EE\u5F55\u4E0B\uFF08\u53EF\u80FD\u662F\u5B50\u4EE3\u7406\uFF09\uFF0C\u4E0D\u63A8\u8BBE\u5907");
|
|
@@ -7800,7 +8400,7 @@ function detailSettled(text, toolUseId) {
|
|
|
7800
8400
|
}
|
|
7801
8401
|
function readConfigFile(p) {
|
|
7802
8402
|
try {
|
|
7803
|
-
return
|
|
8403
|
+
return readFileSync23(p, "utf8");
|
|
7804
8404
|
} catch {
|
|
7805
8405
|
return null;
|
|
7806
8406
|
}
|
|
@@ -7810,13 +8410,13 @@ function sleep(ms) {
|
|
|
7810
8410
|
}
|
|
7811
8411
|
|
|
7812
8412
|
// src/daemon/workbuddyBridge.ts
|
|
7813
|
-
import { closeSync as closeSync6, openSync as openSync6, readSync as readSync5, realpathSync as realpathSync4, statSync as
|
|
7814
|
-
import { dirname as
|
|
8413
|
+
import { closeSync as closeSync6, openSync as openSync6, readSync as readSync5, realpathSync as realpathSync4, statSync as statSync11 } from "fs";
|
|
8414
|
+
import { dirname as dirname18, sep as sep3 } from "path";
|
|
7815
8415
|
|
|
7816
8416
|
// src/core/wrapWindows.ts
|
|
7817
|
-
import { chmodSync as chmodSync9, mkdirSync as
|
|
8417
|
+
import { chmodSync as chmodSync9, mkdirSync as mkdirSync24, readdirSync as readdirSync7, readFileSync as readFileSync24, renameSync as renameSync15, rmSync as rmSync13, statSync as statSync10, writeFileSync as writeFileSync22 } from "fs";
|
|
7818
8418
|
import { tmpdir as tmpdir3 } from "os";
|
|
7819
|
-
import { join as
|
|
8419
|
+
import { join as join31 } from "path";
|
|
7820
8420
|
var WINDOW_ID_RE = /^[a-f0-9]{32}$/;
|
|
7821
8421
|
var WINDOW_EVENTS = [
|
|
7822
8422
|
"SessionStart",
|
|
@@ -7829,24 +8429,24 @@ var WINDOW_EVENTS = [
|
|
|
7829
8429
|
"SessionEnd"
|
|
7830
8430
|
];
|
|
7831
8431
|
function windowsDir() {
|
|
7832
|
-
return
|
|
8432
|
+
return join31(deskbuddyHome(), "windows");
|
|
7833
8433
|
}
|
|
7834
8434
|
function bySessionDir() {
|
|
7835
|
-
return
|
|
8435
|
+
return join31(windowsDir(), "by-session");
|
|
7836
8436
|
}
|
|
7837
8437
|
function safeSid2(sessionId) {
|
|
7838
8438
|
return String(sessionId).replace(/[^A-Za-z0-9_-]/g, "");
|
|
7839
8439
|
}
|
|
7840
8440
|
function ensureDir(dir) {
|
|
7841
|
-
|
|
8441
|
+
mkdirSync24(dir, { recursive: true, mode: 448 });
|
|
7842
8442
|
}
|
|
7843
8443
|
function atomicWrite2(file, text) {
|
|
7844
8444
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
7845
|
-
|
|
8445
|
+
writeFileSync22(tmp, text, { encoding: "utf8", mode: 384 });
|
|
7846
8446
|
renameSync15(tmp, file);
|
|
7847
8447
|
}
|
|
7848
8448
|
function windowFile(windowId) {
|
|
7849
|
-
return
|
|
8449
|
+
return join31(windowsDir(), `${windowId}.json`);
|
|
7850
8450
|
}
|
|
7851
8451
|
function writeWindow(windowId, rec) {
|
|
7852
8452
|
if (!WINDOW_ID_RE.test(windowId)) return;
|
|
@@ -7861,7 +8461,7 @@ function windowSecret(windowId) {
|
|
|
7861
8461
|
function readWindow(windowId) {
|
|
7862
8462
|
if (!WINDOW_ID_RE.test(windowId)) return null;
|
|
7863
8463
|
try {
|
|
7864
|
-
const r = JSON.parse(
|
|
8464
|
+
const r = JSON.parse(readFileSync24(windowFile(windowId), "utf8"));
|
|
7865
8465
|
return r && typeof r.pid === "number" ? r : null;
|
|
7866
8466
|
} catch {
|
|
7867
8467
|
return null;
|
|
@@ -7871,7 +8471,7 @@ function removeWindow(windowId) {
|
|
|
7871
8471
|
if (!WINDOW_ID_RE.test(windowId)) return;
|
|
7872
8472
|
for (const f of [windowFile(windowId), lastEventFile(windowId)]) {
|
|
7873
8473
|
try {
|
|
7874
|
-
|
|
8474
|
+
rmSync13(f, { force: true });
|
|
7875
8475
|
} catch {
|
|
7876
8476
|
}
|
|
7877
8477
|
}
|
|
@@ -7882,9 +8482,9 @@ function removeWindow(windowId) {
|
|
|
7882
8482
|
return;
|
|
7883
8483
|
}
|
|
7884
8484
|
for (const n of names) {
|
|
7885
|
-
const f =
|
|
8485
|
+
const f = join31(bySessionDir(), n);
|
|
7886
8486
|
try {
|
|
7887
|
-
if (
|
|
8487
|
+
if (readFileSync24(f, "utf8").trim() === windowId) rmSync13(f, { force: true });
|
|
7888
8488
|
} catch {
|
|
7889
8489
|
}
|
|
7890
8490
|
}
|
|
@@ -7893,19 +8493,19 @@ function readBySession(sessionId) {
|
|
|
7893
8493
|
const safe = safeSid2(sessionId);
|
|
7894
8494
|
if (!safe) return null;
|
|
7895
8495
|
try {
|
|
7896
|
-
const id =
|
|
8496
|
+
const id = readFileSync24(join31(bySessionDir(), safe), "utf8").trim();
|
|
7897
8497
|
return WINDOW_ID_RE.test(id) ? id : null;
|
|
7898
8498
|
} catch {
|
|
7899
8499
|
return null;
|
|
7900
8500
|
}
|
|
7901
8501
|
}
|
|
7902
8502
|
function lastEventFile(windowId) {
|
|
7903
|
-
return
|
|
8503
|
+
return join31(windowsDir(), `${windowId}.last`);
|
|
7904
8504
|
}
|
|
7905
8505
|
function readLastEvent(windowId) {
|
|
7906
8506
|
if (!WINDOW_ID_RE.test(windowId)) return null;
|
|
7907
8507
|
try {
|
|
7908
|
-
const e = JSON.parse(
|
|
8508
|
+
const e = JSON.parse(readFileSync24(lastEventFile(windowId), "utf8"));
|
|
7909
8509
|
return e && typeof e.ev === "string" && typeof e.at === "number" ? { ev: e.ev, sid: String(e.sid ?? ""), at: e.at } : null;
|
|
7910
8510
|
} catch {
|
|
7911
8511
|
return null;
|
|
@@ -7919,16 +8519,16 @@ function writeLastEvent(windowId, ev, sid, at) {
|
|
|
7919
8519
|
function channelPath(windowId, platform = process.platform, home = deskbuddyHome()) {
|
|
7920
8520
|
if (platform === "win32") return `\\\\.\\pipe\\deskbuddy-${windowId}`;
|
|
7921
8521
|
const name = `${windowId.slice(0, 16)}.sock`;
|
|
7922
|
-
const primary =
|
|
8522
|
+
const primary = join31(home, "win", name);
|
|
7923
8523
|
if (Buffer.byteLength(primary) <= 103) return primary;
|
|
7924
8524
|
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
7925
|
-
return
|
|
8525
|
+
return join31(tmpdir3(), `deskbuddy-${uid}`, name);
|
|
7926
8526
|
}
|
|
7927
8527
|
function ensureChannelDir(sockPath) {
|
|
7928
|
-
const dir =
|
|
8528
|
+
const dir = join31(sockPath, "..");
|
|
7929
8529
|
try {
|
|
7930
|
-
|
|
7931
|
-
const st =
|
|
8530
|
+
mkdirSync24(dir, { recursive: true, mode: 448 });
|
|
8531
|
+
const st = statSync10(dir);
|
|
7932
8532
|
if (typeof process.getuid === "function" && st.uid !== process.getuid()) return false;
|
|
7933
8533
|
chmodSync9(dir, 448);
|
|
7934
8534
|
return true;
|
|
@@ -7948,8 +8548,8 @@ var FINGERPRINT_CHARS = 40;
|
|
|
7948
8548
|
var FINGERPRINT_TAIL = 30;
|
|
7949
8549
|
var FP_SEP = "\u2026";
|
|
7950
8550
|
var CURSOR = "[\u276F>\u203A]";
|
|
7951
|
-
function joined(
|
|
7952
|
-
return
|
|
8551
|
+
function joined(lines3) {
|
|
8552
|
+
return lines3.join("\n");
|
|
7953
8553
|
}
|
|
7954
8554
|
function squash(s2) {
|
|
7955
8555
|
return s2.replace(/[\s\u2500-\u257F]+/g, "");
|
|
@@ -7988,10 +8588,10 @@ async function waitUntil(io, ok, ms = CONFIRM_MS) {
|
|
|
7988
8588
|
}
|
|
7989
8589
|
return ok(io.screen());
|
|
7990
8590
|
}
|
|
7991
|
-
function permissionRegion(
|
|
8591
|
+
function permissionRegion(lines3, toolName) {
|
|
7992
8592
|
let q = -1;
|
|
7993
|
-
for (let i =
|
|
7994
|
-
if (/^\s*Do you want to .+\?\s*$/.test(
|
|
8593
|
+
for (let i = lines3.length - 1; i >= 0; i--) {
|
|
8594
|
+
if (/^\s*Do you want to .+\?\s*$/.test(lines3[i])) {
|
|
7995
8595
|
q = i;
|
|
7996
8596
|
break;
|
|
7997
8597
|
}
|
|
@@ -8000,21 +8600,21 @@ function permissionRegion(lines2, toolName) {
|
|
|
8000
8600
|
const head = new RegExp(`^\\s*\u25CF\\s*${toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\(`);
|
|
8001
8601
|
let h = -1;
|
|
8002
8602
|
for (let i = q - 1; i >= 0; i--) {
|
|
8003
|
-
if (head.test(
|
|
8603
|
+
if (head.test(lines3[i])) {
|
|
8004
8604
|
h = i;
|
|
8005
8605
|
break;
|
|
8006
8606
|
}
|
|
8007
|
-
if (/^\s*●/.test(
|
|
8607
|
+
if (/^\s*●/.test(lines3[i])) return null;
|
|
8008
8608
|
}
|
|
8009
8609
|
if (h < 0) return null;
|
|
8010
|
-
const after = joined(
|
|
8610
|
+
const after = joined(lines3.slice(q + 1, q + 6));
|
|
8011
8611
|
if (!new RegExp(`^\\s*(?:${CURSOR}\\s*)?1\\.\\s+Yes\\s*$`, "m").test(after)) return null;
|
|
8012
8612
|
if (!/^\s*(?:[❯>›]\s*)?3\.\s+No\b/m.test(after)) return null;
|
|
8013
|
-
return
|
|
8613
|
+
return lines3.slice(h, q + 1);
|
|
8014
8614
|
}
|
|
8015
|
-
function isPermissionDialog(
|
|
8615
|
+
function isPermissionDialog(lines3, toolName, fingerprint2) {
|
|
8016
8616
|
if (!fingerprint2) return false;
|
|
8017
|
-
const region = permissionRegion(
|
|
8617
|
+
const region = permissionRegion(lines3, toolName);
|
|
8018
8618
|
if (!region) return false;
|
|
8019
8619
|
const text = squash(joined(region));
|
|
8020
8620
|
return fingerprint2.split(FP_SEP).every((part) => !!part && text.includes(squash(part)));
|
|
@@ -8026,42 +8626,42 @@ async function answerPermission(io, toolName, fingerprint2, decision) {
|
|
|
8026
8626
|
return await waitUntil(io, (l) => !ok(l)) ? "done" : "partial";
|
|
8027
8627
|
}
|
|
8028
8628
|
var TAB_ROW = /←.*[☐☒].*Submit.*→/;
|
|
8029
|
-
function isQuestionDialog(
|
|
8030
|
-
return
|
|
8629
|
+
function isQuestionDialog(lines3) {
|
|
8630
|
+
return lines3.some((l) => TAB_ROW.test(l));
|
|
8031
8631
|
}
|
|
8032
|
-
function onQuestion(
|
|
8033
|
-
const start = questionStart(
|
|
8632
|
+
function onQuestion(lines3, q) {
|
|
8633
|
+
const start = questionStart(lines3);
|
|
8034
8634
|
if (start < 0) return false;
|
|
8035
8635
|
const t = squash(q.question);
|
|
8036
|
-
return !!t && squash(joined(
|
|
8636
|
+
return !!t && squash(joined(lines3.slice(start))).includes(t);
|
|
8037
8637
|
}
|
|
8038
|
-
function isReviewPage(
|
|
8039
|
-
const t = joined(
|
|
8638
|
+
function isReviewPage(lines3) {
|
|
8639
|
+
const t = joined(lines3);
|
|
8040
8640
|
return /Review your answers/.test(t) && /Submit answers/.test(t);
|
|
8041
8641
|
}
|
|
8042
|
-
function questionStart(
|
|
8043
|
-
for (let i =
|
|
8642
|
+
function questionStart(lines3) {
|
|
8643
|
+
for (let i = lines3.length - 1; i >= 0; i--) if (TAB_ROW.test(lines3[i])) return i + 1;
|
|
8044
8644
|
return -1;
|
|
8045
8645
|
}
|
|
8046
|
-
function optionLine(
|
|
8646
|
+
function optionLine(lines3, n, label2) {
|
|
8047
8647
|
const lab = squash(label2).slice(0, 6);
|
|
8048
|
-
const start = questionStart(
|
|
8648
|
+
const start = questionStart(lines3);
|
|
8049
8649
|
if (start < 0) return -1;
|
|
8050
|
-
for (let i = start; i <
|
|
8051
|
-
const m =
|
|
8650
|
+
for (let i = start; i < lines3.length; i++) {
|
|
8651
|
+
const m = lines3[i].match(/^\s*(?:[❯>›]\s*)?[☐☒]?\s*(\d+)\.\s+(.*)$/);
|
|
8052
8652
|
if (m && Number(m[1]) === n && squash(m[2]).startsWith(lab)) return i;
|
|
8053
8653
|
}
|
|
8054
8654
|
return -1;
|
|
8055
8655
|
}
|
|
8056
|
-
function checked(
|
|
8057
|
-
return line >= 0 && /^\s*☒/.test(
|
|
8656
|
+
function checked(lines3, line) {
|
|
8657
|
+
return line >= 0 && /^\s*☒/.test(lines3[line]);
|
|
8058
8658
|
}
|
|
8059
|
-
function singleCursor(
|
|
8659
|
+
function singleCursor(lines3, q) {
|
|
8060
8660
|
const n = q.options.length;
|
|
8061
|
-
const start = questionStart(
|
|
8661
|
+
const start = questionStart(lines3);
|
|
8062
8662
|
if (start < 0) return -1;
|
|
8063
|
-
for (let i = start; i <
|
|
8064
|
-
const l =
|
|
8663
|
+
for (let i = start; i < lines3.length; i++) {
|
|
8664
|
+
const l = lines3[i];
|
|
8065
8665
|
const m = l.match(new RegExp(`^\\s*${CURSOR}\\s*(\\d+)\\.\\s+(.*)$`));
|
|
8066
8666
|
if (m) {
|
|
8067
8667
|
const k = Number(m[1]) - 1;
|
|
@@ -8138,14 +8738,14 @@ async function answerQuestions(io, questions, selections) {
|
|
|
8138
8738
|
io.write(KEY_ENTER);
|
|
8139
8739
|
return await waitUntil(io, (l) => !isReviewPage(l) && !isQuestionDialog(l)) ? "done" : "partial";
|
|
8140
8740
|
}
|
|
8141
|
-
function stillWaiting(
|
|
8142
|
-
if (probe.questions && probe.questions.length > 0) return probe.questions.some((q) => onQuestion(
|
|
8143
|
-
return isPermissionDialog(
|
|
8741
|
+
function stillWaiting(lines3, probe) {
|
|
8742
|
+
if (probe.questions && probe.questions.length > 0) return probe.questions.some((q) => onQuestion(lines3, q)) || isReviewPage(lines3);
|
|
8743
|
+
return isPermissionDialog(lines3, probe.toolName, probe.fingerprint ?? "");
|
|
8144
8744
|
}
|
|
8145
8745
|
|
|
8146
8746
|
// src/wrap/channel.ts
|
|
8147
8747
|
import { randomBytes as randomBytes5 } from "crypto";
|
|
8148
|
-
import { chmodSync as chmodSync10, rmSync as
|
|
8748
|
+
import { chmodSync as chmodSync10, rmSync as rmSync14 } from "fs";
|
|
8149
8749
|
import { connect, createServer as createServer2 } from "net";
|
|
8150
8750
|
function str3(v, max) {
|
|
8151
8751
|
return typeof v === "string" && v.length > 0 && v.length <= max ? v : null;
|
|
@@ -8281,13 +8881,13 @@ function replyMac(secret, nonceC, r) {
|
|
|
8281
8881
|
function startChannelServer(path, secret, onRequest, platform = process.platform, onAnswer) {
|
|
8282
8882
|
return new Promise((resolve10) => {
|
|
8283
8883
|
const server = createServer2((sock) => {
|
|
8284
|
-
let
|
|
8884
|
+
let open2 = true;
|
|
8285
8885
|
let phase = "hello";
|
|
8286
8886
|
let nonceC = "";
|
|
8287
8887
|
let nonceS = "";
|
|
8288
8888
|
sock.on("error", () => {
|
|
8289
8889
|
});
|
|
8290
|
-
sock.on("close", () =>
|
|
8890
|
+
sock.on("close", () => open2 = false);
|
|
8291
8891
|
const authTimer = setTimeout(() => {
|
|
8292
8892
|
if (phase === "hello") sock.destroy();
|
|
8293
8893
|
}, 5e3);
|
|
@@ -8332,7 +8932,7 @@ function startChannelServer(path, secret, onRequest, platform = process.platform
|
|
|
8332
8932
|
return;
|
|
8333
8933
|
}
|
|
8334
8934
|
phase = "done";
|
|
8335
|
-
onRequest(req2, reply, () =>
|
|
8935
|
+
onRequest(req2, reply, () => open2);
|
|
8336
8936
|
} catch {
|
|
8337
8937
|
sock.destroy();
|
|
8338
8938
|
}
|
|
@@ -8340,7 +8940,7 @@ function startChannelServer(path, secret, onRequest, platform = process.platform
|
|
|
8340
8940
|
});
|
|
8341
8941
|
if (platform !== "win32") {
|
|
8342
8942
|
try {
|
|
8343
|
-
|
|
8943
|
+
rmSync14(path, { force: true });
|
|
8344
8944
|
} catch {
|
|
8345
8945
|
}
|
|
8346
8946
|
}
|
|
@@ -8373,7 +8973,7 @@ function closeChannelServer(server, path, platform = process.platform) {
|
|
|
8373
8973
|
}
|
|
8374
8974
|
if (platform !== "win32") {
|
|
8375
8975
|
try {
|
|
8376
|
-
|
|
8976
|
+
rmSync14(path, { force: true });
|
|
8377
8977
|
} catch {
|
|
8378
8978
|
}
|
|
8379
8979
|
}
|
|
@@ -8489,7 +9089,7 @@ function sendAnswer(path, secret, req2, opts = {}) {
|
|
|
8489
9089
|
}
|
|
8490
9090
|
|
|
8491
9091
|
// src/daemon/workbuddyBridge.ts
|
|
8492
|
-
function transcriptAllowed(path, root =
|
|
9092
|
+
function transcriptAllowed(path, root = dirname18(workbuddySettingsPath()), realpath = realpathSync4) {
|
|
8493
9093
|
if (!path) return false;
|
|
8494
9094
|
let real2;
|
|
8495
9095
|
let base;
|
|
@@ -8508,7 +9108,7 @@ var SEEN_TTL_MS2 = 60 * 60 * 1e3;
|
|
|
8508
9108
|
var ANSWER_TTL_MS = 60 * 1e3;
|
|
8509
9109
|
function tail(path) {
|
|
8510
9110
|
try {
|
|
8511
|
-
const size =
|
|
9111
|
+
const size = statSync11(path).size;
|
|
8512
9112
|
const len = Math.min(size, TAIL_BYTES2);
|
|
8513
9113
|
const buf = Buffer.alloc(len);
|
|
8514
9114
|
const fd = openSync6(path, "r");
|
|
@@ -8565,10 +9165,10 @@ function toolOfMessage(message) {
|
|
|
8565
9165
|
}
|
|
8566
9166
|
function pendingCall(text, toolName) {
|
|
8567
9167
|
const { calls, done } = scanTranscript(text);
|
|
8568
|
-
const
|
|
8569
|
-
const last =
|
|
9168
|
+
const open2 = calls.filter((c) => !done.has(c.callId));
|
|
9169
|
+
const last = open2[open2.length - 1];
|
|
8570
9170
|
if (!last || last.name !== toolName) return null;
|
|
8571
|
-
if (
|
|
9171
|
+
if (open2.filter((c) => c.name === toolName).length > 1) return null;
|
|
8572
9172
|
return last;
|
|
8573
9173
|
}
|
|
8574
9174
|
function selectionsOf(input, lists) {
|
|
@@ -8624,7 +9224,7 @@ var WorkbuddyBridge = class {
|
|
|
8624
9224
|
return null;
|
|
8625
9225
|
}
|
|
8626
9226
|
if (!rec || rec.answer !== true || rec.tool !== "workbuddy") return null;
|
|
8627
|
-
return (this.#d.alive ??
|
|
9227
|
+
return (this.#d.alive ?? pidAlive3)(rec.pid) ? rec : null;
|
|
8628
9228
|
}
|
|
8629
9229
|
onNotify(req2) {
|
|
8630
9230
|
void this.#onNotify(req2).catch((e) => this.#d.log(`WorkBuddy \u63A8\u5361\u51FA\u9519\uFF1A${e?.message ?? e}`));
|
|
@@ -8832,7 +9432,7 @@ var WorkbuddyBridge = class {
|
|
|
8832
9432
|
}
|
|
8833
9433
|
}
|
|
8834
9434
|
};
|
|
8835
|
-
function
|
|
9435
|
+
function pidAlive3(pid) {
|
|
8836
9436
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
8837
9437
|
try {
|
|
8838
9438
|
process.kill(pid, 0);
|
|
@@ -8847,19 +9447,19 @@ function defaultSend(windowId, rec, req2) {
|
|
|
8847
9447
|
}
|
|
8848
9448
|
|
|
8849
9449
|
// src/wrap/trustPrompt.ts
|
|
8850
|
-
import { existsSync as
|
|
9450
|
+
import { existsSync as existsSync20, realpathSync as realpathSync5, statSync as statSync12 } from "fs";
|
|
8851
9451
|
import { homedir as homedir10 } from "os";
|
|
8852
|
-
import { dirname as
|
|
9452
|
+
import { dirname as dirname19, join as join32, resolve as resolve7 } from "path";
|
|
8853
9453
|
var TRUST_TOOLS = /* @__PURE__ */ new Set(["claude_code", "workbuddy", "codex"]);
|
|
8854
9454
|
var CURSOR_CHARS = "\u276F>\u203A";
|
|
8855
9455
|
var BOX = /[─-╿|]/g;
|
|
8856
9456
|
function squash2(s2) {
|
|
8857
9457
|
return s2.replace(BOX, "").replace(/\s+/g, "");
|
|
8858
9458
|
}
|
|
8859
|
-
function squashLines(
|
|
9459
|
+
function squashLines(lines3) {
|
|
8860
9460
|
let body = "";
|
|
8861
9461
|
const breaks = [];
|
|
8862
|
-
for (const line of
|
|
9462
|
+
for (const line of lines3) {
|
|
8863
9463
|
for (const ch of line) {
|
|
8864
9464
|
if (/\s/.test(ch) || /[\u2500-\u257F|]/.test(ch)) {
|
|
8865
9465
|
if (breaks.length) breaks[breaks.length - 1] = true;
|
|
@@ -8921,18 +9521,18 @@ var HOOKS_SPEC = {
|
|
|
8921
9521
|
return null;
|
|
8922
9522
|
}
|
|
8923
9523
|
};
|
|
8924
|
-
function inspectTrust(
|
|
9524
|
+
function inspectTrust(lines3, tool, kind) {
|
|
8925
9525
|
if (kind !== "hooks") {
|
|
8926
|
-
const v = inspectWith(
|
|
9526
|
+
const v = inspectWith(lines3, tool, SPECS[tool], "trust");
|
|
8927
9527
|
if (v || kind === "trust") return v;
|
|
8928
9528
|
}
|
|
8929
|
-
return tool === "codex" ? inspectWith(
|
|
9529
|
+
return tool === "codex" ? inspectWith(lines3, tool, HOOKS_SPEC, "hooks") : null;
|
|
8930
9530
|
}
|
|
8931
|
-
function inspectWith(
|
|
9531
|
+
function inspectWith(lines3, tool, spec, kind) {
|
|
8932
9532
|
if (!spec) return null;
|
|
8933
9533
|
let start = -1;
|
|
8934
|
-
for (let i =
|
|
8935
|
-
if (spec.title.test(
|
|
9534
|
+
for (let i = lines3.length - 1; i >= 0; i--) {
|
|
9535
|
+
if (spec.title.test(lines3[i])) {
|
|
8936
9536
|
start = i;
|
|
8937
9537
|
break;
|
|
8938
9538
|
}
|
|
@@ -8943,8 +9543,8 @@ function inspectWith(lines2, tool, spec, kind) {
|
|
|
8943
9543
|
let cursor = -1;
|
|
8944
9544
|
let cursors = 0;
|
|
8945
9545
|
let last = start;
|
|
8946
|
-
for (let i = start + 1; i <
|
|
8947
|
-
const raw = inner(
|
|
9546
|
+
for (let i = start + 1; i < lines3.length; i++) {
|
|
9547
|
+
const raw = inner(lines3[i]).trim();
|
|
8948
9548
|
let text = raw;
|
|
8949
9549
|
let here2 = false;
|
|
8950
9550
|
if (text && CURSOR_CHARS.includes(text[0])) {
|
|
@@ -8961,7 +9561,7 @@ function inspectWith(lines2, tool, spec, kind) {
|
|
|
8961
9561
|
last = i;
|
|
8962
9562
|
}
|
|
8963
9563
|
if (!options.some((o) => o.role === "folder") || !options.some((o) => o.role === "no")) return null;
|
|
8964
|
-
const region =
|
|
9564
|
+
const region = lines3.slice(top, last + 1);
|
|
8965
9565
|
const { body, breaks } = squashLines(region);
|
|
8966
9566
|
const widened = !!spec.widened && spec.widened.test(region.join("\n"));
|
|
8967
9567
|
const widenedRoot = widened ? noteRoot(region) : "";
|
|
@@ -9033,38 +9633,38 @@ function trustCwdForms(cwd, real2 = realpathSync5, home = homedir10()) {
|
|
|
9033
9633
|
}
|
|
9034
9634
|
function gitStat(p) {
|
|
9035
9635
|
try {
|
|
9036
|
-
const s2 =
|
|
9636
|
+
const s2 = statSync12(p);
|
|
9037
9637
|
return s2.isDirectory() ? "dir" : "file";
|
|
9038
9638
|
} catch {
|
|
9039
9639
|
return null;
|
|
9040
9640
|
}
|
|
9041
9641
|
}
|
|
9042
|
-
function findRepoScope(tool, cwd,
|
|
9642
|
+
function findRepoScope(tool, cwd, stat2 = gitStat, home = homedir10(), real2 = realpathSync5) {
|
|
9043
9643
|
if (tool !== "claude_code" && tool !== "codex" || !cwd) return null;
|
|
9044
9644
|
const logical = resolve7(cwd);
|
|
9045
|
-
const a = scanRepo(logical,
|
|
9645
|
+
const a = scanRepo(logical, stat2, home);
|
|
9046
9646
|
let physical = logical;
|
|
9047
9647
|
try {
|
|
9048
9648
|
physical = real2(logical);
|
|
9049
9649
|
} catch {
|
|
9050
9650
|
}
|
|
9051
9651
|
if (physical === logical) return a;
|
|
9052
|
-
const b = scanRepo(physical,
|
|
9652
|
+
const b = scanRepo(physical, stat2, home);
|
|
9053
9653
|
return sameScope(a, b, real2) ? a : { kind: "unknown" };
|
|
9054
9654
|
}
|
|
9055
|
-
function scanRepo(start,
|
|
9655
|
+
function scanRepo(start, stat2, home) {
|
|
9056
9656
|
const h = home ? home.replace(/[\\/]+$/, "") : "";
|
|
9057
9657
|
let d = start.replace(/[\\/]+$/, "") || start;
|
|
9058
9658
|
let aboveHome = false;
|
|
9059
9659
|
for (let i = 0; i < 64; i++) {
|
|
9060
|
-
const g =
|
|
9660
|
+
const g = stat2(join32(d, ".git"));
|
|
9061
9661
|
if (g) {
|
|
9062
9662
|
if (aboveHome) return { kind: "unknown" };
|
|
9063
9663
|
if (g === "file") return { kind: "worktree", at: d };
|
|
9064
9664
|
return i === 0 ? null : { kind: "repo", root: d };
|
|
9065
9665
|
}
|
|
9066
9666
|
if (h && sameDir2(d, h)) aboveHome = true;
|
|
9067
|
-
const up =
|
|
9667
|
+
const up = dirname19(d);
|
|
9068
9668
|
if (up === d) return null;
|
|
9069
9669
|
d = up;
|
|
9070
9670
|
}
|
|
@@ -9143,8 +9743,8 @@ async function answerTrust(io, tool, decision, forms, kind = "trust", repoRoot)
|
|
|
9143
9743
|
}
|
|
9144
9744
|
return await waitUntil2(io, (l) => !inspectTrust(l, tool, kind)) ? "done" : "partial";
|
|
9145
9745
|
}
|
|
9146
|
-
function trustWaiting(
|
|
9147
|
-
return inspectTrust(
|
|
9746
|
+
function trustWaiting(lines3, tool, kind = "trust") {
|
|
9747
|
+
return inspectTrust(lines3, tool, kind) !== null;
|
|
9148
9748
|
}
|
|
9149
9749
|
var TRUST_STABLE_MS = 500;
|
|
9150
9750
|
var TRUST_RENOTIFY_MS = 3e4;
|
|
@@ -9190,8 +9790,8 @@ var TrustWatcher = class {
|
|
|
9190
9790
|
this.#mismatchLogged = true;
|
|
9191
9791
|
this.#d.log(msg);
|
|
9192
9792
|
}
|
|
9193
|
-
tick(
|
|
9194
|
-
const v =
|
|
9793
|
+
tick(lines3) {
|
|
9794
|
+
const v = lines3 ? inspectTrust(lines3, this.#d.tool) : null;
|
|
9195
9795
|
if (!v || v.kind !== this.#kind) {
|
|
9196
9796
|
this.#since = null;
|
|
9197
9797
|
this.#reported = false;
|
|
@@ -9257,7 +9857,7 @@ var TrustWatcher = class {
|
|
|
9257
9857
|
};
|
|
9258
9858
|
|
|
9259
9859
|
// src/wrap/trustNotify.ts
|
|
9260
|
-
import { readFileSync as
|
|
9860
|
+
import { readFileSync as readFileSync25 } from "fs";
|
|
9261
9861
|
import { request } from "http";
|
|
9262
9862
|
var TRUST_EVENT = "DeskBuddyTrustPrompt";
|
|
9263
9863
|
var LOGIN_EVENT = "DeskBuddyLoginRequired";
|
|
@@ -9285,7 +9885,7 @@ function postNotify(body, confPath) {
|
|
|
9285
9885
|
};
|
|
9286
9886
|
let conf;
|
|
9287
9887
|
try {
|
|
9288
|
-
conf = JSON.parse(
|
|
9888
|
+
conf = JSON.parse(readFileSync25(confPath, "utf8"));
|
|
9289
9889
|
} catch {
|
|
9290
9890
|
return finish(false);
|
|
9291
9891
|
}
|
|
@@ -9369,7 +9969,7 @@ var TrustBridge = class {
|
|
|
9369
9969
|
return null;
|
|
9370
9970
|
}
|
|
9371
9971
|
if (!rec || rec.trust !== true || rec.launched !== true || rec.tool !== tool || !rec.cwd) return null;
|
|
9372
|
-
return (this.#d.alive ??
|
|
9972
|
+
return (this.#d.alive ?? pidAlive4)(rec.pid) ? rec : null;
|
|
9373
9973
|
}
|
|
9374
9974
|
onNotify(req2) {
|
|
9375
9975
|
void this.#onNotify(req2).catch((e) => this.#d.log(`\u4FE1\u4EFB\u6846\u63A8\u5361\u51FA\u9519\uFF1A${e?.message ?? e}`));
|
|
@@ -9508,7 +10108,7 @@ var TrustBridge = class {
|
|
|
9508
10108
|
}
|
|
9509
10109
|
}
|
|
9510
10110
|
};
|
|
9511
|
-
function
|
|
10111
|
+
function pidAlive4(pid) {
|
|
9512
10112
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
9513
10113
|
try {
|
|
9514
10114
|
process.kill(pid, 0);
|
|
@@ -9592,7 +10192,7 @@ var LoginRequired = class {
|
|
|
9592
10192
|
rec = null;
|
|
9593
10193
|
}
|
|
9594
10194
|
if (!rec || rec.tool !== "workbuddy" || rec.answer !== true || rec.launched !== true) return;
|
|
9595
|
-
if (!(this.#d.alive ??
|
|
10195
|
+
if (!(this.#d.alive ?? pidAlive5)(rec.pid)) return;
|
|
9596
10196
|
this.#done.add(windowId);
|
|
9597
10197
|
const short = windowId.slice(0, 8);
|
|
9598
10198
|
const sid = this.#d.sessionOfWindow(windowId);
|
|
@@ -9617,7 +10217,7 @@ var LoginRequired = class {
|
|
|
9617
10217
|
this.#d.report(entry.cmd, entry.sid || sid, status, LOGIN_REASON);
|
|
9618
10218
|
}
|
|
9619
10219
|
};
|
|
9620
|
-
function
|
|
10220
|
+
function pidAlive5(pid) {
|
|
9621
10221
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
9622
10222
|
try {
|
|
9623
10223
|
process.kill(pid, 0);
|
|
@@ -9628,10 +10228,10 @@ function pidAlive4(pid) {
|
|
|
9628
10228
|
}
|
|
9629
10229
|
|
|
9630
10230
|
// src/daemon/dumateWatch.ts
|
|
9631
|
-
import { execFileSync as execFileSync14, spawn as
|
|
9632
|
-
import { readdirSync as readdirSync8, realpathSync as realpathSync6, statSync as
|
|
10231
|
+
import { execFileSync as execFileSync14, spawn as spawn5 } from "child_process";
|
|
10232
|
+
import { readdirSync as readdirSync8, realpathSync as realpathSync6, statSync as statSync13 } from "fs";
|
|
9633
10233
|
import { request as httpRequest } from "http";
|
|
9634
|
-
import { join as
|
|
10234
|
+
import { join as join33 } from "path";
|
|
9635
10235
|
|
|
9636
10236
|
// hook/ocTranslate.mjs
|
|
9637
10237
|
function toCcTool(tool, args) {
|
|
@@ -9857,7 +10457,7 @@ function scanSessionDirs(root, now, maxAgeMs = ACTIVE_DIR_MS) {
|
|
|
9857
10457
|
const out = [];
|
|
9858
10458
|
const list = (p) => {
|
|
9859
10459
|
try {
|
|
9860
|
-
return readdirSync8(p, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) =>
|
|
10460
|
+
return readdirSync8(p, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join33(p, d.name));
|
|
9861
10461
|
} catch {
|
|
9862
10462
|
return [];
|
|
9863
10463
|
}
|
|
@@ -9866,7 +10466,7 @@ function scanSessionDirs(root, now, maxAgeMs = ACTIVE_DIR_MS) {
|
|
|
9866
10466
|
for (const day of list(u).sort().reverse().slice(0, 2))
|
|
9867
10467
|
for (const dir of list(day)) {
|
|
9868
10468
|
try {
|
|
9869
|
-
const at =
|
|
10469
|
+
const at = statSync13(dir).mtimeMs;
|
|
9870
10470
|
if (now - at <= maxAgeMs) out.push({ dir, at });
|
|
9871
10471
|
} catch {
|
|
9872
10472
|
}
|
|
@@ -10316,7 +10916,7 @@ var DumateWatch = class {
|
|
|
10316
10916
|
const now = this.#now();
|
|
10317
10917
|
if (now - this.#scanAt >= SCAN_DIRS_MS) {
|
|
10318
10918
|
this.#scanAt = now;
|
|
10319
|
-
for (const d of (this.#d.sessionDirs ?? (() => scanSessionDirs(
|
|
10919
|
+
for (const d of (this.#d.sessionDirs ?? (() => scanSessionDirs(join33(qianfanHome(), "workspace", "sessions"), now)))()) {
|
|
10320
10920
|
if (!this.#dirs.has(dirKey(d))) this.#touchDir(d, now);
|
|
10321
10921
|
}
|
|
10322
10922
|
}
|
|
@@ -10387,7 +10987,7 @@ function runHook(node, input, agentPid) {
|
|
|
10387
10987
|
return new Promise((resolve10) => {
|
|
10388
10988
|
let child;
|
|
10389
10989
|
try {
|
|
10390
|
-
child =
|
|
10990
|
+
child = spawn5(node, [installedHookPath(), "dumate"], {
|
|
10391
10991
|
stdio: ["pipe", "ignore", "ignore"],
|
|
10392
10992
|
env: { ...process.env, DESKBUDDY_AGENT_PID: String(agentPid) }
|
|
10393
10993
|
});
|
|
@@ -10411,8 +11011,376 @@ function runHook(node, input, agentPid) {
|
|
|
10411
11011
|
});
|
|
10412
11012
|
}
|
|
10413
11013
|
|
|
11014
|
+
// src/daemon/workbuddyAppDb.ts
|
|
11015
|
+
import { execFile as execFile8 } from "child_process";
|
|
11016
|
+
import { closeSync as closeSync8, existsSync as existsSync22, mkdirSync as mkdirSync25, openSync as openSync8, readdirSync as readdirSync9, readFileSync as readFileSync27, readSync as readSync7, renameSync as renameSync16, rmSync as rmSync15, statSync as statSync15, writeFileSync as writeFileSync23 } from "fs";
|
|
11017
|
+
import { createRequire as createRequire4 } from "module";
|
|
11018
|
+
import { basename as basename10, dirname as dirname21, isAbsolute as isAbsolute3, join as join35 } from "path";
|
|
11019
|
+
|
|
11020
|
+
// src/core/title.ts
|
|
11021
|
+
import { closeSync as closeSync7, existsSync as existsSync21, openSync as openSync7, readFileSync as readFileSync26, readSync as readSync6, statSync as statSync14 } from "fs";
|
|
11022
|
+
import { createRequire as createRequire3 } from "module";
|
|
11023
|
+
import { homedir as homedir11 } from "os";
|
|
11024
|
+
import { basename as basename9, dirname as dirname20, isAbsolute as isAbsolute2, join as join34 } from "path";
|
|
11025
|
+
var TRANSCRIPT_TAIL_BYTES = 64 * 1024;
|
|
11026
|
+
var SESSION_TITLE_MAX = 100;
|
|
11027
|
+
function cleanTitle(text) {
|
|
11028
|
+
if (typeof text !== "string") return "";
|
|
11029
|
+
const flat = text.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]+/g, " ").replace(/\s+/g, " ").trim();
|
|
11030
|
+
return Array.from(flat).slice(0, SESSION_TITLE_MAX).join("").trimEnd();
|
|
11031
|
+
}
|
|
11032
|
+
var requireHere = createRequire3(import.meta.url);
|
|
11033
|
+
var QODER_DATA_DIRS = { qoder_cn: "com.qodercn.app.stable", qoder: "com.qoder.app.stable" };
|
|
11034
|
+
var QODER_APP_DIR_ENV = { qoder_cn: "DESKBUDDY_QODER_CN_APP_DIR", qoder: "DESKBUDDY_QODER_APP_DIR" };
|
|
11035
|
+
function qoderDbPaths(tool, platform = process.platform, home = homedir11(), env = process.env) {
|
|
11036
|
+
let root;
|
|
11037
|
+
if (platform === "darwin") root = join34(home, "Library", "Application Support");
|
|
11038
|
+
else if (platform === "win32") root = env.APPDATA || join34(home, "AppData", "Roaming");
|
|
11039
|
+
else root = env.XDG_CONFIG_HOME || join34(home, ".config");
|
|
11040
|
+
const one = tool ? QODER_DATA_DIRS[tool] : void 0;
|
|
11041
|
+
const tools = one ? [tool] : ["qoder_cn", "qoder"];
|
|
11042
|
+
return tools.map((t) => join34(env[QODER_APP_DIR_ENV[t] ?? ""] || join34(root, QODER_DATA_DIRS[t] ?? ""), "main.sqlite"));
|
|
11043
|
+
}
|
|
11044
|
+
|
|
11045
|
+
// src/daemon/workbuddyAppDb.ts
|
|
11046
|
+
var WB_SESSION_COLUMNS = [
|
|
11047
|
+
"id",
|
|
11048
|
+
"cwd",
|
|
11049
|
+
"user_id",
|
|
11050
|
+
"title",
|
|
11051
|
+
"custom_title",
|
|
11052
|
+
"status",
|
|
11053
|
+
"created_at",
|
|
11054
|
+
"updated_at",
|
|
11055
|
+
"deleted_at",
|
|
11056
|
+
"is_playground",
|
|
11057
|
+
"model",
|
|
11058
|
+
"last_activity_at",
|
|
11059
|
+
"source_mode",
|
|
11060
|
+
"mode",
|
|
11061
|
+
"permission_mode",
|
|
11062
|
+
"transport",
|
|
11063
|
+
"unread",
|
|
11064
|
+
"addon_selection"
|
|
11065
|
+
];
|
|
11066
|
+
var WB_DB_SCRIPT = String.raw`
|
|
11067
|
+
const { DatabaseSync } = require('node:sqlite');
|
|
11068
|
+
const db = new DatabaseSync(input.db);
|
|
11069
|
+
try {
|
|
11070
|
+
db.exec('PRAGMA busy_timeout = 1000');
|
|
11071
|
+
const info = db.prepare('PRAGMA table_info(sessions)').all();
|
|
11072
|
+
const have = new Set(info.map((r) => r.name));
|
|
11073
|
+
const miss = cols.filter((c) => !have.has(c));
|
|
11074
|
+
if (miss.length) return { status: 'schema', detail: miss.join(',') };
|
|
11075
|
+
const pk = info.filter((r) => Number(r.pk) > 0).map((r) => r.name);
|
|
11076
|
+
if (pk.length !== 1 || pk[0] !== 'id') return { status: 'schema', detail: 'pk' };
|
|
11077
|
+
db.exec('BEGIN IMMEDIATE');
|
|
11078
|
+
try {
|
|
11079
|
+
if (db.prepare('select 1 as x from sessions where id = ?').get(input.sessionId)) {
|
|
11080
|
+
db.exec('ROLLBACK');
|
|
11081
|
+
return { status: 'exists' };
|
|
11082
|
+
}
|
|
11083
|
+
const u = db.prepare("select user_id from sessions where transport = 'local' and deleted_at is null and user_id <> '' order by coalesce(last_activity_at, updated_at, created_at) desc limit 1").get();
|
|
11084
|
+
if (!u || typeof u.user_id !== 'string' || !u.user_id) {
|
|
11085
|
+
db.exec('ROLLBACK');
|
|
11086
|
+
return { status: 'no_user' };
|
|
11087
|
+
}
|
|
11088
|
+
const addon = JSON.stringify({ version: 1, welcomeMode: 'working', interactionMode: 'craft', permissionMode: 'default' });
|
|
11089
|
+
const r = db.prepare("insert into sessions(id,cwd,user_id,title,custom_title,status,created_at,updated_at,deleted_at,is_playground,model,last_activity_at,source_mode,mode,permission_mode,transport,unread,addon_selection) values(?,?,?,?,NULL,'completed',?,?,NULL,0,'auto',?,'working','craft','default','local',1,?) on conflict(id) do nothing")
|
|
11090
|
+
.run(input.sessionId, input.cwd, u.user_id, input.title, input.createdAt, input.updatedAt, input.updatedAt, addon);
|
|
11091
|
+
db.exec('COMMIT');
|
|
11092
|
+
return { status: Number(r.changes) > 0 ? 'inserted' : 'exists' };
|
|
11093
|
+
} catch (e) {
|
|
11094
|
+
try { db.exec('ROLLBACK'); } catch (_) {}
|
|
11095
|
+
throw e;
|
|
11096
|
+
}
|
|
11097
|
+
} finally {
|
|
11098
|
+
db.close();
|
|
11099
|
+
}
|
|
11100
|
+
`;
|
|
11101
|
+
function wbElectronScript() {
|
|
11102
|
+
return `let out;try{const input=JSON.parse(require('fs').readFileSync(0,'utf8'));out=(function(require,input,cols){${WB_DB_SCRIPT}})(require,input,${JSON.stringify(WB_SESSION_COLUMNS)})}catch(e){out={status:'error',detail:String(e&&e.message||e).slice(0,200)}}process.stdout.write(JSON.stringify(out))`;
|
|
11103
|
+
}
|
|
11104
|
+
function findWbTranscript(configDir, sessionId) {
|
|
11105
|
+
if (!SESSION_UUID_RE.test(sessionId)) return null;
|
|
11106
|
+
let dirs;
|
|
11107
|
+
try {
|
|
11108
|
+
dirs = readdirSync9(join35(configDir, "projects"));
|
|
11109
|
+
} catch {
|
|
11110
|
+
return null;
|
|
11111
|
+
}
|
|
11112
|
+
for (const d of dirs) {
|
|
11113
|
+
const p = join35(configDir, "projects", d, `${sessionId}.jsonl`);
|
|
11114
|
+
if (existsSync22(p)) return p;
|
|
11115
|
+
}
|
|
11116
|
+
return null;
|
|
11117
|
+
}
|
|
11118
|
+
var SLICE = 64 * 1024;
|
|
11119
|
+
function readSlice(p, from) {
|
|
11120
|
+
const fd = openSync8(p, "r");
|
|
11121
|
+
try {
|
|
11122
|
+
const size = statSync15(p).size;
|
|
11123
|
+
const len = Math.min(size, SLICE);
|
|
11124
|
+
const buf = Buffer.alloc(len);
|
|
11125
|
+
readSync7(fd, buf, 0, len, from === "head" ? 0 : size - len);
|
|
11126
|
+
return buf.toString("utf8");
|
|
11127
|
+
} finally {
|
|
11128
|
+
closeSync8(fd);
|
|
11129
|
+
}
|
|
11130
|
+
}
|
|
11131
|
+
function tsOf(v) {
|
|
11132
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 1e12) return Math.floor(v);
|
|
11133
|
+
if (typeof v === "string") {
|
|
11134
|
+
const n = Date.parse(v);
|
|
11135
|
+
if (Number.isFinite(n)) return n;
|
|
11136
|
+
}
|
|
11137
|
+
return null;
|
|
11138
|
+
}
|
|
11139
|
+
function jsonLines(text) {
|
|
11140
|
+
const out = [];
|
|
11141
|
+
for (const l of text.split("\n")) {
|
|
11142
|
+
try {
|
|
11143
|
+
const o = JSON.parse(l);
|
|
11144
|
+
if (o && typeof o === "object" && !Array.isArray(o)) out.push(o);
|
|
11145
|
+
} catch {
|
|
11146
|
+
}
|
|
11147
|
+
}
|
|
11148
|
+
return out;
|
|
11149
|
+
}
|
|
11150
|
+
function userText2(o) {
|
|
11151
|
+
let c;
|
|
11152
|
+
if (o.type === "user") c = o.message?.content;
|
|
11153
|
+
else if (o.type === "message" && o.role === "user") c = o.content;
|
|
11154
|
+
else return "";
|
|
11155
|
+
if (typeof c === "string") return c;
|
|
11156
|
+
if (Array.isArray(c)) {
|
|
11157
|
+
for (const b of c) {
|
|
11158
|
+
if ((b?.type === "text" || b?.type === "input_text") && typeof b.text === "string" && !b.text.startsWith("<system-reminder")) return b.text;
|
|
11159
|
+
}
|
|
11160
|
+
}
|
|
11161
|
+
return "";
|
|
11162
|
+
}
|
|
11163
|
+
function wbTranscriptInfo(p) {
|
|
11164
|
+
const head = jsonLines(readSlice(p, "head"));
|
|
11165
|
+
const tail2 = jsonLines(readSlice(p, "tail"));
|
|
11166
|
+
const mtime = Math.floor(statSync15(p).mtimeMs);
|
|
11167
|
+
const hs = head.map((o) => tsOf(o.timestamp)).filter((n) => n !== null);
|
|
11168
|
+
const ts = tail2.map((o) => tsOf(o.timestamp)).filter((n) => n !== null);
|
|
11169
|
+
const createdAt = hs.length ? Math.min(...hs) : mtime;
|
|
11170
|
+
const updatedAt = Math.max(createdAt, ts.length ? Math.max(...ts) : mtime);
|
|
11171
|
+
let title = "";
|
|
11172
|
+
for (const o of head) {
|
|
11173
|
+
title = cleanTitle(userText2(o));
|
|
11174
|
+
if (title) break;
|
|
11175
|
+
}
|
|
11176
|
+
return { createdAt, updatedAt, title };
|
|
11177
|
+
}
|
|
11178
|
+
var electronBroken = false;
|
|
11179
|
+
function markElectronBroken(v = true) {
|
|
11180
|
+
electronBroken = v;
|
|
11181
|
+
}
|
|
11182
|
+
var requireHere2 = createRequire4(import.meta.url);
|
|
11183
|
+
function loadNodeSqlite() {
|
|
11184
|
+
const orig = process.emitWarning;
|
|
11185
|
+
process.emitWarning = function(...args) {
|
|
11186
|
+
if (String(args[0] ?? "").includes("SQLite")) return;
|
|
11187
|
+
return orig.apply(process, args);
|
|
11188
|
+
};
|
|
11189
|
+
try {
|
|
11190
|
+
return requireHere2("node:sqlite");
|
|
11191
|
+
} finally {
|
|
11192
|
+
process.emitWarning = orig;
|
|
11193
|
+
}
|
|
11194
|
+
}
|
|
11195
|
+
function wbElectronCandidates(appCliPath) {
|
|
11196
|
+
const m = /^(.*?)[\\/]resources[\\/]app\.asar\.unpacked[\\/]/i.exec(appCliPath);
|
|
11197
|
+
if (!m) return [];
|
|
11198
|
+
const root = m[1];
|
|
11199
|
+
if (/\.app[\\/]Contents$/.test(root)) return [join35(root, "MacOS", "Electron"), join35(root, "MacOS", "WorkBuddy")];
|
|
11200
|
+
return [join35(root, "WorkBuddy.exe")];
|
|
11201
|
+
}
|
|
11202
|
+
function defaultWbElectron(d = defaultLocateDeps()) {
|
|
11203
|
+
if ((d.platform ?? process.platform) === "win32") return null;
|
|
11204
|
+
if (electronBroken) return null;
|
|
11205
|
+
try {
|
|
11206
|
+
const app = locateWorkbuddyApp(d);
|
|
11207
|
+
if (!app) return null;
|
|
11208
|
+
const cli = [app.file, ...app.prefixArgs].find((p) => /app\.asar\.unpacked/i.test(p));
|
|
11209
|
+
if (!cli) return null;
|
|
11210
|
+
return wbElectronCandidates(cli).find((p) => d.isExecutable(p) || d.exists(p)) ?? null;
|
|
11211
|
+
} catch {
|
|
11212
|
+
return null;
|
|
11213
|
+
}
|
|
11214
|
+
}
|
|
11215
|
+
function defaultAppRunning(platform = process.platform) {
|
|
11216
|
+
return new Promise((resolve10) => {
|
|
11217
|
+
try {
|
|
11218
|
+
if (platform === "darwin") {
|
|
11219
|
+
execFile8("pgrep", ["-f", "WorkBuddy.app/Contents/MacOS/"], { timeout: 5e3 }, (err) => {
|
|
11220
|
+
const code = err?.code;
|
|
11221
|
+
resolve10(!err ? true : code === 1 ? false : true);
|
|
11222
|
+
});
|
|
11223
|
+
return;
|
|
11224
|
+
}
|
|
11225
|
+
if (platform === "win32") {
|
|
11226
|
+
execFile8(
|
|
11227
|
+
"tasklist",
|
|
11228
|
+
["/FI", "IMAGENAME eq WorkBuddy.exe", "/FO", "CSV", "/NH"],
|
|
11229
|
+
{ timeout: 1e4, windowsHide: true, encoding: "utf8" },
|
|
11230
|
+
(err, stdout7) => resolve10(err ? true : /"WorkBuddy\.exe"/i.test(String(stdout7)))
|
|
11231
|
+
);
|
|
11232
|
+
return;
|
|
11233
|
+
}
|
|
11234
|
+
resolve10(true);
|
|
11235
|
+
} catch {
|
|
11236
|
+
resolve10(true);
|
|
11237
|
+
}
|
|
11238
|
+
});
|
|
11239
|
+
}
|
|
11240
|
+
var PENDING_MAX = 20;
|
|
11241
|
+
var PENDING_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
11242
|
+
function pendingPath() {
|
|
11243
|
+
return join35(deskbuddyHome(), "workbuddy-pending.json");
|
|
11244
|
+
}
|
|
11245
|
+
function loadPendingFile() {
|
|
11246
|
+
try {
|
|
11247
|
+
const v = JSON.parse(readFileSync27(pendingPath(), "utf8"));
|
|
11248
|
+
return Array.isArray(v) ? v.filter((j) => j && typeof j.sessionId === "string" && typeof j.cwd === "string") : [];
|
|
11249
|
+
} catch {
|
|
11250
|
+
return [];
|
|
11251
|
+
}
|
|
11252
|
+
}
|
|
11253
|
+
function savePendingFile(jobs) {
|
|
11254
|
+
try {
|
|
11255
|
+
if (jobs.length === 0) {
|
|
11256
|
+
rmSync15(pendingPath(), { force: true });
|
|
11257
|
+
return;
|
|
11258
|
+
}
|
|
11259
|
+
mkdirSync25(deskbuddyHome(), { recursive: true });
|
|
11260
|
+
const tmp = pendingPath() + ".tmp";
|
|
11261
|
+
writeFileSync23(tmp, JSON.stringify(jobs), { mode: 384 });
|
|
11262
|
+
renameSync16(tmp, pendingPath());
|
|
11263
|
+
} catch {
|
|
11264
|
+
}
|
|
11265
|
+
}
|
|
11266
|
+
var defaultWbAppDbDeps = () => ({
|
|
11267
|
+
dbPath: () => workbuddyDbPath(),
|
|
11268
|
+
configDir: () => dirname21(workbuddySettingsPath()),
|
|
11269
|
+
loadSqlite: loadNodeSqlite,
|
|
11270
|
+
electron: () => defaultWbElectron(),
|
|
11271
|
+
runElectron: (bin, script, input) => new Promise((resolve10, reject) => {
|
|
11272
|
+
const env = { ...process.env, ELECTRON_RUN_AS_NODE: "1" };
|
|
11273
|
+
const c = execFile8(
|
|
11274
|
+
bin,
|
|
11275
|
+
["-e", script],
|
|
11276
|
+
{ env, timeout: 15e3, windowsHide: true, maxBuffer: 1024 * 1024 },
|
|
11277
|
+
(err, stdout7) => err ? reject(err) : resolve10(String(stdout7))
|
|
11278
|
+
);
|
|
11279
|
+
c.stdin?.on("error", () => {
|
|
11280
|
+
});
|
|
11281
|
+
c.stdin?.end(input);
|
|
11282
|
+
}),
|
|
11283
|
+
appRunning: () => defaultAppRunning(),
|
|
11284
|
+
loadPending: loadPendingFile,
|
|
11285
|
+
savePending: savePendingFile,
|
|
11286
|
+
onElectronBroken: () => markElectronBroken()
|
|
11287
|
+
});
|
|
11288
|
+
async function runDb(input, d) {
|
|
11289
|
+
let mod = null;
|
|
11290
|
+
try {
|
|
11291
|
+
mod = d.loadSqlite();
|
|
11292
|
+
} catch {
|
|
11293
|
+
mod = null;
|
|
11294
|
+
}
|
|
11295
|
+
if (mod) {
|
|
11296
|
+
const fn = new Function("require", "input", "cols", WB_DB_SCRIPT);
|
|
11297
|
+
try {
|
|
11298
|
+
return fn((id) => id === "node:sqlite" ? mod : requireHere2(id), input, WB_SESSION_COLUMNS);
|
|
11299
|
+
} catch (e) {
|
|
11300
|
+
return { status: "error", detail: String(e?.message ?? e).slice(0, 200) };
|
|
11301
|
+
}
|
|
11302
|
+
}
|
|
11303
|
+
const bin = d.electron();
|
|
11304
|
+
if (!bin) return "no_sqlite";
|
|
11305
|
+
let raw;
|
|
11306
|
+
try {
|
|
11307
|
+
raw = await d.runElectron(bin, wbElectronScript(), JSON.stringify(input));
|
|
11308
|
+
} catch (e) {
|
|
11309
|
+
return { status: "error", detail: `electron: ${String(e?.message ?? e).slice(0, 160)}` };
|
|
11310
|
+
}
|
|
11311
|
+
let out = null;
|
|
11312
|
+
try {
|
|
11313
|
+
out = JSON.parse(raw);
|
|
11314
|
+
} catch {
|
|
11315
|
+
out = null;
|
|
11316
|
+
}
|
|
11317
|
+
if (out && typeof out.status === "string") return out;
|
|
11318
|
+
try {
|
|
11319
|
+
d.onElectronBroken?.();
|
|
11320
|
+
} catch {
|
|
11321
|
+
}
|
|
11322
|
+
return { status: "error", detail: "electron: bad output\uFF0C\u4E4B\u540E\u4E0D\u518D\u8D70\u8FD9\u6761\u8DEF" };
|
|
11323
|
+
}
|
|
11324
|
+
async function registerWorkbuddySession(job, d = defaultWbAppDbDeps(), now = Date.now()) {
|
|
11325
|
+
try {
|
|
11326
|
+
if (!SESSION_UUID_RE.test(job.sessionId)) return "skip bad_session";
|
|
11327
|
+
if (await d.appRunning()) {
|
|
11328
|
+
const list = d.loadPending().filter((j) => j.sessionId !== job.sessionId);
|
|
11329
|
+
list.push({ sessionId: job.sessionId, cwd: job.cwd, fallbackTitle: job.fallbackTitle, at: now });
|
|
11330
|
+
d.savePending(list.slice(-PENDING_MAX));
|
|
11331
|
+
return "deferred app_running\uFF08WorkBuddy \u5F00\u7740\uFF0C\u5173\u6389\u540E\u518D\u767B\u8BB0\uFF09";
|
|
11332
|
+
}
|
|
11333
|
+
const flushed = await flushWithAppClosed(d, now, job.sessionId);
|
|
11334
|
+
const r = await registerNow(job, d);
|
|
11335
|
+
return flushed ? `${r}\uFF08\u53E6\u8865\u767B\u8BB0 ${flushed} \u6761\uFF09` : r;
|
|
11336
|
+
} catch (e) {
|
|
11337
|
+
return `error ${String(e?.message ?? e).slice(0, 160)}`;
|
|
11338
|
+
}
|
|
11339
|
+
}
|
|
11340
|
+
async function flushPendingWorkbuddy(d = defaultWbAppDbDeps(), now = Date.now()) {
|
|
11341
|
+
try {
|
|
11342
|
+
if (d.loadPending().length === 0) return "";
|
|
11343
|
+
if (await d.appRunning()) return "";
|
|
11344
|
+
const n = await flushWithAppClosed(d, now);
|
|
11345
|
+
return n ? `\u8865\u767B\u8BB0 ${n} \u6761` : "";
|
|
11346
|
+
} catch (e) {
|
|
11347
|
+
return `error ${String(e?.message ?? e).slice(0, 160)}`;
|
|
11348
|
+
}
|
|
11349
|
+
}
|
|
11350
|
+
async function flushWithAppClosed(d, now, except) {
|
|
11351
|
+
const list = d.loadPending();
|
|
11352
|
+
if (list.length === 0) return 0;
|
|
11353
|
+
let n = 0;
|
|
11354
|
+
for (const j of list) {
|
|
11355
|
+
if (j.sessionId === except || j.at !== void 0 && now - j.at > PENDING_TTL_MS) continue;
|
|
11356
|
+
const r = await registerNow(j, d);
|
|
11357
|
+
if (r === "inserted") n++;
|
|
11358
|
+
}
|
|
11359
|
+
d.savePending([]);
|
|
11360
|
+
return n;
|
|
11361
|
+
}
|
|
11362
|
+
async function registerNow(job, d) {
|
|
11363
|
+
try {
|
|
11364
|
+
if (!SESSION_UUID_RE.test(job.sessionId)) return "skip bad_session";
|
|
11365
|
+
if (!job.cwd || !isAbsolute3(job.cwd)) return "skip bad_cwd";
|
|
11366
|
+
const db = d.dbPath();
|
|
11367
|
+
if (!db || !existsSync22(db)) return "skip no_db";
|
|
11368
|
+
const tp = findWbTranscript(d.configDir(), job.sessionId);
|
|
11369
|
+
if (!tp) return "skip no_transcript";
|
|
11370
|
+
const info = wbTranscriptInfo(tp);
|
|
11371
|
+
const title = info.title || cleanTitle(job.fallbackTitle ?? "") || basename10(job.cwd) || job.cwd;
|
|
11372
|
+
const r = await runDb({ db, sessionId: job.sessionId, cwd: job.cwd, title, createdAt: info.createdAt, updatedAt: info.updatedAt }, d);
|
|
11373
|
+
if (r === "no_sqlite") return "skip no_sqlite";
|
|
11374
|
+
if (r.status === "schema") return `skip schema ${r.detail ?? ""}`.trim();
|
|
11375
|
+
if (r.status === "no_user") return "skip no_user";
|
|
11376
|
+
return [r.status, r.detail ?? ""].filter(Boolean).join(" ");
|
|
11377
|
+
} catch (e) {
|
|
11378
|
+
return `error ${String(e?.message ?? e).slice(0, 160)}`;
|
|
11379
|
+
}
|
|
11380
|
+
}
|
|
11381
|
+
|
|
10414
11382
|
// src/daemon/codexWatch.ts
|
|
10415
|
-
import { execFileSync as execFileSync15, spawn as
|
|
11383
|
+
import { execFileSync as execFileSync15, spawn as spawn6 } from "child_process";
|
|
10416
11384
|
import WebSocket from "ws";
|
|
10417
11385
|
var TURN_LIMIT_MS = 10 * 60 * 1e3;
|
|
10418
11386
|
var QUEUE_MAX_MS = 10 * 60 * 1e3;
|
|
@@ -10499,6 +11467,8 @@ var CodexWatch = class {
|
|
|
10499
11467
|
#idle = /* @__PURE__ */ new Set();
|
|
10500
11468
|
/** 连接代数:断线重连后,上一条连接遗留的订阅重试不再跑 */
|
|
10501
11469
|
#gen = 0;
|
|
11470
|
+
/** 这条连接上 thread/resume 不能带 excludeTurns(app-server 要求 experimentalApi,见 #resume) */
|
|
11471
|
+
#noExcludeTurns = false;
|
|
10502
11472
|
/** initialize 已成功 */
|
|
10503
11473
|
#ready = false;
|
|
10504
11474
|
/** app-server 里已加载的会话(thread/loaded/list + 通知维护)。窗口开着 = 能往里发,据此上报 remoteWindow */
|
|
@@ -10565,6 +11535,7 @@ var CodexWatch = class {
|
|
|
10565
11535
|
this.#ws = ws;
|
|
10566
11536
|
this.#url = url;
|
|
10567
11537
|
this.#gen++;
|
|
11538
|
+
this.#noExcludeTurns = false;
|
|
10568
11539
|
ws.on("open", () => void this.#onOpen());
|
|
10569
11540
|
ws.on("message", (buf) => this.#onMessage(String(buf)));
|
|
10570
11541
|
ws.on("close", (code) => this.#teardown(`\u5173\u95ED ${code}`));
|
|
@@ -10623,8 +11594,21 @@ var CodexWatch = class {
|
|
|
10623
11594
|
this.#send({ id, method, params });
|
|
10624
11595
|
});
|
|
10625
11596
|
}
|
|
11597
|
+
/**
|
|
11598
|
+
* thread/resume。excludeTurns(不回历史轮次,省流量)是实验性字段:app-server 回 -32600 且提到 experimentalApi 时,
|
|
11599
|
+
* 去掉它重发,并且这条连接之后都不再带。不带的代价只是回话里多了历史轮次;#applyThread 只看最后一轮是否进行中,没有副作用
|
|
11600
|
+
*/
|
|
11601
|
+
async #resume(threadId, excludeTurns) {
|
|
11602
|
+
const gen = this.#gen;
|
|
11603
|
+
if (!excludeTurns || this.#noExcludeTurns) return this.#request("thread/resume", { threadId });
|
|
11604
|
+
const r = await this.#request("thread/resume", { threadId, excludeTurns: true });
|
|
11605
|
+
if (!needsExperimental(r) || gen !== this.#gen) return r;
|
|
11606
|
+
this.#noExcludeTurns = true;
|
|
11607
|
+
this.#d.log("Codex app-server \u4E0D\u8BA4 excludeTurns\uFF08\u8981 experimentalApi\uFF09\uFF0C\u53BB\u6389\u540E\u91CD\u65B0\u8BA2\u9605");
|
|
11608
|
+
return this.#request("thread/resume", { threadId });
|
|
11609
|
+
}
|
|
10626
11610
|
async #onOpen() {
|
|
10627
|
-
const r = await this.#request("initialize", { clientInfo: { name: "deskbuddy", version: "1" } });
|
|
11611
|
+
const r = await this.#request("initialize", { clientInfo: { name: "deskbuddy", version: "1" }, capabilities: { experimentalApi: true } });
|
|
10628
11612
|
if (r.error) {
|
|
10629
11613
|
this.#d.log(`Codex app-server \u521D\u59CB\u5316\u5931\u8D25\uFF1A${JSON.stringify(r.error).slice(0, 120)}`);
|
|
10630
11614
|
this.#teardown();
|
|
@@ -10748,7 +11732,7 @@ var CodexWatch = class {
|
|
|
10748
11732
|
const gen = this.#gen;
|
|
10749
11733
|
const attempt = async () => {
|
|
10750
11734
|
if (!this.#ws || gen !== this.#gen || !this.#retrying.has(threadId)) return;
|
|
10751
|
-
const req2 = this.#
|
|
11735
|
+
const req2 = this.#resume(threadId, !this.#waiting.has(threadId));
|
|
10752
11736
|
this.#inflight.set(threadId, req2);
|
|
10753
11737
|
const r = await req2;
|
|
10754
11738
|
if (this.#inflight.get(threadId) === req2) this.#inflight.delete(threadId);
|
|
@@ -10853,7 +11837,7 @@ var CodexWatch = class {
|
|
|
10853
11837
|
}
|
|
10854
11838
|
if (!this.#subscribed.has(tid)) {
|
|
10855
11839
|
this.#retrying.delete(tid);
|
|
10856
|
-
const req3 = this.#
|
|
11840
|
+
const req3 = this.#resume(tid, !this.#waiting.has(tid));
|
|
10857
11841
|
this.#inflight.set(tid, req3);
|
|
10858
11842
|
const r2 = await req3;
|
|
10859
11843
|
if (this.#inflight.get(tid) === req3) this.#inflight.delete(tid);
|
|
@@ -11244,6 +12228,11 @@ var CodexWatch = class {
|
|
|
11244
12228
|
}
|
|
11245
12229
|
};
|
|
11246
12230
|
var NO_FILE_LIST = "\u53D6\u4E0D\u5230\u6539\u52A8\u7684\u6587\u4EF6";
|
|
12231
|
+
function needsExperimental(r) {
|
|
12232
|
+
const e = r.error;
|
|
12233
|
+
if (!e || r.local) return false;
|
|
12234
|
+
return e.code === -32600 && typeof e.message === "string" && /experimentalApi/.test(e.message);
|
|
12235
|
+
}
|
|
11247
12236
|
var FEEDBACK_EXPIRE_MS = 2 * 60 * 1e3;
|
|
11248
12237
|
function nativeReply(reply, field) {
|
|
11249
12238
|
const n = reply.native;
|
|
@@ -11371,7 +12360,7 @@ function runCodexHook(node, input) {
|
|
|
11371
12360
|
if (!pid) return;
|
|
11372
12361
|
let child;
|
|
11373
12362
|
try {
|
|
11374
|
-
child =
|
|
12363
|
+
child = spawn6(node, [installedHookPath(), "codex"], {
|
|
11375
12364
|
stdio: ["pipe", "ignore", "ignore"],
|
|
11376
12365
|
env: { ...process.env, DESKBUDDY_AGENT_PID: String(pid) }
|
|
11377
12366
|
});
|
|
@@ -11410,23 +12399,23 @@ function codexQuestionInput(p) {
|
|
|
11410
12399
|
}
|
|
11411
12400
|
|
|
11412
12401
|
// src/core/codexRemote.ts
|
|
11413
|
-
import { mkdirSync as
|
|
11414
|
-
import { join as
|
|
12402
|
+
import { mkdirSync as mkdirSync26, renameSync as renameSync17, rmSync as rmSync16, writeFileSync as writeFileSync24 } from "fs";
|
|
12403
|
+
import { join as join36 } from "path";
|
|
11415
12404
|
function codexRemotePath() {
|
|
11416
|
-
return
|
|
12405
|
+
return join36(deskbuddyHome(), "codex-remote.json");
|
|
11417
12406
|
}
|
|
11418
12407
|
function writeCodexRemote(threads, pid = process.pid, now = Date.now()) {
|
|
11419
12408
|
try {
|
|
11420
|
-
|
|
12409
|
+
mkdirSync26(deskbuddyHome(), { recursive: true });
|
|
11421
12410
|
const p = codexRemotePath();
|
|
11422
|
-
|
|
11423
|
-
|
|
12411
|
+
writeFileSync24(p + ".tmp", JSON.stringify({ pid, threads: [...threads], at: now }), "utf8");
|
|
12412
|
+
renameSync17(p + ".tmp", p);
|
|
11424
12413
|
} catch {
|
|
11425
12414
|
}
|
|
11426
12415
|
}
|
|
11427
12416
|
function removeCodexRemote() {
|
|
11428
12417
|
try {
|
|
11429
|
-
|
|
12418
|
+
rmSync16(codexRemotePath(), { force: true });
|
|
11430
12419
|
} catch {
|
|
11431
12420
|
}
|
|
11432
12421
|
}
|
|
@@ -11457,19 +12446,19 @@ function isTerminalReport(s2) {
|
|
|
11457
12446
|
return rest === "";
|
|
11458
12447
|
}
|
|
11459
12448
|
var PERMISSION_RE = /Do you want to (proceed|make|create|run|allow)|[❯>]\s*1\. Yes/;
|
|
11460
|
-
function inspectLines(
|
|
11461
|
-
const text =
|
|
12449
|
+
function inspectLines(lines3, undimmed) {
|
|
12450
|
+
const text = lines3.join("\n");
|
|
11462
12451
|
const trust = TRUST_RE.test(text);
|
|
11463
12452
|
const permission = PERMISSION_RE.test(text);
|
|
11464
12453
|
const rules = [];
|
|
11465
|
-
|
|
12454
|
+
lines3.forEach((l, i) => {
|
|
11466
12455
|
if (/^─{20,}/.test(l.trim())) rules.push(i);
|
|
11467
12456
|
});
|
|
11468
12457
|
let input = null;
|
|
11469
12458
|
if (rules.length >= 2) {
|
|
11470
12459
|
const a = rules[rules.length - 2];
|
|
11471
12460
|
const b = rules[rules.length - 1];
|
|
11472
|
-
const src = undimmed && undimmed.length ===
|
|
12461
|
+
const src = undimmed && undimmed.length === lines3.length ? undimmed : lines3;
|
|
11473
12462
|
input = src.slice(a + 1, b).map((l) => l.trimEnd()).join("\n").replace(/^\s*[❯>]\s?/, "").trimEnd();
|
|
11474
12463
|
}
|
|
11475
12464
|
const v = { permission, input };
|
|
@@ -11493,18 +12482,18 @@ function windowForSession(sessionId) {
|
|
|
11493
12482
|
}
|
|
11494
12483
|
|
|
11495
12484
|
// src/core/interrupt.ts
|
|
11496
|
-
import { closeSync as
|
|
11497
|
-
import { dirname as
|
|
12485
|
+
import { closeSync as closeSync9, openSync as openSync9, readdirSync as readdirSync10, readSync as readSync8, statSync as statSync16 } from "fs";
|
|
12486
|
+
import { dirname as dirname22, join as join37 } from "path";
|
|
11498
12487
|
var INTERRUPT_TOOLS = ["claude_code", "qoder_cn", "qoder"];
|
|
11499
12488
|
function transcriptHome(tool) {
|
|
11500
12489
|
if (tool === "claude_code") return claudeHome();
|
|
11501
|
-
if (tool === "qoder_cn") return
|
|
11502
|
-
if (tool === "qoder") return
|
|
12490
|
+
if (tool === "qoder_cn") return dirname22(qoderCnSettingsPath());
|
|
12491
|
+
if (tool === "qoder") return dirname22(qoderSettingsPath());
|
|
11503
12492
|
return null;
|
|
11504
12493
|
}
|
|
11505
12494
|
var TAIL = 64 * 1024;
|
|
11506
12495
|
var SID_RE2 = /^[A-Za-z0-9_-]{1,64}$/;
|
|
11507
|
-
function
|
|
12496
|
+
function userText3(content) {
|
|
11508
12497
|
if (typeof content === "string") return content;
|
|
11509
12498
|
if (!Array.isArray(content)) return "";
|
|
11510
12499
|
return content.filter((x) => x && x.type === "text").map((x) => String(x.text ?? "")).join("\n");
|
|
@@ -11512,14 +12501,14 @@ function userText2(content) {
|
|
|
11512
12501
|
function interruptedAt(file) {
|
|
11513
12502
|
let fd;
|
|
11514
12503
|
try {
|
|
11515
|
-
const st =
|
|
12504
|
+
const st = statSync16(file);
|
|
11516
12505
|
const len = Math.min(st.size, TAIL);
|
|
11517
12506
|
const buf = Buffer.alloc(len);
|
|
11518
|
-
fd =
|
|
11519
|
-
|
|
11520
|
-
const
|
|
11521
|
-
for (let i =
|
|
11522
|
-
const line =
|
|
12507
|
+
fd = openSync9(file, "r");
|
|
12508
|
+
readSync8(fd, buf, 0, len, st.size - len);
|
|
12509
|
+
const lines3 = buf.toString("utf8").split("\n");
|
|
12510
|
+
for (let i = lines3.length - 1; i >= 0; i--) {
|
|
12511
|
+
const line = lines3[i];
|
|
11523
12512
|
if (line.indexOf('"assistant"') === -1 && line.indexOf('"user"') === -1) continue;
|
|
11524
12513
|
let o;
|
|
11525
12514
|
try {
|
|
@@ -11530,7 +12519,7 @@ function interruptedAt(file) {
|
|
|
11530
12519
|
if (!o || !o.message || o.isSidechain === true) continue;
|
|
11531
12520
|
if (o.type === "assistant") return null;
|
|
11532
12521
|
if (o.type !== "user") continue;
|
|
11533
|
-
const text =
|
|
12522
|
+
const text = userText3(o.message.content).trim();
|
|
11534
12523
|
if (!text) return null;
|
|
11535
12524
|
if (INTERRUPT_RE.test(text)) {
|
|
11536
12525
|
const at = typeof o.timestamp === "string" ? Date.parse(o.timestamp) : NaN;
|
|
@@ -11545,7 +12534,7 @@ function interruptedAt(file) {
|
|
|
11545
12534
|
} finally {
|
|
11546
12535
|
if (fd !== void 0) {
|
|
11547
12536
|
try {
|
|
11548
|
-
|
|
12537
|
+
closeSync9(fd);
|
|
11549
12538
|
} catch {
|
|
11550
12539
|
}
|
|
11551
12540
|
}
|
|
@@ -11556,23 +12545,23 @@ function claudeTranscriptFile(sessionId, cwd, home = claudeHome()) {
|
|
|
11556
12545
|
const name = `${sessionId}.jsonl`;
|
|
11557
12546
|
const exists = (f) => {
|
|
11558
12547
|
try {
|
|
11559
|
-
return
|
|
12548
|
+
return statSync16(f).isFile();
|
|
11560
12549
|
} catch {
|
|
11561
12550
|
return false;
|
|
11562
12551
|
}
|
|
11563
12552
|
};
|
|
11564
12553
|
if (cwd) {
|
|
11565
|
-
const f =
|
|
12554
|
+
const f = join37(home, "projects", projectDirName(cwd), name);
|
|
11566
12555
|
if (exists(f)) return f;
|
|
11567
12556
|
}
|
|
11568
12557
|
let dirs = [];
|
|
11569
12558
|
try {
|
|
11570
|
-
dirs =
|
|
12559
|
+
dirs = readdirSync10(join37(home, "projects"));
|
|
11571
12560
|
} catch {
|
|
11572
12561
|
return null;
|
|
11573
12562
|
}
|
|
11574
12563
|
for (const d of dirs) {
|
|
11575
|
-
const f =
|
|
12564
|
+
const f = join37(home, "projects", d, name);
|
|
11576
12565
|
if (exists(f)) return f;
|
|
11577
12566
|
}
|
|
11578
12567
|
return null;
|
|
@@ -11589,9 +12578,272 @@ function shouldSettleInterrupt(st, markerAt, now) {
|
|
|
11589
12578
|
return true;
|
|
11590
12579
|
}
|
|
11591
12580
|
|
|
12581
|
+
// src/daemon/qoderAppDb.ts
|
|
12582
|
+
import { execFile as execFile9 } from "child_process";
|
|
12583
|
+
import { createHash as createHash4 } from "crypto";
|
|
12584
|
+
import { existsSync as existsSync23 } from "fs";
|
|
12585
|
+
import { access, open, readdir, stat } from "fs/promises";
|
|
12586
|
+
import { createRequire as createRequire5 } from "module";
|
|
12587
|
+
import { basename as basename11, dirname as dirname23, isAbsolute as isAbsolute4, join as join38 } from "path";
|
|
12588
|
+
var QODER_APP_TOOLS = ["qoder_cn", "qoder"];
|
|
12589
|
+
var DB_SCRIPT = String.raw`
|
|
12590
|
+
const { DatabaseSync } = require('node:sqlite');
|
|
12591
|
+
const NEED = {
|
|
12592
|
+
register: {
|
|
12593
|
+
chat_sessions: ['session_id','session_kind','conversation_mode','product_mode','title','cwd','execution_kind','workspace_id','model','permission_mode','additional_direcotries','extra_json','created_at','updated_at','execution_target_json','registration_json','archived','unread','deleted_at'],
|
|
12594
|
+
workspaces: ['workspace_id','name','workspace_source','root_paths_json','product_mode','extra_json','created_at','updated_at','archived','deleted_at'],
|
|
12595
|
+
},
|
|
12596
|
+
clear: { chat_session_messages: ['session_id','source'] },
|
|
12597
|
+
};
|
|
12598
|
+
const isBusy = (e) => /SQLITE_BUSY|database is locked|database table is locked/i.test(String(e && (e.code || '') + ' ' + (e.message || e)));
|
|
12599
|
+
const db = new DatabaseSync(input.db);
|
|
12600
|
+
try {
|
|
12601
|
+
db.exec('PRAGMA busy_timeout = ' + Math.max(0, Math.min(10000, Math.floor(Number(input.busyMs) || 3000))));
|
|
12602
|
+
const need = NEED[input.op];
|
|
12603
|
+
if (!need) return { status: 'error', detail: 'bad op' };
|
|
12604
|
+
for (const t of Object.keys(need)) {
|
|
12605
|
+
const have = new Set(db.prepare('PRAGMA table_info(' + t + ')').all().map((r) => r.name));
|
|
12606
|
+
const miss = need[t].filter((c) => !have.has(c));
|
|
12607
|
+
if (miss.length) return { status: 'schema', detail: t + ':' + miss.join(',') };
|
|
12608
|
+
}
|
|
12609
|
+
if (input.op === 'clear') {
|
|
12610
|
+
try {
|
|
12611
|
+
const r = db.prepare("delete from chat_session_messages where session_id = ? and source = 'cli-import'").run(input.sessionId);
|
|
12612
|
+
return { status: 'cleared', n: Number(r.changes) };
|
|
12613
|
+
} catch (e) {
|
|
12614
|
+
if (isBusy(e)) return { status: 'busy' };
|
|
12615
|
+
throw e;
|
|
12616
|
+
}
|
|
12617
|
+
}
|
|
12618
|
+
try {
|
|
12619
|
+
db.exec('BEGIN IMMEDIATE');
|
|
12620
|
+
} catch (e) {
|
|
12621
|
+
if (isBusy(e)) return { status: 'busy' };
|
|
12622
|
+
throw e;
|
|
12623
|
+
}
|
|
12624
|
+
try {
|
|
12625
|
+
if (db.prepare('select 1 as x from chat_sessions where session_id = ?').get(input.sessionId)) {
|
|
12626
|
+
db.exec('ROLLBACK');
|
|
12627
|
+
return { status: 'exists' };
|
|
12628
|
+
}
|
|
12629
|
+
const ws = db.prepare('select archived, deleted_at from workspaces where workspace_id = ?').get(input.wsid);
|
|
12630
|
+
let wsUse = null;
|
|
12631
|
+
if (!ws) {
|
|
12632
|
+
db.prepare("insert into workspaces(workspace_id,name,workspace_source,root_paths_json,product_mode,extra_json,created_at,updated_at,archived,deleted_at) values(?,?,'local',?,'coding','{}',?,?,0,NULL) on conflict(workspace_id) do nothing")
|
|
12633
|
+
.run(input.wsid, input.wsName, JSON.stringify([input.cwd]), input.now, input.now);
|
|
12634
|
+
wsUse = input.wsid;
|
|
12635
|
+
} else if (Number(ws.archived) === 0 && ws.deleted_at == null) {
|
|
12636
|
+
wsUse = input.wsid;
|
|
12637
|
+
}
|
|
12638
|
+
const extra = { source: 'workspace', cliSessionReady: true, modelSource: { kind: 'system' }, provisionalTitle: input.title, titleResolved: true, titleSource: 'user', titleRevision: 0 };
|
|
12639
|
+
const target = { mode: 'local', host: { id: 'local', kind: 'local', label: 'Local' }, workspace: { id: input.wsid, path: input.cwd } };
|
|
12640
|
+
const r = db.prepare("insert into chat_sessions(session_id,session_kind,conversation_mode,product_mode,title,cwd,execution_kind,workspace_id,model,permission_mode,additional_direcotries,extra_json,created_at,updated_at,execution_target_json,archived,unread) values(?,'standard','normal','coding',?,?,'local',?,'auto','auto','{}',?,?,?,?,0,1) on conflict(session_id) do nothing")
|
|
12641
|
+
.run(input.sessionId, input.title, input.cwd, wsUse, JSON.stringify(extra), input.createdAt, input.updatedAt, JSON.stringify(target));
|
|
12642
|
+
db.exec('COMMIT');
|
|
12643
|
+
return { status: Number(r.changes) > 0 ? 'inserted' : 'exists', detail: wsUse ? 'ws' : 'no_ws' };
|
|
12644
|
+
} catch (e) {
|
|
12645
|
+
try { db.exec('ROLLBACK'); } catch (_) {}
|
|
12646
|
+
if (isBusy(e)) return { status: 'busy' };
|
|
12647
|
+
throw e;
|
|
12648
|
+
}
|
|
12649
|
+
} finally {
|
|
12650
|
+
db.close();
|
|
12651
|
+
}
|
|
12652
|
+
`;
|
|
12653
|
+
function electronScript() {
|
|
12654
|
+
return `let out;try{const input=JSON.parse(require('fs').readFileSync(0,'utf8'));out=(function(require,input){${DB_SCRIPT}})(require,input)}catch(e){out={status:'error',detail:String(e&&e.message||e).slice(0,200)}}process.stdout.write(JSON.stringify(out))`;
|
|
12655
|
+
}
|
|
12656
|
+
function workspaceId(cwd) {
|
|
12657
|
+
return "ws_" + createHash4("sha256").update("local\0" + cwd).digest("hex");
|
|
12658
|
+
}
|
|
12659
|
+
function qoderConfigDir(tool) {
|
|
12660
|
+
return dirname23(tool === "qoder" ? qoderSettingsPath() : qoderCnSettingsPath());
|
|
12661
|
+
}
|
|
12662
|
+
async function findTranscript(configDir, sessionId) {
|
|
12663
|
+
if (!SESSION_UUID_RE.test(sessionId)) return null;
|
|
12664
|
+
const root = join38(configDir, "projects");
|
|
12665
|
+
let dirs;
|
|
12666
|
+
try {
|
|
12667
|
+
dirs = await readdir(root);
|
|
12668
|
+
} catch {
|
|
12669
|
+
return null;
|
|
12670
|
+
}
|
|
12671
|
+
for (const d of dirs) {
|
|
12672
|
+
const p = join38(root, d, `${sessionId}.jsonl`);
|
|
12673
|
+
try {
|
|
12674
|
+
await access(p);
|
|
12675
|
+
return p;
|
|
12676
|
+
} catch {
|
|
12677
|
+
}
|
|
12678
|
+
}
|
|
12679
|
+
return null;
|
|
12680
|
+
}
|
|
12681
|
+
var SLICE2 = 64 * 1024;
|
|
12682
|
+
async function readSlice2(p, from) {
|
|
12683
|
+
const fh = await open(p, "r");
|
|
12684
|
+
try {
|
|
12685
|
+
const size = (await fh.stat()).size;
|
|
12686
|
+
const len = Math.min(size, SLICE2);
|
|
12687
|
+
const buf = Buffer.alloc(len);
|
|
12688
|
+
await fh.read(buf, 0, len, from === "head" ? 0 : size - len);
|
|
12689
|
+
return buf.toString("utf8");
|
|
12690
|
+
} finally {
|
|
12691
|
+
await fh.close();
|
|
12692
|
+
}
|
|
12693
|
+
}
|
|
12694
|
+
function tsOf2(v) {
|
|
12695
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 1e12) return Math.floor(v);
|
|
12696
|
+
if (typeof v === "string") {
|
|
12697
|
+
const n = Date.parse(v);
|
|
12698
|
+
if (Number.isFinite(n)) return n;
|
|
12699
|
+
}
|
|
12700
|
+
return null;
|
|
12701
|
+
}
|
|
12702
|
+
function lines2(text) {
|
|
12703
|
+
const out = [];
|
|
12704
|
+
for (const l of text.split("\n")) {
|
|
12705
|
+
try {
|
|
12706
|
+
const o = JSON.parse(l);
|
|
12707
|
+
if (o && typeof o === "object") out.push(o);
|
|
12708
|
+
} catch {
|
|
12709
|
+
}
|
|
12710
|
+
}
|
|
12711
|
+
return out;
|
|
12712
|
+
}
|
|
12713
|
+
function userText4(o) {
|
|
12714
|
+
if (o.type !== "user") return "";
|
|
12715
|
+
const c = o.message?.content;
|
|
12716
|
+
if (typeof c === "string") return c;
|
|
12717
|
+
if (Array.isArray(c)) {
|
|
12718
|
+
for (const b of c) if (b?.type === "text" && typeof b.text === "string") return b.text;
|
|
12719
|
+
}
|
|
12720
|
+
return "";
|
|
12721
|
+
}
|
|
12722
|
+
async function transcriptInfo(p) {
|
|
12723
|
+
const head = lines2(await readSlice2(p, "head"));
|
|
12724
|
+
const tail2 = lines2(await readSlice2(p, "tail"));
|
|
12725
|
+
const mtime = Math.floor((await stat(p)).mtimeMs);
|
|
12726
|
+
const hs = head.map((o) => tsOf2(o.timestamp)).filter((n) => n !== null);
|
|
12727
|
+
const ts = tail2.map((o) => tsOf2(o.timestamp)).filter((n) => n !== null);
|
|
12728
|
+
const createdAt = hs.length ? Math.min(...hs) : mtime;
|
|
12729
|
+
const updatedAt = Math.max(createdAt, ts.length ? Math.max(...ts) : mtime);
|
|
12730
|
+
let title = "";
|
|
12731
|
+
for (const o of head) {
|
|
12732
|
+
title = cleanTitle(userText4(o));
|
|
12733
|
+
if (title) break;
|
|
12734
|
+
}
|
|
12735
|
+
return { createdAt, updatedAt, title };
|
|
12736
|
+
}
|
|
12737
|
+
var requireHere3 = createRequire5(import.meta.url);
|
|
12738
|
+
function loadNodeSqlite2() {
|
|
12739
|
+
const orig = process.emitWarning;
|
|
12740
|
+
process.emitWarning = function(...args) {
|
|
12741
|
+
if (String(args[0] ?? "").includes("SQLite")) return;
|
|
12742
|
+
return orig.apply(process, args);
|
|
12743
|
+
};
|
|
12744
|
+
try {
|
|
12745
|
+
return requireHere3("node:sqlite");
|
|
12746
|
+
} finally {
|
|
12747
|
+
process.emitWarning = orig;
|
|
12748
|
+
}
|
|
12749
|
+
}
|
|
12750
|
+
var defaultQoderAppDeps = () => ({
|
|
12751
|
+
now: () => Date.now(),
|
|
12752
|
+
dbPath: (tool) => qoderDbPaths(tool)[0] ?? "",
|
|
12753
|
+
configDir: qoderConfigDir,
|
|
12754
|
+
loadSqlite: loadNodeSqlite2,
|
|
12755
|
+
electron: (tool) => {
|
|
12756
|
+
try {
|
|
12757
|
+
const loc = locate(tool);
|
|
12758
|
+
return loc?.env?.ELECTRON_RUN_AS_NODE === "1" ? loc.file : null;
|
|
12759
|
+
} catch {
|
|
12760
|
+
return null;
|
|
12761
|
+
}
|
|
12762
|
+
},
|
|
12763
|
+
runElectron: (bin, script, input) => new Promise((resolve10, reject) => {
|
|
12764
|
+
const env = { ...process.env, ELECTRON_RUN_AS_NODE: "1" };
|
|
12765
|
+
const c = execFile9(
|
|
12766
|
+
bin,
|
|
12767
|
+
["-e", script],
|
|
12768
|
+
{ env, timeout: 15e3, windowsHide: true, maxBuffer: 1024 * 1024 },
|
|
12769
|
+
(err, stdout7) => err ? reject(err) : resolve10(String(stdout7))
|
|
12770
|
+
);
|
|
12771
|
+
c.stdin?.on("error", () => {
|
|
12772
|
+
});
|
|
12773
|
+
c.stdin?.end(input);
|
|
12774
|
+
})
|
|
12775
|
+
});
|
|
12776
|
+
var IN_PROCESS_BUSY_MS = 500;
|
|
12777
|
+
async function runDb2(tool, input, d) {
|
|
12778
|
+
let mod = null;
|
|
12779
|
+
try {
|
|
12780
|
+
mod = d.loadSqlite();
|
|
12781
|
+
} catch {
|
|
12782
|
+
mod = null;
|
|
12783
|
+
}
|
|
12784
|
+
if (mod) {
|
|
12785
|
+
const fn = new Function("require", "input", DB_SCRIPT);
|
|
12786
|
+
try {
|
|
12787
|
+
return fn((id) => id === "node:sqlite" ? mod : requireHere3(id), { ...input, busyMs: IN_PROCESS_BUSY_MS });
|
|
12788
|
+
} catch (e) {
|
|
12789
|
+
return { status: "error", detail: String(e?.message ?? e).slice(0, 200) };
|
|
12790
|
+
}
|
|
12791
|
+
}
|
|
12792
|
+
const bin = d.electron(tool);
|
|
12793
|
+
if (!bin) return "no_sqlite";
|
|
12794
|
+
try {
|
|
12795
|
+
const out = JSON.parse(await d.runElectron(bin, electronScript(), JSON.stringify(input)));
|
|
12796
|
+
return out && typeof out.status === "string" ? out : { status: "error", detail: "bad output" };
|
|
12797
|
+
} catch (e) {
|
|
12798
|
+
return { status: "error", detail: `electron: ${String(e?.message ?? e).slice(0, 160)}` };
|
|
12799
|
+
}
|
|
12800
|
+
}
|
|
12801
|
+
async function syncQoderApp(job, d = defaultQoderAppDeps()) {
|
|
12802
|
+
try {
|
|
12803
|
+
if (!QODER_APP_TOOLS.includes(job.tool)) return "skip tool";
|
|
12804
|
+
if (!SESSION_UUID_RE.test(job.sessionId)) return "skip bad_session";
|
|
12805
|
+
const db = d.dbPath(job.tool);
|
|
12806
|
+
if (!db || !existsSync23(db)) return "skip no_db";
|
|
12807
|
+
if (job.kind === "clear") {
|
|
12808
|
+
const r2 = await runDb2(job.tool, { op: "clear", db, sessionId: job.sessionId }, d);
|
|
12809
|
+
return r2 === "no_sqlite" ? "skip no_sqlite" : describe2(r2);
|
|
12810
|
+
}
|
|
12811
|
+
if (!job.cwd || !isAbsolute4(job.cwd)) return "skip bad_cwd";
|
|
12812
|
+
const tp = await findTranscript(d.configDir(job.tool), job.sessionId);
|
|
12813
|
+
if (!tp) return "skip no_transcript";
|
|
12814
|
+
const info = await transcriptInfo(tp);
|
|
12815
|
+
const name = basename11(job.cwd) || job.cwd;
|
|
12816
|
+
const title = info.title || cleanTitle(job.fallbackTitle ?? "") || name;
|
|
12817
|
+
const r = await runDb2(
|
|
12818
|
+
job.tool,
|
|
12819
|
+
{
|
|
12820
|
+
op: "register",
|
|
12821
|
+
db,
|
|
12822
|
+
sessionId: job.sessionId,
|
|
12823
|
+
cwd: job.cwd,
|
|
12824
|
+
title,
|
|
12825
|
+
wsid: workspaceId(job.cwd),
|
|
12826
|
+
wsName: name,
|
|
12827
|
+
createdAt: info.createdAt,
|
|
12828
|
+
updatedAt: info.updatedAt,
|
|
12829
|
+
now: d.now()
|
|
12830
|
+
},
|
|
12831
|
+
d
|
|
12832
|
+
);
|
|
12833
|
+
return r === "no_sqlite" ? "skip no_sqlite" : describe2(r);
|
|
12834
|
+
} catch (e) {
|
|
12835
|
+
return `error ${String(e?.message ?? e).slice(0, 160)}`;
|
|
12836
|
+
}
|
|
12837
|
+
}
|
|
12838
|
+
function describe2(r) {
|
|
12839
|
+
if (r.status === "schema") return `skip schema ${r.detail ?? ""}`.trim();
|
|
12840
|
+
if (r.status === "busy") return "skip busy";
|
|
12841
|
+
return [r.status, r.n !== void 0 ? `n=${r.n}` : "", r.detail ?? ""].filter(Boolean).join(" ");
|
|
12842
|
+
}
|
|
12843
|
+
|
|
11592
12844
|
// src/daemon/wake.ts
|
|
11593
12845
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
11594
|
-
import { rmSync as
|
|
12846
|
+
import { rmSync as rmSync17 } from "fs";
|
|
11595
12847
|
import { createServer as createServer3 } from "net";
|
|
11596
12848
|
var BUSY_STATES = /* @__PURE__ */ new Set(["watch", "ask_approval"]);
|
|
11597
12849
|
var STOP_MIN_MS = 1500;
|
|
@@ -11886,7 +13138,7 @@ function startWakeServer(path, hub, secret, platform = process.platform) {
|
|
|
11886
13138
|
if (platform !== "win32") {
|
|
11887
13139
|
if (!ensureChannelDir(path)) return Promise.resolve(null);
|
|
11888
13140
|
try {
|
|
11889
|
-
|
|
13141
|
+
rmSync17(path, { force: true });
|
|
11890
13142
|
} catch {
|
|
11891
13143
|
}
|
|
11892
13144
|
}
|
|
@@ -11988,24 +13240,24 @@ function startWakeServer(path, hub, secret, platform = process.platform) {
|
|
|
11988
13240
|
}
|
|
11989
13241
|
|
|
11990
13242
|
// src/core/appended.ts
|
|
11991
|
-
import { chmodSync as chmodSync11, mkdirSync as
|
|
11992
|
-
import { join as
|
|
13243
|
+
import { chmodSync as chmodSync11, mkdirSync as mkdirSync27, readFileSync as readFileSync28, renameSync as renameSync18, rmSync as rmSync18, writeFileSync as writeFileSync25 } from "fs";
|
|
13244
|
+
import { join as join39 } from "path";
|
|
11993
13245
|
var APPENDED_MAX_ITEMS = 5;
|
|
11994
13246
|
var APPENDED_TEXT_MAX = 500;
|
|
11995
13247
|
var APPENDED_RESULT_MAX = 800;
|
|
11996
13248
|
var APPENDED_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
11997
13249
|
function appendedDir() {
|
|
11998
|
-
return
|
|
13250
|
+
return join39(deskbuddyHome(), "appended");
|
|
11999
13251
|
}
|
|
12000
13252
|
function appendedPath(sessionId) {
|
|
12001
13253
|
const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
|
|
12002
|
-
return safe ?
|
|
13254
|
+
return safe ? join39(appendedDir(), `${safe}.json`) : null;
|
|
12003
13255
|
}
|
|
12004
13256
|
function readAppended(sessionId) {
|
|
12005
13257
|
const file = appendedPath(sessionId);
|
|
12006
13258
|
if (!file) return [];
|
|
12007
13259
|
try {
|
|
12008
|
-
const d = JSON.parse(
|
|
13260
|
+
const d = JSON.parse(readFileSync28(file, "utf8"));
|
|
12009
13261
|
return Array.isArray(d.items) ? d.items : [];
|
|
12010
13262
|
} catch {
|
|
12011
13263
|
return [];
|
|
@@ -12018,26 +13270,26 @@ function writeAppended(sessionId, item) {
|
|
|
12018
13270
|
const items = readAppended(sessionId).concat([
|
|
12019
13271
|
{ text: item.text.slice(0, APPENDED_TEXT_MAX), result: item.result.slice(0, APPENDED_RESULT_MAX), ok: item.ok, at: item.at }
|
|
12020
13272
|
]);
|
|
12021
|
-
|
|
13273
|
+
mkdirSync27(appendedDir(), { recursive: true, mode: 448 });
|
|
12022
13274
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
12023
|
-
|
|
13275
|
+
writeFileSync25(tmp, JSON.stringify({ items: items.slice(-APPENDED_MAX_ITEMS) }), { encoding: "utf8", mode: 384 });
|
|
12024
13276
|
chmodSync11(tmp, 384);
|
|
12025
|
-
|
|
13277
|
+
renameSync18(tmp, file);
|
|
12026
13278
|
} catch {
|
|
12027
13279
|
}
|
|
12028
13280
|
}
|
|
12029
13281
|
var RUNNING_TTL_MS = 60 * 60 * 1e3;
|
|
12030
13282
|
function runningPath(sessionId) {
|
|
12031
13283
|
const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
|
|
12032
|
-
return safe ?
|
|
13284
|
+
return safe ? join39(deskbuddyHome(), "running", `${safe}.json`) : null;
|
|
12033
13285
|
}
|
|
12034
13286
|
function writeRunning(sessionId, now = Date.now()) {
|
|
12035
13287
|
const file = runningPath(sessionId);
|
|
12036
13288
|
if (!file) return;
|
|
12037
13289
|
try {
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
13290
|
+
mkdirSync27(join39(deskbuddyHome(), "running"), { recursive: true, mode: 448 });
|
|
13291
|
+
writeFileSync25(`${file}.tmp`, JSON.stringify({ at: now, pid: process.pid }), { encoding: "utf8", mode: 384 });
|
|
13292
|
+
renameSync18(`${file}.tmp`, file);
|
|
12041
13293
|
} catch {
|
|
12042
13294
|
}
|
|
12043
13295
|
}
|
|
@@ -12045,7 +13297,7 @@ function removeRunning(sessionId) {
|
|
|
12045
13297
|
const file = runningPath(sessionId);
|
|
12046
13298
|
if (!file) return;
|
|
12047
13299
|
try {
|
|
12048
|
-
|
|
13300
|
+
rmSync18(file, { force: true });
|
|
12049
13301
|
} catch {
|
|
12050
13302
|
}
|
|
12051
13303
|
}
|
|
@@ -12082,8 +13334,8 @@ function resultSummary(tool, stdout7) {
|
|
|
12082
13334
|
}
|
|
12083
13335
|
|
|
12084
13336
|
// src/core/dshMailbox.ts
|
|
12085
|
-
import { chmodSync as chmodSync12, existsSync as
|
|
12086
|
-
import { join as
|
|
13337
|
+
import { chmodSync as chmodSync12, existsSync as existsSync24, mkdirSync as mkdirSync28, readdirSync as readdirSync11, readFileSync as readFileSync29, renameSync as renameSync19, rmSync as rmSync19, writeFileSync as writeFileSync26 } from "fs";
|
|
13338
|
+
import { join as join40 } from "path";
|
|
12087
13339
|
var DSH_HEARTBEAT_FRESH_MS = 5 * 1e3;
|
|
12088
13340
|
var DSH_ACK_TIMEOUT_MS = 8 * 1e3;
|
|
12089
13341
|
var ACK_POLL_MS = 200;
|
|
@@ -12094,13 +13346,13 @@ var REVOKE_RETRY_MS = 50;
|
|
|
12094
13346
|
var SID_RE3 = /^[A-Za-z0-9_-]{1,128}$/;
|
|
12095
13347
|
var IID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
12096
13348
|
function dshMailboxDir(home = deskbuddyHome()) {
|
|
12097
|
-
return
|
|
13349
|
+
return join40(home, "dsh");
|
|
12098
13350
|
}
|
|
12099
13351
|
function sub(home, name) {
|
|
12100
|
-
return
|
|
13352
|
+
return join40(dshMailboxDir(home), name);
|
|
12101
13353
|
}
|
|
12102
13354
|
function ensureDir2(p) {
|
|
12103
|
-
|
|
13355
|
+
mkdirSync28(p, { recursive: true, mode: 448 });
|
|
12104
13356
|
try {
|
|
12105
13357
|
chmodSync12(p, 448);
|
|
12106
13358
|
} catch {
|
|
@@ -12108,8 +13360,8 @@ function ensureDir2(p) {
|
|
|
12108
13360
|
}
|
|
12109
13361
|
function writeJson(path, value) {
|
|
12110
13362
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
12111
|
-
|
|
12112
|
-
|
|
13363
|
+
writeFileSync26(tmp, JSON.stringify(value), { encoding: "utf8", mode: 384 });
|
|
13364
|
+
renameSync19(tmp, path);
|
|
12113
13365
|
}
|
|
12114
13366
|
var defaultPidAlive = (pid) => {
|
|
12115
13367
|
try {
|
|
@@ -12127,7 +13379,7 @@ function dshLiveFor(sessionId, d0 = {}) {
|
|
|
12127
13379
|
const d = deps(d0);
|
|
12128
13380
|
let names;
|
|
12129
13381
|
try {
|
|
12130
|
-
names =
|
|
13382
|
+
names = readdirSync11(sub(d.home, "alive"));
|
|
12131
13383
|
} catch {
|
|
12132
13384
|
return null;
|
|
12133
13385
|
}
|
|
@@ -12135,7 +13387,7 @@ function dshLiveFor(sessionId, d0 = {}) {
|
|
|
12135
13387
|
if (!/^\d+\.json$/.test(n)) continue;
|
|
12136
13388
|
let hb;
|
|
12137
13389
|
try {
|
|
12138
|
-
hb = JSON.parse(
|
|
13390
|
+
hb = JSON.parse(readFileSync29(join40(sub(d.home, "alive"), n), "utf8"));
|
|
12139
13391
|
} catch {
|
|
12140
13392
|
continue;
|
|
12141
13393
|
}
|
|
@@ -12155,9 +13407,9 @@ async function dshDeliver(sessionId, msg, d0 = {}) {
|
|
|
12155
13407
|
const ackDir = sub(d.home, "ack");
|
|
12156
13408
|
ensureDir2(inbox);
|
|
12157
13409
|
ensureDir2(ackDir);
|
|
12158
|
-
const letter =
|
|
12159
|
-
const ackPath =
|
|
12160
|
-
if (
|
|
13410
|
+
const letter = join40(inbox, `${sessionId}.${msg.instructionId}.json`);
|
|
13411
|
+
const ackPath = join40(ackDir, `${msg.instructionId}.json`);
|
|
13412
|
+
if (existsSync24(letter)) return { ok: false, reason: "inbox_busy" };
|
|
12161
13413
|
const writtenAt = d.now();
|
|
12162
13414
|
writeJson(letter, { instructionId: msg.instructionId, text: msg.text, at: writtenAt });
|
|
12163
13415
|
const sleep3 = d0.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -12165,19 +13417,19 @@ async function dshDeliver(sessionId, msg, d0 = {}) {
|
|
|
12165
13417
|
let claimed = false;
|
|
12166
13418
|
let revokeFailed = false;
|
|
12167
13419
|
for (; ; ) {
|
|
12168
|
-
if (
|
|
13420
|
+
if (existsSync24(ackPath)) {
|
|
12169
13421
|
let ack = { ok: false, reason: "bad_ack" };
|
|
12170
13422
|
try {
|
|
12171
|
-
const v = JSON.parse(
|
|
13423
|
+
const v = JSON.parse(readFileSync29(ackPath, "utf8"));
|
|
12172
13424
|
if (v && typeof v.ok === "boolean") ack = v;
|
|
12173
13425
|
} catch {
|
|
12174
13426
|
}
|
|
12175
|
-
|
|
13427
|
+
rmSync19(ackPath, { force: true });
|
|
12176
13428
|
return ack;
|
|
12177
13429
|
}
|
|
12178
13430
|
if (d.now() >= deadline) {
|
|
12179
13431
|
if (revokeFailed) {
|
|
12180
|
-
if (
|
|
13432
|
+
if (existsSync24(letter)) return { ok: false, reason: "revoke_failed" };
|
|
12181
13433
|
revokeFailed = false;
|
|
12182
13434
|
claimed = true;
|
|
12183
13435
|
deadline = d.now() + DSH_ACK_GRACE_MS;
|
|
@@ -12203,28 +13455,28 @@ async function dshDeliver(sessionId, msg, d0 = {}) {
|
|
|
12203
13455
|
async function revoke(letter, sleep3) {
|
|
12204
13456
|
for (let i = 0; i <= REVOKE_RETRIES; i++) {
|
|
12205
13457
|
try {
|
|
12206
|
-
|
|
12207
|
-
|
|
13458
|
+
renameSync19(letter, `${letter}.revoked`);
|
|
13459
|
+
rmSync19(`${letter}.revoked`, { force: true });
|
|
12208
13460
|
return "revoked";
|
|
12209
13461
|
} catch (e) {
|
|
12210
13462
|
if (e?.code === "ENOENT") return "claimed";
|
|
12211
13463
|
if (i < REVOKE_RETRIES) await sleep3(REVOKE_RETRY_MS);
|
|
12212
13464
|
}
|
|
12213
13465
|
}
|
|
12214
|
-
return
|
|
13466
|
+
return existsSync24(letter) ? "failed" : "claimed";
|
|
12215
13467
|
}
|
|
12216
13468
|
function dshClearInbox(home = deskbuddyHome()) {
|
|
12217
13469
|
const inbox = sub(home, "inbox");
|
|
12218
13470
|
let names;
|
|
12219
13471
|
try {
|
|
12220
|
-
names =
|
|
13472
|
+
names = readdirSync11(inbox);
|
|
12221
13473
|
} catch {
|
|
12222
13474
|
return 0;
|
|
12223
13475
|
}
|
|
12224
13476
|
let n = 0;
|
|
12225
13477
|
for (const f of names) {
|
|
12226
13478
|
try {
|
|
12227
|
-
|
|
13479
|
+
rmSync19(join40(inbox, f), { force: true });
|
|
12228
13480
|
n++;
|
|
12229
13481
|
} catch {
|
|
12230
13482
|
}
|
|
@@ -12256,7 +13508,7 @@ function defaultCodexAppServers() {
|
|
|
12256
13508
|
}
|
|
12257
13509
|
function opencodeReady() {
|
|
12258
13510
|
const mod = ocPluginModule(installedHookPath());
|
|
12259
|
-
return isOcPluginInstalled(opencodePluginPath()) &&
|
|
13511
|
+
return isOcPluginInstalled(opencodePluginPath()) && existsSync25(mod) && existsSync25(join41(dirname24(mod), "ocTranslate.mjs"));
|
|
12260
13512
|
}
|
|
12261
13513
|
var INTERNAL_PROMPT_MARK = "[DeskBuddy]";
|
|
12262
13514
|
function orphanResumeText(summary2) {
|
|
@@ -12290,7 +13542,7 @@ function shouldIdleWindow(last, sessionId, markerAt) {
|
|
|
12290
13542
|
}
|
|
12291
13543
|
function transcriptSig(file) {
|
|
12292
13544
|
try {
|
|
12293
|
-
const st =
|
|
13545
|
+
const st = statSync17(file);
|
|
12294
13546
|
return `${st.size}:${st.mtimeMs}`;
|
|
12295
13547
|
} catch {
|
|
12296
13548
|
return "";
|
|
@@ -12397,9 +13649,15 @@ var Daemon = class {
|
|
|
12397
13649
|
}),
|
|
12398
13650
|
onLink: deps2.onLink ?? (() => {
|
|
12399
13651
|
}),
|
|
13652
|
+
qoderAppSync: deps2.qoderAppSync ?? ((job) => syncQoderApp(job)),
|
|
12400
13653
|
dshLive: deps2.dshLive ?? ((sid) => dshLiveFor(sid) != null),
|
|
12401
13654
|
dshDeliver: deps2.dshDeliver ?? ((sid, msg) => dshDeliver(sid, msg)),
|
|
12402
|
-
loginGuide: deps2.loginGuide ?? defaultLoginGuide(log, deps2.now ?? (() => Date.now()))
|
|
13655
|
+
loginGuide: deps2.loginGuide ?? defaultLoginGuide(log, deps2.now ?? (() => Date.now()), () => {
|
|
13656
|
+
const offer = defaultWorkbuddyOffer(log);
|
|
13657
|
+
if (offer) void offer.maybeAsk("runtime").catch(() => {
|
|
13658
|
+
});
|
|
13659
|
+
}),
|
|
13660
|
+
workbuddyAppSync: deps2.workbuddyAppSync ?? ((job) => registerWorkbuddySession(job))
|
|
12403
13661
|
};
|
|
12404
13662
|
this.#wake = new WakeHub({
|
|
12405
13663
|
now: this.#deps.now,
|
|
@@ -12640,9 +13898,9 @@ var Daemon = class {
|
|
|
12640
13898
|
}
|
|
12641
13899
|
/** 会话的 transcript:hook 记在状态里的优先;没有再按目录找,找不到的一段时间内不再找 */
|
|
12642
13900
|
#transcriptOf(st, now) {
|
|
12643
|
-
if (st.transcriptPath &&
|
|
13901
|
+
if (st.transcriptPath && existsSync25(st.transcriptPath)) return st.transcriptPath;
|
|
12644
13902
|
const cached = this.#transcripts.get(st.sessionId);
|
|
12645
|
-
if (cached &&
|
|
13903
|
+
if (cached && existsSync25(cached)) return cached;
|
|
12646
13904
|
const missAt = this.#transcriptMiss.get(st.sessionId);
|
|
12647
13905
|
if (missAt !== void 0 && now - missAt < TRANSCRIPT_MISS_TTL_MS) return null;
|
|
12648
13906
|
const home = transcriptHome(st.tool);
|
|
@@ -12938,6 +14196,11 @@ var Daemon = class {
|
|
|
12938
14196
|
this.#seen.save();
|
|
12939
14197
|
cmd.receivedAt = this.#deps.now();
|
|
12940
14198
|
log(`\u6536\u5230\u6307\u4EE4 ${cmd.instructionId} tool=${cmd.tool} ${cmd.sessionId ? `\u56DE\u590D ${cmd.sessionId.slice(0, 8)}` : "\u65B0\u5EFA"}`);
|
|
14199
|
+
if (QODER_APP_TOOLS.includes(cmd.tool) && cmd.sessionId && !SESSION_UUID_RE.test(cmd.sessionId)) {
|
|
14200
|
+
log(`\u6307\u4EE4 ${cmd.instructionId} \u4F1A\u8BDD\u53F7\u4E0D\u662F UUID\uFF0C\u62D2\u6536`);
|
|
14201
|
+
this.#report(cmd, cmd.sessionId, "rejected", "", "session_gone");
|
|
14202
|
+
return;
|
|
14203
|
+
}
|
|
12941
14204
|
this.#route(cmd);
|
|
12942
14205
|
}
|
|
12943
14206
|
/**
|
|
@@ -13481,7 +14744,7 @@ var Daemon = class {
|
|
|
13481
14744
|
this.#report(cmd, sessionId, "rejected", "", "tool_unsupported");
|
|
13482
14745
|
return;
|
|
13483
14746
|
}
|
|
13484
|
-
log(`\u6267\u884C ${cmd.instructionId} tool=${cmd.tool} ${sessionId ? "resume" : "new"} cwd=\u2026/${
|
|
14747
|
+
log(`\u6267\u884C ${cmd.instructionId} tool=${cmd.tool} ${sessionId ? "resume" : "new"} cwd=\u2026/${basename12(cwd)} len=${cmd.text.length}`);
|
|
13485
14748
|
const t0 = this.#deps.now();
|
|
13486
14749
|
const result = await this.#run(plan, cmd.tool, runId, sessionId, cwd, () => this.#report(cmd, sessionId, "started", "headless"), cmd.text);
|
|
13487
14750
|
if (cmd.tool === "deepseek_harness" && !("fail" in result)) {
|
|
@@ -13514,11 +14777,37 @@ var Daemon = class {
|
|
|
13514
14777
|
);
|
|
13515
14778
|
if (notLoggedIn) this.#guideLogin(cmd.tool);
|
|
13516
14779
|
}
|
|
14780
|
+
let newId = null;
|
|
13517
14781
|
if (!sessionId) {
|
|
13518
|
-
|
|
14782
|
+
newId = result.sessionId ?? parseSessionId(cmd.tool, result.stdout);
|
|
13519
14783
|
log(`\u6307\u4EE4 ${cmd.instructionId} ${newId ? `\u5EFA\u51FA\u4F1A\u8BDD ${newId}` : "\u672A\u80FD\u89E3\u6790\u51FA\u4F1A\u8BDD id"}`);
|
|
14784
|
+
if (cmd.tool === "workbuddy" && newId && result.ok) this.#syncWorkbuddyApp({ sessionId: newId, cwd, fallbackTitle: cmd.text });
|
|
14785
|
+
}
|
|
14786
|
+
if (QODER_APP_TOOLS.includes(cmd.tool) && (sessionId || newId)) {
|
|
14787
|
+
this.#syncQoderApp({
|
|
14788
|
+
tool: cmd.tool,
|
|
14789
|
+
kind: sessionId ? "clear" : "register",
|
|
14790
|
+
sessionId: sessionId || newId,
|
|
14791
|
+
cwd,
|
|
14792
|
+
fallbackTitle: cmd.text
|
|
14793
|
+
});
|
|
13520
14794
|
}
|
|
13521
14795
|
}
|
|
14796
|
+
#syncQoderApp(job) {
|
|
14797
|
+
const tag = `Qoder \u4F1A\u8BDD ${job.sessionId.slice(0, 8)} ${job.kind === "register" ? "\u767B\u8BB0\u8FDB\u684C\u9762\u7AEF\u5217\u8868" : "\u6E05\u684C\u9762\u7AEF\u6D88\u606F\u7F13\u5B58"}`;
|
|
14798
|
+
setImmediate(() => {
|
|
14799
|
+
let p;
|
|
14800
|
+
try {
|
|
14801
|
+
p = this.#deps.qoderAppSync(job);
|
|
14802
|
+
} catch (e) {
|
|
14803
|
+
p = Promise.resolve(`error ${e?.message ?? String(e)}`);
|
|
14804
|
+
}
|
|
14805
|
+
void Promise.resolve(p).then(
|
|
14806
|
+
(r) => log(`${tag}\uFF1A${r}`),
|
|
14807
|
+
(e) => log(`${tag}\u51FA\u9519\uFF1A${e?.message ?? String(e)}`)
|
|
14808
|
+
);
|
|
14809
|
+
});
|
|
14810
|
+
}
|
|
13522
14811
|
/**
|
|
13523
14812
|
* 命令行没登录:在电脑上开登录窗口(同一工具 30 分钟内一次,见 loginGuide.ts)。不阻塞回报链路。
|
|
13524
14813
|
* 🔴 daemon 开的 WorkBuddy 窗口停在登录页(LoginRequired)不走这里:那个窗口本身就在登录页上
|
|
@@ -13531,6 +14820,19 @@ var Daemon = class {
|
|
|
13531
14820
|
log(`${tool} \u767B\u5F55\u5F15\u5BFC\u51FA\u9519\uFF1A${e?.message ?? e}`);
|
|
13532
14821
|
}
|
|
13533
14822
|
}
|
|
14823
|
+
#syncWorkbuddyApp(job) {
|
|
14824
|
+
const tag = `WorkBuddy \u4F1A\u8BDD ${job.sessionId.slice(0, 8)} \u767B\u8BB0\u8FDB\u684C\u9762\u7AEF\u5217\u8868`;
|
|
14825
|
+
let p;
|
|
14826
|
+
try {
|
|
14827
|
+
p = this.#deps.workbuddyAppSync(job);
|
|
14828
|
+
} catch (e) {
|
|
14829
|
+
p = Promise.resolve(`error ${e?.message ?? String(e)}`);
|
|
14830
|
+
}
|
|
14831
|
+
void Promise.resolve(p).then(
|
|
14832
|
+
(r) => log(`${tag}\uFF1A${r}`),
|
|
14833
|
+
(e) => log(`${tag}\u51FA\u9519\uFF1A${e?.message ?? String(e)}`)
|
|
14834
|
+
);
|
|
14835
|
+
}
|
|
13534
14836
|
#gateTarget(runId) {
|
|
13535
14837
|
return { runId, nodePath: this.#deps.nodePath, gateScript: this.#deps.gateScript };
|
|
13536
14838
|
}
|
|
@@ -13635,7 +14937,7 @@ var Daemon = class {
|
|
|
13635
14937
|
* 起不了(关卡不可用 / 找不到可执行文件)返回 {fail};进程起来那一刻调 onStarted
|
|
13636
14938
|
*/
|
|
13637
14939
|
async #run(plan, tool, runId, sessionId, cwd, onStarted, text = "") {
|
|
13638
|
-
if (!
|
|
14940
|
+
if (!existsSync25(this.#deps.nodePath) || !existsSync25(this.#deps.gateScript)) {
|
|
13639
14941
|
log(`\u6388\u6743\u5173\u5361\u4E0D\u53EF\u7528\uFF08node \u6216 gate.cjs \u627E\u4E0D\u5230\uFF09\uFF0C\u62D2\u7EDD\u6267\u884C ${tool}`);
|
|
13640
14942
|
return { fail: "gate_unavailable" };
|
|
13641
14943
|
}
|
|
@@ -13721,7 +15023,7 @@ async function runDaemonCommand(opts = {}) {
|
|
|
13721
15023
|
}
|
|
13722
15024
|
if (opts.foreground) process.env.DESKBUDDY_DAEMON_FOREGROUND = "1";
|
|
13723
15025
|
try {
|
|
13724
|
-
|
|
15026
|
+
writeFileSync27(pidFilePath(), String(process.pid), "utf8");
|
|
13725
15027
|
} catch {
|
|
13726
15028
|
}
|
|
13727
15029
|
let daemon;
|
|
@@ -13747,16 +15049,23 @@ async function runDaemonCommand(opts = {}) {
|
|
|
13747
15049
|
log: (l) => log(`[\u81EA\u52A8\u5347\u7EA7] ${l}`)
|
|
13748
15050
|
});
|
|
13749
15051
|
updater?.start();
|
|
13750
|
-
const
|
|
13751
|
-
|
|
15052
|
+
const wbOffer = defaultWorkbuddyOffer((l) => log(l));
|
|
15053
|
+
const autoEnabler = new AutoEnabler({
|
|
15054
|
+
...defaultAutoEnableDeps(
|
|
13752
15055
|
(l) => log(l),
|
|
13753
15056
|
(tools) => {
|
|
13754
15057
|
d.reportAliveNow();
|
|
13755
15058
|
void autoEnableLoginLog(tools, (t) => checkLogin(t, defaultCheckDeps()), (l) => log(l)).catch(() => {
|
|
13756
15059
|
});
|
|
13757
15060
|
}
|
|
13758
|
-
)
|
|
13759
|
-
|
|
15061
|
+
),
|
|
15062
|
+
onTick: () => {
|
|
15063
|
+
if (wbOffer) void wbOffer.maybeAsk("daemon").catch(() => {
|
|
15064
|
+
});
|
|
15065
|
+
void flushPendingWorkbuddy().then((r) => r && log(`[WorkBuddy \u4F1A\u8BDD\u767B\u8BB0] ${r}`)).catch(() => {
|
|
15066
|
+
});
|
|
15067
|
+
}
|
|
15068
|
+
});
|
|
13760
15069
|
autoEnabler.start();
|
|
13761
15070
|
const shutdown = (sig) => {
|
|
13762
15071
|
log(`\u6536\u5230 ${sig}\uFF0C\u9000\u51FA`);
|
|
@@ -13764,7 +15073,7 @@ async function runDaemonCommand(opts = {}) {
|
|
|
13764
15073
|
autoEnabler.stop();
|
|
13765
15074
|
daemon.stop();
|
|
13766
15075
|
try {
|
|
13767
|
-
|
|
15076
|
+
rmSync20(pidFilePath(), { force: true });
|
|
13768
15077
|
} catch {
|
|
13769
15078
|
}
|
|
13770
15079
|
process.exit(0);
|
|
@@ -13845,28 +15154,28 @@ async function runDisable(tools, opts = {}) {
|
|
|
13845
15154
|
}
|
|
13846
15155
|
|
|
13847
15156
|
// src/commands/install.ts
|
|
13848
|
-
import { mkdirSync as
|
|
13849
|
-
import { join as
|
|
15157
|
+
import { mkdirSync as mkdirSync29, rmSync as rmSync21, writeFileSync as writeFileSync28 } from "fs";
|
|
15158
|
+
import { join as join42 } from "path";
|
|
13850
15159
|
|
|
13851
15160
|
// src/commands/setup.ts
|
|
13852
15161
|
import qrcode from "qrcode-terminal";
|
|
13853
15162
|
|
|
13854
15163
|
// src/commands/remoteCwd.ts
|
|
13855
|
-
import { existsSync as
|
|
15164
|
+
import { existsSync as existsSync26, statSync as statSync18 } from "fs";
|
|
13856
15165
|
import { createInterface } from "readline";
|
|
13857
|
-
import { homedir as
|
|
13858
|
-
import { isAbsolute as
|
|
13859
|
-
function normalizePath(raw, home =
|
|
15166
|
+
import { homedir as homedir12 } from "os";
|
|
15167
|
+
import { isAbsolute as isAbsolute5, resolve as resolve8 } from "path";
|
|
15168
|
+
function normalizePath(raw, home = homedir12()) {
|
|
13860
15169
|
const s2 = (raw || "").trim();
|
|
13861
15170
|
if (!s2) return null;
|
|
13862
15171
|
const expanded = s2 === "~" ? home : s2.startsWith("~/") || process.platform === "win32" && s2.startsWith("~\\") ? resolve8(home, s2.slice(2)) : s2;
|
|
13863
|
-
const abs =
|
|
15172
|
+
const abs = isAbsolute5(expanded) ? expanded : resolve8(process.cwd(), expanded);
|
|
13864
15173
|
return abs;
|
|
13865
15174
|
}
|
|
13866
15175
|
function checkPath(abs) {
|
|
13867
|
-
if (!
|
|
15176
|
+
if (!existsSync26(abs)) return "missing";
|
|
13868
15177
|
try {
|
|
13869
|
-
return
|
|
15178
|
+
return statSync18(abs).isDirectory() ? "ok" : "not_a_dir";
|
|
13870
15179
|
} catch {
|
|
13871
15180
|
return "missing";
|
|
13872
15181
|
}
|
|
@@ -14063,11 +15372,11 @@ function inSandbox(env) {
|
|
|
14063
15372
|
}
|
|
14064
15373
|
function precheckWritable(dirs) {
|
|
14065
15374
|
for (const dir of dirs) {
|
|
14066
|
-
const probe =
|
|
15375
|
+
const probe = join42(dir, `.deskbuddy-probe-${process.pid}`);
|
|
14067
15376
|
try {
|
|
14068
|
-
|
|
14069
|
-
|
|
14070
|
-
|
|
15377
|
+
mkdirSync29(dir, { recursive: true });
|
|
15378
|
+
writeFileSync28(probe, "");
|
|
15379
|
+
rmSync21(probe, { force: true });
|
|
14071
15380
|
} catch (e) {
|
|
14072
15381
|
return { ok: false, dir, error: e.message };
|
|
14073
15382
|
}
|
|
@@ -14088,12 +15397,16 @@ var defaultInstallDeps = () => ({
|
|
|
14088
15397
|
loginCheck: (tools, out) => {
|
|
14089
15398
|
const guide = defaultGuideDeps();
|
|
14090
15399
|
return guide ? runLoginCheck(tools, out, guide) : Promise.resolve([]);
|
|
15400
|
+
},
|
|
15401
|
+
workbuddyPrompt: (out) => {
|
|
15402
|
+
const d = defaultPromptStartDeps();
|
|
15403
|
+
return d ? startStandalonePrompt("install", out, d) : "none";
|
|
14091
15404
|
}
|
|
14092
15405
|
});
|
|
14093
|
-
function summary(out,
|
|
15406
|
+
function summary(out, lines3) {
|
|
14094
15407
|
out("");
|
|
14095
15408
|
out("=== DeskBuddy \u5B89\u88C5\u7ED3\u679C ===");
|
|
14096
|
-
for (const l of
|
|
15409
|
+
for (const l of lines3) out(l);
|
|
14097
15410
|
out("\u8BF7\u628A\u4EE5\u4E0A\u7ED3\u679C\u539F\u6837\u544A\u8BC9\u7528\u6237\u3002");
|
|
14098
15411
|
}
|
|
14099
15412
|
async function runInstall(opts, deps2 = defaultInstallDeps()) {
|
|
@@ -14169,6 +15482,12 @@ async function runInstall(opts, deps2 = defaultInstallDeps()) {
|
|
|
14169
15482
|
await deps2.refreshRegistry();
|
|
14170
15483
|
out("");
|
|
14171
15484
|
const r = tools.length > 0 ? deps2.installTools(tools, out) : { installed: [], failed: [] };
|
|
15485
|
+
let wbPrompt = "none";
|
|
15486
|
+
try {
|
|
15487
|
+
wbPrompt = deps2.workbuddyPrompt?.(out) ?? "none";
|
|
15488
|
+
} catch (e) {
|
|
15489
|
+
out(`\xB7 WorkBuddy \u72EC\u7ACB\u7248\u68C0\u67E5\u51FA\u9519\uFF1A${e?.message ?? e}\uFF08\u4E0D\u5F71\u54CD\u5B89\u88C5\uFF09`);
|
|
15490
|
+
}
|
|
14172
15491
|
const bound = loadConfig();
|
|
14173
15492
|
if (!bound.remoteCwd) {
|
|
14174
15493
|
bound.remoteCwd = defaultRemoteCwd();
|
|
@@ -14183,7 +15502,7 @@ async function runInstall(opts, deps2 = defaultInstallDeps()) {
|
|
|
14183
15502
|
}
|
|
14184
15503
|
const label2 = (t) => TOOL_LABELS[t] ?? t;
|
|
14185
15504
|
const ok = r.installed.length > 0;
|
|
14186
|
-
const
|
|
15505
|
+
const lines3 = [
|
|
14187
15506
|
`RESULT: ${ok ? "ok" : "partial"}`,
|
|
14188
15507
|
`\u8D26\u53F7: ${redeemed.accountHint || "\uFF08\u672A\u77E5\uFF09"}${redeemed.reused ? "\uFF08\u8FD9\u53F0\u7535\u8111\u4E4B\u524D\u5DF2\u7ED1\u5B9A\u8BE5\u8D26\u53F7\uFF09" : ""}\u2014\u2014\u5982\u679C\u8FD9\u4E0D\u662F\u4F60\u7684\u8D26\u53F7\uFF0C\u8BF7\u7ACB\u5373\u8FD0\u884C ${npx} logout --yes`,
|
|
14189
15508
|
`\u5DF2\u63A5\u5165: ${r.installed.map(label2).join("\u3001") || "\u65E0"}` + (r.failed.length > 0 ? ` \u672A\u63A5\u5165: ${r.failed.map((f) => `${label2(f.tool)}\uFF08${f.reason}\uFF09`).join("\u3001")}` : ""),
|
|
@@ -14191,24 +15510,26 @@ async function runInstall(opts, deps2 = defaultInstallDeps()) {
|
|
|
14191
15510
|
`\u8FDC\u7A0B\u65B0\u5EFA\u4EFB\u52A1\u76EE\u5F55: ${loadConfig().remoteCwd}`
|
|
14192
15511
|
];
|
|
14193
15512
|
const loginLine = summaryLine(logins);
|
|
14194
|
-
if (loginLine)
|
|
15513
|
+
if (loginLine) lines3.push(loginLine);
|
|
15514
|
+
const wbLine = promptSummaryLine(wbPrompt);
|
|
15515
|
+
if (wbLine) lines3.push(wbLine);
|
|
14195
15516
|
if (ok) {
|
|
14196
15517
|
const restart = r.installed.map(label2).join(" / ");
|
|
14197
|
-
|
|
15518
|
+
lines3.push(
|
|
14198
15519
|
`\u4E0B\u4E00\u6B65: \u91CD\u542F ${restart} \u540E\u5F00\u59CB\u540C\u6B65\u72B6\u6001\uFF08\u6B63\u5728\u8FD0\u884C\u7684\u4F1A\u8BDD\u4E0D\u4F1A\u91CD\u65B0\u52A0\u8F7D hook\uFF09` + (r.installed.includes("codex") ? "\uFF1BCodex \u4E0B\u6B21\u542F\u52A8\u4F1A\u8981\u6C42\u4FE1\u4EFB DeskBuddy \u7684 hook\uFF0C\u8BF7\u9009\u62E9\u540C\u610F" : "")
|
|
14199
15520
|
);
|
|
14200
15521
|
} else {
|
|
14201
|
-
|
|
15522
|
+
lines3.push(
|
|
14202
15523
|
tools.length === 0 ? `\u4E0B\u4E00\u6B65: \u8FD9\u53F0\u7535\u8111\u4E0A\u6CA1\u68C0\u6D4B\u5230\u652F\u6301\u7684\u667A\u80FD\u4F53\uFF08Claude Code / Codex / WorkBuddy / Qoder CN / Qoder / opencode / DeepSeek Harness\uFF09\u3002\u88C5\u597D\u540E\u8FD0\u884C ${npx} enable <\u5DE5\u5177\u540D>\uFF0C\u5982 claude_code` : `\u4E0B\u4E00\u6B65: \u624B\u52A8\u8FD0\u884C ${npx} enable ${tools.join(",")} \u91CD\u8BD5\uFF0C\u5E76\u628A\u62A5\u9519\u544A\u8BC9\u7528\u6237`
|
|
14203
15524
|
);
|
|
14204
15525
|
}
|
|
14205
|
-
summary(out,
|
|
15526
|
+
summary(out, lines3);
|
|
14206
15527
|
return ok ? EXIT.ok : EXIT.partial;
|
|
14207
15528
|
}
|
|
14208
15529
|
|
|
14209
15530
|
// src/commands/logout.ts
|
|
14210
15531
|
import { createInterface as createInterface2 } from "readline";
|
|
14211
|
-
import { existsSync as
|
|
15532
|
+
import { existsSync as existsSync27, rmSync as rmSync22 } from "fs";
|
|
14212
15533
|
function stdout4(line) {
|
|
14213
15534
|
process.stdout.write(line + "\n");
|
|
14214
15535
|
}
|
|
@@ -14239,8 +15560,8 @@ async function runLogout(opts = {}) {
|
|
|
14239
15560
|
}
|
|
14240
15561
|
}
|
|
14241
15562
|
await runDisable([], { out, brief: true, keepAutostart: opts.keepAutostart });
|
|
14242
|
-
if (
|
|
14243
|
-
|
|
15563
|
+
if (existsSync27(home)) {
|
|
15564
|
+
rmSync22(home, { recursive: true, force: true });
|
|
14244
15565
|
}
|
|
14245
15566
|
out("");
|
|
14246
15567
|
out("\u2705 \u5DF2\u6E05\u9664\u672C\u673A\u7684 DeskBuddy \u6570\u636E\u3002");
|
|
@@ -14276,58 +15597,63 @@ function pad(s2, width) {
|
|
|
14276
15597
|
return s2 + " ".repeat(Math.max(1, width - w));
|
|
14277
15598
|
}
|
|
14278
15599
|
function formatStatus(input) {
|
|
14279
|
-
const
|
|
15600
|
+
const lines3 = [];
|
|
14280
15601
|
const cfg = input.config;
|
|
14281
|
-
|
|
15602
|
+
lines3.push("DeskBuddy \u72B6\u6001");
|
|
14282
15603
|
const account = cfg.boundAt ? `\u5DF2\u6388\u6743\uFF08clientId ${cfg.clientId.slice(0, 4)}\u2026\uFF0C\u7ED1\u5B9A\u4E8E ${fmtTime(cfg.boundAt)}\uFF09` : "\u672A\u6388\u6743 \u2014\u2014 \u8FD0\u884C deskbuddy setup \u626B\u7801\u6388\u6743";
|
|
14283
|
-
|
|
14284
|
-
|
|
15604
|
+
lines3.push(` ${pad("\u8D26\u53F7", 10)}${account}`);
|
|
15605
|
+
lines3.push(` ${pad("\u670D\u52A1\u7AEF", 10)}${cfg.apiBase}\uFF08${environmentLabel(cfg.apiBase)}\uFF09`);
|
|
14285
15606
|
if (cfg.boundAt && cfg.boundApiBase && cfg.boundApiBase !== cfg.apiBase) {
|
|
14286
|
-
|
|
15607
|
+
lines3.push(
|
|
14287
15608
|
` \u26A0\uFE0F \u6388\u6743\u65F6\u8FDE\u7684\u662F ${cfg.boundApiBase}\uFF08${environmentLabel(cfg.boundApiBase)}\uFF09\uFF0C\u4E0E\u5F53\u524D\u4E0D\u4E00\u81F4 \u2014\u2014 \u5F53\u524D\u73AF\u5883\u5E76\u4E0D\u8BA4\u8BC6\u8FD9\u53F0\u673A\u5668\uFF0C\u9700\u8981\u91CD\u65B0 deskbuddy setup`
|
|
14288
15609
|
);
|
|
14289
15610
|
}
|
|
14290
15611
|
if (input.envOverride?.active) {
|
|
14291
|
-
|
|
15612
|
+
lines3.push(
|
|
14292
15613
|
` \u26A0\uFE0F \u73AF\u5883\u53D8\u91CF DESKBUDDY_API_BASE=${input.envOverride.envValue} \u6B63\u5728\u8986\u76D6 config.json \u91CC\u7684 ${input.envOverride.fileValue}\uFF08\u591A\u534A\u662F\u5199\u8FDB\u4E86 .zshrc \u4E4B\u7C7B\uFF09`
|
|
14293
15614
|
);
|
|
14294
15615
|
}
|
|
14295
15616
|
if (input.tools.length === 0) {
|
|
14296
|
-
|
|
15617
|
+
lines3.push(` ${pad("\u5DF2\u542F\u7528", 10)}\u65E0 \u2014\u2014 \u8FD0\u884C deskbuddy enable claude_code,codex`);
|
|
14297
15618
|
} else {
|
|
14298
15619
|
const desc = input.tools.map((t) => `${t.tool}\uFF08${t.installed ? "hook \u5DF2\u88C5" : "\u26A0\uFE0F hook \u672A\u88C5\uFF0C\u91CD\u8DD1 deskbuddy enable"}\uFF09`).join("\u3001");
|
|
14299
|
-
|
|
15620
|
+
lines3.push(` ${pad("\u5DF2\u542F\u7528", 10)}${desc}`);
|
|
14300
15621
|
}
|
|
14301
15622
|
const versionNote = input.hookVersion === null ? "\u672A\u5B89\u88C5" : input.hookVersion === input.currentVersion ? `${input.hookVersion}\uFF08\u4E0E\u5F53\u524D\u5305\u4E00\u81F4\uFF09` : `${input.hookVersion} \u26A0\uFE0F \u5F53\u524D\u5305\u662F ${input.currentVersion}\uFF0C\u4E0B\u6B21\u8FD0\u884C\u4EFB\u610F deskbuddy \u547D\u4EE4\u4F1A\u81EA\u52A8\u6821\u51C6`;
|
|
14302
|
-
|
|
15623
|
+
lines3.push(` ${pad("hook \u7248\u672C", 10)}${versionNote}`);
|
|
14303
15624
|
if (input.windowWrap) {
|
|
14304
15625
|
const w = input.windowWrap;
|
|
14305
15626
|
const text = w.winPending ? WIN_PENDING : !w.installed ? "\u672A\u5B89\u88C5 \u2014\u2014 \u91CD\u8DD1 deskbuddy enable claude_code" : !w.inPath ? `\u672A\u751F\u6548\uFF08\u5F53\u524D shell \u7684 PATH \u91CC\u6CA1\u6709 ${w.binDir}\uFF0C\u65B0\u5F00\u4E00\u4E2A\u7EC8\u7AEF\u518D\u770B\uFF09` : !w.ptyOk ? `\u5DF2\u5F00\u542F\uFF0C\u4F46 ${w.ptyWhy ?? "node-pty \u6CA1\u88C5\u4E0A"}\uFF0C\u6682\u65F6\u964D\u7EA7\u4E3A\u76F4\u63A5\u8FD0\u884C` : "\u5DF2\u5F00\u542F\uFF08\u7EC8\u7AEF\u91CC\u6572 claude \u4F1A\u7ECF DeskBuddy \u6253\u5F00\uFF0C\u8BBE\u5907\u56DE\u590D\u80FD\u5199\u8FDB\u7A97\u53E3\uFF09";
|
|
14306
|
-
|
|
15627
|
+
lines3.push(` ${pad("\u7A97\u53E3\u63A5\u7BA1", 10)}${text}`);
|
|
14307
15628
|
}
|
|
14308
15629
|
if (input.codexWrap) {
|
|
14309
15630
|
const w = input.codexWrap;
|
|
14310
15631
|
const text = w.winPending ? WIN_PENDING : !w.installed ? "\u672A\u5B89\u88C5 \u2014\u2014 \u91CD\u8DD1 deskbuddy enable codex" : !w.inPath ? `\u672A\u751F\u6548\uFF08\u5F53\u524D shell \u7684 PATH \u91CC\u6CA1\u6709 ${w.binDir}\uFF0C\u65B0\u5F00\u4E00\u4E2A\u7EC8\u7AEF\u518D\u770B\uFF09` : "\u5DF2\u5F00\u542F\uFF08\u7EC8\u7AEF\u91CC\u6572 codex \u4F1A\u7ED9\u8FD9\u4E2A\u7A97\u53E3\u8D77\u4E00\u4E2A app-server \u518D\u8FDE\u4E0A\u53BB\uFF1Bdaemon \u5728\u8DD1\u65F6\u8BBE\u5907\u6307\u4EE4\u80FD\u53D1\u8FDB\u7A97\u53E3\uFF09";
|
|
14311
|
-
|
|
15632
|
+
lines3.push(` ${pad("Codex\u7A97\u53E3", 10)}${text}`);
|
|
15633
|
+
}
|
|
15634
|
+
if (input.workbuddyCli) {
|
|
15635
|
+
const w = input.workbuddyCli;
|
|
15636
|
+
const text = w.standalone ? `\u72EC\u7ACB\u7248 ${w.standalone}\uFF08\u8BBE\u5907\u4E0A\u53EF\u65B0\u5EFA / \u56DE\u590D\u4EFB\u52A1\uFF09` : w.app ? w.declined ? `\u53EA\u88C5\u4E86 App\uFF0C\u5DF2\u9009\u62E9\u6682\u4E0D\u5B89\u88C5\u72EC\u7ACB\u7248\uFF08${LIMITED_NOTE}\uFF09\uFF1B\u8FD0\u884C deskbuddy workbuddy-setup \u53EF\u518D\u88C5` : `\u53EA\u88C5\u4E86 App\uFF08${LIMITED_NOTE}\uFF09\uFF1B\u8FD0\u884C deskbuddy workbuddy-setup \u5B89\u88C5\u72EC\u7ACB\u7248\u547D\u4EE4\u884C` : "\u6CA1\u627E\u5230\uFF08\u88C5 WorkBuddy App \u6216\u8FD0\u884C deskbuddy workbuddy-setup \u88C5\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF09";
|
|
15637
|
+
lines3.push(` ${pad("WorkBuddy", 10)}${text}`);
|
|
14312
15638
|
}
|
|
14313
15639
|
for (const t of input.tools) {
|
|
14314
|
-
if (t.notice)
|
|
15640
|
+
if (t.notice) lines3.push(` ${t.notice}`);
|
|
14315
15641
|
}
|
|
14316
|
-
|
|
15642
|
+
lines3.push("");
|
|
14317
15643
|
const sorted = [...input.sessions].sort((a, b) => b.updatedAt - a.updatedAt);
|
|
14318
|
-
|
|
15644
|
+
lines3.push(`\u5F53\u524D\u672C\u5730\u4F1A\u8BDD\uFF08${sorted.length}\uFF09`);
|
|
14319
15645
|
if (sorted.length === 0) {
|
|
14320
|
-
|
|
15646
|
+
lines3.push(" \u6682\u65E0 \u2014\u2014 \u667A\u80FD\u4F53\u8DD1\u8D77\u6765\u4E4B\u540E\u8FD9\u91CC\u5C31\u4F1A\u6709\u5185\u5BB9");
|
|
14321
15647
|
}
|
|
14322
15648
|
for (const s2 of sorted) {
|
|
14323
|
-
|
|
15649
|
+
lines3.push(
|
|
14324
15650
|
` ${pad(s2.tool, 14)}${pad(s2.title || s2.project || "-", 24)}${pad(STATE_LABELS[s2.state] ?? s2.state, 8)}${relativeTime(s2.updatedAt, input.now)}`
|
|
14325
15651
|
);
|
|
14326
15652
|
}
|
|
14327
|
-
|
|
14328
|
-
|
|
14329
|
-
|
|
14330
|
-
return
|
|
15653
|
+
lines3.push("");
|
|
15654
|
+
lines3.push("\u91C7\u96C6\u8303\u56F4\uFF1A\u53EA\u4E0A\u62A5 \u5DE5\u5177\u540D/\u72B6\u6001/\u4F1A\u8BDD\u6807\u9898/\u9879\u76EE\u76EE\u5F55\u540D/\u65F6\u95F4\u6233");
|
|
15655
|
+
lines3.push("\u4E0D\u4E0A\u62A5\uFF1Aprompt \u539F\u6587\u3001\u547D\u4EE4\u3001\u6587\u4EF6\u8DEF\u5F84\u3001\u4EE3\u7801\u5185\u5BB9");
|
|
15656
|
+
return lines3.join("\n");
|
|
14331
15657
|
}
|
|
14332
15658
|
function collectStatus(now = Date.now()) {
|
|
14333
15659
|
const config = loadConfig();
|
|
@@ -14369,9 +15695,23 @@ function collectStatus(now = Date.now()) {
|
|
|
14369
15695
|
winPending: process.platform === "win32" && !WIN_SHIM_ENABLED
|
|
14370
15696
|
};
|
|
14371
15697
|
}
|
|
15698
|
+
let workbuddyCli;
|
|
15699
|
+
if (config.enabledTools.includes("workbuddy")) {
|
|
15700
|
+
try {
|
|
15701
|
+
const s2 = locateWorkbuddyStandalone();
|
|
15702
|
+
workbuddyCli = {
|
|
15703
|
+
standalone: s2 ? [s2.file, ...s2.prefixArgs].join(" ") : null,
|
|
15704
|
+
app: s2 ? true : locateWorkbuddyApp() != null,
|
|
15705
|
+
declined: config.workbuddySetup?.declined === true
|
|
15706
|
+
};
|
|
15707
|
+
} catch {
|
|
15708
|
+
workbuddyCli = void 0;
|
|
15709
|
+
}
|
|
15710
|
+
}
|
|
14372
15711
|
return {
|
|
14373
15712
|
windowWrap,
|
|
14374
15713
|
codexWrap,
|
|
15714
|
+
workbuddyCli,
|
|
14375
15715
|
config,
|
|
14376
15716
|
hookVersion: installedHookVersion(),
|
|
14377
15717
|
currentVersion: packageVersion(),
|
|
@@ -14436,8 +15776,8 @@ async function runAutoSwitch(opts) {
|
|
|
14436
15776
|
|
|
14437
15777
|
// src/wrap/run.ts
|
|
14438
15778
|
import { randomBytes as randomBytes10 } from "crypto";
|
|
14439
|
-
import { appendFileSync as appendFileSync2, mkdirSync as
|
|
14440
|
-
import { join as
|
|
15779
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync31, statSync as statSync20, truncateSync as truncateSync3 } from "fs";
|
|
15780
|
+
import { join as join45 } from "path";
|
|
14441
15781
|
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
14442
15782
|
|
|
14443
15783
|
// src/wrap/injector.ts
|
|
@@ -14685,10 +16025,10 @@ var Injector = class {
|
|
|
14685
16025
|
};
|
|
14686
16026
|
|
|
14687
16027
|
// src/wrap/passthrough.ts
|
|
14688
|
-
import { spawn as
|
|
16028
|
+
import { spawn as spawn7 } from "child_process";
|
|
14689
16029
|
import { realpathSync as realpathSync7 } from "fs";
|
|
14690
16030
|
import { constants as constants2 } from "os";
|
|
14691
|
-
import { dirname as
|
|
16031
|
+
import { dirname as dirname25, resolve as resolve9 } from "path";
|
|
14692
16032
|
var CLAUDE_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
14693
16033
|
"agents",
|
|
14694
16034
|
"attach",
|
|
@@ -14752,7 +16092,7 @@ function real(p) {
|
|
|
14752
16092
|
function findReal(tool, d = defaultLocateDeps(), skip = shimDir()) {
|
|
14753
16093
|
const win = (d.platform ?? process.platform) === "win32";
|
|
14754
16094
|
const skipKeys = /* @__PURE__ */ new Set([norm2(skip, win), norm2(real(skip), win)]);
|
|
14755
|
-
const inSkip = (file) => skipKeys.has(norm2(
|
|
16095
|
+
const inSkip = (file) => skipKeys.has(norm2(dirname25(file), win)) || skipKeys.has(norm2(dirname25(real(file)), win));
|
|
14756
16096
|
const dd = { ...d, skipDirs: [...d.skipDirs ?? [], skip] };
|
|
14757
16097
|
const name = SHIM_COMMANDS[tool];
|
|
14758
16098
|
if (!name) {
|
|
@@ -14785,7 +16125,7 @@ function runDirect(real2, args, env = process.env) {
|
|
|
14785
16125
|
return new Promise((done) => {
|
|
14786
16126
|
let child;
|
|
14787
16127
|
try {
|
|
14788
|
-
child =
|
|
16128
|
+
child = spawn7(real2.file, [...real2.prefixArgs, ...args], {
|
|
14789
16129
|
stdio: "inherit",
|
|
14790
16130
|
// 🔴 再经过垫片也直接放行,不会绕回来
|
|
14791
16131
|
env: withoutShimFlag({ ...env, DESKBUDDY_WRAPPED: "1" }),
|
|
@@ -14835,11 +16175,11 @@ function runDirect(real2, args, env = process.env) {
|
|
|
14835
16175
|
}
|
|
14836
16176
|
|
|
14837
16177
|
// src/wrap/codexLaunch.ts
|
|
14838
|
-
import { execFileSync as execFileSync16, spawn as
|
|
14839
|
-
import { closeSync as
|
|
16178
|
+
import { execFileSync as execFileSync16, spawn as spawn8 } from "child_process";
|
|
16179
|
+
import { closeSync as closeSync10, mkdirSync as mkdirSync30, openSync as openSync10, readdirSync as readdirSync12, readFileSync as readFileSync30, rmSync as rmSync23, statSync as statSync19, truncateSync as truncateSync2, writeFileSync as writeFileSync29, renameSync as renameSync20 } from "fs";
|
|
14840
16180
|
import { request as request2 } from "http";
|
|
14841
16181
|
import { createServer as createServer4 } from "net";
|
|
14842
|
-
import { join as
|
|
16182
|
+
import { join as join43 } from "path";
|
|
14843
16183
|
var TAKEOVER_VALUE_OPTS = /* @__PURE__ */ new Set(["-c", "--config", "-m", "--model", "-s", "--sandbox", "-a", "--ask-for-approval", "-C", "--cd", "--enable", "--disable"]);
|
|
14844
16184
|
var SHORT_VALUE_OPTS = /* @__PURE__ */ new Set(["-c", "-m", "-s", "-a", "-C"]);
|
|
14845
16185
|
var TAKEOVER_FLAGS = /* @__PURE__ */ new Set(["--search", "--no-alt-screen", "--dangerously-bypass-approvals-and-sandbox"]);
|
|
@@ -15008,12 +16348,12 @@ async function runCodexWindow(real2, args, d) {
|
|
|
15008
16348
|
}
|
|
15009
16349
|
var MAX_LOG_BYTES2 = 1024 * 1024;
|
|
15010
16350
|
function appServerLogPath() {
|
|
15011
|
-
return
|
|
16351
|
+
return join43(deskbuddyHome(), "codex-app-server.log");
|
|
15012
16352
|
}
|
|
15013
16353
|
function windowsDir2() {
|
|
15014
|
-
return
|
|
16354
|
+
return join43(deskbuddyHome(), "codex-windows");
|
|
15015
16355
|
}
|
|
15016
|
-
function
|
|
16356
|
+
function pidAlive6(pid) {
|
|
15017
16357
|
try {
|
|
15018
16358
|
process.kill(pid, 0);
|
|
15019
16359
|
return true;
|
|
@@ -15024,11 +16364,11 @@ function pidAlive5(pid) {
|
|
|
15024
16364
|
function daemonAlive(pidFile = pidFilePath(), args = processArgs) {
|
|
15025
16365
|
let pid = 0;
|
|
15026
16366
|
try {
|
|
15027
|
-
pid = Number(
|
|
16367
|
+
pid = Number(readFileSync30(pidFile, "utf8").trim());
|
|
15028
16368
|
} catch {
|
|
15029
16369
|
return false;
|
|
15030
16370
|
}
|
|
15031
|
-
if (!Number.isInteger(pid) || pid <= 0 || !
|
|
16371
|
+
if (!Number.isInteger(pid) || pid <= 0 || !pidAlive6(pid)) return false;
|
|
15032
16372
|
const a = args(pid);
|
|
15033
16373
|
return !!a && /cli\.js daemon(?:\s|$)/.test(a);
|
|
15034
16374
|
}
|
|
@@ -15061,22 +16401,22 @@ function serverSpawnOptions(platform, fd, cwd, env) {
|
|
|
15061
16401
|
}
|
|
15062
16402
|
function startServer(real2, url, env, cwd, platform = process.platform) {
|
|
15063
16403
|
const logPath2 = appServerLogPath();
|
|
15064
|
-
|
|
16404
|
+
mkdirSync30(deskbuddyHome(), { recursive: true });
|
|
15065
16405
|
try {
|
|
15066
|
-
if (
|
|
16406
|
+
if (statSync19(logPath2).size > MAX_LOG_BYTES2) truncateSync2(logPath2, 0);
|
|
15067
16407
|
} catch {
|
|
15068
16408
|
}
|
|
15069
|
-
const fd =
|
|
16409
|
+
const fd = openSync10(logPath2, "a");
|
|
15070
16410
|
try {
|
|
15071
16411
|
const childEnv2 = { ...env, DESKBUDDY_WRAPPED: "1" };
|
|
15072
16412
|
delete childEnv2.DESKBUDDY_SHIM;
|
|
15073
|
-
const child =
|
|
16413
|
+
const child = spawn8(real2.file, [...real2.prefixArgs, "app-server", "--listen", url], serverSpawnOptions(platform, fd, cwd, childEnv2));
|
|
15074
16414
|
child.on("error", () => {
|
|
15075
16415
|
});
|
|
15076
16416
|
child.unref();
|
|
15077
16417
|
return child.pid ?? null;
|
|
15078
16418
|
} finally {
|
|
15079
|
-
|
|
16419
|
+
closeSync10(fd);
|
|
15080
16420
|
}
|
|
15081
16421
|
}
|
|
15082
16422
|
function killServer(pid, platform = process.platform, run2 = taskkill) {
|
|
@@ -15101,41 +16441,41 @@ function killOwnServer(pid, url, args = freshArgs, kill = (p) => killServer(p))
|
|
|
15101
16441
|
if (a && isAppServerArgs(a, url)) kill(pid);
|
|
15102
16442
|
}
|
|
15103
16443
|
function recordPath(rec) {
|
|
15104
|
-
return
|
|
16444
|
+
return join43(windowsDir2(), `${rec.server}.json`);
|
|
15105
16445
|
}
|
|
15106
16446
|
function writeRecord(rec) {
|
|
15107
16447
|
try {
|
|
15108
|
-
|
|
16448
|
+
mkdirSync30(windowsDir2(), { recursive: true, mode: 448 });
|
|
15109
16449
|
const p = recordPath(rec);
|
|
15110
|
-
|
|
15111
|
-
|
|
16450
|
+
writeFileSync29(p + ".tmp", JSON.stringify(rec), { encoding: "utf8", mode: 384 });
|
|
16451
|
+
renameSync20(p + ".tmp", p);
|
|
15112
16452
|
} catch {
|
|
15113
16453
|
}
|
|
15114
16454
|
}
|
|
15115
16455
|
function removeRecord(rec) {
|
|
15116
16456
|
try {
|
|
15117
|
-
|
|
16457
|
+
rmSync23(recordPath(rec), { force: true });
|
|
15118
16458
|
} catch {
|
|
15119
16459
|
}
|
|
15120
16460
|
}
|
|
15121
|
-
function reapWindows(dir = windowsDir2(), alive =
|
|
16461
|
+
function reapWindows(dir = windowsDir2(), alive = pidAlive6, args = freshArgs, kill = (pid) => killServer(pid)) {
|
|
15122
16462
|
let names = [];
|
|
15123
16463
|
try {
|
|
15124
|
-
names =
|
|
16464
|
+
names = readdirSync12(dir).filter((n) => n.endsWith(".json"));
|
|
15125
16465
|
} catch {
|
|
15126
16466
|
return;
|
|
15127
16467
|
}
|
|
15128
16468
|
for (const n of names) {
|
|
15129
|
-
const p =
|
|
16469
|
+
const p = join43(dir, n);
|
|
15130
16470
|
let rec;
|
|
15131
16471
|
try {
|
|
15132
|
-
rec = JSON.parse(
|
|
16472
|
+
rec = JSON.parse(readFileSync30(p, "utf8"));
|
|
15133
16473
|
} catch {
|
|
15134
|
-
|
|
16474
|
+
rmSync23(p, { force: true });
|
|
15135
16475
|
continue;
|
|
15136
16476
|
}
|
|
15137
16477
|
if (typeof rec.supervisor !== "number" || typeof rec.server !== "number" || typeof rec.url !== "string") {
|
|
15138
|
-
|
|
16478
|
+
rmSync23(p, { force: true });
|
|
15139
16479
|
continue;
|
|
15140
16480
|
}
|
|
15141
16481
|
if (alive(rec.supervisor)) continue;
|
|
@@ -15143,13 +16483,13 @@ function reapWindows(dir = windowsDir2(), alive = pidAlive5, args = freshArgs, k
|
|
|
15143
16483
|
const a = args(rec.server);
|
|
15144
16484
|
if (a && isAppServerArgs(a, rec.url)) kill(rec.server);
|
|
15145
16485
|
}
|
|
15146
|
-
|
|
16486
|
+
rmSync23(p, { force: true });
|
|
15147
16487
|
}
|
|
15148
16488
|
}
|
|
15149
16489
|
function launchLog(line) {
|
|
15150
16490
|
try {
|
|
15151
|
-
|
|
15152
|
-
|
|
16491
|
+
mkdirSync30(deskbuddyHome(), { recursive: true });
|
|
16492
|
+
writeFileSync29(join43(deskbuddyHome(), "wrap.log"), `${(/* @__PURE__ */ new Date()).toISOString()} [codex ${process.pid}] ${line}
|
|
15153
16493
|
`, { flag: "a" });
|
|
15154
16494
|
} catch {
|
|
15155
16495
|
}
|
|
@@ -15172,9 +16512,9 @@ function defaultCodexWindowDeps(base) {
|
|
|
15172
16512
|
}
|
|
15173
16513
|
|
|
15174
16514
|
// src/wrap/promptLaunch.ts
|
|
15175
|
-
import { randomBytes as randomBytes8 } from "crypto";
|
|
15176
|
-
import { homedir as
|
|
15177
|
-
import { join as
|
|
16515
|
+
import { randomBytes as randomBytes8, randomUUID as randomUUID4 } from "crypto";
|
|
16516
|
+
import { homedir as homedir13 } from "os";
|
|
16517
|
+
import { join as join44 } from "path";
|
|
15178
16518
|
var FIRST_LINE_WAIT_MS2 = 2 * 60 * 1e3;
|
|
15179
16519
|
function promptArgs(text) {
|
|
15180
16520
|
return text ? ["--", prepare(text)] : [];
|
|
@@ -15182,7 +16522,7 @@ function promptArgs(text) {
|
|
|
15182
16522
|
function promptEnv(tool, env, launcherPid = process.pid, answerWindowId = "") {
|
|
15183
16523
|
const out = { ...env };
|
|
15184
16524
|
if (tool === "workbuddy") {
|
|
15185
|
-
if (!out.CODEBUDDY_CONFIG_DIR) out.CODEBUDDY_CONFIG_DIR =
|
|
16525
|
+
if (!out.CODEBUDDY_CONFIG_DIR) out.CODEBUDDY_CONFIG_DIR = join44(homedir13(), ".workbuddy");
|
|
15186
16526
|
out.DESKBUDDY_LAUNCHER_PID = String(launcherPid);
|
|
15187
16527
|
out.DESKBUDDY_LAUNCHED_TOOL = tool;
|
|
15188
16528
|
if (answerWindowId) out.DESKBUDDY_LAUNCHED_WINDOW = answerWindowId;
|
|
@@ -15199,8 +16539,10 @@ function workbuddyNotifyArgs(hookPath, platform = process.platform) {
|
|
|
15199
16539
|
};
|
|
15200
16540
|
return ["--settings", JSON.stringify(settings)];
|
|
15201
16541
|
}
|
|
15202
|
-
function launchArgs(tool, platform, hookPath = installedHookPath()) {
|
|
15203
|
-
|
|
16542
|
+
function launchArgs(tool, platform, hookPath = installedHookPath(), sessionId = "") {
|
|
16543
|
+
if (tool === "workbuddy") return workbuddyNotifyArgs(hookPath, platform);
|
|
16544
|
+
if (tool === "qoder_cn" && sessionId) return ["--session-id", sessionId];
|
|
16545
|
+
return [];
|
|
15204
16546
|
}
|
|
15205
16547
|
async function runPromptLaunch(tool, windowId, cwd, d) {
|
|
15206
16548
|
let real2 = null;
|
|
@@ -15234,7 +16576,20 @@ async function runPromptLaunch(tool, windowId, cwd, d) {
|
|
|
15234
16576
|
d.log(`[${windowId.slice(0, 8)}] \u6CA1\u7B49\u5230\u9996\u53E5\uFF0C\u8D77\u4E00\u4E2A\u7A7A\u7684 ${tool}`);
|
|
15235
16577
|
}
|
|
15236
16578
|
const answerWindowId = tool === "workbuddy" || tool === "codex" ? randomBytes8(16).toString("hex") : "";
|
|
15237
|
-
|
|
16579
|
+
if (tool !== "qoder_cn") {
|
|
16580
|
+
return d.run(tool, real2, [...launchArgs(tool, d.platform), ...promptArgs(text)], promptEnv(tool, d.env, process.pid, answerWindowId), answerWindowId);
|
|
16581
|
+
}
|
|
16582
|
+
const sid = (d.newSessionId ?? randomUUID4)();
|
|
16583
|
+
const env = { ...promptEnv(tool, d.env), ...real2.env ?? {} };
|
|
16584
|
+
const code = await d.run(tool, real2, [...launchArgs(tool, d.platform, "", sid), ...promptArgs(text)], env);
|
|
16585
|
+
if (d.afterQoder) {
|
|
16586
|
+
try {
|
|
16587
|
+
await d.afterQoder(sid, cwd, text);
|
|
16588
|
+
} catch (e) {
|
|
16589
|
+
d.log(`\u767B\u8BB0 Qoder \u4F1A\u8BDD\u51FA\u9519\uFF1A${e.message}`);
|
|
16590
|
+
}
|
|
16591
|
+
}
|
|
16592
|
+
return code;
|
|
15238
16593
|
}
|
|
15239
16594
|
function receiveFirstLine(windowId, tool, cwd, opts = {}) {
|
|
15240
16595
|
const platform = opts.platform ?? process.platform;
|
|
@@ -15321,18 +16676,18 @@ import { StringDecoder } from "string_decoder";
|
|
|
15321
16676
|
var TITLE = /^\s*Select login method:\s*$/;
|
|
15322
16677
|
var OPTION = /^\s*(?:[❯>›]\s*)?Log in via (?:Chinese Site|International Site|Enterprise Domain|iOA\b)/;
|
|
15323
16678
|
var HINT = /^\s*\(Use ↑↓ to select, Enter to login\)\s*$/;
|
|
15324
|
-
function isLoginPage(
|
|
16679
|
+
function isLoginPage(lines3) {
|
|
15325
16680
|
let title = -1;
|
|
15326
|
-
for (let i =
|
|
15327
|
-
if (TITLE.test(
|
|
16681
|
+
for (let i = lines3.length - 1; i >= 0; i--) {
|
|
16682
|
+
if (TITLE.test(lines3[i])) {
|
|
15328
16683
|
title = i;
|
|
15329
16684
|
break;
|
|
15330
16685
|
}
|
|
15331
16686
|
}
|
|
15332
16687
|
if (title < 0) return false;
|
|
15333
|
-
const below =
|
|
16688
|
+
const below = lines3.slice(title + 1);
|
|
15334
16689
|
const options = below.filter((l) => OPTION.test(l)).length;
|
|
15335
|
-
const tail2 =
|
|
16690
|
+
const tail2 = lines3.filter((l) => l.trim() !== "").slice(-HINT_TAIL_LINES);
|
|
15336
16691
|
return options >= 2 && tail2.some((l) => HINT.test(l));
|
|
15337
16692
|
}
|
|
15338
16693
|
var HINT_TAIL_LINES = 3;
|
|
@@ -15349,8 +16704,8 @@ var LoginWatcher = class {
|
|
|
15349
16704
|
get onScreen() {
|
|
15350
16705
|
return this.#onScreen;
|
|
15351
16706
|
}
|
|
15352
|
-
tick(
|
|
15353
|
-
this.#onScreen = !!
|
|
16707
|
+
tick(lines3) {
|
|
16708
|
+
this.#onScreen = !!lines3 && isLoginPage(lines3);
|
|
15354
16709
|
if (this.#reported) return;
|
|
15355
16710
|
if (!this.#onScreen) {
|
|
15356
16711
|
this.#since = null;
|
|
@@ -15488,9 +16843,9 @@ async function runAnswerWindow(tool, real2, args, env, windowId, d) {
|
|
|
15488
16843
|
}) : null;
|
|
15489
16844
|
const trustTimer = setInterval(() => {
|
|
15490
16845
|
try {
|
|
15491
|
-
const
|
|
15492
|
-
trust.tick(
|
|
15493
|
-
login?.tick(
|
|
16846
|
+
const lines3 = term && !finished ? screenLines(term) : null;
|
|
16847
|
+
trust.tick(lines3);
|
|
16848
|
+
login?.tick(lines3);
|
|
15494
16849
|
} catch {
|
|
15495
16850
|
}
|
|
15496
16851
|
}, TRUST_TICK_MS);
|
|
@@ -15613,10 +16968,10 @@ function childEnv(src, windowId) {
|
|
|
15613
16968
|
}
|
|
15614
16969
|
function wrapLog(line) {
|
|
15615
16970
|
try {
|
|
15616
|
-
const p =
|
|
15617
|
-
|
|
16971
|
+
const p = join45(deskbuddyHome(), "wrap.log");
|
|
16972
|
+
mkdirSync31(deskbuddyHome(), { recursive: true });
|
|
15618
16973
|
try {
|
|
15619
|
-
if (
|
|
16974
|
+
if (statSync20(p).size > MAX_LOG_BYTES3) truncateSync3(p, 0);
|
|
15620
16975
|
} catch {
|
|
15621
16976
|
}
|
|
15622
16977
|
appendFileSync2(p, `${(/* @__PURE__ */ new Date()).toISOString()} [${process.pid}] ${line}
|
|
@@ -15720,7 +17075,8 @@ function defaultPromptDeps(deps2, cwd, launchId) {
|
|
|
15720
17075
|
now: () => Date.now(),
|
|
15721
17076
|
stderr: deps2.stderr,
|
|
15722
17077
|
// WorkBuddy 开窗:要有交互界面的那份 CLI(app 自带的可能没有,退到 PATH 上的,见 locateWorkbuddyTui)
|
|
15723
|
-
|
|
17078
|
+
// Qoder CN 开窗:PATH 上的 qoderclicn 优先(不带 ELECTRON_RUN_AS_NODE),没有才用 worker(见 locateQoderCnTui)
|
|
17079
|
+
findReal: (tool) => tool === "workbuddy" ? locateWorkbuddyTui() : tool === "qoder_cn" ? locateQoderCnTui() : deps2.findReal(tool),
|
|
15724
17080
|
receive: (id, tool, c) => receiveFirstLine(id, tool, c, { platform: deps2.platform, log: deps2.log }),
|
|
15725
17081
|
run: async (tool, real2, args, env, answerWindowId) => {
|
|
15726
17082
|
if (tool === "workbuddy" && answerWindowId) {
|
|
@@ -15754,7 +17110,11 @@ function defaultPromptDeps(deps2, cwd, launchId) {
|
|
|
15754
17110
|
return runDirect(real2, args, env);
|
|
15755
17111
|
},
|
|
15756
17112
|
env: deps2.env,
|
|
15757
|
-
log: deps2.log
|
|
17113
|
+
log: deps2.log,
|
|
17114
|
+
afterQoder: async (sid, c, first) => {
|
|
17115
|
+
const r = await syncQoderApp({ tool: "qoder_cn", kind: "register", sessionId: sid, cwd: c, fallbackTitle: first ?? "" });
|
|
17116
|
+
deps2.log(`[${(launchId ?? "").slice(0, 8)}] Qoder \u4F1A\u8BDD ${sid.slice(0, 8)} \u767B\u8BB0\u8FDB\u684C\u9762\u7AEF\u5217\u8868\uFF1A${r}`);
|
|
17117
|
+
}
|
|
15758
17118
|
};
|
|
15759
17119
|
}
|
|
15760
17120
|
async function runLaunch(argv, deps2 = defaultWrapDeps(), ld = defaultLaunchDeps()) {
|
|
@@ -16076,6 +17436,46 @@ ${WRAP_HINT}\uFF08\u8BBE\u5907\u56DE\u590D\u6682\u65F6\u5199\u4E0D\u8FDB\u8FD9\u
|
|
|
16076
17436
|
});
|
|
16077
17437
|
}
|
|
16078
17438
|
|
|
17439
|
+
// src/commands/workbuddySetup.ts
|
|
17440
|
+
function parseFrom(argv) {
|
|
17441
|
+
const i = argv.indexOf("--from");
|
|
17442
|
+
const v = i >= 0 ? argv[i + 1] : void 0;
|
|
17443
|
+
return v === "install" || v === "enable" ? v : null;
|
|
17444
|
+
}
|
|
17445
|
+
async function runWorkbuddySetup(opts = {}) {
|
|
17446
|
+
const out = opts.out ?? ((l) => process.stdout.write(l + "\n"));
|
|
17447
|
+
if (opts.from) {
|
|
17448
|
+
const d2 = opts.deps ?? defaultOfferDeps(log);
|
|
17449
|
+
await runOffer(opts.from, d2);
|
|
17450
|
+
return 0;
|
|
17451
|
+
}
|
|
17452
|
+
const base = opts.deps ?? defaultOfferDeps(log);
|
|
17453
|
+
const d = { ...base, log: (l) => (out(l), base.log(l)) };
|
|
17454
|
+
const cur = opts.current ? opts.current() : (() => {
|
|
17455
|
+
const w = locateWorkbuddy();
|
|
17456
|
+
return w ? { file: [w.loc.file, ...w.loc.prefixArgs].join(" "), kind: w.kind } : null;
|
|
17457
|
+
})();
|
|
17458
|
+
if (cur?.kind === "standalone") {
|
|
17459
|
+
out(`\u5DF2\u5B89\u88C5 WorkBuddy \u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF1A${cur.file}`);
|
|
17460
|
+
out(`\u6CA1\u767B\u5F55\u7684\u8BDD\u5728\u7EC8\u7AEF\u8FD0\u884C ${manualLoginCommand(d.platform)}\uFF0C\u8F93\u5165 /login \u9009\u56FD\u5185\u7248`);
|
|
17461
|
+
return 0;
|
|
17462
|
+
}
|
|
17463
|
+
const r = await runOffer("manual", d, { skipAsk: !!opts.yes });
|
|
17464
|
+
switch (r) {
|
|
17465
|
+
case "installed":
|
|
17466
|
+
return 0;
|
|
17467
|
+
case "no_gui":
|
|
17468
|
+
out(`\u6CA1\u6709\u56FE\u5F62\u4F1A\u8BDD\uFF0C\u6CA1\u5F39\u6846\u3002\u53EF\u4EE5\u52A0 --yes \u76F4\u63A5\u88C5\uFF1Adeskbuddy workbuddy-setup --yes\uFF1B\u6216\u8005${manualHint(d.platform).replace(/^请/, "")}`);
|
|
17469
|
+
return 0;
|
|
17470
|
+
case "later":
|
|
17471
|
+
case "timeout":
|
|
17472
|
+
out(`\u6CA1\u88C5\u3002\u4E0D\u88C5\u7684\u8BDD${LIMITED_NOTE}\uFF1B\u4E4B\u540E\u8FD0\u884C deskbuddy workbuddy-setup \u53EF\u518D\u88C5`);
|
|
17473
|
+
return 0;
|
|
17474
|
+
default:
|
|
17475
|
+
return 1;
|
|
17476
|
+
}
|
|
17477
|
+
}
|
|
17478
|
+
|
|
16079
17479
|
// src/cli.ts
|
|
16080
17480
|
var USAGE = `deskbuddy \u2014\u2014 \u628A AI \u7F16\u7801\u667A\u80FD\u4F53\u6BCF\u4E2A\u4F1A\u8BDD\u7A97\u53E3\u7684\u72B6\u6001\u540C\u6B65\u5230\u684C\u9762\u786C\u4EF6\u5C4F\u5E55
|
|
16081
17481
|
|
|
@@ -16091,6 +17491,8 @@ var USAGE = `deskbuddy \u2014\u2014 \u628A AI \u7F16\u7801\u667A\u80FD\u4F53\u6B
|
|
|
16091
17491
|
deskbuddy daemon [--foreground] \u5E38\u9A7B\u6536\u53D6\u8BBE\u5907\u4E0B\u53D1\u7684\u6307\u4EE4\uFF08\u5F00\u673A\u81EA\u542F\u4F1A\u81EA\u52A8\u62C9\u8D77\uFF09
|
|
16092
17492
|
deskbuddy status \u770B\u91C7\u96C6\u5230\u4E86\u4EC0\u4E48\u3001\u4E0A\u62A5\u4E86\u54EA\u4E9B\u5B57\u6BB5
|
|
16093
17493
|
deskbuddy logout [--yes] \u79FB\u9664 hook \u5E76\u6E05\u9664\u672C\u673A\u6570\u636E
|
|
17494
|
+
deskbuddy workbuddy-setup [--yes] \u5B89\u88C5 WorkBuddy \u7684\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF08CodeBuddy Code\uFF09\uFF0C\u88C5\u4E86\u624D\u80FD\u5728
|
|
17495
|
+
\u8BBE\u5907\u4E0A\u65B0\u5EFA / \u56DE\u590D WorkBuddy \u4EFB\u52A1\uFF08--yes \u4E0D\u5F39\u6846\u76F4\u63A5\u88C5\uFF09
|
|
16094
17496
|
deskbuddy wrap <tool> [\u53C2\u6570\u2026] \uFF08\u57AB\u7247\u5185\u90E8\u7528\uFF09\u7ECF DeskBuddy \u6253\u5F00\u667A\u80FD\u4F53\u7A97\u53E3\uFF0C
|
|
16095
17497
|
\u8BBE\u5907\u4E0A\u7684\u56DE\u590D\u80FD\u5199\u8FDB\u8FD9\u4E2A\u7A97\u53E3\uFF1BDESKBUDDY_WRAP_OFF=1 \u53EF\u7ED5\u5F00
|
|
16096
17498
|
|
|
@@ -16135,6 +17537,8 @@ async function main(argv) {
|
|
|
16135
17537
|
return runStatus();
|
|
16136
17538
|
case "logout":
|
|
16137
17539
|
return runLogout({ yes: argv.includes("--yes") || argv.includes("-y") });
|
|
17540
|
+
case "workbuddy-setup":
|
|
17541
|
+
return runWorkbuddySetup({ yes: argv.includes("--yes") || argv.includes("-y"), from: parseFrom(argv) });
|
|
16138
17542
|
case "wrap":
|
|
16139
17543
|
return runWrap(argv.slice(3));
|
|
16140
17544
|
default:
|