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.
- package/dist/{chunk-RDJUEATP.js → chunk-Y6D6FMMF.js} +538 -103
- package/dist/cli.js +1145 -242
- package/dist/{enable-RN5EXNR3.js → enable-XTCQ6IFF.js} +1 -1
- package/dist/hook.cjs +99 -0
- package/hook/hook-debug.log +36 -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",
|
|
@@ -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
|
|
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 :
|
|
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
|
|
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
|
|
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 ??
|
|
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 ??
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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(
|
|
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:
|
|
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-
|
|
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-
|
|
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,
|