deskbuddy 0.2.0-beta.43 → 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.
- package/dist/{chunk-ET52BEIS.js → chunk-Y6D6FMMF.js} +536 -103
- package/dist/cli.js +1145 -242
- package/dist/{enable-AF2A67ZD.js → enable-XTCQ6IFF.js} +1 -1
- package/dist/hook.cjs +99 -0
- package/hook/hook-debug.log +24 -0
- package/hook/hook.cjs +99 -0
- package/package.json +1 -1
|
@@ -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:
|
|
2518
|
-
no:
|
|
2519
|
-
noInput:
|
|
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:
|
|
2846
|
+
dontAskPrefix: CC_TEXT.dontAskPrefix,
|
|
2522
2847
|
/** 编辑类 setMode acceptEdits:整句作 label(101 字,用户拍板不拆;不含快捷键提示 `(shift+tab)`) */
|
|
2523
|
-
acceptEdits:
|
|
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:
|
|
2850
|
+
autoMode: CC_AUTO_LABEL,
|
|
2526
2851
|
/** 设备上给「切 auto 模式」的说明(问题表 272:它会把整个会话切过去,要写清楚) */
|
|
2527
|
-
autoModeDesc:
|
|
2528
|
-
/**
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
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
|
|
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
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
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
|
-
|
|
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",
|
|
@@ -3747,7 +4017,7 @@ function resolveAlias(sessionId, now = Date.now()) {
|
|
|
3747
4017
|
// src/wrap/shim.ts
|
|
3748
4018
|
import { execFile as execFile3, execFileSync as execFileSync7 } from "child_process";
|
|
3749
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";
|
|
3750
|
-
import { homedir as
|
|
4020
|
+
import { homedir as homedir4 } from "os";
|
|
3751
4021
|
import { delimiter, join as join15, resolve as resolve4 } from "path";
|
|
3752
4022
|
var SHIM_FALLBACK_EXIT = 213;
|
|
3753
4023
|
var WIN_SHIM_ENABLED = true;
|
|
@@ -4073,7 +4343,7 @@ function psWinWhere(exec = execFileSync7) {
|
|
|
4073
4343
|
function defaultShimEnv(nodePath, cliPath) {
|
|
4074
4344
|
const env = process.env;
|
|
4075
4345
|
const isolated = !!env.VITEST || !!env.DESKBUDDY_HOME;
|
|
4076
|
-
const rcHome = env.DESKBUDDY_RC_HOME || (isolated ? null :
|
|
4346
|
+
const rcHome = env.DESKBUDDY_RC_HOME || (isolated ? null : homedir4());
|
|
4077
4347
|
return {
|
|
4078
4348
|
platform: process.platform,
|
|
4079
4349
|
binDir: shimDir(),
|
|
@@ -4497,11 +4767,11 @@ function launchPending(runId) {
|
|
|
4497
4767
|
// src/core/title.ts
|
|
4498
4768
|
import { closeSync as closeSync3, existsSync as existsSync15, openSync as openSync3, readFileSync as readFileSync20, readSync as readSync3, statSync as statSync7 } from "fs";
|
|
4499
4769
|
import { createRequire as createRequire2 } from "module";
|
|
4500
|
-
import { homedir as
|
|
4770
|
+
import { homedir as homedir6 } from "os";
|
|
4501
4771
|
import { basename as basename3, dirname as dirname10, isAbsolute, join as join18 } from "path";
|
|
4502
4772
|
|
|
4503
4773
|
// src/core/dirFilter.ts
|
|
4504
|
-
import { homedir as
|
|
4774
|
+
import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
|
|
4505
4775
|
import { posix, win32 as win323 } from "path";
|
|
4506
4776
|
function norm(p, win) {
|
|
4507
4777
|
if (win) return win323.resolve(p).replace(/[\\/]+$/, "").toLowerCase();
|
|
@@ -4514,7 +4784,7 @@ function isAutoWorkspace(p, opts = {}) {
|
|
|
4514
4784
|
if (!p) return false;
|
|
4515
4785
|
const win = (opts.platform ?? process.platform) === "win32";
|
|
4516
4786
|
const P = win ? win323 : posix;
|
|
4517
|
-
const home = norm(opts.home ??
|
|
4787
|
+
const home = norm(opts.home ?? homedir5(), win);
|
|
4518
4788
|
const full = norm(p, win);
|
|
4519
4789
|
const name = P.basename(full);
|
|
4520
4790
|
const parent = P.dirname(full);
|
|
@@ -4527,7 +4797,7 @@ function isReportableDir(p, opts = {}) {
|
|
|
4527
4797
|
const P = win ? win323 : posix;
|
|
4528
4798
|
const full = norm(p, win);
|
|
4529
4799
|
if (full === norm(P.parse(P.resolve(p)).root, win)) return false;
|
|
4530
|
-
if (full === norm(opts.home ??
|
|
4800
|
+
if (full === norm(opts.home ?? homedir5(), win)) return false;
|
|
4531
4801
|
const tmps = [opts.tmp ?? tmpdir2()];
|
|
4532
4802
|
if (!win) tmps.push("/tmp", "/private/tmp", "/var/folders", "/private/var/folders");
|
|
4533
4803
|
for (const t of tmps) if (t && isUnder(full, norm(t, win), win)) return false;
|
|
@@ -4536,6 +4806,7 @@ function isReportableDir(p, opts = {}) {
|
|
|
4536
4806
|
|
|
4537
4807
|
// src/core/title.ts
|
|
4538
4808
|
var TRANSCRIPT_TAIL_BYTES = 64 * 1024;
|
|
4809
|
+
var GIT_LOOKUP_MAX_UP = 5;
|
|
4539
4810
|
var SESSION_TITLE_MAX = 100;
|
|
4540
4811
|
function cleanTitle(text) {
|
|
4541
4812
|
if (typeof text !== "string") return "";
|
|
@@ -4543,9 +4814,21 @@ function cleanTitle(text) {
|
|
|
4543
4814
|
return Array.from(flat).slice(0, SESSION_TITLE_MAX).join("").trimEnd();
|
|
4544
4815
|
}
|
|
4545
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
|
+
}
|
|
4546
4829
|
var QODER_DATA_DIRS = { qoder_cn: "com.qodercn.app.stable", qoder: "com.qoder.app.stable" };
|
|
4547
4830
|
var QODER_APP_DIR_ENV = { qoder_cn: "DESKBUDDY_QODER_CN_APP_DIR", qoder: "DESKBUDDY_QODER_APP_DIR" };
|
|
4548
|
-
function qoderDbPaths(tool, platform = process.platform, home =
|
|
4831
|
+
function qoderDbPaths(tool, platform = process.platform, home = homedir6(), env = process.env) {
|
|
4549
4832
|
let root;
|
|
4550
4833
|
if (platform === "darwin") root = join18(home, "Library", "Application Support");
|
|
4551
4834
|
else if (platform === "win32") root = env.APPDATA || join18(home, "AppData", "Roaming");
|
|
@@ -4554,11 +4837,157 @@ function qoderDbPaths(tool, platform = process.platform, home = homedir5(), env
|
|
|
4554
4837
|
const tools = one ? [tool] : ["qoder_cn", "qoder"];
|
|
4555
4838
|
return tools.map((t) => join18(env[QODER_APP_DIR_ENV[t] ?? ""] || join18(root, QODER_DATA_DIRS[t] ?? ""), "main.sqlite"));
|
|
4556
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
|
+
}
|
|
4557
4986
|
|
|
4558
4987
|
// src/daemon/locate.ts
|
|
4559
4988
|
import { execFile as execFile4, execFileSync as execFileSync9 } from "child_process";
|
|
4560
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";
|
|
4561
|
-
import { homedir as
|
|
4990
|
+
import { homedir as homedir7 } from "os";
|
|
4562
4991
|
import { delimiter as delimiter2, dirname as dirname11, join as join20, win32 as win325 } from "path";
|
|
4563
4992
|
|
|
4564
4993
|
// src/core/agentEnv.ts
|
|
@@ -4856,7 +5285,7 @@ function qoderWorker(d, worker, appBin) {
|
|
|
4856
5285
|
return { file: d.nodePath, prefixArgs: [worker] };
|
|
4857
5286
|
}
|
|
4858
5287
|
var defaultLocateDeps = () => ({
|
|
4859
|
-
home:
|
|
5288
|
+
home: homedir7(),
|
|
4860
5289
|
path: process.env.PATH ?? "",
|
|
4861
5290
|
nodePath: process.execPath,
|
|
4862
5291
|
isExecutable: (p) => {
|
|
@@ -5540,7 +5969,7 @@ function markElectronBroken(v = true) {
|
|
|
5540
5969
|
electronBroken = v;
|
|
5541
5970
|
}
|
|
5542
5971
|
var requireHere2 = createRequire3(import.meta.url);
|
|
5543
|
-
function
|
|
5972
|
+
function loadNodeSqlite2() {
|
|
5544
5973
|
const orig = process.emitWarning;
|
|
5545
5974
|
process.emitWarning = function(...args) {
|
|
5546
5975
|
if (String(args[0] ?? "").includes("SQLite")) return;
|
|
@@ -5626,7 +6055,7 @@ function savePendingFile(jobs) {
|
|
|
5626
6055
|
var defaultWbAppDbDeps = () => ({
|
|
5627
6056
|
dbPath: () => workbuddyDbPath(),
|
|
5628
6057
|
configDir: () => dirname12(workbuddySettingsPath()),
|
|
5629
|
-
loadSqlite:
|
|
6058
|
+
loadSqlite: loadNodeSqlite2,
|
|
5630
6059
|
electron: () => defaultWbElectron(),
|
|
5631
6060
|
runElectron: (bin, script, input) => new Promise((resolve7, reject) => {
|
|
5632
6061
|
const env = { ...process.env, ELECTRON_RUN_AS_NODE: "1" };
|
|
@@ -8535,7 +8964,7 @@ function runHook(node, input, agentPid, tool = "dumate") {
|
|
|
8535
8964
|
|
|
8536
8965
|
// src/daemon/qoderApp/keeper.ts
|
|
8537
8966
|
import { existsSync as existsSync20, mkdirSync as mkdirSync22, readdirSync as readdirSync12, readFileSync as readFileSync25, renameSync as renameSync16, writeFileSync as writeFileSync22 } from "fs";
|
|
8538
|
-
import { homedir as
|
|
8967
|
+
import { homedir as homedir8 } from "os";
|
|
8539
8968
|
import { join as join24, win32 as win327 } from "path";
|
|
8540
8969
|
var WAKE_LAUNCH_GAP_MS2 = 60 * 1e3;
|
|
8541
8970
|
var RESTART_GAP_MS2 = 30 * 60 * 1e3;
|
|
@@ -8836,7 +9265,7 @@ var QoderCdpKeeper = class {
|
|
|
8836
9265
|
}
|
|
8837
9266
|
if (this.#platform() !== "darwin") return null;
|
|
8838
9267
|
const name = this.#d.brand.macApp.split("/").pop() ?? "";
|
|
8839
|
-
for (const p of [this.#d.brand.macApp, join24(
|
|
9268
|
+
for (const p of [this.#d.brand.macApp, join24(homedir8(), "Applications", name)]) if (existsSync20(p)) return p;
|
|
8840
9269
|
return null;
|
|
8841
9270
|
}
|
|
8842
9271
|
/** 一轮:健康检查 → 活着核对属主。🔴 平时不重启 App;只有端口无响应满 HUNG_RESTART_MS 时按节流与在场规则重启 */
|
|
@@ -10272,12 +10701,12 @@ function migrateLegacyQoder(out, detect = () => detectTools()) {
|
|
|
10272
10701
|
// src/daemon/loginGuide.ts
|
|
10273
10702
|
import { execFile as execFile9, execFileSync as execFileSync13 } from "child_process";
|
|
10274
10703
|
import { chmodSync as chmodSync9, existsSync as existsSync27, mkdirSync as mkdirSync26, readFileSync as readFileSync30, writeFileSync as writeFileSync26 } from "fs";
|
|
10275
|
-
import { homedir as
|
|
10704
|
+
import { homedir as homedir10 } from "os";
|
|
10276
10705
|
import { join as join35 } from "path";
|
|
10277
10706
|
|
|
10278
10707
|
// src/daemon/executor.ts
|
|
10279
10708
|
import { execFile as execFile7, execFileSync as execFileSync11 } from "child_process";
|
|
10280
|
-
import { homedir as
|
|
10709
|
+
import { homedir as homedir9 } from "os";
|
|
10281
10710
|
import { join as join33 } from "path";
|
|
10282
10711
|
|
|
10283
10712
|
// src/core/codebuddyDirs.ts
|
|
@@ -10371,7 +10800,7 @@ var TOOL_BINARY = {
|
|
|
10371
10800
|
opencode: "opencode",
|
|
10372
10801
|
deepseek_harness: "dsh"
|
|
10373
10802
|
};
|
|
10374
|
-
function buildSpawnPlan(cmd, cwd, gate, home =
|
|
10803
|
+
function buildSpawnPlan(cmd, cwd, gate, home = homedir9(), configDir, platform = process.platform) {
|
|
10375
10804
|
const file3 = TOOL_BINARY[cmd.tool];
|
|
10376
10805
|
if (!file3) return null;
|
|
10377
10806
|
switch (cmd.tool) {
|
|
@@ -10951,7 +11380,7 @@ var defaultCheckDeps = () => ({
|
|
|
10951
11380
|
run: defaultProbeRun,
|
|
10952
11381
|
env: process.env,
|
|
10953
11382
|
childPath: () => childPath(),
|
|
10954
|
-
home:
|
|
11383
|
+
home: homedir10(),
|
|
10955
11384
|
codexConfig: () => {
|
|
10956
11385
|
try {
|
|
10957
11386
|
return readFileSync30(join35(codexHome(), "config.toml"), "utf8");
|
|
@@ -10963,7 +11392,7 @@ var defaultCheckDeps = () => ({
|
|
|
10963
11392
|
workbuddyKind: () => locateWorkbuddy()?.kind ?? null,
|
|
10964
11393
|
// 与设备任务唤起 App 同一个 locate(locateWorkbuddyAppExe:注册表卸载项 / App Paths / 开始菜单快捷方式…);App 自带 CLI 找得到也算
|
|
10965
11394
|
workbuddyAppInstalled: () => locateWorkbuddyAppExe() !== null || locateWorkbuddyApp() !== null,
|
|
10966
|
-
workbuddyAuthFile: () => workbuddyAuthFile(
|
|
11395
|
+
workbuddyAuthFile: () => workbuddyAuthFile(homedir10(), process.platform, process.env.LOCALAPPDATA ?? ""),
|
|
10967
11396
|
exists: (p) => existsSync27(p)
|
|
10968
11397
|
});
|
|
10969
11398
|
function daemonCheckDeps(base = defaultLocateDeps(), exeHintsAsync = winWorkbuddyLocatorHints) {
|
|
@@ -10986,7 +11415,7 @@ var SAFE_ARGS = /* @__PURE__ */ new Set(["auth", "login"]);
|
|
|
10986
11415
|
var defaultCommandDeps = () => ({
|
|
10987
11416
|
locate: (tool) => locate(tool),
|
|
10988
11417
|
locateWorkbuddyTui: () => locateWorkbuddyTui(),
|
|
10989
|
-
home:
|
|
11418
|
+
home: homedir10()
|
|
10990
11419
|
});
|
|
10991
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";
|
|
10992
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";
|
|
@@ -11112,7 +11541,7 @@ var defaultLoginOpenDeps = () => ({
|
|
|
11112
11541
|
});
|
|
11113
11542
|
async function installCodebuddyHooks(log) {
|
|
11114
11543
|
try {
|
|
11115
|
-
const { installTools: installTools2 } = await import("./enable-
|
|
11544
|
+
const { installTools: installTools2 } = await import("./enable-XTCQ6IFF.js");
|
|
11116
11545
|
installTools2(["workbuddy"], () => {
|
|
11117
11546
|
}, null, { auto: true });
|
|
11118
11547
|
} catch (e) {
|
|
@@ -11626,7 +12055,7 @@ function defaultOfferDeps(log) {
|
|
|
11626
12055
|
}),
|
|
11627
12056
|
openLogin: () => defaultOpenLogin(platform),
|
|
11628
12057
|
installHooks: async () => {
|
|
11629
|
-
const { installTools: installTools2 } = await import("./enable-
|
|
12058
|
+
const { installTools: installTools2 } = await import("./enable-XTCQ6IFF.js");
|
|
11630
12059
|
installTools2(["workbuddy"], (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`), null, { auto: true });
|
|
11631
12060
|
},
|
|
11632
12061
|
log: (l) => log(`[WorkBuddy \u72EC\u7ACB\u7248] ${l}`),
|
|
@@ -11962,6 +12391,7 @@ export {
|
|
|
11962
12391
|
detectTools,
|
|
11963
12392
|
loadRegistry,
|
|
11964
12393
|
refreshRegistry,
|
|
12394
|
+
titleSources,
|
|
11965
12395
|
waitModeFor,
|
|
11966
12396
|
shQuote,
|
|
11967
12397
|
winCmdPath,
|
|
@@ -12002,8 +12432,10 @@ export {
|
|
|
12002
12432
|
isAppSession,
|
|
12003
12433
|
stateDir,
|
|
12004
12434
|
readState,
|
|
12435
|
+
renameRetrying,
|
|
12005
12436
|
writeState,
|
|
12006
12437
|
retireState,
|
|
12438
|
+
removeState,
|
|
12007
12439
|
STALE_NO_PID_LOG,
|
|
12008
12440
|
readAliveStates,
|
|
12009
12441
|
writeNotice,
|
|
@@ -12045,6 +12477,7 @@ export {
|
|
|
12045
12477
|
isReportableDir,
|
|
12046
12478
|
cleanTitle,
|
|
12047
12479
|
qoderDbPaths,
|
|
12480
|
+
resolveTitle,
|
|
12048
12481
|
findWbTranscript,
|
|
12049
12482
|
wbTranscriptInfo,
|
|
12050
12483
|
registerWorkbuddySession,
|