deskbuddy 0.2.0-beta.30 → 0.2.0-beta.32
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/{chunk-UWGEPFS6.js → chunk-4ICQ3BAL.js} +879 -286
- package/dist/cli.js +663 -372
- package/dist/deskbuddy-dsh.mjs +139 -3
- package/dist/{enable-KQYXVSQN.js → enable-SIY4AEF7.js} +1 -1
- package/dist/gate.cjs +40 -3
- package/dist/hook.cjs +3 -1
- package/dist/opencode.mjs +84 -4
- package/hook/deskbuddy-dsh.d.mts +10 -0
- package/hook/deskbuddy-dsh.mjs +139 -3
- package/hook/gate.cjs +40 -3
- package/hook/hook-debug.log +54 -0
- package/hook/hook.cjs +3 -1
- package/hook/opencode.d.mts +3 -0
- package/hook/opencode.mjs +84 -4
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/commands/enable.ts
|
|
4
|
-
import { dirname as dirname19, join as
|
|
4
|
+
import { dirname as dirname19, join as join36 } from "path";
|
|
5
5
|
|
|
6
6
|
// src/core/config.ts
|
|
7
7
|
import { randomUUID } from "crypto";
|
|
@@ -67,6 +67,9 @@ function qoderCnSettingsPath() {
|
|
|
67
67
|
function qwenworkHome() {
|
|
68
68
|
return process.env.DESKBUDDY_QWENWORK_DIR || join(homedir(), ".QwenWorkCN");
|
|
69
69
|
}
|
|
70
|
+
function qwenworkDataHome() {
|
|
71
|
+
return process.env.DESKBUDDY_QWENWORK_CN_HOME || join(homedir(), ".qwenworkcn");
|
|
72
|
+
}
|
|
70
73
|
function qwenworkSettingsPath() {
|
|
71
74
|
return join(qwenworkHome(), "settings.json");
|
|
72
75
|
}
|
|
@@ -1523,6 +1526,19 @@ function installCcWindowGate(settingsPath, gatePath, tool = "claude_code", platf
|
|
|
1523
1526
|
});
|
|
1524
1527
|
writeSettings(settingsPath, settings);
|
|
1525
1528
|
}
|
|
1529
|
+
var PRETOOL_QUESTION_TIMEOUT_S = 660;
|
|
1530
|
+
var PRETOOL_QUESTION_MATCHER = "^AskUserQuestion$";
|
|
1531
|
+
function installCcPreToolQuestion(settingsPath, gatePath, tool, platform = process.platform) {
|
|
1532
|
+
const settings = readSettings(settingsPath);
|
|
1533
|
+
settings.hooks ??= {};
|
|
1534
|
+
const groups = settings.hooks.PreToolUse ??= [];
|
|
1535
|
+
if (groups.some((g) => g.hooks?.some((h) => mentions(h.command, gatePath) && h.command.includes("--pretool-question")))) return;
|
|
1536
|
+
groups.push({
|
|
1537
|
+
matcher: PRETOOL_QUESTION_MATCHER,
|
|
1538
|
+
hooks: [{ type: "command", command: `node ${scriptArg(gatePath, platform, shQuote2)} --pretool-question --tool ${tool}`, timeout: PRETOOL_QUESTION_TIMEOUT_S }]
|
|
1539
|
+
});
|
|
1540
|
+
writeSettings(settingsPath, settings);
|
|
1541
|
+
}
|
|
1526
1542
|
var REWAKE_TIMEOUT_S = 20 * 24 * 60 * 60;
|
|
1527
1543
|
var REWAKE_MESSAGE = "\u4EE5\u4E0B\u662F\u4E00\u6761 DeskBuddy \u8BBE\u5907\u6307\u4EE4\uFF08\u6309\u4F1A\u8BDD\u5F00\u5934 DeskBuddy \u8BF4\u660E\u91CC\u7684\u53E3\u4EE4\u6838\u5BF9\uFF0C\u53E3\u4EE4\u4E00\u81F4\u624D\u7167\u5E38\u6267\u884C\uFF0C\u5B8C\u6210\u540E\u7B80\u8981\u56DE\u590D\u7ED3\u679C\uFF09\uFF1A";
|
|
1528
1544
|
var REWAKE_SUMMARY = "DeskBuddy \u8BBE\u5907\u6307\u4EE4";
|
|
@@ -1871,12 +1887,12 @@ function isDshPluginInstalled(patchPath) {
|
|
|
1871
1887
|
var DSH_NOTICE = " \u5DF2\u5F00\u7740\u7684 DeepSeek Harness\uFF08dsh web / \u684C\u9762\u7AEF\uFF09\u91CD\u542F\u540E\u751F\u6548";
|
|
1872
1888
|
|
|
1873
1889
|
// src/daemon/dumateApp/shortcuts.ts
|
|
1874
|
-
import { join as
|
|
1890
|
+
import { join as join26 } from "path";
|
|
1875
1891
|
|
|
1876
1892
|
// src/daemon/appShortcuts.ts
|
|
1877
1893
|
import { execFile as execFile2, execFileSync as execFileSync4 } from "child_process";
|
|
1878
|
-
import { mkdirSync as mkdirSync11, readFileSync as
|
|
1879
|
-
import { join as join10, win32 } from "path";
|
|
1894
|
+
import { mkdirSync as mkdirSync11, readFileSync as readFileSync13, renameSync as renameSync8, writeFileSync as writeFileSync11 } from "fs";
|
|
1895
|
+
import { join as join10, win32 as win322 } from "path";
|
|
1880
1896
|
|
|
1881
1897
|
// src/core/fileLock.ts
|
|
1882
1898
|
import { existsSync as existsSync10, mkdirSync as mkdirSync10, readFileSync as readFileSync11, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
@@ -1937,7 +1953,73 @@ function pidAlive2(pid) {
|
|
|
1937
1953
|
}
|
|
1938
1954
|
}
|
|
1939
1955
|
|
|
1956
|
+
// src/daemon/winPaths.ts
|
|
1957
|
+
import { readFileSync as readFileSync12, statSync as statSync4 } from "fs";
|
|
1958
|
+
import { win32 } from "path";
|
|
1959
|
+
function decodeText(b) {
|
|
1960
|
+
if (b.length >= 2 && b[0] === 255 && b[1] === 254) return b.subarray(2).toString("utf16le");
|
|
1961
|
+
if (b.length >= 2 && b[0] === 254 && b[1] === 255) {
|
|
1962
|
+
const be = Buffer.from(b.subarray(2, 2 + (b.length - 2 & ~1)));
|
|
1963
|
+
return be.swap16().toString("utf16le");
|
|
1964
|
+
}
|
|
1965
|
+
if (b.length >= 3 && b[0] === 239 && b[1] === 187 && b[2] === 191) return b.subarray(3).toString("utf8");
|
|
1966
|
+
return b.toString("utf8");
|
|
1967
|
+
}
|
|
1968
|
+
function readSmallText(p) {
|
|
1969
|
+
try {
|
|
1970
|
+
if (statSync4(p).size > 64 * 1024) return null;
|
|
1971
|
+
return decodeText(readFileSync12(p));
|
|
1972
|
+
} catch {
|
|
1973
|
+
return null;
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
function isValidWinPath(p) {
|
|
1977
|
+
if (typeof p !== "string" || p.length < 4 || p.length > 1024) return false;
|
|
1978
|
+
if (!/^[a-z]:\\/i.test(p)) return false;
|
|
1979
|
+
if (/[\u0000-\u001f*?"<>|%]/.test(p)) return false;
|
|
1980
|
+
return !p.split("\\").includes("..");
|
|
1981
|
+
}
|
|
1982
|
+
function normalizeHint(raw) {
|
|
1983
|
+
if (typeof raw !== "string") return null;
|
|
1984
|
+
let v = raw.trim();
|
|
1985
|
+
if (!v) return null;
|
|
1986
|
+
if (v.startsWith('"')) {
|
|
1987
|
+
const end = v.indexOf('"', 1);
|
|
1988
|
+
if (end < 0) return null;
|
|
1989
|
+
v = v.slice(1, end);
|
|
1990
|
+
} else {
|
|
1991
|
+
v = v.replace(/,\s*-?\d+$/, "");
|
|
1992
|
+
const m = /^(.*?\.exe)(\s|$)/i.exec(v);
|
|
1993
|
+
if (m) v = m[1];
|
|
1994
|
+
}
|
|
1995
|
+
v = v.replace(/\//g, "\\").replace(/\\+$/, "").trim();
|
|
1996
|
+
return isValidWinPath(v) ? v : null;
|
|
1997
|
+
}
|
|
1998
|
+
function launcherTarget(text, exe) {
|
|
1999
|
+
const kv = /* @__PURE__ */ new Map();
|
|
2000
|
+
for (const line of text.split(/\r?\n/)) {
|
|
2001
|
+
const i = line.indexOf("=");
|
|
2002
|
+
if (i > 0) kv.set(line.slice(0, i).trim(), line.slice(i + 1).trim());
|
|
2003
|
+
}
|
|
2004
|
+
const dir2 = (kv.get("installDir") ?? "").replace(/\//g, "\\").replace(/\\+$/, "");
|
|
2005
|
+
const app = (kv.get("appExecutable") ?? "").replace(/\//g, "\\");
|
|
2006
|
+
const seg = app.split("\\");
|
|
2007
|
+
const shapeOk = seg.length === 1 || seg.length === 3 && seg[0].toLowerCase() === ".qoder-versions" && /^[0-9a-z][0-9a-z._-]*$/i.test(seg[1]);
|
|
2008
|
+
if (!shapeOk || seg[seg.length - 1].toLowerCase() !== exe.toLowerCase() || !isValidWinPath(dir2)) return null;
|
|
2009
|
+
const p = win32.join(dir2, app);
|
|
2010
|
+
return isValidWinPath(p) ? p : null;
|
|
2011
|
+
}
|
|
2012
|
+
|
|
1940
2013
|
// src/daemon/appShortcuts.ts
|
|
2014
|
+
function exeNameOf(app) {
|
|
2015
|
+
let p = null;
|
|
2016
|
+
try {
|
|
2017
|
+
p = app.locatedExe?.() ?? null;
|
|
2018
|
+
} catch {
|
|
2019
|
+
p = null;
|
|
2020
|
+
}
|
|
2021
|
+
return p ? win322.basename(p) : app.exe;
|
|
2022
|
+
}
|
|
1941
2023
|
var LNK_LIST_PS = String.raw`[Console]::OutputEncoding=[Text.Encoding]::UTF8
|
|
1942
2024
|
$s = New-Object -ComObject WScript.Shell
|
|
1943
2025
|
$dirs = @(@([Environment]::GetFolderPath('Programs'), $true), @([Environment]::GetFolderPath('CommonPrograms'), $true), @([Environment]::GetFolderPath('Desktop'), $false), @([Environment]::GetFolderPath('CommonDesktopDirectory'), $false), @((Join-Path $env:APPDATA 'Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar'), $false))
|
|
@@ -1951,13 +2033,16 @@ foreach ($d in $dirs) {
|
|
|
1951
2033
|
ConvertTo-Json -InputObject @($out) -Compress`;
|
|
1952
2034
|
var LNK_WRITE_PS = String.raw`[Console]::OutputEncoding=[Text.Encoding]::UTF8
|
|
1953
2035
|
$ops = $env:DB_LNK_OPS | ConvertFrom-Json
|
|
2036
|
+
# 允许的 Launcher 目标(完整路径,小写);PowerShell 5.1 的 ConvertFrom-Json 数组要展开
|
|
2037
|
+
$launchers = @($env:DB_LNK_LAUNCHERS | ConvertFrom-Json | ForEach-Object { $_ } | ForEach-Object { ([string]$_).ToLowerInvariant() })
|
|
1954
2038
|
$s = New-Object -ComObject WScript.Shell
|
|
1955
2039
|
$out = @()
|
|
1956
2040
|
foreach ($o in @($ops)) {
|
|
1957
2041
|
$ok = $false
|
|
1958
2042
|
try {
|
|
1959
2043
|
$l = $s.CreateShortcut([string]$o.path)
|
|
1960
|
-
|
|
2044
|
+
$t = [string]$l.TargetPath
|
|
2045
|
+
if (([IO.Path]::GetFileName($t) -ieq [string]$env:DB_LNK_EXE) -or ($launchers -contains $t.ToLowerInvariant())) { $l.Arguments = [string]$o.args; $l.Save(); $ok = $true }
|
|
1961
2046
|
} catch {}
|
|
1962
2047
|
$out += [pscustomobject]@{ path = [string]$o.path; ok = $ok }
|
|
1963
2048
|
}
|
|
@@ -1969,7 +2054,42 @@ function hasPortFlag(args) {
|
|
|
1969
2054
|
return /(^|\s)--remote-debugging-port(=|\s|$)/.test(args);
|
|
1970
2055
|
}
|
|
1971
2056
|
function isAppLnk(l, exe) {
|
|
1972
|
-
|
|
2057
|
+
if (typeof exe === "function") return exe(l);
|
|
2058
|
+
return win322.basename(l.target || "").toLowerCase() === exe.toLowerCase();
|
|
2059
|
+
}
|
|
2060
|
+
function lnkMatcher(app, readText = readSmall, restore = false) {
|
|
2061
|
+
const exe = exeNameOf(app);
|
|
2062
|
+
let located = null;
|
|
2063
|
+
try {
|
|
2064
|
+
located = app.locatedExe?.() ?? null;
|
|
2065
|
+
} catch {
|
|
2066
|
+
located = null;
|
|
2067
|
+
}
|
|
2068
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
2069
|
+
return (l) => {
|
|
2070
|
+
const target = l.target || "";
|
|
2071
|
+
if (win322.basename(target).toLowerCase() === exe.toLowerCase()) return true;
|
|
2072
|
+
if (!located && !restore || !isValidWinPath(target) || !/\.exe$/i.test(target)) return false;
|
|
2073
|
+
const k = target.toLowerCase();
|
|
2074
|
+
if (!cache2.has(k)) {
|
|
2075
|
+
const ini = readText(win322.join(win322.dirname(target), "install.ini"));
|
|
2076
|
+
const to = ini ? launcherTarget(ini, exe) : null;
|
|
2077
|
+
cache2.set(k, !!to && (restore || !!located && to.toLowerCase() === located.toLowerCase()));
|
|
2078
|
+
}
|
|
2079
|
+
return cache2.get(k) === true;
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
function launcherTargets(list, writes, exe) {
|
|
2083
|
+
const byPath = new Map(list.map((l) => [l.path.toLowerCase(), l.target]));
|
|
2084
|
+
const out = /* @__PURE__ */ new Set();
|
|
2085
|
+
for (const w of writes) {
|
|
2086
|
+
const t = byPath.get(w.path.toLowerCase());
|
|
2087
|
+
if (t && win322.basename(t).toLowerCase() !== exe.toLowerCase()) out.add(t.toLowerCase());
|
|
2088
|
+
}
|
|
2089
|
+
return [...out];
|
|
2090
|
+
}
|
|
2091
|
+
function readSmall(p) {
|
|
2092
|
+
return readSmallText(p);
|
|
1973
2093
|
}
|
|
1974
2094
|
var keyOf = (p) => p.toLowerCase();
|
|
1975
2095
|
function planPatchFor(exe, list, records, port) {
|
|
@@ -2001,7 +2121,7 @@ function recordsFile(app) {
|
|
|
2001
2121
|
}
|
|
2002
2122
|
function loadRecords(file3) {
|
|
2003
2123
|
try {
|
|
2004
|
-
const v = JSON.parse(
|
|
2124
|
+
const v = JSON.parse(readFileSync13(file3, "utf8"));
|
|
2005
2125
|
if (!v || typeof v !== "object") return {};
|
|
2006
2126
|
const out = {};
|
|
2007
2127
|
for (const [k, r] of Object.entries(v)) {
|
|
@@ -2030,9 +2150,9 @@ function parseJsonArray(out) {
|
|
|
2030
2150
|
}
|
|
2031
2151
|
}
|
|
2032
2152
|
function parseList(out) {
|
|
2033
|
-
const
|
|
2034
|
-
if (!
|
|
2035
|
-
return
|
|
2153
|
+
const arr2 = parseJsonArray(out);
|
|
2154
|
+
if (!arr2) return null;
|
|
2155
|
+
return arr2.filter((x) => !!x && typeof x.path === "string" && typeof x.target === "string").map((x) => ({ path: x.path, target: x.target, args: typeof x.args === "string" ? x.args : "" }));
|
|
2036
2156
|
}
|
|
2037
2157
|
var psArgs = (script) => ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script];
|
|
2038
2158
|
var defaultPs = (script, env) => new Promise((done) => {
|
|
@@ -2062,10 +2182,15 @@ var nowOf = (d) => d.now ? d.now() : Date.now();
|
|
|
2062
2182
|
var fileOf = (app, d) => d.file ?? recordsFile(app);
|
|
2063
2183
|
var failFileOf = (app, d) => d.failFile ?? join10(deskbuddyHome(), app.dir, `${app.stem}-fail.json`);
|
|
2064
2184
|
var lockFileOf = (app, d) => d.lockFile ?? join10(deskbuddyHome(), app.dir, `${app.stem}.lock`);
|
|
2065
|
-
var writeEnv = (app, ops) => ({
|
|
2185
|
+
var writeEnv = (app, ops, list) => ({
|
|
2186
|
+
DB_LNK_OPS: JSON.stringify(ops),
|
|
2187
|
+
DB_LNK_EXE: exeNameOf(app),
|
|
2188
|
+
DB_LNK_LAUNCHERS: JSON.stringify(launcherTargets(list, ops, exeNameOf(app)))
|
|
2189
|
+
});
|
|
2190
|
+
var matcherOf = (app, d, restore = false) => lnkMatcher(app, d.readText, restore);
|
|
2066
2191
|
function loadFails(app, d) {
|
|
2067
2192
|
try {
|
|
2068
|
-
const v = JSON.parse(
|
|
2193
|
+
const v = JSON.parse(readFileSync13(failFileOf(app, d), "utf8"));
|
|
2069
2194
|
const out = {};
|
|
2070
2195
|
for (const [k, t] of Object.entries(v ?? {})) if (typeof t === "number") out[k] = t;
|
|
2071
2196
|
return out;
|
|
@@ -2116,7 +2241,7 @@ function applyResults(app, d, writes, results, records, prev) {
|
|
|
2116
2241
|
return out;
|
|
2117
2242
|
}
|
|
2118
2243
|
function restoreWith(app, d, list, records, write3) {
|
|
2119
|
-
const writes = planRestoreFor(app
|
|
2244
|
+
const writes = planRestoreFor(matcherOf(app, d, true), list, records, portOf(app, d));
|
|
2120
2245
|
const finish = (results) => {
|
|
2121
2246
|
const ok = new Set((results ?? []).filter((r2) => r2 && r2.ok === true && typeof r2.path === "string").map((r2) => keyOf(r2.path)));
|
|
2122
2247
|
const keep = {};
|
|
@@ -2146,7 +2271,7 @@ async function ensureAppShortcuts(app, d, ps = defaultPs) {
|
|
|
2146
2271
|
const prev = loadRecords(fileOf(app, d));
|
|
2147
2272
|
const fails = loadFails(app, d);
|
|
2148
2273
|
const now = nowOf(d);
|
|
2149
|
-
const plan = planPatchFor(app
|
|
2274
|
+
const plan = planPatchFor(matcherOf(app, d), list, prev, portOf(app, d));
|
|
2150
2275
|
const writes = plan.writes.filter((w) => {
|
|
2151
2276
|
const t = fails[keyOf(w.path)];
|
|
2152
2277
|
return !t || now - t >= FAIL_RETRY_MS;
|
|
@@ -2158,11 +2283,11 @@ async function ensureAppShortcuts(app, d, ps = defaultPs) {
|
|
|
2158
2283
|
if (prev[k]) records[k] = prev[k];
|
|
2159
2284
|
else delete records[k];
|
|
2160
2285
|
}
|
|
2161
|
-
const results = parseJsonArray(await ps(LNK_WRITE_PS, writeEnv(app, writes)));
|
|
2286
|
+
const results = parseJsonArray(await ps(LNK_WRITE_PS, writeEnv(app, writes, list)));
|
|
2162
2287
|
let next = applyResults(app, d, writes, results, records, prev);
|
|
2163
2288
|
if (!wanted()) {
|
|
2164
2289
|
const list2 = parseList(await ps(LNK_LIST_PS));
|
|
2165
|
-
if (list2) next = await restoreWith(app, d, list2, next, async (ops) => parseJsonArray(await ps(LNK_WRITE_PS, writeEnv(app, ops))));
|
|
2290
|
+
if (list2) next = await restoreWith(app, d, list2, next, async (ops) => parseJsonArray(await ps(LNK_WRITE_PS, writeEnv(app, ops, list2))));
|
|
2166
2291
|
}
|
|
2167
2292
|
saveRecords(next, fileOf(app, d));
|
|
2168
2293
|
} finally {
|
|
@@ -2190,9 +2315,9 @@ function ensureAppShortcutsSync(app, d, ps = defaultPsSync) {
|
|
|
2190
2315
|
return;
|
|
2191
2316
|
}
|
|
2192
2317
|
const prev = loadRecords(fileOf(app, d));
|
|
2193
|
-
const { writes, records } = planPatchFor(app
|
|
2318
|
+
const { writes, records } = planPatchFor(matcherOf(app, d), list, prev, portOf(app, d));
|
|
2194
2319
|
if (writes.length === 0) return;
|
|
2195
|
-
const results = parseJsonArray(ps(LNK_WRITE_PS, writeEnv(app, writes)));
|
|
2320
|
+
const results = parseJsonArray(ps(LNK_WRITE_PS, writeEnv(app, writes, list)));
|
|
2196
2321
|
saveRecords(applyResults(app, d, writes, results, records, prev), fileOf(app, d));
|
|
2197
2322
|
} finally {
|
|
2198
2323
|
release2();
|
|
@@ -2213,7 +2338,7 @@ function restoreAppShortcutsSync(app, d, ps = defaultPsSync) {
|
|
|
2213
2338
|
d.log(`\u8BFB\u4E0D\u4E86\u5FEB\u6377\u65B9\u5F0F\uFF0C${app.label}\u5FEB\u6377\u65B9\u5F0F\u6CA1\u8FD8\u539F\uFF08\u8BB0\u5F55\u4FDD\u7559\uFF0C\u4E0B\u6B21 disable \u518D\u8BD5\uFF09`);
|
|
2214
2339
|
return;
|
|
2215
2340
|
}
|
|
2216
|
-
const keep = restoreWith(app, d, list, loadRecords(fileOf(app, d)), (ops) => parseJsonArray(ps(LNK_WRITE_PS, writeEnv(app, ops))));
|
|
2341
|
+
const keep = restoreWith(app, d, list, loadRecords(fileOf(app, d)), (ops) => parseJsonArray(ps(LNK_WRITE_PS, writeEnv(app, ops, list))));
|
|
2217
2342
|
saveRecords(keep, fileOf(app, d));
|
|
2218
2343
|
} finally {
|
|
2219
2344
|
release2();
|
|
@@ -2221,14 +2346,14 @@ function restoreAppShortcutsSync(app, d, ps = defaultPsSync) {
|
|
|
2221
2346
|
}
|
|
2222
2347
|
|
|
2223
2348
|
// src/daemon/dumateApp/keeper.ts
|
|
2224
|
-
import { readdirSync as
|
|
2225
|
-
import { join as
|
|
2349
|
+
import { readdirSync as readdirSync13, statSync as statSync11 } from "fs";
|
|
2350
|
+
import { join as join25 } from "path";
|
|
2226
2351
|
|
|
2227
2352
|
// src/daemon/dumateWatch.ts
|
|
2228
2353
|
import { execFileSync as execFileSync10, spawn as spawn3 } from "child_process";
|
|
2229
|
-
import { readdirSync as
|
|
2354
|
+
import { readdirSync as readdirSync11, realpathSync as realpathSync4, statSync as statSync10 } from "fs";
|
|
2230
2355
|
import { request as httpRequest } from "http";
|
|
2231
|
-
import { join as
|
|
2356
|
+
import { join as join23 } from "path";
|
|
2232
2357
|
|
|
2233
2358
|
// hook/ocTranslate.mjs
|
|
2234
2359
|
function toCcTool(tool, args) {
|
|
@@ -2943,8 +3068,8 @@ function traeNoticeSummary(toolName, toolInput, described, notice) {
|
|
|
2943
3068
|
|
|
2944
3069
|
// src/daemon/workbuddyApp/keeper.ts
|
|
2945
3070
|
import { execFile as execFile6, spawn as spawn2 } from "child_process";
|
|
2946
|
-
import { existsSync as
|
|
2947
|
-
import { delimiter as delimiter3, join as
|
|
3071
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync21, readdirSync as readdirSync10, readFileSync as readFileSync24, renameSync as renameSync15, writeFileSync as writeFileSync21 } from "fs";
|
|
3072
|
+
import { delimiter as delimiter3, join as join22, resolve as resolve4 } from "path";
|
|
2948
3073
|
|
|
2949
3074
|
// src/core/process.ts
|
|
2950
3075
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
@@ -3006,7 +3131,7 @@ function isAlive(pid, expectName, kill = process.kill.bind(process)) {
|
|
|
3006
3131
|
}
|
|
3007
3132
|
|
|
3008
3133
|
// src/core/state.ts
|
|
3009
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync15, readdirSync as readdirSync5, readFileSync as
|
|
3134
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync15, readdirSync as readdirSync5, readFileSync as readFileSync16, renameSync as renameSync10, rmSync as rmSync8, writeFileSync as writeFileSync15 } from "fs";
|
|
3010
3135
|
import { join as join14 } from "path";
|
|
3011
3136
|
|
|
3012
3137
|
// src/core/codexServer.ts
|
|
@@ -3057,7 +3182,7 @@ function programOf(args) {
|
|
|
3057
3182
|
}
|
|
3058
3183
|
|
|
3059
3184
|
// src/core/ended.ts
|
|
3060
|
-
import { closeSync, mkdirSync as mkdirSync12, openSync, readdirSync as readdirSync2, readFileSync as
|
|
3185
|
+
import { closeSync, mkdirSync as mkdirSync12, openSync, readdirSync as readdirSync2, readFileSync as readFileSync14, readSync, realpathSync, renameSync as renameSync9, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync12 } from "fs";
|
|
3061
3186
|
import { join as join11 } from "path";
|
|
3062
3187
|
var ENDED_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3063
3188
|
var SID_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
@@ -3079,7 +3204,7 @@ function writeEnded(s, now = Date.now()) {
|
|
|
3079
3204
|
for (const n of readdirSync2(dir2)) {
|
|
3080
3205
|
const f = join11(dir2, n);
|
|
3081
3206
|
try {
|
|
3082
|
-
if (now -
|
|
3207
|
+
if (now - statSync5(f).mtimeMs > ENDED_TTL_MS) rmSync5(f, { force: true });
|
|
3083
3208
|
} catch {
|
|
3084
3209
|
}
|
|
3085
3210
|
}
|
|
@@ -3089,7 +3214,7 @@ function writeEnded(s, now = Date.now()) {
|
|
|
3089
3214
|
function readEnded(sessionId, now = Date.now()) {
|
|
3090
3215
|
if (!SID_RE.test(sessionId)) return null;
|
|
3091
3216
|
try {
|
|
3092
|
-
const r = JSON.parse(
|
|
3217
|
+
const r = JSON.parse(readFileSync14(join11(endedDir(), `${sessionId}.json`), "utf8"));
|
|
3093
3218
|
if (!r || r.sessionId !== sessionId || typeof r.endedAt !== "number" || now - r.endedAt > ENDED_TTL_MS) return null;
|
|
3094
3219
|
return r;
|
|
3095
3220
|
} catch {
|
|
@@ -3104,7 +3229,7 @@ function projectDirName(cwd) {
|
|
|
3104
3229
|
function readChunk(file3, fromEnd, max) {
|
|
3105
3230
|
let fd;
|
|
3106
3231
|
try {
|
|
3107
|
-
const size =
|
|
3232
|
+
const size = statSync5(file3).size;
|
|
3108
3233
|
const len = Math.min(size, max);
|
|
3109
3234
|
const buf = Buffer.alloc(len);
|
|
3110
3235
|
fd = openSync(file3, "r");
|
|
@@ -3137,7 +3262,7 @@ function claudeTranscriptCwd(sessionId, home = claudeHome()) {
|
|
|
3137
3262
|
for (const d of dirs) {
|
|
3138
3263
|
const file3 = join11(home, "projects", d, `${sessionId}.jsonl`);
|
|
3139
3264
|
try {
|
|
3140
|
-
|
|
3265
|
+
statSync5(file3);
|
|
3141
3266
|
} catch {
|
|
3142
3267
|
continue;
|
|
3143
3268
|
}
|
|
@@ -3173,14 +3298,14 @@ function resolveEndedCwd(sessionId, tool, d) {
|
|
|
3173
3298
|
}
|
|
3174
3299
|
function isDirSync(p) {
|
|
3175
3300
|
try {
|
|
3176
|
-
return
|
|
3301
|
+
return statSync5(p).isDirectory();
|
|
3177
3302
|
} catch {
|
|
3178
3303
|
return false;
|
|
3179
3304
|
}
|
|
3180
3305
|
}
|
|
3181
3306
|
|
|
3182
3307
|
// src/core/qoderAppSessions.ts
|
|
3183
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync13, readdirSync as readdirSync3, readFileSync as
|
|
3308
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync13, readdirSync as readdirSync3, readFileSync as readFileSync15, rmSync as rmSync6, writeFileSync as writeFileSync13 } from "fs";
|
|
3184
3309
|
import { join as join12 } from "path";
|
|
3185
3310
|
var QODER_APP_SESSION_TOOLS = ["qoder_cn", "qoder"];
|
|
3186
3311
|
var KEEPER_FILE = "keeper";
|
|
@@ -3224,7 +3349,7 @@ function listQoderAppSessions(tool) {
|
|
|
3224
3349
|
for (const f of names) {
|
|
3225
3350
|
let t = "";
|
|
3226
3351
|
try {
|
|
3227
|
-
t = String(JSON.parse(
|
|
3352
|
+
t = String(JSON.parse(readFileSync15(join12(dir(), f), "utf8")).tool ?? "");
|
|
3228
3353
|
} catch {
|
|
3229
3354
|
t = "";
|
|
3230
3355
|
}
|
|
@@ -3238,7 +3363,7 @@ function qoderAppMarkedAt(sessionId) {
|
|
|
3238
3363
|
const f = file(sessionId);
|
|
3239
3364
|
if (!f) return 0;
|
|
3240
3365
|
try {
|
|
3241
|
-
const at = JSON.parse(
|
|
3366
|
+
const at = JSON.parse(readFileSync15(f, "utf8")).at;
|
|
3242
3367
|
return typeof at === "number" ? at : 0;
|
|
3243
3368
|
} catch {
|
|
3244
3369
|
return 0;
|
|
@@ -3311,7 +3436,7 @@ function statePath(sessionId) {
|
|
|
3311
3436
|
}
|
|
3312
3437
|
function readState(sessionId) {
|
|
3313
3438
|
try {
|
|
3314
|
-
return JSON.parse(
|
|
3439
|
+
return JSON.parse(readFileSync16(statePath(sessionId), "utf8"));
|
|
3315
3440
|
} catch {
|
|
3316
3441
|
return null;
|
|
3317
3442
|
}
|
|
@@ -3376,7 +3501,7 @@ var STALE_STATES = /* @__PURE__ */ new Set(["watch", "ask_approval"]);
|
|
|
3376
3501
|
var STALE_NO_PID_LOG = "\u65E0\u8FDB\u7A0B\u53F7\u4E14 2 \u5C0F\u65F6\u65E0\u4E8B\u4EF6\uFF0C\u6309\u5DF2\u7ED3\u675F\u5904\u7406";
|
|
3377
3502
|
function pendingApprovalSessions() {
|
|
3378
3503
|
try {
|
|
3379
|
-
const recs = JSON.parse(
|
|
3504
|
+
const recs = JSON.parse(readFileSync16(join14(deskbuddyHome(), "approvals.json"), "utf8"));
|
|
3380
3505
|
return new Set(Array.isArray(recs) ? recs.filter((r) => r && r.status === "pending" && typeof r.sessionId === "string").map((r) => r.sessionId) : []);
|
|
3381
3506
|
} catch {
|
|
3382
3507
|
return /* @__PURE__ */ new Set();
|
|
@@ -3403,7 +3528,7 @@ function readAliveStates(tool, opts = {}) {
|
|
|
3403
3528
|
if (!file3.endsWith(".json")) continue;
|
|
3404
3529
|
let s;
|
|
3405
3530
|
try {
|
|
3406
|
-
s = JSON.parse(
|
|
3531
|
+
s = JSON.parse(readFileSync16(join14(stateDir(), file3), "utf8"));
|
|
3407
3532
|
} catch {
|
|
3408
3533
|
rmSync8(join14(stateDir(), file3), { force: true });
|
|
3409
3534
|
continue;
|
|
@@ -3455,7 +3580,7 @@ var ALIAS_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
3455
3580
|
function resolveAlias(sessionId, now = Date.now()) {
|
|
3456
3581
|
let map = {};
|
|
3457
3582
|
try {
|
|
3458
|
-
map = JSON.parse(
|
|
3583
|
+
map = JSON.parse(readFileSync16(join14(deskbuddyHome(), "alias.json"), "utf8")) || {};
|
|
3459
3584
|
} catch {
|
|
3460
3585
|
return sessionId;
|
|
3461
3586
|
}
|
|
@@ -3470,7 +3595,7 @@ function resolveAlias(sessionId, now = Date.now()) {
|
|
|
3470
3595
|
|
|
3471
3596
|
// src/wrap/shim.ts
|
|
3472
3597
|
import { execFile as execFile3, execFileSync as execFileSync7 } from "child_process";
|
|
3473
|
-
import { chmodSync as chmodSync3, existsSync as existsSync14, mkdirSync as mkdirSync16, readdirSync as readdirSync6, readFileSync as
|
|
3598
|
+
import { chmodSync as chmodSync3, existsSync as existsSync14, mkdirSync as mkdirSync16, readdirSync as readdirSync6, readFileSync as readFileSync17, rmdirSync, rmSync as rmSync9, writeFileSync as writeFileSync16 } from "fs";
|
|
3474
3599
|
import { homedir as homedir3 } from "os";
|
|
3475
3600
|
import { delimiter, join as join15, resolve as resolve3 } from "path";
|
|
3476
3601
|
var SHIM_FALLBACK_EXIT = 213;
|
|
@@ -3626,7 +3751,7 @@ function removeRcBlock(content) {
|
|
|
3626
3751
|
return content.slice(0, start) + content.slice(end);
|
|
3627
3752
|
}
|
|
3628
3753
|
function readRc(p) {
|
|
3629
|
-
return
|
|
3754
|
+
return readFileSync17(p).toString("latin1");
|
|
3630
3755
|
}
|
|
3631
3756
|
function writeRc(p, latin1) {
|
|
3632
3757
|
writeFileSync16(p, Buffer.from(latin1, "latin1"));
|
|
@@ -3881,7 +4006,7 @@ function ourShims(binDir) {
|
|
|
3881
4006
|
}
|
|
3882
4007
|
return names.filter((n) => {
|
|
3883
4008
|
try {
|
|
3884
|
-
return
|
|
4009
|
+
return readFileSync17(join15(binDir, n), "utf8").includes(SHIM_MARKER);
|
|
3885
4010
|
} catch {
|
|
3886
4011
|
return false;
|
|
3887
4012
|
}
|
|
@@ -3893,7 +4018,7 @@ function uninstallShim(tool, e) {
|
|
|
3893
4018
|
let removed = false;
|
|
3894
4019
|
const file3 = shimFile(e.binDir, command, e.platform);
|
|
3895
4020
|
try {
|
|
3896
|
-
if (existsSync14(file3) &&
|
|
4021
|
+
if (existsSync14(file3) && readFileSync17(file3, "utf8").includes(SHIM_MARKER)) {
|
|
3897
4022
|
rmSync9(file3, { force: true });
|
|
3898
4023
|
removed = true;
|
|
3899
4024
|
}
|
|
@@ -3937,7 +4062,7 @@ function shimState(tool, binDir, pathEnv, platform = process.platform) {
|
|
|
3937
4062
|
const file3 = shimFile(binDir, command, platform);
|
|
3938
4063
|
let installed = false;
|
|
3939
4064
|
try {
|
|
3940
|
-
installed =
|
|
4065
|
+
installed = readFileSync17(file3, "utf8").includes(SHIM_MARKER);
|
|
3941
4066
|
} catch {
|
|
3942
4067
|
installed = false;
|
|
3943
4068
|
}
|
|
@@ -3949,18 +4074,18 @@ function shimState(tool, binDir, pathEnv, platform = process.platform) {
|
|
|
3949
4074
|
|
|
3950
4075
|
// src/daemon/workbuddyAppDb.ts
|
|
3951
4076
|
import { execFile as execFile5 } from "child_process";
|
|
3952
|
-
import { closeSync as closeSync5, existsSync as
|
|
4077
|
+
import { closeSync as closeSync5, existsSync as existsSync18, mkdirSync as mkdirSync20, openSync as openSync5, readdirSync as readdirSync9, readFileSync as readFileSync23, readSync as readSync5, renameSync as renameSync14, rmSync as rmSync12, statSync as statSync9, writeFileSync as writeFileSync20 } from "fs";
|
|
3953
4078
|
import { createRequire as createRequire3 } from "module";
|
|
3954
|
-
import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as
|
|
4079
|
+
import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as join21 } from "path";
|
|
3955
4080
|
|
|
3956
4081
|
// src/core/launch.ts
|
|
3957
|
-
import { mkdirSync as mkdirSync18, readFileSync as
|
|
4082
|
+
import { mkdirSync as mkdirSync18, readFileSync as readFileSync19, renameSync as renameSync12, rmSync as rmSync11, writeFileSync as writeFileSync18 } from "fs";
|
|
3958
4083
|
import { join as join17 } from "path";
|
|
3959
4084
|
|
|
3960
4085
|
// src/core/wake.ts
|
|
3961
4086
|
import { execFileSync as execFileSync8 } from "child_process";
|
|
3962
4087
|
import { createHash as createHash2, createHmac, timingSafeEqual } from "crypto";
|
|
3963
|
-
import { chmodSync as chmodSync4, closeSync as closeSync2, mkdirSync as mkdirSync17, openSync as openSync2, readdirSync as readdirSync7, readFileSync as
|
|
4088
|
+
import { chmodSync as chmodSync4, closeSync as closeSync2, mkdirSync as mkdirSync17, openSync as openSync2, readdirSync as readdirSync7, readFileSync as readFileSync18, readSync as readSync2, renameSync as renameSync11, rmSync as rmSync10, statSync as statSync6, writeFileSync as writeFileSync17 } from "fs";
|
|
3964
4089
|
import { tmpdir } from "os";
|
|
3965
4090
|
import { join as join16 } from "path";
|
|
3966
4091
|
function wakeDir(home = deskbuddyHome()) {
|
|
@@ -4029,7 +4154,7 @@ function isWakeMarked(sessionId, alive = pidExists) {
|
|
|
4029
4154
|
const safe = safeSid(sessionId);
|
|
4030
4155
|
if (!safe) return false;
|
|
4031
4156
|
try {
|
|
4032
|
-
const m = JSON.parse(
|
|
4157
|
+
const m = JSON.parse(readFileSync18(join16(marksDir(), `${safe}.json`), "utf8"));
|
|
4033
4158
|
const daemonPid = Number(m?.daemonPid) || 0;
|
|
4034
4159
|
if (!daemonPid || !alive(daemonPid)) return false;
|
|
4035
4160
|
const pid = Number(m?.agentPid) || 0;
|
|
@@ -4099,16 +4224,17 @@ function macEqual(a, b) {
|
|
|
4099
4224
|
var WAKE_MARK_RE = /\[DeskBuddy#([0-9A-Za-z_-]{1,64})(?::[0-9a-f]{32})?\]/;
|
|
4100
4225
|
var INTERRUPT_RE = /^\[Request interrupted by user/;
|
|
4101
4226
|
var TRANSCRIPT_TAIL2 = 256 * 1024;
|
|
4227
|
+
var TEXT_PART_TYPES = /* @__PURE__ */ new Set(["text", "input_text", "output_text"]);
|
|
4102
4228
|
function userText(content) {
|
|
4103
4229
|
if (typeof content === "string") return content;
|
|
4104
4230
|
if (!Array.isArray(content)) return "";
|
|
4105
|
-
return content.filter((x) => x && x.type
|
|
4231
|
+
return content.filter((x) => x && TEXT_PART_TYPES.has(x.type)).map((x) => String(x.text ?? "")).join("\n");
|
|
4106
4232
|
}
|
|
4107
4233
|
function analyzeTranscript(file3) {
|
|
4108
4234
|
if (!file3) return null;
|
|
4109
4235
|
let fd;
|
|
4110
4236
|
try {
|
|
4111
|
-
const size =
|
|
4237
|
+
const size = statSync6(file3).size;
|
|
4112
4238
|
const len = Math.min(size, TRANSCRIPT_TAIL2);
|
|
4113
4239
|
const buf = Buffer.alloc(len);
|
|
4114
4240
|
fd = openSync2(file3, "r");
|
|
@@ -4127,7 +4253,9 @@ function analyzeTranscript(file3) {
|
|
|
4127
4253
|
} catch {
|
|
4128
4254
|
continue;
|
|
4129
4255
|
}
|
|
4130
|
-
if (!o
|
|
4256
|
+
if (!o) continue;
|
|
4257
|
+
if (o.type === "message" && (o.role === "user" || o.role === "assistant")) o = { type: o.role, message: { content: o.content } };
|
|
4258
|
+
if (!o.message) continue;
|
|
4131
4259
|
if (o.type === "assistant") {
|
|
4132
4260
|
const t = userText(o.message.content).trim();
|
|
4133
4261
|
if (t) {
|
|
@@ -4193,7 +4321,7 @@ function claimLaunch(runId) {
|
|
|
4193
4321
|
return null;
|
|
4194
4322
|
}
|
|
4195
4323
|
try {
|
|
4196
|
-
const p = JSON.parse(
|
|
4324
|
+
const p = JSON.parse(readFileSync19(claimed, "utf8"));
|
|
4197
4325
|
if (!p || typeof p.cwd !== "string" || typeof p.resume !== "string") return null;
|
|
4198
4326
|
if (p.tool === void 0) p.tool = "claude_code";
|
|
4199
4327
|
return validPlan(p) ? p : null;
|
|
@@ -4208,7 +4336,7 @@ function claimLaunch(runId) {
|
|
|
4208
4336
|
}
|
|
4209
4337
|
function launchPending(runId) {
|
|
4210
4338
|
try {
|
|
4211
|
-
|
|
4339
|
+
readFileSync19(planFile(runId));
|
|
4212
4340
|
return true;
|
|
4213
4341
|
} catch {
|
|
4214
4342
|
return false;
|
|
@@ -4216,16 +4344,16 @@ function launchPending(runId) {
|
|
|
4216
4344
|
}
|
|
4217
4345
|
|
|
4218
4346
|
// src/core/title.ts
|
|
4219
|
-
import { closeSync as closeSync3, existsSync as existsSync15, openSync as openSync3, readFileSync as
|
|
4347
|
+
import { closeSync as closeSync3, existsSync as existsSync15, openSync as openSync3, readFileSync as readFileSync20, readSync as readSync3, statSync as statSync7 } from "fs";
|
|
4220
4348
|
import { createRequire as createRequire2 } from "module";
|
|
4221
4349
|
import { homedir as homedir5 } from "os";
|
|
4222
4350
|
import { basename as basename3, dirname as dirname10, isAbsolute, join as join18 } from "path";
|
|
4223
4351
|
|
|
4224
4352
|
// src/core/dirFilter.ts
|
|
4225
4353
|
import { homedir as homedir4, tmpdir as tmpdir2 } from "os";
|
|
4226
|
-
import { posix, win32 as
|
|
4354
|
+
import { posix, win32 as win323 } from "path";
|
|
4227
4355
|
function norm(p, win) {
|
|
4228
|
-
if (win) return
|
|
4356
|
+
if (win) return win323.resolve(p).replace(/[\\/]+$/, "").toLowerCase();
|
|
4229
4357
|
return posix.resolve(p).replace(/\/+$/, "");
|
|
4230
4358
|
}
|
|
4231
4359
|
function isUnder(p, base, win) {
|
|
@@ -4234,7 +4362,7 @@ function isUnder(p, base, win) {
|
|
|
4234
4362
|
function isAutoWorkspace(p, opts = {}) {
|
|
4235
4363
|
if (!p) return false;
|
|
4236
4364
|
const win = (opts.platform ?? process.platform) === "win32";
|
|
4237
|
-
const P = win ?
|
|
4365
|
+
const P = win ? win323 : posix;
|
|
4238
4366
|
const home = norm(opts.home ?? homedir4(), win);
|
|
4239
4367
|
const full = norm(p, win);
|
|
4240
4368
|
const name = P.basename(full);
|
|
@@ -4245,7 +4373,7 @@ function isAutoWorkspace(p, opts = {}) {
|
|
|
4245
4373
|
function isReportableDir(p, opts = {}) {
|
|
4246
4374
|
if (!p) return false;
|
|
4247
4375
|
const win = (opts.platform ?? process.platform) === "win32";
|
|
4248
|
-
const P = win ?
|
|
4376
|
+
const P = win ? win323 : posix;
|
|
4249
4377
|
const full = norm(p, win);
|
|
4250
4378
|
if (full === norm(P.parse(P.resolve(p)).root, win)) return false;
|
|
4251
4379
|
if (full === norm(opts.home ?? homedir4(), win)) return false;
|
|
@@ -4278,9 +4406,9 @@ function qoderDbPaths(tool, platform = process.platform, home = homedir5(), env
|
|
|
4278
4406
|
|
|
4279
4407
|
// src/daemon/locate.ts
|
|
4280
4408
|
import { execFile as execFile4, execFileSync as execFileSync9 } from "child_process";
|
|
4281
|
-
import { accessSync, closeSync as closeSync4, constants, existsSync as
|
|
4409
|
+
import { accessSync, closeSync as closeSync4, constants, existsSync as existsSync17, lstatSync, openSync as openSync4, readFileSync as readFileSync22, readSync as readSync4, realpathSync as realpathSync2, statSync as statSync8 } from "fs";
|
|
4282
4410
|
import { homedir as homedir6 } from "os";
|
|
4283
|
-
import { delimiter as delimiter2, dirname as dirname11, join as
|
|
4411
|
+
import { delimiter as delimiter2, dirname as dirname11, join as join20, win32 as win325 } from "path";
|
|
4284
4412
|
|
|
4285
4413
|
// src/core/agentEnv.ts
|
|
4286
4414
|
var BUN_HIJACK_ENV = ["BUN_BE_BUN", "BUN_OPTIONS"];
|
|
@@ -4297,6 +4425,280 @@ function scrubForCommand(cmd, env = process.env) {
|
|
|
4297
4425
|
return true;
|
|
4298
4426
|
}
|
|
4299
4427
|
|
|
4428
|
+
// src/daemon/winAppLocator.ts
|
|
4429
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync19, readdirSync as readdirSync8, readFileSync as readFileSync21, renameSync as renameSync13, writeFileSync as writeFileSync19 } from "fs";
|
|
4430
|
+
import { join as join19, win32 as win324 } from "path";
|
|
4431
|
+
var SOURCE_TEXT = {
|
|
4432
|
+
process: "\u6B63\u5728\u8FD0\u884C\u7684\u8FDB\u7A0B",
|
|
4433
|
+
remembered: "\u4E4B\u524D\u8BB0\u4E0B\u7684\u4F4D\u7F6E",
|
|
4434
|
+
uninstall: "\u6CE8\u518C\u8868\u5378\u8F7D\u9879",
|
|
4435
|
+
shortcut: "\u5FEB\u6377\u65B9\u5F0F",
|
|
4436
|
+
appPaths: "App Paths",
|
|
4437
|
+
protocol: "URL \u534F\u8BAE",
|
|
4438
|
+
default: "\u9ED8\u8BA4\u76EE\u5F55",
|
|
4439
|
+
path: "PATH"
|
|
4440
|
+
};
|
|
4441
|
+
var QODER_CN_WORKER_REL = "resources/app.asar.unpacked/node_modules/@qoder-ai/qoder-cn-agent-sdk/dist/_worker/qoder-worker-runtime.obf.mjs";
|
|
4442
|
+
var QODER_WORKER_REL = "resources/app.asar.unpacked/node_modules/@qoder-ai/qoder-agent-sdk/dist/_worker/qoder-worker-runtime.obf.mjs";
|
|
4443
|
+
function stdDirs(env, ...names) {
|
|
4444
|
+
const out = [];
|
|
4445
|
+
for (const n of names) {
|
|
4446
|
+
if (env.localAppData) out.push(win324.join(env.localAppData, "Programs", n));
|
|
4447
|
+
out.push(win324.join(env.programFiles || "C:\\Program Files", n));
|
|
4448
|
+
out.push(win324.join(env.programFilesX86 || "C:\\Program Files (x86)", n));
|
|
4449
|
+
}
|
|
4450
|
+
return out;
|
|
4451
|
+
}
|
|
4452
|
+
var WIN_APP_SPECS = {
|
|
4453
|
+
qoder_cn: { id: "qoder_cn", label: "Qoder CN", exe: "Qoder CN.exe", names: /^Qoder CN(\s|$)/i, protocols: ["qoder-cn"], required: [QODER_CN_WORKER_REL], defaultDirs: (e) => stdDirs(e, "Qoder CN") },
|
|
4454
|
+
qoder: { id: "qoder", label: "Qoder", exe: "Qoder.exe", names: /^Qoder(?! CN)(\s|$)/i, protocols: ["qoder"], required: [QODER_WORKER_REL], defaultDirs: (e) => stdDirs(e, "Qoder") },
|
|
4455
|
+
// 与原 locateWorkbuddyAppExe 一致:DisplayName 含 WorkBuddy、只要 WorkBuddy.exe 在
|
|
4456
|
+
workbuddy: { id: "workbuddy", label: "WorkBuddy", exe: "WorkBuddy.exe", names: /WorkBuddy/i, protocols: ["workbuddy"], required: [], defaultDirs: (e) => stdDirs(e, "WorkBuddy") },
|
|
4457
|
+
qwenwork: { id: "qwenwork", label: "\u5343\u95EE\u529E\u516C", exe: "QwenWorkCN.exe", names: /千问办公|QwenWork/i, protocols: ["qwenwork-cn"], required: [], defaultDirs: (e) => stdDirs(e, "QwenWorkCN") },
|
|
4458
|
+
dumate: { id: "dumate", label: "\u767E\u5EA6\u642D\u5B50", exe: "DuMate.exe", names: /^DuMate(\s|$)/i, protocols: ["dumate"], required: [], defaultDirs: (e) => stdDirs(e, "DuMate", "qianfan-desktop-app") }
|
|
4459
|
+
};
|
|
4460
|
+
var defaultLocFs = {
|
|
4461
|
+
exists: (p) => existsSync16(p),
|
|
4462
|
+
readText: (p) => readSmallText(p),
|
|
4463
|
+
subdirs: (p) => {
|
|
4464
|
+
try {
|
|
4465
|
+
return readdirSync8(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
4466
|
+
} catch {
|
|
4467
|
+
return [];
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4470
|
+
};
|
|
4471
|
+
function validExe(p, spec, fs) {
|
|
4472
|
+
if (!isValidWinPath(p) || win324.basename(p).toLowerCase() !== spec.exe.toLowerCase() || !fs.exists(p)) return false;
|
|
4473
|
+
const dir2 = win324.dirname(p);
|
|
4474
|
+
return spec.required.every((r) => fs.exists(win324.join(dir2, ...r.split("/"))));
|
|
4475
|
+
}
|
|
4476
|
+
function expandHint(hint, spec, fs, deep = true) {
|
|
4477
|
+
const out = [];
|
|
4478
|
+
const add = (p) => {
|
|
4479
|
+
if (p && isValidWinPath(p) && !out.some((x) => x.toLowerCase() === p.toLowerCase())) out.push(p);
|
|
4480
|
+
};
|
|
4481
|
+
const base = win324.basename(hint);
|
|
4482
|
+
if (base.toLowerCase() === spec.exe.toLowerCase()) add(hint);
|
|
4483
|
+
const dir2 = /\.[a-z0-9]{2,4}$/i.test(base) ? win324.dirname(hint) : hint;
|
|
4484
|
+
add(win324.join(dir2, spec.exe));
|
|
4485
|
+
const ini = fs.readText(win324.join(dir2, "install.ini"));
|
|
4486
|
+
if (ini) add(launcherTarget(ini, spec.exe));
|
|
4487
|
+
if (deep) for (const sub of fs.subdirs(dir2).filter((s) => /^[0-9a-z][0-9a-z._-]*$/i.test(s)).sort().reverse().slice(0, 20)) add(win324.join(dir2, sub, spec.exe));
|
|
4488
|
+
return out;
|
|
4489
|
+
}
|
|
4490
|
+
var WIN_LOCATE_PS = String.raw`[Console]::OutputEncoding=[Text.Encoding]::UTF8
|
|
4491
|
+
# PowerShell 5.1 的 ConvertFrom-Json 把数组当一个对象输出,经 ForEach-Object 展开成元素(SF-Test 实测,否则 -contains 全不中)
|
|
4492
|
+
$exes = @($env:DB_LOC_EXES | ConvertFrom-Json | ForEach-Object { $_ } | ForEach-Object { [string]$_ }); $protos = @($env:DB_LOC_PROTOS | ConvertFrom-Json | ForEach-Object { $_ } | ForEach-Object { [string]$_ })
|
|
4493
|
+
$o = [ordered]@{ procs = @(); uninstall = @(); appPaths = @(); protocols = @() }
|
|
4494
|
+
try { $o.procs = @(Get-CimInstance Win32_Process -ErrorAction Stop | Where-Object { $exes -contains $_.Name } | ForEach-Object { [pscustomobject]@{ name = [string]$_.Name; path = [string]$_.ExecutablePath } }) } catch {}
|
|
4495
|
+
$o.uninstall = @(Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName } | ForEach-Object { [pscustomobject]@{ name = [string]$_.DisplayName; il = [string]$_.InstallLocation; icon = [string]$_.DisplayIcon; un = [string]$_.UninstallString } })
|
|
4496
|
+
foreach ($e in $exes) { foreach ($h in @('HKCU:','HKLM:')) { $p = Get-ItemProperty -LiteralPath "$h\Software\Microsoft\Windows\CurrentVersion\App Paths\$e" -ErrorAction SilentlyContinue; if ($p) { $o.appPaths += [pscustomobject]@{ exe = $e; def = [string]$p.'(default)'; path = [string]$p.Path } } } }
|
|
4497
|
+
foreach ($n in $protos) { foreach ($h in @('HKCU:\Software\Classes','HKLM:\Software\Classes')) { $c = (Get-ItemProperty -LiteralPath "$h\$n\shell\open\command" -ErrorAction SilentlyContinue).'(default)'; if ($c) { $o.protocols += [pscustomobject]@{ name = $n; cmd = [string]$c } } } }
|
|
4498
|
+
ConvertTo-Json -InputObject $o -Compress -Depth 4`;
|
|
4499
|
+
var str = (v) => typeof v === "string" ? v : "";
|
|
4500
|
+
var arr = (v) => (Array.isArray(v) ? v : v && typeof v === "object" ? [v] : []).filter((x) => x && typeof x === "object");
|
|
4501
|
+
function parseFacts(out) {
|
|
4502
|
+
if (out === null) return null;
|
|
4503
|
+
try {
|
|
4504
|
+
const v = JSON.parse(out.trim());
|
|
4505
|
+
if (!v || typeof v !== "object") return null;
|
|
4506
|
+
return {
|
|
4507
|
+
procs: arr(v.procs).map((x) => ({ name: str(x.name), path: str(x.path) })),
|
|
4508
|
+
uninstall: arr(v.uninstall).map((x) => ({ name: str(x.name), il: str(x.il), icon: str(x.icon), un: str(x.un) })),
|
|
4509
|
+
appPaths: arr(v.appPaths).map((x) => ({ exe: str(x.exe), def: str(x.def), path: str(x.path) })),
|
|
4510
|
+
protocols: arr(v.protocols).map((x) => ({ name: str(x.name), cmd: str(x.cmd) }))
|
|
4511
|
+
};
|
|
4512
|
+
} catch {
|
|
4513
|
+
return null;
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
function parseLnks(out) {
|
|
4517
|
+
if (out === null) return null;
|
|
4518
|
+
try {
|
|
4519
|
+
const v = JSON.parse(out.trim() || "[]");
|
|
4520
|
+
return arr(v).map((x) => ({ path: str(x.path), target: str(x.target) })).filter((x) => x.path && x.target);
|
|
4521
|
+
} catch {
|
|
4522
|
+
return null;
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
function locateFromFacts(spec, facts, lnks, remembered, env, fs) {
|
|
4526
|
+
const exeLow = spec.exe.toLowerCase();
|
|
4527
|
+
const groups = [
|
|
4528
|
+
{ source: "process", hints: (facts?.procs ?? []).filter((p) => p.name.toLowerCase() === exeLow).map((p) => p.path), failed: facts === null },
|
|
4529
|
+
{ source: "remembered", hints: remembered ? [remembered] : [] },
|
|
4530
|
+
{ source: "uninstall", hints: (facts?.uninstall ?? []).filter((u) => spec.names.test(u.name.trim())).flatMap((u) => [u.il, u.icon, u.un]), failed: facts === null },
|
|
4531
|
+
{
|
|
4532
|
+
source: "shortcut",
|
|
4533
|
+
hints: (lnks ?? []).filter((l) => win324.basename(l.target).toLowerCase() === exeLow || spec.names.test(win324.basename(l.path).replace(/\.lnk$/i, ""))).map((l) => l.target),
|
|
4534
|
+
failed: lnks === null
|
|
4535
|
+
},
|
|
4536
|
+
{ source: "appPaths", hints: (facts?.appPaths ?? []).filter((a) => a.exe.toLowerCase() === exeLow).flatMap((a) => [a.def, a.path]), failed: facts === null },
|
|
4537
|
+
{ source: "protocol", hints: (facts?.protocols ?? []).filter((p) => spec.protocols.includes(p.name.toLowerCase())).map((p) => p.cmd), failed: facts === null },
|
|
4538
|
+
{ source: "default", hints: spec.defaultDirs(env) },
|
|
4539
|
+
{ source: "path", hints: (env.path ?? "").split(";").filter((x) => x.trim()).map((x) => x.trim()) }
|
|
4540
|
+
];
|
|
4541
|
+
const notes = [];
|
|
4542
|
+
for (const g of groups) {
|
|
4543
|
+
const hints = g.hints.map(normalizeHint).filter((h) => h !== null);
|
|
4544
|
+
for (const h of hints) for (const c of expandHint(h, spec, fs, g.source !== "path")) if (validExe(c, spec, fs)) return { found: { exe: c, source: g.source }, tried: "" };
|
|
4545
|
+
if (g.source === "remembered" && hints.length === 0) continue;
|
|
4546
|
+
const what = g.failed ? "\u67E5\u8BE2\u5931\u8D25\u6216\u8D85\u65F6" : hints.length === 0 ? "\u6CA1\u6709\u7EBF\u7D22" : `${hints.length} \u6761\u7EBF\u7D22\u90FD\u4E0D\u5408\u683C\uFF08\u5982 ${hints[0]}\uFF09`;
|
|
4547
|
+
notes.push(`${SOURCE_TEXT[g.source]}\uFF1A${what}`);
|
|
4548
|
+
}
|
|
4549
|
+
return { found: null, tried: notes.join("\uFF1B") };
|
|
4550
|
+
}
|
|
4551
|
+
var WIN_APPS_FILE = "winapps.json";
|
|
4552
|
+
var LOCATE_FAIL_LOG_MS = 6 * 60 * 60 * 1e3;
|
|
4553
|
+
var LOCATE_MIN_GAP_MS = 60 * 1e3;
|
|
4554
|
+
var LOCATE_EVERY_MS = 10 * 60 * 1e3;
|
|
4555
|
+
var cacheFile = () => join19(deskbuddyHome(), WIN_APPS_FILE);
|
|
4556
|
+
function loadCache(file3 = cacheFile()) {
|
|
4557
|
+
try {
|
|
4558
|
+
const v = JSON.parse(readFileSync21(file3, "utf8"));
|
|
4559
|
+
const out = {};
|
|
4560
|
+
for (const [k, e] of Object.entries(v ?? {})) {
|
|
4561
|
+
const x = e;
|
|
4562
|
+
if (x && isValidWinPath(x.exe) && typeof x.source === "string" && x.source in SOURCE_TEXT) out[k] = { exe: x.exe, source: x.source, at: typeof x.at === "number" ? x.at : 0 };
|
|
4563
|
+
}
|
|
4564
|
+
return out;
|
|
4565
|
+
} catch {
|
|
4566
|
+
return {};
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4569
|
+
function saveCache(c, file3) {
|
|
4570
|
+
try {
|
|
4571
|
+
mkdirSync19(join19(file3, ".."), { recursive: true });
|
|
4572
|
+
writeFileSync19(file3 + ".tmp", JSON.stringify(c, null, 2));
|
|
4573
|
+
renameSync13(file3 + ".tmp", file3);
|
|
4574
|
+
} catch {
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
4577
|
+
function cachedWinAppExe(id, fs = defaultLocFs, file3 = cacheFile()) {
|
|
4578
|
+
const spec = WIN_APP_SPECS[id];
|
|
4579
|
+
const e = spec ? loadCache(file3)[id] : void 0;
|
|
4580
|
+
return spec && e && validExe(e.exe, spec, fs) ? e.exe : null;
|
|
4581
|
+
}
|
|
4582
|
+
var WinAppLocator = class {
|
|
4583
|
+
#d;
|
|
4584
|
+
/** 正在进行的查询(只覆盖 ids 里的工具) */
|
|
4585
|
+
#inflight = null;
|
|
4586
|
+
/** 每个工具上次查完的时间(最小间隔按工具分别记) */
|
|
4587
|
+
#lastRunAt = /* @__PURE__ */ new Map();
|
|
4588
|
+
#logFn;
|
|
4589
|
+
#lastResult = {};
|
|
4590
|
+
#failLogged = /* @__PURE__ */ new Map();
|
|
4591
|
+
constructor(d = {}) {
|
|
4592
|
+
this.#d = d;
|
|
4593
|
+
this.#logFn = d.log;
|
|
4594
|
+
}
|
|
4595
|
+
/** 补上日志函数(先被不带日志的调用方建出来时,daemon 后给的 log 也要生效);已有的不覆盖 */
|
|
4596
|
+
setLog(log) {
|
|
4597
|
+
if (log && !this.#logFn) this.#logFn = log;
|
|
4598
|
+
}
|
|
4599
|
+
get hasLog() {
|
|
4600
|
+
return !!this.#logFn;
|
|
4601
|
+
}
|
|
4602
|
+
#now() {
|
|
4603
|
+
return this.#d.now ? this.#d.now() : Date.now();
|
|
4604
|
+
}
|
|
4605
|
+
#active() {
|
|
4606
|
+
if ((this.#d.platform ?? process.platform) !== "win32") return false;
|
|
4607
|
+
return !(this.#d.skip ? this.#d.skip() : process.env.DESKBUDDY_SKIP_WIN_LOCATE === "1");
|
|
4608
|
+
}
|
|
4609
|
+
#env() {
|
|
4610
|
+
return this.#d.env ?? { localAppData: process.env.LOCALAPPDATA, programFiles: process.env.ProgramFiles, programFilesX86: process.env["ProgramFiles(x86)"], path: process.env.PATH };
|
|
4611
|
+
}
|
|
4612
|
+
/**
|
|
4613
|
+
* 查一遍(一次 PowerShell 查进程 / 注册表 + 一次枚举快捷方式,异步带超时),结果写缓存。
|
|
4614
|
+
* 正在进行的查询已覆盖这些工具 → 共用它;没覆盖 → 等它结束再查自己这批(不并发起两次 PowerShell)
|
|
4615
|
+
*/
|
|
4616
|
+
refresh(ids = Object.keys(WIN_APP_SPECS)) {
|
|
4617
|
+
if (!this.#active()) return Promise.resolve({});
|
|
4618
|
+
const want = ids.filter((i) => WIN_APP_SPECS[i]);
|
|
4619
|
+
const cur = this.#inflight;
|
|
4620
|
+
if (cur && want.every((i) => cur.ids.has(i))) return cur.p;
|
|
4621
|
+
const p = (cur ? cur.p.then(() => void 0, () => void 0) : Promise.resolve()).then(() => this.#run(want));
|
|
4622
|
+
const entry = { ids: new Set(want), p };
|
|
4623
|
+
this.#inflight = entry;
|
|
4624
|
+
void p.finally(() => {
|
|
4625
|
+
if (this.#inflight === entry) this.#inflight = null;
|
|
4626
|
+
}).catch(() => void 0);
|
|
4627
|
+
return p;
|
|
4628
|
+
}
|
|
4629
|
+
/** 某一个工具的主程序:只查这一个工具;它近 60 秒内查过就直接用缓存(正在查它就等查完)。非 Windows / 测试兜底返回缓存 */
|
|
4630
|
+
async locate(id) {
|
|
4631
|
+
const fs = this.#d.fs ?? defaultLocFs;
|
|
4632
|
+
const file3 = this.#d.file ?? cacheFile();
|
|
4633
|
+
if (this.#active() && WIN_APP_SPECS[id]) {
|
|
4634
|
+
const cur = this.#inflight;
|
|
4635
|
+
if (cur && cur.ids.has(id)) await cur.p.catch(() => void 0);
|
|
4636
|
+
else if (this.#now() - (this.#lastRunAt.get(id) ?? -Infinity) >= LOCATE_MIN_GAP_MS) await this.refresh([id]).catch(() => void 0);
|
|
4637
|
+
}
|
|
4638
|
+
return cachedWinAppExe(id, fs, file3);
|
|
4639
|
+
}
|
|
4640
|
+
async #run(ids) {
|
|
4641
|
+
const ps = this.#d.ps ?? defaultPs;
|
|
4642
|
+
const fs = this.#d.fs ?? defaultLocFs;
|
|
4643
|
+
const file3 = this.#d.file ?? cacheFile();
|
|
4644
|
+
const specs = ids.map((i) => WIN_APP_SPECS[i]);
|
|
4645
|
+
const facts = parseFacts(
|
|
4646
|
+
await ps(WIN_LOCATE_PS, { DB_LOC_EXES: JSON.stringify(specs.map((s) => s.exe)), DB_LOC_PROTOS: JSON.stringify(specs.flatMap((s) => s.protocols)) }).catch(() => null)
|
|
4647
|
+
);
|
|
4648
|
+
const lnks = parseLnks(await ps(LNK_LIST_PS).catch(() => null));
|
|
4649
|
+
const cache2 = loadCache(file3);
|
|
4650
|
+
const out = {};
|
|
4651
|
+
let dirty = false;
|
|
4652
|
+
for (const spec of specs) {
|
|
4653
|
+
const prev = cache2[spec.id];
|
|
4654
|
+
const r = locateFromFacts(spec, facts, lnks, prev?.exe ?? null, this.#env(), fs);
|
|
4655
|
+
out[spec.id] = r.found;
|
|
4656
|
+
if (r.found) {
|
|
4657
|
+
this.#failLogged.delete(spec.id);
|
|
4658
|
+
const changed = !prev || prev.exe.toLowerCase() !== r.found.exe.toLowerCase();
|
|
4659
|
+
if (changed) this.#log(`${spec.label} \u684C\u9762\u7248\u4F4D\u7F6E\uFF1A${r.found.exe}\uFF08\u4ECE${SOURCE_TEXT[r.found.source]}\u627E\u5230\uFF09`);
|
|
4660
|
+
if (changed || r.found.source !== "remembered") {
|
|
4661
|
+
cache2[spec.id] = { exe: r.found.exe, source: r.found.source, at: this.#now() };
|
|
4662
|
+
dirty = true;
|
|
4663
|
+
}
|
|
4664
|
+
} else {
|
|
4665
|
+
if (prev) {
|
|
4666
|
+
delete cache2[spec.id];
|
|
4667
|
+
dirty = true;
|
|
4668
|
+
}
|
|
4669
|
+
this.#noteMissing(spec, r.tried);
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
if (dirty) saveCache(cache2, file3);
|
|
4673
|
+
for (const id of ids) this.#lastRunAt.set(id, this.#now());
|
|
4674
|
+
this.#lastResult = out;
|
|
4675
|
+
return out;
|
|
4676
|
+
}
|
|
4677
|
+
/** 同一原因 LOCATE_FAIL_LOG_MS 内只记一次(不刷屏) */
|
|
4678
|
+
#noteMissing(spec, tried) {
|
|
4679
|
+
const key = `${spec.id}\0${tried}`;
|
|
4680
|
+
const at = this.#failLogged.get(key);
|
|
4681
|
+
const now = this.#now();
|
|
4682
|
+
if (at !== void 0 && now - at < LOCATE_FAIL_LOG_MS) return;
|
|
4683
|
+
for (const k of this.#failLogged.keys()) if (k.startsWith(`${spec.id}\0`)) this.#failLogged.delete(k);
|
|
4684
|
+
this.#failLogged.set(key, now);
|
|
4685
|
+
this.#log(`\u672C\u673A\u627E\u4E0D\u5230 ${spec.label} \u684C\u9762\u7248\uFF08${tried}\uFF09`);
|
|
4686
|
+
}
|
|
4687
|
+
#log(msg) {
|
|
4688
|
+
this.#logFn?.(msg);
|
|
4689
|
+
}
|
|
4690
|
+
/** 测试用:上一轮结果 */
|
|
4691
|
+
get lastResult() {
|
|
4692
|
+
return this.#lastResult;
|
|
4693
|
+
}
|
|
4694
|
+
};
|
|
4695
|
+
var shared = null;
|
|
4696
|
+
function winAppLocator(log) {
|
|
4697
|
+
if (!shared) shared = new WinAppLocator({ log });
|
|
4698
|
+
else shared.setLog(log);
|
|
4699
|
+
return shared;
|
|
4700
|
+
}
|
|
4701
|
+
|
|
4300
4702
|
// src/daemon/locate.ts
|
|
4301
4703
|
function qoderWorker(d, worker, appBin) {
|
|
4302
4704
|
if (d.isExecutable(appBin)) return { file: appBin, prefixArgs: [worker], env: { ELECTRON_RUN_AS_NODE: "1" } };
|
|
@@ -4308,14 +4710,14 @@ var defaultLocateDeps = () => ({
|
|
|
4308
4710
|
nodePath: process.execPath,
|
|
4309
4711
|
isExecutable: (p) => {
|
|
4310
4712
|
try {
|
|
4311
|
-
if (!
|
|
4713
|
+
if (!statSync8(p).isFile()) return false;
|
|
4312
4714
|
accessSync(p, constants.X_OK);
|
|
4313
4715
|
return true;
|
|
4314
4716
|
} catch {
|
|
4315
4717
|
return false;
|
|
4316
4718
|
}
|
|
4317
4719
|
},
|
|
4318
|
-
exists: (p) =>
|
|
4720
|
+
exists: (p) => existsSync17(p),
|
|
4319
4721
|
lexists: (p) => {
|
|
4320
4722
|
try {
|
|
4321
4723
|
lstatSync(p);
|
|
@@ -4329,15 +4731,16 @@ var defaultLocateDeps = () => ({
|
|
|
4329
4731
|
localAppData: process.env.LOCALAPPDATA ?? "",
|
|
4330
4732
|
readText: (p) => {
|
|
4331
4733
|
try {
|
|
4332
|
-
return
|
|
4734
|
+
return readFileSync22(p, "utf8");
|
|
4333
4735
|
} catch {
|
|
4334
4736
|
return null;
|
|
4335
4737
|
}
|
|
4336
4738
|
},
|
|
4337
4739
|
// 🔴 daemon 自己也不能找到垫片:远程任务要的是真程序(窗口接管 spec 2026-09-30 §3.1)
|
|
4338
|
-
skipDirs: [
|
|
4740
|
+
skipDirs: [join20(deskbuddyHome(), "bin")],
|
|
4339
4741
|
winAppHints: (product, exe) => winInstallDirs(product, exe),
|
|
4340
4742
|
winAppExeHints: () => winWorkbuddyExeHints(),
|
|
4743
|
+
winAppExe: (id) => cachedWinAppExe(id),
|
|
4341
4744
|
isPe: (p) => {
|
|
4342
4745
|
let fd = null;
|
|
4343
4746
|
try {
|
|
@@ -4412,7 +4815,7 @@ function classifyAsyncError(e, stdout2, stderr) {
|
|
|
4412
4815
|
var defaultProbe = createVersionProbe({
|
|
4413
4816
|
stat: (p) => {
|
|
4414
4817
|
try {
|
|
4415
|
-
const st =
|
|
4818
|
+
const st = statSync8(p);
|
|
4416
4819
|
return { size: st.size, mtimeMs: st.mtimeMs };
|
|
4417
4820
|
} catch {
|
|
4418
4821
|
return null;
|
|
@@ -4460,9 +4863,9 @@ function installDirsFromProcs(procs, exe) {
|
|
|
4460
4863
|
const out = [];
|
|
4461
4864
|
for (const p of procs ?? []) {
|
|
4462
4865
|
const first = winSplitArgs(p.cmdline)[0] ?? "";
|
|
4463
|
-
if (
|
|
4464
|
-
const dir2 =
|
|
4465
|
-
if (
|
|
4866
|
+
if (win325.basename(first).toLowerCase() !== exe.toLowerCase()) continue;
|
|
4867
|
+
const dir2 = win325.dirname(first);
|
|
4868
|
+
if (win325.isAbsolute(dir2) && !out.some((d) => d.toLowerCase() === dir2.toLowerCase())) out.push(dir2);
|
|
4466
4869
|
}
|
|
4467
4870
|
return out;
|
|
4468
4871
|
}
|
|
@@ -4470,8 +4873,8 @@ function parseUninstallDirs(out) {
|
|
|
4470
4873
|
const dirs = [];
|
|
4471
4874
|
for (const raw of out.split(/\r?\n/)) {
|
|
4472
4875
|
let v = raw.trim().replace(/,\s*-?\d+$/, "").replace(/^"|"$/g, "").trim();
|
|
4473
|
-
if (!v || !
|
|
4474
|
-
if (/\.exe$/i.test(v)) v =
|
|
4876
|
+
if (!v || !win325.isAbsolute(v)) continue;
|
|
4877
|
+
if (/\.exe$/i.test(v)) v = win325.dirname(v);
|
|
4475
4878
|
v = v.replace(/[\\/]+$/, "");
|
|
4476
4879
|
if (!dirs.some((d) => d.toLowerCase() === v.toLowerCase())) dirs.push(v);
|
|
4477
4880
|
}
|
|
@@ -4498,7 +4901,7 @@ function parseExeHints(out) {
|
|
|
4498
4901
|
const res = [];
|
|
4499
4902
|
for (const raw of out.split(/\r?\n/)) {
|
|
4500
4903
|
const v = raw.trim().replace(/,\s*-?\d+$/, "").replace(/^"|"$/g, "").trim().replace(/[\\/]+$/, "");
|
|
4501
|
-
if (!v || !
|
|
4904
|
+
if (!v || !win325.isAbsolute(v)) continue;
|
|
4502
4905
|
if (!res.some((x) => x.toLowerCase() === v.toLowerCase())) res.push(v);
|
|
4503
4906
|
}
|
|
4504
4907
|
return res;
|
|
@@ -4522,57 +4925,54 @@ function winWorkbuddyExeHints() {
|
|
|
4522
4925
|
function locateWorkbuddyAppExe(d = defaultLocateDeps()) {
|
|
4523
4926
|
if (!isWin(d)) {
|
|
4524
4927
|
for (const dir2 of appDirs(d.home)) {
|
|
4525
|
-
const p =
|
|
4928
|
+
const p = join20(dir2, "WorkBuddy.app");
|
|
4526
4929
|
if (d.exists(p)) return p;
|
|
4527
4930
|
}
|
|
4528
4931
|
return null;
|
|
4529
4932
|
}
|
|
4530
|
-
return pickWorkbuddyExe(winAppDirs(d, "WorkBuddy"), d) ?? pickWorkbuddyExe(d.winAppExeHints?.() ?? [], d);
|
|
4933
|
+
return pickWorkbuddyExe(locatedExe(d, "workbuddy"), d) ?? pickWorkbuddyExe(winAppDirs(d, "WorkBuddy"), d) ?? pickWorkbuddyExe(d.winAppExeHints?.() ?? [], d);
|
|
4934
|
+
}
|
|
4935
|
+
function locatedExe(d, id) {
|
|
4936
|
+
const p = d.winAppExe?.(id);
|
|
4937
|
+
return p ? [p] : [];
|
|
4938
|
+
}
|
|
4939
|
+
function locatedDirs(d, id) {
|
|
4940
|
+
return locatedExe(d, id).map((p) => win325.dirname(p));
|
|
4531
4941
|
}
|
|
4532
4942
|
function pickWorkbuddyExe(cands, d) {
|
|
4533
4943
|
for (const c of cands) {
|
|
4534
|
-
const exe = /\.exe$/i.test(c) ? c :
|
|
4535
|
-
if (
|
|
4944
|
+
const exe = /\.exe$/i.test(c) ? c : win325.join(c, "WorkBuddy.exe");
|
|
4945
|
+
if (win325.basename(exe).toLowerCase() === "workbuddy.exe" && d.exists(exe)) return exe;
|
|
4536
4946
|
}
|
|
4537
4947
|
return null;
|
|
4538
4948
|
}
|
|
4539
|
-
function
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
execFile4(
|
|
4543
|
-
"powershell.exe",
|
|
4544
|
-
["-NoProfile", "-NonInteractive", "-Command", WIN_WORKBUDDY_EXE_PS],
|
|
4545
|
-
{ encoding: "utf8", windowsHide: true, timeout: 15e3 },
|
|
4546
|
-
(err, stdout2) => resolve6(err && !stdout2 ? [] : parseExeHints(String(stdout2 ?? "")))
|
|
4547
|
-
);
|
|
4548
|
-
} catch {
|
|
4549
|
-
resolve6([]);
|
|
4550
|
-
}
|
|
4551
|
-
});
|
|
4949
|
+
async function winWorkbuddyLocatorHints() {
|
|
4950
|
+
const p = await winAppLocator().locate("workbuddy");
|
|
4951
|
+
return p ? [p] : [];
|
|
4552
4952
|
}
|
|
4553
|
-
async function locateWorkbuddyAppExeAsync(d = defaultLocateDeps(), hints =
|
|
4953
|
+
async function locateWorkbuddyAppExeAsync(d = defaultLocateDeps(), hints = winWorkbuddyLocatorHints) {
|
|
4554
4954
|
if (!isWin(d)) return locateWorkbuddyAppExe(d);
|
|
4555
|
-
return pickWorkbuddyExe(winAppDirs(d, "WorkBuddy"), d) ?? pickWorkbuddyExe(await hints(), d);
|
|
4955
|
+
return pickWorkbuddyExe(locatedExe(d, "workbuddy"), d) ?? pickWorkbuddyExe(winAppDirs(d, "WorkBuddy"), d) ?? pickWorkbuddyExe(await hints(), d);
|
|
4556
4956
|
}
|
|
4557
4957
|
var isWin = (d) => (d.platform ?? process.platform) === "win32";
|
|
4558
4958
|
var WIN_EXTS = [".exe", ".cmd", ".bat"];
|
|
4559
4959
|
function extraBinDirs(home, nodePath = "", win) {
|
|
4560
4960
|
if (win) {
|
|
4561
4961
|
return [
|
|
4562
|
-
|
|
4563
|
-
...win.appData ? [
|
|
4564
|
-
|
|
4565
|
-
...win.localAppData ? [
|
|
4566
|
-
|
|
4962
|
+
join20(home, ".local", "bin"),
|
|
4963
|
+
...win.appData ? [join20(win.appData, "npm")] : [],
|
|
4964
|
+
join20(home, ".bun", "bin"),
|
|
4965
|
+
...win.localAppData ? [join20(win.localAppData, "Volta", "bin")] : [],
|
|
4966
|
+
join20(home, "scoop", "shims"),
|
|
4567
4967
|
...nodePath ? [dirname11(nodePath)] : []
|
|
4568
4968
|
];
|
|
4569
4969
|
}
|
|
4570
4970
|
return [
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4971
|
+
join20(home, ".local", "bin"),
|
|
4972
|
+
join20(home, ".claude", "local"),
|
|
4973
|
+
join20(home, ".npm-global", "bin"),
|
|
4974
|
+
join20(home, ".bun", "bin"),
|
|
4975
|
+
join20(home, ".volta", "bin"),
|
|
4576
4976
|
"/opt/homebrew/bin",
|
|
4577
4977
|
"/usr/local/bin",
|
|
4578
4978
|
...nodePath ? [dirname11(nodePath)] : []
|
|
@@ -4586,7 +4986,7 @@ function whichAllIn(name, d) {
|
|
|
4586
4986
|
const out = [];
|
|
4587
4987
|
for (const dir2 of binDirs(d)) {
|
|
4588
4988
|
for (const ext of exts) {
|
|
4589
|
-
const p =
|
|
4989
|
+
const p = join20(dir2, name + ext);
|
|
4590
4990
|
if (d.isExecutable(p)) out.push(p);
|
|
4591
4991
|
}
|
|
4592
4992
|
}
|
|
@@ -4610,7 +5010,7 @@ function binDirs(d) {
|
|
|
4610
5010
|
function parseNpmCmdShim(shimPath, text) {
|
|
4611
5011
|
const m = text.match(/"%~?dp0%?\\?([^"%]+)"\s+%\*/i);
|
|
4612
5012
|
if (!m) return null;
|
|
4613
|
-
return
|
|
5013
|
+
return join20(dirname11(shimPath), m[1].replace(/^\\/, ""));
|
|
4614
5014
|
}
|
|
4615
5015
|
var JS_RUNTIME_EXE = /^(?:node|bun|bunx|deno)(?:\.exe)?$/i;
|
|
4616
5016
|
function resolveWinLaunch(file3, d) {
|
|
@@ -4621,7 +5021,7 @@ function resolveWinLaunch(file3, d) {
|
|
|
4621
5021
|
const target = text ? parseNpmCmdShim(file3, text) : null;
|
|
4622
5022
|
if (!target || !d.exists(target)) return null;
|
|
4623
5023
|
if (/\.(?:cmd|bat)$/i.test(target)) return null;
|
|
4624
|
-
if (JS_RUNTIME_EXE.test(
|
|
5024
|
+
if (JS_RUNTIME_EXE.test(win325.basename(target.replace(/\//g, "\\")))) return null;
|
|
4625
5025
|
return resolveWinLaunch(target, d);
|
|
4626
5026
|
}
|
|
4627
5027
|
if (/\.(?:c|m)?js$/.test(lower)) return { file: d.nodePath, prefixArgs: [file3] };
|
|
@@ -4630,22 +5030,22 @@ function resolveWinLaunch(file3, d) {
|
|
|
4630
5030
|
return null;
|
|
4631
5031
|
}
|
|
4632
5032
|
function appDirs(home) {
|
|
4633
|
-
return ["/Applications",
|
|
5033
|
+
return ["/Applications", join20(home, "Applications")];
|
|
4634
5034
|
}
|
|
4635
5035
|
function winAppDirs(d, product) {
|
|
4636
5036
|
return [
|
|
4637
|
-
...d.localAppData ? [
|
|
4638
|
-
|
|
4639
|
-
|
|
5037
|
+
...d.localAppData ? [join20(d.localAppData, "Programs", product)] : [],
|
|
5038
|
+
join20("C:\\Program Files", product),
|
|
5039
|
+
join20("C:\\Program Files (x86)", product)
|
|
4640
5040
|
];
|
|
4641
5041
|
}
|
|
4642
5042
|
var WORKBUDDY_CLI_IN_WIN_APP = "resources/app.asar.unpacked/cli/bin/codebuddy";
|
|
4643
|
-
var QODER_CN_WORKER_IN_WIN_APP =
|
|
4644
|
-
var QODER_WORKER_IN_WIN_APP =
|
|
5043
|
+
var QODER_CN_WORKER_IN_WIN_APP = QODER_CN_WORKER_REL;
|
|
5044
|
+
var QODER_WORKER_IN_WIN_APP = QODER_WORKER_REL;
|
|
4645
5045
|
function winClaudeExeCandidates(d) {
|
|
4646
5046
|
return [
|
|
4647
|
-
...d.home ? [
|
|
4648
|
-
...d.appData ? [
|
|
5047
|
+
...d.home ? [join20(d.home, ".local", "bin", "claude.exe")] : [],
|
|
5048
|
+
...d.appData ? [join20(d.appData, "npm", "node_modules", "@anthropic-ai", "claude-code", "bin", "claude.exe")] : []
|
|
4649
5049
|
];
|
|
4650
5050
|
}
|
|
4651
5051
|
function winClaudeLaunches(d) {
|
|
@@ -4690,21 +5090,21 @@ function locateWin(tool, d) {
|
|
|
4690
5090
|
case "workbuddy":
|
|
4691
5091
|
return locateWorkbuddy(d)?.loc ?? null;
|
|
4692
5092
|
case "qoder_cn": {
|
|
4693
|
-
for (const dir2 of winAppDirs(d, "Qoder CN")) {
|
|
4694
|
-
const p =
|
|
4695
|
-
if (d.exists(p)) return qoderWorker(d, p,
|
|
5093
|
+
for (const dir2 of [...locatedDirs(d, "qoder_cn"), ...winAppDirs(d, "Qoder CN")]) {
|
|
5094
|
+
const p = join20(dir2, QODER_CN_WORKER_IN_WIN_APP);
|
|
5095
|
+
if (d.exists(p)) return qoderWorker(d, p, join20(dir2, "Qoder CN.exe"));
|
|
4696
5096
|
}
|
|
4697
5097
|
return plain("qoderclicn");
|
|
4698
5098
|
}
|
|
4699
5099
|
case "qoder": {
|
|
4700
|
-
for (const dir2 of winAppDirs(d, "Qoder")) {
|
|
4701
|
-
const p =
|
|
4702
|
-
if (d.exists(p)) return qoderWorker(d, p,
|
|
5100
|
+
for (const dir2 of [...locatedDirs(d, "qoder"), ...winAppDirs(d, "Qoder")]) {
|
|
5101
|
+
const p = join20(dir2, QODER_WORKER_IN_WIN_APP);
|
|
5102
|
+
if (d.exists(p)) return qoderWorker(d, p, join20(dir2, "Qoder.exe"));
|
|
4703
5103
|
}
|
|
4704
5104
|
return plain("qodercli");
|
|
4705
5105
|
}
|
|
4706
5106
|
case "opencode": {
|
|
4707
|
-
const p =
|
|
5107
|
+
const p = join20(d.home, ".opencode", "bin", "opencode.exe");
|
|
4708
5108
|
if (d.exists(p)) return { file: p, prefixArgs: [] };
|
|
4709
5109
|
return plain("opencode");
|
|
4710
5110
|
}
|
|
@@ -4716,7 +5116,7 @@ function locateWin(tool, d) {
|
|
|
4716
5116
|
return null;
|
|
4717
5117
|
}
|
|
4718
5118
|
}
|
|
4719
|
-
function workbuddyHasTui(loc, exists =
|
|
5119
|
+
function workbuddyHasTui(loc, exists = existsSync17, realpath = realpathSync2) {
|
|
4720
5120
|
const real = (p) => {
|
|
4721
5121
|
try {
|
|
4722
5122
|
return realpath(p);
|
|
@@ -4728,8 +5128,8 @@ function workbuddyHasTui(loc, exists = existsSync16, realpath = realpathSync2) {
|
|
|
4728
5128
|
const cands = [...loc.prefixArgs, loc.file].map(real);
|
|
4729
5129
|
const script = cands.find((a) => BIN.test(a));
|
|
4730
5130
|
if (!script) return true;
|
|
4731
|
-
const dist =
|
|
4732
|
-
return exists(
|
|
5131
|
+
const dist = join20(dirname11(dirname11(script)), "dist");
|
|
5132
|
+
return exists(join20(dist, "codebuddy.js"));
|
|
4733
5133
|
}
|
|
4734
5134
|
function locateWorkbuddyTui(d = defaultLocateDeps(), exists, realpath) {
|
|
4735
5135
|
const cands = [locateWorkbuddyStandalone(d), locateWorkbuddyApp(d)].filter((c) => c !== null);
|
|
@@ -4770,7 +5170,7 @@ function locateWorkbuddyStandalone(d = defaultLocateDeps()) {
|
|
|
4770
5170
|
function locateWorkbuddyApp(d = defaultLocateDeps()) {
|
|
4771
5171
|
if (!isWin(d)) {
|
|
4772
5172
|
for (const dir2 of appDirs(d.home)) {
|
|
4773
|
-
const p =
|
|
5173
|
+
const p = join20(dir2, WORKBUDDY_CLI_IN_APP);
|
|
4774
5174
|
if (d.isExecutable(p)) return { file: p, prefixArgs: [] };
|
|
4775
5175
|
}
|
|
4776
5176
|
return null;
|
|
@@ -4778,7 +5178,7 @@ function locateWorkbuddyApp(d = defaultLocateDeps()) {
|
|
|
4778
5178
|
const inApp = (dirs) => {
|
|
4779
5179
|
for (const dir2 of dirs) {
|
|
4780
5180
|
for (const ext of [".exe", ".cmd", ""]) {
|
|
4781
|
-
const p =
|
|
5181
|
+
const p = join20(dir2, WORKBUDDY_CLI_IN_WIN_APP + ext);
|
|
4782
5182
|
if (d.exists(p)) {
|
|
4783
5183
|
const r = resolveWinLaunch(p, d);
|
|
4784
5184
|
if (r) return r;
|
|
@@ -4787,7 +5187,7 @@ function locateWorkbuddyApp(d = defaultLocateDeps()) {
|
|
|
4787
5187
|
}
|
|
4788
5188
|
return null;
|
|
4789
5189
|
};
|
|
4790
|
-
return inApp(winAppDirs(d, "WorkBuddy")) ?? inApp(d.winAppHints?.("WorkBuddy", "WorkBuddy.exe") ?? []);
|
|
5190
|
+
return inApp([...locatedDirs(d, "workbuddy"), ...winAppDirs(d, "WorkBuddy")]) ?? inApp(d.winAppHints?.("WorkBuddy", "WorkBuddy.exe") ?? []);
|
|
4791
5191
|
}
|
|
4792
5192
|
function locateWorkbuddy(d = defaultLocateDeps()) {
|
|
4793
5193
|
const s = locateWorkbuddyStandalone(d);
|
|
@@ -4813,20 +5213,20 @@ function locate(tool, d = defaultLocateDeps()) {
|
|
|
4813
5213
|
return locateWorkbuddy(d)?.loc ?? null;
|
|
4814
5214
|
case "qoder_cn": {
|
|
4815
5215
|
for (const dir2 of appDirs(d.home)) {
|
|
4816
|
-
const p =
|
|
4817
|
-
if (d.exists(p)) return qoderWorker(d, p,
|
|
5216
|
+
const p = join20(dir2, QODER_CN_WORKER_IN_APP);
|
|
5217
|
+
if (d.exists(p)) return qoderWorker(d, p, join20(dir2, "Qoder CN.app", "Contents", "MacOS", "Qoder CN"));
|
|
4818
5218
|
}
|
|
4819
5219
|
return plain("qoderclicn");
|
|
4820
5220
|
}
|
|
4821
5221
|
case "qoder": {
|
|
4822
5222
|
for (const dir2 of appDirs(d.home)) {
|
|
4823
|
-
const p =
|
|
4824
|
-
if (d.exists(p)) return qoderWorker(d, p,
|
|
5223
|
+
const p = join20(dir2, QODER_WORKER_IN_APP);
|
|
5224
|
+
if (d.exists(p)) return qoderWorker(d, p, join20(dir2, "Qoder.app", "Contents", "MacOS", "Qoder"));
|
|
4825
5225
|
}
|
|
4826
5226
|
return plain("qodercli");
|
|
4827
5227
|
}
|
|
4828
5228
|
case "opencode": {
|
|
4829
|
-
const p =
|
|
5229
|
+
const p = join20(d.home, ".opencode", "bin", "opencode");
|
|
4830
5230
|
if (d.isExecutable(p)) return { file: p, prefixArgs: [] };
|
|
4831
5231
|
return plain("opencode");
|
|
4832
5232
|
}
|
|
@@ -4847,7 +5247,7 @@ function findWt(d = defaultLocateDeps()) {
|
|
|
4847
5247
|
if (!isWin(d)) return null;
|
|
4848
5248
|
const hit = whichIn("wt", d);
|
|
4849
5249
|
if (hit) return hit;
|
|
4850
|
-
const alias = d.localAppData ?
|
|
5250
|
+
const alias = d.localAppData ? join20(d.localAppData, "Microsoft", "WindowsApps", "wt.exe") : "";
|
|
4851
5251
|
return alias && (d.exists(alias) || (d.lexists?.(alias) ?? false)) ? alias : null;
|
|
4852
5252
|
}
|
|
4853
5253
|
|
|
@@ -4914,13 +5314,13 @@ function findWbTranscript(configDir, sessionId) {
|
|
|
4914
5314
|
if (!SESSION_UUID_RE.test(sessionId)) return null;
|
|
4915
5315
|
let dirs;
|
|
4916
5316
|
try {
|
|
4917
|
-
dirs =
|
|
5317
|
+
dirs = readdirSync9(join21(configDir, "projects"));
|
|
4918
5318
|
} catch {
|
|
4919
5319
|
return null;
|
|
4920
5320
|
}
|
|
4921
5321
|
for (const d of dirs) {
|
|
4922
|
-
const p =
|
|
4923
|
-
if (
|
|
5322
|
+
const p = join21(configDir, "projects", d, `${sessionId}.jsonl`);
|
|
5323
|
+
if (existsSync18(p)) return p;
|
|
4924
5324
|
}
|
|
4925
5325
|
return null;
|
|
4926
5326
|
}
|
|
@@ -4928,7 +5328,7 @@ var SLICE = 64 * 1024;
|
|
|
4928
5328
|
function readSlice(p, from) {
|
|
4929
5329
|
const fd = openSync5(p, "r");
|
|
4930
5330
|
try {
|
|
4931
|
-
const size =
|
|
5331
|
+
const size = statSync9(p).size;
|
|
4932
5332
|
const len = Math.min(size, SLICE);
|
|
4933
5333
|
const buf = Buffer.alloc(len);
|
|
4934
5334
|
readSync5(fd, buf, 0, len, from === "head" ? 0 : size - len);
|
|
@@ -4972,7 +5372,7 @@ function userText2(o) {
|
|
|
4972
5372
|
function wbTranscriptInfo(p) {
|
|
4973
5373
|
const head = jsonLines(readSlice(p, "head"));
|
|
4974
5374
|
const tail = jsonLines(readSlice(p, "tail"));
|
|
4975
|
-
const mtime = Math.floor(
|
|
5375
|
+
const mtime = Math.floor(statSync9(p).mtimeMs);
|
|
4976
5376
|
const hs = head.map((o) => tsOf(o.timestamp)).filter((n) => n !== null);
|
|
4977
5377
|
const ts = tail.map((o) => tsOf(o.timestamp)).filter((n) => n !== null);
|
|
4978
5378
|
const createdAt = hs.length ? Math.min(...hs) : mtime;
|
|
@@ -5005,8 +5405,8 @@ function wbElectronCandidates(appCliPath) {
|
|
|
5005
5405
|
const m = /^(.*?)[\\/]resources[\\/]app\.asar\.unpacked[\\/]/i.exec(appCliPath);
|
|
5006
5406
|
if (!m) return [];
|
|
5007
5407
|
const root = m[1];
|
|
5008
|
-
if (/\.app[\\/]Contents$/.test(root)) return [
|
|
5009
|
-
return [
|
|
5408
|
+
if (/\.app[\\/]Contents$/.test(root)) return [join21(root, "MacOS", "Electron"), join21(root, "MacOS", "WorkBuddy")];
|
|
5409
|
+
return [join21(root, "WorkBuddy.exe")];
|
|
5010
5410
|
}
|
|
5011
5411
|
function defaultWbElectron(d = defaultLocateDeps()) {
|
|
5012
5412
|
if ((d.platform ?? process.platform) === "win32") return null;
|
|
@@ -5049,11 +5449,11 @@ function defaultAppRunning(platform = process.platform) {
|
|
|
5049
5449
|
var PENDING_MAX = 20;
|
|
5050
5450
|
var PENDING_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
5051
5451
|
function pendingPath() {
|
|
5052
|
-
return
|
|
5452
|
+
return join21(deskbuddyHome(), "workbuddy-pending.json");
|
|
5053
5453
|
}
|
|
5054
5454
|
function loadPendingFile() {
|
|
5055
5455
|
try {
|
|
5056
|
-
const v = JSON.parse(
|
|
5456
|
+
const v = JSON.parse(readFileSync23(pendingPath(), "utf8"));
|
|
5057
5457
|
return Array.isArray(v) ? v.filter((j) => j && typeof j.sessionId === "string" && typeof j.cwd === "string") : [];
|
|
5058
5458
|
} catch {
|
|
5059
5459
|
return [];
|
|
@@ -5065,10 +5465,10 @@ function savePendingFile(jobs) {
|
|
|
5065
5465
|
rmSync12(pendingPath(), { force: true });
|
|
5066
5466
|
return;
|
|
5067
5467
|
}
|
|
5068
|
-
|
|
5468
|
+
mkdirSync20(deskbuddyHome(), { recursive: true });
|
|
5069
5469
|
const tmp = pendingPath() + ".tmp";
|
|
5070
|
-
|
|
5071
|
-
|
|
5470
|
+
writeFileSync20(tmp, JSON.stringify(jobs), { mode: 384 });
|
|
5471
|
+
renameSync14(tmp, pendingPath());
|
|
5072
5472
|
} catch {
|
|
5073
5473
|
}
|
|
5074
5474
|
}
|
|
@@ -5173,7 +5573,7 @@ async function registerNow(job, d) {
|
|
|
5173
5573
|
if (!SESSION_UUID_RE.test(job.sessionId)) return "skip bad_session";
|
|
5174
5574
|
if (!job.cwd || !isAbsolute2(job.cwd)) return "skip bad_cwd";
|
|
5175
5575
|
const db = d.dbPath();
|
|
5176
|
-
if (!db || !
|
|
5576
|
+
if (!db || !existsSync18(db)) return "skip no_db";
|
|
5177
5577
|
const tp = findWbTranscript(d.configDir(), job.sessionId);
|
|
5178
5578
|
if (!tp) return "skip no_transcript";
|
|
5179
5579
|
const info = wbTranscriptInfo(tp);
|
|
@@ -5305,7 +5705,7 @@ var WsCdpClient = class {
|
|
|
5305
5705
|
};
|
|
5306
5706
|
|
|
5307
5707
|
// src/daemon/workbuddyApp/keeper.ts
|
|
5308
|
-
import { posix as posix2, win32 as
|
|
5708
|
+
import { posix as posix2, win32 as win326 } from "path";
|
|
5309
5709
|
var DEFAULT_CDP_PORT = 39217;
|
|
5310
5710
|
var ENV_NAME = "WORKBUDDY_REMOTE_DEBUGGING_PORT";
|
|
5311
5711
|
var RESTART_GAP_MS = 30 * 60 * 1e3;
|
|
@@ -5367,21 +5767,21 @@ function cleanLaunchEnv(src = process.env, shim = shimDir()) {
|
|
|
5367
5767
|
}
|
|
5368
5768
|
var REMEMBER_EXE_MS = 30 * 60 * 1e3;
|
|
5369
5769
|
var AWAY_IDLE_MS = 5 * 60 * 1e3;
|
|
5370
|
-
var keeperThrottleFile = () =>
|
|
5770
|
+
var keeperThrottleFile = () => join22(deskbuddyHome(), "wbapp", "keeper.json");
|
|
5371
5771
|
var filePersist = {
|
|
5372
5772
|
load: () => {
|
|
5373
5773
|
try {
|
|
5374
|
-
return JSON.parse(
|
|
5774
|
+
return JSON.parse(readFileSync24(keeperThrottleFile(), "utf8"));
|
|
5375
5775
|
} catch {
|
|
5376
5776
|
return null;
|
|
5377
5777
|
}
|
|
5378
5778
|
},
|
|
5379
5779
|
save: (s) => {
|
|
5380
5780
|
try {
|
|
5381
|
-
|
|
5781
|
+
mkdirSync21(join22(deskbuddyHome(), "wbapp"), { recursive: true });
|
|
5382
5782
|
const f = keeperThrottleFile();
|
|
5383
|
-
|
|
5384
|
-
|
|
5783
|
+
writeFileSync21(f + ".tmp", JSON.stringify(s));
|
|
5784
|
+
renameSync15(f + ".tmp", f);
|
|
5385
5785
|
} catch {
|
|
5386
5786
|
}
|
|
5387
5787
|
}
|
|
@@ -5413,7 +5813,7 @@ function defaultWorkbuddyAppPath() {
|
|
|
5413
5813
|
}
|
|
5414
5814
|
function isValidAppPath(p, platform = process.platform) {
|
|
5415
5815
|
if (typeof p !== "string" || !p) return false;
|
|
5416
|
-
if (platform === "win32") return
|
|
5816
|
+
if (platform === "win32") return win326.isAbsolute(p) && /^[a-z]:[\\/]/i.test(p) && win326.basename(p).toLowerCase() === "workbuddy.exe";
|
|
5417
5817
|
return posix2.isAbsolute(p) && posix2.basename(p) === "WorkBuddy.app";
|
|
5418
5818
|
}
|
|
5419
5819
|
var HUNG_RESTART_MS = 20 * 60 * 1e3;
|
|
@@ -5561,11 +5961,11 @@ function winMainProc(procs) {
|
|
|
5561
5961
|
return null;
|
|
5562
5962
|
}
|
|
5563
5963
|
function workbuddyBusy(now = Date.now(), dir2 = stateDir(), alive = isAlive) {
|
|
5564
|
-
if (!
|
|
5565
|
-
for (const f of
|
|
5964
|
+
if (!existsSync19(dir2)) return false;
|
|
5965
|
+
for (const f of readdirSync10(dir2)) {
|
|
5566
5966
|
if (!f.endsWith(".json")) continue;
|
|
5567
5967
|
try {
|
|
5568
|
-
const s = JSON.parse(
|
|
5968
|
+
const s = JSON.parse(readFileSync24(join22(dir2, f), "utf8"));
|
|
5569
5969
|
if (s.tool !== "workbuddy" || s.state !== "watch" && s.state !== "ask_approval") continue;
|
|
5570
5970
|
if (typeof s.pid === "number" && s.pid > 0) {
|
|
5571
5971
|
if (alive(s.pid, s.pidName)) return true;
|
|
@@ -5883,7 +6283,7 @@ var CdpKeeper = class {
|
|
|
5883
6283
|
this.#loadThrottle();
|
|
5884
6284
|
let path = null;
|
|
5885
6285
|
try {
|
|
5886
|
-
const exists = this.#d.exists ??
|
|
6286
|
+
const exists = this.#d.exists ?? existsSync19;
|
|
5887
6287
|
if (this.#appExe && isValidAppPath(this.#appExe, this.#platform()) && exists(this.#appExe)) path = this.#appExe;
|
|
5888
6288
|
else {
|
|
5889
6289
|
const found = await (this.#d.appPath ? this.#d.appPath() : defaultWorkbuddyAppPath());
|
|
@@ -6099,12 +6499,12 @@ var TYPE_TEXT = "text";
|
|
|
6099
6499
|
var TYPE_SUMMARY = "summary";
|
|
6100
6500
|
var MAX_TEXT_LENGTH = 2e3;
|
|
6101
6501
|
var MAX_ID_LENGTH = 64;
|
|
6102
|
-
function
|
|
6502
|
+
function str2(v) {
|
|
6103
6503
|
return typeof v === "string" ? v : "";
|
|
6104
6504
|
}
|
|
6105
6505
|
function parseMillis(v) {
|
|
6106
6506
|
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
6107
|
-
const s =
|
|
6507
|
+
const s = str2(v).trim();
|
|
6108
6508
|
if (!/^\d{1,16}$/.test(s)) return 0;
|
|
6109
6509
|
const n = Number(s);
|
|
6110
6510
|
return Number.isSafeInteger(n) ? n : 0;
|
|
@@ -6117,21 +6517,21 @@ function sanitizeText(raw) {
|
|
|
6117
6517
|
}
|
|
6118
6518
|
function parseCommand(frame) {
|
|
6119
6519
|
const f = frame;
|
|
6120
|
-
if (!f ||
|
|
6520
|
+
if (!f || str2(f.service) !== SERVICE_COMMAND) return null;
|
|
6121
6521
|
const d = f.data;
|
|
6122
6522
|
if (!d || typeof d !== "object") return null;
|
|
6123
|
-
const instructionId =
|
|
6523
|
+
const instructionId = str2(d.instructionId);
|
|
6124
6524
|
if (!isValidId(instructionId)) return null;
|
|
6125
|
-
const type =
|
|
6525
|
+
const type = str2(d.type);
|
|
6126
6526
|
if (type !== TYPE_TEXT && type !== TYPE_SUMMARY) return null;
|
|
6127
|
-
const tool =
|
|
6527
|
+
const tool = str2(d.tool);
|
|
6128
6528
|
if (!isValidId(tool)) return null;
|
|
6129
|
-
const sessionId =
|
|
6529
|
+
const sessionId = str2(d.sessionId);
|
|
6130
6530
|
if (sessionId && !isValidId(sessionId)) return null;
|
|
6131
6531
|
if (type === TYPE_SUMMARY && !sessionId) return null;
|
|
6132
|
-
const text = sanitizeText(
|
|
6532
|
+
const text = sanitizeText(str2(d.text));
|
|
6133
6533
|
if (!text || text.length > MAX_TEXT_LENGTH) return null;
|
|
6134
|
-
const rawDir =
|
|
6534
|
+
const rawDir = str2(d.dirRef);
|
|
6135
6535
|
const dirRef = !sessionId && /^[0-9a-f]{16}$/.test(rawDir) ? rawDir : "";
|
|
6136
6536
|
const cmd = { instructionId, type, sessionId, tool, text, expireAt: parseMillis(d.expireAt), dirRef };
|
|
6137
6537
|
if (type === TYPE_SUMMARY) cmd.kind = "summary";
|
|
@@ -6151,14 +6551,14 @@ function cleanFeedback(v) {
|
|
|
6151
6551
|
}
|
|
6152
6552
|
function parseApprovalFrame(frame) {
|
|
6153
6553
|
const f = frame;
|
|
6154
|
-
if (!f ||
|
|
6554
|
+
if (!f || str2(f.service) !== SERVICE_COMMAND) return null;
|
|
6155
6555
|
const d = f.data;
|
|
6156
|
-
if (!d || typeof d !== "object" ||
|
|
6157
|
-
const instructionId =
|
|
6556
|
+
if (!d || typeof d !== "object" || str2(d.type) !== TYPE_APPROVAL) return null;
|
|
6557
|
+
const instructionId = str2(d.instructionId);
|
|
6158
6558
|
if (!isValidId(instructionId)) return null;
|
|
6159
|
-
const approvalId =
|
|
6559
|
+
const approvalId = str2(d.approvalId);
|
|
6160
6560
|
if (!isValidId(approvalId)) return null;
|
|
6161
|
-
const decision =
|
|
6561
|
+
const decision = str2(d.decision);
|
|
6162
6562
|
if (decision !== "allow" && decision !== "deny" && decision !== "allow_always") return null;
|
|
6163
6563
|
return {
|
|
6164
6564
|
instructionId,
|
|
@@ -6178,6 +6578,11 @@ function optionFields(d) {
|
|
|
6178
6578
|
function cleanDeviceName(v) {
|
|
6179
6579
|
return typeof v === "string" ? v.replace(/[\u0000-\u001f\u007f]/g, "").trim().slice(0, 32) : "";
|
|
6180
6580
|
}
|
|
6581
|
+
var APP_WAIT_MARGIN_MS = 20 * 1e3;
|
|
6582
|
+
function appWaitBudget(expireAt, now, max) {
|
|
6583
|
+
if (!(expireAt > 0)) return max;
|
|
6584
|
+
return Math.min(max, expireAt - now - APP_WAIT_MARGIN_MS);
|
|
6585
|
+
}
|
|
6181
6586
|
function isExpired(cmd, now = Date.now()) {
|
|
6182
6587
|
return cmd.expireAt > 0 && now > cmd.expireAt;
|
|
6183
6588
|
}
|
|
@@ -6408,6 +6813,12 @@ var QoderAppTasks = class {
|
|
|
6408
6813
|
async #ensureReady(cmd, sid, q, report, mayWake) {
|
|
6409
6814
|
if (this.available()) return true;
|
|
6410
6815
|
if (!mayWake || !this.#d.wake || !this.canWake()) return false;
|
|
6816
|
+
const raw = appWaitBudget(cmd.expireAt, this.#d.now(), QODER_APP_WAKE_WAIT_MS);
|
|
6817
|
+
const budget = Math.max(0, raw);
|
|
6818
|
+
if (raw <= 0 && !sid) {
|
|
6819
|
+
this.#d.log(`\u6307\u4EE4 ${cmd.instructionId} \u79BB\u8FC7\u671F\u4E0D\u5230 ${APP_WAIT_MARGIN_MS / 1e3} \u79D2\uFF0C\u4E0D\u7B49 ${this.#d.label} App\uFF0C\u76F4\u63A5\u56DE\u843D`);
|
|
6820
|
+
return false;
|
|
6821
|
+
}
|
|
6411
6822
|
let w;
|
|
6412
6823
|
q.wakeAt = this.#d.now();
|
|
6413
6824
|
try {
|
|
@@ -6425,13 +6836,13 @@ var QoderAppTasks = class {
|
|
|
6425
6836
|
this.#d.log(`\u6307\u4EE4 ${cmd.instructionId} \u7B49 ${this.#d.label} App ${w === "launched" ? "\u6253\u5F00" : w === "restarted" ? "\u5E26\u7AEF\u53E3\u91CD\u542F" : "\u8FDE\u4E0A"}`);
|
|
6426
6837
|
report("queued", sid);
|
|
6427
6838
|
}
|
|
6428
|
-
const end =
|
|
6839
|
+
const end = q.wakeAt + budget;
|
|
6429
6840
|
while (this.#d.now() < end) {
|
|
6430
6841
|
this.#d.kick?.();
|
|
6431
|
-
await this.#sleep(this.#d.pollMs ?? QODER_APP_POLL_MS);
|
|
6842
|
+
await this.#sleep(Math.max(1, Math.min(this.#d.pollMs ?? QODER_APP_POLL_MS, end - this.#d.now())));
|
|
6432
6843
|
if (this.available()) return true;
|
|
6433
6844
|
}
|
|
6434
|
-
this.#d.log(`\u6307\u4EE4 ${cmd.instructionId} ${this.#d.label} App ${
|
|
6845
|
+
this.#d.log(`\u6307\u4EE4 ${cmd.instructionId} ${this.#d.label} App ${Math.round(budget / 1e3)} \u79D2\u5185\u6CA1\u8FDE\u4E0A${budget < QODER_APP_WAKE_WAIT_MS ? "\uFF08\u518D\u7B49\u6307\u4EE4\u5C31\u8FC7\u671F\u4E86\uFF09" : ""}`);
|
|
6435
6846
|
return false;
|
|
6436
6847
|
}
|
|
6437
6848
|
/** 只给测试和诊断用 */
|
|
@@ -6719,6 +7130,149 @@ var QoderAppTasks = class {
|
|
|
6719
7130
|
}
|
|
6720
7131
|
};
|
|
6721
7132
|
|
|
7133
|
+
// src/core/summaryTools.ts
|
|
7134
|
+
var SUMMARY_TOOLS = ["claude_code", "codex", "qoder", "qoder_cn", "opencode", "workbuddy", "qwenwork", "dumate", "deepseek_harness"];
|
|
7135
|
+
var SUMMARY_CAPS = SUMMARY_TOOLS.map((t) => `summary:${t}`);
|
|
7136
|
+
|
|
7137
|
+
// src/daemon/summary.ts
|
|
7138
|
+
var SUMMARY_TOOLS2 = SUMMARY_TOOLS;
|
|
7139
|
+
var SUMMARY_NO_OUTPUT = "no_output";
|
|
7140
|
+
var SUMMARY_TIMEOUT = "timeout";
|
|
7141
|
+
function isSummary(cmd) {
|
|
7142
|
+
return cmd.kind === "summary";
|
|
7143
|
+
}
|
|
7144
|
+
function summaryMark(instructionId) {
|
|
7145
|
+
return `[DeskBuddy#${instructionId}]`;
|
|
7146
|
+
}
|
|
7147
|
+
function withSummaryMark(text, instructionId) {
|
|
7148
|
+
if (text.includes(`[DeskBuddy#${instructionId}]`) || text.includes(`[DeskBuddy#${instructionId}:`)) return text;
|
|
7149
|
+
return `${text}
|
|
7150
|
+
${summaryMark(instructionId)}`;
|
|
7151
|
+
}
|
|
7152
|
+
function clipOutput(text) {
|
|
7153
|
+
return text.length > COMMAND_OUTPUT_MAX ? text.slice(0, COMMAND_OUTPUT_MAX) : text;
|
|
7154
|
+
}
|
|
7155
|
+
var BUSY = /* @__PURE__ */ new Set(["watch", "ask_approval"]);
|
|
7156
|
+
var PTY_SETTLE_MS = 1500;
|
|
7157
|
+
var SUMMARY_WAIT_MS = 4 * 60 * 1e3;
|
|
7158
|
+
var PTY_SUMMARY_WAIT_MS = SUMMARY_WAIT_MS;
|
|
7159
|
+
function ptyVerdict(job, st, tail, now) {
|
|
7160
|
+
if (now - job.startedAt > PTY_SUMMARY_WAIT_MS) return { kind: "fail", reason: SUMMARY_TIMEOUT };
|
|
7161
|
+
if (!st) return { kind: "fail", reason: "session_gone" };
|
|
7162
|
+
if (!tail || tail.id !== job.instructionId) return { kind: "wait" };
|
|
7163
|
+
if (tail.humanAfter || tail.interrupted) return { kind: "fail", reason: "exec_failed" };
|
|
7164
|
+
if (!tail.answered) return { kind: "wait" };
|
|
7165
|
+
if (BUSY.has(st.state) || now - st.updatedAt < PTY_SETTLE_MS) return { kind: "wait" };
|
|
7166
|
+
const text = (tail.reply ?? "").trim();
|
|
7167
|
+
return text ? { kind: "done", text } : { kind: "fail", reason: SUMMARY_NO_OUTPUT };
|
|
7168
|
+
}
|
|
7169
|
+
function hasSummaryMark(text, instructionId) {
|
|
7170
|
+
return text.includes(`[DeskBuddy#${instructionId}]`) || text.includes(`[DeskBuddy#${instructionId}:`);
|
|
7171
|
+
}
|
|
7172
|
+
function messagesVerdict(list, instructionId, idle) {
|
|
7173
|
+
if (!Array.isArray(list)) return { kind: "wait" };
|
|
7174
|
+
const msgs = list;
|
|
7175
|
+
const textOf = (m) => Array.isArray(m.parts) ? m.parts.filter((p) => p && p.type === "text" && typeof p.text === "string" && p.synthetic !== true).map((p) => p.text).join("\n").trim() : "";
|
|
7176
|
+
let at = -1;
|
|
7177
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
7178
|
+
const m = msgs[i];
|
|
7179
|
+
if (m && m.info?.role === "user" && hasSummaryMark(textOf(m), instructionId)) {
|
|
7180
|
+
at = i;
|
|
7181
|
+
break;
|
|
7182
|
+
}
|
|
7183
|
+
}
|
|
7184
|
+
if (at < 0) return { kind: "wait" };
|
|
7185
|
+
const after = msgs.slice(at + 1).filter((m) => m && m.info);
|
|
7186
|
+
if (after.some((m) => m.info?.role === "user")) return { kind: "fail", reason: "exec_failed" };
|
|
7187
|
+
const markId = typeof msgs[at].info?.id === "string" ? msgs[at].info?.id : "";
|
|
7188
|
+
const replies = after.filter((m) => m.info?.role === "assistant" && (!markId || typeof m.info.parentID !== "string" || m.info.parentID === markId));
|
|
7189
|
+
if (!idle || replies.length === 0) return { kind: "wait" };
|
|
7190
|
+
const last = replies[replies.length - 1];
|
|
7191
|
+
if (!last.info?.time || last.info.time.completed == null) return { kind: "wait" };
|
|
7192
|
+
for (let i = replies.length - 1; i >= 0; i--) {
|
|
7193
|
+
const t = textOf(replies[i]);
|
|
7194
|
+
if (t) return { kind: "done", text: t };
|
|
7195
|
+
}
|
|
7196
|
+
return { kind: "fail", reason: SUMMARY_NO_OUTPUT };
|
|
7197
|
+
}
|
|
7198
|
+
function replyVerdict(r) {
|
|
7199
|
+
if (!r || typeof r !== "object") return { kind: "wait" };
|
|
7200
|
+
const v = r;
|
|
7201
|
+
if (v.ok === true) {
|
|
7202
|
+
const t = typeof v.text === "string" ? v.text.trim() : "";
|
|
7203
|
+
return t ? { kind: "done", text: t } : { kind: "fail", reason: SUMMARY_NO_OUTPUT };
|
|
7204
|
+
}
|
|
7205
|
+
const reason = typeof v.reason === "string" && /^[a-z_]{1,32}$/.test(v.reason) ? v.reason : "exec_failed";
|
|
7206
|
+
return { kind: "fail", reason };
|
|
7207
|
+
}
|
|
7208
|
+
var PtySummaryWatch = class {
|
|
7209
|
+
#d;
|
|
7210
|
+
#jobs = /* @__PURE__ */ new Map();
|
|
7211
|
+
constructor(deps) {
|
|
7212
|
+
this.#d = deps;
|
|
7213
|
+
}
|
|
7214
|
+
get size() {
|
|
7215
|
+
return this.#jobs.size;
|
|
7216
|
+
}
|
|
7217
|
+
has(instructionId) {
|
|
7218
|
+
return this.#jobs.has(instructionId);
|
|
7219
|
+
}
|
|
7220
|
+
/** 交出去(started / queued)时登记;handedAt = 交出去的时刻(缺省现在)。同一条重复登记保留最早的时刻 */
|
|
7221
|
+
begin(cmd, sessionId, handedAt, check) {
|
|
7222
|
+
const prev = this.#jobs.get(cmd.instructionId);
|
|
7223
|
+
this.#jobs.set(cmd.instructionId, { cmd, sessionId, startedAt: prev?.startedAt ?? handedAt ?? this.#d.now(), check: check ?? prev?.check });
|
|
7224
|
+
}
|
|
7225
|
+
drop(instructionId) {
|
|
7226
|
+
this.#jobs.delete(instructionId);
|
|
7227
|
+
}
|
|
7228
|
+
#settle(id, job, v) {
|
|
7229
|
+
if (v.kind === "wait" || this.#jobs.get(id) !== job) return;
|
|
7230
|
+
this.#jobs.delete(id);
|
|
7231
|
+
if (v.kind === "done") this.#d.done(job.cmd, job.sessionId, v.text);
|
|
7232
|
+
else this.#d.fail(job.cmd, job.sessionId, v.reason);
|
|
7233
|
+
}
|
|
7234
|
+
tick() {
|
|
7235
|
+
const now = this.#d.now();
|
|
7236
|
+
for (const [id, job] of [...this.#jobs]) {
|
|
7237
|
+
if (job.check) {
|
|
7238
|
+
if (now - job.startedAt > PTY_SUMMARY_WAIT_MS) {
|
|
7239
|
+
this.#settle(id, job, { kind: "fail", reason: SUMMARY_TIMEOUT });
|
|
7240
|
+
continue;
|
|
7241
|
+
}
|
|
7242
|
+
if (job.checking) continue;
|
|
7243
|
+
let r;
|
|
7244
|
+
try {
|
|
7245
|
+
r = job.check({ instructionId: id, sessionId: job.sessionId, startedAt: job.startedAt }, now);
|
|
7246
|
+
} catch {
|
|
7247
|
+
continue;
|
|
7248
|
+
}
|
|
7249
|
+
if (r && typeof r.then === "function") {
|
|
7250
|
+
job.checking = true;
|
|
7251
|
+
void r.then(
|
|
7252
|
+
(v) => {
|
|
7253
|
+
job.checking = false;
|
|
7254
|
+
this.#settle(id, job, v);
|
|
7255
|
+
},
|
|
7256
|
+
() => {
|
|
7257
|
+
job.checking = false;
|
|
7258
|
+
}
|
|
7259
|
+
);
|
|
7260
|
+
} else this.#settle(id, job, r);
|
|
7261
|
+
continue;
|
|
7262
|
+
}
|
|
7263
|
+
let st = null;
|
|
7264
|
+
let tail = null;
|
|
7265
|
+
try {
|
|
7266
|
+
st = this.#d.readState(job.sessionId);
|
|
7267
|
+
const path = st ? this.#d.transcriptOf(st) : null;
|
|
7268
|
+
tail = path ? this.#d.analyze(path) : null;
|
|
7269
|
+
} catch {
|
|
7270
|
+
}
|
|
7271
|
+
this.#settle(id, job, ptyVerdict({ instructionId: id, startedAt: job.startedAt }, st, tail, now));
|
|
7272
|
+
}
|
|
7273
|
+
}
|
|
7274
|
+
};
|
|
7275
|
+
|
|
6722
7276
|
// src/daemon/dumateWatch.ts
|
|
6723
7277
|
var APP_NOT_RUNNING = "app_not_running";
|
|
6724
7278
|
function unavailableMessage(reason) {
|
|
@@ -6772,6 +7326,7 @@ function isRefused(e) {
|
|
|
6772
7326
|
return code(e) === "ECONNREFUSED" || code(e?.cause) === "ECONNREFUSED";
|
|
6773
7327
|
}
|
|
6774
7328
|
var REPLY_HISTORY_LIMIT = 10;
|
|
7329
|
+
var SUMMARY_HISTORY_LIMIT = 100;
|
|
6775
7330
|
function pickOfInfo(info) {
|
|
6776
7331
|
const i = info && typeof info === "object" ? info : null;
|
|
6777
7332
|
if (!i || i.role !== "user") return null;
|
|
@@ -6918,17 +7473,17 @@ var hasArg = (cc) => Object.values(cc.tool_input).some((v) => typeof v === "stri
|
|
|
6918
7473
|
function dumateAskCc(p, call2) {
|
|
6919
7474
|
if (call2 && hasArg(call2.cc)) return call2.cc;
|
|
6920
7475
|
const md = p.metadata && typeof p.metadata === "object" ? p.metadata : {};
|
|
6921
|
-
const
|
|
6922
|
-
const file3 =
|
|
6923
|
-
const dir2 =
|
|
7476
|
+
const str3 = (v) => typeof v === "string" ? v : "";
|
|
7477
|
+
const file3 = str3(md.filepath) || str3(md.filePath) || str3(md.hostPath);
|
|
7478
|
+
const dir2 = str3(md.parentDir) || str3(md.hostParentDir);
|
|
6924
7479
|
const patterns = Array.isArray(p.patterns) ? p.patterns.filter((x) => typeof x === "string").join(" ") : "";
|
|
6925
7480
|
if (call2 && call2.tool) {
|
|
6926
|
-
const cc = toCcTool(call2.tool, { filePath: file3, command:
|
|
7481
|
+
const cc = toCcTool(call2.tool, { filePath: file3, command: str3(md.command), path: dir2 || patterns, pattern: str3(md.pattern), url: str3(md.url) });
|
|
6927
7482
|
if (hasArg(cc)) return cc;
|
|
6928
7483
|
}
|
|
6929
7484
|
if (p.permission === "external_directory") {
|
|
6930
7485
|
const display = p.display && typeof p.display === "object" ? p.display : {};
|
|
6931
|
-
return { tool_name:
|
|
7486
|
+
return { tool_name: str3(display.title) || "\u8BBF\u95EE\u5DE5\u4F5C\u533A\u5916\u76EE\u5F55", tool_input: { path: file3 || dir2 || patterns } };
|
|
6932
7487
|
}
|
|
6933
7488
|
return askToCc(p);
|
|
6934
7489
|
}
|
|
@@ -6936,7 +7491,7 @@ function scanSessionDirs(root, now, maxAgeMs = ACTIVE_DIR_MS) {
|
|
|
6936
7491
|
const out = [];
|
|
6937
7492
|
const list = (p) => {
|
|
6938
7493
|
try {
|
|
6939
|
-
return
|
|
7494
|
+
return readdirSync11(p, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join23(p, d.name));
|
|
6940
7495
|
} catch {
|
|
6941
7496
|
return [];
|
|
6942
7497
|
}
|
|
@@ -6945,7 +7500,7 @@ function scanSessionDirs(root, now, maxAgeMs = ACTIVE_DIR_MS) {
|
|
|
6945
7500
|
for (const day of list(u).sort().reverse().slice(0, 2))
|
|
6946
7501
|
for (const dir2 of list(day)) {
|
|
6947
7502
|
try {
|
|
6948
|
-
const at =
|
|
7503
|
+
const at = statSync10(dir2).mtimeMs;
|
|
6949
7504
|
if (now - at <= maxAgeMs) out.push({ dir: dir2, at });
|
|
6950
7505
|
} catch {
|
|
6951
7506
|
}
|
|
@@ -7088,14 +7643,14 @@ var DumateWatch = class {
|
|
|
7088
7643
|
return this.#d.alt.subscribe;
|
|
7089
7644
|
}
|
|
7090
7645
|
/** 设备新建 / 回复用的目标:可用就用;不可用按规则唤起并等连上(ensure);还是不行返回原因码 */
|
|
7091
|
-
async #ensureTarget() {
|
|
7646
|
+
async #ensureTarget(expireAt = 0) {
|
|
7092
7647
|
const pick = () => this.#discover(false);
|
|
7093
7648
|
let t = this.#target ?? pick();
|
|
7094
7649
|
if (t) return t;
|
|
7095
7650
|
let why = APP_NOT_RUNNING;
|
|
7096
7651
|
if (this.#d.ensure) {
|
|
7097
7652
|
try {
|
|
7098
|
-
why = await this.#d.ensure();
|
|
7653
|
+
why = await this.#d.ensure(expireAt);
|
|
7099
7654
|
} catch {
|
|
7100
7655
|
why = APP_NOT_RUNNING;
|
|
7101
7656
|
}
|
|
@@ -7110,12 +7665,12 @@ var DumateWatch = class {
|
|
|
7110
7665
|
* model / agent 取法同回复:没有历史,直接 GET /config 的 model + agent='build'。
|
|
7111
7666
|
* 幂等:同一 instructionId 在本进程里只建一次(跨重启靠 daemon 的 seen 去重);/session 不收客户端指定 id,所以不做确定性 id
|
|
7112
7667
|
*/
|
|
7113
|
-
async startTask(cwd, text, instructionId) {
|
|
7668
|
+
async startTask(cwd, text, instructionId, expireAt = 0) {
|
|
7114
7669
|
const notSent = (reason, message) => ({ status: "not_sent", reason, message });
|
|
7115
7670
|
if (this.#d.enabled && !this.#d.enabled()) return notSent("tool_unsupported", "\u767E\u5EA6\u642D\u5B50\u6CA1\u6709\u63A5\u5165");
|
|
7116
7671
|
const existing = instructionId ? this.#newTasks.get(instructionId) : void 0;
|
|
7117
7672
|
if (existing) return { status: "started", sessionId: existing };
|
|
7118
|
-
const got = await this.#ensureTarget();
|
|
7673
|
+
const got = await this.#ensureTarget(expireAt);
|
|
7119
7674
|
if (typeof got === "string") return notSent(got, unavailableMessage(got));
|
|
7120
7675
|
const t = got;
|
|
7121
7676
|
const http = this.#http(t);
|
|
@@ -7144,15 +7699,34 @@ var DumateWatch = class {
|
|
|
7144
7699
|
if (r.status >= 400 && r.status < 500) return { status: "failed", reason: "window_failed", message: `\u5EFA\u4E86\u4F1A\u8BDD\u4F46\u7B2C\u4E00\u53E5\u88AB\u62D2\uFF08HTTP ${r.status}\uFF09`, sessionId: sid };
|
|
7145
7700
|
return { status: "failed", reason: "window_failed", message: r.status === 0 ? "\u5EFA\u4E86\u4F1A\u8BDD\u4F46\u53D1\u7B2C\u4E00\u53E5\u65F6\u8FDE\u63A5\u65AD\u5F00\u6216\u8D85\u65F6" : `\u5EFA\u4E86\u4F1A\u8BDD\u4F46\u767E\u5EA6\u642D\u5B50\u51FA\u9519\uFF08HTTP ${r.status}\uFF09`, sessionId: sid };
|
|
7146
7701
|
}
|
|
7702
|
+
/**
|
|
7703
|
+
* 任务总结的读回(spec 2026-10-11 summary-all-tools):GET /session/:id/message + /session/status,按标记认本轮(messagesVerdict)。
|
|
7704
|
+
* 连不上 / 读不出 → 等(总超时由调用方判);不唤起 App
|
|
7705
|
+
*/
|
|
7706
|
+
async summaryCheck(sessionId, instructionId) {
|
|
7707
|
+
const wait = { kind: "wait" };
|
|
7708
|
+
const t = this.#target ?? this.#discover(false);
|
|
7709
|
+
if (!t) return wait;
|
|
7710
|
+
const http = this.#http(t);
|
|
7711
|
+
const dir2 = this.#sessionDir.get(sessionId) ?? "";
|
|
7712
|
+
const sid = encodeURIComponent(sessionId);
|
|
7713
|
+
const msgs = await http(t, { method: "GET", path: `/session/${sid}/message?limit=${SUMMARY_HISTORY_LIMIT}`, ...dir2 ? { directory: dir2 } : {} });
|
|
7714
|
+
if (msgs.status === 404) return { kind: "fail", reason: "session_gone" };
|
|
7715
|
+
if (msgs.status !== 200) return wait;
|
|
7716
|
+
const st = await http(t, { method: "GET", path: "/session/status", ...dir2 ? { directory: dir2 } : {} });
|
|
7717
|
+
if (st.status !== 200 || !st.body || typeof st.body !== "object") return wait;
|
|
7718
|
+
const type = st.body[sessionId]?.type;
|
|
7719
|
+
return messagesVerdict(msgs.body, instructionId, type !== "busy" && type !== "preparing" && type !== "retry");
|
|
7720
|
+
}
|
|
7147
7721
|
/**
|
|
7148
7722
|
* 设备回复:往百度搭子里这个会话发一轮(spec 2026-10-08 dumate-qwenwork-reply §3.1)。界面订着同一个 serve 的事件流,实时显示(10-08 实测)。
|
|
7149
7723
|
* 请求体与界面自己发消息一致:带这个会话最后一条用户消息的 agent / model(没有就 build + /config 的 model)。
|
|
7150
7724
|
* 先查 /session/status 判忙闲(busy / preparing / retry → queued),再 POST prompt_async(带会话目录)
|
|
7151
7725
|
*/
|
|
7152
|
-
async deliverReply(sessionId, text) {
|
|
7726
|
+
async deliverReply(sessionId, text, expireAt = 0) {
|
|
7153
7727
|
const notSent = (reason, message) => ({ status: "not_sent", reason, message });
|
|
7154
7728
|
if (this.#d.enabled && !this.#d.enabled()) return notSent("tool_unsupported", "\u767E\u5EA6\u642D\u5B50\u6CA1\u6709\u63A5\u5165");
|
|
7155
|
-
const got = await this.#ensureTarget();
|
|
7729
|
+
const got = await this.#ensureTarget(expireAt);
|
|
7156
7730
|
if (typeof got === "string") return notSent(got, unavailableMessage(got));
|
|
7157
7731
|
const t = got;
|
|
7158
7732
|
const http = this.#http(t);
|
|
@@ -7567,7 +8141,7 @@ var DumateWatch = class {
|
|
|
7567
8141
|
const now = this.#now();
|
|
7568
8142
|
if (now - this.#scanAt >= SCAN_DIRS_MS) {
|
|
7569
8143
|
this.#scanAt = now;
|
|
7570
|
-
for (const d of (this.#d.sessionDirs ?? (() => scanSessionDirs(
|
|
8144
|
+
for (const d of (this.#d.sessionDirs ?? (() => scanSessionDirs(join23(qianfanHome(), "workspace", "sessions"), now)))()) {
|
|
7571
8145
|
if (!this.#dirs.has(dirKey(d))) this.#touchDir(d, now);
|
|
7572
8146
|
}
|
|
7573
8147
|
}
|
|
@@ -7679,9 +8253,9 @@ function runHook(node, input, agentPid, tool = "dumate") {
|
|
|
7679
8253
|
}
|
|
7680
8254
|
|
|
7681
8255
|
// src/daemon/qoderApp/keeper.ts
|
|
7682
|
-
import { existsSync as
|
|
8256
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync22, readdirSync as readdirSync12, readFileSync as readFileSync25, renameSync as renameSync16, writeFileSync as writeFileSync22 } from "fs";
|
|
7683
8257
|
import { homedir as homedir7 } from "os";
|
|
7684
|
-
import { join as
|
|
8258
|
+
import { join as join24, win32 as win327 } from "path";
|
|
7685
8259
|
var WAKE_LAUNCH_GAP_MS2 = 60 * 1e3;
|
|
7686
8260
|
var RESTART_GAP_MS2 = 30 * 60 * 1e3;
|
|
7687
8261
|
var RESTART_DAILY_MAX2 = 3;
|
|
@@ -7693,7 +8267,7 @@ function winBrandMainProc(procs, exe) {
|
|
|
7693
8267
|
for (const p of procs) {
|
|
7694
8268
|
const argv = winSplitArgs(p.cmdline);
|
|
7695
8269
|
const first = argv[0] ?? "";
|
|
7696
|
-
if (
|
|
8270
|
+
if (win327.basename(first).toLowerCase() !== want) continue;
|
|
7697
8271
|
const rest = argv.slice(1);
|
|
7698
8272
|
if (rest.some((a) => a.startsWith("--type=") || !a.startsWith("-") || /\.(c|m)?js$/i.test(a))) continue;
|
|
7699
8273
|
return { pid: p.pid, exe: first };
|
|
@@ -7709,7 +8283,7 @@ function winBrandMains(procs, exe) {
|
|
|
7709
8283
|
return out;
|
|
7710
8284
|
}
|
|
7711
8285
|
function isWinAppExe(p, exe) {
|
|
7712
|
-
return typeof p === "string" && /^[a-z]:[\\/]/i.test(p) &&
|
|
8286
|
+
return typeof p === "string" && /^[a-z]:[\\/]/i.test(p) && win327.basename(p).toLowerCase() === exe.toLowerCase();
|
|
7713
8287
|
}
|
|
7714
8288
|
var WIN_SESSION_PS = `$me = (Get-Process -Id $PID).SessionId; $ex = @(Get-Process -Name explorer -ErrorAction SilentlyContinue | ForEach-Object { $_.SessionId }) -join ','; Write-Output ("{0}|{1}" -f $me, $ex)`;
|
|
7715
8289
|
function parseWinSession(out) {
|
|
@@ -7740,11 +8314,11 @@ function parseLsappBundle(out) {
|
|
|
7740
8314
|
return m ? m[1] : null;
|
|
7741
8315
|
}
|
|
7742
8316
|
function toolBusy(tool, now = Date.now(), dir2 = stateDir(), alive = isAlive) {
|
|
7743
|
-
if (!
|
|
7744
|
-
for (const f of
|
|
8317
|
+
if (!existsSync20(dir2)) return false;
|
|
8318
|
+
for (const f of readdirSync12(dir2)) {
|
|
7745
8319
|
if (!f.endsWith(".json")) continue;
|
|
7746
8320
|
try {
|
|
7747
|
-
const s = JSON.parse(
|
|
8321
|
+
const s = JSON.parse(readFileSync25(join24(dir2, f), "utf8"));
|
|
7748
8322
|
if (s.tool !== tool || s.state !== "watch" && s.state !== "ask_approval") continue;
|
|
7749
8323
|
if (typeof s.pid === "number" && s.pid > 0) {
|
|
7750
8324
|
if (alive(s.pid, s.pidName)) return true;
|
|
@@ -7757,7 +8331,7 @@ function toolBusy(tool, now = Date.now(), dir2 = stateDir(), alive = isAlive) {
|
|
|
7757
8331
|
return false;
|
|
7758
8332
|
}
|
|
7759
8333
|
function defaultThrottleFile() {
|
|
7760
|
-
return
|
|
8334
|
+
return join24(deskbuddyHome(), "qoderapp", "keeper.json");
|
|
7761
8335
|
}
|
|
7762
8336
|
var QoderCdpKeeper = class {
|
|
7763
8337
|
#d;
|
|
@@ -7847,7 +8421,7 @@ var QoderCdpKeeper = class {
|
|
|
7847
8421
|
/** 节流状态(按品牌一份,落盘;读不出当没有) */
|
|
7848
8422
|
#loadThrottle() {
|
|
7849
8423
|
try {
|
|
7850
|
-
const all = JSON.parse(
|
|
8424
|
+
const all = JSON.parse(readFileSync25(this.#file(), "utf8"));
|
|
7851
8425
|
const t = all?.[this.#d.brand.tool] ?? {};
|
|
7852
8426
|
const out = { lastLaunch: Number(t.lastLaunch) || 0, lastRestart: Number(t.lastRestart) || 0, day: typeof t.day === "string" ? t.day : "", restarts: Number(t.restarts) || 0 };
|
|
7853
8427
|
if (this.#d.brand.win && isWinAppExe(t.appExe, this.#d.brand.win.exe)) out.appExe = t.appExe;
|
|
@@ -7861,14 +8435,14 @@ var QoderCdpKeeper = class {
|
|
|
7861
8435
|
try {
|
|
7862
8436
|
let all = {};
|
|
7863
8437
|
try {
|
|
7864
|
-
all = JSON.parse(
|
|
8438
|
+
all = JSON.parse(readFileSync25(f, "utf8"));
|
|
7865
8439
|
} catch {
|
|
7866
8440
|
all = {};
|
|
7867
8441
|
}
|
|
7868
8442
|
all[this.#d.brand.tool] = t;
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
8443
|
+
mkdirSync22(join24(f, ".."), { recursive: true });
|
|
8444
|
+
writeFileSync22(f + ".tmp", JSON.stringify(all));
|
|
8445
|
+
renameSync16(f + ".tmp", f);
|
|
7872
8446
|
} catch {
|
|
7873
8447
|
}
|
|
7874
8448
|
}
|
|
@@ -7941,14 +8515,15 @@ var QoderCdpKeeper = class {
|
|
|
7941
8515
|
if (this.#d.appPath) return this.#d.appPath();
|
|
7942
8516
|
if (this.#isWin()) {
|
|
7943
8517
|
const win = this.#d.brand.win;
|
|
7944
|
-
const exists = this.#d.exists ??
|
|
8518
|
+
const exists = this.#d.exists ?? existsSync20;
|
|
7945
8519
|
const t = this.#loadThrottle();
|
|
7946
|
-
|
|
8520
|
+
const located = this.#d.locatedExe ? this.#d.locatedExe() : cachedWinAppExe(this.#d.brand.tool);
|
|
8521
|
+
for (const p of [t.appExe, located, ...win.installPaths?.() ?? []]) if (isWinAppExe(p, win.exe) && exists(p)) return p;
|
|
7947
8522
|
return null;
|
|
7948
8523
|
}
|
|
7949
8524
|
if (this.#platform() !== "darwin") return null;
|
|
7950
8525
|
const name = this.#d.brand.macApp.split("/").pop() ?? "";
|
|
7951
|
-
for (const p of [this.#d.brand.macApp,
|
|
8526
|
+
for (const p of [this.#d.brand.macApp, join24(homedir7(), "Applications", name)]) if (existsSync20(p)) return p;
|
|
7952
8527
|
return null;
|
|
7953
8528
|
}
|
|
7954
8529
|
/** 一轮:健康检查 → 活着核对属主。🔴 平时不重启 App;只有端口无响应满 HUNG_RESTART_MS 时按节流与在场规则重启 */
|
|
@@ -8215,33 +8790,33 @@ var DUMATE_BRAND = {
|
|
|
8215
8790
|
installPaths: () => {
|
|
8216
8791
|
const out = [];
|
|
8217
8792
|
const la = process.env.LOCALAPPDATA;
|
|
8218
|
-
if (la) out.push(
|
|
8219
|
-
for (const pf of [process.env.ProgramFiles, process.env["ProgramFiles(x86)"]]) if (pf) out.push(
|
|
8793
|
+
if (la) out.push(join25(la, "Programs", "DuMate", "DuMate.exe"), join25(la, "Programs", "qianfan-desktop-app", "DuMate.exe"));
|
|
8794
|
+
for (const pf of [process.env.ProgramFiles, process.env["ProgramFiles(x86)"]]) if (pf) out.push(join25(pf, "DuMate", "DuMate.exe"));
|
|
8220
8795
|
return out;
|
|
8221
8796
|
}
|
|
8222
8797
|
}
|
|
8223
8798
|
};
|
|
8224
8799
|
var DM_DOWN_ROUNDS = 2;
|
|
8225
8800
|
var DM_ACTIVE_MS = 10 * 60 * 1e3;
|
|
8226
|
-
function dumateActivity(now = Date.now(), dataHome = dumateDataHome(), sessionsRoot =
|
|
8801
|
+
function dumateActivity(now = Date.now(), dataHome = dumateDataHome(), sessionsRoot = join25(qianfanHome(), "workspace", "sessions")) {
|
|
8227
8802
|
let latest = -1;
|
|
8228
|
-
const xdg =
|
|
8803
|
+
const xdg = join25(dataHome, "qianfan_desk_xdg");
|
|
8229
8804
|
let users = [];
|
|
8230
8805
|
try {
|
|
8231
|
-
users =
|
|
8806
|
+
users = readdirSync13(xdg);
|
|
8232
8807
|
} catch {
|
|
8233
8808
|
users = [];
|
|
8234
8809
|
}
|
|
8235
8810
|
let found = false;
|
|
8236
8811
|
for (const u of users) {
|
|
8237
|
-
const dir2 =
|
|
8812
|
+
const dir2 = join25(xdg, u, "data", "opencode");
|
|
8238
8813
|
try {
|
|
8239
|
-
latest = Math.max(latest,
|
|
8814
|
+
latest = Math.max(latest, statSync11(join25(dir2, "opencode.db")).mtimeMs);
|
|
8240
8815
|
found = true;
|
|
8241
8816
|
} catch {
|
|
8242
8817
|
}
|
|
8243
8818
|
try {
|
|
8244
|
-
const w =
|
|
8819
|
+
const w = statSync11(join25(dir2, "opencode.db-wal"));
|
|
8245
8820
|
if (w.size > 0) latest = Math.max(latest, w.mtimeMs);
|
|
8246
8821
|
} catch {
|
|
8247
8822
|
}
|
|
@@ -8252,7 +8827,7 @@ function dumateActivity(now = Date.now(), dataHome = dumateDataHome(), sessionsR
|
|
|
8252
8827
|
return "idle";
|
|
8253
8828
|
}
|
|
8254
8829
|
function dmKeeperThrottleFile() {
|
|
8255
|
-
return
|
|
8830
|
+
return join25(deskbuddyHome(), "dmapp", "keeper.json");
|
|
8256
8831
|
}
|
|
8257
8832
|
var DumateKeeper = class {
|
|
8258
8833
|
#d;
|
|
@@ -8362,7 +8937,8 @@ var DUMATE_SHORTCUT_APP = {
|
|
|
8362
8937
|
port: dmPort,
|
|
8363
8938
|
wanted: shortcutsWanted,
|
|
8364
8939
|
off: () => loadConfig().dumateShortcuts === false,
|
|
8365
|
-
skipEnv: "DESKBUDDY_SKIP_DM_LNK"
|
|
8940
|
+
skipEnv: "DESKBUDDY_SKIP_DM_LNK",
|
|
8941
|
+
locatedExe: () => cachedWinAppExe("dumate")
|
|
8366
8942
|
};
|
|
8367
8943
|
function ensureDumateShortcuts(d, ps = defaultPs) {
|
|
8368
8944
|
return ensureAppShortcuts(DUMATE_SHORTCUT_APP, d, ps);
|
|
@@ -8433,7 +9009,8 @@ function qoderShortcutApp(tool) {
|
|
|
8433
9009
|
port: () => portOf2(tool),
|
|
8434
9010
|
wanted: () => qoderShortcutsWanted(tool),
|
|
8435
9011
|
off: () => loadConfig().qoderShortcuts === false,
|
|
8436
|
-
skipEnv: "DESKBUDDY_SKIP_QODER_LNK"
|
|
9012
|
+
skipEnv: "DESKBUDDY_SKIP_QODER_LNK",
|
|
9013
|
+
locatedExe: () => cachedWinAppExe(tool)
|
|
8437
9014
|
};
|
|
8438
9015
|
}
|
|
8439
9016
|
function ensureQoderShortcuts(tool, d, ps = defaultPs) {
|
|
@@ -8447,15 +9024,15 @@ function restoreQoderShortcutsSync(tool, d, ps = defaultPsSync) {
|
|
|
8447
9024
|
}
|
|
8448
9025
|
|
|
8449
9026
|
// src/adapters/traeHook.ts
|
|
8450
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
8451
|
-
import { dirname as dirname13, join as
|
|
9027
|
+
import { chmodSync as chmodSync5, existsSync as existsSync21, mkdirSync as mkdirSync23, readFileSync as readFileSync26, realpathSync as realpathSync5, renameSync as renameSync17, rmSync as rmSync13, statSync as statSync12, writeFileSync as writeFileSync23 } from "fs";
|
|
9028
|
+
import { dirname as dirname13, join as join27 } from "path";
|
|
8452
9029
|
var TRAE_EVENTS = ["UserPromptSubmit", "PreToolUse", "PostToolUse", "Stop", "Notification"];
|
|
8453
9030
|
var TRAE_NOTICE_TYPES = ["permission_prompt", "ask_user_question", "document_review", "browser_interaction"];
|
|
8454
9031
|
var TRAE_NOTICE_MATCHER = `^(${TRAE_NOTICE_TYPES.join("|")})$`;
|
|
8455
9032
|
var TRAE_NOTICE_TIMEOUT_S = 7 * 24 * 60 * 60;
|
|
8456
9033
|
function readHooksFile(path) {
|
|
8457
|
-
if (!
|
|
8458
|
-
const raw =
|
|
9034
|
+
if (!existsSync21(path)) return {};
|
|
9035
|
+
const raw = readFileSync26(path, "utf8");
|
|
8459
9036
|
if (raw.trim() === "") return {};
|
|
8460
9037
|
let parsed;
|
|
8461
9038
|
try {
|
|
@@ -8477,19 +9054,19 @@ function writeTarget(path) {
|
|
|
8477
9054
|
}
|
|
8478
9055
|
}
|
|
8479
9056
|
function writeHooksFile(linkPath, f) {
|
|
8480
|
-
|
|
9057
|
+
mkdirSync23(dirname13(linkPath), { recursive: true });
|
|
8481
9058
|
const path = writeTarget(linkPath);
|
|
8482
9059
|
let mode;
|
|
8483
9060
|
try {
|
|
8484
|
-
mode =
|
|
9061
|
+
mode = statSync12(path).mode & 511;
|
|
8485
9062
|
} catch {
|
|
8486
9063
|
mode = void 0;
|
|
8487
9064
|
}
|
|
8488
9065
|
const tmp = path + ".deskbuddy.tmp";
|
|
8489
9066
|
try {
|
|
8490
|
-
|
|
9067
|
+
writeFileSync23(tmp, JSON.stringify(f, null, 2) + "\n", "utf8");
|
|
8491
9068
|
if (mode !== void 0) chmodSync5(tmp, mode);
|
|
8492
|
-
|
|
9069
|
+
renameSync17(tmp, path);
|
|
8493
9070
|
} catch (e) {
|
|
8494
9071
|
try {
|
|
8495
9072
|
rmSync13(tmp, { force: true });
|
|
@@ -8536,7 +9113,7 @@ function strip(f, scripts) {
|
|
|
8536
9113
|
}
|
|
8537
9114
|
function installTraeHook(path, hookPath, events = TRAE_EVENTS, platform = process.platform) {
|
|
8538
9115
|
const f = readHooksFile(path);
|
|
8539
|
-
const gatePath =
|
|
9116
|
+
const gatePath = join27(dirname13(hookPath), "gate.cjs");
|
|
8540
9117
|
const before = JSON.stringify(f);
|
|
8541
9118
|
strip(f, [hookPath, gatePath]);
|
|
8542
9119
|
if (f.version === void 0) f.version = 1;
|
|
@@ -8553,13 +9130,13 @@ function installTraeHook(path, hookPath, events = TRAE_EVENTS, platform = proces
|
|
|
8553
9130
|
if (JSON.stringify(f) !== before) writeHooksFile(path, f);
|
|
8554
9131
|
}
|
|
8555
9132
|
function uninstallTraeHook(path, hookPath) {
|
|
8556
|
-
if (!
|
|
9133
|
+
if (!existsSync21(path)) return;
|
|
8557
9134
|
const f = readHooksFile(path);
|
|
8558
|
-
if (strip(f, [hookPath,
|
|
9135
|
+
if (strip(f, [hookPath, join27(dirname13(hookPath), "gate.cjs")])) writeHooksFile(path, f);
|
|
8559
9136
|
}
|
|
8560
9137
|
function isTraeHookInstalled(path, hookPath) {
|
|
8561
9138
|
try {
|
|
8562
|
-
if (!
|
|
9139
|
+
if (!existsSync21(path)) return false;
|
|
8563
9140
|
const f = readHooksFile(path);
|
|
8564
9141
|
return Object.values(f.hooks ?? {}).some((groups) => (groups ?? []).some((g) => (g?.hooks ?? []).some((h) => mentions2(h?.command, hookPath))));
|
|
8565
9142
|
} catch {
|
|
@@ -8569,19 +9146,19 @@ function isTraeHookInstalled(path, hookPath) {
|
|
|
8569
9146
|
var TRAE_NOTICE = " TRAE\uFF1A\u8BF7\u5728 TRAE \u7684 \u8BBE\u7F6E > Hooks \u91CC\u786E\u8BA4\u300C\u5168\u5C40\u300DHooks \u5DF2\u542F\u7528\u3001\u8FD0\u884C\u65B9\u5F0F\u9009\u300C\u672C\u5730\u81EA\u52A8\u8FD0\u884C\u300D\uFF08\u6C99\u7BB1\u91CC hook \u8FDE\u4E0D\u4E0A\u5B88\u62A4\u8FDB\u7A0B\uFF09\uFF1B\u7535\u8111\u4E0A\u5F39\u6388\u6743\u6846\u65F6\u8BBE\u5907\u4E0A\u4F1A\u51FA\u73B0\u63D0\u793A\u5361\uFF0C\u9700\u56DE\u5230\u7535\u8111\u4E0A\u5904\u7406";
|
|
8570
9147
|
|
|
8571
9148
|
// src/commands/tools.ts
|
|
8572
|
-
import { existsSync as
|
|
9149
|
+
import { existsSync as existsSync23, readFileSync as readFileSync28 } from "fs";
|
|
8573
9150
|
|
|
8574
9151
|
// src/adapters/cbChannel.ts
|
|
8575
|
-
import { chmodSync as chmodSync6, existsSync as
|
|
8576
|
-
import { dirname as dirname14, join as
|
|
9152
|
+
import { chmodSync as chmodSync6, existsSync as existsSync22, lstatSync as lstatSync2, mkdirSync as mkdirSync24, readFileSync as readFileSync27, realpathSync as realpathSync6, renameSync as renameSync18, statSync as statSync13, writeFileSync as writeFileSync24 } from "fs";
|
|
9153
|
+
import { dirname as dirname14, join as join28 } from "path";
|
|
8577
9154
|
var CB_MCP_NAME = "deskbuddy";
|
|
8578
9155
|
var CB_CHANNEL_SCRIPT = "cbchannel.cjs";
|
|
8579
9156
|
function cbChannelScript(hookPath) {
|
|
8580
|
-
return
|
|
9157
|
+
return join28(dirname14(hookPath), CB_CHANNEL_SCRIPT);
|
|
8581
9158
|
}
|
|
8582
9159
|
function read(path) {
|
|
8583
|
-
if (!
|
|
8584
|
-
const raw =
|
|
9160
|
+
if (!existsSync22(path)) return {};
|
|
9161
|
+
const raw = readFileSync27(path, "utf8");
|
|
8585
9162
|
if (raw.trim() === "") return {};
|
|
8586
9163
|
let v;
|
|
8587
9164
|
try {
|
|
@@ -8599,17 +9176,17 @@ function write2(file3, v) {
|
|
|
8599
9176
|
} catch {
|
|
8600
9177
|
path = file3;
|
|
8601
9178
|
}
|
|
8602
|
-
|
|
9179
|
+
mkdirSync24(dirname14(path), { recursive: true });
|
|
8603
9180
|
let mode;
|
|
8604
9181
|
try {
|
|
8605
|
-
mode =
|
|
9182
|
+
mode = statSync13(path).mode & 511;
|
|
8606
9183
|
} catch {
|
|
8607
9184
|
mode = void 0;
|
|
8608
9185
|
}
|
|
8609
9186
|
const tmp = path + ".deskbuddy.tmp";
|
|
8610
|
-
|
|
9187
|
+
writeFileSync24(tmp, JSON.stringify(v, null, 2), "utf8");
|
|
8611
9188
|
if (mode !== void 0) chmodSync6(tmp, mode);
|
|
8612
|
-
|
|
9189
|
+
renameSync18(tmp, path);
|
|
8613
9190
|
}
|
|
8614
9191
|
function ours(entry) {
|
|
8615
9192
|
if (!entry || typeof entry !== "object") return false;
|
|
@@ -8628,7 +9205,7 @@ function installCbChannel(mcpPath, scriptPath, nodePath = CB_NODE) {
|
|
|
8628
9205
|
write2(mcpPath, f);
|
|
8629
9206
|
}
|
|
8630
9207
|
function uninstallCbChannel(mcpPath) {
|
|
8631
|
-
if (!
|
|
9208
|
+
if (!existsSync22(mcpPath)) return;
|
|
8632
9209
|
const f = read(mcpPath);
|
|
8633
9210
|
const servers = f.mcpServers;
|
|
8634
9211
|
if (!servers || typeof servers !== "object" || Array.isArray(servers) || !ours(servers[CB_MCP_NAME])) return;
|
|
@@ -8649,8 +9226,8 @@ function codebuddyWanted(d = { standalone: () => standaloneProbe() }) {
|
|
|
8649
9226
|
}
|
|
8650
9227
|
function fileContains(path, needle) {
|
|
8651
9228
|
try {
|
|
8652
|
-
if (!
|
|
8653
|
-
return
|
|
9229
|
+
if (!existsSync23(path)) return false;
|
|
9230
|
+
return readFileSync28(path, "utf8").includes(needle);
|
|
8654
9231
|
} catch {
|
|
8655
9232
|
return false;
|
|
8656
9233
|
}
|
|
@@ -8697,6 +9274,7 @@ var ADAPTERS = {
|
|
|
8697
9274
|
if (tool === "workbuddy") return installCodebuddyTerminalApprovals(gate);
|
|
8698
9275
|
if (!WINDOW_GATE_TOOLS.has(tool)) return false;
|
|
8699
9276
|
installCcWindowGate(p, gate, tool);
|
|
9277
|
+
if (tool === "qoder_cn" || tool === "qoder") installCcPreToolQuestion(p, gate, tool);
|
|
8700
9278
|
return true;
|
|
8701
9279
|
},
|
|
8702
9280
|
// asyncRewake 只有 Claude Code 认(2.1.280 实测);别家当成同步 hook 会一直等
|
|
@@ -8751,7 +9329,7 @@ ADAPTERS["dumate-http"] = {
|
|
|
8751
9329
|
uninstall: () => restoreDumateShortcutsSync({ log: dmLnkLog }),
|
|
8752
9330
|
isInstalled: (p) => {
|
|
8753
9331
|
try {
|
|
8754
|
-
const enabled = JSON.parse(
|
|
9332
|
+
const enabled = JSON.parse(readFileSync28(p, "utf8")).enabledTools;
|
|
8755
9333
|
return Array.isArray(enabled) && enabled.includes("dumate");
|
|
8756
9334
|
} catch {
|
|
8757
9335
|
return false;
|
|
@@ -8772,7 +9350,7 @@ ADAPTERS["trae-hooks"] = {
|
|
|
8772
9350
|
extraConfigPaths: (tool, purpose) => {
|
|
8773
9351
|
if (tool !== "trae") return [];
|
|
8774
9352
|
const primary = pathResolve(traeHooksPath());
|
|
8775
|
-
return traeHooksPaths().filter((p) => pathResolve(p) !== primary && (purpose === "uninstall" ||
|
|
9353
|
+
return traeHooksPaths().filter((p) => pathResolve(p) !== primary && (purpose === "uninstall" || existsSync23(pathDirname(p))));
|
|
8776
9354
|
}
|
|
8777
9355
|
};
|
|
8778
9356
|
var COZE_NOTICE = " \u6263\u5B50\u4E0D\u9700\u8981\u6539\u5B83\u7684\u4EFB\u4F55\u914D\u7F6E\uFF1A\u5B88\u62A4\u8FDB\u7A0B\u5728\u8DD1\u65F6\u4F1A\u8BA9\u6263\u5B50\u5E26\u8C03\u8BD5\u7AEF\u53E3\u542F\u52A8\uFF08\u4F60\u4E0D\u5728\u7535\u8111\u524D\u4E14\u6263\u5B50\u7A7A\u95F2\u65F6\u624D\u4F1A\u91CD\u5F00\u5B83\uFF09\uFF0C\u72B6\u6001\u548C\u5F85\u786E\u8BA4\u90FD\u4F1A\u63A8\u5230\u8BBE\u5907";
|
|
@@ -8784,7 +9362,7 @@ ADAPTERS["coze-cdp"] = {
|
|
|
8784
9362
|
},
|
|
8785
9363
|
isInstalled: (p) => {
|
|
8786
9364
|
try {
|
|
8787
|
-
const enabled = JSON.parse(
|
|
9365
|
+
const enabled = JSON.parse(readFileSync28(p, "utf8")).enabledTools;
|
|
8788
9366
|
return Array.isArray(enabled) && enabled.includes("coze");
|
|
8789
9367
|
} catch {
|
|
8790
9368
|
return false;
|
|
@@ -8815,9 +9393,9 @@ function subscribedEvents(registry, tool, fallback) {
|
|
|
8815
9393
|
}
|
|
8816
9394
|
|
|
8817
9395
|
// src/wrap/pty.ts
|
|
8818
|
-
import { chmodSync as chmodSync7, existsSync as
|
|
9396
|
+
import { chmodSync as chmodSync7, existsSync as existsSync24, statSync as statSync14 } from "fs";
|
|
8819
9397
|
import { createRequire as createRequire4 } from "module";
|
|
8820
|
-
import { dirname as dirname15, join as
|
|
9398
|
+
import { dirname as dirname15, join as join29 } from "path";
|
|
8821
9399
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
8822
9400
|
var req = createRequire4(import.meta.url);
|
|
8823
9401
|
var VENDORED_PTY_PLATFORMS = ["darwin-arm64", "darwin-x64", "win32-arm64", "win32-x64"];
|
|
@@ -8827,8 +9405,8 @@ function hasVendoredPty(platform = process.platform, arch = process.arch) {
|
|
|
8827
9405
|
function vendoredPtyDir(from = dirname15(fileURLToPath4(import.meta.url))) {
|
|
8828
9406
|
let dir2 = from;
|
|
8829
9407
|
for (let i = 0; i < 4; i++) {
|
|
8830
|
-
const cand =
|
|
8831
|
-
if (
|
|
9408
|
+
const cand = join29(dir2, "vendor", "node-pty");
|
|
9409
|
+
if (existsSync24(join29(cand, "package.json"))) return cand;
|
|
8832
9410
|
const up = dirname15(dir2);
|
|
8833
9411
|
if (up === dir2) break;
|
|
8834
9412
|
dir2 = up;
|
|
@@ -8838,8 +9416,8 @@ function vendoredPtyDir(from = dirname15(fileURLToPath4(import.meta.url))) {
|
|
|
8838
9416
|
function fixSpawnHelper(ptyDir) {
|
|
8839
9417
|
if (process.platform !== "darwin") return;
|
|
8840
9418
|
try {
|
|
8841
|
-
const helper =
|
|
8842
|
-
if (
|
|
9419
|
+
const helper = join29(ptyDir, "prebuilds", `darwin-${process.arch}`, "spawn-helper");
|
|
9420
|
+
if (existsSync24(helper) && (statSync14(helper).mode & 73) === 0) chmodSync7(helper, 493);
|
|
8843
9421
|
} catch {
|
|
8844
9422
|
}
|
|
8845
9423
|
}
|
|
@@ -8943,10 +9521,10 @@ function accentLine(term, tabRow = /←.*[☐☒].*Submit.*→/) {
|
|
|
8943
9521
|
}
|
|
8944
9522
|
|
|
8945
9523
|
// src/commands/migrateQoder.ts
|
|
8946
|
-
import { dirname as dirname17, join as
|
|
9524
|
+
import { dirname as dirname17, join as join30 } from "path";
|
|
8947
9525
|
|
|
8948
9526
|
// src/core/detect.ts
|
|
8949
|
-
import { existsSync as
|
|
9527
|
+
import { existsSync as existsSync25 } from "fs";
|
|
8950
9528
|
import { dirname as dirname16 } from "path";
|
|
8951
9529
|
|
|
8952
9530
|
// src/daemon/cozeApp/brand.ts
|
|
@@ -9026,7 +9604,7 @@ function toolDetectDirs(tool) {
|
|
|
9026
9604
|
}
|
|
9027
9605
|
var defaultDetectDeps = () => ({
|
|
9028
9606
|
located: (tool) => locate(tool) != null,
|
|
9029
|
-
exists:
|
|
9607
|
+
exists: existsSync25,
|
|
9030
9608
|
env: process.env
|
|
9031
9609
|
});
|
|
9032
9610
|
function callerTool(env) {
|
|
@@ -9047,7 +9625,7 @@ function migrateLegacyQoder(out, detect = () => detectTools()) {
|
|
|
9047
9625
|
try {
|
|
9048
9626
|
const cfg = loadConfig();
|
|
9049
9627
|
const hookPath = installedHookPath();
|
|
9050
|
-
const scripts = [hookPath,
|
|
9628
|
+
const scripts = [hookPath, join30(dirname17(hookPath), "gate.cjs")];
|
|
9051
9629
|
if (!retagCcHook(qoderCnSettingsPath(), scripts, "qoder", "qoder_cn")) return false;
|
|
9052
9630
|
const hadQoder = cfg.enabledTools.includes("qoder");
|
|
9053
9631
|
let intl = false;
|
|
@@ -9076,26 +9654,26 @@ function migrateLegacyQoder(out, detect = () => detectTools()) {
|
|
|
9076
9654
|
|
|
9077
9655
|
// src/daemon/loginGuide.ts
|
|
9078
9656
|
import { execFile as execFile9, execFileSync as execFileSync13 } from "child_process";
|
|
9079
|
-
import { chmodSync as chmodSync9, existsSync as
|
|
9657
|
+
import { chmodSync as chmodSync9, existsSync as existsSync26, mkdirSync as mkdirSync26, readFileSync as readFileSync29, writeFileSync as writeFileSync26 } from "fs";
|
|
9080
9658
|
import { homedir as homedir9 } from "os";
|
|
9081
|
-
import { join as
|
|
9659
|
+
import { join as join34 } from "path";
|
|
9082
9660
|
|
|
9083
9661
|
// src/daemon/executor.ts
|
|
9084
9662
|
import { execFile as execFile7, execFileSync as execFileSync11 } from "child_process";
|
|
9085
9663
|
import { homedir as homedir8 } from "os";
|
|
9086
|
-
import { join as
|
|
9664
|
+
import { join as join32 } from "path";
|
|
9087
9665
|
|
|
9088
9666
|
// src/core/codebuddyDirs.ts
|
|
9089
|
-
import { join as
|
|
9667
|
+
import { join as join31, win32 as win328, posix as posix3 } from "path";
|
|
9090
9668
|
function same(a, b, platform) {
|
|
9091
|
-
const pa = platform === "win32" ?
|
|
9092
|
-
const pb = platform === "win32" ?
|
|
9669
|
+
const pa = platform === "win32" ? win328.resolve(a).toLowerCase() : posix3.resolve(a);
|
|
9670
|
+
const pb = platform === "win32" ? win328.resolve(b).toLowerCase() : posix3.resolve(b);
|
|
9093
9671
|
return pa === pb;
|
|
9094
9672
|
}
|
|
9095
9673
|
function workbuddyRunConfigDir(stateDir2, home, platform = process.platform) {
|
|
9096
|
-
const wb =
|
|
9674
|
+
const wb = join31(home, ".workbuddy");
|
|
9097
9675
|
if (typeof stateDir2 !== "string" || !stateDir2) return wb;
|
|
9098
|
-
const cb =
|
|
9676
|
+
const cb = join31(home, ".codebuddy");
|
|
9099
9677
|
if (same(stateDir2, cb, platform)) return cb;
|
|
9100
9678
|
return wb;
|
|
9101
9679
|
}
|
|
@@ -9187,7 +9765,7 @@ function buildSpawnPlan(cmd, cwd, gate, home = homedir8(), configDir, platform =
|
|
|
9187
9765
|
file: file3,
|
|
9188
9766
|
args: workbuddyPlan(cmd, gate),
|
|
9189
9767
|
cwd,
|
|
9190
|
-
env: { CODEBUDDY_CONFIG_DIR: cmd.sessionId ? workbuddyRunConfigDir(configDir, home, platform) :
|
|
9768
|
+
env: { CODEBUDDY_CONFIG_DIR: cmd.sessionId ? workbuddyRunConfigDir(configDir, home, platform) : join32(home, ".workbuddy") }
|
|
9191
9769
|
};
|
|
9192
9770
|
case "qoder_cn":
|
|
9193
9771
|
case "qoder":
|
|
@@ -9455,9 +10033,9 @@ function killTree(child) {
|
|
|
9455
10033
|
|
|
9456
10034
|
// src/daemon/openWindow.ts
|
|
9457
10035
|
import { execFile as execFile8, execFileSync as execFileSync12, spawn as spawn4 } from "child_process";
|
|
9458
|
-
import { chmodSync as chmodSync8, mkdirSync as
|
|
10036
|
+
import { chmodSync as chmodSync8, mkdirSync as mkdirSync25, rmSync as rmSync14, writeFileSync as writeFileSync25 } from "fs";
|
|
9459
10037
|
import { userInfo } from "os";
|
|
9460
|
-
import { join as
|
|
10038
|
+
import { join as join33 } from "path";
|
|
9461
10039
|
function safePath(p) {
|
|
9462
10040
|
return /^(?:[A-Za-z]:[\\/]|\/)/.test(p) && !/[\r\n\0"'`$%;\u2018-\u201b]/.test(p);
|
|
9463
10041
|
}
|
|
@@ -9556,9 +10134,9 @@ var defaultOpenerDeps = (findWt2) => ({
|
|
|
9556
10134
|
async function openTerminal(t, d) {
|
|
9557
10135
|
checkTarget(t);
|
|
9558
10136
|
if (d.platform === "darwin") {
|
|
9559
|
-
|
|
9560
|
-
const file3 =
|
|
9561
|
-
|
|
10137
|
+
mkdirSync25(launchDir(t.home), { recursive: true, mode: 448 });
|
|
10138
|
+
const file3 = join33(launchDir(t.home), `${t.runId}.command`);
|
|
10139
|
+
writeFileSync25(file3, macCommandScript(t), { encoding: "utf8", mode: 448 });
|
|
9562
10140
|
chmodSync8(file3, 448);
|
|
9563
10141
|
await d.run("open", ["-a", "Terminal", file3]);
|
|
9564
10142
|
return "terminal";
|
|
@@ -9577,7 +10155,7 @@ async function openTerminal(t, d) {
|
|
|
9577
10155
|
function removeCommandFile(home, runId) {
|
|
9578
10156
|
if (!RUN_ID_RE.test(runId)) return;
|
|
9579
10157
|
try {
|
|
9580
|
-
rmSync14(
|
|
10158
|
+
rmSync14(join33(launchDir(home), `${runId}.command`), { force: true });
|
|
9581
10159
|
} catch {
|
|
9582
10160
|
}
|
|
9583
10161
|
}
|
|
@@ -9659,8 +10237,8 @@ var WORKBUDDY_PROBE_ARGS = ["-p", PROBE_TEXT, "--output-format", "json", "--no-s
|
|
|
9659
10237
|
var WORKBUDDY_AUTH_FILE = "Tencent-Cloud.coding-copilot.info";
|
|
9660
10238
|
function workbuddyAuthFile(home, platform, localAppData = "") {
|
|
9661
10239
|
const tail = ["CodeBuddyExtension", "Data", "Public", "auth", WORKBUDDY_AUTH_FILE];
|
|
9662
|
-
if (platform === "darwin") return
|
|
9663
|
-
if (platform === "win32") return localAppData ?
|
|
10240
|
+
if (platform === "darwin") return join34(home, "Library", "Application Support", ...tail);
|
|
10241
|
+
if (platform === "win32") return localAppData ? join34(localAppData, ...tail) : null;
|
|
9664
10242
|
return null;
|
|
9665
10243
|
}
|
|
9666
10244
|
var WORKBUDDY_VIA_APP_NOTE = "WorkBuddy \u5C06\u901A\u8FC7\u684C\u9762 App \u8FD0\u884C\uFF0C\u5728 App \u91CC\u767B\u5F55\u5373\u53EF";
|
|
@@ -9674,7 +10252,7 @@ async function checkWorkbuddy(d) {
|
|
|
9674
10252
|
const auth = d.workbuddyAuthFile?.() ?? null;
|
|
9675
10253
|
if (auth && d.exists?.(auth)) return { tool, state: "logged_in", note: "\u72EC\u7ACB\u7248\u51ED\u636E\u6587\u4EF6\u5728" };
|
|
9676
10254
|
}
|
|
9677
|
-
const env = envFor(d, loc, { CODEBUDDY_CONFIG_DIR:
|
|
10255
|
+
const env = envFor(d, loc, { CODEBUDDY_CONFIG_DIR: join34(d.home, ".workbuddy"), DESKBUDDY_NO_WAKE: "1", [PROBE_ENV]: "1" });
|
|
9678
10256
|
const r = await d.run(loc.file, [...loc.prefixArgs, ...WORKBUDDY_PROBE_ARGS], env, WORKBUDDY_PROBE_TIMEOUT_MS);
|
|
9679
10257
|
const bad = failedRun(r);
|
|
9680
10258
|
if (bad) return { tool, state: "unknown", note: bad };
|
|
@@ -9759,7 +10337,7 @@ var defaultCheckDeps = () => ({
|
|
|
9759
10337
|
home: homedir9(),
|
|
9760
10338
|
codexConfig: () => {
|
|
9761
10339
|
try {
|
|
9762
|
-
return
|
|
10340
|
+
return readFileSync29(join34(codexHome(), "config.toml"), "utf8");
|
|
9763
10341
|
} catch {
|
|
9764
10342
|
return null;
|
|
9765
10343
|
}
|
|
@@ -9769,9 +10347,9 @@ var defaultCheckDeps = () => ({
|
|
|
9769
10347
|
// 与设备任务唤起 App 同一个 locate(locateWorkbuddyAppExe:注册表卸载项 / App Paths / 开始菜单快捷方式…);App 自带 CLI 找得到也算
|
|
9770
10348
|
workbuddyAppInstalled: () => locateWorkbuddyAppExe() !== null || locateWorkbuddyApp() !== null,
|
|
9771
10349
|
workbuddyAuthFile: () => workbuddyAuthFile(homedir9(), process.platform, process.env.LOCALAPPDATA ?? ""),
|
|
9772
|
-
exists: (p) =>
|
|
10350
|
+
exists: (p) => existsSync26(p)
|
|
9773
10351
|
});
|
|
9774
|
-
function daemonCheckDeps(base = defaultLocateDeps(), exeHintsAsync =
|
|
10352
|
+
function daemonCheckDeps(base = defaultLocateDeps(), exeHintsAsync = winWorkbuddyLocatorHints) {
|
|
9775
10353
|
const noPs = { ...base, winAppHints: void 0, winAppExeHints: void 0 };
|
|
9776
10354
|
return {
|
|
9777
10355
|
...defaultCheckDeps(),
|
|
@@ -9873,7 +10451,7 @@ function winLoginScript(cmd) {
|
|
|
9873
10451
|
return lines.join("\r\n") + "\r\n";
|
|
9874
10452
|
}
|
|
9875
10453
|
function loginScriptPath(home, tool, platform) {
|
|
9876
|
-
return
|
|
10454
|
+
return join34(launchDir(home), `login-${tool}.${platform === "win32" ? "ps1" : "command"}`);
|
|
9877
10455
|
}
|
|
9878
10456
|
function winLoginWtArgs(script) {
|
|
9879
10457
|
if (!safePath(script)) throw new Error("\u811A\u672C\u8DEF\u5F84\u91CC\u6709\u4E0D\u80FD\u8FDB\u547D\u4EE4\u884C\u7684\u5B57\u7B26");
|
|
@@ -9910,14 +10488,14 @@ var defaultLoginOpenDeps = () => ({
|
|
|
9910
10488
|
home: deskbuddyHome(),
|
|
9911
10489
|
pathDirs: () => childPath().split(":"),
|
|
9912
10490
|
write: (file3, content) => {
|
|
9913
|
-
|
|
9914
|
-
|
|
10491
|
+
mkdirSync26(join34(file3, ".."), { recursive: true, mode: 448 });
|
|
10492
|
+
writeFileSync26(file3, content, { mode: 448 });
|
|
9915
10493
|
chmodSync9(file3, 448);
|
|
9916
10494
|
}
|
|
9917
10495
|
});
|
|
9918
10496
|
async function installCodebuddyHooks(log) {
|
|
9919
10497
|
try {
|
|
9920
|
-
const { installTools: installTools2 } = await import("./enable-
|
|
10498
|
+
const { installTools: installTools2 } = await import("./enable-SIY4AEF7.js");
|
|
9921
10499
|
installTools2(["workbuddy"], () => {
|
|
9922
10500
|
}, null, { auto: true });
|
|
9923
10501
|
} catch (e) {
|
|
@@ -10102,8 +10680,8 @@ async function autoEnableLoginLog(tools, check, log) {
|
|
|
10102
10680
|
|
|
10103
10681
|
// src/daemon/workbuddySetup.ts
|
|
10104
10682
|
import { execFile as execFile10, spawn as spawn5 } from "child_process";
|
|
10105
|
-
import { existsSync as
|
|
10106
|
-
import { dirname as dirname18, join as
|
|
10683
|
+
import { existsSync as existsSync27 } from "fs";
|
|
10684
|
+
import { dirname as dirname18, join as join35 } from "path";
|
|
10107
10685
|
var DIALOG_TITLE = "DeskBuddy";
|
|
10108
10686
|
var BTN_INSTALL = "\u5B89\u88C5";
|
|
10109
10687
|
var BTN_LATER = "\u6682\u4E0D";
|
|
@@ -10242,9 +10820,9 @@ function npmPlan(d) {
|
|
|
10242
10820
|
for (const f of whichAllIn("npm", d)) {
|
|
10243
10821
|
if (!/\.cmd$/i.test(f)) continue;
|
|
10244
10822
|
const dir2 = dirname18(f);
|
|
10245
|
-
const cli =
|
|
10823
|
+
const cli = join35(dir2, "node_modules", "npm", "bin", "npm-cli.js");
|
|
10246
10824
|
if (!d.exists(cli)) continue;
|
|
10247
|
-
const node =
|
|
10825
|
+
const node = join35(dir2, "node.exe");
|
|
10248
10826
|
return { file: d.exists(node) ? node : d.nodePath, args: [cli, ...NPM_INSTALL_ARGS] };
|
|
10249
10827
|
}
|
|
10250
10828
|
return null;
|
|
@@ -10361,7 +10939,7 @@ async function runOffer(source, d, opts = {}) {
|
|
|
10361
10939
|
}
|
|
10362
10940
|
}
|
|
10363
10941
|
function lockPath() {
|
|
10364
|
-
return
|
|
10942
|
+
return join35(deskbuddyHome(), "workbuddy-setup.lock");
|
|
10365
10943
|
}
|
|
10366
10944
|
var defaultLockDeps = () => ({ path: lockPath(), pid: process.pid, now: () => Date.now(), alive: pidAlive2 });
|
|
10367
10945
|
var WorkbuddyOffer = class {
|
|
@@ -10431,7 +11009,7 @@ function defaultOfferDeps(log) {
|
|
|
10431
11009
|
}),
|
|
10432
11010
|
openLogin: () => defaultOpenLogin(platform),
|
|
10433
11011
|
installHooks: async () => {
|
|
10434
|
-
const { installTools: installTools2 } = await import("./enable-
|
|
11012
|
+
const { installTools: installTools2 } = await import("./enable-SIY4AEF7.js");
|
|
10435
11013
|
installTools2(["workbuddy"], (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`), null, { auto: true });
|
|
10436
11014
|
},
|
|
10437
11015
|
log: (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`),
|
|
@@ -10520,7 +11098,7 @@ function defaultPromptStartDeps(env = process.env) {
|
|
|
10520
11098
|
gui: () => guiAvailable(defaultGuiDeps()),
|
|
10521
11099
|
spawnChild: (source) => {
|
|
10522
11100
|
const cli = process.argv[1];
|
|
10523
|
-
if (!cli || !
|
|
11101
|
+
if (!cli || !existsSync27(cli)) throw new Error("\u627E\u4E0D\u5230 deskbuddy \u81EA\u5DF1\u7684\u5165\u53E3\u6587\u4EF6");
|
|
10524
11102
|
const c = spawn5(process.execPath, [cli, "workbuddy-setup", "--from", source], {
|
|
10525
11103
|
detached: true,
|
|
10526
11104
|
stdio: "ignore",
|
|
@@ -10600,14 +11178,14 @@ function installTools(tools, out, shim = defaultShimEnvForInstall(), opts = {})
|
|
|
10600
11178
|
installed.push(tool);
|
|
10601
11179
|
out(`\u2705 ${tool}\uFF1Ahook \u5DF2\u88C5\u8FDB ${configPath2}\uFF08${events.length} \u4E2A\u4E8B\u4EF6\uFF09`);
|
|
10602
11180
|
try {
|
|
10603
|
-
if (adapter.installWindowGate?.(configPath2,
|
|
11181
|
+
if (adapter.installWindowGate?.(configPath2, join36(dirname19(hookPath), "gate.cjs"), tool)) {
|
|
10604
11182
|
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`);
|
|
10605
11183
|
}
|
|
10606
11184
|
} catch (e) {
|
|
10607
11185
|
out(`\u26A0\uFE0F ${tool}\uFF1A\u8BBE\u5907\u786E\u8BA4\u6388\u6743\u6CA1\u88C5\u4E0A\uFF1A${e.message}`);
|
|
10608
11186
|
}
|
|
10609
11187
|
try {
|
|
10610
|
-
if (adapter.installRewake?.(configPath2,
|
|
11188
|
+
if (adapter.installRewake?.(configPath2, join36(dirname19(hookPath), "wake.cjs"), tool)) {
|
|
10611
11189
|
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`);
|
|
10612
11190
|
}
|
|
10613
11191
|
} catch (e) {
|
|
@@ -10682,6 +11260,7 @@ export {
|
|
|
10682
11260
|
codebuddyConfigDir,
|
|
10683
11261
|
qoderCnSettingsPath,
|
|
10684
11262
|
qwenworkHome,
|
|
11263
|
+
qwenworkDataHome,
|
|
10685
11264
|
qoderSettingsPath,
|
|
10686
11265
|
opencodePluginPath,
|
|
10687
11266
|
installedHookPath,
|
|
@@ -10695,7 +11274,6 @@ export {
|
|
|
10695
11274
|
packageVersion,
|
|
10696
11275
|
installedHookVersion,
|
|
10697
11276
|
syncHookFile,
|
|
10698
|
-
COMMAND_OUTPUT_MAX,
|
|
10699
11277
|
APP_ID,
|
|
10700
11278
|
APP_SECRET,
|
|
10701
11279
|
sign,
|
|
@@ -10731,6 +11309,9 @@ export {
|
|
|
10731
11309
|
accentLine,
|
|
10732
11310
|
scrubAgentEnv,
|
|
10733
11311
|
scrubForCommand,
|
|
11312
|
+
WIN_APP_SPECS,
|
|
11313
|
+
LOCATE_EVERY_MS,
|
|
11314
|
+
winAppLocator,
|
|
10734
11315
|
defaultLocateDeps,
|
|
10735
11316
|
PROBE_TIMEOUT_MS,
|
|
10736
11317
|
PROBE_SHORT_TTL_MS,
|
|
@@ -10859,12 +11440,24 @@ export {
|
|
|
10859
11440
|
cleanFeedback,
|
|
10860
11441
|
parseApprovalFrame,
|
|
10861
11442
|
cleanDeviceName,
|
|
11443
|
+
APP_WAIT_MARGIN_MS,
|
|
11444
|
+
appWaitBudget,
|
|
10862
11445
|
isExpired,
|
|
10863
11446
|
NOT_REGISTERED_RE_SRC,
|
|
10864
11447
|
PROBE,
|
|
10865
11448
|
APP_IN_USE_REASON,
|
|
10866
11449
|
QODER_APP_POLL_MS,
|
|
10867
11450
|
QoderAppTasks,
|
|
11451
|
+
SUMMARY_CAPS,
|
|
11452
|
+
SUMMARY_TOOLS2 as SUMMARY_TOOLS,
|
|
11453
|
+
SUMMARY_NO_OUTPUT,
|
|
11454
|
+
isSummary,
|
|
11455
|
+
withSummaryMark,
|
|
11456
|
+
clipOutput,
|
|
11457
|
+
SUMMARY_WAIT_MS,
|
|
11458
|
+
ptyVerdict,
|
|
11459
|
+
replyVerdict,
|
|
11460
|
+
PtySummaryWatch,
|
|
10868
11461
|
APP_NOT_RUNNING,
|
|
10869
11462
|
DumateWatch,
|
|
10870
11463
|
runHook,
|