deskbuddy 0.2.0-beta.42 → 0.2.0-beta.46

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.
@@ -1433,6 +1433,13 @@ async function refreshRegistry() {
1433
1433
  } catch {
1434
1434
  }
1435
1435
  }
1436
+ var DEFAULT_TITLE_SOURCES = ["git-branch", "project"];
1437
+ function titleSources(registry, tool) {
1438
+ const fromRegistry = registry?.tools?.[tool]?.titleSources;
1439
+ if (fromRegistry?.length) return fromRegistry;
1440
+ const builtin = FALLBACK.tools[tool]?.titleSources;
1441
+ return builtin?.length ? builtin : DEFAULT_TITLE_SOURCES;
1442
+ }
1436
1443
 
1437
1444
  // src/adapters/ccHook.ts
1438
1445
  import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync3, statSync, writeFileSync as writeFileSync6 } from "fs";
@@ -2475,6 +2482,324 @@ function rememberTitle(titles, info) {
2475
2482
  titles.set(info.id, t.slice(0, 100));
2476
2483
  }
2477
2484
 
2485
+ // src/daemon/gate/options.ts
2486
+ import { homedir as homedir3 } from "os";
2487
+
2488
+ // src/daemon/gate/claudeLabels.ts
2489
+ var CC_MERGE_MAX = 8;
2490
+ var CC_LABEL_MAX_WIDTH = 160;
2491
+ var CC_VALUE_MAX = 256;
2492
+ var CC_SUGGESTIONS_MAX = 64;
2493
+ var CC_TEXT = {
2494
+ yes: "Yes",
2495
+ no: "No",
2496
+ noTell: "No, and tell Claude what to do differently",
2497
+ readHead: "Yes, allow reading from ",
2498
+ fromProject: " from this project",
2499
+ duringSession: " during this session",
2500
+ forSession: " for this session",
2501
+ alwaysAccessHead: "Yes, and always allow access to ",
2502
+ accessHead: "Yes, and allow access to ",
2503
+ allowHead: "Yes, and allow ",
2504
+ accessAnd: " access and",
2505
+ and: " and",
2506
+ commands: " commands",
2507
+ dontAskHead: "Yes, and don't ask again for ",
2508
+ /** 可编辑前缀项(单条子命令):标签 + `: ` + 前缀(弯撇号 U+2019) */
2509
+ dontAskPrefix: "Yes, and don\u2019t ask again for: ",
2510
+ switchHead: "Yes, and switch to ",
2511
+ claudeFolderProject: "Yes, and allow Claude to edit files in this project's .claude folder for this session",
2512
+ claudeFolderHome: "Yes, and allow Claude to edit files in its ~/.claude folder for this session",
2513
+ autoMode: "Yes, and switch to auto mode",
2514
+ autoModeNote: "\xB7 auto mode handles these prompts for you"
2515
+ };
2516
+ var CC_MODE_TEXT = {
2517
+ acceptEdits: "accept edits (auto-approve file edits and common file commands)"
2518
+ };
2519
+ function isWide(cp) {
2520
+ return cp >= 4352 && cp <= 4447 || cp >= 11904 && cp <= 12350 || cp >= 12353 && cp <= 13311 || cp >= 13312 && cp <= 19903 || cp >= 19968 && cp <= 40959 || cp >= 40960 && cp <= 42191 || cp >= 44032 && cp <= 55203 || cp >= 63744 && cp <= 64255 || cp >= 65072 && cp <= 65103 || cp >= 65280 && cp <= 65376 || cp >= 65504 && cp <= 65510;
2521
+ }
2522
+ function plainChars(s) {
2523
+ for (const ch of s) {
2524
+ const cp = ch.codePointAt(0);
2525
+ if (cp >= 32 && cp <= 126) continue;
2526
+ if (isWide(cp)) continue;
2527
+ return false;
2528
+ }
2529
+ return true;
2530
+ }
2531
+ function displayWidth(s) {
2532
+ let w = 0;
2533
+ for (const ch of s) w += isWide(ch.codePointAt(0)) ? 2 : 1;
2534
+ return w;
2535
+ }
2536
+ function shownAsIs(s) {
2537
+ return s.length > 0 && s.length <= CC_VALUE_MAX && s.trim() === s && !/\s\s/.test(s) && !s.startsWith('"') && !s.endsWith('"') && plainChars(s);
2538
+ }
2539
+ function listItemAsIs(s) {
2540
+ return shownAsIs(s) && !s.includes(",") && !s.includes(";") && !s.includes(" and ") && !s.startsWith("and ") && !s.endsWith(" and");
2541
+ }
2542
+ function joinAnd(items) {
2543
+ if (items.length <= 2) return items.join(" and ");
2544
+ return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
2545
+ }
2546
+ var uniq = (a) => [...new Set(a)];
2547
+ function distinctShown(raw, show) {
2548
+ const seen = /* @__PURE__ */ new Set();
2549
+ for (const r of raw) {
2550
+ const k = show(r).normalize("NFC");
2551
+ if (seen.has(k)) return false;
2552
+ seen.add(k);
2553
+ }
2554
+ return true;
2555
+ }
2556
+ var isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
2557
+ var ruleOut = (r) => ({ toolName: r.toolName, ruleContent: r.ruleContent });
2558
+ var addRulesOut = (rules, destination) => ({ type: "addRules", rules: rules.map(ruleOut), behavior: "allow", destination });
2559
+ var addDirsOut = (directories, destination) => ({ type: "addDirectories", directories, destination });
2560
+ var DESTS = /* @__PURE__ */ new Set(["localSettings", "session"]);
2561
+ function readShown(rc) {
2562
+ if (rc.includes("\\")) return null;
2563
+ const s = rc.replace(/\/\*\*$/, "").replace(/^\.\//, "").replace(/^\/\//, "/");
2564
+ return s ? s : null;
2565
+ }
2566
+ function commandShown(rc) {
2567
+ const s = rc.endsWith(":*") || rc.endsWith(" *") ? rc.slice(0, -2) : rc;
2568
+ if (!s || /[<>]/.test(s)) return null;
2569
+ return s;
2570
+ }
2571
+ function shellApplies(sugg, tool) {
2572
+ if (sugg.length > CC_SUGGESTIONS_MAX) return null;
2573
+ const out = [];
2574
+ for (const s of sugg) {
2575
+ if (!isObj(s)) return null;
2576
+ if (s.type === "addRules") {
2577
+ if (s.behavior !== "allow" || !DESTS.has(s.destination)) continue;
2578
+ if (!Array.isArray(s.rules)) return null;
2579
+ const rules = [];
2580
+ for (const r of s.rules) {
2581
+ if (!isObj(r) || typeof r.toolName !== "string" || r.ruleContent !== void 0 && typeof r.ruleContent !== "string") return null;
2582
+ if (r.toolName === tool) {
2583
+ if (typeof r.ruleContent !== "string" || !r.ruleContent) continue;
2584
+ if (commandShown(r.ruleContent) === null) return null;
2585
+ rules.push(ruleOut(r));
2586
+ } else if (r.toolName === "Read") {
2587
+ if (typeof r.ruleContent !== "string" || !r.ruleContent) continue;
2588
+ if (readShown(r.ruleContent) === null) return null;
2589
+ rules.push(ruleOut(r));
2590
+ }
2591
+ }
2592
+ if (rules.length) out.push(addRulesOut(rules, s.destination));
2593
+ } else if (s.type === "addDirectories") {
2594
+ if (!DESTS.has(s.destination)) continue;
2595
+ if (!Array.isArray(s.directories)) return null;
2596
+ const dirs = s.directories.filter((d) => typeof d === "string" && d.trim() !== "");
2597
+ if (dirs.length !== s.directories.length) return null;
2598
+ if (dirs.length) out.push(addDirsOut(dirs, s.destination));
2599
+ }
2600
+ }
2601
+ return out;
2602
+ }
2603
+ function shellRowLabel(applies, tool, cwdShown) {
2604
+ const rules = applies.filter((s) => s.type === "addRules").flatMap((s) => s.rules);
2605
+ const dirsRaw = applies.filter((s) => s.type === "addDirectories").flatMap((s) => s.directories);
2606
+ if (rules.length + dirsRaw.length > CC_MERGE_MAX) return null;
2607
+ const dirs = uniq(dirsRaw);
2608
+ const reads = uniq(rules.filter((r) => r.toolName === "Read").map((r) => r.ruleContent));
2609
+ const cmds = uniq(rules.filter((r) => r.toolName === tool).map((r) => r.ruleContent));
2610
+ const readShow = (r) => readShown(r);
2611
+ const cmdShow = (r) => commandShown(r);
2612
+ const pathRaw = [...reads, ...dirs];
2613
+ const pathShow = (r) => reads.includes(r) ? readShow(r) : r;
2614
+ if (!distinctShown(pathRaw, pathShow) || !distinctShown(cmds, cmdShow)) return null;
2615
+ const R = cmds.map(cmdShow);
2616
+ const E = reads.map(readShow);
2617
+ const K = dirs;
2618
+ if (![...R, ...E, ...K].every(listItemAsIs)) return null;
2619
+ if (displayWidth([...R, ...E, ...K].join(" and ")) > CC_LABEL_MAX_WIDTH) return null;
2620
+ const hasDir = K.length > 0;
2621
+ const hasRead = E.length > 0;
2622
+ const hasCmd = R.length > 0;
2623
+ if (hasRead && !hasDir && !hasCmd) return CC_TEXT.readHead + joinAnd(E) + CC_TEXT.fromProject;
2624
+ if (!hasCmd && (hasDir || hasRead)) return CC_TEXT.alwaysAccessHead + joinAnd([...K, ...E]) + CC_TEXT.fromProject;
2625
+ if (hasCmd && !hasDir && !hasRead) {
2626
+ if (cwdShown === null) return null;
2627
+ return CC_TEXT.dontAskHead + joinAnd(R) + CC_TEXT.commands + " in " + cwdShown;
2628
+ }
2629
+ if (hasCmd) {
2630
+ const paths = [...K, ...E];
2631
+ if (paths.length === 1 && R.length === 1) return CC_TEXT.accessHead + paths[0] + CC_TEXT.and + " " + R[0] + CC_TEXT.commands;
2632
+ return CC_TEXT.allowHead + joinAnd(paths) + CC_TEXT.accessAnd + " " + joinAnd(R) + CC_TEXT.commands;
2633
+ }
2634
+ return null;
2635
+ }
2636
+ function projectSlug(p) {
2637
+ return p.replace(/[^a-zA-Z0-9]/g, "-");
2638
+ }
2639
+ function sessionRoot(cwd, transcriptPath) {
2640
+ if (typeof cwd !== "string" || !cwd || typeof transcriptPath !== "string" || !transcriptPath) return null;
2641
+ if (!shownAsIs(cwd) || /[\u0000-\u001f]/.test(cwd)) return null;
2642
+ const parts = transcriptPath.split(/[\\/]/);
2643
+ const dir2 = parts.length >= 2 ? parts[parts.length - 2] : "";
2644
+ const slug = projectSlug(cwd);
2645
+ if (slug.length <= 200 ? dir2 !== slug : !dir2.startsWith(`${slug.slice(0, 200)}-`)) return null;
2646
+ return cwd;
2647
+ }
2648
+ var CC_AUTO_LABEL = `${CC_TEXT.autoMode} ${CC_TEXT.autoModeNote}`;
2649
+ var CC_AUTO_DESC = "\u5141\u8BB8\u8FD9\u4E00\u6B21\uFF0C\u5E76\u628A\u6574\u4E2A\u4F1A\u8BDD\u5207\u5230\u81EA\u52A8\u6A21\u5F0F\uFF1A\u4E4B\u540E\u8FD9\u7C7B\u786E\u8BA4\u7531 Claude \u81EA\u5DF1\u5224\u65AD\u653E\u884C\uFF0C\u4E0D\u518D\u9010\u6761\u95EE\u4F60";
2650
+ var CC_AUTO_MODE_UPDATE = { type: "setMode", mode: "auto", destination: "session" };
2651
+ function yesRowNo(middle, action, opts = {}) {
2652
+ const options = [{ id: "allow", label: CC_TEXT.yes, kind: "allow" }];
2653
+ const actions = { allow: { decision: "allow" } };
2654
+ if (middle && action) {
2655
+ options.push(middle);
2656
+ actions[middle.id] = action;
2657
+ }
2658
+ if (opts.auto) {
2659
+ options.push({ id: "auto", label: CC_AUTO_LABEL, description: CC_AUTO_DESC, kind: "allow_session" });
2660
+ actions.auto = { decision: "allow", updatedPermissions: [{ ...CC_AUTO_MODE_UPDATE }] };
2661
+ }
2662
+ if (opts.noTell) options.push({ id: "deny", label: CC_TEXT.noTell, kind: "deny" });
2663
+ else options.push({ id: "deny", label: CC_TEXT.no, description: CC_TEXT.noTell, kind: "deny", input: true });
2664
+ actions.deny = { decision: "deny" };
2665
+ return { options, actions };
2666
+ }
2667
+ function claudeShellLabels(tool, sugg, auto, ctx = {}) {
2668
+ if (isObj(ctx.toolInput) && "_host" in ctx.toolInput) return null;
2669
+ const applies = shellApplies(sugg, tool);
2670
+ if (applies === null || applies.length === 0) return null;
2671
+ const mixed = applies.some((s) => s.type === "addDirectories" || s.rules?.some((r) => r.toolName !== tool));
2672
+ const cmds = uniq(applies.flatMap((s) => s.type === "addRules" ? s.rules : []).filter((r) => r.toolName === tool).map((r) => r.ruleContent));
2673
+ const autoOpt = tool === "Bash" && auto;
2674
+ if (mixed) {
2675
+ const label3 = shellRowLabel(applies, tool, null);
2676
+ if (label3 === null) return null;
2677
+ const kind2 = applies.some((s) => s.destination === "localSettings") ? "allow_always" : "allow_session";
2678
+ const id = cmds.length ? "always" : applies.some((s) => s.type === "addDirectories") ? "dir" : "read";
2679
+ return yesRowNo({ id, label: label3, kind: kind2 }, { decision: "allow", updatedPermissions: applies }, { auto: autoOpt });
2680
+ }
2681
+ if (tool === "PowerShell") return null;
2682
+ if (cmds.length === 1) {
2683
+ if (sugg.length !== 1) return null;
2684
+ const raw = sugg[0];
2685
+ if (!Array.isArray(raw.rules) || raw.rules.length !== 1) return null;
2686
+ const s = applies[0];
2687
+ const rules = s.rules;
2688
+ if (s.destination !== "localSettings" || rules.length !== 1) return null;
2689
+ const rc = rules[0].ruleContent;
2690
+ if (!isObj(raw.rules[0]) || raw.rules[0].toolName !== tool || raw.rules[0].ruleContent !== rc) return null;
2691
+ if (!rc.endsWith(" *") || rc.trim() === "*") return null;
2692
+ return yesRowNo(
2693
+ { id: "always", label: CC_TEXT.dontAskPrefix + rc, kind: "allow_always" },
2694
+ { decision: "allow", updatedPermissions: [addRulesOut([{ toolName: tool, ruleContent: rc }], "localSettings")] },
2695
+ { auto: autoOpt }
2696
+ );
2697
+ }
2698
+ const root = sessionRoot(ctx.cwd, ctx.transcriptPath);
2699
+ if (root === null) return null;
2700
+ const label2 = shellRowLabel(applies, tool, root);
2701
+ if (label2 === null) return null;
2702
+ const kind = applies.some((s) => s.destination === "localSettings") ? "allow_always" : "allow_session";
2703
+ return yesRowNo({ id: "always", label: label2, kind }, { decision: "allow", updatedPermissions: applies }, { auto: autoOpt });
2704
+ }
2705
+ function underDir(file3, dir2, platform) {
2706
+ const fold = platform === "darwin" || platform === "win32" ? (s) => s.toLowerCase() : (s) => s;
2707
+ return fold(file3).startsWith(fold(dir2.replace(/\/+$/, "")) + "/");
2708
+ }
2709
+ function claudeFileLabels(toolName, sugg, ctx = {}) {
2710
+ const read2 = toolName === "Read";
2711
+ if (read2 && ctx.permissionMode === "auto") return null;
2712
+ if (sugg.length === 0 || sugg.length > CC_SUGGESTIONS_MAX || !sugg.every(isObj)) return null;
2713
+ const sg = sugg;
2714
+ const file3 = isObj(ctx.toolInput) ? ctx.toolInput.file_path ?? ctx.toolInput.notebook_path : void 0;
2715
+ if (!read2 && typeof file3 === "string") {
2716
+ const platform = ctx.platform ?? process.platform;
2717
+ const home = ctx.home;
2718
+ const root = sessionRoot(ctx.cwd, ctx.transcriptPath);
2719
+ const absPosix = file3.startsWith("/") && !file3.split("/").includes("..");
2720
+ const inHome = !!home && absPosix && underDir(file3, `${home}/.claude`, platform);
2721
+ const inProject = !!root && absPosix && underDir(file3, `${root}/.claude`, platform);
2722
+ const claudeSegment = /(^|[\\/])\.claude([\\/]|$)/i.test(file3);
2723
+ if (claudeSegment && !inHome && !inProject) return null;
2724
+ if (inHome || inProject) {
2725
+ const content = inHome ? "~/.claude/**" : "/.claude/**";
2726
+ const want = { type: "addRules", rules: [{ toolName: "Edit", ruleContent: content }], behavior: "allow", destination: "session" };
2727
+ const s = sg.length === 1 ? sg[0] : null;
2728
+ const r = s && Array.isArray(s.rules) && s.rules.length === 1 && isObj(s.rules[0]) ? s.rules[0] : null;
2729
+ if (!s || s.type !== "addRules" || s.behavior !== "allow" || s.destination !== "session" || !r || r.toolName !== "Edit" || r.ruleContent !== content) return null;
2730
+ return yesRowNo(
2731
+ { id: "session", label: inHome ? CC_TEXT.claudeFolderHome : CC_TEXT.claudeFolderProject, kind: "allow_session" },
2732
+ { decision: "allow", updatedPermissions: [want] }
2733
+ );
2734
+ }
2735
+ }
2736
+ const rows = [];
2737
+ for (const s of sg) {
2738
+ if (s.destination !== "session") return null;
2739
+ if (s.type === "setMode") {
2740
+ if (read2 || s.mode !== "acceptEdits") return null;
2741
+ } else if (s.type === "addDirectories") {
2742
+ if (read2) return null;
2743
+ } else if (s.type === "addRules") {
2744
+ if (s.behavior !== "allow") return null;
2745
+ } else return null;
2746
+ }
2747
+ if (sg.filter((s) => s.type === "setMode").length > 1 || sg.filter((s) => s.type === "addDirectories").length > 1) return null;
2748
+ if (sg.some((s) => s.type === "setMode")) {
2749
+ rows.push({ label: CC_TEXT.switchHead + CC_MODE_TEXT.acceptEdits + CC_TEXT.forSession, applies: [{ type: "setMode", destination: "session", mode: "acceptEdits" }] });
2750
+ }
2751
+ const dirSugg = sg.find((s) => s.type === "addDirectories");
2752
+ if (dirSugg) {
2753
+ const dirsIn = Array.isArray(dirSugg.directories) ? dirSugg.directories : null;
2754
+ if (!dirsIn || dirsIn.some((d) => typeof d !== "string" || d.trim() === "")) return null;
2755
+ const dirs = uniq(dirsIn);
2756
+ if (dirs.length === 0 || !dirs.every(listItemAsIs) || !distinctShown(dirs, (d) => d)) return null;
2757
+ if (dirs.length <= CC_MERGE_MAX && displayWidth(dirs.join(", ")) <= CC_LABEL_MAX_WIDTH)
2758
+ rows.push({ label: CC_TEXT.alwaysAccessHead + dirs.join(", ") + CC_TEXT.forSession, applies: [{ type: "addDirectories", destination: "session", directories: dirs }] });
2759
+ }
2760
+ const ruleSuggs = sg.filter((s) => s.type === "addRules");
2761
+ if (ruleSuggs.length) {
2762
+ const applies = [];
2763
+ const reads = [];
2764
+ for (const s of ruleSuggs) {
2765
+ if (!Array.isArray(s.rules)) return null;
2766
+ const keep = [];
2767
+ for (const r of s.rules) {
2768
+ if (!isObj(r) || r.toolName !== "Read" || typeof r.ruleContent !== "string" || readShown(r.ruleContent) === null) return null;
2769
+ keep.push(ruleOut(r));
2770
+ reads.push(r.ruleContent);
2771
+ }
2772
+ if (keep.length) applies.push(addRulesOut(keep, s.destination));
2773
+ }
2774
+ if (reads.length === 0) return null;
2775
+ const u = uniq(reads);
2776
+ const shown = u.map((r) => readShown(r));
2777
+ if (!distinctShown(u, (r) => readShown(r)) || !shown.every(listItemAsIs)) return null;
2778
+ if (reads.length <= CC_MERGE_MAX && displayWidth(shown.join(" and ")) <= CC_LABEL_MAX_WIDTH)
2779
+ rows.push({ label: CC_TEXT.readHead + joinAnd(shown) + CC_TEXT.duringSession, applies });
2780
+ }
2781
+ if (rows.length === 0) return null;
2782
+ const label2 = rows.map((r) => r.label).join("; ");
2783
+ return yesRowNo({ id: "session", label: label2, kind: "allow_session" }, { decision: "allow", updatedPermissions: rows.flatMap((r) => r.applies) });
2784
+ }
2785
+ function claudeWebFetchLabels(sugg, ctx = {}) {
2786
+ if (sugg.length !== 1 || !isObj(sugg[0])) return null;
2787
+ const s = sugg[0];
2788
+ if (s.type !== "addRules" || s.behavior !== "allow" || s.destination !== "localSettings" || !Array.isArray(s.rules) || s.rules.length !== 1) return null;
2789
+ const r = s.rules[0];
2790
+ if (!isObj(r) || r.toolName !== "WebFetch" || typeof r.ruleContent !== "string" || !r.ruleContent.startsWith("domain:")) return null;
2791
+ const host = r.ruleContent.slice("domain:".length);
2792
+ const url = isObj(ctx.toolInput) ? ctx.toolInput.url : void 0;
2793
+ let urlHost = null;
2794
+ try {
2795
+ urlHost = typeof url === "string" ? new URL(url).hostname : null;
2796
+ } catch {
2797
+ urlHost = null;
2798
+ }
2799
+ if (!host || host !== urlHost || host.includes("*") || !shownAsIs(host)) return null;
2800
+ return yesRowNo({ id: "always", label: CC_TEXT.dontAskHead + host, kind: "allow_always" }, { decision: "allow", updatedPermissions: [addRulesOut([{ toolName: "WebFetch", ruleContent: r.ruleContent }], "localSettings")] }, { noTell: true });
2801
+ }
2802
+
2478
2803
  // src/daemon/gate/options.ts
2479
2804
  var OPTIONS_MAX = 16;
2480
2805
  var OPTION_LABEL_MAX = 2e3;
@@ -2514,95 +2839,40 @@ function checkOptions(set, limits = {}) {
2514
2839
  return { options: out, actions };
2515
2840
  }
2516
2841
  var CLAUDE_TEXT = {
2517
- yes: "Yes",
2518
- no: "No",
2519
- noInput: "No, and tell Claude what to do differently",
2842
+ yes: CC_TEXT.yes,
2843
+ no: CC_TEXT.no,
2844
+ noInput: CC_TEXT.noTell,
2520
2845
  /** 单条 Bash 前缀规则:`Yes, and don’t ask again for: <规则>`(注意是弯撇号 U+2019) */
2521
- dontAskPrefix: "Yes, and don\u2019t ask again for: ",
2846
+ dontAskPrefix: CC_TEXT.dontAskPrefix,
2522
2847
  /** 编辑类 setMode acceptEdits:整句作 label(101 字,用户拍板不拆;不含快捷键提示 `(shift+tab)`) */
2523
- acceptEdits: "Yes, and switch to accept edits (auto-approve file edits and common file commands) for this session",
2848
+ acceptEdits: CC_TEXT.switchHead + CC_MODE_TEXT.acceptEdits + CC_TEXT.forSession,
2524
2849
  /** Bash 的切 auto 模式(只在默认 / acceptEdits 模式下出现;非 workflow 来源时界面上带 ` · auto mode handles these prompts for you`) */
2525
- autoMode: "Yes, and switch to auto mode \xB7 auto mode handles these prompts for you",
2850
+ autoMode: CC_AUTO_LABEL,
2526
2851
  /** 设备上给「切 auto 模式」的说明(问题表 272:它会把整个会话切过去,要写清楚) */
2527
- autoModeDesc: "\u5141\u8BB8\u8FD9\u4E00\u6B21\uFF0C\u5E76\u628A\u6574\u4E2A\u4F1A\u8BDD\u5207\u5230\u81EA\u52A8\u6A21\u5F0F\uFF1A\u4E4B\u540E\u8FD9\u7C7B\u786E\u8BA4\u7531 Claude \u81EA\u5DF1\u5224\u65AD\u653E\u884C\uFF0C\u4E0D\u518D\u9010\u6761\u95EE\u4F60",
2528
- /**
2529
- * Bash 碰到工作区外的目录(2.1.295 实测,问题表 272):建议是 addDirectories(session) + setMode acceptEdits(session),
2530
- * 界面合成一项 `Yes, and always allow access to <目录> from this project`,选中两条一起生效
2531
- */
2532
- dirAccessHead: "Yes, and always allow access to ",
2533
- dirAccessTail: " from this project"
2852
+ autoModeDesc: CC_AUTO_DESC,
2853
+ /** Bash 碰到工作区外的目录(2.1.295 实测,问题表 272):`Yes, and always allow access to <目录> from this project` */
2854
+ dirAccessHead: CC_TEXT.alwaysAccessHead,
2855
+ dirAccessTail: CC_TEXT.fromProject,
2856
+ /** Bash 读工作区外的路径(问题表 278,2.1.295 实测 `ls /tmp | wc -l`):`Yes, allow reading from <目录> from this project` */
2857
+ readAccessHead: CC_TEXT.readHead
2534
2858
  };
2535
- var CLAUDE_AUTO_MODE_UPDATE = { type: "setMode", mode: "auto", destination: "session" };
2536
- var CLAUDE_EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
2537
- function claudeYesNo(middle, action, auto = false) {
2538
- const options = [{ id: "allow", label: CLAUDE_TEXT.yes, kind: "allow" }];
2539
- const actions = { allow: { decision: "allow" } };
2540
- if (middle && action) {
2541
- options.push(middle);
2542
- actions[middle.id] = action;
2543
- }
2544
- if (auto) {
2545
- options.push({ id: "auto", label: CLAUDE_TEXT.autoMode, description: CLAUDE_TEXT.autoModeDesc, kind: "allow_session" });
2546
- actions.auto = { decision: "allow", updatedPermissions: [{ ...CLAUDE_AUTO_MODE_UPDATE }] };
2547
- }
2548
- options.push({ id: "deny", label: CLAUDE_TEXT.no, description: CLAUDE_TEXT.noInput, kind: "deny", input: true });
2549
- actions.deny = { decision: "deny" };
2550
- return { options, actions };
2551
- }
2552
- function claudeDirAccess(sugg) {
2553
- if (sugg.length < 1 || sugg.length > 2) return null;
2554
- let dir2 = null;
2555
- let mode = 0;
2556
- for (const x of sugg) {
2557
- const s = x;
2558
- if (s && s.type === "addDirectories" && s.destination === "session" && Array.isArray(s.directories) && s.directories.length === 1 && typeof s.directories[0] === "string" && s.directories[0]) {
2559
- if (dir2 !== null) return null;
2560
- dir2 = s.directories[0];
2561
- } else if (s && s.type === "setMode" && s.mode === "acceptEdits" && s.destination === "session") mode++;
2562
- else return null;
2563
- }
2564
- if (!dir2 || mode > 1 || /[\u0000-\u001f\u007f]/.test(dir2)) return null;
2565
- return dir2;
2566
- }
2859
+ var CLAUDE_FILE_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit", "Read"]);
2567
2860
  function claudeOptions(toolName, suggestions, ctx = {}) {
2568
- const sugg = suggestions === void 0 || suggestions === null ? [] : suggestions;
2569
- if (!Array.isArray(sugg)) return null;
2570
- if (toolName === "Bash") {
2571
- const auto = ctx.autoModeAllowed === true && (ctx.permissionMode === "default" || ctx.permissionMode === "acceptEdits");
2572
- if (sugg.length === 0) return checkOptions(claudeYesNo(null, void 0, auto));
2573
- const dir2 = claudeDirAccess(sugg);
2574
- if (dir2) {
2575
- return checkOptions(
2576
- claudeYesNo(
2577
- { id: "dir", label: CLAUDE_TEXT.dirAccessHead + dir2 + CLAUDE_TEXT.dirAccessTail, kind: "allow_session" },
2578
- { decision: "allow", updatedPermissions: sugg },
2579
- auto
2580
- )
2581
- );
2582
- }
2583
- if (sugg.length !== 1) return null;
2584
- const s = sugg[0];
2585
- if (!s || s.type !== "addRules" || s.behavior !== "allow" || s.destination !== "localSettings") return null;
2586
- const rules = Array.isArray(s.rules) ? s.rules : [];
2587
- if (rules.length !== 1 || rules[0]?.toolName !== "Bash") return null;
2588
- const rc = rules[0].ruleContent;
2589
- if (typeof rc !== "string" || !rc.endsWith(" *") || rc.trim() === "*") return null;
2590
- return checkOptions(
2591
- claudeYesNo({ id: "always", label: CLAUDE_TEXT.dontAskPrefix + rc, kind: "allow_always" }, { decision: "allow", updatedPermissions: [s] }, auto)
2592
- );
2593
- }
2594
- if (CLAUDE_EDIT_TOOLS.has(toolName)) {
2595
- if (sugg.length !== 1) return null;
2596
- const s = sugg[0];
2597
- if (!s || s.type !== "setMode" || s.mode !== "acceptEdits" || s.destination !== "session") return null;
2598
- return checkOptions(
2599
- claudeYesNo(
2600
- { id: "session", label: CLAUDE_TEXT.acceptEdits, kind: "allow_session" },
2601
- { decision: "allow", updatedPermissions: [s] }
2602
- )
2603
- );
2861
+ const fallback = (auto) => {
2862
+ const set = checkOptions(yesRowNo(null, null, { auto }));
2863
+ return set && Array.isArray(suggestions) && suggestions.length > 0 ? { ...set, pcAllowAmbiguous: true } : set;
2864
+ };
2865
+ const lctx = { cwd: ctx.cwd, toolInput: ctx.toolInput, transcriptPath: ctx.transcriptPath, permissionMode: ctx.permissionMode, home: ctx.home ?? homedir3(), platform: ctx.platform };
2866
+ if (toolName === "Bash" || toolName === "PowerShell") {
2867
+ const auto = toolName === "Bash" && Array.isArray(suggestions) && ctx.autoModeAllowed === true && (ctx.permissionMode === "default" || ctx.permissionMode === "acceptEdits");
2868
+ if (!Array.isArray(suggestions)) return fallback(false);
2869
+ if (suggestions.length === 0) return fallback(auto);
2870
+ return checkOptions(claudeShellLabels(toolName, suggestions, auto, lctx)) ?? fallback(auto);
2604
2871
  }
2605
- return null;
2872
+ const sugg = Array.isArray(suggestions) ? suggestions : [];
2873
+ if (CLAUDE_FILE_TOOLS.has(toolName)) return checkOptions(claudeFileLabels(toolName, sugg, lctx)) ?? fallback(false);
2874
+ if (toolName === "WebFetch") return checkOptions(claudeWebFetchLabels(sugg, lctx)) ?? fallback(false);
2875
+ return fallback(false);
2606
2876
  }
2607
2877
  var CODEX_TEXT = {
2608
2878
  mcpAllow: "Allow",
@@ -2676,10 +2946,12 @@ function codexPermissionsCard(p) {
2676
2946
  const summary = `\u7533\u8BF7${parts.join("\u3001")}${reason ? `\uFF1A${reason}` : ""}`;
2677
2947
  const options = [
2678
2948
  { id: "allow", label: "Allow once", description: "\u8FD9\u4E00\u8F6E\u653E\u5F00\u7533\u8BF7\u7684\u6743\u9650", kind: "allow" },
2949
+ { id: "session", label: "Allow this conversation", description: "\u8FD9\u4E2A\u5BF9\u8BDD\u91CC\u4E4B\u540E\u540C\u6837\u7684\u7533\u8BF7\u90FD\u653E\u5F00", kind: "allow_session" },
2679
2950
  { id: "deny", label: "Deny", kind: "deny" }
2680
2951
  ];
2681
2952
  const actions = {
2682
2953
  allow: { decision: "allow", native: { permissions: grant, scope: "turn" } },
2954
+ session: { decision: "allow", native: { permissions: grant, scope: "session" } },
2683
2955
  deny: { decision: "deny", native: { permissions: {}, scope: "turn" } }
2684
2956
  };
2685
2957
  const set = checkOptions({ options, actions });
@@ -3745,7 +4017,7 @@ function resolveAlias(sessionId, now = Date.now()) {
3745
4017
  // src/wrap/shim.ts
3746
4018
  import { execFile as execFile3, execFileSync as execFileSync7 } from "child_process";
3747
4019
  import { chmodSync as chmodSync3, existsSync as existsSync14, mkdirSync as mkdirSync16, readdirSync as readdirSync6, readFileSync as readFileSync17, rmdirSync, rmSync as rmSync9, writeFileSync as writeFileSync16 } from "fs";
3748
- import { homedir as homedir3 } from "os";
4020
+ import { homedir as homedir4 } from "os";
3749
4021
  import { delimiter, join as join15, resolve as resolve4 } from "path";
3750
4022
  var SHIM_FALLBACK_EXIT = 213;
3751
4023
  var WIN_SHIM_ENABLED = true;
@@ -4071,7 +4343,7 @@ function psWinWhere(exec = execFileSync7) {
4071
4343
  function defaultShimEnv(nodePath, cliPath) {
4072
4344
  const env = process.env;
4073
4345
  const isolated = !!env.VITEST || !!env.DESKBUDDY_HOME;
4074
- const rcHome = env.DESKBUDDY_RC_HOME || (isolated ? null : homedir3());
4346
+ const rcHome = env.DESKBUDDY_RC_HOME || (isolated ? null : homedir4());
4075
4347
  return {
4076
4348
  platform: process.platform,
4077
4349
  binDir: shimDir(),
@@ -4495,11 +4767,11 @@ function launchPending(runId) {
4495
4767
  // src/core/title.ts
4496
4768
  import { closeSync as closeSync3, existsSync as existsSync15, openSync as openSync3, readFileSync as readFileSync20, readSync as readSync3, statSync as statSync7 } from "fs";
4497
4769
  import { createRequire as createRequire2 } from "module";
4498
- import { homedir as homedir5 } from "os";
4770
+ import { homedir as homedir6 } from "os";
4499
4771
  import { basename as basename3, dirname as dirname10, isAbsolute, join as join18 } from "path";
4500
4772
 
4501
4773
  // src/core/dirFilter.ts
4502
- import { homedir as homedir4, tmpdir as tmpdir2 } from "os";
4774
+ import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
4503
4775
  import { posix, win32 as win323 } from "path";
4504
4776
  function norm(p, win) {
4505
4777
  if (win) return win323.resolve(p).replace(/[\\/]+$/, "").toLowerCase();
@@ -4512,7 +4784,7 @@ function isAutoWorkspace(p, opts = {}) {
4512
4784
  if (!p) return false;
4513
4785
  const win = (opts.platform ?? process.platform) === "win32";
4514
4786
  const P = win ? win323 : posix;
4515
- const home = norm(opts.home ?? homedir4(), win);
4787
+ const home = norm(opts.home ?? homedir5(), win);
4516
4788
  const full = norm(p, win);
4517
4789
  const name = P.basename(full);
4518
4790
  const parent = P.dirname(full);
@@ -4525,7 +4797,7 @@ function isReportableDir(p, opts = {}) {
4525
4797
  const P = win ? win323 : posix;
4526
4798
  const full = norm(p, win);
4527
4799
  if (full === norm(P.parse(P.resolve(p)).root, win)) return false;
4528
- if (full === norm(opts.home ?? homedir4(), win)) return false;
4800
+ if (full === norm(opts.home ?? homedir5(), win)) return false;
4529
4801
  const tmps = [opts.tmp ?? tmpdir2()];
4530
4802
  if (!win) tmps.push("/tmp", "/private/tmp", "/var/folders", "/private/var/folders");
4531
4803
  for (const t of tmps) if (t && isUnder(full, norm(t, win), win)) return false;
@@ -4534,6 +4806,7 @@ function isReportableDir(p, opts = {}) {
4534
4806
 
4535
4807
  // src/core/title.ts
4536
4808
  var TRANSCRIPT_TAIL_BYTES = 64 * 1024;
4809
+ var GIT_LOOKUP_MAX_UP = 5;
4537
4810
  var SESSION_TITLE_MAX = 100;
4538
4811
  function cleanTitle(text) {
4539
4812
  if (typeof text !== "string") return "";
@@ -4541,9 +4814,21 @@ function cleanTitle(text) {
4541
4814
  return Array.from(flat).slice(0, SESSION_TITLE_MAX).join("").trimEnd();
4542
4815
  }
4543
4816
  var requireHere = createRequire2(import.meta.url);
4817
+ function loadNodeSqlite() {
4818
+ const orig = process.emitWarning;
4819
+ process.emitWarning = function(...args) {
4820
+ if (String(args[0] ?? "").includes("SQLite")) return;
4821
+ return orig.apply(process, args);
4822
+ };
4823
+ try {
4824
+ return requireHere("node:sqlite");
4825
+ } finally {
4826
+ process.emitWarning = orig;
4827
+ }
4828
+ }
4544
4829
  var QODER_DATA_DIRS = { qoder_cn: "com.qodercn.app.stable", qoder: "com.qoder.app.stable" };
4545
4830
  var QODER_APP_DIR_ENV = { qoder_cn: "DESKBUDDY_QODER_CN_APP_DIR", qoder: "DESKBUDDY_QODER_APP_DIR" };
4546
- function qoderDbPaths(tool, platform = process.platform, home = homedir5(), env = process.env) {
4831
+ function qoderDbPaths(tool, platform = process.platform, home = homedir6(), env = process.env) {
4547
4832
  let root;
4548
4833
  if (platform === "darwin") root = join18(home, "Library", "Application Support");
4549
4834
  else if (platform === "win32") root = env.APPDATA || join18(home, "AppData", "Roaming");
@@ -4552,11 +4837,157 @@ function qoderDbPaths(tool, platform = process.platform, home = homedir5(), env
4552
4837
  const tools = one ? [tool] : ["qoder_cn", "qoder"];
4553
4838
  return tools.map((t) => join18(env[QODER_APP_DIR_ENV[t] ?? ""] || join18(root, QODER_DATA_DIRS[t] ?? ""), "main.sqlite"));
4554
4839
  }
4840
+ function readQoderSessionTitle(sessionId, paths = qoderDbPaths(null), load = loadNodeSqlite) {
4841
+ if (!sessionId) return null;
4842
+ const existing = paths.filter((p) => existsSync15(p));
4843
+ if (existing.length === 0) return null;
4844
+ let mod;
4845
+ try {
4846
+ mod = load();
4847
+ } catch {
4848
+ return null;
4849
+ }
4850
+ for (const p of existing) {
4851
+ let db;
4852
+ try {
4853
+ db = new mod.DatabaseSync(p, { readOnly: true });
4854
+ const row = db.prepare("select title from chat_sessions where session_id = ?").get(sessionId);
4855
+ const t = cleanTitle(row?.title);
4856
+ if (t) return t;
4857
+ } catch {
4858
+ } finally {
4859
+ try {
4860
+ db?.close();
4861
+ } catch {
4862
+ }
4863
+ }
4864
+ }
4865
+ return null;
4866
+ }
4867
+ function readTailLines(path, bytes) {
4868
+ let fd;
4869
+ try {
4870
+ const size = statSync7(path).size;
4871
+ if (size <= 0) return [];
4872
+ const len = Math.min(size, bytes);
4873
+ const buf = Buffer.alloc(len);
4874
+ fd = openSync3(path, "r");
4875
+ readSync3(fd, buf, 0, len, size - len);
4876
+ return buf.toString("utf8").split("\n");
4877
+ } catch {
4878
+ return [];
4879
+ } finally {
4880
+ if (fd !== void 0) {
4881
+ try {
4882
+ closeSync3(fd);
4883
+ } catch {
4884
+ }
4885
+ }
4886
+ }
4887
+ }
4888
+ function readAiTitle(transcriptPath) {
4889
+ if (!transcriptPath) return null;
4890
+ const lines = readTailLines(transcriptPath, TRANSCRIPT_TAIL_BYTES);
4891
+ for (let i = lines.length - 1; i >= 0; i--) {
4892
+ const line = lines[i];
4893
+ if (!line.includes('"ai-title"')) continue;
4894
+ try {
4895
+ const obj = JSON.parse(line);
4896
+ if (obj?.type === "ai-title") {
4897
+ const title = String(obj.aiTitle ?? "").trim();
4898
+ if (title !== "") return title;
4899
+ }
4900
+ } catch {
4901
+ }
4902
+ }
4903
+ return null;
4904
+ }
4905
+ function readCodexThreadName(sessionId, home) {
4906
+ if (!sessionId) return null;
4907
+ const indexPath = join18(home || codexHome(), "session_index.jsonl");
4908
+ const lines = readTailLines(indexPath, TRANSCRIPT_TAIL_BYTES);
4909
+ for (let i = lines.length - 1; i >= 0; i--) {
4910
+ const line = lines[i];
4911
+ if (!line.includes(sessionId)) continue;
4912
+ try {
4913
+ const obj = JSON.parse(line);
4914
+ if (obj?.id === sessionId) {
4915
+ const name = String(obj.thread_name ?? "").trim();
4916
+ if (name !== "") return name;
4917
+ }
4918
+ } catch {
4919
+ }
4920
+ }
4921
+ return null;
4922
+ }
4923
+ function readGitHead(dir2) {
4924
+ try {
4925
+ const dotGit = join18(dir2, ".git");
4926
+ let headPath = join18(dotGit, "HEAD");
4927
+ if (!statSync7(dotGit).isDirectory()) {
4928
+ const m = /^gitdir:\s*(.+)$/m.exec(readFileSync20(dotGit, "utf8"));
4929
+ if (!m) return null;
4930
+ const gitdir = m[1].trim();
4931
+ headPath = join18(isAbsolute(gitdir) ? gitdir : join18(dir2, gitdir), "HEAD");
4932
+ }
4933
+ return readFileSync20(headPath, "utf8").trim();
4934
+ } catch {
4935
+ return null;
4936
+ }
4937
+ }
4938
+ function readGitBranch(cwd, maxUp = GIT_LOOKUP_MAX_UP) {
4939
+ if (!cwd) return null;
4940
+ let dir2 = cwd;
4941
+ for (let up = 0; up <= maxUp; up++) {
4942
+ const head = readGitHead(dir2);
4943
+ if (head !== null) {
4944
+ const ref = /^ref:\s*refs\/heads\/(.+)$/.exec(head);
4945
+ if (!ref) return null;
4946
+ return ref[1].trim() || null;
4947
+ }
4948
+ const parent = dirname10(dir2);
4949
+ if (parent === dir2) break;
4950
+ dir2 = parent;
4951
+ }
4952
+ return null;
4953
+ }
4954
+ function resolveTitle(sources, ctx) {
4955
+ for (const source of sources ?? []) {
4956
+ let value = null;
4957
+ switch (source) {
4958
+ case "cc-ai-title":
4959
+ value = readAiTitle(ctx.transcriptPath);
4960
+ break;
4961
+ case "codex-thread-name":
4962
+ value = readCodexThreadName(ctx.sessionId, ctx.codexHome);
4963
+ break;
4964
+ case "git-branch":
4965
+ value = readGitBranch(ctx.cwd);
4966
+ break;
4967
+ case "first-prompt":
4968
+ value = ctx.firstPrompt || null;
4969
+ break;
4970
+ case "qoder-session-title":
4971
+ value = readQoderSessionTitle(ctx.sessionId, ctx.qoderDbPaths ?? qoderDbPaths(ctx.tool), ctx.loadSqlite);
4972
+ break;
4973
+ case "oc-session-title":
4974
+ value = typeof ctx.ocSessionTitle === "string" ? ctx.ocSessionTitle.trim() || null : null;
4975
+ break;
4976
+ case "project":
4977
+ value = ctx.cwd && !isAutoWorkspace(ctx.cwd) ? basename3(ctx.cwd) || null : null;
4978
+ break;
4979
+ default:
4980
+ value = null;
4981
+ }
4982
+ if (value) return value;
4983
+ }
4984
+ return "";
4985
+ }
4555
4986
 
4556
4987
  // src/daemon/locate.ts
4557
4988
  import { execFile as execFile4, execFileSync as execFileSync9 } from "child_process";
4558
4989
  import { accessSync, closeSync as closeSync4, constants, existsSync as existsSync17, lstatSync, openSync as openSync4, readFileSync as readFileSync22, readSync as readSync4, realpathSync as realpathSync2, statSync as statSync8 } from "fs";
4559
- import { homedir as homedir6 } from "os";
4990
+ import { homedir as homedir7 } from "os";
4560
4991
  import { delimiter as delimiter2, dirname as dirname11, join as join20, win32 as win325 } from "path";
4561
4992
 
4562
4993
  // src/core/agentEnv.ts
@@ -4854,7 +5285,7 @@ function qoderWorker(d, worker, appBin) {
4854
5285
  return { file: d.nodePath, prefixArgs: [worker] };
4855
5286
  }
4856
5287
  var defaultLocateDeps = () => ({
4857
- home: homedir6(),
5288
+ home: homedir7(),
4858
5289
  path: process.env.PATH ?? "",
4859
5290
  nodePath: process.execPath,
4860
5291
  isExecutable: (p) => {
@@ -5538,7 +5969,7 @@ function markElectronBroken(v = true) {
5538
5969
  electronBroken = v;
5539
5970
  }
5540
5971
  var requireHere2 = createRequire3(import.meta.url);
5541
- function loadNodeSqlite() {
5972
+ function loadNodeSqlite2() {
5542
5973
  const orig = process.emitWarning;
5543
5974
  process.emitWarning = function(...args) {
5544
5975
  if (String(args[0] ?? "").includes("SQLite")) return;
@@ -5624,7 +6055,7 @@ function savePendingFile(jobs) {
5624
6055
  var defaultWbAppDbDeps = () => ({
5625
6056
  dbPath: () => workbuddyDbPath(),
5626
6057
  configDir: () => dirname12(workbuddySettingsPath()),
5627
- loadSqlite: loadNodeSqlite,
6058
+ loadSqlite: loadNodeSqlite2,
5628
6059
  electron: () => defaultWbElectron(),
5629
6060
  runElectron: (bin, script, input) => new Promise((resolve7, reject) => {
5630
6061
  const env = { ...process.env, ELECTRON_RUN_AS_NODE: "1" };
@@ -8533,7 +8964,7 @@ function runHook(node, input, agentPid, tool = "dumate") {
8533
8964
 
8534
8965
  // src/daemon/qoderApp/keeper.ts
8535
8966
  import { existsSync as existsSync20, mkdirSync as mkdirSync22, readdirSync as readdirSync12, readFileSync as readFileSync25, renameSync as renameSync16, writeFileSync as writeFileSync22 } from "fs";
8536
- import { homedir as homedir7 } from "os";
8967
+ import { homedir as homedir8 } from "os";
8537
8968
  import { join as join24, win32 as win327 } from "path";
8538
8969
  var WAKE_LAUNCH_GAP_MS2 = 60 * 1e3;
8539
8970
  var RESTART_GAP_MS2 = 30 * 60 * 1e3;
@@ -8834,7 +9265,7 @@ var QoderCdpKeeper = class {
8834
9265
  }
8835
9266
  if (this.#platform() !== "darwin") return null;
8836
9267
  const name = this.#d.brand.macApp.split("/").pop() ?? "";
8837
- for (const p of [this.#d.brand.macApp, join24(homedir7(), "Applications", name)]) if (existsSync20(p)) return p;
9268
+ for (const p of [this.#d.brand.macApp, join24(homedir8(), "Applications", name)]) if (existsSync20(p)) return p;
8838
9269
  return null;
8839
9270
  }
8840
9271
  /** 一轮:健康检查 → 活着核对属主。🔴 平时不重启 App;只有端口无响应满 HUNG_RESTART_MS 时按节流与在场规则重启 */
@@ -10270,12 +10701,12 @@ function migrateLegacyQoder(out, detect = () => detectTools()) {
10270
10701
  // src/daemon/loginGuide.ts
10271
10702
  import { execFile as execFile9, execFileSync as execFileSync13 } from "child_process";
10272
10703
  import { chmodSync as chmodSync9, existsSync as existsSync27, mkdirSync as mkdirSync26, readFileSync as readFileSync30, writeFileSync as writeFileSync26 } from "fs";
10273
- import { homedir as homedir9 } from "os";
10704
+ import { homedir as homedir10 } from "os";
10274
10705
  import { join as join35 } from "path";
10275
10706
 
10276
10707
  // src/daemon/executor.ts
10277
10708
  import { execFile as execFile7, execFileSync as execFileSync11 } from "child_process";
10278
- import { homedir as homedir8 } from "os";
10709
+ import { homedir as homedir9 } from "os";
10279
10710
  import { join as join33 } from "path";
10280
10711
 
10281
10712
  // src/core/codebuddyDirs.ts
@@ -10369,7 +10800,7 @@ var TOOL_BINARY = {
10369
10800
  opencode: "opencode",
10370
10801
  deepseek_harness: "dsh"
10371
10802
  };
10372
- function buildSpawnPlan(cmd, cwd, gate, home = homedir8(), configDir, platform = process.platform) {
10803
+ function buildSpawnPlan(cmd, cwd, gate, home = homedir9(), configDir, platform = process.platform) {
10373
10804
  const file3 = TOOL_BINARY[cmd.tool];
10374
10805
  if (!file3) return null;
10375
10806
  switch (cmd.tool) {
@@ -10949,7 +11380,7 @@ var defaultCheckDeps = () => ({
10949
11380
  run: defaultProbeRun,
10950
11381
  env: process.env,
10951
11382
  childPath: () => childPath(),
10952
- home: homedir9(),
11383
+ home: homedir10(),
10953
11384
  codexConfig: () => {
10954
11385
  try {
10955
11386
  return readFileSync30(join35(codexHome(), "config.toml"), "utf8");
@@ -10961,7 +11392,7 @@ var defaultCheckDeps = () => ({
10961
11392
  workbuddyKind: () => locateWorkbuddy()?.kind ?? null,
10962
11393
  // 与设备任务唤起 App 同一个 locate(locateWorkbuddyAppExe:注册表卸载项 / App Paths / 开始菜单快捷方式…);App 自带 CLI 找得到也算
10963
11394
  workbuddyAppInstalled: () => locateWorkbuddyAppExe() !== null || locateWorkbuddyApp() !== null,
10964
- workbuddyAuthFile: () => workbuddyAuthFile(homedir9(), process.platform, process.env.LOCALAPPDATA ?? ""),
11395
+ workbuddyAuthFile: () => workbuddyAuthFile(homedir10(), process.platform, process.env.LOCALAPPDATA ?? ""),
10965
11396
  exists: (p) => existsSync27(p)
10966
11397
  });
10967
11398
  function daemonCheckDeps(base = defaultLocateDeps(), exeHintsAsync = winWorkbuddyLocatorHints) {
@@ -10984,7 +11415,7 @@ var SAFE_ARGS = /* @__PURE__ */ new Set(["auth", "login"]);
10984
11415
  var defaultCommandDeps = () => ({
10985
11416
  locate: (tool) => locate(tool),
10986
11417
  locateWorkbuddyTui: () => locateWorkbuddyTui(),
10987
- home: homedir9()
11418
+ home: homedir10()
10988
11419
  });
10989
11420
  var WORKBUDDY_LOGIN_HINT = "\uFF08\u5982\u679C\u5148\u5F39\u51FA\u4FE1\u4EFB\u6846\uFF0C\u9009 1 \u56DE\u8F66\uFF1B\u51FA\u73B0 Select login method \u65F6\u9009 Log in via Chinese Site \u56DE\u8F66\uFF0C\u5230\u6D4F\u89C8\u5668\u5B8C\u6210\u767B\u5F55\uFF1B\u82E5\u53EA\u770B\u5230\u8F93\u5165\u6846\uFF0C\u8F93\u5165 /login \u56DE\u8F66\uFF09";
10990
11421
  var WORKBUDDY_NEED_STANDALONE = "\u53EA\u88C5\u4E86 WorkBuddy App\uFF0C\u5B83\u81EA\u5E26\u7684\u547D\u4EE4\u884C\u7528\u4E0D\u4E86 App \u7684\u767B\u5F55\uFF1B\u8BBE\u5907\u4E0A\u65B0\u5EFA / \u56DE\u590D\u4EFB\u52A1\u9700\u8981\u72EC\u7ACB\u7248\u547D\u4EE4\u884C\uFF08\u8FD0\u884C deskbuddy workbuddy-setup \u5B89\u88C5\uFF09";
@@ -11110,7 +11541,7 @@ var defaultLoginOpenDeps = () => ({
11110
11541
  });
11111
11542
  async function installCodebuddyHooks(log) {
11112
11543
  try {
11113
- const { installTools: installTools2 } = await import("./enable-RN5EXNR3.js");
11544
+ const { installTools: installTools2 } = await import("./enable-XTCQ6IFF.js");
11114
11545
  installTools2(["workbuddy"], () => {
11115
11546
  }, null, { auto: true });
11116
11547
  } catch (e) {
@@ -11624,7 +12055,7 @@ function defaultOfferDeps(log) {
11624
12055
  }),
11625
12056
  openLogin: () => defaultOpenLogin(platform),
11626
12057
  installHooks: async () => {
11627
- const { installTools: installTools2 } = await import("./enable-RN5EXNR3.js");
12058
+ const { installTools: installTools2 } = await import("./enable-XTCQ6IFF.js");
11628
12059
  installTools2(["workbuddy"], (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`), null, { auto: true });
11629
12060
  },
11630
12061
  log: (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`),
@@ -11960,6 +12391,7 @@ export {
11960
12391
  detectTools,
11961
12392
  loadRegistry,
11962
12393
  refreshRegistry,
12394
+ titleSources,
11963
12395
  waitModeFor,
11964
12396
  shQuote,
11965
12397
  winCmdPath,
@@ -12000,8 +12432,10 @@ export {
12000
12432
  isAppSession,
12001
12433
  stateDir,
12002
12434
  readState,
12435
+ renameRetrying,
12003
12436
  writeState,
12004
12437
  retireState,
12438
+ removeState,
12005
12439
  STALE_NO_PID_LOG,
12006
12440
  readAliveStates,
12007
12441
  writeNotice,
@@ -12043,6 +12477,7 @@ export {
12043
12477
  isReportableDir,
12044
12478
  cleanTitle,
12045
12479
  qoderDbPaths,
12480
+ resolveTitle,
12046
12481
  findWbTranscript,
12047
12482
  wbTranscriptInfo,
12048
12483
  registerWorkbuddySession,