deskbuddy 0.2.0-beta.7 → 0.2.0-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/commands/daemon.ts
4
- import { writeFileSync as writeFileSync17, rmSync as rmSync12 } from "fs";
4
+ import { writeFileSync as writeFileSync18, rmSync as rmSync14 } from "fs";
5
+
6
+ // src/core/api.ts
7
+ import { execFileSync } from "child_process";
8
+ import { createHash, randomBytes, randomUUID as randomUUID2 } from "crypto";
9
+ import { readFileSync as readFileSync3 } from "fs";
10
+ import { release } from "os";
5
11
 
6
12
  // src/core/config.ts
7
13
  import { randomUUID } from "crypto";
@@ -89,7 +95,8 @@ function loadConfig() {
89
95
  remoteCwd: parsed.remoteCwd,
90
96
  // 不认识的值一律回落默认,不能让一个手滑写错的配置变成"全放行"或"全拦"
91
97
  approvalPolicy: parsed.approvalPolicy === "all" ? "all" : parsed.approvalPolicy === "commands" ? "commands" : void 0,
92
- codexAppServer: typeof parsed.codexAppServer === "string" ? parsed.codexAppServer : void 0
98
+ codexAppServer: typeof parsed.codexAppServer === "string" ? parsed.codexAppServer : void 0,
99
+ autoUpdate: typeof parsed.autoUpdate === "boolean" ? parsed.autoUpdate : void 0
93
100
  };
94
101
  } catch {
95
102
  return fallback;
@@ -110,16 +117,531 @@ function ensureClientId() {
110
117
  return config.clientId;
111
118
  }
112
119
 
120
+ // src/core/hookFile.ts
121
+ import { copyFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
122
+ import { createRequire } from "module";
123
+ import { dirname, join as join2 } from "path";
124
+ import { fileURLToPath } from "url";
125
+ var here = dirname(fileURLToPath(import.meta.url));
126
+ function packagedHookPath() {
127
+ const candidates = [
128
+ join2(here, "hook.cjs"),
129
+ // dist/cli.js 旁边(发布形态)
130
+ join2(here, "..", "hook", "hook.cjs"),
131
+ // dist/../hook/
132
+ join2(here, "..", "..", "hook", "hook.cjs")
133
+ // src/core/../../hook/(开发形态)
134
+ ];
135
+ for (const c of candidates) {
136
+ if (existsSync2(c)) return c;
137
+ }
138
+ throw new Error("\u627E\u4E0D\u5230\u5305\u5185\u7684 hook.cjs\uFF0C\u5B89\u88C5\u5305\u53EF\u80FD\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5 deskbuddy");
139
+ }
140
+ function packageVersion() {
141
+ const require2 = createRequire(import.meta.url);
142
+ for (const c of ["../package.json", "../../package.json", "../../../package.json"]) {
143
+ try {
144
+ const pkg = require2(c);
145
+ if (pkg?.name === "deskbuddy" && pkg.version) return pkg.version;
146
+ } catch {
147
+ }
148
+ }
149
+ return "0.0.0-dev";
150
+ }
151
+ function hookVersionPath(target = installedHookPath()) {
152
+ return join2(dirname(target), "hook-version");
153
+ }
154
+ function installedHookVersion(target = installedHookPath()) {
155
+ try {
156
+ return readFileSync2(hookVersionPath(target), "utf8").trim() || null;
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+ function atomicCopy(from, to) {
162
+ const tmp = `${to}.${process.pid}.tmp`;
163
+ copyFileSync(from, tmp);
164
+ renameSync2(tmp, to);
165
+ }
166
+ function syncHookFile(opts = {}) {
167
+ const target = opts.target ?? installedHookPath();
168
+ const version = opts.version ?? packageVersion();
169
+ const SIBLINGS = ["gate.cjs", "opencode.mjs", "wake.cjs", "launcher.cjs"];
170
+ const siblingMissing = () => SIBLINGS.some((name) => {
171
+ if (existsSync2(join2(dirname(target), name))) return false;
172
+ try {
173
+ return existsSync2(join2(dirname(opts.source ?? packagedHookPath()), name));
174
+ } catch {
175
+ return false;
176
+ }
177
+ });
178
+ if (installedHookVersion(target) === version && existsSync2(target) && !siblingMissing()) return false;
179
+ const source = opts.source ?? packagedHookPath();
180
+ mkdirSync2(dirname(target), { recursive: true });
181
+ atomicCopy(source, target);
182
+ for (const name of SIBLINGS) {
183
+ const from = join2(dirname(source), name);
184
+ if (existsSync2(from)) atomicCopy(from, join2(dirname(target), name));
185
+ }
186
+ writeFileSync2(hookVersionPath(target), version, "utf8");
187
+ return true;
188
+ }
189
+
190
+ // src/core/api.ts
191
+ var SERVICES = {
192
+ authCode: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyauthcodeapi",
193
+ authToken: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyauthtokenapi",
194
+ sessionReport: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddysessionreportapi",
195
+ toolRegistry: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddytoolregistryapi",
196
+ /** App 扫码确认时调的,CLI 用不到,列在这里只为对照 */
197
+ confirmAuth: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyconfirmauthapi"
198
+ };
199
+ var DIRS_SERVICE = "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyclientdirsreportapi";
200
+ var INSTALL_REDEEM_SERVICE = "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyinstallredeemapi";
201
+ var CMD_RESULT_SERVICE = "com.shifeng.api.service.api.v2.deskbuddy.deskbuddycommandresultapi";
202
+ var APPROVAL_SERVICES = {
203
+ create: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyapprovalcreateapi",
204
+ cancel: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyapprovalcancelapi",
205
+ sync: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyapprovalsyncapi"
206
+ };
207
+ var APP_ID = process.env.DESKBUDDY_APPID || "deskbuddy-cli";
208
+ var APP_SECRET = process.env.DESKBUDDY_APPSECRET || "3aeb6e0afc144e188dd3ede5c7865643";
209
+ var GATEWAY_VERSION = "1.0";
210
+ var DEFAULT_TIMEOUT_MS = 1e4;
211
+ function sign(p) {
212
+ const source = [p.appId, p.appSecret, p.timestamp, p.deviceId, p.deviceName, p.nonce, p.body].join("_");
213
+ return createHash("md5").update(source, "utf8").digest("hex");
214
+ }
215
+ function buildGatewayBody(service, data, requestId) {
216
+ return JSON.stringify({
217
+ service,
218
+ version: GATEWAY_VERSION,
219
+ requestId: requestId || randomUUID2(),
220
+ base: { encode: "false" },
221
+ // data 是业务 JSON 序列化后的字符串
222
+ data: JSON.stringify(data ?? {})
223
+ });
224
+ }
225
+ function buildHeaders(cfg, body, opts = {}) {
226
+ const timestamp = opts.timestamp || String(Date.now());
227
+ const nonce = opts.nonce || randomBytes(8).toString("hex");
228
+ return {
229
+ "Content-Type": "application/json;charset=UTF-8",
230
+ "Content-Length": String(Buffer.byteLength(body)),
231
+ "ShiFeng-AppId": APP_ID,
232
+ "ShiFeng-Timestamp": timestamp,
233
+ "ShiFeng-Device-ID": cfg.clientId,
234
+ "ShiFeng-Device-Name": cfg.hostName,
235
+ "ShiFeng-Nonce": nonce,
236
+ "ShiFeng-Signature": sign({
237
+ appId: APP_ID,
238
+ appSecret: APP_SECRET,
239
+ timestamp,
240
+ deviceId: cfg.clientId,
241
+ deviceName: cfg.hostName,
242
+ nonce,
243
+ body
244
+ })
245
+ // 没有 ShiFeng-Token:鉴权靠 clientId ↔ userId 绑定关系,CLI 不持有用户凭证
246
+ };
247
+ }
248
+ function isSuccessCode(code) {
249
+ return code === "0000";
250
+ }
251
+ var ApiError = class extends Error {
252
+ constructor(code, message) {
253
+ super(`${code}: ${message}`);
254
+ this.code = code;
255
+ this.name = "ApiError";
256
+ }
257
+ code;
258
+ };
259
+ function humanOs(platform = process.platform) {
260
+ const known = {
261
+ darwin: "macOS",
262
+ win32: "Windows",
263
+ linux: "Linux"
264
+ };
265
+ return known[platform] ?? platform;
266
+ }
267
+ var defaultOsVersionDeps = {
268
+ runSwVers: () => execFileSync("sw_vers", ["-productVersion"], {
269
+ encoding: "utf8",
270
+ stdio: ["ignore", "pipe", "ignore"],
271
+ timeout: 2e3
272
+ }),
273
+ readOsRelease: () => readFileSync3("/etc/os-release", "utf8"),
274
+ kernelRelease: () => release()
275
+ };
276
+ function productOsVersion(platform = process.platform, deps = defaultOsVersionDeps) {
277
+ try {
278
+ if (platform === "darwin") {
279
+ const v = deps.runSwVers().trim();
280
+ if (v) return v;
281
+ } else if (platform === "linux") {
282
+ const m = /^PRETTY_NAME=(.*)$/m.exec(deps.readOsRelease());
283
+ if (m) {
284
+ const v = m[1].trim().replace(/^(["'])(.*)\1$/, "$2").trim();
285
+ if (v) return v;
286
+ }
287
+ }
288
+ } catch {
289
+ }
290
+ try {
291
+ return deps.kernelRelease();
292
+ } catch {
293
+ return "";
294
+ }
295
+ }
296
+ var machineInfoCache = null;
297
+ function machineInfo() {
298
+ if (!machineInfoCache) {
299
+ machineInfoCache = {
300
+ os: humanOs(),
301
+ osVersion: productOsVersion(),
302
+ arch: process.arch,
303
+ cliVersion: packageVersion()
304
+ };
305
+ }
306
+ return machineInfoCache;
307
+ }
308
+ async function call(service, data, timeoutMs = DEFAULT_TIMEOUT_MS) {
309
+ const cfg = loadConfig();
310
+ const body = buildGatewayBody(service, data);
311
+ const controller = new AbortController();
312
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
313
+ try {
314
+ const res = await fetch(`${cfg.apiBase}/gateway`, {
315
+ method: "POST",
316
+ headers: buildHeaders(cfg, body),
317
+ body,
318
+ // 与签名用的是同一个字符串
319
+ signal: controller.signal
320
+ });
321
+ const json = await res.json();
322
+ if (!isSuccessCode(json?.code)) {
323
+ throw new ApiError(String(json?.code), json?.message ?? "");
324
+ }
325
+ return json?.data;
326
+ } finally {
327
+ clearTimeout(timer);
328
+ }
329
+ }
330
+ var api = {
331
+ authCode: (clientId, hostName) => call(SERVICES.authCode, { clientId, hostName, ...machineInfo() }),
332
+ authToken: (authCode) => call(SERVICES.authToken, { authCode }),
333
+ toolRegistry: () => call(SERVICES.toolRegistry, {}),
334
+ // 🔴 2 秒超时:这条调用发生在 hook 链路上,慢一点就是拖慢用户的 agent
335
+ report: (payload) => call(SERVICES.sessionReport, payload, 2e3),
336
+ approvalCreate: (payload) => call(APPROVAL_SERVICES.create, payload),
337
+ approvalCancel: (approvalId, reason) => call(APPROVAL_SERVICES.cancel, { approvalId, reason }),
338
+ approvalSync: (approvalIds) => call(APPROVAL_SERVICES.sync, { approvalIds }),
339
+ dirsReport: (payload) => call(DIRS_SERVICE, payload),
340
+ installRedeem: (installCode, clientId, hostName) => call(INSTALL_REDEEM_SERVICE, { installCode, clientId, hostName, ...machineInfo() }),
341
+ // 2 秒超时、失败不重试(spec §4.1):回报丢了只是设备少一句提示,任务状态仍由会话快照负责
342
+ commandResult: (payload) => call(CMD_RESULT_SERVICE, payload, 2e3)
343
+ };
344
+
345
+ // src/core/versions.ts
346
+ import { execFileSync as execFileSync2 } from "child_process";
347
+ import { cpSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync4, renameSync as renameSync3, rmSync, writeFileSync as writeFileSync3 } from "fs";
348
+ import { basename, dirname as dirname2, join as join3, relative, resolve, sep } from "path";
349
+
350
+ // src/core/semver.ts
351
+ var SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?$/;
352
+ var MAX_LEN = 64;
353
+ function isValidVersion(v) {
354
+ return typeof v === "string" && v.length <= MAX_LEN && SEMVER_RE.test(v);
355
+ }
356
+ function parse(v) {
357
+ const m = SEMVER_RE.exec(v);
358
+ if (!m) return null;
359
+ return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] ? m[4].split(".") : [] };
360
+ }
361
+ function cmpIdent(a, b) {
362
+ const an = /^\d+$/.test(a);
363
+ const bn = /^\d+$/.test(b);
364
+ if (an && bn) return Math.sign(Number(a) - Number(b));
365
+ if (an) return -1;
366
+ if (bn) return 1;
367
+ return a < b ? -1 : a > b ? 1 : 0;
368
+ }
369
+ function compareVersions(a, b) {
370
+ const pa = parse(a);
371
+ const pb = parse(b);
372
+ if (!pa || !pb) throw new Error(`\u4E0D\u662F\u5408\u6CD5\u7684\u7248\u672C\u53F7\uFF1A${!pa ? a : b}`);
373
+ for (let i = 0; i < 3; i++) {
374
+ if (pa.nums[i] !== pb.nums[i]) return Math.sign(pa.nums[i] - pb.nums[i]);
375
+ }
376
+ if (pa.pre.length === 0 || pb.pre.length === 0) return pa.pre.length === pb.pre.length ? 0 : pa.pre.length === 0 ? 1 : -1;
377
+ for (let i = 0; i < Math.max(pa.pre.length, pb.pre.length); i++) {
378
+ if (i >= pa.pre.length) return -1;
379
+ if (i >= pb.pre.length) return 1;
380
+ const c = cmpIdent(pa.pre[i], pb.pre[i]);
381
+ if (c !== 0) return c;
382
+ }
383
+ return 0;
384
+ }
385
+ function semverGt(a, b) {
386
+ return compareVersions(a, b) > 0;
387
+ }
388
+ function semverLt(a, b) {
389
+ return compareVersions(a, b) < 0;
390
+ }
391
+
392
+ // src/core/versions.ts
393
+ function versionsDir(home = deskbuddyHome()) {
394
+ return join3(home, "versions");
395
+ }
396
+ function currentJsonPath(home) {
397
+ return join3(versionsDir(home), "current.json");
398
+ }
399
+ function failedJsonPath(home) {
400
+ return join3(versionsDir(home), "failed.json");
401
+ }
402
+ function crashesJsonPath(home) {
403
+ return join3(versionsDir(home), "crashes.json");
404
+ }
405
+ function launcherPath(home = deskbuddyHome()) {
406
+ return join3(home, "launcher.cjs");
407
+ }
408
+ function versionDir(version, home) {
409
+ if (!isValidVersion(version)) throw new Error(`\u4E0D\u662F\u5408\u6CD5\u7684\u7248\u672C\u53F7\uFF1A${version}`);
410
+ return join3(versionsDir(home), version);
411
+ }
412
+ function versionCliPath(version, home) {
413
+ return join3(versionDir(version, home), "node_modules", "deskbuddy", "dist", "cli.js");
414
+ }
415
+ function atomicWriteJson(path, value) {
416
+ mkdirSync3(dirname2(path), { recursive: true });
417
+ const tmp = `${path}.${process.pid}.tmp`;
418
+ writeFileSync3(tmp, JSON.stringify(value, null, 2), "utf8");
419
+ renameSync3(tmp, path);
420
+ }
421
+ function isRef(v) {
422
+ const r = v;
423
+ return !!r && typeof r.version === "string" && typeof r.cliPath === "string" && r.cliPath !== "";
424
+ }
425
+ function readCurrent(home) {
426
+ try {
427
+ const raw = JSON.parse(readFileSync4(currentJsonPath(home), "utf8"));
428
+ if (!isRef(raw)) return null;
429
+ return {
430
+ version: raw.version,
431
+ cliPath: raw.cliPath,
432
+ previous: isRef(raw.previous) ? { version: raw.previous.version, cliPath: raw.previous.cliPath } : null,
433
+ state: raw.state === "verifying" ? "verifying" : "good",
434
+ switchedAt: typeof raw.switchedAt === "number" ? raw.switchedAt : 0
435
+ };
436
+ } catch {
437
+ return null;
438
+ }
439
+ }
440
+ function writeCurrent(cur, home) {
441
+ atomicWriteJson(currentJsonPath(home), cur);
442
+ }
443
+ function readFailed(home) {
444
+ try {
445
+ const raw = JSON.parse(readFileSync4(failedJsonPath(home), "utf8"));
446
+ const versions = Array.isArray(raw.versions) ? raw.versions.filter((v) => typeof v === "string") : [];
447
+ const attempts = {};
448
+ if (raw.attempts && typeof raw.attempts === "object") {
449
+ for (const [k, v] of Object.entries(raw.attempts)) if (typeof v === "number") attempts[k] = v;
450
+ }
451
+ return { versions, attempts };
452
+ } catch {
453
+ return { versions: [], attempts: {} };
454
+ }
455
+ }
456
+ function recordAttemptFailure(version, now, home) {
457
+ const f = readFailed(home);
458
+ f.attempts[version] = now;
459
+ atomicWriteJson(failedJsonPath(home), f);
460
+ }
461
+ function markGood(version, home) {
462
+ const cur = readCurrent(home);
463
+ if (!cur || cur.version !== version || cur.state !== "verifying") return false;
464
+ writeCurrent({ ...cur, state: "good" }, home);
465
+ try {
466
+ rmSync(crashesJsonPath(home), { force: true });
467
+ } catch {
468
+ }
469
+ return true;
470
+ }
471
+ function versionDirOf(cliPath, home) {
472
+ const rel = relative(versionsDir(home), resolve(cliPath));
473
+ if (!rel || rel.startsWith("..") || rel.startsWith(sep) || /^[a-zA-Z]:/.test(rel)) return null;
474
+ const first = rel.split(/[\\/]/)[0];
475
+ return isValidVersion(first) ? first : null;
476
+ }
477
+ function cleanupVersions(cur, home) {
478
+ const keep = /* @__PURE__ */ new Set();
479
+ for (const ref of [cur, cur.previous]) {
480
+ if (!ref) continue;
481
+ const d = versionDirOf(ref.cliPath, home);
482
+ if (d) keep.add(d);
483
+ }
484
+ const removed = [];
485
+ let names = [];
486
+ try {
487
+ names = readdirSync(versionsDir(home), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
488
+ } catch {
489
+ return removed;
490
+ }
491
+ for (const name of names) {
492
+ const ours = isValidVersion(name) || /^\.tmp-/.test(name);
493
+ if (!ours || keep.has(name)) continue;
494
+ try {
495
+ rmSync(join3(versionsDir(home), name), { recursive: true, force: true });
496
+ removed.push(name);
497
+ } catch {
498
+ }
499
+ }
500
+ return removed;
501
+ }
502
+ function cliReportsVersion(cliPath, version, nodePath = process.execPath) {
503
+ try {
504
+ const out = execFileSync2(nodePath, [cliPath, "--version"], {
505
+ encoding: "utf8",
506
+ timeout: 3e4,
507
+ windowsHide: true,
508
+ shell: false,
509
+ stdio: ["ignore", "pipe", "ignore"]
510
+ });
511
+ return out.trim() === version;
512
+ } catch {
513
+ return false;
514
+ }
515
+ }
516
+ function installedPackageVersion(dir) {
517
+ try {
518
+ const pkg = JSON.parse(readFileSync4(join3(dir, "node_modules", "deskbuddy", "package.json"), "utf8"));
519
+ return pkg.name === "deskbuddy" && typeof pkg.version === "string" ? pkg.version : null;
520
+ } catch {
521
+ return null;
522
+ }
523
+ }
524
+ function readPkg(dir) {
525
+ try {
526
+ return JSON.parse(readFileSync4(join3(dir, "package.json"), "utf8"));
527
+ } catch {
528
+ return null;
529
+ }
530
+ }
531
+ function findDepDir(name, fromDir) {
532
+ let d = fromDir;
533
+ for (; ; ) {
534
+ const c = join3(d, "node_modules", name);
535
+ if (existsSync3(join3(c, "package.json"))) return c;
536
+ const up = dirname2(d);
537
+ if (up === d) return null;
538
+ d = up;
539
+ }
540
+ }
541
+ function copyRunningCli(pkgRoot, version, home) {
542
+ const dest = versionDir(version, home);
543
+ const tmp = join3(versionsDir(home), `.tmp-${version}-${process.pid}`);
544
+ rmSync(tmp, { recursive: true, force: true });
545
+ const nm = join3(tmp, "node_modules");
546
+ mkdirSync3(nm, { recursive: true });
547
+ const copied = /* @__PURE__ */ new Set();
548
+ const copyPkg = (name, from) => {
549
+ copied.add(name);
550
+ cpSync(from, join3(nm, name), {
551
+ recursive: true,
552
+ dereference: true,
553
+ // 包自己的嵌套 node_modules 不整个搬:依赖由下面的递归平铺
554
+ filter: (src) => !(src !== from && basename(src) === "node_modules" && dirname2(src) === from)
555
+ });
556
+ };
557
+ copyPkg("deskbuddy", pkgRoot);
558
+ const queue = [{ dir: pkgRoot, pkg: readPkg(pkgRoot) ?? {} }];
559
+ while (queue.length > 0) {
560
+ const { dir, pkg } = queue.shift();
561
+ const deps = [
562
+ ...Object.keys(pkg.dependencies ?? {}).map((n) => [n, false]),
563
+ ...Object.keys(pkg.optionalDependencies ?? {}).map((n) => [n, true])
564
+ ];
565
+ for (const [name, optional] of deps) {
566
+ if (copied.has(name)) continue;
567
+ const found = findDepDir(name, dir);
568
+ if (!found) {
569
+ if (optional) continue;
570
+ rmSync(tmp, { recursive: true, force: true });
571
+ throw new Error(`\u627E\u4E0D\u5230\u4F9D\u8D56 ${name}`);
572
+ }
573
+ copyPkg(name, found);
574
+ queue.push({ dir: found, pkg: readPkg(found) ?? {} });
575
+ }
576
+ }
577
+ rmSync(dest, { recursive: true, force: true });
578
+ renameSync3(tmp, dest);
579
+ return versionCliPath(version, home);
580
+ }
581
+ function registerRunningCli(running, deps = {}) {
582
+ const home = deps.home;
583
+ const now = deps.now ?? Date.now();
584
+ const verify = deps.verify ?? ((p, v) => cliReportsVersion(p, v));
585
+ if (!isValidVersion(running.version) || running.version.startsWith("0.0.0")) {
586
+ throw new Error(`\u5F00\u53D1\u7248\uFF08${running.version}\uFF09\u4E0D\u767B\u8BB0`);
587
+ }
588
+ let cliPath = resolve(running.cliPath);
589
+ if (!existsSync3(cliPath)) throw new Error(`\u5165\u53E3\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${cliPath}`);
590
+ let how = "already";
591
+ let note;
592
+ if (versionDirOf(cliPath, home) !== running.version) {
593
+ const distDir = dirname2(cliPath);
594
+ const pkgRoot = dirname2(distDir);
595
+ const pkg = readPkg(pkgRoot);
596
+ const copyable = basename(distDir) === "dist" && pkg?.name === "deskbuddy" && pkg.version === running.version && !existsSync3(join3(pkgRoot, "src"));
597
+ how = "inPlace";
598
+ if (copyable) {
599
+ const target = versionCliPath(running.version, home);
600
+ try {
601
+ if (!(installedPackageVersion(versionDir(running.version, home)) === running.version && verify(target, running.version))) {
602
+ copyRunningCli(pkgRoot, running.version, home);
603
+ }
604
+ if (verify(target, running.version)) {
605
+ cliPath = target;
606
+ how = "copied";
607
+ } else note = "\u590D\u5236\u51FA\u6765\u7684\u90A3\u4EFD\u8DD1\u4E0D\u8D77\u6765";
608
+ } catch (e) {
609
+ note = `\u590D\u5236\u5931\u8D25\uFF1A${e.message}`;
610
+ }
611
+ } else note = "\u4E0D\u662F\u53D1\u5E03\u5F62\u6001\u7684\u5B89\u88C5\u5305";
612
+ }
613
+ const old = readCurrent(home);
614
+ const from = deps.from && isValidVersion(deps.from.version) && deps.from.version !== running.version && existsSync3(deps.from.cliPath) ? deps.from : null;
615
+ const changed = !!old && old.version !== running.version || !old && !!from;
616
+ const prevRef = old && old.version !== running.version ? { version: old.version, cliPath: old.cliPath } : from;
617
+ const current = {
618
+ version: running.version,
619
+ cliPath,
620
+ previous: changed ? prevRef : old?.previous ?? from,
621
+ // 版本变了一律 verifying(新版起不来要能退回去);同版本重复登记保持原状态
622
+ state: changed ? "verifying" : old && old.cliPath === cliPath ? old.state : from ? "verifying" : "good",
623
+ switchedAt: changed || !old ? now : old.switchedAt
624
+ };
625
+ writeCurrent(current, home);
626
+ if (changed) {
627
+ try {
628
+ rmSync(crashesJsonPath(home), { force: true });
629
+ } catch {
630
+ }
631
+ }
632
+ return { current, how, note };
633
+ }
634
+
113
635
  // src/daemon/autostart.ts
114
636
  import { execFile, spawn } from "child_process";
115
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
637
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
116
638
  import { homedir as homedir2 } from "os";
117
- import { fileURLToPath } from "url";
118
- import { basename, dirname, join as join2 } from "path";
639
+ import { fileURLToPath as fileURLToPath2 } from "url";
640
+ import { basename as basename2, dirname as dirname3, join as join4 } from "path";
119
641
  import { promisify } from "util";
120
642
 
121
643
  // src/core/winproc.ts
122
- import { execFileSync } from "child_process";
644
+ import { execFileSync as execFileSync3 } from "child_process";
123
645
  function winSplitArgs(cmdline) {
124
646
  const out = [];
125
647
  let cur = "";
@@ -213,7 +735,7 @@ var cache = null;
213
735
  function winProcs(now = Date.now()) {
214
736
  if (cache && now - cache.at < (cache.procs ? CACHE_MS : FAIL_CACHE_MS)) return cache.procs;
215
737
  try {
216
- const out = execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", WIN_PS_SCRIPT], {
738
+ const out = execFileSync3("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", WIN_PS_SCRIPT], {
217
739
  encoding: "utf8",
218
740
  stdio: ["ignore", "pipe", "ignore"],
219
741
  maxBuffer: 32 * 1024 * 1024,
@@ -235,8 +757,8 @@ var SYSTEMD_UNIT = "deskbuddy.service";
235
757
  var SCHTASK_NAME = "DeskBuddyDaemon";
236
758
  var CLI_ENTRY = "cli.js";
237
759
  function defaultCliPath(argv1 = process.argv[1]) {
238
- const sibling = fileURLToPath(new URL("./" + CLI_ENTRY, import.meta.url));
239
- if (typeof argv1 === "string" && basename(argv1) === CLI_ENTRY) return argv1;
760
+ const sibling = fileURLToPath2(new URL("./" + CLI_ENTRY, import.meta.url));
761
+ if (typeof argv1 === "string" && basename2(argv1) === CLI_ENTRY) return argv1;
240
762
  return sibling;
241
763
  }
242
764
  function xmlEscape(s) {
@@ -275,8 +797,8 @@ RestartSec=10
275
797
  WantedBy=default.target
276
798
  `;
277
799
  }
278
- function winStartupVbsPath(appData = process.env.APPDATA ?? join2(homedir2(), "AppData", "Roaming")) {
279
- return join2(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup", "DeskBuddyDaemon.vbs");
800
+ function winStartupVbsPath(appData = process.env.APPDATA ?? join4(homedir2(), "AppData", "Roaming")) {
801
+ return join4(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup", "DeskBuddyDaemon.vbs");
280
802
  }
281
803
  function vbsQuote(s) {
282
804
  return '"' + s.replace(/"/g, '""') + '"';
@@ -294,16 +816,25 @@ function isOurDaemonCmdline(cmdline) {
294
816
  const i = argv.findIndex((a) => /(?:^|[\\/])deskbuddy[\\/]dist[\\/]cli\.js$/i.test(a));
295
817
  return i >= 1 && argv[i + 1] === "daemon";
296
818
  }
819
+ function isOurLauncherCmdline(cmdline) {
820
+ const argv = winSplitArgs(cmdline);
821
+ const i = argv.findIndex((a) => /(?:^|[\\/])launcher\.cjs$/i.test(a));
822
+ return i >= 1 && argv[i + 1] === "daemon";
823
+ }
297
824
  async function stopPidFileDaemon() {
825
+ await stopByPidFile(launcherPidPath(), isOurLauncherCmdline);
826
+ await stopByPidFile(pidFilePath(), isOurDaemonCmdline);
827
+ }
828
+ async function stopByPidFile(file, ours) {
298
829
  let pid = 0;
299
830
  try {
300
- pid = Number(readFileSync2(pidFilePath(), "utf8").trim());
831
+ pid = Number(readFileSync5(file, "utf8").trim());
301
832
  } catch {
302
833
  return;
303
834
  }
304
835
  if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return;
305
836
  const p = winProcs()?.find((x) => x.pid === pid);
306
- if (!p || !isOurDaemonCmdline(p.cmdline)) return;
837
+ if (!p || !ours(p.cmdline)) return;
307
838
  try {
308
839
  process.kill(pid);
309
840
  } catch {
@@ -319,33 +850,69 @@ async function stopPidFileDaemon() {
319
850
  }
320
851
  }
321
852
  function launchAgentPath(home = homedir2()) {
322
- return join2(home, "Library", "LaunchAgents", `${LAUNCH_LABEL}.plist`);
853
+ return join4(home, "Library", "LaunchAgents", `${LAUNCH_LABEL}.plist`);
323
854
  }
324
855
  function systemdUnitPath(home = homedir2()) {
325
- return join2(home, ".config", "systemd", "user", SYSTEMD_UNIT);
856
+ return join4(home, ".config", "systemd", "user", SYSTEMD_UNIT);
326
857
  }
327
858
  function write(path, content) {
328
- mkdirSync2(dirname(path), { recursive: true });
329
- writeFileSync2(path, content, "utf8");
859
+ mkdirSync4(dirname3(path), { recursive: true });
860
+ writeFileSync4(path, content, "utf8");
330
861
  }
331
862
  var BOOTSTRAP_RETRIES = 10;
332
863
  var BOOTSTRAP_RETRY_MS = 500;
333
- async function installAutostart(t, platform = process.platform) {
864
+ var AUTO_BOOTSTRAP_RETRIES = 120;
865
+ async function retryAsync(fn, retries, delayMs) {
866
+ for (let i = 0; ; i++) {
867
+ try {
868
+ await fn();
869
+ return;
870
+ } catch (e) {
871
+ if (i >= retries) throw e;
872
+ await new Promise((r) => setTimeout(r, delayMs));
873
+ }
874
+ }
875
+ }
876
+ function shouldFallbackSpawn(daemonPid, alive) {
877
+ return !(daemonPid && daemonPid > 0 && alive(daemonPid));
878
+ }
879
+ function readDaemonPid() {
880
+ try {
881
+ const n = Number(readFileSync5(pidFilePath(), "utf8").trim());
882
+ return Number.isInteger(n) && n > 0 ? n : null;
883
+ } catch {
884
+ return null;
885
+ }
886
+ }
887
+ function pidAlive(pid) {
888
+ try {
889
+ process.kill(pid, 0);
890
+ return true;
891
+ } catch (e) {
892
+ return e.code === "EPERM";
893
+ }
894
+ }
895
+ function fallbackSpawn(t) {
896
+ if (!shouldFallbackSpawn(readDaemonPid(), pidAlive)) return "\u65E7\u5B88\u62A4\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF0C\u672A\u53E6\u8D77";
897
+ try {
898
+ spawn(t.execPath, [t.cliPath, "daemon"], { detached: true, stdio: "ignore", windowsHide: true, shell: false }).unref();
899
+ return "\u5DF2\u76F4\u63A5\u62C9\u8D77\u5B88\u62A4\u8FDB\u7A0B\u515C\u5E95\uFF08\u672C\u6B21\u5F00\u673A\u5185\u6709\u6548\uFF09";
900
+ } catch (e) {
901
+ return `\u76F4\u63A5\u62C9\u8D77\u5B88\u62A4\u8FDB\u7A0B\u4E5F\u5931\u8D25\u4E86\uFF1A${e.message}`;
902
+ }
903
+ }
904
+ async function installAutostart(t, platform = process.platform, opts = {}) {
334
905
  try {
335
906
  if (platform === "darwin") {
336
907
  const p = launchAgentPath();
337
908
  write(p, launchAgentPlist(t));
338
909
  await run("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${LAUNCH_LABEL}`]).catch(() => {
339
910
  });
340
- for (let i = 0; ; i++) {
341
- try {
342
- await run("launchctl", ["bootstrap", `gui/${process.getuid?.() ?? 501}`, p]);
343
- break;
344
- } catch (e) {
345
- if (i >= BOOTSTRAP_RETRIES) throw e;
346
- await new Promise((r) => setTimeout(r, BOOTSTRAP_RETRY_MS));
347
- }
348
- }
911
+ await retryAsync(
912
+ () => run("launchctl", ["bootstrap", `gui/${process.getuid?.() ?? 501}`, p]),
913
+ opts.auto ? AUTO_BOOTSTRAP_RETRIES : BOOTSTRAP_RETRIES,
914
+ BOOTSTRAP_RETRY_MS
915
+ );
349
916
  return { ok: true, message: "\u5DF2\u8BBE\u7F6E\u5F00\u673A\u81EA\u542F\uFF08launchd\uFF09" };
350
917
  }
351
918
  if (platform === "linux") {
@@ -357,8 +924,8 @@ async function installAutostart(t, platform = process.platform) {
357
924
  }
358
925
  if (platform === "win32") {
359
926
  const p = winStartupVbsPath();
360
- mkdirSync2(dirname(p), { recursive: true });
361
- writeFileSync2(p, "\uFEFF" + winStartupVbs(t), "utf16le");
927
+ mkdirSync4(dirname3(p), { recursive: true });
928
+ writeFileSync4(p, "\uFEFF" + winStartupVbs(t), "utf16le");
362
929
  await run("schtasks", ["/delete", "/f", "/tn", SCHTASK_NAME], { windowsHide: true }).catch(() => {
363
930
  });
364
931
  await stopPidFileDaemon();
@@ -367,6 +934,9 @@ async function installAutostart(t, platform = process.platform) {
367
934
  }
368
935
  return { ok: false, message: `\u6682\u4E0D\u652F\u6301\u5728 ${platform} \u4E0A\u8BBE\u7F6E\u5F00\u673A\u81EA\u542F\uFF0C\u8BF7\u81EA\u884C\u5E38\u9A7B\u8FD0\u884C deskbuddy daemon` };
369
936
  } catch (e) {
937
+ if (opts.auto && platform !== "win32") {
938
+ return { ok: false, message: `\u5F00\u673A\u81EA\u542F\u6CA1\u8BBE\u7F6E\u6210\u529F\uFF08${e.message}\uFF09\uFF0C${fallbackSpawn(t)}` };
939
+ }
370
940
  return { ok: false, message: `\u5F00\u673A\u81EA\u542F\u6CA1\u8BBE\u7F6E\u6210\u529F\uFF08${e.message}\uFF09\uFF0C\u53EF\u4EE5\u624B\u52A8\u8DD1 deskbuddy daemon` };
371
941
  }
372
942
  }
@@ -376,14 +946,14 @@ async function uninstallAutostart(platform = process.platform) {
376
946
  await run("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${LAUNCH_LABEL}`]).catch(() => {
377
947
  });
378
948
  const p = launchAgentPath();
379
- if (existsSync2(p)) rmSync(p, { force: true });
949
+ if (existsSync4(p)) rmSync2(p, { force: true });
380
950
  return { ok: true, message: "\u5DF2\u53D6\u6D88\u5F00\u673A\u81EA\u542F" };
381
951
  }
382
952
  if (platform === "linux") {
383
953
  await run("systemctl", ["--user", "disable", "--now", SYSTEMD_UNIT]).catch(() => {
384
954
  });
385
955
  const p = systemdUnitPath();
386
- if (existsSync2(p)) rmSync(p, { force: true });
956
+ if (existsSync4(p)) rmSync2(p, { force: true });
387
957
  await run("systemctl", ["--user", "daemon-reload"]).catch(() => {
388
958
  });
389
959
  return { ok: true, message: "\u5DF2\u53D6\u6D88\u5F00\u673A\u81EA\u542F" };
@@ -391,29 +961,390 @@ async function uninstallAutostart(platform = process.platform) {
391
961
  if (platform === "win32") {
392
962
  await run("schtasks", ["/delete", "/f", "/tn", SCHTASK_NAME], { windowsHide: true }).catch(() => {
393
963
  });
394
- const p = winStartupVbsPath();
395
- if (existsSync2(p)) rmSync(p, { force: true });
396
- await stopPidFileDaemon();
397
- return { ok: true, message: "\u5DF2\u53D6\u6D88\u5F00\u673A\u81EA\u542F" };
964
+ const p = winStartupVbsPath();
965
+ if (existsSync4(p)) rmSync2(p, { force: true });
966
+ await stopPidFileDaemon();
967
+ return { ok: true, message: "\u5DF2\u53D6\u6D88\u5F00\u673A\u81EA\u542F" };
968
+ }
969
+ return { ok: true, message: "" };
970
+ } catch (e) {
971
+ return { ok: false, message: `\u53D6\u6D88\u5F00\u673A\u81EA\u542F\u65F6\u51FA\u9519\uFF08${e.message}\uFF09\uFF0C\u53EF\u80FD\u9700\u8981\u624B\u52A8\u6E05\u7406` };
972
+ }
973
+ }
974
+ function pidFilePath() {
975
+ return join4(deskbuddyHome(), "daemon.pid");
976
+ }
977
+ function launcherPidPath() {
978
+ return join4(deskbuddyHome(), "launcher.pid");
979
+ }
980
+ function autostartTarget(out = () => {
981
+ }, from = null) {
982
+ const legacy = { execPath: process.execPath, cliPath: defaultCliPath() };
983
+ try {
984
+ const r = registerRunningCli({ version: packageVersion(), cliPath: defaultCliPath() }, { from });
985
+ syncHookFile();
986
+ const launcher = launcherPath();
987
+ if (!existsSync4(launcher)) throw new Error("launcher.cjs \u6CA1\u6709\u540C\u6B65\u5230 " + deskbuddyHome());
988
+ if (r.note) out(`\u26A0\uFE0F \u5F53\u524D\u7248\u672C\u6CA1\u80FD\u590D\u5236\u8FDB ${deskbuddyHome()}/versions\uFF08${r.note}\uFF09\uFF0C\u5148\u539F\u5730\u767B\u8BB0`);
989
+ const removed = cleanupVersions(r.current);
990
+ if (removed.length > 0) out(`\u5DF2\u6E05\u7406\u65E7\u7248\u672C\uFF1A${removed.join(", ")}`);
991
+ return { execPath: process.execPath, cliPath: launcher };
992
+ } catch (e) {
993
+ out(`\u26A0\uFE0F \u6CA1\u80FD\u63A5\u5165\u81EA\u52A8\u5347\u7EA7\uFF08${e.message}\uFF09\uFF0C\u5F00\u673A\u81EA\u542F\u4ECD\u76F4\u63A5\u6307\u5411\u5F53\u524D\u7248\u672C`);
994
+ return legacy;
995
+ }
996
+ }
997
+
998
+ // src/daemon/autoUpdate.ts
999
+ import { execFile as execFile2, execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
1000
+ import { closeSync, existsSync as existsSync6, mkdirSync as mkdirSync5, openSync, rmSync as rmSync3 } from "fs";
1001
+ import { dirname as dirname5, join as join6, posix, win32 } from "path";
1002
+
1003
+ // src/wrap/pty.ts
1004
+ import { chmodSync, existsSync as existsSync5, statSync } from "fs";
1005
+ import { createRequire as createRequire2 } from "module";
1006
+ import { dirname as dirname4, join as join5 } from "path";
1007
+ var req = createRequire2(import.meta.url);
1008
+ function fixSpawnHelper() {
1009
+ if (process.platform !== "darwin") return;
1010
+ try {
1011
+ const dir = join5(dirname4(req.resolve("node-pty/package.json")), "prebuilds", `darwin-${process.arch}`);
1012
+ const helper = join5(dir, "spawn-helper");
1013
+ if (existsSync5(helper) && (statSync(helper).mode & 73) === 0) chmodSync(helper, 493);
1014
+ } catch {
1015
+ }
1016
+ }
1017
+ function loadPty() {
1018
+ try {
1019
+ fixSpawnHelper();
1020
+ const m = req("node-pty");
1021
+ return typeof m?.spawn === "function" ? m : null;
1022
+ } catch {
1023
+ return null;
1024
+ }
1025
+ }
1026
+ function loadHeadless() {
1027
+ try {
1028
+ const m = req("@xterm/headless");
1029
+ return typeof m?.Terminal === "function" ? m.Terminal : null;
1030
+ } catch {
1031
+ return null;
1032
+ }
1033
+ }
1034
+ function screenLines(term) {
1035
+ const b = term.buffer.active;
1036
+ const out = [];
1037
+ for (let i = b.viewportY; i < b.viewportY + term.rows; i++) {
1038
+ const l = b.getLine(i);
1039
+ out.push(l ? l.translateToString(true) : "");
1040
+ }
1041
+ return out;
1042
+ }
1043
+ function screenLinesUndimmed(term) {
1044
+ const b = term.buffer.active;
1045
+ const out = [];
1046
+ for (let i = b.viewportY; i < b.viewportY + term.rows; i++) {
1047
+ const l = b.getLine(i);
1048
+ if (!l) {
1049
+ out.push("");
1050
+ continue;
1051
+ }
1052
+ if (typeof l.getCell !== "function") {
1053
+ out.push(l.translateToString(true));
1054
+ continue;
1055
+ }
1056
+ let s = "";
1057
+ for (let x = 0; x < term.cols; x++) {
1058
+ const c = l.getCell(x);
1059
+ if (!c) break;
1060
+ const w = c.getWidth();
1061
+ if (w === 0) continue;
1062
+ s += c.isDim() ? " ".repeat(w) : c.getChars() || " ";
1063
+ }
1064
+ out.push(s.trimEnd());
1065
+ }
1066
+ return out;
1067
+ }
1068
+
1069
+ // src/daemon/autoUpdate.ts
1070
+ var PACKAGE_NAME = "deskbuddy";
1071
+ var REGISTRIES = ["https://registry.npmjs.org/", "https://registry.npmmirror.com/"];
1072
+ var FIRST_CHECK_MS = 60 * 1e3;
1073
+ var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
1074
+ var IDLE_POLL_MS = 30 * 1e3;
1075
+ var IDLE_MAX_WAIT_MS = 24 * 60 * 60 * 1e3;
1076
+ var RETRY_AFTER_FAIL_MS = 6 * 60 * 60 * 1e3;
1077
+ var INSTALL_TIMEOUT_MS = 5 * 60 * 1e3;
1078
+ var SWITCH_GRACE_MS = 10 * 60 * 1e3;
1079
+ var SWITCH_EXIT_GRACE_MS = 5 * 1e3;
1080
+ function decideUpdate(current, cli, cfg, failed, now) {
1081
+ const none = (reason2, target2 = "") => ({ action: "none", target: target2, reason: reason2 });
1082
+ if (cfg.autoUpdate === false) return none("\u672C\u5730\u5DF2\u5173\u95ED\u81EA\u52A8\u5347\u7EA7");
1083
+ if (!isValidVersion(current) || current.startsWith("0.0.0")) return none(`\u5F53\u524D\u662F\u5F00\u53D1\u7248\uFF08${current}\uFF09\uFF0C\u4E0D\u81EA\u52A8\u5347\u7EA7`);
1084
+ const latest = typeof cli?.latestVersion === "string" ? cli.latestVersion.trim() : "";
1085
+ if (!latest) return none("\u670D\u52A1\u7AEF\u6CA1\u6709\u6307\u5B9A\u7248\u672C");
1086
+ if (!isValidVersion(latest)) return none(`\u670D\u52A1\u7AEF\u4E0B\u53D1\u7684\u7248\u672C\u53F7\u4E0D\u5408\u6CD5\uFF1A${latest.slice(0, 64)}`);
1087
+ const minRaw = typeof cli?.minVersion === "string" ? cli.minVersion.trim() : "";
1088
+ const min = isValidVersion(minRaw) ? minRaw : "";
1089
+ let target = "";
1090
+ let reason = "";
1091
+ if (min && semverLt(current, min)) {
1092
+ target = compareVersions(latest, min) >= 0 ? latest : min;
1093
+ reason = `\u5F53\u524D ${current} \u4F4E\u4E8E\u6700\u4F4E\u7248\u672C ${min}\uFF0C\u5F3A\u5236\u5347\u7EA7\u5230 ${target}`;
1094
+ } else if (semverGt(latest, current)) {
1095
+ target = latest;
1096
+ reason = `\u6709\u65B0\u7248\u672C ${latest}\uFF08\u5F53\u524D ${current}\uFF09`;
1097
+ } else {
1098
+ return none(`\u5DF2\u662F\u6700\u65B0\uFF08\u5F53\u524D ${current}\uFF0C\u670D\u52A1\u7AEF\u6307\u5B9A ${latest}\uFF09`);
1099
+ }
1100
+ if (failed.versions.includes(target)) return none(`${target} \u66FE\u7ECF\u56DE\u6EDA\u8FC7\uFF0C\u4E0D\u518D\u81EA\u52A8\u5347\u5230\u5B83`, target);
1101
+ const at = failed.attempts[target];
1102
+ if (typeof at === "number" && now - at < RETRY_AFTER_FAIL_MS) {
1103
+ return none(`${target} \u4E0A\u6B21\u5B89\u88C5\u5931\u8D25\uFF0C${Math.ceil((RETRY_AFTER_FAIL_MS - (now - at)) / 6e4)} \u5206\u949F\u540E\u518D\u8BD5`, target);
1104
+ }
1105
+ return { action: "update", target, reason };
1106
+ }
1107
+ function npmCliCandidates(execPath = process.execPath, platform = process.platform) {
1108
+ if (platform === "win32") return [win32.join(win32.dirname(execPath), "node_modules", "npm", "bin", "npm-cli.js")];
1109
+ const dir = posix.dirname(execPath);
1110
+ return [
1111
+ posix.join(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
1112
+ posix.join(dir, "..", "libexec", "lib", "node_modules", "npm", "bin", "npm-cli.js")
1113
+ ];
1114
+ }
1115
+ function npmCliPath(execPath = process.execPath, platform = process.platform, exists = existsSync6) {
1116
+ return npmCliCandidates(execPath, platform).find((p) => exists(p)) ?? null;
1117
+ }
1118
+ function npmInstallArgs(npmCli, dir, target, registry) {
1119
+ if (!isValidVersion(target)) throw new Error(`\u4E0D\u662F\u5408\u6CD5\u7684\u7248\u672C\u53F7\uFF1A${target}`);
1120
+ if (!REGISTRIES.includes(registry)) throw new Error(`\u4E0D\u5141\u8BB8\u7684\u6E90\uFF1A${registry}`);
1121
+ return [npmCli, "install", "--prefix", dir, "--no-audit", "--no-fund", "--omit=dev", `${PACKAGE_NAME}@${target}`, "--registry", registry];
1122
+ }
1123
+ var defaultExec = (file, args, opts) => new Promise((resolve6, reject) => {
1124
+ execFile2(file, args, { ...opts, maxBuffer: 16 * 1024 * 1024 }, (err, _stdout, stderr) => {
1125
+ if (err) {
1126
+ const tail = String(stderr || "").trim().split("\n").slice(-3).join(" | ");
1127
+ reject(new Error(`${err.message.split("\n")[0]}${tail ? ` \u2014 ${tail.slice(0, 300)}` : ""}`));
1128
+ } else resolve6();
1129
+ });
1130
+ });
1131
+ function ptyLoadsIn(dir, execPath = process.execPath) {
1132
+ try {
1133
+ execFileSync4(execPath, ["-e", "const m=require(process.argv[1]);if(typeof m.spawn!=='function')process.exit(3)", join6(dir, "node_modules", "node-pty")], {
1134
+ timeout: 3e4,
1135
+ windowsHide: true,
1136
+ shell: false,
1137
+ stdio: "ignore"
1138
+ });
1139
+ return true;
1140
+ } catch {
1141
+ return false;
1142
+ }
1143
+ }
1144
+ async function installVersion(target, deps) {
1145
+ const execPath = deps.execPath ?? process.execPath;
1146
+ const verifyCli = deps.verifyCli ?? ((p, v) => cliReportsVersion(p, v, execPath));
1147
+ const dir = versionDir(target, deps.home);
1148
+ const cliPath = versionCliPath(target, deps.home);
1149
+ const oldPtyOk = deps.oldPtyOk ?? (() => loadPty() !== null);
1150
+ const newPtyOk = deps.newPtyOk ?? ((d) => ptyLoadsIn(d, execPath));
1151
+ const verified = () => installedPackageVersion(dir) === target && existsSync6(cliPath) && verifyCli(cliPath, target) && (!oldPtyOk() || newPtyOk(dir));
1152
+ if (verified()) {
1153
+ deps.log(`${target} \u5DF2\u5728\u672C\u673A\u88C5\u597D\uFF0C\u76F4\u63A5\u7528`);
1154
+ return cliPath;
1155
+ }
1156
+ const platform = deps.platform ?? process.platform;
1157
+ const npmCli = npmCliPath(execPath, platform);
1158
+ if (!npmCli) throw new Error(`\u627E\u4E0D\u5230 node \u81EA\u5E26\u7684 npm\uFF08${npmCliCandidates(execPath, platform).join(" / ")}\uFF09`);
1159
+ const exec = deps.exec ?? defaultExec;
1160
+ const errors = [];
1161
+ for (const registry of REGISTRIES) {
1162
+ rmSync3(dir, { recursive: true, force: true });
1163
+ mkdirSync5(dir, { recursive: true });
1164
+ deps.log(`\u5B89\u88C5 ${PACKAGE_NAME}@${target}\uFF08\u6E90 ${registry}\uFF09`);
1165
+ try {
1166
+ await exec(execPath, npmInstallArgs(npmCli, dir, target, registry), {
1167
+ timeout: INSTALL_TIMEOUT_MS,
1168
+ shell: false,
1169
+ windowsHide: true,
1170
+ env: { ...process.env, npm_config_update_notifier: "false", npm_config_fund: "false" }
1171
+ });
1172
+ } catch (e) {
1173
+ errors.push(`${registry} ${e.message}`);
1174
+ deps.log(`\u4ECE ${registry} \u5B89\u88C5\u5931\u8D25\uFF1A${e.message}`);
1175
+ continue;
398
1176
  }
399
- return { ok: true, message: "" };
400
- } catch (e) {
401
- return { ok: false, message: `\u53D6\u6D88\u5F00\u673A\u81EA\u542F\u65F6\u51FA\u9519\uFF08${e.message}\uFF09\uFF0C\u53EF\u80FD\u9700\u8981\u624B\u52A8\u6E05\u7406` };
1177
+ const got = installedPackageVersion(dir);
1178
+ if (got !== target) {
1179
+ errors.push(`${registry} \u88C5\u51FA\u6765\u7684\u7248\u672C\u662F ${got ?? "(\u65E0)"}`);
1180
+ continue;
1181
+ }
1182
+ if (!verifyCli(cliPath, target)) {
1183
+ errors.push(`${registry} \u88C5\u597D\u7684 cli.js --version \u4E0D\u662F ${target}`);
1184
+ continue;
1185
+ }
1186
+ if (oldPtyOk() && !newPtyOk(dir)) {
1187
+ errors.push(`${registry} \u88C5\u597D\u7684\u7248\u672C\u52A0\u8F7D\u4E0D\u4E86 node-pty`);
1188
+ continue;
1189
+ }
1190
+ return cliPath;
402
1191
  }
1192
+ rmSync3(dir, { recursive: true, force: true });
1193
+ throw new Error(errors.join("\uFF1B"));
403
1194
  }
404
- function pidFilePath() {
405
- return join2(deskbuddyHome(), "daemon.pid");
1195
+ var FROM_VERSION_ENV = "DESKBUDDY_UPDATE_FROM_VERSION";
1196
+ var FROM_CLI_ENV = "DESKBUDDY_UPDATE_FROM_CLI";
1197
+ function switchFromEnv(env = process.env) {
1198
+ const version = env[FROM_VERSION_ENV] ?? "";
1199
+ const cliPath = env[FROM_CLI_ENV] ?? "";
1200
+ return isValidVersion(version) && cliPath ? { version, cliPath } : null;
1201
+ }
1202
+ function spawnSwitch(cliPath, logFile, from, onExit, execPath = process.execPath) {
1203
+ let fd = "ignore";
1204
+ try {
1205
+ mkdirSync5(dirname5(logFile), { recursive: true });
1206
+ fd = openSync(logFile, "a");
1207
+ } catch {
1208
+ fd = "ignore";
1209
+ }
1210
+ const child = spawn2(execPath, [cliPath, "update", "--auto"], {
1211
+ detached: true,
1212
+ stdio: ["ignore", fd, fd],
1213
+ windowsHide: true,
1214
+ shell: false,
1215
+ env: { ...process.env, [FROM_VERSION_ENV]: from.version, [FROM_CLI_ENV]: from.cliPath }
1216
+ });
1217
+ child.on("exit", (code) => onExit?.(code));
1218
+ child.on("error", () => onExit?.(null));
1219
+ child.unref();
1220
+ if (typeof fd === "number") closeSync(fd);
406
1221
  }
1222
+ var AutoUpdater = class {
1223
+ #d;
1224
+ #running = false;
1225
+ #stopped = false;
1226
+ #switched = null;
1227
+ #first = null;
1228
+ #timer = null;
1229
+ constructor(deps) {
1230
+ this.#d = deps;
1231
+ }
1232
+ start() {
1233
+ this.#first = setTimeout(() => void this.check(), this.#d.firstCheckMs ?? FIRST_CHECK_MS);
1234
+ this.#first.unref?.();
1235
+ this.#timer = setInterval(() => void this.check(), this.#d.intervalMs ?? CHECK_INTERVAL_MS);
1236
+ this.#timer.unref?.();
1237
+ }
1238
+ stop() {
1239
+ this.#stopped = true;
1240
+ if (this.#first) clearTimeout(this.#first);
1241
+ if (this.#timer) clearInterval(this.#timer);
1242
+ }
1243
+ #now() {
1244
+ return this.#d.now ? this.#d.now() : Date.now();
1245
+ }
1246
+ /** 查一次并在需要时走完整个升级。正在进行中的一轮没结束就跳过。返回这一轮的结局(给测试和日志) */
1247
+ async check() {
1248
+ if (this.#switched) {
1249
+ if (this.#now() - this.#switched.at < (this.#d.switchGraceMs ?? SWITCH_GRACE_MS)) return "busy";
1250
+ this.#d.log(`\u4EA4\u7ED9 ${this.#switched.target} \u5207\u6362\u4E4B\u540E\u5B88\u62A4\u8FDB\u7A0B\u4E00\u76F4\u6CA1\u88AB\u91CD\u542F\uFF0C\u89C6\u4E3A\u5207\u6362\u5931\u8D25`);
1251
+ (this.#d.recordFailure ?? ((t, n) => recordAttemptFailure(t, n)))(this.#switched.target, this.#now());
1252
+ this.#switched = null;
1253
+ this.#d.endDrain?.();
1254
+ }
1255
+ if (this.#running) return "busy";
1256
+ this.#running = true;
1257
+ try {
1258
+ return await this.#check();
1259
+ } catch (e) {
1260
+ this.#d.log(`\u81EA\u52A8\u5347\u7EA7\u51FA\u9519\uFF1A${e?.stack ?? String(e)}`);
1261
+ return "failed";
1262
+ } finally {
1263
+ this.#running = false;
1264
+ }
1265
+ }
1266
+ async #check() {
1267
+ const log2 = this.#d.log;
1268
+ let policy;
1269
+ try {
1270
+ policy = await this.#d.fetchPolicy();
1271
+ } catch (e) {
1272
+ log2(`\u7248\u672C\u68C0\u67E5\u5931\u8D25\uFF1A${e.message}`);
1273
+ return "failed";
1274
+ }
1275
+ const decision = decideUpdate(
1276
+ this.#d.currentVersion,
1277
+ policy,
1278
+ this.#d.config(),
1279
+ (this.#d.failed ?? (() => readFailed()))(),
1280
+ this.#now()
1281
+ );
1282
+ log2(`\u7248\u672C\u68C0\u67E5\uFF1A${decision.action === "update" ? "\u5347\u7EA7" : "\u4E0D\u5347\u7EA7"} \u2014\u2014 ${decision.reason}`);
1283
+ if (decision.action !== "update") return "none";
1284
+ const target = decision.target;
1285
+ let cliPath;
1286
+ try {
1287
+ cliPath = await this.#d.install(target);
1288
+ } catch (e) {
1289
+ log2(`\u5B89\u88C5 ${target} \u5931\u8D25\uFF1A${e.message}\uFF1B6 \u5C0F\u65F6\u5185\u4E0D\u518D\u91CD\u8BD5\u8FD9\u4E00\u7248`);
1290
+ (this.#d.recordFailure ?? ((t, n) => recordAttemptFailure(t, n)))(target, this.#now());
1291
+ return "failed";
1292
+ }
1293
+ log2(`${target} \u5DF2\u88C5\u597D\u5E76\u6821\u9A8C\u901A\u8FC7\uFF0C\u7B49\u5B88\u62A4\u8FDB\u7A0B\u7A7A\u95F2\u540E\u5207\u6362`);
1294
+ const sleep2 = this.#d.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms).unref?.()));
1295
+ const deadline = this.#now() + (this.#d.idleMaxWaitMs ?? IDLE_MAX_WAIT_MS);
1296
+ let lastWhy = "";
1297
+ for (; ; ) {
1298
+ if (this.#stopped) return "stopped";
1299
+ const st = this.#d.idle();
1300
+ if (st.idle) break;
1301
+ if (st.why !== lastWhy) {
1302
+ log2(`\u7B49\u5F85\u7A7A\u95F2\uFF1A${st.why}`);
1303
+ lastWhy = st.why;
1304
+ }
1305
+ if (this.#now() >= deadline) {
1306
+ log2(`\u7B49\u4E86 24 \u5C0F\u65F6\u4ECD\u4E0D\u7A7A\u95F2\uFF0C\u653E\u5F03\u8FD9\u4E00\u8F6E\uFF0C\u4E0B\u6B21\u68C0\u67E5\u518D\u5224\u5B9A`);
1307
+ return "timeout";
1308
+ }
1309
+ await sleep2(this.#d.idlePollMs ?? IDLE_POLL_MS);
1310
+ }
1311
+ if (this.#stopped) return "stopped";
1312
+ this.#d.beginDrain?.();
1313
+ log2(`\u5B88\u62A4\u8FDB\u7A0B\u7A7A\u95F2\uFF0C\u7528 ${target} \u6267\u884C update --auto \u5B8C\u6210\u5207\u6362\uFF08\u5B88\u62A4\u8FDB\u7A0B\u4F1A\u88AB\u91CD\u542F\uFF09`);
1314
+ const mine = { target, at: this.#now() };
1315
+ this.#switched = mine;
1316
+ const onExit = (code) => {
1317
+ const t = setTimeout(() => {
1318
+ if (this.#switched !== mine || this.#stopped) return;
1319
+ this.#switched = null;
1320
+ this.#d.endDrain?.();
1321
+ log2(`\u5207\u6362\u5B50\u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF08\u9000\u51FA\u7801 ${code ?? "\u65E0"}\uFF09\u800C\u5B88\u62A4\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF0C\u89C6\u4E3A\u5207\u6362\u5230 ${target} \u5931\u8D25\uFF0C\u6062\u590D\u63A5\u6307\u4EE4`);
1322
+ (this.#d.recordFailure ?? ((t2, n) => recordAttemptFailure(t2, n)))(target, this.#now());
1323
+ }, this.#d.switchExitGraceMs ?? SWITCH_EXIT_GRACE_MS);
1324
+ t.unref?.();
1325
+ };
1326
+ try {
1327
+ this.#d.switchTo(cliPath, target, onExit);
1328
+ } catch (e) {
1329
+ this.#switched = null;
1330
+ this.#d.endDrain?.();
1331
+ log2(`\u5207\u6362\u5230 ${target} \u5931\u8D25\uFF1A${e.message}`);
1332
+ (this.#d.recordFailure ?? ((t, n) => recordAttemptFailure(t, n)))(target, this.#now());
1333
+ return "failed";
1334
+ }
1335
+ return "switched";
1336
+ }
1337
+ };
407
1338
 
408
1339
  // src/daemon/index.ts
409
1340
  import { randomBytes as randomBytes7 } from "crypto";
410
- import { existsSync as existsSync10 } from "fs";
411
- import { basename as basename6 } from "path";
1341
+ import { existsSync as existsSync13 } from "fs";
1342
+ import { basename as basename7 } from "path";
412
1343
  import WebSocket2 from "ws";
413
1344
 
414
1345
  // src/adapters/ocPlugin.ts
415
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
416
- import { dirname as dirname2, join as join3 } from "path";
1346
+ import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync6, renameSync as renameSync4, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
1347
+ import { dirname as dirname6, join as join7 } from "path";
417
1348
  import { pathToFileURL } from "url";
418
1349
  var OC_MARKER = "// deskbuddy:opencode-plugin";
419
1350
  function ocPluginSource(pluginModule, nodePath, platform = process.platform) {
@@ -427,26 +1358,26 @@ function ocPluginSource(pluginModule, nodePath, platform = process.platform) {
427
1358
  ].join("\n");
428
1359
  }
429
1360
  function ocPluginModule(hookPath) {
430
- return join3(dirname2(hookPath), "opencode.mjs");
1361
+ return join7(dirname6(hookPath), "opencode.mjs");
431
1362
  }
432
1363
  function installOcPlugin(pluginPath, hookPath, nodePath) {
433
- if (existsSync3(pluginPath) && !readFileSync3(pluginPath, "utf8").includes(OC_MARKER)) {
1364
+ if (existsSync7(pluginPath) && !readFileSync6(pluginPath, "utf8").includes(OC_MARKER)) {
434
1365
  throw new Error(`${pluginPath} \u5DF2\u5B58\u5728\u4E14\u4E0D\u662F DeskBuddy \u751F\u6210\u7684\uFF0C\u8BF7\u5148\u632A\u8D70\u518D\u91CD\u8BD5`);
435
1366
  }
436
- mkdirSync3(dirname2(pluginPath), { recursive: true });
1367
+ mkdirSync6(dirname6(pluginPath), { recursive: true });
437
1368
  const tmp = pluginPath + ".deskbuddy.tmp";
438
- writeFileSync3(tmp, ocPluginSource(ocPluginModule(hookPath), nodePath), "utf8");
439
- renameSync2(tmp, pluginPath);
1369
+ writeFileSync5(tmp, ocPluginSource(ocPluginModule(hookPath), nodePath), "utf8");
1370
+ renameSync4(tmp, pluginPath);
440
1371
  }
441
1372
  function uninstallOcPlugin(pluginPath) {
442
1373
  try {
443
- if (readFileSync3(pluginPath, "utf8").includes(OC_MARKER)) unlinkSync(pluginPath);
1374
+ if (readFileSync6(pluginPath, "utf8").includes(OC_MARKER)) unlinkSync(pluginPath);
444
1375
  } catch {
445
1376
  }
446
1377
  }
447
1378
  function isOcPluginInstalled(pluginPath) {
448
1379
  try {
449
- return readFileSync3(pluginPath, "utf8").includes(OC_MARKER);
1380
+ return readFileSync6(pluginPath, "utf8").includes(OC_MARKER);
450
1381
  } catch {
451
1382
  return false;
452
1383
  }
@@ -454,16 +1385,16 @@ function isOcPluginInstalled(pluginPath) {
454
1385
  var OC_NOTICE = " \u5DF2\u5F00\u7740\u7684 opencode \u7A97\u53E3\u91CD\u5F00\u540E\u751F\u6548";
455
1386
 
456
1387
  // src/core/state.ts
457
- import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
458
- import { join as join5 } from "path";
1388
+ import { existsSync as existsSync8, mkdirSync as mkdirSync8, readdirSync as readdirSync3, readFileSync as readFileSync8, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
1389
+ import { join as join9 } from "path";
459
1390
 
460
1391
  // src/core/process.ts
461
- import { execFileSync as execFileSync2 } from "child_process";
1392
+ import { execFileSync as execFileSync5 } from "child_process";
462
1393
  var WIN = process.platform === "win32";
463
1394
  function processName(pid) {
464
1395
  if (WIN) return winProcs()?.find((p) => p.pid === pid)?.name ?? null;
465
1396
  try {
466
- const out = execFileSync2("ps", ["-p", String(pid), "-o", "comm="], {
1397
+ const out = execFileSync5("ps", ["-p", String(pid), "-o", "comm="], {
467
1398
  encoding: "utf8",
468
1399
  stdio: ["ignore", "pipe", "ignore"]
469
1400
  });
@@ -492,27 +1423,27 @@ function isAlive(pid, expectName) {
492
1423
  }
493
1424
 
494
1425
  // src/core/ended.ts
495
- import { closeSync, mkdirSync as mkdirSync4, openSync, readdirSync, readFileSync as readFileSync4, readSync, realpathSync, renameSync as renameSync3, rmSync as rmSync2, statSync, writeFileSync as writeFileSync4 } from "fs";
496
- import { join as join4 } from "path";
1426
+ import { closeSync as closeSync2, mkdirSync as mkdirSync7, openSync as openSync2, readdirSync as readdirSync2, readFileSync as readFileSync7, readSync, realpathSync, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
1427
+ import { join as join8 } from "path";
497
1428
  var ENDED_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
498
1429
  var SID_RE = /^[A-Za-z0-9_-]{1,64}$/;
499
1430
  function endedDir(home = deskbuddyHome()) {
500
- return join4(home, "ended");
1431
+ return join8(home, "ended");
501
1432
  }
502
1433
  function writeEnded(s, now = Date.now()) {
503
1434
  if (!SID_RE.test(s.sessionId)) return;
504
1435
  try {
505
1436
  const dir = endedDir();
506
- mkdirSync4(dir, { recursive: true, mode: 448 });
1437
+ mkdirSync7(dir, { recursive: true, mode: 448 });
507
1438
  const rec = { sessionId: s.sessionId, tool: s.tool, cwd: s.cwd ?? "", interactive: s.interactive === true, endedAt: now };
508
- const file = join4(dir, `${s.sessionId}.json`);
1439
+ const file = join8(dir, `${s.sessionId}.json`);
509
1440
  const tmp = `${file}.${process.pid}.tmp`;
510
- writeFileSync4(tmp, JSON.stringify(rec), { encoding: "utf8", mode: 384 });
511
- renameSync3(tmp, file);
512
- for (const n of readdirSync(dir)) {
513
- const f = join4(dir, n);
1441
+ writeFileSync6(tmp, JSON.stringify(rec), { encoding: "utf8", mode: 384 });
1442
+ renameSync5(tmp, file);
1443
+ for (const n of readdirSync2(dir)) {
1444
+ const f = join8(dir, n);
514
1445
  try {
515
- if (now - statSync(f).mtimeMs > ENDED_TTL_MS) rmSync2(f, { force: true });
1446
+ if (now - statSync2(f).mtimeMs > ENDED_TTL_MS) rmSync4(f, { force: true });
516
1447
  } catch {
517
1448
  }
518
1449
  }
@@ -522,7 +1453,7 @@ function writeEnded(s, now = Date.now()) {
522
1453
  function readEnded(sessionId, now = Date.now()) {
523
1454
  if (!SID_RE.test(sessionId)) return null;
524
1455
  try {
525
- const r = JSON.parse(readFileSync4(join4(endedDir(), `${sessionId}.json`), "utf8"));
1456
+ const r = JSON.parse(readFileSync7(join8(endedDir(), `${sessionId}.json`), "utf8"));
526
1457
  if (!r || r.sessionId !== sessionId || typeof r.endedAt !== "number" || now - r.endedAt > ENDED_TTL_MS) return null;
527
1458
  return r;
528
1459
  } catch {
@@ -537,14 +1468,14 @@ function projectDirName(cwd) {
537
1468
  function readChunk(file, fromEnd, max) {
538
1469
  let fd;
539
1470
  try {
540
- const size = statSync(file).size;
1471
+ const size = statSync2(file).size;
541
1472
  const len = Math.min(size, max);
542
1473
  const buf = Buffer.alloc(len);
543
- fd = openSync(file, "r");
1474
+ fd = openSync2(file, "r");
544
1475
  readSync(fd, buf, 0, len, fromEnd ? size - len : 0);
545
1476
  return buf.toString("utf8");
546
1477
  } finally {
547
- if (fd !== void 0) closeSync(fd);
1478
+ if (fd !== void 0) closeSync2(fd);
548
1479
  }
549
1480
  }
550
1481
  function cwdsIn(text) {
@@ -563,14 +1494,14 @@ function claudeTranscriptCwd(sessionId, home = claudeHome()) {
563
1494
  if (!SID_RE.test(sessionId)) return "";
564
1495
  let dirs = [];
565
1496
  try {
566
- dirs = readdirSync(join4(home, "projects"));
1497
+ dirs = readdirSync2(join8(home, "projects"));
567
1498
  } catch {
568
1499
  return "";
569
1500
  }
570
1501
  for (const d of dirs) {
571
- const file = join4(home, "projects", d, `${sessionId}.jsonl`);
1502
+ const file = join8(home, "projects", d, `${sessionId}.jsonl`);
572
1503
  try {
573
- statSync(file);
1504
+ statSync2(file);
574
1505
  } catch {
575
1506
  continue;
576
1507
  }
@@ -606,320 +1537,89 @@ function resolveEndedCwd(sessionId, tool, d) {
606
1537
  }
607
1538
  function isDirSync(p) {
608
1539
  try {
609
- return statSync(p).isDirectory();
1540
+ return statSync2(p).isDirectory();
610
1541
  } catch {
611
1542
  return false;
612
- }
613
- }
614
-
615
- // src/core/state.ts
616
- function stateDir() {
617
- return join5(deskbuddyHome(), "state");
618
- }
619
- function statePath(sessionId) {
620
- const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
621
- return join5(stateDir(), `${safe || "unknown"}.json`);
622
- }
623
- function readState(sessionId) {
624
- try {
625
- return JSON.parse(readFileSync5(statePath(sessionId), "utf8"));
626
- } catch {
627
- return null;
628
- }
629
- }
630
- function retireState(s, now = Date.now()) {
631
- writeEnded(s, now);
632
- removeState(s.sessionId);
633
- }
634
- function removeState(sessionId) {
635
- try {
636
- rmSync3(statePath(sessionId), { force: true });
637
- } catch {
638
- }
639
- }
640
- function readAliveStates(tool) {
641
- if (!existsSync4(stateDir())) return [];
642
- const alive = [];
643
- for (const file of readdirSync2(stateDir())) {
644
- if (!file.endsWith(".json")) continue;
645
- let s;
646
- try {
647
- s = JSON.parse(readFileSync5(join5(stateDir(), file), "utf8"));
648
- } catch {
649
- rmSync3(join5(stateDir(), file), { force: true });
650
- continue;
651
- }
652
- if (s.tool !== tool) continue;
653
- if (s.pid && !isAlive(s.pid, s.pidName)) {
654
- retireState(s);
655
- continue;
656
- }
657
- alive.push(s);
658
- }
659
- return alive;
660
- }
661
- function noticeDir() {
662
- return join5(deskbuddyHome(), "notice");
663
- }
664
- function writeNotice(sessionId, text, now = Date.now()) {
665
- try {
666
- const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
667
- if (!safe) return;
668
- mkdirSync5(noticeDir(), { recursive: true });
669
- const file = join5(noticeDir(), `${safe}.json`);
670
- writeFileSync5(file + ".tmp", JSON.stringify({ text, at: now }), "utf8");
671
- renameSync4(file + ".tmp", file);
672
- } catch {
673
- }
674
- }
675
- var NOTICE_TTL_MS = 60 * 60 * 1e3;
676
- var ALIAS_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
677
- function resolveAlias(sessionId, now = Date.now()) {
678
- let map = {};
679
- try {
680
- map = JSON.parse(readFileSync5(join5(deskbuddyHome(), "alias.json"), "utf8")) || {};
681
- } catch {
682
- return sessionId;
683
- }
684
- let cur = sessionId;
685
- for (let i = 0; i < 5; i++) {
686
- const a = map[cur];
687
- if (!a || typeof a.to !== "string" || !a.to || typeof a.at !== "number" || now - a.at >= ALIAS_TTL_MS) break;
688
- cur = a.to;
689
- }
690
- return cur;
691
- }
692
-
693
- // src/core/api.ts
694
- import { execFileSync as execFileSync3 } from "child_process";
695
- import { createHash, randomBytes, randomUUID as randomUUID2 } from "crypto";
696
- import { readFileSync as readFileSync7 } from "fs";
697
- import { release } from "os";
698
-
699
- // src/core/hookFile.ts
700
- import { copyFileSync, existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync6, renameSync as renameSync5, writeFileSync as writeFileSync6 } from "fs";
701
- import { createRequire } from "module";
702
- import { dirname as dirname3, join as join6 } from "path";
703
- import { fileURLToPath as fileURLToPath2 } from "url";
704
- var here = dirname3(fileURLToPath2(import.meta.url));
705
- function packagedHookPath() {
706
- const candidates = [
707
- join6(here, "hook.cjs"),
708
- // dist/cli.js 旁边(发布形态)
709
- join6(here, "..", "hook", "hook.cjs"),
710
- // dist/../hook/
711
- join6(here, "..", "..", "hook", "hook.cjs")
712
- // src/core/../../hook/(开发形态)
713
- ];
714
- for (const c of candidates) {
715
- if (existsSync5(c)) return c;
716
- }
717
- throw new Error("\u627E\u4E0D\u5230\u5305\u5185\u7684 hook.cjs\uFF0C\u5B89\u88C5\u5305\u53EF\u80FD\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5 deskbuddy");
718
- }
719
- function packageVersion() {
720
- const require2 = createRequire(import.meta.url);
721
- for (const c of ["../package.json", "../../package.json", "../../../package.json"]) {
722
- try {
723
- const pkg = require2(c);
724
- if (pkg?.name === "deskbuddy" && pkg.version) return pkg.version;
725
- } catch {
726
- }
727
- }
728
- return "0.0.0-dev";
729
- }
730
- function hookVersionPath(target = installedHookPath()) {
731
- return join6(dirname3(target), "hook-version");
732
- }
733
- function installedHookVersion(target = installedHookPath()) {
734
- try {
735
- return readFileSync6(hookVersionPath(target), "utf8").trim() || null;
736
- } catch {
737
- return null;
738
- }
739
- }
740
- function atomicCopy(from, to) {
741
- const tmp = `${to}.${process.pid}.tmp`;
742
- copyFileSync(from, tmp);
743
- renameSync5(tmp, to);
744
- }
745
- function syncHookFile(opts = {}) {
746
- const target = opts.target ?? installedHookPath();
747
- const version = opts.version ?? packageVersion();
748
- const SIBLINGS = ["gate.cjs", "opencode.mjs", "wake.cjs"];
749
- const siblingMissing = () => SIBLINGS.some((name) => {
750
- if (existsSync5(join6(dirname3(target), name))) return false;
751
- try {
752
- return existsSync5(join6(dirname3(opts.source ?? packagedHookPath()), name));
753
- } catch {
754
- return false;
755
- }
756
- });
757
- if (installedHookVersion(target) === version && existsSync5(target) && !siblingMissing()) return false;
758
- const source = opts.source ?? packagedHookPath();
759
- mkdirSync6(dirname3(target), { recursive: true });
760
- atomicCopy(source, target);
761
- for (const name of SIBLINGS) {
762
- const from = join6(dirname3(source), name);
763
- if (existsSync5(from)) atomicCopy(from, join6(dirname3(target), name));
764
- }
765
- writeFileSync6(hookVersionPath(target), version, "utf8");
766
- return true;
767
- }
768
-
769
- // src/core/api.ts
770
- var SERVICES = {
771
- authCode: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyauthcodeapi",
772
- authToken: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyauthtokenapi",
773
- sessionReport: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddysessionreportapi",
774
- toolRegistry: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddytoolregistryapi",
775
- /** App 扫码确认时调的,CLI 用不到,列在这里只为对照 */
776
- confirmAuth: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyconfirmauthapi"
777
- };
778
- var DIRS_SERVICE = "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyclientdirsreportapi";
779
- var INSTALL_REDEEM_SERVICE = "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyinstallredeemapi";
780
- var CMD_RESULT_SERVICE = "com.shifeng.api.service.api.v2.deskbuddy.deskbuddycommandresultapi";
781
- var APPROVAL_SERVICES = {
782
- create: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyapprovalcreateapi",
783
- cancel: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyapprovalcancelapi",
784
- sync: "com.shifeng.api.service.api.v2.deskbuddy.deskbuddyapprovalsyncapi"
785
- };
786
- var APP_ID = process.env.DESKBUDDY_APPID || "deskbuddy-cli";
787
- var APP_SECRET = process.env.DESKBUDDY_APPSECRET || "3aeb6e0afc144e188dd3ede5c7865643";
788
- var GATEWAY_VERSION = "1.0";
789
- var DEFAULT_TIMEOUT_MS = 1e4;
790
- function sign(p) {
791
- const source = [p.appId, p.appSecret, p.timestamp, p.deviceId, p.deviceName, p.nonce, p.body].join("_");
792
- return createHash("md5").update(source, "utf8").digest("hex");
793
- }
794
- function buildGatewayBody(service, data, requestId) {
795
- return JSON.stringify({
796
- service,
797
- version: GATEWAY_VERSION,
798
- requestId: requestId || randomUUID2(),
799
- base: { encode: "false" },
800
- // data 是业务 JSON 序列化后的字符串
801
- data: JSON.stringify(data ?? {})
802
- });
803
- }
804
- function buildHeaders(cfg, body, opts = {}) {
805
- const timestamp = opts.timestamp || String(Date.now());
806
- const nonce = opts.nonce || randomBytes(8).toString("hex");
807
- return {
808
- "Content-Type": "application/json;charset=UTF-8",
809
- "Content-Length": String(Buffer.byteLength(body)),
810
- "ShiFeng-AppId": APP_ID,
811
- "ShiFeng-Timestamp": timestamp,
812
- "ShiFeng-Device-ID": cfg.clientId,
813
- "ShiFeng-Device-Name": cfg.hostName,
814
- "ShiFeng-Nonce": nonce,
815
- "ShiFeng-Signature": sign({
816
- appId: APP_ID,
817
- appSecret: APP_SECRET,
818
- timestamp,
819
- deviceId: cfg.clientId,
820
- deviceName: cfg.hostName,
821
- nonce,
822
- body
823
- })
824
- // 没有 ShiFeng-Token:鉴权靠 clientId ↔ userId 绑定关系,CLI 不持有用户凭证
825
- };
826
- }
827
- function isSuccessCode(code) {
828
- return code === "0000";
829
- }
830
- var ApiError = class extends Error {
831
- constructor(code, message) {
832
- super(`${code}: ${message}`);
833
- this.code = code;
834
- this.name = "ApiError";
835
- }
836
- code;
837
- };
838
- function humanOs(platform = process.platform) {
839
- const known = {
840
- darwin: "macOS",
841
- win32: "Windows",
842
- linux: "Linux"
843
- };
844
- return known[platform] ?? platform;
1543
+ }
845
1544
  }
846
- var defaultOsVersionDeps = {
847
- runSwVers: () => execFileSync3("sw_vers", ["-productVersion"], {
848
- encoding: "utf8",
849
- stdio: ["ignore", "pipe", "ignore"],
850
- timeout: 2e3
851
- }),
852
- readOsRelease: () => readFileSync7("/etc/os-release", "utf8"),
853
- kernelRelease: () => release()
854
- };
855
- function productOsVersion(platform = process.platform, deps = defaultOsVersionDeps) {
1545
+
1546
+ // src/core/state.ts
1547
+ function stateDir() {
1548
+ return join9(deskbuddyHome(), "state");
1549
+ }
1550
+ function statePath(sessionId) {
1551
+ const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
1552
+ return join9(stateDir(), `${safe || "unknown"}.json`);
1553
+ }
1554
+ function readState(sessionId) {
856
1555
  try {
857
- if (platform === "darwin") {
858
- const v = deps.runSwVers().trim();
859
- if (v) return v;
860
- } else if (platform === "linux") {
861
- const m = /^PRETTY_NAME=(.*)$/m.exec(deps.readOsRelease());
862
- if (m) {
863
- const v = m[1].trim().replace(/^(["'])(.*)\1$/, "$2").trim();
864
- if (v) return v;
865
- }
866
- }
1556
+ return JSON.parse(readFileSync8(statePath(sessionId), "utf8"));
867
1557
  } catch {
1558
+ return null;
868
1559
  }
1560
+ }
1561
+ function retireState(s, now = Date.now()) {
1562
+ writeEnded(s, now);
1563
+ removeState(s.sessionId);
1564
+ }
1565
+ function removeState(sessionId) {
869
1566
  try {
870
- return deps.kernelRelease();
1567
+ rmSync5(statePath(sessionId), { force: true });
871
1568
  } catch {
872
- return "";
873
1569
  }
874
1570
  }
875
- var machineInfoCache = null;
876
- function machineInfo() {
877
- if (!machineInfoCache) {
878
- machineInfoCache = {
879
- os: humanOs(),
880
- osVersion: productOsVersion(),
881
- arch: process.arch,
882
- cliVersion: packageVersion()
883
- };
1571
+ function readAliveStates(tool) {
1572
+ if (!existsSync8(stateDir())) return [];
1573
+ const alive = [];
1574
+ for (const file of readdirSync3(stateDir())) {
1575
+ if (!file.endsWith(".json")) continue;
1576
+ let s;
1577
+ try {
1578
+ s = JSON.parse(readFileSync8(join9(stateDir(), file), "utf8"));
1579
+ } catch {
1580
+ rmSync5(join9(stateDir(), file), { force: true });
1581
+ continue;
1582
+ }
1583
+ if (s.tool !== tool) continue;
1584
+ if (s.pid && !isAlive(s.pid, s.pidName)) {
1585
+ retireState(s);
1586
+ continue;
1587
+ }
1588
+ alive.push(s);
884
1589
  }
885
- return machineInfoCache;
1590
+ return alive;
886
1591
  }
887
- async function call(service, data, timeoutMs = DEFAULT_TIMEOUT_MS) {
888
- const cfg = loadConfig();
889
- const body = buildGatewayBody(service, data);
890
- const controller = new AbortController();
891
- const timer = setTimeout(() => controller.abort(), timeoutMs);
1592
+ function noticeDir() {
1593
+ return join9(deskbuddyHome(), "notice");
1594
+ }
1595
+ function writeNotice(sessionId, text, now = Date.now()) {
892
1596
  try {
893
- const res = await fetch(`${cfg.apiBase}/gateway`, {
894
- method: "POST",
895
- headers: buildHeaders(cfg, body),
896
- body,
897
- // 与签名用的是同一个字符串
898
- signal: controller.signal
899
- });
900
- const json = await res.json();
901
- if (!isSuccessCode(json?.code)) {
902
- throw new ApiError(String(json?.code), json?.message ?? "");
903
- }
904
- return json?.data;
905
- } finally {
906
- clearTimeout(timer);
1597
+ const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "");
1598
+ if (!safe) return;
1599
+ mkdirSync8(noticeDir(), { recursive: true });
1600
+ const file = join9(noticeDir(), `${safe}.json`);
1601
+ writeFileSync7(file + ".tmp", JSON.stringify({ text, at: now }), "utf8");
1602
+ renameSync6(file + ".tmp", file);
1603
+ } catch {
907
1604
  }
908
1605
  }
909
- var api = {
910
- authCode: (clientId, hostName) => call(SERVICES.authCode, { clientId, hostName, ...machineInfo() }),
911
- authToken: (authCode) => call(SERVICES.authToken, { authCode }),
912
- toolRegistry: () => call(SERVICES.toolRegistry, {}),
913
- // 🔴 2 秒超时:这条调用发生在 hook 链路上,慢一点就是拖慢用户的 agent
914
- report: (payload) => call(SERVICES.sessionReport, payload, 2e3),
915
- approvalCreate: (payload) => call(APPROVAL_SERVICES.create, payload),
916
- approvalCancel: (approvalId, reason) => call(APPROVAL_SERVICES.cancel, { approvalId, reason }),
917
- approvalSync: (approvalIds) => call(APPROVAL_SERVICES.sync, { approvalIds }),
918
- dirsReport: (payload) => call(DIRS_SERVICE, payload),
919
- installRedeem: (installCode, clientId, hostName) => call(INSTALL_REDEEM_SERVICE, { installCode, clientId, hostName, ...machineInfo() }),
920
- // 2 秒超时、失败不重试(spec §4.1):回报丢了只是设备少一句提示,任务状态仍由会话快照负责
921
- commandResult: (payload) => call(CMD_RESULT_SERVICE, payload, 2e3)
922
- };
1606
+ var NOTICE_TTL_MS = 60 * 60 * 1e3;
1607
+ var ALIAS_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
1608
+ function resolveAlias(sessionId, now = Date.now()) {
1609
+ let map = {};
1610
+ try {
1611
+ map = JSON.parse(readFileSync8(join9(deskbuddyHome(), "alias.json"), "utf8")) || {};
1612
+ } catch {
1613
+ return sessionId;
1614
+ }
1615
+ let cur = sessionId;
1616
+ for (let i = 0; i < 5; i++) {
1617
+ const a = map[cur];
1618
+ if (!a || typeof a.to !== "string" || !a.to || typeof a.at !== "number" || now - a.at >= ALIAS_TTL_MS) break;
1619
+ cur = a.to;
1620
+ }
1621
+ return cur;
1622
+ }
923
1623
 
924
1624
  // src/core/report.ts
925
1625
  async function postReport(payload) {
@@ -930,7 +1630,7 @@ async function postReport(payload) {
930
1630
  }
931
1631
 
932
1632
  // src/core/snapshot.ts
933
- import { basename as basename2 } from "path";
1633
+ import { basename as basename3 } from "path";
934
1634
  var KEEPALIVE_MS = 2 * 60 * 1e3;
935
1635
  var MAX_SESSIONS = 20;
936
1636
  var CAPS = ["cmd_result", "window_inject", "resume_ended"];
@@ -939,7 +1639,7 @@ function buildSnapshot(states, tool, cfg, nowMs, remote) {
939
1639
  sessionId: s.sessionId,
940
1640
  title: s.title,
941
1641
  // 隐私:只带目录名,不带全路径——全路径会暴露公司目录结构与用户名
942
- project: basename2(s.project || ""),
1642
+ project: basename3(s.project || ""),
943
1643
  state: s.state,
944
1644
  startedAt: s.startedAt,
945
1645
  updatedAt: s.updatedAt,
@@ -951,15 +1651,15 @@ function buildSnapshot(states, tool, cfg, nowMs, remote) {
951
1651
 
952
1652
  // src/core/dirs.ts
953
1653
  import { createHmac, randomBytes as randomBytes2 } from "crypto";
954
- import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
955
- import { basename as basename3, join as join7 } from "path";
1654
+ import { chmodSync as chmodSync2, existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
1655
+ import { basename as basename4, join as join10 } from "path";
956
1656
 
957
1657
  // src/core/dirFilter.ts
958
1658
  import { homedir as homedir3, tmpdir } from "os";
959
- import { posix, win32 } from "path";
1659
+ import { posix as posix2, win32 as win322 } from "path";
960
1660
  function norm(p, win) {
961
- if (win) return win32.resolve(p).replace(/[\\/]+$/, "").toLowerCase();
962
- return posix.resolve(p).replace(/\/+$/, "");
1661
+ if (win) return win322.resolve(p).replace(/[\\/]+$/, "").toLowerCase();
1662
+ return posix2.resolve(p).replace(/\/+$/, "");
963
1663
  }
964
1664
  function isUnder(p, base, win) {
965
1665
  return p === base || p.startsWith(base + (win ? "\\" : "/"));
@@ -967,7 +1667,7 @@ function isUnder(p, base, win) {
967
1667
  function isAutoWorkspace(p, opts = {}) {
968
1668
  if (!p) return false;
969
1669
  const win = (opts.platform ?? process.platform) === "win32";
970
- const P = win ? win32 : posix;
1670
+ const P = win ? win322 : posix2;
971
1671
  const home = norm(opts.home ?? homedir3(), win);
972
1672
  const full = norm(p, win);
973
1673
  const name = P.basename(full);
@@ -978,7 +1678,7 @@ function isAutoWorkspace(p, opts = {}) {
978
1678
  function isReportableDir(p, opts = {}) {
979
1679
  if (!p) return false;
980
1680
  const win = (opts.platform ?? process.platform) === "win32";
981
- const P = win ? win32 : posix;
1681
+ const P = win ? win322 : posix2;
982
1682
  const full = norm(p, win);
983
1683
  if (full === norm(P.parse(P.resolve(p)).root, win)) return false;
984
1684
  if (full === norm(opts.home ?? homedir3(), win)) return false;
@@ -991,20 +1691,20 @@ function isReportableDir(p, opts = {}) {
991
1691
  // src/core/dirs.ts
992
1692
  var DIRS_REPORT_MAX = 10;
993
1693
  function dirsPath() {
994
- return join7(deskbuddyHome(), "dirs.json");
1694
+ return join10(deskbuddyHome(), "dirs.json");
995
1695
  }
996
1696
  function dirSecret() {
997
- const file = join7(deskbuddyHome(), "dir-secret");
1697
+ const file = join10(deskbuddyHome(), "dir-secret");
998
1698
  try {
999
- const s2 = readFileSync8(file, "utf8").trim();
1699
+ const s2 = readFileSync9(file, "utf8").trim();
1000
1700
  if (s2) return s2;
1001
1701
  } catch {
1002
1702
  }
1003
1703
  const s = randomBytes2(32).toString("hex");
1004
1704
  try {
1005
- mkdirSync7(deskbuddyHome(), { recursive: true });
1006
- writeFileSync7(file, s, { encoding: "utf8", mode: 384 });
1007
- chmodSync(file, 384);
1705
+ mkdirSync9(deskbuddyHome(), { recursive: true });
1706
+ writeFileSync8(file, s, { encoding: "utf8", mode: 384 });
1707
+ chmodSync2(file, 384);
1008
1708
  } catch {
1009
1709
  }
1010
1710
  return s;
@@ -1014,7 +1714,7 @@ function dirRef(path) {
1014
1714
  }
1015
1715
  function readMap() {
1016
1716
  try {
1017
- const m = JSON.parse(readFileSync8(dirsPath(), "utf8"));
1717
+ const m = JSON.parse(readFileSync9(dirsPath(), "utf8"));
1018
1718
  const out = {};
1019
1719
  for (const [k, v] of Object.entries(m ?? {})) if (typeof v === "number") out[k] = v;
1020
1720
  return out;
@@ -1022,17 +1722,17 @@ function readMap() {
1022
1722
  return {};
1023
1723
  }
1024
1724
  }
1025
- function recentDirs(limit = DIRS_REPORT_MAX, exists = existsSync6, reportable = (p) => isReportableDir(p)) {
1725
+ function recentDirs(limit = DIRS_REPORT_MAX, exists = existsSync9, reportable = (p) => isReportableDir(p)) {
1026
1726
  const list = Object.entries(readMap()).filter(([p]) => p && reportable(p) && exists(p)).sort((a, b) => b[1] - a[1]).slice(0, limit);
1027
1727
  const counts = /* @__PURE__ */ new Map();
1028
- for (const [p] of list) counts.set(basename3(p), (counts.get(basename3(p)) ?? 0) + 1);
1728
+ for (const [p] of list) counts.set(basename4(p), (counts.get(basename4(p)) ?? 0) + 1);
1029
1729
  return list.map(([p, at]) => {
1030
1730
  const ref = dirRef(p);
1031
- const name = basename3(p) || "/";
1032
- return { path: p, ref, name: (counts.get(basename3(p)) ?? 0) > 1 ? `${name} \xB7 ${ref.slice(0, 4)}` : name, lastUsedAt: at };
1731
+ const name = basename4(p) || "/";
1732
+ return { path: p, ref, name: (counts.get(basename4(p)) ?? 0) > 1 ? `${name} \xB7 ${ref.slice(0, 4)}` : name, lastUsedAt: at };
1033
1733
  });
1034
1734
  }
1035
- function dirOf(ref, exists = existsSync6) {
1735
+ function dirOf(ref, exists = existsSync9) {
1036
1736
  for (const p of Object.keys(readMap())) if (dirRef(p) === ref && exists(p)) return p;
1037
1737
  return "";
1038
1738
  }
@@ -1110,12 +1810,12 @@ function isExpired(cmd, now = Date.now()) {
1110
1810
  }
1111
1811
 
1112
1812
  // src/daemon/executor.ts
1113
- import { execFile as execFile2, execFileSync as execFileSync5 } from "child_process";
1813
+ import { execFile as execFile3, execFileSync as execFileSync7 } from "child_process";
1114
1814
  import { homedir as homedir5 } from "os";
1115
- import { join as join9 } from "path";
1815
+ import { join as join12 } from "path";
1116
1816
 
1117
1817
  // src/daemon/gate/settings.ts
1118
- import { existsSync as existsSync7 } from "fs";
1818
+ import { existsSync as existsSync10 } from "fs";
1119
1819
  import { fileURLToPath as fileURLToPath3 } from "url";
1120
1820
  var GATE_HOOK_TIMEOUT_S = 7 * 24 * 60 * 60;
1121
1821
  function waitModeFor(tool) {
@@ -1142,7 +1842,7 @@ function defaultGateScript() {
1142
1842
  fileURLToPath3(new URL("./gate.cjs", import.meta.url)),
1143
1843
  fileURLToPath3(new URL("../../../hook/gate.cjs", import.meta.url))
1144
1844
  ];
1145
- return candidates.find((p) => existsSync7(p)) ?? candidates[0];
1845
+ return candidates.find((p) => existsSync10(p)) ?? candidates[0];
1146
1846
  }
1147
1847
  function gateCommand(t, platform = process.platform) {
1148
1848
  if (platform === "win32") return winGateCommand(t);
@@ -1170,24 +1870,24 @@ function codexGateArgs(t) {
1170
1870
  }
1171
1871
 
1172
1872
  // src/daemon/locate.ts
1173
- import { execFileSync as execFileSync4 } from "child_process";
1174
- import { accessSync, constants, existsSync as existsSync8, lstatSync, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
1873
+ import { execFileSync as execFileSync6 } from "child_process";
1874
+ import { accessSync, constants, existsSync as existsSync11, lstatSync, readFileSync as readFileSync10, statSync as statSync3 } from "fs";
1175
1875
  import { homedir as homedir4 } from "os";
1176
- import { delimiter, dirname as dirname4, join as join8, win32 as win322 } from "path";
1876
+ import { delimiter, dirname as dirname7, join as join11, win32 as win323 } from "path";
1177
1877
  var defaultLocateDeps = () => ({
1178
1878
  home: homedir4(),
1179
1879
  path: process.env.PATH ?? "",
1180
1880
  nodePath: process.execPath,
1181
1881
  isExecutable: (p) => {
1182
1882
  try {
1183
- if (!statSync2(p).isFile()) return false;
1883
+ if (!statSync3(p).isFile()) return false;
1184
1884
  accessSync(p, constants.X_OK);
1185
1885
  return true;
1186
1886
  } catch {
1187
1887
  return false;
1188
1888
  }
1189
1889
  },
1190
- exists: (p) => existsSync8(p),
1890
+ exists: (p) => existsSync11(p),
1191
1891
  lexists: (p) => {
1192
1892
  try {
1193
1893
  lstatSync(p);
@@ -1201,22 +1901,22 @@ var defaultLocateDeps = () => ({
1201
1901
  localAppData: process.env.LOCALAPPDATA ?? "",
1202
1902
  readText: (p) => {
1203
1903
  try {
1204
- return readFileSync9(p, "utf8");
1904
+ return readFileSync10(p, "utf8");
1205
1905
  } catch {
1206
1906
  return null;
1207
1907
  }
1208
1908
  },
1209
1909
  // 🔴 daemon 自己也不能找到垫片:远程任务要的是真程序(窗口接管 spec 2026-09-30 §3.1)
1210
- skipDirs: [join8(deskbuddyHome(), "bin")],
1910
+ skipDirs: [join11(deskbuddyHome(), "bin")],
1211
1911
  winAppHints: (product, exe) => winInstallDirs(product, exe)
1212
1912
  });
1213
1913
  function installDirsFromProcs(procs, exe) {
1214
1914
  const out = [];
1215
1915
  for (const p of procs ?? []) {
1216
1916
  const first = winSplitArgs(p.cmdline)[0] ?? "";
1217
- if (win322.basename(first).toLowerCase() !== exe.toLowerCase()) continue;
1218
- const dir = win322.dirname(first);
1219
- if (win322.isAbsolute(dir) && !out.some((d) => d.toLowerCase() === dir.toLowerCase())) out.push(dir);
1917
+ if (win323.basename(first).toLowerCase() !== exe.toLowerCase()) continue;
1918
+ const dir = win323.dirname(first);
1919
+ if (win323.isAbsolute(dir) && !out.some((d) => d.toLowerCase() === dir.toLowerCase())) out.push(dir);
1220
1920
  }
1221
1921
  return out;
1222
1922
  }
@@ -1224,8 +1924,8 @@ function parseUninstallDirs(out) {
1224
1924
  const dirs = [];
1225
1925
  for (const raw of out.split(/\r?\n/)) {
1226
1926
  let v = raw.trim().replace(/,\s*-?\d+$/, "").replace(/^"|"$/g, "").trim();
1227
- if (!v || !win322.isAbsolute(v)) continue;
1228
- if (/\.exe$/i.test(v)) v = win322.dirname(v);
1927
+ if (!v || !win323.isAbsolute(v)) continue;
1928
+ if (/\.exe$/i.test(v)) v = win323.dirname(v);
1229
1929
  v = v.replace(/[\\/]+$/, "");
1230
1930
  if (!dirs.some((d) => d.toLowerCase() === v.toLowerCase())) dirs.push(v);
1231
1931
  }
@@ -1237,7 +1937,7 @@ function winInstallDirs(product, exe) {
1237
1937
  try {
1238
1938
  const name = product.replace(/'/g, "''");
1239
1939
  const script = `[Console]::OutputEncoding=[Text.Encoding]::UTF8;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 -like '${name}*' } | ForEach-Object { $_.InstallLocation; $_.DisplayIcon }`;
1240
- const out = execFileSync4("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], {
1940
+ const out = execFileSync6("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], {
1241
1941
  encoding: "utf8",
1242
1942
  stdio: ["ignore", "pipe", "ignore"],
1243
1943
  windowsHide: true,
@@ -1253,23 +1953,23 @@ var WIN_EXTS = [".exe", ".cmd", ".bat"];
1253
1953
  function extraBinDirs(home, nodePath = "", win) {
1254
1954
  if (win) {
1255
1955
  return [
1256
- join8(home, ".local", "bin"),
1257
- ...win.appData ? [join8(win.appData, "npm")] : [],
1258
- join8(home, ".bun", "bin"),
1259
- ...win.localAppData ? [join8(win.localAppData, "Volta", "bin")] : [],
1260
- join8(home, "scoop", "shims"),
1261
- ...nodePath ? [dirname4(nodePath)] : []
1956
+ join11(home, ".local", "bin"),
1957
+ ...win.appData ? [join11(win.appData, "npm")] : [],
1958
+ join11(home, ".bun", "bin"),
1959
+ ...win.localAppData ? [join11(win.localAppData, "Volta", "bin")] : [],
1960
+ join11(home, "scoop", "shims"),
1961
+ ...nodePath ? [dirname7(nodePath)] : []
1262
1962
  ];
1263
1963
  }
1264
1964
  return [
1265
- join8(home, ".local", "bin"),
1266
- join8(home, ".claude", "local"),
1267
- join8(home, ".npm-global", "bin"),
1268
- join8(home, ".bun", "bin"),
1269
- join8(home, ".volta", "bin"),
1965
+ join11(home, ".local", "bin"),
1966
+ join11(home, ".claude", "local"),
1967
+ join11(home, ".npm-global", "bin"),
1968
+ join11(home, ".bun", "bin"),
1969
+ join11(home, ".volta", "bin"),
1270
1970
  "/opt/homebrew/bin",
1271
1971
  "/usr/local/bin",
1272
- ...nodePath ? [dirname4(nodePath)] : []
1972
+ ...nodePath ? [dirname7(nodePath)] : []
1273
1973
  ];
1274
1974
  }
1275
1975
  function whichIn(name, d) {
@@ -1280,7 +1980,7 @@ function whichAllIn(name, d) {
1280
1980
  const out = [];
1281
1981
  for (const dir of binDirs(d)) {
1282
1982
  for (const ext of exts) {
1283
- const p = join8(dir, name + ext);
1983
+ const p = join11(dir, name + ext);
1284
1984
  if (d.isExecutable(p)) out.push(p);
1285
1985
  }
1286
1986
  }
@@ -1288,9 +1988,9 @@ function whichAllIn(name, d) {
1288
1988
  }
1289
1989
  function binDirs(d) {
1290
1990
  const win = isWin(d);
1291
- const sep2 = win ? ";" : delimiter;
1991
+ const sep3 = win ? ";" : delimiter;
1292
1992
  const all = [
1293
- ...d.path.split(sep2).filter(Boolean),
1993
+ ...d.path.split(sep3).filter(Boolean),
1294
1994
  ...extraBinDirs(d.home, d.nodePath, win ? { appData: d.appData, localAppData: d.localAppData } : void 0)
1295
1995
  ];
1296
1996
  if (!d.skipDirs || d.skipDirs.length === 0) return all;
@@ -1304,7 +2004,7 @@ function binDirs(d) {
1304
2004
  function parseNpmCmdShim(shimPath, text) {
1305
2005
  const m = text.match(/"%~?dp0%?\\?([^"%]+)"\s+%\*/i);
1306
2006
  if (!m) return null;
1307
- return join8(dirname4(shimPath), m[1].replace(/^\\/, ""));
2007
+ return join11(dirname7(shimPath), m[1].replace(/^\\/, ""));
1308
2008
  }
1309
2009
  function resolveWinLaunch(file, d) {
1310
2010
  const lower = file.toLowerCase();
@@ -1322,21 +2022,21 @@ function resolveWinLaunch(file, d) {
1322
2022
  return null;
1323
2023
  }
1324
2024
  function appDirs(home) {
1325
- return ["/Applications", join8(home, "Applications")];
2025
+ return ["/Applications", join11(home, "Applications")];
1326
2026
  }
1327
2027
  function winAppDirs(d, product) {
1328
2028
  return [
1329
- ...d.localAppData ? [join8(d.localAppData, "Programs", product)] : [],
1330
- join8("C:\\Program Files", product),
1331
- join8("C:\\Program Files (x86)", product)
2029
+ ...d.localAppData ? [join11(d.localAppData, "Programs", product)] : [],
2030
+ join11("C:\\Program Files", product),
2031
+ join11("C:\\Program Files (x86)", product)
1332
2032
  ];
1333
2033
  }
1334
2034
  var WORKBUDDY_CLI_IN_WIN_APP = "resources/app.asar.unpacked/cli/bin/codebuddy";
1335
2035
  var QODER_CN_WORKER_IN_WIN_APP = "resources/app.asar.unpacked/node_modules/@qoder-ai/qoder-cn-agent-sdk/dist/_worker/qoder-worker-runtime.obf.mjs";
1336
2036
  function winClaudeExeCandidates(d) {
1337
2037
  return [
1338
- ...d.home ? [join8(d.home, ".local", "bin", "claude.exe")] : [],
1339
- ...d.appData ? [join8(d.appData, "npm", "node_modules", "@anthropic-ai", "claude-code", "bin", "claude.exe")] : []
2038
+ ...d.home ? [join11(d.home, ".local", "bin", "claude.exe")] : [],
2039
+ ...d.appData ? [join11(d.appData, "npm", "node_modules", "@anthropic-ai", "claude-code", "bin", "claude.exe")] : []
1340
2040
  ];
1341
2041
  }
1342
2042
  function locateWin(tool, d) {
@@ -1357,7 +2057,7 @@ function locateWin(tool, d) {
1357
2057
  const inApp = (dirs) => {
1358
2058
  for (const dir of dirs) {
1359
2059
  for (const ext of [".exe", ".cmd", ""]) {
1360
- const p = join8(dir, WORKBUDDY_CLI_IN_WIN_APP + ext);
2060
+ const p = join11(dir, WORKBUDDY_CLI_IN_WIN_APP + ext);
1361
2061
  if (d.exists(p)) {
1362
2062
  const r = resolveWinLaunch(p, d);
1363
2063
  if (r) return r;
@@ -1370,13 +2070,13 @@ function locateWin(tool, d) {
1370
2070
  }
1371
2071
  case "qoder": {
1372
2072
  for (const dir of winAppDirs(d, "Qoder CN")) {
1373
- const p = join8(dir, QODER_CN_WORKER_IN_WIN_APP);
2073
+ const p = join11(dir, QODER_CN_WORKER_IN_WIN_APP);
1374
2074
  if (d.exists(p)) return { file: d.nodePath, prefixArgs: [p] };
1375
2075
  }
1376
2076
  return plain("qodercli");
1377
2077
  }
1378
2078
  case "opencode": {
1379
- const p = join8(d.home, ".opencode", "bin", "opencode.exe");
2079
+ const p = join11(d.home, ".opencode", "bin", "opencode.exe");
1380
2080
  if (d.exists(p)) return { file: p, prefixArgs: [] };
1381
2081
  return plain("opencode");
1382
2082
  }
@@ -1399,20 +2099,20 @@ function locate(tool, d = defaultLocateDeps()) {
1399
2099
  return plain("codex");
1400
2100
  case "workbuddy": {
1401
2101
  for (const dir of appDirs(d.home)) {
1402
- const p = join8(dir, WORKBUDDY_CLI_IN_APP);
2102
+ const p = join11(dir, WORKBUDDY_CLI_IN_APP);
1403
2103
  if (d.isExecutable(p)) return { file: p, prefixArgs: [] };
1404
2104
  }
1405
2105
  return plain("codebuddy");
1406
2106
  }
1407
2107
  case "qoder": {
1408
2108
  for (const dir of appDirs(d.home)) {
1409
- const p = join8(dir, QODER_CN_WORKER_IN_APP);
2109
+ const p = join11(dir, QODER_CN_WORKER_IN_APP);
1410
2110
  if (d.exists(p)) return { file: d.nodePath, prefixArgs: [p] };
1411
2111
  }
1412
2112
  return plain("qodercli");
1413
2113
  }
1414
2114
  case "opencode": {
1415
- const p = join8(d.home, ".opencode", "bin", "opencode");
2115
+ const p = join11(d.home, ".opencode", "bin", "opencode");
1416
2116
  if (d.isExecutable(p)) return { file: p, prefixArgs: [] };
1417
2117
  return plain("opencode");
1418
2118
  }
@@ -1429,7 +2129,7 @@ function findWt(d = defaultLocateDeps()) {
1429
2129
  if (!isWin(d)) return null;
1430
2130
  const hit = whichIn("wt", d);
1431
2131
  if (hit) return hit;
1432
- const alias = d.localAppData ? join8(d.localAppData, "Microsoft", "WindowsApps", "wt.exe") : "";
2132
+ const alias = d.localAppData ? join11(d.localAppData, "Microsoft", "WindowsApps", "wt.exe") : "";
1433
2133
  return alias && (d.exists(alias) || (d.lexists?.(alias) ?? false)) ? alias : null;
1434
2134
  }
1435
2135
 
@@ -1511,7 +2211,7 @@ function buildSpawnPlan(cmd, cwd, gate, home = homedir5()) {
1511
2211
  case "codex":
1512
2212
  return { file, args: codexPlan(cmd, gate), cwd };
1513
2213
  case "workbuddy":
1514
- return { file, args: workbuddyPlan(cmd, gate), cwd, env: { CODEBUDDY_CONFIG_DIR: join9(home, ".workbuddy") } };
2214
+ return { file, args: workbuddyPlan(cmd, gate), cwd, env: { CODEBUDDY_CONFIG_DIR: join12(home, ".workbuddy") } };
1515
2215
  case "qoder":
1516
2216
  return gate ? { file, args: qoderPlan(cmd, gate), cwd } : null;
1517
2217
  case "opencode":
@@ -1611,8 +2311,8 @@ function startCommand(plan, tool, limitMs = EXEC_TIMEOUT_MS) {
1611
2311
  }, Math.max(0, remaining));
1612
2312
  };
1613
2313
  let timedOut = false;
1614
- const done = new Promise((resolve5) => {
1615
- child = execFile2(
2314
+ const done = new Promise((resolve6) => {
2315
+ child = execFile3(
1616
2316
  plan.file,
1617
2317
  plan.args,
1618
2318
  {
@@ -1629,10 +2329,10 @@ function startCommand(plan, tool, limitMs = EXEC_TIMEOUT_MS) {
1629
2329
  const out = String(stdout7 ?? "");
1630
2330
  if (err || timedOut) {
1631
2331
  const msg = timedOut ? `\u6267\u884C\u8D85\u8FC7 ${Math.round(limitMs / 1e3)} \u79D2\u6D3B\u52A8\u65F6\u95F4\uFF0C\u5DF2\u7EC8\u6B62` : err.message;
1632
- resolve5({ ok: false, sessionId: parseSessionId(tool, out), stdout: out, error: msg });
2332
+ resolve6({ ok: false, sessionId: parseSessionId(tool, out), stdout: out, error: msg });
1633
2333
  return;
1634
2334
  }
1635
- resolve5({ ok: true, sessionId: parseSessionId(tool, out), stdout: out });
2335
+ resolve6({ ok: true, sessionId: parseSessionId(tool, out), stdout: out });
1636
2336
  }
1637
2337
  );
1638
2338
  child?.stdin?.end();
@@ -1668,7 +2368,7 @@ function killTree(child) {
1668
2368
  if (!child) return;
1669
2369
  if (process.platform === "win32" && child.pid) {
1670
2370
  try {
1671
- execFileSync5("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
2371
+ execFileSync7("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
1672
2372
  return;
1673
2373
  } catch {
1674
2374
  }
@@ -1678,8 +2378,8 @@ function killTree(child) {
1678
2378
 
1679
2379
  // src/daemon/gate/approvals.ts
1680
2380
  import { createHash as createHash2 } from "crypto";
1681
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync10, renameSync as renameSync6, writeFileSync as writeFileSync8 } from "fs";
1682
- import { join as join10 } from "path";
2381
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync11, renameSync as renameSync7, writeFileSync as writeFileSync9 } from "fs";
2382
+ import { join as join13 } from "path";
1683
2383
  var RETAIN_MS = 7 * 24 * 60 * 60 * 1e3;
1684
2384
  function fingerprint(toolName, toolInput) {
1685
2385
  let input = "";
@@ -1691,7 +2391,7 @@ function fingerprint(toolName, toolInput) {
1691
2391
  return createHash2("sha256").update(toolName).update("\0").update(input).digest("hex");
1692
2392
  }
1693
2393
  function storePath() {
1694
- return join10(deskbuddyHome(), "approvals.json");
2394
+ return join13(deskbuddyHome(), "approvals.json");
1695
2395
  }
1696
2396
  var ApprovalStore = class _ApprovalStore {
1697
2397
  #records;
@@ -1700,7 +2400,7 @@ var ApprovalStore = class _ApprovalStore {
1700
2400
  }
1701
2401
  static load() {
1702
2402
  try {
1703
- const raw = JSON.parse(readFileSync10(storePath(), "utf8"));
2403
+ const raw = JSON.parse(readFileSync11(storePath(), "utf8"));
1704
2404
  return new _ApprovalStore(Array.isArray(raw) ? raw : []);
1705
2405
  } catch {
1706
2406
  return new _ApprovalStore();
@@ -1708,10 +2408,10 @@ var ApprovalStore = class _ApprovalStore {
1708
2408
  }
1709
2409
  save() {
1710
2410
  try {
1711
- mkdirSync8(deskbuddyHome(), { recursive: true });
2411
+ mkdirSync10(deskbuddyHome(), { recursive: true });
1712
2412
  const tmp = storePath() + ".tmp";
1713
- writeFileSync8(tmp, JSON.stringify(this.#records), { encoding: "utf8", mode: 384 });
1714
- renameSync6(tmp, storePath());
2413
+ writeFileSync9(tmp, JSON.stringify(this.#records), { encoding: "utf8", mode: 384 });
2414
+ renameSync7(tmp, storePath());
1715
2415
  } catch {
1716
2416
  }
1717
2417
  }
@@ -1751,7 +2451,7 @@ var ApprovalStore = class _ApprovalStore {
1751
2451
  };
1752
2452
 
1753
2453
  // src/daemon/gate/backend.ts
1754
- import { basename as basename4 } from "path";
2454
+ import { basename as basename5 } from "path";
1755
2455
  function serverBackend(client = api) {
1756
2456
  return {
1757
2457
  create: (r) => client.approvalCreate({
@@ -1760,7 +2460,7 @@ function serverBackend(client = api) {
1760
2460
  sessionId: r.sessionId,
1761
2461
  kind: r.kind,
1762
2462
  summary: r.summary,
1763
- cwdName: basename4(r.cwd),
2463
+ cwdName: basename5(r.cwd),
1764
2464
  source: r.mode === "window" ? "window" : "remote",
1765
2465
  ...r.remember ? { remember: r.remember } : {}
1766
2466
  }),
@@ -1792,11 +2492,11 @@ function mapStatus(s) {
1792
2492
 
1793
2493
  // src/daemon/gate/coordinator.ts
1794
2494
  import { randomUUID as randomUUID3 } from "crypto";
1795
- import { existsSync as existsSync9, realpathSync as realpathSync2 } from "fs";
1796
- import { basename as basename5, dirname as dirname5, join as join11 } from "path";
2495
+ import { existsSync as existsSync12, realpathSync as realpathSync2 } from "fs";
2496
+ import { basename as basename6, dirname as dirname8, join as join14 } from "path";
1797
2497
 
1798
2498
  // src/daemon/gate/classify.ts
1799
- import { isAbsolute, relative, resolve, sep } from "path";
2499
+ import { isAbsolute, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
1800
2500
  var SUMMARY_MAX = 200;
1801
2501
  var READ_TOOLS = /* @__PURE__ */ new Set([
1802
2502
  "Read",
@@ -1837,13 +2537,13 @@ var PROTECTED_DIRS = /* @__PURE__ */ new Set([
1837
2537
  ]);
1838
2538
  var PROTECTED_FILES = /* @__PURE__ */ new Set([".envrc", ".mcp.json", ".gitmodules", ".gitattributes"]);
1839
2539
  function isProtectedPath(cwd, p) {
1840
- const segs = resolve(cwd || "/", p).split(sep).filter(Boolean).map((s) => s.toLowerCase());
2540
+ const segs = resolve2(cwd || "/", p).split(sep2).filter(Boolean).map((s) => s.toLowerCase());
1841
2541
  if (segs.some((s) => PROTECTED_DIRS.has(s))) return true;
1842
2542
  return PROTECTED_FILES.has(segs[segs.length - 1] ?? "");
1843
2543
  }
1844
2544
  function isInside(cwd, p) {
1845
2545
  if (!cwd || !p) return false;
1846
- const rel = relative(resolve(cwd), resolve(cwd, p));
2546
+ const rel = relative2(resolve2(cwd), resolve2(cwd, p));
1847
2547
  if (rel === "") return true;
1848
2548
  return !rel.startsWith("..") && !isAbsolute(rel);
1849
2549
  }
@@ -1900,12 +2600,12 @@ function classify(toolName, toolInput, cwd, policy = "commands", opts = {}) {
1900
2600
  const inside = (p) => {
1901
2601
  if (!roots.some((r) => isInside(r, p))) return false;
1902
2602
  if (!real2) return true;
1903
- const rp = real2(resolve(roots[0] ?? "/", p));
2603
+ const rp = real2(resolve2(roots[0] ?? "/", p));
1904
2604
  return roots.some((r) => isInside(real2(r), rp));
1905
2605
  };
1906
2606
  const rel = (p) => {
1907
2607
  const r = roots.find((x) => isInside(x, p)) ?? roots[0] ?? "";
1908
- return relative(resolve(r), resolve(r, p));
2608
+ return relative2(resolve2(r), resolve2(r, p));
1909
2609
  };
1910
2610
  const input = toolInput && typeof toolInput === "object" ? toolInput : {};
1911
2611
  if (READ_TOOLS.has(toolName)) return { action: "auto" };
@@ -1918,7 +2618,7 @@ function classify(toolName, toolInput, cwd, policy = "commands", opts = {}) {
1918
2618
  if (paths) {
1919
2619
  const allInside = paths.length > 0 && paths.every(inside);
1920
2620
  const touchesProtected = paths.some(
1921
- (p) => isProtectedPath(roots[0] ?? "/", p) || (real2 ? isProtectedPath("/", real2(resolve(roots[0] ?? "/", p))) : false)
2621
+ (p) => isProtectedPath(roots[0] ?? "/", p) || (real2 ? isProtectedPath("/", real2(resolve2(roots[0] ?? "/", p))) : false)
1922
2622
  );
1923
2623
  if (policy === "commands" && allInside && !touchesProtected) return { action: "auto" };
1924
2624
  const shown = paths.map((p) => inside(p) ? rel(p) : p);
@@ -1941,14 +2641,14 @@ function describe(toolName, toolInput, cwd) {
1941
2641
  }
1942
2642
 
1943
2643
  // src/daemon/gate/procs.ts
1944
- import { execFileSync as execFileSync6 } from "child_process";
2644
+ import { execFileSync as execFileSync8 } from "child_process";
1945
2645
  function listDescendants(rootPid) {
1946
2646
  if (process.platform === "win32") {
1947
2647
  return descendantsOf(rootPid, (winProcs() ?? []).map((p) => ({ pid: p.pid, ppid: p.ppid, args: p.cmdline })));
1948
2648
  }
1949
2649
  let out = "";
1950
2650
  try {
1951
- out = execFileSync6("ps", ["-A", "-ww", "-o", "pid=,ppid=,args="], {
2651
+ out = execFileSync8("ps", ["-A", "-ww", "-o", "pid=,ppid=,args="], {
1952
2652
  encoding: "utf8",
1953
2653
  stdio: ["ignore", "pipe", "ignore"],
1954
2654
  maxBuffer: 16 * 1024 * 1024
@@ -2080,7 +2780,7 @@ var ApprovalCoordinator = class {
2080
2780
  store.save();
2081
2781
  let early = null;
2082
2782
  let settle;
2083
- const decided = new Promise((resolve5) => settle = resolve5);
2783
+ const decided = new Promise((resolve6) => settle = resolve6);
2084
2784
  this.#waiters.set(rec.approvalId, (r) => {
2085
2785
  early = r;
2086
2786
  settle(r);
@@ -2104,8 +2804,8 @@ var ApprovalCoordinator = class {
2104
2804
  }
2105
2805
  run2.onWaitStart?.();
2106
2806
  try {
2107
- return await new Promise((resolve5) => {
2108
- void decided.then(resolve5);
2807
+ return await new Promise((resolve6) => {
2808
+ void decided.then(resolve6);
2109
2809
  const onAbort = () => {
2110
2810
  if (!this.#waiters.delete(rec.approvalId)) return;
2111
2811
  if (rec.status === "pending") {
@@ -2114,7 +2814,7 @@ var ApprovalCoordinator = class {
2114
2814
  store.save();
2115
2815
  void this.#cancelRemote(rec.approvalId, "\u667A\u80FD\u4F53\u8FDB\u7A0B\u5DF2\u7ED3\u675F");
2116
2816
  }
2117
- resolve5({ decision: "deny", reason: "\u5DF2\u53D6\u6D88" });
2817
+ resolve6({ decision: "deny", reason: "\u5DF2\u53D6\u6D88" });
2118
2818
  };
2119
2819
  if (signal.aborted) onAbort();
2120
2820
  else signal.addEventListener("abort", onAbort, { once: true });
@@ -2166,7 +2866,7 @@ var ApprovalCoordinator = class {
2166
2866
  ...watchProcs ? { pid, cmd, baseline: new Set(this.#descendants(pid).map((p) => p.pid)) } : {}
2167
2867
  });
2168
2868
  let settle;
2169
- const decided = new Promise((resolve5) => settle = resolve5);
2869
+ const decided = new Promise((resolve6) => settle = resolve6);
2170
2870
  this.#waiters.set(rec.approvalId, settle);
2171
2871
  try {
2172
2872
  await this.#d.backend.create(rec);
@@ -2182,11 +2882,11 @@ var ApprovalCoordinator = class {
2182
2882
  }
2183
2883
  this.#d.log(`\u7A97\u53E3\u6388\u6743 ${rec.approvalId} \u5DF2\u63A8\u5230\u8BBE\u5907 kind=${rec.kind}`);
2184
2884
  if (rec.status !== "pending") void this.#cancelRemote(rec.approvalId, "\u7535\u8111\u4E0A\u5DF2\u5904\u7406");
2185
- return new Promise((resolve5) => {
2186
- void decided.then(resolve5);
2885
+ return new Promise((resolve6) => {
2886
+ void decided.then(resolve6);
2187
2887
  const onAbort = () => {
2188
2888
  if (this.#waiters.has(rec.approvalId)) this.#settleWindow(rec, "hook \u5DF2\u7ED3\u675F");
2189
- resolve5(none);
2889
+ resolve6(none);
2190
2890
  };
2191
2891
  if (signal.aborted) onAbort();
2192
2892
  else signal.addEventListener("abort", onAbort, { once: true });
@@ -2447,13 +3147,13 @@ function realpathNearest(p) {
2447
3147
  const tail = [];
2448
3148
  for (let i = 0; i < 64; i++) {
2449
3149
  try {
2450
- if (existsSync9(cur)) return join11(realpathSync2(cur), ...tail.reverse());
3150
+ if (existsSync12(cur)) return join14(realpathSync2(cur), ...tail.reverse());
2451
3151
  } catch {
2452
3152
  return p;
2453
3153
  }
2454
- const parent = dirname5(cur);
3154
+ const parent = dirname8(cur);
2455
3155
  if (parent === cur) return p;
2456
- tail.push(basename5(cur));
3156
+ tail.push(basename6(cur));
2457
3157
  cur = parent;
2458
3158
  }
2459
3159
  return p;
@@ -2470,11 +3170,11 @@ function cwdForms(cwd) {
2470
3170
  // src/daemon/gate/server.ts
2471
3171
  import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
2472
3172
  import { createServer } from "http";
2473
- import { mkdirSync as mkdirSync9, renameSync as renameSync7, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
2474
- import { join as join12 } from "path";
3173
+ import { mkdirSync as mkdirSync11, renameSync as renameSync8, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "fs";
3174
+ import { join as join15 } from "path";
2475
3175
  var MAX_BODY_BYTES = 8 * 1024 * 1024;
2476
3176
  function gateConfPath() {
2477
- return join12(deskbuddyHome(), "gate.json");
3177
+ return join15(deskbuddyHome(), "gate.json");
2478
3178
  }
2479
3179
  function tokenEquals(a, b) {
2480
3180
  const x = Buffer.from(a);
@@ -2485,39 +3185,46 @@ var GateServer = class {
2485
3185
  #handler;
2486
3186
  #token = randomBytes3(32).toString("hex");
2487
3187
  #server = null;
3188
+ #inflight = 0;
3189
+ /** 自动升级排空中:所有请求直接 503(远程 gate → exit 2 拦下;窗口 gate → 退回窗口里本地确认) */
3190
+ refusing = false;
2488
3191
  constructor(handler) {
2489
3192
  this.#handler = handler;
2490
3193
  }
2491
3194
  get token() {
2492
3195
  return this.#token;
2493
3196
  }
3197
+ /** 正在处理(多半是挂着等设备批准)的授权请求数。自动升级的空闲判定用(spec 2026-10-01 §4.3) */
3198
+ get inflight() {
3199
+ return this.#inflight;
3200
+ }
2494
3201
  /** 起监听并写 gate.json。返回实际端口 */
2495
3202
  async start() {
2496
3203
  const server = createServer((req2, res) => void this.#serve(req2, res));
2497
- await new Promise((resolve5, reject) => {
3204
+ await new Promise((resolve6, reject) => {
2498
3205
  server.once("error", reject);
2499
- server.listen(0, "127.0.0.1", () => resolve5());
3206
+ server.listen(0, "127.0.0.1", () => resolve6());
2500
3207
  });
2501
3208
  this.#server = server;
2502
3209
  const addr = server.address();
2503
3210
  const port = typeof addr === "object" && addr ? addr.port : 0;
2504
- mkdirSync9(deskbuddyHome(), { recursive: true });
3211
+ mkdirSync11(deskbuddyHome(), { recursive: true });
2505
3212
  const tmp = gateConfPath() + ".tmp";
2506
- writeFileSync9(tmp, JSON.stringify({ port, token: this.#token, pid: process.pid }), {
3213
+ writeFileSync10(tmp, JSON.stringify({ port, token: this.#token, pid: process.pid }), {
2507
3214
  encoding: "utf8",
2508
3215
  mode: 384
2509
3216
  });
2510
- renameSync7(tmp, gateConfPath());
3217
+ renameSync8(tmp, gateConfPath());
2511
3218
  return port;
2512
3219
  }
2513
3220
  async stop() {
2514
3221
  try {
2515
- rmSync4(gateConfPath(), { force: true });
3222
+ rmSync6(gateConfPath(), { force: true });
2516
3223
  } catch {
2517
3224
  }
2518
3225
  const s = this.#server;
2519
3226
  this.#server = null;
2520
- if (s) await new Promise((resolve5) => s.close(() => resolve5()));
3227
+ if (s) await new Promise((resolve6) => s.close(() => resolve6()));
2521
3228
  }
2522
3229
  async #serve(req2, res) {
2523
3230
  const reply = (status, body2) => {
@@ -2528,6 +3235,7 @@ var GateServer = class {
2528
3235
  if (req2.method !== "POST" || req2.url !== "/gate") return reply(404);
2529
3236
  const token = req2.headers["x-gate-token"];
2530
3237
  if (typeof token !== "string" || !tokenEquals(token, this.#token)) return reply(403);
3238
+ if (this.refusing) return reply(503);
2531
3239
  let body = "";
2532
3240
  let size = 0;
2533
3241
  try {
@@ -2552,6 +3260,7 @@ var GateServer = class {
2552
3260
  res.on("close", () => {
2553
3261
  if (!res.writableEnded) ac.abort();
2554
3262
  });
3263
+ this.#inflight++;
2555
3264
  try {
2556
3265
  const req3 = window ? {
2557
3266
  window: true,
@@ -2562,6 +3271,8 @@ var GateServer = class {
2562
3271
  reply(200, out);
2563
3272
  } catch {
2564
3273
  reply(500);
3274
+ } finally {
3275
+ this.#inflight--;
2565
3276
  }
2566
3277
  }
2567
3278
  };
@@ -2576,6 +3287,7 @@ var PARAM = {
2576
3287
  deviceName: "ShiFeng-Device-Name",
2577
3288
  nonce: "ShiFeng-Nonce"
2578
3289
  };
3290
+ var CLI_VERSION_PARAM = "cliVersion";
2579
3291
  var WS_PATH = "/deskbuddy";
2580
3292
  function sanitizeParamValue(value) {
2581
3293
  return (value || "").replace(/[=&]/g, "-");
@@ -2616,21 +3328,23 @@ function buildHandshakeUrl(cfg, opts = {}) {
2616
3328
  [PARAM.signature, p.signature],
2617
3329
  [PARAM.deviceId, p.deviceId],
2618
3330
  [PARAM.deviceName, p.deviceName],
2619
- [PARAM.nonce, p.nonce]
3331
+ [PARAM.nonce, p.nonce],
3332
+ // 不参与签名,服务端可据此刷新 DeskBuddyClient.cliVersion(自动升级 spec 2026-10-01 §4.6)
3333
+ [CLI_VERSION_PARAM, packageVersion()]
2620
3334
  ].map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
2621
3335
  return `${toWsOrigin(cfg.apiBase)}${WS_PATH}?${query}`;
2622
3336
  }
2623
3337
 
2624
3338
  // src/daemon/log.ts
2625
- import { appendFileSync, mkdirSync as mkdirSync10, statSync as statSync3, truncateSync } from "fs";
2626
- import { join as join13 } from "path";
3339
+ import { appendFileSync, mkdirSync as mkdirSync12, statSync as statSync4, truncateSync } from "fs";
3340
+ import { join as join16 } from "path";
2627
3341
  var MAX_LOG_BYTES = 2 * 1024 * 1024;
2628
3342
  function logPath() {
2629
- return join13(deskbuddyHome(), "daemon.log");
3343
+ return join16(deskbuddyHome(), "daemon.log");
2630
3344
  }
2631
3345
  function rotateIfNeeded(path) {
2632
3346
  try {
2633
- if (statSync3(path).size > MAX_LOG_BYTES) truncateSync(path, 0);
3347
+ if (statSync4(path).size > MAX_LOG_BYTES) truncateSync(path, 0);
2634
3348
  } catch {
2635
3349
  }
2636
3350
  }
@@ -2638,7 +3352,7 @@ function log(line) {
2638
3352
  const stamped = `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
2639
3353
  `;
2640
3354
  try {
2641
- mkdirSync10(deskbuddyHome(), { recursive: true });
3355
+ mkdirSync12(deskbuddyHome(), { recursive: true });
2642
3356
  const p = logPath();
2643
3357
  rotateIfNeeded(p);
2644
3358
  appendFileSync(p, stamped, "utf8");
@@ -2696,18 +3410,18 @@ var SessionQueue = class {
2696
3410
  };
2697
3411
 
2698
3412
  // src/daemon/remote.ts
2699
- import { mkdirSync as mkdirSync11, readFileSync as readFileSync11, renameSync as renameSync8, writeFileSync as writeFileSync10 } from "fs";
2700
- import { join as join14 } from "path";
3413
+ import { mkdirSync as mkdirSync13, readFileSync as readFileSync12, renameSync as renameSync9, writeFileSync as writeFileSync11 } from "fs";
3414
+ import { join as join17 } from "path";
2701
3415
  function handleDir() {
2702
- return join14(deskbuddyHome(), "remote");
3416
+ return join17(deskbuddyHome(), "remote");
2703
3417
  }
2704
3418
  function handlePath(tool) {
2705
3419
  const safe = (tool || "").replace(/[^0-9a-zA-Z_-]/g, "");
2706
- return join14(handleDir(), `${safe || "unknown"}.json`);
3420
+ return join17(handleDir(), `${safe || "unknown"}.json`);
2707
3421
  }
2708
3422
  function readHandle(tool) {
2709
3423
  try {
2710
- const h = JSON.parse(readFileSync11(handlePath(tool), "utf8"));
3424
+ const h = JSON.parse(readFileSync12(handlePath(tool), "utf8"));
2711
3425
  if (typeof h?.sessionId !== "string" || !h.sessionId) return null;
2712
3426
  if (typeof h?.cwd !== "string" || !h.cwd) return null;
2713
3427
  return h;
@@ -2717,10 +3431,10 @@ function readHandle(tool) {
2717
3431
  }
2718
3432
  function writeHandle(tool, handle) {
2719
3433
  try {
2720
- mkdirSync11(handleDir(), { recursive: true });
3434
+ mkdirSync13(handleDir(), { recursive: true });
2721
3435
  const tmp = handlePath(tool) + ".tmp";
2722
- writeFileSync10(tmp, JSON.stringify(handle), "utf8");
2723
- renameSync8(tmp, handlePath(tool));
3436
+ writeFileSync11(tmp, JSON.stringify(handle), "utf8");
3437
+ renameSync9(tmp, handlePath(tool));
2724
3438
  } catch {
2725
3439
  }
2726
3440
  }
@@ -2773,11 +3487,11 @@ function failureMessage(reason) {
2773
3487
  }
2774
3488
 
2775
3489
  // src/daemon/seen.ts
2776
- import { mkdirSync as mkdirSync12, readFileSync as readFileSync12, renameSync as renameSync9, writeFileSync as writeFileSync11 } from "fs";
2777
- import { join as join15 } from "path";
3490
+ import { mkdirSync as mkdirSync14, readFileSync as readFileSync13, renameSync as renameSync10, writeFileSync as writeFileSync12 } from "fs";
3491
+ import { join as join18 } from "path";
2778
3492
  var SEEN_CAPACITY = 200;
2779
3493
  function seenPath() {
2780
- return join15(deskbuddyHome(), "seen.json");
3494
+ return join18(deskbuddyHome(), "seen.json");
2781
3495
  }
2782
3496
  var SeenIds = class _SeenIds {
2783
3497
  #ids = [];
@@ -2808,7 +3522,7 @@ var SeenIds = class _SeenIds {
2808
3522
  }
2809
3523
  static load() {
2810
3524
  try {
2811
- const raw = JSON.parse(readFileSync12(seenPath(), "utf8"));
3525
+ const raw = JSON.parse(readFileSync13(seenPath(), "utf8"));
2812
3526
  return new _SeenIds(Array.isArray(raw?.ids) ? raw.ids.filter((x) => typeof x === "string") : []);
2813
3527
  } catch {
2814
3528
  return new _SeenIds();
@@ -2816,17 +3530,17 @@ var SeenIds = class _SeenIds {
2816
3530
  }
2817
3531
  save() {
2818
3532
  try {
2819
- mkdirSync12(deskbuddyHome(), { recursive: true });
3533
+ mkdirSync14(deskbuddyHome(), { recursive: true });
2820
3534
  const tmp = seenPath() + ".tmp";
2821
- writeFileSync11(tmp, JSON.stringify({ ids: this.#ids }), "utf8");
2822
- renameSync9(tmp, seenPath());
3535
+ writeFileSync12(tmp, JSON.stringify({ ids: this.#ids }), "utf8");
3536
+ renameSync10(tmp, seenPath());
2823
3537
  } catch {
2824
3538
  }
2825
3539
  }
2826
3540
  };
2827
3541
 
2828
3542
  // src/daemon/codexWatch.ts
2829
- import { execFileSync as execFileSync7 } from "child_process";
3543
+ import { execFileSync as execFileSync9 } from "child_process";
2830
3544
  import WebSocket from "ws";
2831
3545
  var TURN_LIMIT_MS = 10 * 60 * 1e3;
2832
3546
  var QUEUE_MAX_MS = 10 * 60 * 1e3;
@@ -2871,7 +3585,7 @@ function discoverAppServers(configured) {
2871
3585
  return parseAppServerUrls((winProcs() ?? []).map((p) => p.cmdline).join("\n"));
2872
3586
  }
2873
3587
  try {
2874
- const out = execFileSync7("ps", ["-A", "-ww", "-o", "args="], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
3588
+ const out = execFileSync9("ps", ["-A", "-ww", "-o", "args="], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
2875
3589
  return parseAppServerUrls(out);
2876
3590
  } catch {
2877
3591
  return [];
@@ -3014,13 +3728,13 @@ var CodexWatch = class {
3014
3728
  }
3015
3729
  #request(method, params) {
3016
3730
  const id = this.#nextId++;
3017
- return new Promise((resolve5) => {
3731
+ return new Promise((resolve6) => {
3018
3732
  const timer = setTimeout(() => {
3019
3733
  this.#calls.delete(id);
3020
- resolve5({ error: { message: "timeout" }, local: true });
3734
+ resolve6({ error: { message: "timeout" }, local: true });
3021
3735
  }, REQUEST_TIMEOUT_MS);
3022
3736
  timer.unref?.();
3023
- this.#calls.set(id, { resolve: resolve5, timer });
3737
+ this.#calls.set(id, { resolve: resolve6, timer });
3024
3738
  this.#send({ id, method, params });
3025
3739
  });
3026
3740
  }
@@ -3243,7 +3957,7 @@ var CodexWatch = class {
3243
3957
  return { ...out, ...merged ? { merged } : {} };
3244
3958
  }
3245
3959
  #trackTurn(threadId, turnId) {
3246
- return new Promise((resolve5) => {
3960
+ return new Promise((resolve6) => {
3247
3961
  const limit = this.#d.turnLimitMs ?? TURN_LIMIT_MS;
3248
3962
  const step = this.#d.turnTickMs ?? 1e3;
3249
3963
  const t = {
@@ -3253,7 +3967,7 @@ var CodexWatch = class {
3253
3967
  used: 0,
3254
3968
  timer: null,
3255
3969
  grace: null,
3256
- done: (end, text, error) => resolve5({ sent: true, end, turnId, text, ...error ? { error } : {} })
3970
+ done: (end, text, error) => resolve6({ sent: true, end, turnId, text, ...error ? { error } : {} })
3257
3971
  };
3258
3972
  this.#turns.set(turnId, t);
3259
3973
  const early = this.#early.get(turnId);
@@ -3300,7 +4014,7 @@ var CodexWatch = class {
3300
4014
  t.done(end, text || (end === "completed" ? "" : finalReply(t.items)), error);
3301
4015
  }
3302
4016
  #waitThread(threadId, ms) {
3303
- return new Promise((resolve5) => {
4017
+ return new Promise((resolve6) => {
3304
4018
  let set = this.#wake.get(threadId);
3305
4019
  if (!set) this.#wake.set(threadId, set = /* @__PURE__ */ new Set());
3306
4020
  const s = set;
@@ -3308,7 +4022,7 @@ var CodexWatch = class {
3308
4022
  clearTimeout(timer);
3309
4023
  s.delete(fire);
3310
4024
  if (s.size === 0 && this.#wake.get(threadId) === s) this.#wake.delete(threadId);
3311
- resolve5();
4025
+ resolve6();
3312
4026
  };
3313
4027
  const timer = setTimeout(fire, ms);
3314
4028
  timer.unref?.();
@@ -3481,43 +4195,43 @@ var CodexWatch = class {
3481
4195
  };
3482
4196
 
3483
4197
  // src/core/codexRemote.ts
3484
- import { mkdirSync as mkdirSync13, renameSync as renameSync10, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "fs";
3485
- import { join as join16 } from "path";
4198
+ import { mkdirSync as mkdirSync15, renameSync as renameSync11, rmSync as rmSync7, writeFileSync as writeFileSync13 } from "fs";
4199
+ import { join as join19 } from "path";
3486
4200
  function codexRemotePath() {
3487
- return join16(deskbuddyHome(), "codex-remote.json");
4201
+ return join19(deskbuddyHome(), "codex-remote.json");
3488
4202
  }
3489
4203
  function writeCodexRemote(threads, pid = process.pid, now = Date.now()) {
3490
4204
  try {
3491
- mkdirSync13(deskbuddyHome(), { recursive: true });
4205
+ mkdirSync15(deskbuddyHome(), { recursive: true });
3492
4206
  const p = codexRemotePath();
3493
- writeFileSync12(p + ".tmp", JSON.stringify({ pid, threads: [...threads], at: now }), "utf8");
3494
- renameSync10(p + ".tmp", p);
4207
+ writeFileSync13(p + ".tmp", JSON.stringify({ pid, threads: [...threads], at: now }), "utf8");
4208
+ renameSync11(p + ".tmp", p);
3495
4209
  } catch {
3496
4210
  }
3497
4211
  }
3498
4212
  function removeCodexRemote() {
3499
4213
  try {
3500
- rmSync5(codexRemotePath(), { force: true });
4214
+ rmSync7(codexRemotePath(), { force: true });
3501
4215
  } catch {
3502
4216
  }
3503
4217
  }
3504
4218
 
3505
4219
  // src/wrap/channel.ts
3506
4220
  import { randomBytes as randomBytes5 } from "crypto";
3507
- import { chmodSync as chmodSync3, rmSync as rmSync7 } from "fs";
4221
+ import { chmodSync as chmodSync4, rmSync as rmSync9 } from "fs";
3508
4222
  import { connect, createServer as createServer2 } from "net";
3509
4223
 
3510
4224
  // src/core/wake.ts
3511
- import { execFileSync as execFileSync8 } from "child_process";
4225
+ import { execFileSync as execFileSync10 } from "child_process";
3512
4226
  import { createHash as createHash3, createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
3513
- import { chmodSync as chmodSync2, closeSync as closeSync2, mkdirSync as mkdirSync14, openSync as openSync2, readdirSync as readdirSync3, readFileSync as readFileSync13, readSync as readSync2, renameSync as renameSync11, rmSync as rmSync6, statSync as statSync4, writeFileSync as writeFileSync13 } from "fs";
4227
+ import { chmodSync as chmodSync3, closeSync as closeSync3, mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync4, readFileSync as readFileSync14, readSync as readSync2, renameSync as renameSync12, rmSync as rmSync8, statSync as statSync5, writeFileSync as writeFileSync14 } from "fs";
3514
4228
  import { tmpdir as tmpdir2 } from "os";
3515
- import { join as join17 } from "path";
4229
+ import { join as join20 } from "path";
3516
4230
  function wakeDir(home = deskbuddyHome()) {
3517
- return join17(home, "wake");
4231
+ return join20(home, "wake");
3518
4232
  }
3519
4233
  function marksDir(home = deskbuddyHome()) {
3520
- return join17(wakeDir(home), "marks");
4234
+ return join20(wakeDir(home), "marks");
3521
4235
  }
3522
4236
  function safeSid(sessionId) {
3523
4237
  return String(sessionId).replace(/[^A-Za-z0-9_-]/g, "");
@@ -3527,10 +4241,10 @@ function homeTag(home) {
3527
4241
  }
3528
4242
  function wakeChannelPath(platform = process.platform, home = deskbuddyHome()) {
3529
4243
  if (platform === "win32") return `\\\\.\\pipe\\deskbuddy-wake-${homeTag(home)}`;
3530
- const primary = join17(wakeDir(home), "wake.sock");
4244
+ const primary = join20(wakeDir(home), "wake.sock");
3531
4245
  if (Buffer.byteLength(primary) <= 103) return primary;
3532
4246
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
3533
- return join17(tmpdir2(), `deskbuddy-${uid}`, `wake-${homeTag(home)}.sock`);
4247
+ return join20(tmpdir2(), `deskbuddy-${uid}`, `wake-${homeTag(home)}.sock`);
3534
4248
  }
3535
4249
  function pidExists(pid) {
3536
4250
  if (!Number.isInteger(pid) || pid <= 0) return false;
@@ -3545,11 +4259,11 @@ function writeWakeMark(sessionId, agentPid, now = Date.now()) {
3545
4259
  const safe = safeSid(sessionId);
3546
4260
  if (!safe) return;
3547
4261
  try {
3548
- mkdirSync14(marksDir(), { recursive: true, mode: 448 });
3549
- const file = join17(marksDir(), `${safe}.json`);
4262
+ mkdirSync16(marksDir(), { recursive: true, mode: 448 });
4263
+ const file = join20(marksDir(), `${safe}.json`);
3550
4264
  const tmp = `${file}.${process.pid}.tmp`;
3551
- writeFileSync13(tmp, JSON.stringify({ agentPid: agentPid ?? 0, daemonPid: process.pid, at: now }), { encoding: "utf8", mode: 384 });
3552
- renameSync11(tmp, file);
4265
+ writeFileSync14(tmp, JSON.stringify({ agentPid: agentPid ?? 0, daemonPid: process.pid, at: now }), { encoding: "utf8", mode: 384 });
4266
+ renameSync12(tmp, file);
3553
4267
  } catch {
3554
4268
  }
3555
4269
  }
@@ -3557,20 +4271,20 @@ function removeWakeMark(sessionId) {
3557
4271
  const safe = safeSid(sessionId);
3558
4272
  if (!safe) return;
3559
4273
  try {
3560
- rmSync6(join17(marksDir(), `${safe}.json`), { force: true });
4274
+ rmSync8(join20(marksDir(), `${safe}.json`), { force: true });
3561
4275
  } catch {
3562
4276
  }
3563
4277
  }
3564
4278
  function clearWakeMarks() {
3565
4279
  let names = [];
3566
4280
  try {
3567
- names = readdirSync3(marksDir());
4281
+ names = readdirSync4(marksDir());
3568
4282
  } catch {
3569
4283
  return;
3570
4284
  }
3571
4285
  for (const n of names) {
3572
4286
  try {
3573
- rmSync6(join17(marksDir(), n), { force: true });
4287
+ rmSync8(join20(marksDir(), n), { force: true });
3574
4288
  } catch {
3575
4289
  }
3576
4290
  }
@@ -3579,7 +4293,7 @@ function isWakeMarked(sessionId, alive = pidExists) {
3579
4293
  const safe = safeSid(sessionId);
3580
4294
  if (!safe) return false;
3581
4295
  try {
3582
- const m = JSON.parse(readFileSync13(join17(marksDir(), `${safe}.json`), "utf8"));
4296
+ const m = JSON.parse(readFileSync14(join20(marksDir(), `${safe}.json`), "utf8"));
3583
4297
  const daemonPid = Number(m?.daemonPid) || 0;
3584
4298
  if (!daemonPid || !alive(daemonPid)) return false;
3585
4299
  const pid = Number(m?.agentPid) || 0;
@@ -3592,30 +4306,30 @@ function withWake(states, alive) {
3592
4306
  return states.map((s) => s.remoteWindow === true || !isWakeMarked(s.sessionId, alive) ? s : { ...s, remoteWindow: true });
3593
4307
  }
3594
4308
  function wakeSecretPath(home = deskbuddyHome()) {
3595
- return join17(wakeDir(home), "secret");
4309
+ return join20(wakeDir(home), "secret");
3596
4310
  }
3597
4311
  function writeWakeSecret(hex, platform = process.platform) {
3598
4312
  const file = wakeSecretPath();
3599
- mkdirSync14(wakeDir(), { recursive: true, mode: 448 });
4313
+ mkdirSync16(wakeDir(), { recursive: true, mode: 448 });
3600
4314
  const tmp = `${file}.${process.pid}.tmp`;
3601
- writeFileSync13(tmp, hex, { encoding: "utf8", mode: 384 });
4315
+ writeFileSync14(tmp, hex, { encoding: "utf8", mode: 384 });
3602
4316
  if (platform === "win32") {
3603
4317
  try {
3604
- execFileSync8("icacls", [tmp, "/inheritance:r", "/grant:r", `${currentUserSid()}:F`], { stdio: "ignore", windowsHide: true });
4318
+ execFileSync10("icacls", [tmp, "/inheritance:r", "/grant:r", `${currentUserSid()}:F`], { stdio: "ignore", windowsHide: true });
3605
4319
  } catch {
3606
- renameSync11(tmp, file);
4320
+ renameSync12(tmp, file);
3607
4321
  return "\u7A97\u53E3\u5524\u9192\u5BC6\u94A5\u6587\u4EF6\u7684\u6743\u9650\u6CA1\u80FD\u6536\u7D27\uFF08icacls \u5931\u8D25\uFF09";
3608
4322
  }
3609
4323
  } else {
3610
- chmodSync2(tmp, 384);
4324
+ chmodSync3(tmp, 384);
3611
4325
  }
3612
- renameSync11(tmp, file);
4326
+ renameSync12(tmp, file);
3613
4327
  return "";
3614
4328
  }
3615
4329
  function restrictToUser(file, platform = process.platform) {
3616
4330
  if (platform !== "win32") return true;
3617
4331
  try {
3618
- execFileSync8("icacls", [file, "/inheritance:r", "/grant:r", `${currentUserSid()}:F`], { stdio: "ignore", windowsHide: true });
4332
+ execFileSync10("icacls", [file, "/inheritance:r", "/grant:r", `${currentUserSid()}:F`], { stdio: "ignore", windowsHide: true });
3619
4333
  return true;
3620
4334
  } catch {
3621
4335
  return false;
@@ -3623,7 +4337,7 @@ function restrictToUser(file, platform = process.platform) {
3623
4337
  }
3624
4338
  function currentUserSid() {
3625
4339
  try {
3626
- const out = execFileSync8("whoami", ["/user", "/fo", "csv", "/nh"], { encoding: "utf8", windowsHide: true });
4340
+ const out = execFileSync10("whoami", ["/user", "/fo", "csv", "/nh"], { encoding: "utf8", windowsHide: true });
3627
4341
  const m = /"(S-1-[0-9-]+)"/.exec(out);
3628
4342
  if (m) return `*${m[1]}`;
3629
4343
  } catch {
@@ -3658,10 +4372,10 @@ function analyzeTranscript(file) {
3658
4372
  if (!file) return null;
3659
4373
  let fd;
3660
4374
  try {
3661
- const size = statSync4(file).size;
4375
+ const size = statSync5(file).size;
3662
4376
  const len = Math.min(size, TRANSCRIPT_TAIL2);
3663
4377
  const buf = Buffer.alloc(len);
3664
- fd = openSync2(file, "r");
4378
+ fd = openSync3(file, "r");
3665
4379
  readSync2(fd, buf, 0, len, size - len);
3666
4380
  const lines2 = buf.toString("utf8").split("\n");
3667
4381
  let seg = false;
@@ -3703,7 +4417,7 @@ function analyzeTranscript(file) {
3703
4417
  } finally {
3704
4418
  if (fd !== void 0) {
3705
4419
  try {
3706
- closeSync2(fd);
4420
+ closeSync3(fd);
3707
4421
  } catch {
3708
4422
  }
3709
4423
  }
@@ -3754,7 +4468,7 @@ function replyMac(secret, nonceC, r) {
3754
4468
  return wakeMac(secret, "r", nonceC, r.instructionId, r.status, r.reason ?? "");
3755
4469
  }
3756
4470
  function startChannelServer(path, secret, onRequest, platform = process.platform) {
3757
- return new Promise((resolve5) => {
4471
+ return new Promise((resolve6) => {
3758
4472
  const server = createServer2((sock) => {
3759
4473
  let open = true;
3760
4474
  let phase = "hello";
@@ -3805,7 +4519,7 @@ function startChannelServer(path, secret, onRequest, platform = process.platform
3805
4519
  });
3806
4520
  if (platform !== "win32") {
3807
4521
  try {
3808
- rmSync7(path, { force: true });
4522
+ rmSync9(path, { force: true });
3809
4523
  } catch {
3810
4524
  }
3811
4525
  }
@@ -3815,19 +4529,19 @@ function startChannelServer(path, secret, onRequest, platform = process.platform
3815
4529
  };
3816
4530
  server.once("error", () => {
3817
4531
  restore();
3818
- resolve5(null);
4532
+ resolve6(null);
3819
4533
  });
3820
4534
  server.listen(path, () => {
3821
4535
  restore();
3822
4536
  if (platform !== "win32") {
3823
4537
  try {
3824
- chmodSync3(path, 384);
4538
+ chmodSync4(path, 384);
3825
4539
  } catch {
3826
4540
  }
3827
4541
  }
3828
4542
  server.on("error", () => {
3829
4543
  });
3830
- resolve5(server);
4544
+ resolve6(server);
3831
4545
  });
3832
4546
  });
3833
4547
  }
@@ -3838,16 +4552,16 @@ function closeChannelServer(server, path, platform = process.platform) {
3838
4552
  }
3839
4553
  if (platform !== "win32") {
3840
4554
  try {
3841
- rmSync7(path, { force: true });
4555
+ rmSync9(path, { force: true });
3842
4556
  } catch {
3843
4557
  }
3844
4558
  }
3845
4559
  }
3846
4560
  var FINAL = /* @__PURE__ */ new Set(["started", "rejected", "failed", "not_here"]);
3847
4561
  function sendToWindow(path, secret, req2, onReply, opts = {}) {
3848
- return new Promise((resolve5) => {
4562
+ return new Promise((resolve6) => {
3849
4563
  if (!secret) {
3850
- resolve5("fallback");
4564
+ resolve6("fallback");
3851
4565
  return;
3852
4566
  }
3853
4567
  let settled = false;
@@ -3860,13 +4574,13 @@ function sendToWindow(path, secret, req2, onReply, opts = {}) {
3860
4574
  settled = true;
3861
4575
  handled = v === "handled";
3862
4576
  clearTimeout(timer);
3863
- resolve5(v);
4577
+ resolve6(v);
3864
4578
  };
3865
4579
  let sock;
3866
4580
  try {
3867
4581
  sock = connect(path);
3868
4582
  } catch {
3869
- resolve5("fallback");
4583
+ resolve6("fallback");
3870
4584
  return;
3871
4585
  }
3872
4586
  const timer = setTimeout(() => {
@@ -3988,29 +4702,29 @@ function boxMatches(view, body) {
3988
4702
  }
3989
4703
 
3990
4704
  // src/core/wrapWindows.ts
3991
- import { chmodSync as chmodSync4, mkdirSync as mkdirSync15, readdirSync as readdirSync4, readFileSync as readFileSync14, renameSync as renameSync12, rmSync as rmSync8, statSync as statSync5, writeFileSync as writeFileSync14 } from "fs";
4705
+ import { chmodSync as chmodSync5, mkdirSync as mkdirSync17, readdirSync as readdirSync5, readFileSync as readFileSync15, renameSync as renameSync13, rmSync as rmSync10, statSync as statSync6, writeFileSync as writeFileSync15 } from "fs";
3992
4706
  import { tmpdir as tmpdir3 } from "os";
3993
- import { join as join18 } from "path";
4707
+ import { join as join21 } from "path";
3994
4708
  var WINDOW_ID_RE = /^[a-f0-9]{32}$/;
3995
4709
  function windowsDir() {
3996
- return join18(deskbuddyHome(), "windows");
4710
+ return join21(deskbuddyHome(), "windows");
3997
4711
  }
3998
4712
  function bySessionDir() {
3999
- return join18(windowsDir(), "by-session");
4713
+ return join21(windowsDir(), "by-session");
4000
4714
  }
4001
4715
  function safeSid2(sessionId) {
4002
4716
  return String(sessionId).replace(/[^A-Za-z0-9_-]/g, "");
4003
4717
  }
4004
4718
  function ensureDir(dir) {
4005
- mkdirSync15(dir, { recursive: true, mode: 448 });
4719
+ mkdirSync17(dir, { recursive: true, mode: 448 });
4006
4720
  }
4007
4721
  function atomicWrite(file, text) {
4008
4722
  const tmp = `${file}.${process.pid}.tmp`;
4009
- writeFileSync14(tmp, text, { encoding: "utf8", mode: 384 });
4010
- renameSync12(tmp, file);
4723
+ writeFileSync15(tmp, text, { encoding: "utf8", mode: 384 });
4724
+ renameSync13(tmp, file);
4011
4725
  }
4012
4726
  function windowFile(windowId) {
4013
- return join18(windowsDir(), `${windowId}.json`);
4727
+ return join21(windowsDir(), `${windowId}.json`);
4014
4728
  }
4015
4729
  function writeWindow(windowId, rec) {
4016
4730
  if (!WINDOW_ID_RE.test(windowId)) return;
@@ -4025,7 +4739,7 @@ function windowSecret(windowId) {
4025
4739
  function readWindow(windowId) {
4026
4740
  if (!WINDOW_ID_RE.test(windowId)) return null;
4027
4741
  try {
4028
- const r = JSON.parse(readFileSync14(windowFile(windowId), "utf8"));
4742
+ const r = JSON.parse(readFileSync15(windowFile(windowId), "utf8"));
4029
4743
  return r && typeof r.pid === "number" ? r : null;
4030
4744
  } catch {
4031
4745
  return null;
@@ -4035,20 +4749,20 @@ function removeWindow(windowId) {
4035
4749
  if (!WINDOW_ID_RE.test(windowId)) return;
4036
4750
  for (const f of [windowFile(windowId), lastEventFile(windowId)]) {
4037
4751
  try {
4038
- rmSync8(f, { force: true });
4752
+ rmSync10(f, { force: true });
4039
4753
  } catch {
4040
4754
  }
4041
4755
  }
4042
4756
  let names = [];
4043
4757
  try {
4044
- names = readdirSync4(bySessionDir());
4758
+ names = readdirSync5(bySessionDir());
4045
4759
  } catch {
4046
4760
  return;
4047
4761
  }
4048
4762
  for (const n of names) {
4049
- const f = join18(bySessionDir(), n);
4763
+ const f = join21(bySessionDir(), n);
4050
4764
  try {
4051
- if (readFileSync14(f, "utf8").trim() === windowId) rmSync8(f, { force: true });
4765
+ if (readFileSync15(f, "utf8").trim() === windowId) rmSync10(f, { force: true });
4052
4766
  } catch {
4053
4767
  }
4054
4768
  }
@@ -4057,19 +4771,19 @@ function readBySession(sessionId) {
4057
4771
  const safe = safeSid2(sessionId);
4058
4772
  if (!safe) return null;
4059
4773
  try {
4060
- const id = readFileSync14(join18(bySessionDir(), safe), "utf8").trim();
4774
+ const id = readFileSync15(join21(bySessionDir(), safe), "utf8").trim();
4061
4775
  return WINDOW_ID_RE.test(id) ? id : null;
4062
4776
  } catch {
4063
4777
  return null;
4064
4778
  }
4065
4779
  }
4066
4780
  function lastEventFile(windowId) {
4067
- return join18(windowsDir(), `${windowId}.last`);
4781
+ return join21(windowsDir(), `${windowId}.last`);
4068
4782
  }
4069
4783
  function readLastEvent(windowId) {
4070
4784
  if (!WINDOW_ID_RE.test(windowId)) return null;
4071
4785
  try {
4072
- const e = JSON.parse(readFileSync14(lastEventFile(windowId), "utf8"));
4786
+ const e = JSON.parse(readFileSync15(lastEventFile(windowId), "utf8"));
4073
4787
  return e && typeof e.ev === "string" && typeof e.at === "number" ? { ev: e.ev, sid: String(e.sid ?? ""), at: e.at } : null;
4074
4788
  } catch {
4075
4789
  return null;
@@ -4078,18 +4792,18 @@ function readLastEvent(windowId) {
4078
4792
  function channelPath(windowId, platform = process.platform, home = deskbuddyHome()) {
4079
4793
  if (platform === "win32") return `\\\\.\\pipe\\deskbuddy-${windowId}`;
4080
4794
  const name = `${windowId.slice(0, 16)}.sock`;
4081
- const primary = join18(home, "win", name);
4795
+ const primary = join21(home, "win", name);
4082
4796
  if (Buffer.byteLength(primary) <= 103) return primary;
4083
4797
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
4084
- return join18(tmpdir3(), `deskbuddy-${uid}`, name);
4798
+ return join21(tmpdir3(), `deskbuddy-${uid}`, name);
4085
4799
  }
4086
4800
  function ensureChannelDir(sockPath) {
4087
- const dir = join18(sockPath, "..");
4801
+ const dir = join21(sockPath, "..");
4088
4802
  try {
4089
- mkdirSync15(dir, { recursive: true, mode: 448 });
4090
- const st = statSync5(dir);
4803
+ mkdirSync17(dir, { recursive: true, mode: 448 });
4804
+ const st = statSync6(dir);
4091
4805
  if (typeof process.getuid === "function" && st.uid !== process.getuid()) return false;
4092
- chmodSync4(dir, 448);
4806
+ chmodSync5(dir, 448);
4093
4807
  return true;
4094
4808
  } catch {
4095
4809
  return false;
@@ -4106,35 +4820,35 @@ function windowForSession(sessionId) {
4106
4820
  }
4107
4821
 
4108
4822
  // src/core/launch.ts
4109
- import { mkdirSync as mkdirSync16, readFileSync as readFileSync15, renameSync as renameSync13, rmSync as rmSync9, writeFileSync as writeFileSync15 } from "fs";
4110
- import { join as join19 } from "path";
4823
+ import { mkdirSync as mkdirSync18, readFileSync as readFileSync16, renameSync as renameSync14, rmSync as rmSync11, writeFileSync as writeFileSync16 } from "fs";
4824
+ import { join as join22 } from "path";
4111
4825
  var RUN_ID_RE = /^[a-f0-9]{32}$/;
4112
4826
  var SESSION_UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
4113
4827
  function launchDir(home = deskbuddyHome()) {
4114
- return join19(home, "launch");
4828
+ return join22(home, "launch");
4115
4829
  }
4116
4830
  function planFile(runId) {
4117
- return join19(launchDir(), `${runId}.json`);
4831
+ return join22(launchDir(), `${runId}.json`);
4118
4832
  }
4119
4833
  function writeLaunch(runId, plan) {
4120
4834
  if (!RUN_ID_RE.test(runId)) throw new Error("bad runId");
4121
4835
  if (plan.resume && !SESSION_UUID_RE.test(plan.resume)) throw new Error("bad session id");
4122
- mkdirSync16(launchDir(), { recursive: true, mode: 448 });
4836
+ mkdirSync18(launchDir(), { recursive: true, mode: 448 });
4123
4837
  const tmp = `${planFile(runId)}.${process.pid}.tmp`;
4124
- writeFileSync15(tmp, JSON.stringify(plan), { encoding: "utf8", mode: 384 });
4838
+ writeFileSync16(tmp, JSON.stringify(plan), { encoding: "utf8", mode: 384 });
4125
4839
  restrictToUser(tmp);
4126
- renameSync13(tmp, planFile(runId));
4840
+ renameSync14(tmp, planFile(runId));
4127
4841
  }
4128
4842
  function claimLaunch(runId) {
4129
4843
  if (!RUN_ID_RE.test(runId)) return null;
4130
- const claimed = join19(launchDir(), `${runId}.${process.pid}.claimed`);
4844
+ const claimed = join22(launchDir(), `${runId}.${process.pid}.claimed`);
4131
4845
  try {
4132
- renameSync13(planFile(runId), claimed);
4846
+ renameSync14(planFile(runId), claimed);
4133
4847
  } catch {
4134
4848
  return null;
4135
4849
  }
4136
4850
  try {
4137
- const p = JSON.parse(readFileSync15(claimed, "utf8"));
4851
+ const p = JSON.parse(readFileSync16(claimed, "utf8"));
4138
4852
  if (!p || typeof p.cwd !== "string" || typeof p.resume !== "string") return null;
4139
4853
  if (p.resume && !SESSION_UUID_RE.test(p.resume)) return null;
4140
4854
  return p;
@@ -4142,14 +4856,14 @@ function claimLaunch(runId) {
4142
4856
  return null;
4143
4857
  } finally {
4144
4858
  try {
4145
- rmSync9(claimed, { force: true });
4859
+ rmSync11(claimed, { force: true });
4146
4860
  } catch {
4147
4861
  }
4148
4862
  }
4149
4863
  }
4150
4864
  function launchPending(runId) {
4151
4865
  try {
4152
- readFileSync15(planFile(runId));
4866
+ readFileSync16(planFile(runId));
4153
4867
  return true;
4154
4868
  } catch {
4155
4869
  return false;
@@ -4157,10 +4871,10 @@ function launchPending(runId) {
4157
4871
  }
4158
4872
 
4159
4873
  // src/daemon/openWindow.ts
4160
- import { execFile as execFile3, execFileSync as execFileSync9, spawn as spawn2 } from "child_process";
4161
- import { chmodSync as chmodSync5, mkdirSync as mkdirSync17, rmSync as rmSync10, writeFileSync as writeFileSync16 } from "fs";
4874
+ import { execFile as execFile4, execFileSync as execFileSync11, spawn as spawn3 } from "child_process";
4875
+ import { chmodSync as chmodSync6, mkdirSync as mkdirSync19, rmSync as rmSync12, writeFileSync as writeFileSync17 } from "fs";
4162
4876
  import { userInfo } from "os";
4163
- import { join as join20 } from "path";
4877
+ import { join as join23 } from "path";
4164
4878
  function safePath(p) {
4165
4879
  return /^(?:[A-Za-z]:[\\/]|\/)/.test(p) && !/[\r\n\0"'`$%;]/.test(p);
4166
4880
  }
@@ -4208,14 +4922,14 @@ var defaultGuiDeps = () => ({
4208
4922
  env: process.env,
4209
4923
  macManager: () => {
4210
4924
  try {
4211
- return execFileSync9("launchctl", ["managername"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 3e3 });
4925
+ return execFileSync11("launchctl", ["managername"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 3e3 });
4212
4926
  } catch {
4213
4927
  return "";
4214
4928
  }
4215
4929
  },
4216
4930
  macConsoleUser: () => {
4217
4931
  try {
4218
- return execFileSync9("stat", ["-f", "%Su", "/dev/console"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 3e3 });
4932
+ return execFileSync11("stat", ["-f", "%Su", "/dev/console"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 3e3 });
4219
4933
  } catch {
4220
4934
  return "";
4221
4935
  }
@@ -4229,7 +4943,7 @@ var defaultGuiDeps = () => ({
4229
4943
  },
4230
4944
  winSessionId: () => {
4231
4945
  try {
4232
- const out = execFileSync9("tasklist", ["/FI", `PID eq ${process.pid}`, "/FO", "CSV", "/NH"], {
4946
+ const out = execFileSync11("tasklist", ["/FI", `PID eq ${process.pid}`, "/FO", "CSV", "/NH"], {
4233
4947
  encoding: "utf8",
4234
4948
  stdio: ["ignore", "pipe", "ignore"],
4235
4949
  windowsHide: true,
@@ -4246,11 +4960,11 @@ var defaultGuiDeps = () => ({
4246
4960
  var defaultOpenerDeps = (findWt2) => ({
4247
4961
  platform: process.platform,
4248
4962
  findWt: findWt2,
4249
- run: (file, args) => new Promise((resolve5, reject) => {
4250
- execFile3(file, args, { windowsHide: true, timeout: 15e3 }, (err) => err ? reject(err) : resolve5());
4963
+ run: (file, args) => new Promise((resolve6, reject) => {
4964
+ execFile4(file, args, { windowsHide: true, timeout: 15e3 }, (err) => err ? reject(err) : resolve6());
4251
4965
  }),
4252
4966
  spawnDetached: (file, args) => {
4253
- const c = spawn2(file, args, { detached: true, stdio: "ignore", shell: false, windowsHide: false });
4967
+ const c = spawn3(file, args, { detached: true, stdio: "ignore", shell: false, windowsHide: false });
4254
4968
  c.on("error", () => {
4255
4969
  });
4256
4970
  c.unref();
@@ -4259,10 +4973,10 @@ var defaultOpenerDeps = (findWt2) => ({
4259
4973
  async function openTerminal(t, d) {
4260
4974
  checkTarget(t);
4261
4975
  if (d.platform === "darwin") {
4262
- mkdirSync17(launchDir(t.home), { recursive: true, mode: 448 });
4263
- const file = join20(launchDir(t.home), `${t.runId}.command`);
4264
- writeFileSync16(file, macCommandScript(t), { encoding: "utf8", mode: 448 });
4265
- chmodSync5(file, 448);
4976
+ mkdirSync19(launchDir(t.home), { recursive: true, mode: 448 });
4977
+ const file = join23(launchDir(t.home), `${t.runId}.command`);
4978
+ writeFileSync17(file, macCommandScript(t), { encoding: "utf8", mode: 448 });
4979
+ chmodSync6(file, 448);
4266
4980
  await d.run("open", ["-a", "Terminal", file]);
4267
4981
  return "terminal";
4268
4982
  }
@@ -4280,14 +4994,14 @@ async function openTerminal(t, d) {
4280
4994
  function removeCommandFile(home, runId) {
4281
4995
  if (!RUN_ID_RE.test(runId)) return;
4282
4996
  try {
4283
- rmSync10(join20(launchDir(home), `${runId}.command`), { force: true });
4997
+ rmSync12(join23(launchDir(home), `${runId}.command`), { force: true });
4284
4998
  } catch {
4285
4999
  }
4286
5000
  }
4287
5001
 
4288
5002
  // src/daemon/wake.ts
4289
5003
  import { randomBytes as randomBytes6 } from "crypto";
4290
- import { rmSync as rmSync11 } from "fs";
5004
+ import { rmSync as rmSync13 } from "fs";
4291
5005
  import { createServer as createServer3 } from "net";
4292
5006
  var BUSY_STATES = /* @__PURE__ */ new Set(["watch", "ask_approval"]);
4293
5007
  var STOP_MIN_MS = 1500;
@@ -4312,6 +5026,14 @@ var WakeHub = class {
4312
5026
  const s = this.#slots.get(sessionId);
4313
5027
  return !!s && (!!s.reg || !!s.flight);
4314
5028
  }
5029
+ /**
5030
+ * 有没有 daemon 交出去、还没落定的指令(排队中 / 交付中 / 唤醒后正在跑的那一轮)。
5031
+ * 只登记着等待者不算忙:那是用户自己的窗口。自动升级的空闲判定用(spec 2026-10-01 §4.3)
5032
+ */
5033
+ get busy() {
5034
+ for (const s of this.#slots.values()) if (s.queue.length > 0 || s.flight || s.reg?.pending) return true;
5035
+ return false;
5036
+ }
4315
5037
  /** 交给唤醒路由。返回 false = 这个会话没有等待者,调用方走别的路 */
4316
5038
  submit(cmd, sessionId) {
4317
5039
  if (cmd.tool !== "claude_code") return false;
@@ -4574,11 +5296,11 @@ function startWakeServer(path, hub, secret, platform = process.platform) {
4574
5296
  if (platform !== "win32") {
4575
5297
  if (!ensureChannelDir(path)) return Promise.resolve(null);
4576
5298
  try {
4577
- rmSync11(path, { force: true });
5299
+ rmSync13(path, { force: true });
4578
5300
  } catch {
4579
5301
  }
4580
5302
  }
4581
- return new Promise((resolve5) => {
5303
+ return new Promise((resolve6) => {
4582
5304
  const server = createServer3((sock) => {
4583
5305
  sock.on("error", () => {
4584
5306
  });
@@ -4664,18 +5386,19 @@ function startWakeServer(path, hub, secret, platform = process.platform) {
4664
5386
  };
4665
5387
  server.once("error", () => {
4666
5388
  restore();
4667
- resolve5(null);
5389
+ resolve6(null);
4668
5390
  });
4669
5391
  server.listen(path, () => {
4670
5392
  restore();
4671
5393
  server.on("error", () => {
4672
5394
  });
4673
- resolve5(server);
5395
+ resolve6(server);
4674
5396
  });
4675
5397
  });
4676
5398
  }
4677
5399
 
4678
5400
  // src/daemon/index.ts
5401
+ var UPDATING_REASON = "updating";
4679
5402
  var IDLE_TIMEOUT_MS = 150 * 1e3;
4680
5403
  var POLICY_VIOLATION = 1008;
4681
5404
  var PING_FRAME_SERVICE = "ping";
@@ -4691,7 +5414,7 @@ function defaultCodexAppServers() {
4691
5414
  return cfg.enabledTools.includes("codex") ? discoverAppServers(cfg.codexAppServer) : [];
4692
5415
  }
4693
5416
  function opencodeReady() {
4694
- return isOcPluginInstalled(opencodePluginPath()) && existsSync10(ocPluginModule(installedHookPath()));
5417
+ return isOcPluginInstalled(opencodePluginPath()) && existsSync13(ocPluginModule(installedHookPath()));
4695
5418
  }
4696
5419
  function orphanResumeText(summary2) {
4697
5420
  return `\uFF08\u4F60\u5728\u8BBE\u5907\u4E0A\u5DF2\u6279\u51C6\uFF1A${summary2}\uFF09\u8BF7\u7EE7\u7EED\u521A\u624D\u88AB\u4E2D\u65AD\u7684\u64CD\u4F5C\u3002`;
@@ -4726,6 +5449,10 @@ var Daemon = class {
4726
5449
  #codex = /* @__PURE__ */ new Map();
4727
5450
  #codexStarted = false;
4728
5451
  #codexReportTimer = null;
5452
+ /** daemon 交出去、还没有结局的指令流程数(开窗 / 写进窗口 / 经 app-server 发一轮)。空闲判定用 */
5453
+ #inflight = 0;
5454
+ /** 自动升级排空中:不再接新指令、新授权请求 */
5455
+ #draining = false;
4729
5456
  constructor(deps = {}) {
4730
5457
  this.#deps = {
4731
5458
  connect: deps.connect ?? ((url) => new WebSocket2(url)),
@@ -4754,6 +5481,8 @@ var Daemon = class {
4754
5481
  windowReadyMs: deps.windowReadyMs ?? WINDOW_READY_MS,
4755
5482
  wakePath: deps.wakePath === void 0 ? wakeChannelPath() : deps.wakePath,
4756
5483
  onWindowDone: deps.onWindowDone ?? (() => {
5484
+ }),
5485
+ onLink: deps.onLink ?? (() => {
4757
5486
  })
4758
5487
  };
4759
5488
  this.#wake = new WakeHub({
@@ -4946,6 +5675,45 @@ var Daemon = class {
4946
5675
  get coordinator() {
4947
5676
  return this.#coord;
4948
5677
  }
5678
+ /** 把一段异步的指令流程记进 #inflight,结束(成功或失败)时减掉 */
5679
+ #track(p) {
5680
+ this.#inflight++;
5681
+ return p.finally(() => {
5682
+ this.#inflight--;
5683
+ });
5684
+ }
5685
+ /**
5686
+ * 进入排空(判定空闲之后、交给新版本切换之前):新指令回报 rejected/updating 且不记 seen,
5687
+ * gate 请求直接拒(远程模式本来也没有在跑的进程;窗口模式拒了会退回窗口里本地确认)
5688
+ */
5689
+ beginDrain() {
5690
+ this.#draining = true;
5691
+ this.#gate.refusing = true;
5692
+ log("\u8FDB\u5165\u5347\u7EA7\u6392\u7A7A\uFF1A\u4E0D\u518D\u63A5\u65B0\u6307\u4EE4");
5693
+ }
5694
+ /** 退出排空(切换失败) */
5695
+ endDrain() {
5696
+ if (!this.#draining) return;
5697
+ this.#draining = false;
5698
+ this.#gate.refusing = false;
5699
+ log("\u9000\u51FA\u5347\u7EA7\u6392\u7A7A\uFF0C\u6062\u590D\u63A5\u6307\u4EE4");
5700
+ }
5701
+ get draining() {
5702
+ return this.#draining;
5703
+ }
5704
+ /**
5705
+ * 能不能现在重启(自动升级 spec 2026-10-01 §4.3):daemon 手上没有任何在途的设备指令、没有挂着的授权请求。
5706
+ * 用户自己窗口里的会话不算忙 —— 它们不是 daemon 的子进程,重启不影响
5707
+ */
5708
+ idleState() {
5709
+ const busy = [];
5710
+ if (this.#queue.activeSessions > 0) busy.push(`\u540E\u53F0\u6307\u4EE4\u961F\u5217 ${this.#queue.activeSessions} \u4E2A\u4F1A\u8BDD`);
5711
+ if (this.#runsDone.size > 0) busy.push(`${this.#runsDone.size} \u4E2A\u540E\u53F0\u8FDB\u7A0B\u5728\u8DD1`);
5712
+ if (this.#gate.inflight > 0) busy.push(`${this.#gate.inflight} \u4E2A\u6388\u6743\u8BF7\u6C42\u6302\u7740`);
5713
+ if (this.#opening.size > 0 || this.#inflight > 0) busy.push(`${this.#opening.size + this.#inflight} \u6761\u6307\u4EE4\u6B63\u5728\u5F00\u7A97 / \u4EA4\u7ED9\u7A97\u53E3`);
5714
+ if (this.#wake.busy) busy.push("\u5524\u9192\u7A97\u53E3\u91CC\u6709\u6392\u961F\u6216\u5728\u8DD1\u7684\u6307\u4EE4");
5715
+ return busy.length === 0 ? { idle: true, why: "" } : { idle: false, why: busy.join("\uFF0C") };
5716
+ }
4949
5717
  stop() {
4950
5718
  this.#stopped = true;
4951
5719
  this.#clearIdle();
@@ -5004,6 +5772,7 @@ var Daemon = class {
5004
5772
  this.#attempt = 0;
5005
5773
  log("\u5DF2\u8FDE\u4E0A");
5006
5774
  this.#armIdle();
5775
+ this.#deps.onLink(true);
5007
5776
  void this.#coord.syncPending();
5008
5777
  void this.#reportDirs(true);
5009
5778
  });
@@ -5019,6 +5788,7 @@ var Daemon = class {
5019
5788
  ws.on("close", (code, reason) => {
5020
5789
  this.#clearIdle();
5021
5790
  this.#ws = null;
5791
+ this.#deps.onLink(false);
5022
5792
  if (code === POLICY_VIOLATION) {
5023
5793
  log(`\u670D\u52A1\u7AEF\u62D2\u7EDD\u8FDE\u63A5(${code} ${reason?.toString() || ""})\uFF1A\u6388\u6743\u5DF2\u5931\u6548\uFF0C\u505C\u6B62\u91CD\u8FDE\u3002\u8BF7\u91CD\u65B0\u8FD0\u884C deskbuddy setup`);
5024
5794
  this.#stopped = true;
@@ -5054,6 +5824,10 @@ var Daemon = class {
5054
5824
  }
5055
5825
  const ap = parseApprovalFrame(frame);
5056
5826
  if (ap) {
5827
+ if (this.#draining) {
5828
+ log(`\u6B63\u5728\u5347\u7EA7\uFF0C\u6388\u6743\u51B3\u5B9A ${ap.approvalId} \u7559\u7ED9\u91CD\u542F\u540E\u7684\u5B88\u62A4\u8FDB\u7A0B\u8865\u6536`);
5829
+ return;
5830
+ }
5057
5831
  if (!this.#seen.remember(ap.instructionId)) return;
5058
5832
  this.#seen.save();
5059
5833
  log(`\u6536\u5230\u6388\u6743\u51B3\u5B9A ${ap.approvalId} ${ap.decision}`);
@@ -5062,6 +5836,11 @@ var Daemon = class {
5062
5836
  }
5063
5837
  const cmd = parseCommand(frame);
5064
5838
  if (!cmd) return;
5839
+ if (this.#draining) {
5840
+ log(`\u6307\u4EE4 ${cmd.instructionId} \u5230\u8FBE\u65F6\u6B63\u5728\u5347\u7EA7\u91CD\u542F\uFF0C\u62D2\u7EDD\uFF08\u53EF\u91CD\u53D1\uFF09`);
5841
+ this.#report(cmd, cmd.sessionId, "rejected", "", UPDATING_REASON);
5842
+ return;
5843
+ }
5065
5844
  if (!this.#seen.remember(cmd.instructionId)) {
5066
5845
  log(`\u6307\u4EE4 ${cmd.instructionId} \u5DF2\u5904\u7406\u8FC7\uFF0C\u4E22\u5F03`);
5067
5846
  return;
@@ -5093,7 +5872,7 @@ var Daemon = class {
5093
5872
  win = null;
5094
5873
  }
5095
5874
  if (win) {
5096
- void this.#toWindow(cmd, sid, win);
5875
+ void this.#track(this.#toWindow(cmd, sid, win));
5097
5876
  return;
5098
5877
  }
5099
5878
  if (cmd.tool === "codex" && !this.#queue.busy(sid)) {
@@ -5109,7 +5888,7 @@ var Daemon = class {
5109
5888
  return;
5110
5889
  }
5111
5890
  if (route) {
5112
- void this.#toCodexWindow(cmd, sid, route);
5891
+ void this.#track(this.#toCodexWindow(cmd, sid, route));
5113
5892
  return;
5114
5893
  }
5115
5894
  }
@@ -5126,7 +5905,7 @@ var Daemon = class {
5126
5905
  if (this.#wake.submit(cmd, sid)) return;
5127
5906
  }
5128
5907
  if (cmd.tool === "claude_code" && !isExpired(cmd, this.#deps.now())) {
5129
- void this.#openOrDispatch(cmd).catch((e) => {
5908
+ void this.#track(this.#openOrDispatch(cmd)).catch((e) => {
5130
5909
  log(`\u6307\u4EE4 ${cmd.instructionId} \u5F00\u7A97\u6D41\u7A0B\u51FA\u9519\uFF1A${e?.stack ?? String(e)}`);
5131
5910
  this.#report(cmd, cmd.sessionId, "failed", "", "window_failed");
5132
5911
  });
@@ -5462,7 +6241,7 @@ var Daemon = class {
5462
6241
  this.#report(cmd, sessionId, "rejected", "", "tool_unsupported");
5463
6242
  return;
5464
6243
  }
5465
- log(`\u6267\u884C ${cmd.instructionId} tool=${cmd.tool} ${sessionId ? "resume" : "new"} cwd=\u2026/${basename6(cwd)} len=${cmd.text.length}`);
6244
+ log(`\u6267\u884C ${cmd.instructionId} tool=${cmd.tool} ${sessionId ? "resume" : "new"} cwd=\u2026/${basename7(cwd)} len=${cmd.text.length}`);
5466
6245
  const result = await this.#run(plan, cmd.tool, runId, sessionId, cwd, () => this.#report(cmd, sessionId, "started", "headless"));
5467
6246
  if ("fail" in result) {
5468
6247
  log(
@@ -5536,7 +6315,7 @@ var Daemon = class {
5536
6315
  * 起不了(关卡不可用 / 找不到可执行文件)返回 {fail};进程起来那一刻调 onStarted
5537
6316
  */
5538
6317
  async #run(plan, tool, runId, sessionId, cwd, onStarted) {
5539
- if (!existsSync10(this.#deps.nodePath) || !existsSync10(this.#deps.gateScript)) {
6318
+ if (!existsSync13(this.#deps.nodePath) || !existsSync13(this.#deps.gateScript)) {
5540
6319
  log(`\u6388\u6743\u5173\u5361\u4E0D\u53EF\u7528\uFF08node \u6216 gate.cjs \u627E\u4E0D\u5230\uFF09\uFF0C\u62D2\u7EDD\u6267\u884C ${tool}`);
5541
6320
  return { fail: "gate_unavailable" };
5542
6321
  }
@@ -5581,6 +6360,22 @@ async function runDaemon(deps = {}) {
5581
6360
  }
5582
6361
 
5583
6362
  // src/commands/daemon.ts
6363
+ var VERIFY_CONNECTED_MS = 3 * 60 * 1e3;
6364
+ function verifyOnLink(version, opts = {}) {
6365
+ let timer = null;
6366
+ return (up) => {
6367
+ if (timer) {
6368
+ clearTimeout(timer);
6369
+ timer = null;
6370
+ }
6371
+ if (!up) return;
6372
+ timer = setTimeout(() => {
6373
+ timer = null;
6374
+ if ((opts.markGood ?? markGood)(version)) log(`[\u81EA\u52A8\u5347\u7EA7] ${version} \u5DF2\u8FDE\u6EE1 3 \u5206\u949F\uFF0C\u9A8C\u8BC1\u901A\u8FC7`);
6375
+ }, opts.delayMs ?? VERIFY_CONNECTED_MS);
6376
+ timer.unref?.();
6377
+ };
6378
+ }
5584
6379
  async function runDaemonCommand(opts = {}) {
5585
6380
  const cfg = loadConfig();
5586
6381
  if (!cfg.boundAt) {
@@ -5589,23 +6384,37 @@ async function runDaemonCommand(opts = {}) {
5589
6384
  }
5590
6385
  if (opts.foreground) process.env.DESKBUDDY_DAEMON_FOREGROUND = "1";
5591
6386
  try {
5592
- writeFileSync17(pidFilePath(), String(process.pid), "utf8");
6387
+ writeFileSync18(pidFilePath(), String(process.pid), "utf8");
5593
6388
  } catch {
5594
6389
  }
5595
6390
  let daemon;
5596
6391
  try {
5597
- daemon = await runDaemon();
6392
+ daemon = await runDaemon({ onLink: verifyOnLink(packageVersion()) });
5598
6393
  } catch (e) {
5599
6394
  log(`\u5B88\u62A4\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25\uFF1A${e.message}`);
5600
6395
  process.stderr.write(`\u2717 \u5B88\u62A4\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25\uFF1A${e.message}
5601
6396
  `);
5602
6397
  return 1;
5603
6398
  }
6399
+ const d = daemon;
6400
+ const updater = opts.foreground ? null : new AutoUpdater({
6401
+ currentVersion: packageVersion(),
6402
+ fetchPolicy: async () => (await api.toolRegistry())?.cli,
6403
+ config: () => loadConfig(),
6404
+ install: (target) => installVersion(target, { log: (l) => log(`[\u81EA\u52A8\u5347\u7EA7] ${l}`) }),
6405
+ idle: () => d.idleState(),
6406
+ beginDrain: () => d.beginDrain(),
6407
+ endDrain: () => d.endDrain(),
6408
+ switchTo: (cliPath, _target, onExit) => spawnSwitch(cliPath, daemonLogPath(), { version: packageVersion(), cliPath: defaultCliPath() }, onExit),
6409
+ log: (l) => log(`[\u81EA\u52A8\u5347\u7EA7] ${l}`)
6410
+ });
6411
+ updater?.start();
5604
6412
  const shutdown = (sig) => {
5605
6413
  log(`\u6536\u5230 ${sig}\uFF0C\u9000\u51FA`);
6414
+ updater?.stop();
5606
6415
  daemon.stop();
5607
6416
  try {
5608
- rmSync12(pidFilePath(), { force: true });
6417
+ rmSync14(pidFilePath(), { force: true });
5609
6418
  } catch {
5610
6419
  }
5611
6420
  process.exit(0);
@@ -5622,8 +6431,8 @@ async function runDaemonCommand(opts = {}) {
5622
6431
  }
5623
6432
 
5624
6433
  // src/core/registry.ts
5625
- import { mkdirSync as mkdirSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync18 } from "fs";
5626
- import { join as join21 } from "path";
6434
+ import { mkdirSync as mkdirSync20, readFileSync as readFileSync17, writeFileSync as writeFileSync19 } from "fs";
6435
+ import { join as join24 } from "path";
5627
6436
  var FALLBACK = {
5628
6437
  version: "builtin",
5629
6438
  tools: {
@@ -5705,11 +6514,11 @@ var FALLBACK = {
5705
6514
  }
5706
6515
  };
5707
6516
  function registryCachePath() {
5708
- return join21(deskbuddyHome(), "tool-registry.json");
6517
+ return join24(deskbuddyHome(), "tool-registry.json");
5709
6518
  }
5710
6519
  function loadRegistry() {
5711
6520
  try {
5712
- return JSON.parse(readFileSync16(registryCachePath(), "utf8"));
6521
+ return JSON.parse(readFileSync17(registryCachePath(), "utf8"));
5713
6522
  } catch {
5714
6523
  return FALLBACK;
5715
6524
  }
@@ -5717,15 +6526,15 @@ function loadRegistry() {
5717
6526
  async function refreshRegistry() {
5718
6527
  try {
5719
6528
  const registry = await api.toolRegistry();
5720
- mkdirSync18(deskbuddyHome(), { recursive: true });
5721
- writeFileSync18(registryCachePath(), JSON.stringify(registry, null, 2), "utf8");
6529
+ mkdirSync20(deskbuddyHome(), { recursive: true });
6530
+ writeFileSync19(registryCachePath(), JSON.stringify(registry, null, 2), "utf8");
5722
6531
  } catch {
5723
6532
  }
5724
6533
  }
5725
6534
 
5726
6535
  // src/adapters/ccHook.ts
5727
- import { chmodSync as chmodSync6, existsSync as existsSync11, mkdirSync as mkdirSync19, readFileSync as readFileSync17, renameSync as renameSync14, rmSync as rmSync13, statSync as statSync6, writeFileSync as writeFileSync19 } from "fs";
5728
- import { dirname as dirname6, join as join22 } from "path";
6536
+ import { chmodSync as chmodSync7, existsSync as existsSync14, mkdirSync as mkdirSync21, readFileSync as readFileSync18, renameSync as renameSync15, rmSync as rmSync15, statSync as statSync7, writeFileSync as writeFileSync20 } from "fs";
6537
+ import { dirname as dirname9, join as join25 } from "path";
5729
6538
  var CC_EVENTS = [
5730
6539
  "UserPromptSubmit",
5731
6540
  "PreToolUse",
@@ -5735,8 +6544,8 @@ var CC_EVENTS = [
5735
6544
  "SessionEnd"
5736
6545
  ];
5737
6546
  function readSettings(path) {
5738
- if (!existsSync11(path)) return {};
5739
- const raw = readFileSync17(path, "utf8");
6547
+ if (!existsSync14(path)) return {};
6548
+ const raw = readFileSync18(path, "utf8");
5740
6549
  if (raw.trim() === "") return {};
5741
6550
  try {
5742
6551
  return JSON.parse(raw);
@@ -5745,17 +6554,17 @@ function readSettings(path) {
5745
6554
  }
5746
6555
  }
5747
6556
  function writeSettings(path, settings) {
5748
- mkdirSync19(dirname6(path), { recursive: true });
6557
+ mkdirSync21(dirname9(path), { recursive: true });
5749
6558
  let mode;
5750
6559
  try {
5751
- mode = statSync6(path).mode & 511;
6560
+ mode = statSync7(path).mode & 511;
5752
6561
  } catch {
5753
6562
  mode = void 0;
5754
6563
  }
5755
6564
  const tmp = path + ".deskbuddy.tmp";
5756
- writeFileSync19(tmp, JSON.stringify(settings, null, 2), "utf8");
5757
- if (mode !== void 0) chmodSync6(tmp, mode);
5758
- renameSync14(tmp, path);
6565
+ writeFileSync20(tmp, JSON.stringify(settings, null, 2), "utf8");
6566
+ if (mode !== void 0) chmodSync7(tmp, mode);
6567
+ renameSync15(tmp, path);
5759
6568
  }
5760
6569
  function mentions(command, script) {
5761
6570
  return !!command && (command.includes(script) || command.includes(fwdSlash(script)));
@@ -5858,8 +6667,8 @@ function installCcRewake(settingsPath, wakePath, platform = process.platform) {
5858
6667
  function uninstallCcHook(settingsPath, hookPath) {
5859
6668
  const settings = readSettings(settingsPath);
5860
6669
  if (!settings.hooks) return;
5861
- const gatePath = join22(dirname6(hookPath), "gate.cjs");
5862
- const wakePath = join22(dirname6(hookPath), "wake.cjs");
6670
+ const gatePath = join25(dirname9(hookPath), "gate.cjs");
6671
+ const wakePath = join25(dirname9(hookPath), "wake.cjs");
5863
6672
  for (const event of Object.keys(settings.hooks)) {
5864
6673
  const groups = settings.hooks[event] ?? [];
5865
6674
  const kept = [];
@@ -5873,14 +6682,14 @@ function uninstallCcHook(settingsPath, hookPath) {
5873
6682
  }
5874
6683
  writeSettings(settingsPath, settings);
5875
6684
  try {
5876
- rmSync13(join22(dirname6(hookPath), "wake", "tokens"), { recursive: true, force: true });
6685
+ rmSync15(join25(dirname9(hookPath), "wake", "tokens"), { recursive: true, force: true });
5877
6686
  } catch {
5878
6687
  }
5879
6688
  }
5880
6689
 
5881
6690
  // src/adapters/codexHook.ts
5882
- import { chmodSync as chmodSync7, existsSync as existsSync12, mkdirSync as mkdirSync20, readFileSync as readFileSync18, renameSync as renameSync15, statSync as statSync7, writeFileSync as writeFileSync20 } from "fs";
5883
- import { dirname as dirname7, join as join23 } from "path";
6691
+ import { chmodSync as chmodSync8, existsSync as existsSync15, mkdirSync as mkdirSync22, readFileSync as readFileSync19, renameSync as renameSync16, statSync as statSync8, writeFileSync as writeFileSync21 } from "fs";
6692
+ import { dirname as dirname10, join as join26 } from "path";
5884
6693
  var CODEX_MARKER_BEGIN = "# >>> deskbuddy >>>";
5885
6694
  var CODEX_MARKER_END = "# <<< deskbuddy <<<";
5886
6695
  var CODEX_EVENTS = [
@@ -5893,7 +6702,7 @@ var CODEX_EVENTS = [
5893
6702
  ];
5894
6703
  var CODEX_TRUST_NOTICE = "\u26A0\uFE0F \u4E0B\u6B21\u542F\u52A8 Codex \u65F6\u4F1A\u8981\u6C42\u4F60\u786E\u8BA4\u4FE1\u4EFB\u8FD9\u4E2A hook\uFF0C\u8BF7\u9009\u62E9\u540C\u610F\u2014\u2014\u4E0D\u786E\u8BA4\u7684\u8BDD Codex \u4E0D\u4F1A\u6267\u884C\u5B83\u3002\n Codex \u7ECF `--remote` \u8FDE\u5E38\u9A7B app-server \u4F7F\u7528\u65F6\uFF0C\u7A97\u53E3\u91CC\u7684\u6388\u6743\u6846\u4E5F\u80FD\u5728\u8BBE\u5907\u4E0A\u786E\u8BA4\uFF08\u5B88\u62A4\u8FDB\u7A0B\u5728\u8DD1\u65F6\u751F\u6548\uFF09";
5895
6704
  function codexConfigPath() {
5896
- return join23(codexHome(), "config.toml");
6705
+ return join26(codexHome(), "config.toml");
5897
6706
  }
5898
6707
  function renderBlock(hookPath, events, tool, platform) {
5899
6708
  const command = JSON.stringify(`node ${platform === "win32" ? winPsPath(hookPath) : hookPath} ${tool}`);
@@ -5912,17 +6721,17 @@ function renderBlock(hookPath, events, tool, platform) {
5912
6721
  return lines2.join("\n");
5913
6722
  }
5914
6723
  function writeAtomic(path, content) {
5915
- mkdirSync20(dirname7(path), { recursive: true });
6724
+ mkdirSync22(dirname10(path), { recursive: true });
5916
6725
  let mode;
5917
6726
  try {
5918
- mode = statSync7(path).mode & 511;
6727
+ mode = statSync8(path).mode & 511;
5919
6728
  } catch {
5920
6729
  mode = void 0;
5921
6730
  }
5922
6731
  const tmp = path + ".deskbuddy.tmp";
5923
- writeFileSync20(tmp, content, "utf8");
5924
- if (mode !== void 0) chmodSync7(tmp, mode);
5925
- renameSync15(tmp, path);
6732
+ writeFileSync21(tmp, content, "utf8");
6733
+ if (mode !== void 0) chmodSync8(tmp, mode);
6734
+ renameSync16(tmp, path);
5926
6735
  }
5927
6736
  function assertSafeToAppend(raw, path) {
5928
6737
  for (const quote of ['"""', "'''"]) {
@@ -5932,7 +6741,7 @@ function assertSafeToAppend(raw, path) {
5932
6741
  }
5933
6742
  }
5934
6743
  function installCodexHook(configPath2, hookPath, events = CODEX_EVENTS, tool = "codex", platform = process.platform) {
5935
- const raw = existsSync12(configPath2) ? readFileSync18(configPath2, "utf8") : "";
6744
+ const raw = existsSync15(configPath2) ? readFileSync19(configPath2, "utf8") : "";
5936
6745
  if (raw.includes(CODEX_MARKER_BEGIN)) return;
5937
6746
  assertSafeToAppend(raw, configPath2);
5938
6747
  const block = renderBlock(hookPath, events, tool, platform);
@@ -5940,8 +6749,8 @@ function installCodexHook(configPath2, hookPath, events = CODEX_EVENTS, tool = "
5940
6749
  writeAtomic(configPath2, head + block + "\n");
5941
6750
  }
5942
6751
  function uninstallCodexHook(configPath2) {
5943
- if (!existsSync12(configPath2)) return;
5944
- const raw = readFileSync18(configPath2, "utf8");
6752
+ if (!existsSync15(configPath2)) return;
6753
+ const raw = readFileSync19(configPath2, "utf8");
5945
6754
  const lines2 = raw.split("\n");
5946
6755
  const begin = lines2.findIndex((l) => l.trim() === CODEX_MARKER_BEGIN);
5947
6756
  if (begin === -1) return;
@@ -5955,11 +6764,11 @@ function uninstallCodexHook(configPath2) {
5955
6764
  }
5956
6765
 
5957
6766
  // src/commands/tools.ts
5958
- import { existsSync as existsSync13, readFileSync as readFileSync19 } from "fs";
6767
+ import { existsSync as existsSync16, readFileSync as readFileSync20 } from "fs";
5959
6768
  function fileContains(path, needle) {
5960
6769
  try {
5961
- if (!existsSync13(path)) return false;
5962
- return readFileSync19(path, "utf8").includes(needle);
6770
+ if (!existsSync16(path)) return false;
6771
+ return readFileSync20(path, "utf8").includes(needle);
5963
6772
  } catch {
5964
6773
  return false;
5965
6774
  }
@@ -6033,13 +6842,13 @@ function subscribedEvents(registry, tool, fallback) {
6033
6842
  }
6034
6843
 
6035
6844
  // src/commands/enable.ts
6036
- import { dirname as dirname9, join as join26 } from "path";
6845
+ import { dirname as dirname11, join as join28 } from "path";
6037
6846
 
6038
6847
  // src/wrap/shim.ts
6039
- import { execFileSync as execFileSync10 } from "child_process";
6040
- import { chmodSync as chmodSync8, existsSync as existsSync14, mkdirSync as mkdirSync21, readdirSync as readdirSync5, readFileSync as readFileSync20, rmdirSync, rmSync as rmSync14, writeFileSync as writeFileSync21 } from "fs";
6848
+ import { execFileSync as execFileSync12 } from "child_process";
6849
+ import { chmodSync as chmodSync9, existsSync as existsSync17, mkdirSync as mkdirSync23, readdirSync as readdirSync6, readFileSync as readFileSync21, rmdirSync, rmSync as rmSync16, writeFileSync as writeFileSync22 } from "fs";
6041
6850
  import { homedir as homedir6 } from "os";
6042
- import { delimiter as delimiter2, join as join24, resolve as resolve2 } from "path";
6851
+ import { delimiter as delimiter2, join as join27, resolve as resolve3 } from "path";
6043
6852
  var SHIM_FALLBACK_EXIT = 213;
6044
6853
  var WIN_SHIM_ENABLED = false;
6045
6854
  var SHIM_COMMANDS = { claude_code: "claude", codex: "codex" };
@@ -6049,10 +6858,10 @@ var RC_END = "# <<< deskbuddy <<<";
6049
6858
  var RC_FILES = [".zshrc", ".bashrc"];
6050
6859
  var WIN_PENDING = "Windows \u7A97\u53E3\u63A5\u7BA1\u5F85\u9A8C\u8BC1\uFF0C\u6682\u672A\u5F00\u542F";
6051
6860
  function shimDir() {
6052
- return join24(deskbuddyHome(), "bin");
6861
+ return join27(deskbuddyHome(), "bin");
6053
6862
  }
6054
6863
  function shimFile(binDir, command, platform = process.platform) {
6055
- return join24(binDir, platform === "win32" ? `${command}.cmd` : command);
6864
+ return join27(binDir, platform === "win32" ? `${command}.cmd` : command);
6056
6865
  }
6057
6866
  function shQuote3(s) {
6058
6867
  return `'${s.replace(/'/g, `'\\''`)}'`;
@@ -6140,7 +6949,7 @@ function cmdScript(tool, command, node, cli, bin) {
6140
6949
  ].join("\r\n");
6141
6950
  }
6142
6951
  function rcBlock(binDir, rcHome) {
6143
- const std = resolve2(rcHome, ".deskbuddy", "bin") === resolve2(binDir);
6952
+ const std = resolve3(rcHome, ".deskbuddy", "bin") === resolve3(binDir);
6144
6953
  const dir = std ? "$HOME/.deskbuddy/bin" : binDir.replace(/(["\\$`])/g, "\\$1");
6145
6954
  return [
6146
6955
  RC_BEGIN,
@@ -6181,10 +6990,10 @@ function removeRcBlock(content) {
6181
6990
  return content.slice(0, start) + content.slice(end);
6182
6991
  }
6183
6992
  function readRc(p) {
6184
- return readFileSync20(p).toString("latin1");
6993
+ return readFileSync21(p).toString("latin1");
6185
6994
  }
6186
6995
  function writeRc(p, latin1) {
6187
- writeFileSync21(p, Buffer.from(latin1, "latin1"));
6996
+ writeFileSync22(p, Buffer.from(latin1, "latin1"));
6188
6997
  }
6189
6998
  function toLatin1(utf8) {
6190
6999
  return Buffer.from(utf8, "utf8").toString("latin1");
@@ -6206,7 +7015,7 @@ function removePathEntry(value, dir) {
6206
7015
  var PS_READ = "$k=[Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment');$v='';if($k){$v=$k.GetValue('Path','',[Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)};[Console]::Out.Write([Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes([string]$v)))";
6207
7016
  var PS_WRITE = "$k=[Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Environment');$kind=[Microsoft.Win32.RegistryValueKind]::ExpandString;try{$kind=$k.GetValueKind('Path')}catch{};if([string]::IsNullOrEmpty($env:DESKBUDDY_NEW_PATH)){$k.DeleteValue('Path',$false)}else{$k.SetValue('Path',$env:DESKBUDDY_NEW_PATH,$kind)};[Environment]::SetEnvironmentVariable('DESKBUDDY_PATH_REFRESH','1','User');[Environment]::SetEnvironmentVariable('DESKBUDDY_PATH_REFRESH',$null,'User')";
6208
7017
  function psWinPath() {
6209
- const ps = (script, env) => execFileSync10("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script], {
7018
+ const ps = (script, env) => execFileSync12("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script], {
6210
7019
  encoding: "utf8",
6211
7020
  stdio: ["ignore", "pipe", "ignore"],
6212
7021
  windowsHide: true,
@@ -6253,12 +7062,12 @@ function installShim(tool, e) {
6253
7062
  if (!node || !cli || !bin) {
6254
7063
  return { ok: false, message: "\u7A97\u53E3\u63A5\u7BA1\u6CA1\u88C5\uFF1A\u5B89\u88C5\u8DEF\u5F84\u91CC\u6709\u975E ASCII \u5B57\u7B26\uFF0CWindows \u7684 .cmd \u8BFB\u4E0D\u4E86\uFF08\u4E0D\u5F71\u54CD\u5176\u5B83\u529F\u80FD\uFF09" };
6255
7064
  }
6256
- mkdirSync21(e.binDir, { recursive: true });
6257
- writeFileSync21(file, cmdScript(tool, command, node, cli, bin), "utf8");
7065
+ mkdirSync23(e.binDir, { recursive: true });
7066
+ writeFileSync22(file, cmdScript(tool, command, node, cli, bin), "utf8");
6258
7067
  } else {
6259
- mkdirSync21(e.binDir, { recursive: true, mode: 448 });
6260
- writeFileSync21(file, shScript(tool, command, e.nodePath, e.cliPath, e.binDir), { encoding: "utf8", mode: 493 });
6261
- chmodSync8(file, 493);
7068
+ mkdirSync23(e.binDir, { recursive: true, mode: 448 });
7069
+ writeFileSync22(file, shScript(tool, command, e.nodePath, e.cliPath, e.binDir), { encoding: "utf8", mode: 493 });
7070
+ chmodSync9(file, 493);
6262
7071
  }
6263
7072
  const touched = [];
6264
7073
  if (e.platform === "win32") {
@@ -6272,8 +7081,8 @@ function installShim(tool, e) {
6272
7081
  } else if (e.rcHome) {
6273
7082
  const block = toLatin1(rcBlock(e.binDir, e.rcHome));
6274
7083
  for (const name of RC_FILES) {
6275
- const rc = join24(e.rcHome, name);
6276
- if (!existsSync14(rc)) continue;
7084
+ const rc = join27(e.rcHome, name);
7085
+ if (!existsSync17(rc)) continue;
6277
7086
  const next = addRcBlock(readRc(rc), block);
6278
7087
  if (next !== null) writeRc(rc, next);
6279
7088
  touched.push(`~/${name}`);
@@ -6293,13 +7102,13 @@ function installShim(tool, e) {
6293
7102
  function ourShims(binDir) {
6294
7103
  let names;
6295
7104
  try {
6296
- names = readdirSync5(binDir);
7105
+ names = readdirSync6(binDir);
6297
7106
  } catch {
6298
7107
  return [];
6299
7108
  }
6300
7109
  return names.filter((n) => {
6301
7110
  try {
6302
- return readFileSync20(join24(binDir, n), "utf8").includes(SHIM_MARKER);
7111
+ return readFileSync21(join27(binDir, n), "utf8").includes(SHIM_MARKER);
6303
7112
  } catch {
6304
7113
  return false;
6305
7114
  }
@@ -6311,8 +7120,8 @@ function uninstallShim(tool, e) {
6311
7120
  let removed = false;
6312
7121
  const file = shimFile(e.binDir, command, e.platform);
6313
7122
  try {
6314
- if (existsSync14(file) && readFileSync20(file, "utf8").includes(SHIM_MARKER)) {
6315
- rmSync14(file, { force: true });
7123
+ if (existsSync17(file) && readFileSync21(file, "utf8").includes(SHIM_MARKER)) {
7124
+ rmSync16(file, { force: true });
6316
7125
  removed = true;
6317
7126
  }
6318
7127
  } catch {
@@ -6330,8 +7139,8 @@ function uninstallShim(tool, e) {
6330
7139
  }
6331
7140
  } else if (e.rcHome) {
6332
7141
  for (const name of RC_FILES) {
6333
- const rc = join24(e.rcHome, name);
6334
- if (!existsSync14(rc)) continue;
7142
+ const rc = join27(e.rcHome, name);
7143
+ if (!existsSync17(rc)) continue;
6335
7144
  const next = removeRcBlock(readRc(rc));
6336
7145
  if (next !== null) {
6337
7146
  writeRc(rc, next);
@@ -6340,7 +7149,7 @@ function uninstallShim(tool, e) {
6340
7149
  }
6341
7150
  }
6342
7151
  try {
6343
- if (existsSync14(e.binDir) && readdirSync5(e.binDir).length === 0) rmdirSync(e.binDir);
7152
+ if (existsSync17(e.binDir) && readdirSync6(e.binDir).length === 0) rmdirSync(e.binDir);
6344
7153
  } catch {
6345
7154
  }
6346
7155
  if (!removed && touched.length === 0) return { ok: true, message: "" };
@@ -6352,7 +7161,7 @@ function shimState(tool, binDir, pathEnv, platform = process.platform) {
6352
7161
  const file = shimFile(binDir, command, platform);
6353
7162
  let installed = false;
6354
7163
  try {
6355
- installed = readFileSync20(file, "utf8").includes(SHIM_MARKER);
7164
+ installed = readFileSync21(file, "utf8").includes(SHIM_MARKER);
6356
7165
  } catch {
6357
7166
  installed = false;
6358
7167
  }
@@ -6362,72 +7171,6 @@ function shimState(tool, binDir, pathEnv, platform = process.platform) {
6362
7171
  return { installed, inPath };
6363
7172
  }
6364
7173
 
6365
- // src/wrap/pty.ts
6366
- import { chmodSync as chmodSync9, existsSync as existsSync15, statSync as statSync8 } from "fs";
6367
- import { createRequire as createRequire2 } from "module";
6368
- import { dirname as dirname8, join as join25 } from "path";
6369
- var req = createRequire2(import.meta.url);
6370
- function fixSpawnHelper() {
6371
- if (process.platform !== "darwin") return;
6372
- try {
6373
- const dir = join25(dirname8(req.resolve("node-pty/package.json")), "prebuilds", `darwin-${process.arch}`);
6374
- const helper = join25(dir, "spawn-helper");
6375
- if (existsSync15(helper) && (statSync8(helper).mode & 73) === 0) chmodSync9(helper, 493);
6376
- } catch {
6377
- }
6378
- }
6379
- function loadPty() {
6380
- try {
6381
- fixSpawnHelper();
6382
- const m = req("node-pty");
6383
- return typeof m?.spawn === "function" ? m : null;
6384
- } catch {
6385
- return null;
6386
- }
6387
- }
6388
- function loadHeadless() {
6389
- try {
6390
- const m = req("@xterm/headless");
6391
- return typeof m?.Terminal === "function" ? m.Terminal : null;
6392
- } catch {
6393
- return null;
6394
- }
6395
- }
6396
- function screenLines(term) {
6397
- const b = term.buffer.active;
6398
- const out = [];
6399
- for (let i = b.viewportY; i < b.viewportY + term.rows; i++) {
6400
- const l = b.getLine(i);
6401
- out.push(l ? l.translateToString(true) : "");
6402
- }
6403
- return out;
6404
- }
6405
- function screenLinesUndimmed(term) {
6406
- const b = term.buffer.active;
6407
- const out = [];
6408
- for (let i = b.viewportY; i < b.viewportY + term.rows; i++) {
6409
- const l = b.getLine(i);
6410
- if (!l) {
6411
- out.push("");
6412
- continue;
6413
- }
6414
- if (typeof l.getCell !== "function") {
6415
- out.push(l.translateToString(true));
6416
- continue;
6417
- }
6418
- let s = "";
6419
- for (let x = 0; x < term.cols; x++) {
6420
- const c = l.getCell(x);
6421
- if (!c) break;
6422
- const w = c.getWidth();
6423
- if (w === 0) continue;
6424
- s += c.isDim() ? " ".repeat(w) : c.getChars() || " ";
6425
- }
6426
- out.push(s.trimEnd());
6427
- }
6428
- return out;
6429
- }
6430
-
6431
7174
  // src/commands/enable.ts
6432
7175
  function stdout(line) {
6433
7176
  process.stdout.write(line + "\n");
@@ -6492,14 +7235,14 @@ function installTools(tools, out, shim = defaultShimEnvForInstall()) {
6492
7235
  installed.push(tool);
6493
7236
  out(`\u2705 ${tool}\uFF1Ahook \u5DF2\u88C5\u8FDB ${configPath2}\uFF08${events.length} \u4E2A\u4E8B\u4EF6\uFF09`);
6494
7237
  try {
6495
- if (adapter.installWindowGate?.(configPath2, join26(dirname9(hookPath), "gate.cjs"), tool)) {
7238
+ if (adapter.installWindowGate?.(configPath2, join28(dirname11(hookPath), "gate.cjs"), tool)) {
6496
7239
  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`);
6497
7240
  }
6498
7241
  } catch (e) {
6499
7242
  out(`\u26A0\uFE0F ${tool}\uFF1A\u8BBE\u5907\u786E\u8BA4\u6388\u6743\u6CA1\u88C5\u4E0A\uFF1A${e.message}`);
6500
7243
  }
6501
7244
  try {
6502
- if (adapter.installRewake?.(configPath2, join26(dirname9(hookPath), "wake.cjs"), tool)) {
7245
+ if (adapter.installRewake?.(configPath2, join28(dirname11(hookPath), "wake.cjs"), tool)) {
6503
7246
  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`);
6504
7247
  }
6505
7248
  } catch (e) {
@@ -6526,8 +7269,9 @@ async function runEnable(tools, opts = {}) {
6526
7269
  if (!opts.skipRefresh) await refreshRegistry();
6527
7270
  const { failed } = installTools(tools, out, opts.shim === void 0 ? defaultShimEnvForInstall() : opts.shim);
6528
7271
  if (!opts.skipAutostart) {
6529
- const r = await installAutostart({ execPath: process.execPath, cliPath: defaultCliPath() });
6530
7272
  out("");
7273
+ const target = autostartTarget(out);
7274
+ const r = process.env.DESKBUDDY_SKIP_AUTOSTART === "1" ? { ok: false, message: "\u5DF2\u6309 DESKBUDDY_SKIP_AUTOSTART \u8DF3\u8FC7\u5F00\u673A\u81EA\u542F" } : await installAutostart(target);
6531
7275
  out(r.ok ? `\u2705 ${r.message}` : `\u26A0\uFE0F ${r.message}`);
6532
7276
  }
6533
7277
  out("");
@@ -6599,12 +7343,12 @@ async function runDisable(tools, opts = {}) {
6599
7343
  }
6600
7344
 
6601
7345
  // src/commands/install.ts
6602
- import { mkdirSync as mkdirSync22, rmSync as rmSync15, writeFileSync as writeFileSync22 } from "fs";
6603
- import { join as join27 } from "path";
7346
+ import { mkdirSync as mkdirSync24, rmSync as rmSync17, writeFileSync as writeFileSync23 } from "fs";
7347
+ import { join as join29 } from "path";
6604
7348
 
6605
7349
  // src/core/detect.ts
6606
- import { existsSync as existsSync16 } from "fs";
6607
- import { dirname as dirname10 } from "path";
7350
+ import { existsSync as existsSync18 } from "fs";
7351
+ import { dirname as dirname12 } from "path";
6608
7352
  var ALL_TOOLS = ["claude_code", "codex", "workbuddy", "qoder", "opencode"];
6609
7353
  function toolConfigDir(tool) {
6610
7354
  switch (tool) {
@@ -6613,18 +7357,18 @@ function toolConfigDir(tool) {
6613
7357
  case "codex":
6614
7358
  return codexHome();
6615
7359
  case "workbuddy":
6616
- return dirname10(workbuddySettingsPath());
7360
+ return dirname12(workbuddySettingsPath());
6617
7361
  case "qoder":
6618
- return dirname10(qoderSettingsPath());
7362
+ return dirname12(qoderSettingsPath());
6619
7363
  case "opencode":
6620
- return dirname10(dirname10(opencodePluginPath()));
7364
+ return dirname12(dirname12(opencodePluginPath()));
6621
7365
  default:
6622
7366
  return null;
6623
7367
  }
6624
7368
  }
6625
7369
  var defaultDetectDeps = () => ({
6626
7370
  located: (tool) => locate(tool) != null,
6627
- exists: existsSync16,
7371
+ exists: existsSync18,
6628
7372
  env: process.env
6629
7373
  });
6630
7374
  function callerTool(env) {
@@ -6645,19 +7389,19 @@ function detectTools(d = defaultDetectDeps()) {
6645
7389
  import qrcode from "qrcode-terminal";
6646
7390
 
6647
7391
  // src/commands/remoteCwd.ts
6648
- import { existsSync as existsSync17, statSync as statSync9 } from "fs";
7392
+ import { existsSync as existsSync19, statSync as statSync9 } from "fs";
6649
7393
  import { createInterface } from "readline";
6650
7394
  import { homedir as homedir7 } from "os";
6651
- import { isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
7395
+ import { isAbsolute as isAbsolute2, resolve as resolve4 } from "path";
6652
7396
  function normalizePath(raw, home = homedir7()) {
6653
7397
  const s = (raw || "").trim();
6654
7398
  if (!s) return null;
6655
- const expanded = s === "~" ? home : s.startsWith("~/") || process.platform === "win32" && s.startsWith("~\\") ? resolve3(home, s.slice(2)) : s;
6656
- const abs = isAbsolute2(expanded) ? expanded : resolve3(process.cwd(), expanded);
7399
+ const expanded = s === "~" ? home : s.startsWith("~/") || process.platform === "win32" && s.startsWith("~\\") ? resolve4(home, s.slice(2)) : s;
7400
+ const abs = isAbsolute2(expanded) ? expanded : resolve4(process.cwd(), expanded);
6657
7401
  return abs;
6658
7402
  }
6659
7403
  function checkPath(abs) {
6660
- if (!existsSync17(abs)) return "missing";
7404
+ if (!existsSync19(abs)) return "missing";
6661
7405
  try {
6662
7406
  return statSync9(abs).isDirectory() ? "ok" : "not_a_dir";
6663
7407
  } catch {
@@ -6863,11 +7607,11 @@ function inSandbox(env) {
6863
7607
  }
6864
7608
  function precheckWritable(dirs) {
6865
7609
  for (const dir of dirs) {
6866
- const probe = join27(dir, `.deskbuddy-probe-${process.pid}`);
7610
+ const probe = join29(dir, `.deskbuddy-probe-${process.pid}`);
6867
7611
  try {
6868
- mkdirSync22(dir, { recursive: true });
6869
- writeFileSync22(probe, "");
6870
- rmSync15(probe, { force: true });
7612
+ mkdirSync24(dir, { recursive: true });
7613
+ writeFileSync23(probe, "");
7614
+ rmSync17(probe, { force: true });
6871
7615
  } catch (e) {
6872
7616
  return { ok: false, dir, error: e.message };
6873
7617
  }
@@ -6884,7 +7628,7 @@ var defaultInstallDeps = () => ({
6884
7628
  refreshRegistry: () => refreshRegistry().catch(() => void 0),
6885
7629
  installTools,
6886
7630
  // DESKBUDDY_SKIP_AUTOSTART=1 只给端到端测试用:自启会真去动 launchd / systemd,并停掉本机正在跑的 daemon
6887
- autostart: () => process.env.DESKBUDDY_SKIP_AUTOSTART === "1" ? Promise.resolve({ ok: false, message: "\u5DF2\u6309 DESKBUDDY_SKIP_AUTOSTART \u8DF3\u8FC7" }) : installAutostart({ execPath: process.execPath, cliPath: defaultCliPath() })
7631
+ autostart: () => process.env.DESKBUDDY_SKIP_AUTOSTART === "1" ? Promise.resolve({ ok: false, message: "\u5DF2\u6309 DESKBUDDY_SKIP_AUTOSTART \u8DF3\u8FC7" }) : installAutostart(autostartTarget())
6888
7632
  });
6889
7633
  function summary(out, lines2) {
6890
7634
  out("");
@@ -6996,16 +7740,16 @@ async function runInstall(opts, deps = defaultInstallDeps()) {
6996
7740
 
6997
7741
  // src/commands/logout.ts
6998
7742
  import { createInterface as createInterface2 } from "readline";
6999
- import { existsSync as existsSync18, rmSync as rmSync16 } from "fs";
7743
+ import { existsSync as existsSync20, rmSync as rmSync18 } from "fs";
7000
7744
  function stdout4(line) {
7001
7745
  process.stdout.write(line + "\n");
7002
7746
  }
7003
7747
  function askYesNo(question) {
7004
- return new Promise((resolve5) => {
7748
+ return new Promise((resolve6) => {
7005
7749
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
7006
7750
  rl.question(question, (answer) => {
7007
7751
  rl.close();
7008
- resolve5(/^(y|yes)$/i.test(answer.trim()));
7752
+ resolve6(/^(y|yes)$/i.test(answer.trim()));
7009
7753
  });
7010
7754
  });
7011
7755
  }
@@ -7027,8 +7771,8 @@ async function runLogout(opts = {}) {
7027
7771
  }
7028
7772
  }
7029
7773
  await runDisable([], { out, brief: true, keepAutostart: opts.keepAutostart });
7030
- if (existsSync18(home)) {
7031
- rmSync16(home, { recursive: true, force: true });
7774
+ if (existsSync20(home)) {
7775
+ rmSync18(home, { recursive: true, force: true });
7032
7776
  }
7033
7777
  out("");
7034
7778
  out("\u2705 \u5DF2\u6E05\u9664\u672C\u673A\u7684 DeskBuddy \u6570\u636E\u3002");
@@ -7172,6 +7916,7 @@ function stdout6(line) {
7172
7916
  process.stdout.write(line + "\n");
7173
7917
  }
7174
7918
  async function runUpdate(opts = {}) {
7919
+ if (opts.auto) return runAutoSwitch(opts);
7175
7920
  const out = opts.out ?? stdout6;
7176
7921
  const cfg = loadConfig();
7177
7922
  if (!cfg.boundAt) {
@@ -7186,11 +7931,35 @@ async function runUpdate(opts = {}) {
7186
7931
  out("");
7187
7932
  return runEnable([...cfg.enabledTools], opts);
7188
7933
  }
7934
+ async function runAutoSwitch(opts) {
7935
+ const out = opts.out ?? ((line) => log(`[\u81EA\u52A8\u5347\u7EA7] ${line}`));
7936
+ const cfg = loadConfig();
7937
+ if (!cfg.boundAt) {
7938
+ out("\u8FD8\u6CA1\u6709\u6388\u6743\uFF0C\u4E0D\u5207\u6362");
7939
+ return 1;
7940
+ }
7941
+ out(`\u5207\u6362\u5230 ${packageVersion()}`);
7942
+ if (cfg.enabledTools.length > 0) {
7943
+ try {
7944
+ installTools([...cfg.enabledTools], out, opts.shim);
7945
+ } catch (e) {
7946
+ out(`\u91CD\u88C5 hook \u51FA\u9519\uFF1A${e.message}\uFF08\u7EE7\u7EED\u5207\u6362\uFF09`);
7947
+ }
7948
+ }
7949
+ const from = switchFromEnv();
7950
+ delete process.env[FROM_VERSION_ENV];
7951
+ delete process.env[FROM_CLI_ENV];
7952
+ const target = autostartTarget(out, from);
7953
+ if (opts.skipAutostart || process.env.DESKBUDDY_SKIP_AUTOSTART === "1") return 0;
7954
+ const r = await installAutostart(target, process.platform, { auto: true });
7955
+ out(r.message);
7956
+ return r.ok ? 0 : 1;
7957
+ }
7189
7958
 
7190
7959
  // src/wrap/run.ts
7191
7960
  import { randomBytes as randomBytes8 } from "crypto";
7192
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync24, statSync as statSync11, truncateSync as truncateSync3 } from "fs";
7193
- import { join as join29 } from "path";
7961
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync26, statSync as statSync11, truncateSync as truncateSync3 } from "fs";
7962
+ import { join as join31 } from "path";
7194
7963
  import { StringDecoder } from "string_decoder";
7195
7964
 
7196
7965
  // src/wrap/injector.ts
@@ -7438,10 +8207,10 @@ var Injector = class {
7438
8207
  };
7439
8208
 
7440
8209
  // src/wrap/passthrough.ts
7441
- import { spawn as spawn3 } from "child_process";
8210
+ import { spawn as spawn4 } from "child_process";
7442
8211
  import { realpathSync as realpathSync3 } from "fs";
7443
8212
  import { constants as constants2 } from "os";
7444
- import { dirname as dirname11, resolve as resolve4 } from "path";
8213
+ import { dirname as dirname13, resolve as resolve5 } from "path";
7445
8214
  var CLAUDE_SUBCOMMANDS = /* @__PURE__ */ new Set([
7446
8215
  "agents",
7447
8216
  "attach",
@@ -7492,7 +8261,7 @@ function passthroughReason(i) {
7492
8261
  return null;
7493
8262
  }
7494
8263
  function norm2(p, win) {
7495
- const r = resolve4(p).replace(/[\\/]+$/, "");
8264
+ const r = resolve5(p).replace(/[\\/]+$/, "");
7496
8265
  return win ? r.replace(/\//g, "\\").toLowerCase() : r;
7497
8266
  }
7498
8267
  function real(p) {
@@ -7505,7 +8274,7 @@ function real(p) {
7505
8274
  function findReal(tool, d = defaultLocateDeps(), skip = shimDir()) {
7506
8275
  const win = (d.platform ?? process.platform) === "win32";
7507
8276
  const skipKeys = /* @__PURE__ */ new Set([norm2(skip, win), norm2(real(skip), win)]);
7508
- const inSkip = (file) => skipKeys.has(norm2(dirname11(file), win)) || skipKeys.has(norm2(dirname11(real(file)), win));
8277
+ const inSkip = (file) => skipKeys.has(norm2(dirname13(file), win)) || skipKeys.has(norm2(dirname13(real(file)), win));
7509
8278
  const dd = { ...d, skipDirs: [...d.skipDirs ?? [], skip] };
7510
8279
  const name = SHIM_COMMANDS[tool];
7511
8280
  if (!name) {
@@ -7534,7 +8303,7 @@ function runDirect(real2, args, env = process.env) {
7534
8303
  return new Promise((done) => {
7535
8304
  let child;
7536
8305
  try {
7537
- child = spawn3(real2.file, [...real2.prefixArgs, ...args], {
8306
+ child = spawn4(real2.file, [...real2.prefixArgs, ...args], {
7538
8307
  stdio: "inherit",
7539
8308
  // 🔴 再经过垫片也直接放行,不会绕回来
7540
8309
  env: withoutShimFlag({ ...env, DESKBUDDY_WRAPPED: "1" }),
@@ -7584,21 +8353,21 @@ function runDirect(real2, args, env = process.env) {
7584
8353
  }
7585
8354
 
7586
8355
  // src/wrap/codexLaunch.ts
7587
- import { spawn as spawn4 } from "child_process";
7588
- import { closeSync as closeSync3, mkdirSync as mkdirSync23, openSync as openSync3, readdirSync as readdirSync6, readFileSync as readFileSync21, rmSync as rmSync17, statSync as statSync10, truncateSync as truncateSync2, writeFileSync as writeFileSync23, renameSync as renameSync16 } from "fs";
8356
+ import { spawn as spawn5 } from "child_process";
8357
+ import { closeSync as closeSync4, mkdirSync as mkdirSync25, openSync as openSync4, readdirSync as readdirSync7, readFileSync as readFileSync22, rmSync as rmSync19, statSync as statSync10, truncateSync as truncateSync2, writeFileSync as writeFileSync24, renameSync as renameSync17 } from "fs";
7589
8358
  import { request } from "http";
7590
8359
  import { createServer as createServer4 } from "net";
7591
- import { join as join28 } from "path";
8360
+ import { join as join30 } from "path";
7592
8361
 
7593
8362
  // src/core/codexServer.ts
7594
- import { execFileSync as execFileSync11 } from "child_process";
8363
+ import { execFileSync as execFileSync13 } from "child_process";
7595
8364
  function isAppServerArgs(args, url) {
7596
8365
  return /\bapp-server\b/.test(args) && new RegExp(`--listen[= ]${url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|$)`).test(args);
7597
8366
  }
7598
8367
  function processArgs(pid) {
7599
8368
  if (process.platform === "win32") return null;
7600
8369
  try {
7601
- const out = execFileSync11("ps", ["-ww", "-o", "args=", "-p", String(pid)], {
8370
+ const out = execFileSync13("ps", ["-ww", "-o", "args=", "-p", String(pid)], {
7602
8371
  encoding: "utf8",
7603
8372
  stdio: ["ignore", "pipe", "ignore"]
7604
8373
  });
@@ -7756,12 +8525,12 @@ async function runCodexWindow(real2, args, d) {
7756
8525
  }
7757
8526
  var MAX_LOG_BYTES2 = 1024 * 1024;
7758
8527
  function appServerLogPath() {
7759
- return join28(deskbuddyHome(), "codex-app-server.log");
8528
+ return join30(deskbuddyHome(), "codex-app-server.log");
7760
8529
  }
7761
8530
  function windowsDir2() {
7762
- return join28(deskbuddyHome(), "codex-windows");
8531
+ return join30(deskbuddyHome(), "codex-windows");
7763
8532
  }
7764
- function pidAlive(pid) {
8533
+ function pidAlive2(pid) {
7765
8534
  try {
7766
8535
  process.kill(pid, 0);
7767
8536
  return true;
@@ -7772,50 +8541,50 @@ function pidAlive(pid) {
7772
8541
  function daemonAlive(pidFile = pidFilePath(), args = processArgs) {
7773
8542
  let pid = 0;
7774
8543
  try {
7775
- pid = Number(readFileSync21(pidFile, "utf8").trim());
8544
+ pid = Number(readFileSync22(pidFile, "utf8").trim());
7776
8545
  } catch {
7777
8546
  return false;
7778
8547
  }
7779
- if (!Number.isInteger(pid) || pid <= 0 || !pidAlive(pid)) return false;
8548
+ if (!Number.isInteger(pid) || pid <= 0 || !pidAlive2(pid)) return false;
7780
8549
  const a = args(pid);
7781
8550
  return !!a && /cli\.js daemon(?:\s|$)/.test(a);
7782
8551
  }
7783
8552
  function probeReady(url) {
7784
8553
  const port = Number(url.slice(url.lastIndexOf(":") + 1));
7785
- return new Promise((resolve5) => {
8554
+ return new Promise((resolve6) => {
7786
8555
  const req2 = request({ host: "127.0.0.1", port, path: "/readyz", method: "GET", timeout: 800 }, (res) => {
7787
8556
  res.resume();
7788
- resolve5((res.statusCode ?? 0) >= 200 && (res.statusCode ?? 0) < 300);
8557
+ resolve6((res.statusCode ?? 0) >= 200 && (res.statusCode ?? 0) < 300);
7789
8558
  });
7790
8559
  req2.on("timeout", () => req2.destroy());
7791
- req2.on("error", () => resolve5(false));
8560
+ req2.on("error", () => resolve6(false));
7792
8561
  req2.end();
7793
8562
  });
7794
8563
  }
7795
8564
  function freePort() {
7796
- return new Promise((resolve5, reject) => {
8565
+ return new Promise((resolve6, reject) => {
7797
8566
  const srv = createServer4();
7798
8567
  srv.unref();
7799
8568
  srv.on("error", reject);
7800
8569
  srv.listen(0, "127.0.0.1", () => {
7801
8570
  const addr = srv.address();
7802
8571
  const port = typeof addr === "object" && addr ? addr.port : 0;
7803
- srv.close(() => port ? resolve5(port) : reject(new Error("no port")));
8572
+ srv.close(() => port ? resolve6(port) : reject(new Error("no port")));
7804
8573
  });
7805
8574
  });
7806
8575
  }
7807
8576
  function startServer(real2, url, env, cwd) {
7808
8577
  const logPath2 = appServerLogPath();
7809
- mkdirSync23(deskbuddyHome(), { recursive: true });
8578
+ mkdirSync25(deskbuddyHome(), { recursive: true });
7810
8579
  try {
7811
8580
  if (statSync10(logPath2).size > MAX_LOG_BYTES2) truncateSync2(logPath2, 0);
7812
8581
  } catch {
7813
8582
  }
7814
- const fd = openSync3(logPath2, "a");
8583
+ const fd = openSync4(logPath2, "a");
7815
8584
  try {
7816
8585
  const childEnv2 = { ...env, DESKBUDDY_WRAPPED: "1" };
7817
8586
  delete childEnv2.DESKBUDDY_SHIM;
7818
- const child = spawn4(real2.file, [...real2.prefixArgs, "app-server", "--listen", url], {
8587
+ const child = spawn5(real2.file, [...real2.prefixArgs, "app-server", "--listen", url], {
7819
8588
  detached: true,
7820
8589
  stdio: ["ignore", fd, fd],
7821
8590
  cwd,
@@ -7826,7 +8595,7 @@ function startServer(real2, url, env, cwd) {
7826
8595
  child.unref();
7827
8596
  return child.pid ?? null;
7828
8597
  } finally {
7829
- closeSync3(fd);
8598
+ closeSync4(fd);
7830
8599
  }
7831
8600
  }
7832
8601
  function killServer(pid) {
@@ -7840,41 +8609,41 @@ function killOwnServer(pid, url) {
7840
8609
  if (a && isAppServerArgs(a, url)) killServer(pid);
7841
8610
  }
7842
8611
  function recordPath(rec) {
7843
- return join28(windowsDir2(), `${rec.server}.json`);
8612
+ return join30(windowsDir2(), `${rec.server}.json`);
7844
8613
  }
7845
8614
  function writeRecord(rec) {
7846
8615
  try {
7847
- mkdirSync23(windowsDir2(), { recursive: true, mode: 448 });
8616
+ mkdirSync25(windowsDir2(), { recursive: true, mode: 448 });
7848
8617
  const p = recordPath(rec);
7849
- writeFileSync23(p + ".tmp", JSON.stringify(rec), { encoding: "utf8", mode: 384 });
7850
- renameSync16(p + ".tmp", p);
8618
+ writeFileSync24(p + ".tmp", JSON.stringify(rec), { encoding: "utf8", mode: 384 });
8619
+ renameSync17(p + ".tmp", p);
7851
8620
  } catch {
7852
8621
  }
7853
8622
  }
7854
8623
  function removeRecord(rec) {
7855
8624
  try {
7856
- rmSync17(recordPath(rec), { force: true });
8625
+ rmSync19(recordPath(rec), { force: true });
7857
8626
  } catch {
7858
8627
  }
7859
8628
  }
7860
- function reapWindows(dir = windowsDir2(), alive = pidAlive, args = processArgs, kill = killServer) {
8629
+ function reapWindows(dir = windowsDir2(), alive = pidAlive2, args = processArgs, kill = killServer) {
7861
8630
  let names = [];
7862
8631
  try {
7863
- names = readdirSync6(dir).filter((n) => n.endsWith(".json"));
8632
+ names = readdirSync7(dir).filter((n) => n.endsWith(".json"));
7864
8633
  } catch {
7865
8634
  return;
7866
8635
  }
7867
8636
  for (const n of names) {
7868
- const p = join28(dir, n);
8637
+ const p = join30(dir, n);
7869
8638
  let rec;
7870
8639
  try {
7871
- rec = JSON.parse(readFileSync21(p, "utf8"));
8640
+ rec = JSON.parse(readFileSync22(p, "utf8"));
7872
8641
  } catch {
7873
- rmSync17(p, { force: true });
8642
+ rmSync19(p, { force: true });
7874
8643
  continue;
7875
8644
  }
7876
8645
  if (typeof rec.supervisor !== "number" || typeof rec.server !== "number" || typeof rec.url !== "string") {
7877
- rmSync17(p, { force: true });
8646
+ rmSync19(p, { force: true });
7878
8647
  continue;
7879
8648
  }
7880
8649
  if (alive(rec.supervisor)) continue;
@@ -7882,13 +8651,13 @@ function reapWindows(dir = windowsDir2(), alive = pidAlive, args = processArgs,
7882
8651
  const a = args(rec.server);
7883
8652
  if (a && isAppServerArgs(a, rec.url)) kill(rec.server);
7884
8653
  }
7885
- rmSync17(p, { force: true });
8654
+ rmSync19(p, { force: true });
7886
8655
  }
7887
8656
  }
7888
8657
  function launchLog(line) {
7889
8658
  try {
7890
- mkdirSync23(deskbuddyHome(), { recursive: true });
7891
- writeFileSync23(join28(deskbuddyHome(), "wrap.log"), `${(/* @__PURE__ */ new Date()).toISOString()} [codex ${process.pid}] ${line}
8659
+ mkdirSync25(deskbuddyHome(), { recursive: true });
8660
+ writeFileSync24(join30(deskbuddyHome(), "wrap.log"), `${(/* @__PURE__ */ new Date()).toISOString()} [codex ${process.pid}] ${line}
7892
8661
  `, { flag: "a" });
7893
8662
  } catch {
7894
8663
  }
@@ -7928,8 +8697,8 @@ function childEnv(src, windowId) {
7928
8697
  }
7929
8698
  function wrapLog(line) {
7930
8699
  try {
7931
- const p = join29(deskbuddyHome(), "wrap.log");
7932
- mkdirSync24(deskbuddyHome(), { recursive: true });
8700
+ const p = join31(deskbuddyHome(), "wrap.log");
8701
+ mkdirSync26(deskbuddyHome(), { recursive: true });
7933
8702
  try {
7934
8703
  if (statSync11(p).size > MAX_LOG_BYTES3) truncateSync3(p, 0);
7935
8704
  } catch {
@@ -8219,7 +8988,7 @@ function runAttached(tool, p, term0, windowId, deps, launched = false) {
8219
8988
  injector.tick().catch(() => {
8220
8989
  });
8221
8990
  }, TICK_MS);
8222
- return new Promise((resolve5) => {
8991
+ return new Promise((resolve6) => {
8223
8992
  let degraded = false;
8224
8993
  const onFatal = (e) => {
8225
8994
  deps.log(`\u5305\u88C5\u5C42\u5F02\u5E38\uFF1A${e?.stack ?? String(e)}`);
@@ -8270,8 +9039,8 @@ ${WRAP_HINT}\uFF08\u8BBE\u5907\u56DE\u590D\u6682\u65F6\u5199\u4E0D\u8FDB\u8FD9\u
8270
9039
  restoreTerminal();
8271
9040
  process.removeListener("uncaughtException", onFatal);
8272
9041
  for (const [s, h] of sigHandlers) process.removeListener(s, h);
8273
- if (pending > 0) setTimeout(() => resolve5(code), 150);
8274
- else resolve5(code);
9042
+ if (pending > 0) setTimeout(() => resolve6(code), 150);
9043
+ else resolve6(code);
8275
9044
  }
8276
9045
  process.on("uncaughtException", onFatal);
8277
9046
  for (const [s, h] of sigHandlers) {
@@ -8298,7 +9067,8 @@ var USAGE = `deskbuddy \u2014\u2014 \u628A AI \u7F16\u7801\u667A\u80FD\u4F53\u6B
8298
9067
  deskbuddy enable <tool[,tool]> \u5B89\u88C5\u91C7\u96C6 hook\uFF08\u5982 claude_code,codex\uFF09
8299
9068
  deskbuddy disable [tool[,tool]] \u79FB\u9664\u91C7\u96C6 hook\uFF08\u4E0D\u5E26\u53C2\u6570=\u5168\u90E8\uFF09
8300
9069
  deskbuddy update \u5347\u7EA7\u5230\u5F53\u524D\u8FD9\u7248\uFF1A\u91CD\u88C5\u5DF2\u542F\u7528\u5DE5\u5177\u7684 hook\u3001\u91CD\u542F daemon
8301
- \uFF08npx deskbuddy@<\u7248\u672C> update\uFF09
9070
+ \uFF08npx deskbuddy@<\u7248\u672C> update\uFF1B\u4E4B\u540E\u7531 daemon \u81EA\u52A8\u5347\u7EA7\uFF0C
9071
+ config.json \u91CC "autoUpdate": false \u53EF\u5173\u95ED\uFF09
8302
9072
  deskbuddy daemon [--foreground] \u5E38\u9A7B\u6536\u53D6\u8BBE\u5907\u4E0B\u53D1\u7684\u6307\u4EE4\uFF08\u5F00\u673A\u81EA\u542F\u4F1A\u81EA\u52A8\u62C9\u8D77\uFF09
8303
9073
  deskbuddy status \u770B\u91C7\u96C6\u5230\u4E86\u4EC0\u4E48\u3001\u4E0A\u62A5\u4E86\u54EA\u4E9B\u5B57\u6BB5
8304
9074
  deskbuddy logout [--yes] \u79FB\u9664 hook \u5E76\u6E05\u9664\u672C\u673A\u6570\u636E
@@ -8338,7 +9108,7 @@ async function main(argv) {
8338
9108
  case "disable":
8339
9109
  return runDisable(parseTools(arg));
8340
9110
  case "update":
8341
- return runUpdate();
9111
+ return runUpdate({ auto: argv.includes("--auto") });
8342
9112
  case "daemon":
8343
9113
  return runDaemonCommand({ foreground: argv.includes("--foreground") || argv.includes("-f") });
8344
9114
  case "status":