@cosmicstack/mercury-agent 1.2.3 → 1.2.5
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/index.js +1440 -897
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/patches/ink+5.2.1.patch +31 -0
package/dist/index.js
CHANGED
|
@@ -2409,6 +2409,525 @@ var init_github_copilot = __esm({
|
|
|
2409
2409
|
}
|
|
2410
2410
|
});
|
|
2411
2411
|
|
|
2412
|
+
// src/utils/highlight.ts
|
|
2413
|
+
import chalk from "chalk";
|
|
2414
|
+
function esc(s) {
|
|
2415
|
+
return s.replace(CONTROL, "");
|
|
2416
|
+
}
|
|
2417
|
+
function highlightLine(line, lang, opts) {
|
|
2418
|
+
if (opts?.uniform) {
|
|
2419
|
+
return opts.uniform === "red" ? chalk.red(line) : chalk.green(line);
|
|
2420
|
+
}
|
|
2421
|
+
try {
|
|
2422
|
+
const ruleset = RULESETS[ALIAS[lang?.toLowerCase() ?? ""] ?? lang?.toLowerCase() ?? ""] ?? C_FAMILY;
|
|
2423
|
+
let rest = esc(line);
|
|
2424
|
+
let out2 = "";
|
|
2425
|
+
let guard = 0;
|
|
2426
|
+
while (rest.length > 0 && guard++ < 400) {
|
|
2427
|
+
let matched = false;
|
|
2428
|
+
for (const rule of ruleset) {
|
|
2429
|
+
const m = rule.re.exec(rest);
|
|
2430
|
+
if (m && m[0].length > 0) {
|
|
2431
|
+
out2 += rule.color(m[0]);
|
|
2432
|
+
rest = rest.slice(m[0].length);
|
|
2433
|
+
matched = true;
|
|
2434
|
+
break;
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
if (!matched) {
|
|
2438
|
+
out2 += rest[0];
|
|
2439
|
+
rest = rest.slice(1);
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
if (rest.length > 0) out2 += rest;
|
|
2443
|
+
return out2;
|
|
2444
|
+
} catch {
|
|
2445
|
+
return line;
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
function highlightDiffLine(line) {
|
|
2449
|
+
if (line.startsWith("+++") || line.startsWith("---")) return chalk.blue(line);
|
|
2450
|
+
if (line.startsWith("+++") || line.startsWith("---")) return chalk.blue(line);
|
|
2451
|
+
if (line.startsWith("diff ")) return chalk.bold.blue(line);
|
|
2452
|
+
if (line.startsWith("@@")) return chalk.cyan(line);
|
|
2453
|
+
if (line.startsWith("+")) return chalk.green(line);
|
|
2454
|
+
if (line.startsWith("-")) return chalk.red(line);
|
|
2455
|
+
return line;
|
|
2456
|
+
}
|
|
2457
|
+
function highlightCodeBlock(body, lang) {
|
|
2458
|
+
const trimmedLang = (lang || "").trim().toLowerCase();
|
|
2459
|
+
if (trimmedLang === "diff" || trimmedLang === "patch" || /^(diff --git|--- a\/|\+\+\+ b\/)/m.test(body)) {
|
|
2460
|
+
return body.split("\n").map(highlightDiffLine);
|
|
2461
|
+
}
|
|
2462
|
+
return body.split("\n").map((l) => highlightLine(l, trimmedLang));
|
|
2463
|
+
}
|
|
2464
|
+
var CONTROL, C_FAMILY, PY_RULES, SHELL_RULES, JSON_RULES, CSS_RULES, GO_RULES, RUST_RULES, RULESETS, ALIAS;
|
|
2465
|
+
var init_highlight = __esm({
|
|
2466
|
+
"src/utils/highlight.ts"() {
|
|
2467
|
+
"use strict";
|
|
2468
|
+
CONTROL = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
|
|
2469
|
+
C_FAMILY = [
|
|
2470
|
+
{ re: /^\/\/[^\n]*/, color: (s) => chalk.gray(s) },
|
|
2471
|
+
{ re: /^\/\*[\s\S]*?\*\//, color: (s) => chalk.gray(s) },
|
|
2472
|
+
{ re: /^`(?:\\.|[^`\\])*`?/, color: (s) => chalk.green(s) },
|
|
2473
|
+
{ re: /^'(?:\\.|[^'\\\n])*'?/, color: (s) => chalk.green(s) },
|
|
2474
|
+
{ re: /^"(?:\\.|[^"\\\n])*"?/, color: (s) => chalk.green(s) },
|
|
2475
|
+
{ re: /^-?\d[\d_]*(?:\.\d+)?(?:[eE][+-]?\d+)?/, color: (s) => chalk.magenta(s) },
|
|
2476
|
+
{ re: /^(?:abstract|as|break|case|catch|class|const|continue|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|namespace|new|of|private|protected|public|readonly|return|satisfies|set|static|super|switch|this|throw|try|type|typeof|var|void|while|yield|async|await)\b/, color: (s) => chalk.yellow(s) },
|
|
2477
|
+
{ re: /^(?:true|false|null|unique|undefined|NaN|Infinity)\b/, color: (s) => chalk.blue(s) },
|
|
2478
|
+
{ re: /^[A-Za-z_$][\w$]*(?=\s*\()/, color: (s) => chalk.cyan(s) },
|
|
2479
|
+
{ re: /^[A-Z][\w$]*/, color: (s) => chalk.blue(s) }
|
|
2480
|
+
];
|
|
2481
|
+
PY_RULES = [
|
|
2482
|
+
{ re: /^#[^\n]*/, color: (s) => chalk.gray(s) },
|
|
2483
|
+
{ re: /^"""[\s\S]*?("""|$)/, color: (s) => chalk.gray(s) },
|
|
2484
|
+
{ re: /^'''[\s\S]*?('''|$)/, color: (s) => chalk.gray(s) },
|
|
2485
|
+
{ re: /^f?"(?:\\.|[^"\\\n])*"?/, color: (s) => chalk.green(s) },
|
|
2486
|
+
{ re: /^f?'(?:\\.|[^'\\\n])*'?/, color: (s) => chalk.green(s) },
|
|
2487
|
+
{ re: /^\d[\d_]*(?:\.\d+)?/, color: (s) => chalk.magenta(s) },
|
|
2488
|
+
{ re: /^(?:def|class|import|from|return|if|elif|else|for|while|try|except|finally|with|as|lambda|yield|raise|pass|break|continue|global|nonlocal|assert|async|await|not|and|or|in|is|del)\b/, color: (s) => chalk.yellow(s) },
|
|
2489
|
+
{ re: /^(?:True|False|None|self|cls)\b/, color: (s) => chalk.blue(s) },
|
|
2490
|
+
{ re: /^[A-Za-z_]\w*(?=\s*\()/, color: (s) => chalk.cyan(s) },
|
|
2491
|
+
{ re: /^[A-Z][\w]*/, color: (s) => chalk.blue(s) },
|
|
2492
|
+
{ re: /^@\w[\w.]*/, color: (s) => chalk.green(s) }
|
|
2493
|
+
];
|
|
2494
|
+
SHELL_RULES = [
|
|
2495
|
+
{ re: /^#[^\n]*/, color: (s) => chalk.gray(s) },
|
|
2496
|
+
{ re: /^(?:if|then|else|elif|fi|for|while|do|done|case|esac|function|return|export|local|source|set|unset|cd|exit)\b/, color: (s) => chalk.yellow(s) },
|
|
2497
|
+
{ re: /^\$\{[^}]*\}?\$?/, color: (s) => chalk.magenta(s) },
|
|
2498
|
+
{ re: /^\$\w*/, color: (s) => chalk.magenta(s) },
|
|
2499
|
+
{ re: /^"(?:\\.|[^"\\])*"?/, color: (s) => chalk.green(s) },
|
|
2500
|
+
{ re: /^'(?:[^'\\])*'?/, color: (s) => chalk.green(s) },
|
|
2501
|
+
{ re: /^\d+/, color: (s) => chalk.magenta(s) },
|
|
2502
|
+
{ re: /^(?:npm|pnpm|yarn|node|npx|git|curl|wget|python|python3|pip|cargo|go|make|brew|ls|cat|echo|mkdir|rm|mv|cp|cd|chmod|docker|kubectl)\b/, color: (s) => chalk.cyan(s) }
|
|
2503
|
+
];
|
|
2504
|
+
JSON_RULES = [
|
|
2505
|
+
{ re: /^"(?:\\.|[^"\\])*"(?=\s*:)/, color: (s) => chalk.blue(s) },
|
|
2506
|
+
{ re: /^"(?:\\.|[^"\\])*"?/, color: (s) => chalk.green(s) },
|
|
2507
|
+
{ re: /^-?\d[\d_]*(?:\.\d+)?(?:[eE][+-]?\d+)?/, color: (s) => chalk.magenta(s) },
|
|
2508
|
+
{ re: /^(?:true|false|null)\b/, color: (s) => chalk.blue(s) }
|
|
2509
|
+
];
|
|
2510
|
+
CSS_RULES = [
|
|
2511
|
+
{ re: /^\/\*[\s\S]*?\*\//, color: (s) => chalk.gray(s) },
|
|
2512
|
+
{ re: /^@[\w-]+/, color: (s) => chalk.yellow(s) },
|
|
2513
|
+
{ re: /^[.#]?[\w-]+(?=\s*\{)/, color: (s) => chalk.cyan(s) },
|
|
2514
|
+
{ re: /^[\w-]+(?=\s*:)/, color: (s) => chalk.blue(s) },
|
|
2515
|
+
{ re: /^"(?:\\.|[^"\\\n])*"?/, color: (s) => chalk.green(s) },
|
|
2516
|
+
{ re: /^'(?:[^'\\\n])*'?/, color: (s) => chalk.green(s) },
|
|
2517
|
+
{ re: /^-?\d[\d.]*(?:px|em|rem|%|vh|vw|s|ms|fr)?/, color: (s) => chalk.magenta(s) }
|
|
2518
|
+
];
|
|
2519
|
+
GO_RULES = [
|
|
2520
|
+
{ re: /^\/\/[^\n]*/, color: (s) => chalk.gray(s) },
|
|
2521
|
+
{ re: /^\/\*[\s\S]*?\*\//, color: (s) => chalk.gray(s) },
|
|
2522
|
+
{ re: /^"(?:\\.|[^"\\\n])*"?/, color: (s) => chalk.green(s) },
|
|
2523
|
+
{ re: /^`(?:\\.|[^`\\])*`?/, color: (s) => chalk.green(s) },
|
|
2524
|
+
{ re: /^\d[\d_]*(?:\.\d+)?/, color: (s) => chalk.magenta(s) },
|
|
2525
|
+
{ re: /^(?:package|import|func|return|if|else|for|range|switch|case|default|type|struct|interface|map|chan|go|defer|var|const|select|break|continue|fallthrough)\b/, color: (s) => chalk.yellow(s) },
|
|
2526
|
+
{ re: /^[A-Za-z_]\w*(?=\s*\()/, color: (s) => chalk.cyan(s) },
|
|
2527
|
+
{ re: /^[A-Z][\w]*/, color: (s) => chalk.blue(s) }
|
|
2528
|
+
];
|
|
2529
|
+
RUST_RULES = [
|
|
2530
|
+
{ re: /^\/\/[^\n]*/, color: (s) => chalk.gray(s) },
|
|
2531
|
+
{ re: /^\/\*[\s\S]*?\*\//, color: (s) => chalk.gray(s) },
|
|
2532
|
+
{ re: /^"(?:\\.|[^"\\\n])*"?/, color: (s) => chalk.green(s) },
|
|
2533
|
+
{ re: /^\d[\d_]*(?:\.\d+)?(?:[eE][+-]?\d+)?/, color: (s) => chalk.magenta(s) },
|
|
2534
|
+
{ re: /^(?:as|break|const|continue|crate|dyn|else|enum|extern|false|fn|for|if|impl|in|let|loop|match|mod|move|mut|pub|ref|return|self|Self|static|struct|super|trait|true|type|unsafe|use|where|while|async|await)\b/, color: (s) => chalk.yellow(s) },
|
|
2535
|
+
{ re: /^&['\u2019]?\w*\b/, color: (s) => chalk.cyan(s) },
|
|
2536
|
+
{ re: /^\w+!/, color: (s) => chalk.cyan(s) },
|
|
2537
|
+
{ re: /^[A-Za-z_]\w*(?=\s*[<(])/, color: (s) => chalk.cyan(s) },
|
|
2538
|
+
{ re: /^[A-Z][\w]*/, color: (s) => chalk.blue(s) }
|
|
2539
|
+
];
|
|
2540
|
+
RULESETS = {
|
|
2541
|
+
javascript: C_FAMILY,
|
|
2542
|
+
json: JSON_RULES,
|
|
2543
|
+
python: PY_RULES,
|
|
2544
|
+
shell: SHELL_RULES,
|
|
2545
|
+
css: CSS_RULES,
|
|
2546
|
+
go: GO_RULES,
|
|
2547
|
+
rust: RUST_RULES
|
|
2548
|
+
};
|
|
2549
|
+
ALIAS = {
|
|
2550
|
+
js: "javascript",
|
|
2551
|
+
jsx: "javascript",
|
|
2552
|
+
mjs: "javascript",
|
|
2553
|
+
cjs: "javascript",
|
|
2554
|
+
ts: "javascript",
|
|
2555
|
+
tsx: "javascript",
|
|
2556
|
+
typescript: "javascript",
|
|
2557
|
+
py: "python",
|
|
2558
|
+
python3: "python",
|
|
2559
|
+
sh: "shell",
|
|
2560
|
+
bash: "shell",
|
|
2561
|
+
zsh: "shell",
|
|
2562
|
+
console: "shell",
|
|
2563
|
+
shellscript: "shell",
|
|
2564
|
+
golang: "go",
|
|
2565
|
+
rs: "rust",
|
|
2566
|
+
jsonc: "json",
|
|
2567
|
+
json5: "json",
|
|
2568
|
+
less: "css",
|
|
2569
|
+
scss: "css",
|
|
2570
|
+
sass: "css",
|
|
2571
|
+
html: "css",
|
|
2572
|
+
xml: "css",
|
|
2573
|
+
vue: "css",
|
|
2574
|
+
svelte: "css",
|
|
2575
|
+
yaml: "json",
|
|
2576
|
+
yml: "json",
|
|
2577
|
+
toml: "json",
|
|
2578
|
+
ini: "json"
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
});
|
|
2582
|
+
|
|
2583
|
+
// src/utils/markdown.ts
|
|
2584
|
+
import { Marked } from "marked";
|
|
2585
|
+
import chalk2 from "chalk";
|
|
2586
|
+
function decodeHtmlEntities(text) {
|
|
2587
|
+
return text.replace(/&(?:#[xX]?[0-9a-fA-F]+|[a-zA-Z]+);/g, (match) => {
|
|
2588
|
+
if (HTML_ENTITIES[match]) return HTML_ENTITIES[match];
|
|
2589
|
+
if (match.startsWith("&#x") || match.startsWith("&#X")) {
|
|
2590
|
+
const code = parseInt(match.slice(3, -1), 16);
|
|
2591
|
+
return isNaN(code) ? match : String.fromCodePoint(code);
|
|
2592
|
+
}
|
|
2593
|
+
if (match.startsWith("&#")) {
|
|
2594
|
+
const code = parseInt(match.slice(2, -1), 10);
|
|
2595
|
+
return isNaN(code) ? match : String.fromCodePoint(code);
|
|
2596
|
+
}
|
|
2597
|
+
return match;
|
|
2598
|
+
});
|
|
2599
|
+
}
|
|
2600
|
+
function renderMarkdown(text) {
|
|
2601
|
+
try {
|
|
2602
|
+
const tokens = lexer.lexer(text);
|
|
2603
|
+
const result = renderTokens(tokens);
|
|
2604
|
+
return decodeHtmlEntities(result.replace(/\n{3,}/g, "\n\n").trimEnd());
|
|
2605
|
+
} catch {
|
|
2606
|
+
return decodeHtmlEntities(text);
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
function renderTokens(tokens) {
|
|
2610
|
+
return tokens.map((t) => renderToken(t)).join("");
|
|
2611
|
+
}
|
|
2612
|
+
function renderToken(t) {
|
|
2613
|
+
if (!t || typeof t !== "object") return String(t ?? "");
|
|
2614
|
+
switch (t.type) {
|
|
2615
|
+
case "heading":
|
|
2616
|
+
return renderHeading(t);
|
|
2617
|
+
case "paragraph":
|
|
2618
|
+
return renderInline(t.tokens) + "\n\n";
|
|
2619
|
+
case "strong":
|
|
2620
|
+
return chalk2.bold(renderInline(t.tokens));
|
|
2621
|
+
case "em":
|
|
2622
|
+
return chalk2.italic(renderInline(t.tokens));
|
|
2623
|
+
case "del":
|
|
2624
|
+
return chalk2.dim.strikethrough(renderInline(t.tokens));
|
|
2625
|
+
case "codespan":
|
|
2626
|
+
return chalk2.yellow(t.text);
|
|
2627
|
+
case "code":
|
|
2628
|
+
return renderCodeBlock(t);
|
|
2629
|
+
case "list":
|
|
2630
|
+
return renderList(t);
|
|
2631
|
+
case "blockquote":
|
|
2632
|
+
return renderBlockquote(t);
|
|
2633
|
+
case "hr":
|
|
2634
|
+
return chalk2.dim("\u2500".repeat(50)) + "\n\n";
|
|
2635
|
+
case "link":
|
|
2636
|
+
return `${chalk2.blue.underline(renderInline(t.tokens))} ${chalk2.dim(`(${t.href})`)}`;
|
|
2637
|
+
case "image":
|
|
2638
|
+
return chalk2.blue(`\u{1F5BC} ${t.title || t.href}`);
|
|
2639
|
+
case "table":
|
|
2640
|
+
return renderTable(t);
|
|
2641
|
+
case "text":
|
|
2642
|
+
if (t.tokens) return renderInline(t.tokens);
|
|
2643
|
+
return t.text || "";
|
|
2644
|
+
case "html":
|
|
2645
|
+
return t.text || "";
|
|
2646
|
+
case "space":
|
|
2647
|
+
return "";
|
|
2648
|
+
default:
|
|
2649
|
+
return t.text || "";
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
function renderHeading(t) {
|
|
2653
|
+
const text = renderInline(t.tokens);
|
|
2654
|
+
if (t.depth === 1) return `
|
|
2655
|
+
${chalk2.bold.cyan(text)}
|
|
2656
|
+
|
|
2657
|
+
`;
|
|
2658
|
+
if (t.depth === 2) return `
|
|
2659
|
+
${chalk2.bold.cyan(` \u25A0 ${text}`)}
|
|
2660
|
+
|
|
2661
|
+
`;
|
|
2662
|
+
return `
|
|
2663
|
+
${chalk2.bold(` \u25A0 ${text}`)}
|
|
2664
|
+
|
|
2665
|
+
`;
|
|
2666
|
+
}
|
|
2667
|
+
function renderInline(tokens) {
|
|
2668
|
+
if (!tokens) return "";
|
|
2669
|
+
return tokens.map((t) => {
|
|
2670
|
+
if (typeof t === "string") return t;
|
|
2671
|
+
if (t.type === "strong") return chalk2.bold(renderInline(t.tokens));
|
|
2672
|
+
if (t.type === "em") return chalk2.italic(renderInline(t.tokens));
|
|
2673
|
+
if (t.type === "del") return chalk2.dim.strikethrough(renderInline(t.tokens));
|
|
2674
|
+
if (t.type === "codespan") return chalk2.yellow(t.text);
|
|
2675
|
+
if (t.type === "link") return `${chalk2.blue.underline(renderInline(t.tokens))} ${chalk2.dim(`(${t.href})`)}`;
|
|
2676
|
+
if (t.type === "image") return chalk2.blue(`\u{1F5BC} ${t.title || t.href}`);
|
|
2677
|
+
if (t.type === "text") {
|
|
2678
|
+
return t.tokens ? renderInline(t.tokens) : t.text || "";
|
|
2679
|
+
}
|
|
2680
|
+
if (t.type === "html") return t.text || "";
|
|
2681
|
+
return t.text || "";
|
|
2682
|
+
}).join("");
|
|
2683
|
+
}
|
|
2684
|
+
function renderCodeBlock(t) {
|
|
2685
|
+
const lines = highlightCodeBlock(t.text ?? "", t.lang).map((l) => ` ${l}`).join("\n");
|
|
2686
|
+
const langStr = t.lang ? chalk2.dim(` [${t.lang}]`) : "";
|
|
2687
|
+
return `
|
|
2688
|
+
${langStr}
|
|
2689
|
+
${lines}
|
|
2690
|
+
|
|
2691
|
+
`;
|
|
2692
|
+
}
|
|
2693
|
+
function renderList(t) {
|
|
2694
|
+
const lines = [];
|
|
2695
|
+
const items = t.items || [];
|
|
2696
|
+
items.forEach((item, i) => {
|
|
2697
|
+
const bullet = t.ordered ? `${i + 1}.` : "\u2022";
|
|
2698
|
+
const firstLine = renderInline(item.tokens?.[0]?.tokens || [{ text: item.text }]);
|
|
2699
|
+
lines.push(` ${chalk2.dim(bullet)} ${firstLine}`);
|
|
2700
|
+
const restTokens = (item.tokens || []).slice(1);
|
|
2701
|
+
for (const sub of restTokens) {
|
|
2702
|
+
if (sub.type === "list") {
|
|
2703
|
+
const subLines = renderList(sub).split("\n").filter(Boolean).map((l) => ` ${l}`).join("\n");
|
|
2704
|
+
lines.push(subLines);
|
|
2705
|
+
} else if (sub.type === "text") {
|
|
2706
|
+
lines.push(` ${chalk2.dim("\u2022")} ${renderInline(sub.tokens)}`);
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
});
|
|
2710
|
+
return lines.join("\n") + "\n\n";
|
|
2711
|
+
}
|
|
2712
|
+
function renderBlockquote(t) {
|
|
2713
|
+
const content = renderTokens(t.tokens || []);
|
|
2714
|
+
const lines = content.split("\n").filter((l) => l.trim()).map((l) => `${chalk2.dim("\u2502 ")}${chalk2.gray(l)}`).join("\n");
|
|
2715
|
+
return `
|
|
2716
|
+
${lines}
|
|
2717
|
+
|
|
2718
|
+
`;
|
|
2719
|
+
}
|
|
2720
|
+
function renderTable(t) {
|
|
2721
|
+
const headers = (t.header || []).map((h) => chalk2.bold(renderInline(h.tokens)));
|
|
2722
|
+
const colWidths = (t.header || []).map((h, i) => {
|
|
2723
|
+
const hLen = (h.text || "").length;
|
|
2724
|
+
const rowLens = (t.rows || []).map((row) => {
|
|
2725
|
+
const cell = row[i];
|
|
2726
|
+
return cell?.text?.length ?? 0;
|
|
2727
|
+
});
|
|
2728
|
+
return Math.max(hLen, ...rowLens) + 2;
|
|
2729
|
+
});
|
|
2730
|
+
const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(chalk2.dim(" \u2502 "));
|
|
2731
|
+
const separator = colWidths.map((w) => "\u2500".repeat(w)).join(chalk2.dim("\u2500\u253C\u2500"));
|
|
2732
|
+
const dataLines = (t.rows || []).map(
|
|
2733
|
+
(row) => row.map((cell, i) => {
|
|
2734
|
+
const text = renderInline(cell.tokens) || cell.text || "";
|
|
2735
|
+
return text.padEnd(colWidths[i]);
|
|
2736
|
+
}).join(chalk2.dim(" \u2502 "))
|
|
2737
|
+
);
|
|
2738
|
+
return `
|
|
2739
|
+
${headerLine}
|
|
2740
|
+
${chalk2.dim(separator)}
|
|
2741
|
+
${dataLines.join("\n")}
|
|
2742
|
+
|
|
2743
|
+
`;
|
|
2744
|
+
}
|
|
2745
|
+
function escapeHtml(text) {
|
|
2746
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2747
|
+
}
|
|
2748
|
+
function mdToTelegram(text) {
|
|
2749
|
+
let out2 = text;
|
|
2750
|
+
const codeBlocks = [];
|
|
2751
|
+
out2 = out2.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
2752
|
+
const placeholder = `__CODEBLOCK_${codeBlocks.length}__`;
|
|
2753
|
+
codeBlocks.push(`<pre><code class="${lang}">${escapeHtml(code)}</code></pre>`);
|
|
2754
|
+
return placeholder;
|
|
2755
|
+
});
|
|
2756
|
+
const inlineCodes = [];
|
|
2757
|
+
out2 = out2.replace(/`([^`]+)`/g, (_match, code) => {
|
|
2758
|
+
const placeholder = `__INLINECODE_${inlineCodes.length}__`;
|
|
2759
|
+
inlineCodes.push(`<code>${escapeHtml(code)}</code>`);
|
|
2760
|
+
return placeholder;
|
|
2761
|
+
});
|
|
2762
|
+
const links = [];
|
|
2763
|
+
out2 = out2.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
|
|
2764
|
+
const placeholder = `__LINK_${links.length}__`;
|
|
2765
|
+
links.push(`<a href="${escapeHtml(href)}">${escapeHtml(label)}</a>`);
|
|
2766
|
+
return placeholder;
|
|
2767
|
+
});
|
|
2768
|
+
out2 = escapeHtml(out2);
|
|
2769
|
+
out2 = out2.replace(/^### (.+)$/gm, "<b><i>$1</i></b>");
|
|
2770
|
+
out2 = out2.replace(/^## (.+)$/gm, "<b>$1</b>");
|
|
2771
|
+
out2 = out2.replace(/^# (.+)$/gm, "<b>$1</b>");
|
|
2772
|
+
out2 = out2.replace(/\*\*([^*]+)\*\*/g, "<b>$1</b>");
|
|
2773
|
+
out2 = out2.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "<i>$1</i>");
|
|
2774
|
+
out2 = out2.replace(/~~([^~]+)~~/g, "<s>$1</s>");
|
|
2775
|
+
for (let i = 0; i < inlineCodes.length; i++) {
|
|
2776
|
+
out2 = out2.replace(`__INLINECODE_${i}__`, inlineCodes[i]);
|
|
2777
|
+
}
|
|
2778
|
+
for (let i = 0; i < codeBlocks.length; i++) {
|
|
2779
|
+
out2 = out2.replace(`__CODEBLOCK_${i}__`, codeBlocks[i]);
|
|
2780
|
+
}
|
|
2781
|
+
for (let i = 0; i < links.length; i++) {
|
|
2782
|
+
out2 = out2.replace(`__LINK_${i}__`, links[i]);
|
|
2783
|
+
}
|
|
2784
|
+
if (out2.length > 4096) {
|
|
2785
|
+
out2 = out2.slice(0, 4090) + "...";
|
|
2786
|
+
}
|
|
2787
|
+
return out2;
|
|
2788
|
+
}
|
|
2789
|
+
function mdToSignal(text) {
|
|
2790
|
+
let out2 = text;
|
|
2791
|
+
const codeBlocks = [];
|
|
2792
|
+
out2 = out2.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, _lang, code) => {
|
|
2793
|
+
const placeholder = `__CB_${codeBlocks.length}__`;
|
|
2794
|
+
codeBlocks.push(code.trim());
|
|
2795
|
+
return placeholder;
|
|
2796
|
+
});
|
|
2797
|
+
const inlineCodes = [];
|
|
2798
|
+
out2 = out2.replace(/`([^`]+)`/g, (_match, code) => {
|
|
2799
|
+
const placeholder = `__IC_${inlineCodes.length}__`;
|
|
2800
|
+
inlineCodes.push(code);
|
|
2801
|
+
return placeholder;
|
|
2802
|
+
});
|
|
2803
|
+
const links = [];
|
|
2804
|
+
out2 = out2.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
|
|
2805
|
+
const placeholder = `__LK_${links.length}__`;
|
|
2806
|
+
links.push(`${label} (${href})`);
|
|
2807
|
+
return placeholder;
|
|
2808
|
+
});
|
|
2809
|
+
out2 = out2.replace(/^### (.+)$/gm, "*$1*");
|
|
2810
|
+
out2 = out2.replace(/^## (.+)$/gm, "*$1*");
|
|
2811
|
+
out2 = out2.replace(/^# (.+)$/gm, "*$1*");
|
|
2812
|
+
out2 = out2.replace(/\*\*([^*]+)\*\*/g, "*$1*");
|
|
2813
|
+
out2 = out2.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "_$1_");
|
|
2814
|
+
out2 = out2.replace(/~~([^~]+)~~/g, "~$1~");
|
|
2815
|
+
for (let i = 0; i < inlineCodes.length; i++) {
|
|
2816
|
+
out2 = out2.replace(`__IC_${i}__`, inlineCodes[i]);
|
|
2817
|
+
}
|
|
2818
|
+
for (let i = 0; i < codeBlocks.length; i++) {
|
|
2819
|
+
out2 = out2.replace(`__CB_${i}__`, codeBlocks[i]);
|
|
2820
|
+
}
|
|
2821
|
+
for (let i = 0; i < links.length; i++) {
|
|
2822
|
+
out2 = out2.replace(`__LK_${i}__`, links[i]);
|
|
2823
|
+
}
|
|
2824
|
+
if (out2.length > 4e3) {
|
|
2825
|
+
out2 = out2.slice(0, 3990) + "...";
|
|
2826
|
+
}
|
|
2827
|
+
return out2;
|
|
2828
|
+
}
|
|
2829
|
+
function mdToDiscord(text) {
|
|
2830
|
+
let out2 = text;
|
|
2831
|
+
const codeBlocks = [];
|
|
2832
|
+
out2 = out2.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
2833
|
+
const placeholder = `__DCB_${codeBlocks.length}__`;
|
|
2834
|
+
codeBlocks.push(`\`\`\`${lang}
|
|
2835
|
+
${code.trim()}
|
|
2836
|
+
\`\`\``);
|
|
2837
|
+
return placeholder;
|
|
2838
|
+
});
|
|
2839
|
+
const inlineCodes = [];
|
|
2840
|
+
out2 = out2.replace(/`([^`]+)`/g, (_match, code) => {
|
|
2841
|
+
const placeholder = `__DIC_${inlineCodes.length}__`;
|
|
2842
|
+
inlineCodes.push(`\`${code}\``);
|
|
2843
|
+
return placeholder;
|
|
2844
|
+
});
|
|
2845
|
+
const links = [];
|
|
2846
|
+
out2 = out2.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
|
|
2847
|
+
const placeholder = `__DLK_${links.length}__`;
|
|
2848
|
+
links.push(`[${label}](${href})`);
|
|
2849
|
+
return placeholder;
|
|
2850
|
+
});
|
|
2851
|
+
out2 = out2.replace(/^### (.+)$/gm, "__$1__");
|
|
2852
|
+
out2 = out2.replace(/^## (.+)$/gm, "**$1**");
|
|
2853
|
+
out2 = out2.replace(/^# (.+)$/gm, "**$1**");
|
|
2854
|
+
out2 = out2.replace(/~~([^~]+)~~/g, "~~$1~~");
|
|
2855
|
+
for (let i = 0; i < inlineCodes.length; i++) {
|
|
2856
|
+
out2 = out2.replace(`__DIC_${i}__`, inlineCodes[i]);
|
|
2857
|
+
}
|
|
2858
|
+
for (let i = 0; i < codeBlocks.length; i++) {
|
|
2859
|
+
out2 = out2.replace(`__DCB_${i}__`, codeBlocks[i]);
|
|
2860
|
+
}
|
|
2861
|
+
for (let i = 0; i < links.length; i++) {
|
|
2862
|
+
out2 = out2.replace(`__DLK_${i}__`, links[i]);
|
|
2863
|
+
}
|
|
2864
|
+
if (out2.length > 2e3) {
|
|
2865
|
+
out2 = out2.slice(0, 1990) + "...";
|
|
2866
|
+
}
|
|
2867
|
+
return out2;
|
|
2868
|
+
}
|
|
2869
|
+
function mdToSlack(text) {
|
|
2870
|
+
let out2 = text;
|
|
2871
|
+
const codeBlocks = [];
|
|
2872
|
+
out2 = out2.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
2873
|
+
const placeholder = `__SCB_${codeBlocks.length}__`;
|
|
2874
|
+
codeBlocks.push(`\`\`\`${lang}
|
|
2875
|
+
${code.trim()}
|
|
2876
|
+
\`\`\``);
|
|
2877
|
+
return placeholder;
|
|
2878
|
+
});
|
|
2879
|
+
const inlineCodes = [];
|
|
2880
|
+
out2 = out2.replace(/`([^`]+)`/g, (_match, code) => {
|
|
2881
|
+
const placeholder = `__SIC_${inlineCodes.length}__`;
|
|
2882
|
+
inlineCodes.push(`\`${code}\``);
|
|
2883
|
+
return placeholder;
|
|
2884
|
+
});
|
|
2885
|
+
const links = [];
|
|
2886
|
+
out2 = out2.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
|
|
2887
|
+
const placeholder = `__SLK_${links.length}__`;
|
|
2888
|
+
links.push(`<${href}|${label}>`);
|
|
2889
|
+
return placeholder;
|
|
2890
|
+
});
|
|
2891
|
+
out2 = out2.replace(/^### (.+)$/gm, "*$1*");
|
|
2892
|
+
out2 = out2.replace(/^## (.+)$/gm, "*$1*");
|
|
2893
|
+
out2 = out2.replace(/^# (.+)$/gm, "*$1*");
|
|
2894
|
+
out2 = out2.replace(/\*\*([^*]+)\*\*/g, "*$1*");
|
|
2895
|
+
out2 = out2.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "_$1_");
|
|
2896
|
+
out2 = out2.replace(/~~([^~]+)~~/g, "~$1~");
|
|
2897
|
+
out2 = out2.replace(/^- /gm, "\u2022 ");
|
|
2898
|
+
for (let i = 0; i < inlineCodes.length; i++) {
|
|
2899
|
+
out2 = out2.replace(`__SIC_${i}__`, inlineCodes[i]);
|
|
2900
|
+
}
|
|
2901
|
+
for (let i = 0; i < codeBlocks.length; i++) {
|
|
2902
|
+
out2 = out2.replace(`__SCB_${i}__`, codeBlocks[i]);
|
|
2903
|
+
}
|
|
2904
|
+
for (let i = 0; i < links.length; i++) {
|
|
2905
|
+
out2 = out2.replace(`__SLK_${i}__`, links[i]);
|
|
2906
|
+
}
|
|
2907
|
+
if (out2.length > 4e4) {
|
|
2908
|
+
out2 = out2.slice(0, 39990) + "...";
|
|
2909
|
+
}
|
|
2910
|
+
return out2;
|
|
2911
|
+
}
|
|
2912
|
+
var lexer, HTML_ENTITIES;
|
|
2913
|
+
var init_markdown = __esm({
|
|
2914
|
+
"src/utils/markdown.ts"() {
|
|
2915
|
+
"use strict";
|
|
2916
|
+
init_highlight();
|
|
2917
|
+
lexer = new Marked();
|
|
2918
|
+
HTML_ENTITIES = {
|
|
2919
|
+
"&": "&",
|
|
2920
|
+
"<": "<",
|
|
2921
|
+
">": ">",
|
|
2922
|
+
""": '"',
|
|
2923
|
+
"'": "'",
|
|
2924
|
+
"'": "'",
|
|
2925
|
+
"'": "'",
|
|
2926
|
+
" ": " "
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2929
|
+
});
|
|
2930
|
+
|
|
2412
2931
|
// src/spotify/ui.ts
|
|
2413
2932
|
var ui_exports = {};
|
|
2414
2933
|
__export(ui_exports, {
|
|
@@ -6296,9 +6815,358 @@ var init_daemon = __esm({
|
|
|
6296
6815
|
}
|
|
6297
6816
|
});
|
|
6298
6817
|
|
|
6818
|
+
// src/ui/attach-tui.tsx
|
|
6819
|
+
var attach_tui_exports = {};
|
|
6820
|
+
__export(attach_tui_exports, {
|
|
6821
|
+
AttachTui: () => AttachTui
|
|
6822
|
+
});
|
|
6823
|
+
import React3 from "react";
|
|
6824
|
+
import { Box as Box2, Text as Text2, Spacer as Spacer2, Static as Static2, useApp as useApp2, useInput as useInput2 } from "ink";
|
|
6825
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
6826
|
+
function AttachMessageBlock({ message }) {
|
|
6827
|
+
const isUser = message.role === "user";
|
|
6828
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, flexShrink: 0, children: [
|
|
6829
|
+
/* @__PURE__ */ jsx2(Box2, { flexShrink: 0, children: /* @__PURE__ */ jsxs2(Text2, { bold: true, color: isUser ? "yellow" : message.role === "system" ? "gray" : "cyan", children: [
|
|
6830
|
+
isUser ? "YOU" : message.role === "system" ? "\u2014" : "MERCURY",
|
|
6831
|
+
":"
|
|
6832
|
+
] }) }),
|
|
6833
|
+
/* @__PURE__ */ jsx2(Box2, { marginLeft: 2, flexDirection: "column", flexShrink: 0, children: (isUser || message.role === "system" ? message.content : renderMarkdown(message.content)).split("\n").map((line, idx) => /* @__PURE__ */ jsx2(Box2, { flexShrink: 0, children: /* @__PURE__ */ jsx2(Text2, { children: line.length > 0 ? line : " " }) }, `${message.id}:${idx}`)) })
|
|
6834
|
+
] });
|
|
6835
|
+
}
|
|
6836
|
+
function AttachPromptView({ prompt, activeIdx }) {
|
|
6837
|
+
return /* @__PURE__ */ jsx2(Box2, { paddingX: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs2(Box2, { borderStyle: "round", borderColor: "yellow", flexDirection: "column", paddingX: 1, flexShrink: 0, children: [
|
|
6838
|
+
/* @__PURE__ */ jsx2(Text2, { color: "yellow", bold: true, children: prompt.kind === "continue" ? "Continue? " : prompt.kind === "choice" ? "Choose " : "Permission: " }),
|
|
6839
|
+
/* @__PURE__ */ jsx2(Text2, { wrap: "truncate-end", children: prompt.text }),
|
|
6840
|
+
prompt.options.map((option, idx) => /* @__PURE__ */ jsx2(Box2, { paddingLeft: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: idx === activeIdx ? "cyan" : "gray", children: [
|
|
6841
|
+
idx === activeIdx ? "\u203A " : " ",
|
|
6842
|
+
option.label
|
|
6843
|
+
] }) }, option.value)),
|
|
6844
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " \u2191\u2193 select \xB7 Enter confirm \xB7 Esc decline" })
|
|
6845
|
+
] }) });
|
|
6846
|
+
}
|
|
6847
|
+
function AttachTui({ client, pid, onExit }) {
|
|
6848
|
+
const { exit } = useApp2();
|
|
6849
|
+
const [phase, setPhase] = React3.useState("pick");
|
|
6850
|
+
const [threads, setThreads] = React3.useState([]);
|
|
6851
|
+
const [threadsLoaded, setThreadsLoaded] = React3.useState(false);
|
|
6852
|
+
const [pickIdx, setPickIdx] = React3.useState(0);
|
|
6853
|
+
const [thread, setThread] = React3.useState(null);
|
|
6854
|
+
const [history, setHistory] = React3.useState([]);
|
|
6855
|
+
const [streamTail, setStreamTail] = React3.useState("");
|
|
6856
|
+
const [streaming, setStreaming] = React3.useState(false);
|
|
6857
|
+
const [steps, setSteps] = React3.useState([]);
|
|
6858
|
+
const [prompt, setPrompt] = React3.useState(null);
|
|
6859
|
+
const [promptIdx, setPromptIdx] = React3.useState(0);
|
|
6860
|
+
const [provider, setProvider] = React3.useState(null);
|
|
6861
|
+
const [notice, setNotice] = React3.useState(null);
|
|
6862
|
+
const [input, setInput] = React3.useState("");
|
|
6863
|
+
const [cursorPos, setCursorPos] = React3.useState(0);
|
|
6864
|
+
React3.useEffect(() => {
|
|
6865
|
+
client.listThreads().then((list) => {
|
|
6866
|
+
setThreads(list.slice(0, 6));
|
|
6867
|
+
setThreadsLoaded(true);
|
|
6868
|
+
}).catch(() => setThreadsLoaded(true));
|
|
6869
|
+
}, [client]);
|
|
6870
|
+
React3.useEffect(() => {
|
|
6871
|
+
if (!thread) return;
|
|
6872
|
+
const controller = new AbortController();
|
|
6873
|
+
const localEcho = (role, content) => {
|
|
6874
|
+
const id = `a_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
6875
|
+
setHistory((prev) => [...prev, { id, role, content }]);
|
|
6876
|
+
return id;
|
|
6877
|
+
};
|
|
6878
|
+
void client.streamEvents(thread.id, (event) => {
|
|
6879
|
+
switch (event.type) {
|
|
6880
|
+
case "connected":
|
|
6881
|
+
setProvider({
|
|
6882
|
+
name: String(event.data.provider ?? ""),
|
|
6883
|
+
model: String(event.data.model ?? "")
|
|
6884
|
+
});
|
|
6885
|
+
setNotice(null);
|
|
6886
|
+
break;
|
|
6887
|
+
case "thinking":
|
|
6888
|
+
setStreaming(true);
|
|
6889
|
+
break;
|
|
6890
|
+
case "text_delta": {
|
|
6891
|
+
const chunk = String(event.data.text ?? "");
|
|
6892
|
+
if (chunk) setStreamTail((prev) => (prev + chunk).slice(-ATTACH_STREAM_TAIL_CHARS));
|
|
6893
|
+
setStreaming(true);
|
|
6894
|
+
break;
|
|
6895
|
+
}
|
|
6896
|
+
case "text_done": {
|
|
6897
|
+
const fullText = String(event.data.fullText ?? "");
|
|
6898
|
+
if (fullText) localEcho("assistant", fullText);
|
|
6899
|
+
setStreamTail("");
|
|
6900
|
+
setStreaming(false);
|
|
6901
|
+
setSteps([]);
|
|
6902
|
+
break;
|
|
6903
|
+
}
|
|
6904
|
+
case "step_start":
|
|
6905
|
+
setSteps((prev) => [...prev.slice(-(ATTACH_STEP_WINDOW - 1)), {
|
|
6906
|
+
key: `step_${String(event.data.step ?? prev.length)}`,
|
|
6907
|
+
label: String(event.data.label ?? event.data.tool ?? "working"),
|
|
6908
|
+
done: false
|
|
6909
|
+
}]);
|
|
6910
|
+
break;
|
|
6911
|
+
case "step_done":
|
|
6912
|
+
setSteps((prev) => {
|
|
6913
|
+
const next = [...prev];
|
|
6914
|
+
const running = next.findIndex((step) => !step.done);
|
|
6915
|
+
if (running !== -1) next[running] = { ...next[running], done: true };
|
|
6916
|
+
return next.slice(-ATTACH_STEP_WINDOW);
|
|
6917
|
+
});
|
|
6918
|
+
break;
|
|
6919
|
+
case "permission_request":
|
|
6920
|
+
setPrompt({
|
|
6921
|
+
kind: "permission",
|
|
6922
|
+
id: String(event.data.id ?? ""),
|
|
6923
|
+
text: String(event.data.prompt ?? ""),
|
|
6924
|
+
options: (Array.isArray(event.data.options) ? event.data.options : ["yes", "no"]).map((value) => ({ value, label: value }))
|
|
6925
|
+
});
|
|
6926
|
+
setPromptIdx(0);
|
|
6927
|
+
break;
|
|
6928
|
+
case "permission_continue":
|
|
6929
|
+
setPrompt({
|
|
6930
|
+
kind: "continue",
|
|
6931
|
+
id: String(event.data.id ?? ""),
|
|
6932
|
+
text: String(event.data.question ?? ""),
|
|
6933
|
+
options: (Array.isArray(event.data.options) ? event.data.options : ["yes", "no"]).map((value) => ({ value, label: value }))
|
|
6934
|
+
});
|
|
6935
|
+
setPromptIdx(0);
|
|
6936
|
+
break;
|
|
6937
|
+
case "choice_prompt":
|
|
6938
|
+
setPrompt({
|
|
6939
|
+
kind: "choice",
|
|
6940
|
+
id: String(event.data.id ?? ""),
|
|
6941
|
+
text: String(event.data.question ?? ""),
|
|
6942
|
+
options: (Array.isArray(event.data.options) ? event.data.options : []).map((o) => ({ value: o.value, label: o.label ?? o.value }))
|
|
6943
|
+
});
|
|
6944
|
+
setPromptIdx(0);
|
|
6945
|
+
break;
|
|
6946
|
+
case "permission_resolved":
|
|
6947
|
+
case "choice_resolved":
|
|
6948
|
+
setPrompt((current) => current && String(event.data.id ?? "") === current.id ? null : current);
|
|
6949
|
+
break;
|
|
6950
|
+
case "error":
|
|
6951
|
+
setNotice(`\u26A0 ${String(event.data.message ?? "agent error")}`);
|
|
6952
|
+
setStreaming(false);
|
|
6953
|
+
break;
|
|
6954
|
+
case "attach_disconnected":
|
|
6955
|
+
setNotice("connection lost \u2014 reconnecting\u2026");
|
|
6956
|
+
setStreaming(false);
|
|
6957
|
+
break;
|
|
6958
|
+
case "attach_auth_error":
|
|
6959
|
+
setNotice("\u26A0 runtime restarted (attach token rotated) \u2014 run `mercury attach` again");
|
|
6960
|
+
setStreaming(false);
|
|
6961
|
+
break;
|
|
6962
|
+
default:
|
|
6963
|
+
break;
|
|
6964
|
+
}
|
|
6965
|
+
}, controller.signal);
|
|
6966
|
+
return () => controller.abort();
|
|
6967
|
+
}, [client, thread?.id]);
|
|
6968
|
+
const startThread = React3.useCallback(async (picked) => {
|
|
6969
|
+
if (picked) {
|
|
6970
|
+
setThread(picked);
|
|
6971
|
+
const messages = await client.getThread(picked.id);
|
|
6972
|
+
setHistory(messages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ id: m.id, role: m.role, content: m.content })));
|
|
6973
|
+
} else {
|
|
6974
|
+
const created = await client.createThread();
|
|
6975
|
+
setThread(created ? { id: created.id, shortId: created.shortId, alias: created.alias, title: created.title } : { id: "", shortId: "", alias: "new session", title: "New session" });
|
|
6976
|
+
}
|
|
6977
|
+
setPhase("chat");
|
|
6978
|
+
}, [client]);
|
|
6979
|
+
const sendInput = React3.useCallback(async (text) => {
|
|
6980
|
+
if (!thread || !text.trim()) return;
|
|
6981
|
+
const id = `a_local_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
6982
|
+
setHistory((prev) => [...prev, { id, role: "user", content: text }]);
|
|
6983
|
+
setStreaming(true);
|
|
6984
|
+
const result = await client.send(text, thread.id || void 0);
|
|
6985
|
+
if (!result.ok) {
|
|
6986
|
+
setNotice(`\u26A0 send failed: ${result.error ?? "unknown error"}`);
|
|
6987
|
+
setStreaming(false);
|
|
6988
|
+
return;
|
|
6989
|
+
}
|
|
6990
|
+
if (!thread.id && result.sessionId) {
|
|
6991
|
+
setThread({ ...thread, id: result.sessionId, alias: "session", title: "New session" });
|
|
6992
|
+
}
|
|
6993
|
+
}, [client, thread]);
|
|
6994
|
+
const resolvePrompt = React3.useCallback(async (value) => {
|
|
6995
|
+
const active = prompt;
|
|
6996
|
+
if (!active) return;
|
|
6997
|
+
setPrompt(null);
|
|
6998
|
+
await client.resolvePermission(active.id, value);
|
|
6999
|
+
}, [client, prompt]);
|
|
7000
|
+
useInput2((ch, key) => {
|
|
7001
|
+
if (ch === "" || key.ctrl && key.name === "c") {
|
|
7002
|
+
exit();
|
|
7003
|
+
return;
|
|
7004
|
+
}
|
|
7005
|
+
if (phase === "pick") {
|
|
7006
|
+
if (threadsLoaded && threads.length >= 0) {
|
|
7007
|
+
const rows = threads.length + 1;
|
|
7008
|
+
if (key.upArrow) setPickIdx((i) => Math.max(0, i - 1));
|
|
7009
|
+
else if (key.downArrow) setPickIdx((i) => Math.min(rows - 1, i + 1));
|
|
7010
|
+
else if (key.return) {
|
|
7011
|
+
if (pickIdx < threads.length) {
|
|
7012
|
+
void startThread(threads[pickIdx]);
|
|
7013
|
+
} else {
|
|
7014
|
+
void startThread(null);
|
|
7015
|
+
}
|
|
7016
|
+
} else if (key.escape) {
|
|
7017
|
+
exit();
|
|
7018
|
+
}
|
|
7019
|
+
}
|
|
7020
|
+
return;
|
|
7021
|
+
}
|
|
7022
|
+
if (prompt) {
|
|
7023
|
+
const lower = ch?.toLowerCase?.();
|
|
7024
|
+
if (prompt.kind === "permission" && lower === "y") {
|
|
7025
|
+
void resolvePrompt("yes");
|
|
7026
|
+
return;
|
|
7027
|
+
}
|
|
7028
|
+
if (prompt.kind === "permission" && lower === "a") {
|
|
7029
|
+
void resolvePrompt("always");
|
|
7030
|
+
return;
|
|
7031
|
+
}
|
|
7032
|
+
if ((prompt.kind === "permission" || prompt.kind === "continue") && lower === "n") {
|
|
7033
|
+
void resolvePrompt("no");
|
|
7034
|
+
return;
|
|
7035
|
+
}
|
|
7036
|
+
if (key.escape) {
|
|
7037
|
+
void resolvePrompt(prompt.kind === "choice" ? "" : "no");
|
|
7038
|
+
return;
|
|
7039
|
+
}
|
|
7040
|
+
if (key.upArrow) setPromptIdx((i) => Math.max(0, i - 1));
|
|
7041
|
+
else if (key.downArrow) setPromptIdx((i) => Math.min(prompt.options.length - 1, i + 1));
|
|
7042
|
+
else if (key.return) {
|
|
7043
|
+
const option = prompt.options[promptIdx] ?? prompt.options[0];
|
|
7044
|
+
if (option) void resolvePrompt(option.value);
|
|
7045
|
+
}
|
|
7046
|
+
return;
|
|
7047
|
+
}
|
|
7048
|
+
if (key.return) {
|
|
7049
|
+
const trimmed = input.trim();
|
|
7050
|
+
if (trimmed) {
|
|
7051
|
+
setInput("");
|
|
7052
|
+
setCursorPos(0);
|
|
7053
|
+
void sendInput(trimmed);
|
|
7054
|
+
}
|
|
7055
|
+
return;
|
|
7056
|
+
}
|
|
7057
|
+
if (key.leftArrow) {
|
|
7058
|
+
setCursorPos((p) => Math.max(0, p - 1));
|
|
7059
|
+
return;
|
|
7060
|
+
}
|
|
7061
|
+
if (key.rightArrow) {
|
|
7062
|
+
setCursorPos((p) => Math.min(input.length, p + 1));
|
|
7063
|
+
return;
|
|
7064
|
+
}
|
|
7065
|
+
if (key.backspace || key.delete) {
|
|
7066
|
+
if (cursorPos > 0) {
|
|
7067
|
+
setInput((prev) => prev.slice(0, cursorPos - 1) + prev.slice(cursorPos));
|
|
7068
|
+
setCursorPos((p) => p - 1);
|
|
7069
|
+
}
|
|
7070
|
+
return;
|
|
7071
|
+
}
|
|
7072
|
+
if (key.ctrl || key.meta) return;
|
|
7073
|
+
if (ch && ch.length > 0 && !key.escape) {
|
|
7074
|
+
const clean = ch.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "").split("").filter((c) => {
|
|
7075
|
+
const code = c.charCodeAt(0);
|
|
7076
|
+
return code >= 32 && code <= 126 || code >= 160;
|
|
7077
|
+
}).join("");
|
|
7078
|
+
if (clean) {
|
|
7079
|
+
setInput((prev) => prev.slice(0, cursorPos) + clean + prev.slice(cursorPos));
|
|
7080
|
+
setCursorPos((p) => p + clean.length);
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
});
|
|
7084
|
+
if (phase === "pick") {
|
|
7085
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7086
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "\u26BF Attach to Mercury" }),
|
|
7087
|
+
pid != null && /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
7088
|
+
"runtime PID ",
|
|
7089
|
+
pid,
|
|
7090
|
+
" \xB7 Ctrl+C cancels"
|
|
7091
|
+
] }),
|
|
7092
|
+
/* @__PURE__ */ jsx2(Text2, { children: " " }),
|
|
7093
|
+
!threadsLoaded ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Loading sessions\u2026" }) : [
|
|
7094
|
+
...threads.map((t, idx) => /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7095
|
+
/* @__PURE__ */ jsx2(Text2, { color: idx === pickIdx ? "cyan" : "gray", children: idx === pickIdx ? "\u203A " : " " }),
|
|
7096
|
+
/* @__PURE__ */ jsxs2(Text2, { bold: idx === pickIdx, color: idx === pickIdx ? "white" : "gray", children: [
|
|
7097
|
+
t.alias,
|
|
7098
|
+
" [",
|
|
7099
|
+
t.shortId,
|
|
7100
|
+
"]"
|
|
7101
|
+
] }),
|
|
7102
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
7103
|
+
" ",
|
|
7104
|
+
t.title
|
|
7105
|
+
] })
|
|
7106
|
+
] }, t.id)),
|
|
7107
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7108
|
+
/* @__PURE__ */ jsx2(Text2, { color: threads.length === pickIdx ? "cyan" : "gray", children: threads.length === pickIdx ? "\u203A " : " " }),
|
|
7109
|
+
/* @__PURE__ */ jsx2(Text2, { bold: threads.length === pickIdx, color: threads.length === pickIdx ? "white" : "gray", children: "Start a new session" })
|
|
7110
|
+
] }, "__new__")
|
|
7111
|
+
],
|
|
7112
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2191\u2193 select \xB7 Enter attach" })
|
|
7113
|
+
] });
|
|
7114
|
+
}
|
|
7115
|
+
const providerLabel = provider && provider.name ? `${provider.name}${provider.model ? ` ${provider.model}` : ""}` : "connected";
|
|
7116
|
+
const tailLines = streamTail.length > 0 ? streamTail.split("\n").slice(-ATTACH_STREAM_TAIL_MAX_LINES) : [];
|
|
7117
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", flexShrink: 0, children: [
|
|
7118
|
+
/* @__PURE__ */ jsx2(Static2, { items: history, itemKey: attachItemKey, children: (message) => /* @__PURE__ */ jsx2(AttachMessageBlock, { message }, message.id) }),
|
|
7119
|
+
tailLines.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", flexShrink: 0, children: tailLines.map((line, idx) => /* @__PURE__ */ jsxs2(Box2, { paddingX: 2, children: [
|
|
7120
|
+
/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "\u2502 " }),
|
|
7121
|
+
/* @__PURE__ */ jsx2(Text2, { children: line || " " })
|
|
7122
|
+
] }, `t:${idx}`)) }),
|
|
7123
|
+
steps.length > 0 && /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", paddingX: 2, flexShrink: 0, children: steps.map((step) => /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7124
|
+
/* @__PURE__ */ jsx2(Text2, { color: step.done ? "green" : "cyan", children: step.done ? "\u2713" : "\u2192" }),
|
|
7125
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
7126
|
+
" ",
|
|
7127
|
+
step.label
|
|
7128
|
+
] })
|
|
7129
|
+
] }, step.key)) }),
|
|
7130
|
+
streaming && tailLines.length === 0 && steps.length === 0 && /* @__PURE__ */ jsxs2(Box2, { paddingX: 2, flexShrink: 0, children: [
|
|
7131
|
+
/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "\u280B" }),
|
|
7132
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " working\u2026" })
|
|
7133
|
+
] }),
|
|
7134
|
+
prompt && /* @__PURE__ */ jsx2(AttachPromptView, { prompt, activeIdx: promptIdx }),
|
|
7135
|
+
notice && /* @__PURE__ */ jsx2(Box2, { paddingX: 1, flexShrink: 0, children: /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: notice }) }),
|
|
7136
|
+
/* @__PURE__ */ jsx2(Box2, { paddingX: 2, flexShrink: 0, children: /* @__PURE__ */ jsx2(Box2, { borderStyle: "round", borderColor: "gray", flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7137
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "> " }),
|
|
7138
|
+
/* @__PURE__ */ jsx2(Text2, { children: input.slice(0, cursorPos) }),
|
|
7139
|
+
/* @__PURE__ */ jsx2(Text2, { inverse: true, children: cursorPos < input.length ? input[cursorPos] : " " }),
|
|
7140
|
+
/* @__PURE__ */ jsx2(Text2, { children: input.slice(cursorPos + 1) })
|
|
7141
|
+
] }) }) }),
|
|
7142
|
+
/* @__PURE__ */ jsxs2(Box2, { paddingX: 3, flexShrink: 0, children: [
|
|
7143
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u21B5 send \xB7 esc esc exit \xB7 ctrl+c detach" }),
|
|
7144
|
+
/* @__PURE__ */ jsx2(Spacer2, {}),
|
|
7145
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "blue", wrap: "truncate-end", children: [
|
|
7146
|
+
"\u26BF ",
|
|
7147
|
+
thread ? `${thread.alias} [${thread.shortId}]` : "",
|
|
7148
|
+
" \xB7 ",
|
|
7149
|
+
providerLabel,
|
|
7150
|
+
pid != null ? ` \xB7 PID ${pid}` : ""
|
|
7151
|
+
] })
|
|
7152
|
+
] })
|
|
7153
|
+
] });
|
|
7154
|
+
}
|
|
7155
|
+
var ATTACH_STREAM_TAIL_CHARS, ATTACH_STREAM_TAIL_MAX_LINES, ATTACH_STEP_WINDOW, attachItemKey;
|
|
7156
|
+
var init_attach_tui = __esm({
|
|
7157
|
+
"src/ui/attach-tui.tsx"() {
|
|
7158
|
+
"use strict";
|
|
7159
|
+
init_markdown();
|
|
7160
|
+
ATTACH_STREAM_TAIL_CHARS = 8 * 1024;
|
|
7161
|
+
ATTACH_STREAM_TAIL_MAX_LINES = 12;
|
|
7162
|
+
ATTACH_STEP_WINDOW = 4;
|
|
7163
|
+
attachItemKey = (message) => message.id;
|
|
7164
|
+
}
|
|
7165
|
+
});
|
|
7166
|
+
|
|
6299
7167
|
// src/cli/service.ts
|
|
6300
|
-
import { existsSync as
|
|
6301
|
-
import { join as
|
|
7168
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync23, writeFileSync as writeFileSync23, unlinkSync as unlinkSync11 } from "fs";
|
|
7169
|
+
import { join as join25 } from "path";
|
|
6302
7170
|
import { homedir as homedir8 } from "os";
|
|
6303
7171
|
import chalk8 from "chalk";
|
|
6304
7172
|
import { execSync as execSync8 } from "child_process";
|
|
@@ -6306,9 +7174,9 @@ function isServiceInstalled() {
|
|
|
6306
7174
|
const platform = process.platform;
|
|
6307
7175
|
if (isTermux()) return false;
|
|
6308
7176
|
if (platform === "darwin") {
|
|
6309
|
-
return
|
|
7177
|
+
return existsSync34(join25(homedir8(), "Library", "LaunchAgents", "com.cosmicstack.mercury.plist"));
|
|
6310
7178
|
} else if (platform === "linux") {
|
|
6311
|
-
return
|
|
7179
|
+
return existsSync34(join25(homedir8(), ".config", "systemd", "user", "mercury.service"));
|
|
6312
7180
|
} else if (platform === "win32") {
|
|
6313
7181
|
try {
|
|
6314
7182
|
execSync8(`schtasks /query /tn "${WIN_TASK_NAME}"`, { stdio: "pipe", shell: "cmd.exe" });
|
|
@@ -6344,9 +7212,9 @@ function getNodeBinPath() {
|
|
|
6344
7212
|
}
|
|
6345
7213
|
function getDistPath() {
|
|
6346
7214
|
if (!process.argv[1]) {
|
|
6347
|
-
return
|
|
7215
|
+
return join25(homedir8(), ".nvm", "versions", "node", `v${process.version.slice(1)}`, "lib", "node_modules", "@cosmicstack", "mercury-agent", "dist", "index.js");
|
|
6348
7216
|
}
|
|
6349
|
-
return
|
|
7217
|
+
return join25(process.argv[1], "..", "..", "lib", "node_modules", "@cosmicstack", "mercury-agent", "dist", "index.js");
|
|
6350
7218
|
}
|
|
6351
7219
|
function getServiceLaunchArgs() {
|
|
6352
7220
|
if (isStandaloneBinary()) {
|
|
@@ -6409,7 +7277,7 @@ function restartService() {
|
|
|
6409
7277
|
try {
|
|
6410
7278
|
execSync8(`launchctl kickstart -k ${target}`, { stdio: "inherit" });
|
|
6411
7279
|
} catch {
|
|
6412
|
-
const plistPath =
|
|
7280
|
+
const plistPath = join25(homedir8(), "Library", "LaunchAgents", "com.cosmicstack.mercury.plist");
|
|
6413
7281
|
execSync8(`launchctl load "${plistPath}"`, { stdio: "inherit" });
|
|
6414
7282
|
execSync8(`launchctl kickstart -k ${target}`, { stdio: "inherit" });
|
|
6415
7283
|
}
|
|
@@ -6471,16 +7339,16 @@ function showTermuxServiceHelp(action) {
|
|
|
6471
7339
|
console.log("");
|
|
6472
7340
|
}
|
|
6473
7341
|
function installMac() {
|
|
6474
|
-
const plistDir =
|
|
6475
|
-
const plistPath =
|
|
6476
|
-
if (!
|
|
6477
|
-
|
|
7342
|
+
const plistDir = join25(homedir8(), "Library", "LaunchAgents");
|
|
7343
|
+
const plistPath = join25(plistDir, "com.cosmicstack.mercury.plist");
|
|
7344
|
+
if (!existsSync34(plistDir)) {
|
|
7345
|
+
mkdirSync23(plistDir, { recursive: true });
|
|
6478
7346
|
}
|
|
6479
7347
|
const nodeBin = getNodeBinPath();
|
|
6480
7348
|
const scriptPath = getDistPath();
|
|
6481
7349
|
const home = getMercuryHome();
|
|
6482
|
-
const logPath2 =
|
|
6483
|
-
const errPath =
|
|
7350
|
+
const logPath2 = join25(home, "daemon.log");
|
|
7351
|
+
const errPath = join25(home, "daemon-error.log");
|
|
6484
7352
|
const launchArgs = getServiceLaunchArgs();
|
|
6485
7353
|
const programArgsXml = launchArgs.map((a) => ` <string>${a}</string>`).join("\n");
|
|
6486
7354
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -6515,7 +7383,7 @@ ${programArgsXml}
|
|
|
6515
7383
|
<string>${homedir8()}</string>
|
|
6516
7384
|
</dict>
|
|
6517
7385
|
</plist>`;
|
|
6518
|
-
|
|
7386
|
+
writeFileSync23(plistPath, plist, "utf-8");
|
|
6519
7387
|
try {
|
|
6520
7388
|
execSync8(`launchctl load ${plistPath}`, { stdio: "inherit" });
|
|
6521
7389
|
} catch {
|
|
@@ -6532,8 +7400,8 @@ ${programArgsXml}
|
|
|
6532
7400
|
console.log("");
|
|
6533
7401
|
}
|
|
6534
7402
|
function uninstallMac() {
|
|
6535
|
-
const plistPath =
|
|
6536
|
-
if (!
|
|
7403
|
+
const plistPath = join25(homedir8(), "Library", "LaunchAgents", "com.cosmicstack.mercury.plist");
|
|
7404
|
+
if (!existsSync34(plistPath)) {
|
|
6537
7405
|
console.log(chalk8.yellow(" Mercury service is not installed."));
|
|
6538
7406
|
console.log("");
|
|
6539
7407
|
process.exit(0);
|
|
@@ -6553,8 +7421,8 @@ function uninstallMac() {
|
|
|
6553
7421
|
console.log("");
|
|
6554
7422
|
}
|
|
6555
7423
|
function showMacStatus() {
|
|
6556
|
-
const plistPath =
|
|
6557
|
-
if (!
|
|
7424
|
+
const plistPath = join25(homedir8(), "Library", "LaunchAgents", "com.cosmicstack.mercury.plist");
|
|
7425
|
+
if (!existsSync34(plistPath)) {
|
|
6558
7426
|
console.log(chalk8.yellow(" Mercury service is not installed."));
|
|
6559
7427
|
console.log(chalk8.dim(" Run `mercury service install` to set it up."));
|
|
6560
7428
|
console.log("");
|
|
@@ -6571,11 +7439,11 @@ function showMacStatus() {
|
|
|
6571
7439
|
console.log("");
|
|
6572
7440
|
}
|
|
6573
7441
|
function installLinux() {
|
|
6574
|
-
const systemdDir =
|
|
6575
|
-
if (!
|
|
6576
|
-
|
|
7442
|
+
const systemdDir = join25(homedir8(), ".config", "systemd", "user");
|
|
7443
|
+
if (!existsSync34(systemdDir)) {
|
|
7444
|
+
mkdirSync23(systemdDir, { recursive: true });
|
|
6577
7445
|
}
|
|
6578
|
-
const servicePath =
|
|
7446
|
+
const servicePath = join25(systemdDir, "mercury.service");
|
|
6579
7447
|
const nodeBin = getNodeBinPath();
|
|
6580
7448
|
const scriptPath = getDistPath();
|
|
6581
7449
|
const home = getMercuryHome();
|
|
@@ -6592,12 +7460,12 @@ RestartSec=5
|
|
|
6592
7460
|
Environment=PATH=${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}
|
|
6593
7461
|
Environment=HOME=${homedir8()}
|
|
6594
7462
|
WorkingDirectory=${homedir8()}
|
|
6595
|
-
StandardOutput=append:${
|
|
6596
|
-
StandardError=append:${
|
|
7463
|
+
StandardOutput=append:${join25(home, "daemon.log")}
|
|
7464
|
+
StandardError=append:${join25(home, "daemon-error.log")}
|
|
6597
7465
|
|
|
6598
7466
|
[Install]
|
|
6599
7467
|
WantedBy=default.target`;
|
|
6600
|
-
|
|
7468
|
+
writeFileSync23(servicePath, service, "utf-8");
|
|
6601
7469
|
try {
|
|
6602
7470
|
execSync8("systemctl --user daemon-reload", { stdio: "inherit" });
|
|
6603
7471
|
execSync8("systemctl --user enable mercury.service", { stdio: "inherit" });
|
|
@@ -6617,15 +7485,15 @@ WantedBy=default.target`;
|
|
|
6617
7485
|
console.log("");
|
|
6618
7486
|
console.log(chalk8.green(" Mercury service installed (systemd --user)"));
|
|
6619
7487
|
console.log(chalk8.dim(` Service: ${servicePath}`));
|
|
6620
|
-
console.log(chalk8.dim(` Logs: ${
|
|
7488
|
+
console.log(chalk8.dim(` Logs: ${join25(home, "daemon.log")}`));
|
|
6621
7489
|
console.log(chalk8.dim(" Auto-starts on login. Auto-restarts on crash (5s delay)."));
|
|
6622
7490
|
console.log("");
|
|
6623
7491
|
console.log(chalk8.dim(" Uninstall: mercury service uninstall"));
|
|
6624
7492
|
console.log("");
|
|
6625
7493
|
}
|
|
6626
7494
|
function uninstallLinux() {
|
|
6627
|
-
const servicePath =
|
|
6628
|
-
if (!
|
|
7495
|
+
const servicePath = join25(homedir8(), ".config", "systemd", "user", "mercury.service");
|
|
7496
|
+
if (!existsSync34(servicePath)) {
|
|
6629
7497
|
console.log(chalk8.yellow(" Mercury service is not installed."));
|
|
6630
7498
|
console.log("");
|
|
6631
7499
|
process.exit(0);
|
|
@@ -6650,8 +7518,8 @@ function uninstallLinux() {
|
|
|
6650
7518
|
console.log("");
|
|
6651
7519
|
}
|
|
6652
7520
|
function showLinuxStatus() {
|
|
6653
|
-
const servicePath =
|
|
6654
|
-
if (!
|
|
7521
|
+
const servicePath = join25(homedir8(), ".config", "systemd", "user", "mercury.service");
|
|
7522
|
+
if (!existsSync34(servicePath)) {
|
|
6655
7523
|
console.log(chalk8.yellow(" Mercury service is not installed."));
|
|
6656
7524
|
console.log(chalk8.dim(" Run `mercury service install` to set it up."));
|
|
6657
7525
|
console.log("");
|
|
@@ -6670,7 +7538,7 @@ function installWindows() {
|
|
|
6670
7538
|
const nodeBin = getNodeBinPath();
|
|
6671
7539
|
const scriptPath = getDistPath();
|
|
6672
7540
|
const home = getMercuryHome();
|
|
6673
|
-
const logPath2 =
|
|
7541
|
+
const logPath2 = join25(home, "daemon.log");
|
|
6674
7542
|
const cmd = getServiceLaunchArgs().map((a) => `"${a}"`).join(" ");
|
|
6675
7543
|
try {
|
|
6676
7544
|
execSync8(
|
|
@@ -6735,21 +7603,21 @@ var init_service = __esm({
|
|
|
6735
7603
|
});
|
|
6736
7604
|
|
|
6737
7605
|
// src/cloud/runtime-status.ts
|
|
6738
|
-
import { existsSync as
|
|
6739
|
-
import { join as
|
|
7606
|
+
import { existsSync as existsSync35, readFileSync as readFileSync24, unlinkSync as unlinkSync12, writeFileSync as writeFileSync24 } from "fs";
|
|
7607
|
+
import { join as join26 } from "path";
|
|
6740
7608
|
function statusPath() {
|
|
6741
|
-
return
|
|
7609
|
+
return join26(getMercuryHome(), "cloud-online.json");
|
|
6742
7610
|
}
|
|
6743
7611
|
function markCloudRuntimeOnline(agentId, mode) {
|
|
6744
|
-
|
|
7612
|
+
writeFileSync24(statusPath(), JSON.stringify({ agentId, mode, pid: process.pid, connectedAt: Date.now() }), {
|
|
6745
7613
|
encoding: "utf-8",
|
|
6746
7614
|
mode: 384
|
|
6747
7615
|
});
|
|
6748
7616
|
}
|
|
6749
7617
|
function clearCloudRuntimeOnline(ownerPid) {
|
|
6750
|
-
if (ownerPid !== void 0 &&
|
|
7618
|
+
if (ownerPid !== void 0 && existsSync35(statusPath())) {
|
|
6751
7619
|
try {
|
|
6752
|
-
const status2 = JSON.parse(
|
|
7620
|
+
const status2 = JSON.parse(readFileSync24(statusPath(), "utf-8"));
|
|
6753
7621
|
if (status2.pid !== ownerPid) return;
|
|
6754
7622
|
} catch {
|
|
6755
7623
|
return;
|
|
@@ -6761,9 +7629,9 @@ function clearCloudRuntimeOnline(ownerPid) {
|
|
|
6761
7629
|
}
|
|
6762
7630
|
}
|
|
6763
7631
|
function isCloudRuntimeOnline(agentId, mode) {
|
|
6764
|
-
if (!
|
|
7632
|
+
if (!existsSync35(statusPath())) return false;
|
|
6765
7633
|
try {
|
|
6766
|
-
const status2 = JSON.parse(
|
|
7634
|
+
const status2 = JSON.parse(readFileSync24(statusPath(), "utf-8"));
|
|
6767
7635
|
if (status2.agentId !== agentId || !status2.pid || mode && status2.mode !== mode) return false;
|
|
6768
7636
|
process.kill(status2.pid, 0);
|
|
6769
7637
|
return true;
|
|
@@ -13239,517 +14107,11 @@ function formatNarrative(steps, current, maxVisible) {
|
|
|
13239
14107
|
}
|
|
13240
14108
|
|
|
13241
14109
|
// src/ui/App.tsx
|
|
14110
|
+
init_markdown();
|
|
14111
|
+
init_highlight();
|
|
13242
14112
|
import React, { useSyncExternalStore } from "react";
|
|
13243
14113
|
import { Box, Text, Spacer, Static, useApp, useInput, useStdout } from "ink";
|
|
13244
14114
|
|
|
13245
|
-
// src/utils/markdown.ts
|
|
13246
|
-
import { Marked } from "marked";
|
|
13247
|
-
import chalk2 from "chalk";
|
|
13248
|
-
|
|
13249
|
-
// src/utils/highlight.ts
|
|
13250
|
-
import chalk from "chalk";
|
|
13251
|
-
var CONTROL = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
|
|
13252
|
-
function esc(s) {
|
|
13253
|
-
return s.replace(CONTROL, "");
|
|
13254
|
-
}
|
|
13255
|
-
var C_FAMILY = [
|
|
13256
|
-
{ re: /^\/\/[^\n]*/, color: (s) => chalk.gray(s) },
|
|
13257
|
-
{ re: /^\/\*[\s\S]*?\*\//, color: (s) => chalk.gray(s) },
|
|
13258
|
-
{ re: /^`(?:\\.|[^`\\])*`?/, color: (s) => chalk.green(s) },
|
|
13259
|
-
{ re: /^'(?:\\.|[^'\\\n])*'?/, color: (s) => chalk.green(s) },
|
|
13260
|
-
{ re: /^"(?:\\.|[^"\\\n])*"?/, color: (s) => chalk.green(s) },
|
|
13261
|
-
{ re: /^-?\d[\d_]*(?:\.\d+)?(?:[eE][+-]?\d+)?/, color: (s) => chalk.magenta(s) },
|
|
13262
|
-
{ re: /^(?:abstract|as|break|case|catch|class|const|continue|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|namespace|new|of|private|protected|public|readonly|return|satisfies|set|static|super|switch|this|throw|try|type|typeof|var|void|while|yield|async|await)\b/, color: (s) => chalk.yellow(s) },
|
|
13263
|
-
{ re: /^(?:true|false|null|unique|undefined|NaN|Infinity)\b/, color: (s) => chalk.blue(s) },
|
|
13264
|
-
{ re: /^[A-Za-z_$][\w$]*(?=\s*\()/, color: (s) => chalk.cyan(s) },
|
|
13265
|
-
{ re: /^[A-Z][\w$]*/, color: (s) => chalk.blue(s) }
|
|
13266
|
-
];
|
|
13267
|
-
var PY_RULES = [
|
|
13268
|
-
{ re: /^#[^\n]*/, color: (s) => chalk.gray(s) },
|
|
13269
|
-
{ re: /^"""[\s\S]*?("""|$)/, color: (s) => chalk.gray(s) },
|
|
13270
|
-
{ re: /^'''[\s\S]*?('''|$)/, color: (s) => chalk.gray(s) },
|
|
13271
|
-
{ re: /^f?"(?:\\.|[^"\\\n])*"?/, color: (s) => chalk.green(s) },
|
|
13272
|
-
{ re: /^f?'(?:\\.|[^'\\\n])*'?/, color: (s) => chalk.green(s) },
|
|
13273
|
-
{ re: /^\d[\d_]*(?:\.\d+)?/, color: (s) => chalk.magenta(s) },
|
|
13274
|
-
{ re: /^(?:def|class|import|from|return|if|elif|else|for|while|try|except|finally|with|as|lambda|yield|raise|pass|break|continue|global|nonlocal|assert|async|await|not|and|or|in|is|del)\b/, color: (s) => chalk.yellow(s) },
|
|
13275
|
-
{ re: /^(?:True|False|None|self|cls)\b/, color: (s) => chalk.blue(s) },
|
|
13276
|
-
{ re: /^[A-Za-z_]\w*(?=\s*\()/, color: (s) => chalk.cyan(s) },
|
|
13277
|
-
{ re: /^[A-Z][\w]*/, color: (s) => chalk.blue(s) },
|
|
13278
|
-
{ re: /^@\w[\w.]*/, color: (s) => chalk.green(s) }
|
|
13279
|
-
];
|
|
13280
|
-
var SHELL_RULES = [
|
|
13281
|
-
{ re: /^#[^\n]*/, color: (s) => chalk.gray(s) },
|
|
13282
|
-
{ re: /^(?:if|then|else|elif|fi|for|while|do|done|case|esac|function|return|export|local|source|set|unset|cd|exit)\b/, color: (s) => chalk.yellow(s) },
|
|
13283
|
-
{ re: /^\$\{[^}]*\}?\$?/, color: (s) => chalk.magenta(s) },
|
|
13284
|
-
{ re: /^\$\w*/, color: (s) => chalk.magenta(s) },
|
|
13285
|
-
{ re: /^"(?:\\.|[^"\\])*"?/, color: (s) => chalk.green(s) },
|
|
13286
|
-
{ re: /^'(?:[^'\\])*'?/, color: (s) => chalk.green(s) },
|
|
13287
|
-
{ re: /^\d+/, color: (s) => chalk.magenta(s) },
|
|
13288
|
-
{ re: /^(?:npm|pnpm|yarn|node|npx|git|curl|wget|python|python3|pip|cargo|go|make|brew|ls|cat|echo|mkdir|rm|mv|cp|cd|chmod|docker|kubectl)\b/, color: (s) => chalk.cyan(s) }
|
|
13289
|
-
];
|
|
13290
|
-
var JSON_RULES = [
|
|
13291
|
-
{ re: /^"(?:\\.|[^"\\])*"(?=\s*:)/, color: (s) => chalk.blue(s) },
|
|
13292
|
-
{ re: /^"(?:\\.|[^"\\])*"?/, color: (s) => chalk.green(s) },
|
|
13293
|
-
{ re: /^-?\d[\d_]*(?:\.\d+)?(?:[eE][+-]?\d+)?/, color: (s) => chalk.magenta(s) },
|
|
13294
|
-
{ re: /^(?:true|false|null)\b/, color: (s) => chalk.blue(s) }
|
|
13295
|
-
];
|
|
13296
|
-
var CSS_RULES = [
|
|
13297
|
-
{ re: /^\/\*[\s\S]*?\*\//, color: (s) => chalk.gray(s) },
|
|
13298
|
-
{ re: /^@[\w-]+/, color: (s) => chalk.yellow(s) },
|
|
13299
|
-
{ re: /^[.#]?[\w-]+(?=\s*\{)/, color: (s) => chalk.cyan(s) },
|
|
13300
|
-
{ re: /^[\w-]+(?=\s*:)/, color: (s) => chalk.blue(s) },
|
|
13301
|
-
{ re: /^"(?:\\.|[^"\\\n])*"?/, color: (s) => chalk.green(s) },
|
|
13302
|
-
{ re: /^'(?:[^'\\\n])*'?/, color: (s) => chalk.green(s) },
|
|
13303
|
-
{ re: /^-?\d[\d.]*(?:px|em|rem|%|vh|vw|s|ms|fr)?/, color: (s) => chalk.magenta(s) }
|
|
13304
|
-
];
|
|
13305
|
-
var GO_RULES = [
|
|
13306
|
-
{ re: /^\/\/[^\n]*/, color: (s) => chalk.gray(s) },
|
|
13307
|
-
{ re: /^\/\*[\s\S]*?\*\//, color: (s) => chalk.gray(s) },
|
|
13308
|
-
{ re: /^"(?:\\.|[^"\\\n])*"?/, color: (s) => chalk.green(s) },
|
|
13309
|
-
{ re: /^`(?:\\.|[^`\\])*`?/, color: (s) => chalk.green(s) },
|
|
13310
|
-
{ re: /^\d[\d_]*(?:\.\d+)?/, color: (s) => chalk.magenta(s) },
|
|
13311
|
-
{ re: /^(?:package|import|func|return|if|else|for|range|switch|case|default|type|struct|interface|map|chan|go|defer|var|const|select|break|continue|fallthrough)\b/, color: (s) => chalk.yellow(s) },
|
|
13312
|
-
{ re: /^[A-Za-z_]\w*(?=\s*\()/, color: (s) => chalk.cyan(s) },
|
|
13313
|
-
{ re: /^[A-Z][\w]*/, color: (s) => chalk.blue(s) }
|
|
13314
|
-
];
|
|
13315
|
-
var RUST_RULES = [
|
|
13316
|
-
{ re: /^\/\/[^\n]*/, color: (s) => chalk.gray(s) },
|
|
13317
|
-
{ re: /^\/\*[\s\S]*?\*\//, color: (s) => chalk.gray(s) },
|
|
13318
|
-
{ re: /^"(?:\\.|[^"\\\n])*"?/, color: (s) => chalk.green(s) },
|
|
13319
|
-
{ re: /^\d[\d_]*(?:\.\d+)?(?:[eE][+-]?\d+)?/, color: (s) => chalk.magenta(s) },
|
|
13320
|
-
{ re: /^(?:as|break|const|continue|crate|dyn|else|enum|extern|false|fn|for|if|impl|in|let|loop|match|mod|move|mut|pub|ref|return|self|Self|static|struct|super|trait|true|type|unsafe|use|where|while|async|await)\b/, color: (s) => chalk.yellow(s) },
|
|
13321
|
-
{ re: /^&['\u2019]?\w*\b/, color: (s) => chalk.cyan(s) },
|
|
13322
|
-
{ re: /^\w+!/, color: (s) => chalk.cyan(s) },
|
|
13323
|
-
{ re: /^[A-Za-z_]\w*(?=\s*[<(])/, color: (s) => chalk.cyan(s) },
|
|
13324
|
-
{ re: /^[A-Z][\w]*/, color: (s) => chalk.blue(s) }
|
|
13325
|
-
];
|
|
13326
|
-
var RULESETS = {
|
|
13327
|
-
javascript: C_FAMILY,
|
|
13328
|
-
json: JSON_RULES,
|
|
13329
|
-
python: PY_RULES,
|
|
13330
|
-
shell: SHELL_RULES,
|
|
13331
|
-
css: CSS_RULES,
|
|
13332
|
-
go: GO_RULES,
|
|
13333
|
-
rust: RUST_RULES
|
|
13334
|
-
};
|
|
13335
|
-
var ALIAS = {
|
|
13336
|
-
js: "javascript",
|
|
13337
|
-
jsx: "javascript",
|
|
13338
|
-
mjs: "javascript",
|
|
13339
|
-
cjs: "javascript",
|
|
13340
|
-
ts: "javascript",
|
|
13341
|
-
tsx: "javascript",
|
|
13342
|
-
typescript: "javascript",
|
|
13343
|
-
py: "python",
|
|
13344
|
-
python3: "python",
|
|
13345
|
-
sh: "shell",
|
|
13346
|
-
bash: "shell",
|
|
13347
|
-
zsh: "shell",
|
|
13348
|
-
console: "shell",
|
|
13349
|
-
shellscript: "shell",
|
|
13350
|
-
golang: "go",
|
|
13351
|
-
rs: "rust",
|
|
13352
|
-
jsonc: "json",
|
|
13353
|
-
json5: "json",
|
|
13354
|
-
less: "css",
|
|
13355
|
-
scss: "css",
|
|
13356
|
-
sass: "css",
|
|
13357
|
-
html: "css",
|
|
13358
|
-
xml: "css",
|
|
13359
|
-
vue: "css",
|
|
13360
|
-
svelte: "css",
|
|
13361
|
-
yaml: "json",
|
|
13362
|
-
yml: "json",
|
|
13363
|
-
toml: "json",
|
|
13364
|
-
ini: "json"
|
|
13365
|
-
};
|
|
13366
|
-
function highlightLine(line, lang, opts) {
|
|
13367
|
-
if (opts?.uniform) {
|
|
13368
|
-
return opts.uniform === "red" ? chalk.red(line) : chalk.green(line);
|
|
13369
|
-
}
|
|
13370
|
-
try {
|
|
13371
|
-
const ruleset = RULESETS[ALIAS[lang?.toLowerCase() ?? ""] ?? lang?.toLowerCase() ?? ""] ?? C_FAMILY;
|
|
13372
|
-
let rest = esc(line);
|
|
13373
|
-
let out2 = "";
|
|
13374
|
-
let guard = 0;
|
|
13375
|
-
while (rest.length > 0 && guard++ < 400) {
|
|
13376
|
-
let matched = false;
|
|
13377
|
-
for (const rule of ruleset) {
|
|
13378
|
-
const m = rule.re.exec(rest);
|
|
13379
|
-
if (m && m[0].length > 0) {
|
|
13380
|
-
out2 += rule.color(m[0]);
|
|
13381
|
-
rest = rest.slice(m[0].length);
|
|
13382
|
-
matched = true;
|
|
13383
|
-
break;
|
|
13384
|
-
}
|
|
13385
|
-
}
|
|
13386
|
-
if (!matched) {
|
|
13387
|
-
out2 += rest[0];
|
|
13388
|
-
rest = rest.slice(1);
|
|
13389
|
-
}
|
|
13390
|
-
}
|
|
13391
|
-
if (rest.length > 0) out2 += rest;
|
|
13392
|
-
return out2;
|
|
13393
|
-
} catch {
|
|
13394
|
-
return line;
|
|
13395
|
-
}
|
|
13396
|
-
}
|
|
13397
|
-
function highlightDiffLine(line) {
|
|
13398
|
-
if (line.startsWith("+++") || line.startsWith("---")) return chalk.blue(line);
|
|
13399
|
-
if (line.startsWith("+++") || line.startsWith("---")) return chalk.blue(line);
|
|
13400
|
-
if (line.startsWith("diff ")) return chalk.bold.blue(line);
|
|
13401
|
-
if (line.startsWith("@@")) return chalk.cyan(line);
|
|
13402
|
-
if (line.startsWith("+")) return chalk.green(line);
|
|
13403
|
-
if (line.startsWith("-")) return chalk.red(line);
|
|
13404
|
-
return line;
|
|
13405
|
-
}
|
|
13406
|
-
function highlightCodeBlock(body, lang) {
|
|
13407
|
-
const trimmedLang = (lang || "").trim().toLowerCase();
|
|
13408
|
-
if (trimmedLang === "diff" || trimmedLang === "patch" || /^(diff --git|--- a\/|\+\+\+ b\/)/m.test(body)) {
|
|
13409
|
-
return body.split("\n").map(highlightDiffLine);
|
|
13410
|
-
}
|
|
13411
|
-
return body.split("\n").map((l) => highlightLine(l, trimmedLang));
|
|
13412
|
-
}
|
|
13413
|
-
|
|
13414
|
-
// src/utils/markdown.ts
|
|
13415
|
-
var lexer = new Marked();
|
|
13416
|
-
var HTML_ENTITIES = {
|
|
13417
|
-
"&": "&",
|
|
13418
|
-
"<": "<",
|
|
13419
|
-
">": ">",
|
|
13420
|
-
""": '"',
|
|
13421
|
-
"'": "'",
|
|
13422
|
-
"'": "'",
|
|
13423
|
-
"'": "'",
|
|
13424
|
-
" ": " "
|
|
13425
|
-
};
|
|
13426
|
-
function decodeHtmlEntities(text) {
|
|
13427
|
-
return text.replace(/&(?:#[xX]?[0-9a-fA-F]+|[a-zA-Z]+);/g, (match) => {
|
|
13428
|
-
if (HTML_ENTITIES[match]) return HTML_ENTITIES[match];
|
|
13429
|
-
if (match.startsWith("&#x") || match.startsWith("&#X")) {
|
|
13430
|
-
const code = parseInt(match.slice(3, -1), 16);
|
|
13431
|
-
return isNaN(code) ? match : String.fromCodePoint(code);
|
|
13432
|
-
}
|
|
13433
|
-
if (match.startsWith("&#")) {
|
|
13434
|
-
const code = parseInt(match.slice(2, -1), 10);
|
|
13435
|
-
return isNaN(code) ? match : String.fromCodePoint(code);
|
|
13436
|
-
}
|
|
13437
|
-
return match;
|
|
13438
|
-
});
|
|
13439
|
-
}
|
|
13440
|
-
function renderMarkdown(text) {
|
|
13441
|
-
try {
|
|
13442
|
-
const tokens = lexer.lexer(text);
|
|
13443
|
-
const result = renderTokens(tokens);
|
|
13444
|
-
return decodeHtmlEntities(result.replace(/\n{3,}/g, "\n\n").trimEnd());
|
|
13445
|
-
} catch {
|
|
13446
|
-
return decodeHtmlEntities(text);
|
|
13447
|
-
}
|
|
13448
|
-
}
|
|
13449
|
-
function renderTokens(tokens) {
|
|
13450
|
-
return tokens.map((t) => renderToken(t)).join("");
|
|
13451
|
-
}
|
|
13452
|
-
function renderToken(t) {
|
|
13453
|
-
if (!t || typeof t !== "object") return String(t ?? "");
|
|
13454
|
-
switch (t.type) {
|
|
13455
|
-
case "heading":
|
|
13456
|
-
return renderHeading(t);
|
|
13457
|
-
case "paragraph":
|
|
13458
|
-
return renderInline(t.tokens) + "\n\n";
|
|
13459
|
-
case "strong":
|
|
13460
|
-
return chalk2.bold(renderInline(t.tokens));
|
|
13461
|
-
case "em":
|
|
13462
|
-
return chalk2.italic(renderInline(t.tokens));
|
|
13463
|
-
case "del":
|
|
13464
|
-
return chalk2.dim.strikethrough(renderInline(t.tokens));
|
|
13465
|
-
case "codespan":
|
|
13466
|
-
return chalk2.yellow(t.text);
|
|
13467
|
-
case "code":
|
|
13468
|
-
return renderCodeBlock(t);
|
|
13469
|
-
case "list":
|
|
13470
|
-
return renderList(t);
|
|
13471
|
-
case "blockquote":
|
|
13472
|
-
return renderBlockquote(t);
|
|
13473
|
-
case "hr":
|
|
13474
|
-
return chalk2.dim("\u2500".repeat(50)) + "\n\n";
|
|
13475
|
-
case "link":
|
|
13476
|
-
return `${chalk2.blue.underline(renderInline(t.tokens))} ${chalk2.dim(`(${t.href})`)}`;
|
|
13477
|
-
case "image":
|
|
13478
|
-
return chalk2.blue(`\u{1F5BC} ${t.title || t.href}`);
|
|
13479
|
-
case "table":
|
|
13480
|
-
return renderTable(t);
|
|
13481
|
-
case "text":
|
|
13482
|
-
if (t.tokens) return renderInline(t.tokens);
|
|
13483
|
-
return t.text || "";
|
|
13484
|
-
case "html":
|
|
13485
|
-
return t.text || "";
|
|
13486
|
-
case "space":
|
|
13487
|
-
return "";
|
|
13488
|
-
default:
|
|
13489
|
-
return t.text || "";
|
|
13490
|
-
}
|
|
13491
|
-
}
|
|
13492
|
-
function renderHeading(t) {
|
|
13493
|
-
const text = renderInline(t.tokens);
|
|
13494
|
-
if (t.depth === 1) return `
|
|
13495
|
-
${chalk2.bold.cyan(text)}
|
|
13496
|
-
|
|
13497
|
-
`;
|
|
13498
|
-
if (t.depth === 2) return `
|
|
13499
|
-
${chalk2.bold.cyan(` \u25A0 ${text}`)}
|
|
13500
|
-
|
|
13501
|
-
`;
|
|
13502
|
-
return `
|
|
13503
|
-
${chalk2.bold(` \u25A0 ${text}`)}
|
|
13504
|
-
|
|
13505
|
-
`;
|
|
13506
|
-
}
|
|
13507
|
-
function renderInline(tokens) {
|
|
13508
|
-
if (!tokens) return "";
|
|
13509
|
-
return tokens.map((t) => {
|
|
13510
|
-
if (typeof t === "string") return t;
|
|
13511
|
-
if (t.type === "strong") return chalk2.bold(renderInline(t.tokens));
|
|
13512
|
-
if (t.type === "em") return chalk2.italic(renderInline(t.tokens));
|
|
13513
|
-
if (t.type === "del") return chalk2.dim.strikethrough(renderInline(t.tokens));
|
|
13514
|
-
if (t.type === "codespan") return chalk2.yellow(t.text);
|
|
13515
|
-
if (t.type === "link") return `${chalk2.blue.underline(renderInline(t.tokens))} ${chalk2.dim(`(${t.href})`)}`;
|
|
13516
|
-
if (t.type === "image") return chalk2.blue(`\u{1F5BC} ${t.title || t.href}`);
|
|
13517
|
-
if (t.type === "text") {
|
|
13518
|
-
return t.tokens ? renderInline(t.tokens) : t.text || "";
|
|
13519
|
-
}
|
|
13520
|
-
if (t.type === "html") return t.text || "";
|
|
13521
|
-
return t.text || "";
|
|
13522
|
-
}).join("");
|
|
13523
|
-
}
|
|
13524
|
-
function renderCodeBlock(t) {
|
|
13525
|
-
const lines = highlightCodeBlock(t.text ?? "", t.lang).map((l) => ` ${l}`).join("\n");
|
|
13526
|
-
const langStr = t.lang ? chalk2.dim(` [${t.lang}]`) : "";
|
|
13527
|
-
return `
|
|
13528
|
-
${langStr}
|
|
13529
|
-
${lines}
|
|
13530
|
-
|
|
13531
|
-
`;
|
|
13532
|
-
}
|
|
13533
|
-
function renderList(t) {
|
|
13534
|
-
const lines = [];
|
|
13535
|
-
const items = t.items || [];
|
|
13536
|
-
items.forEach((item, i) => {
|
|
13537
|
-
const bullet = t.ordered ? `${i + 1}.` : "\u2022";
|
|
13538
|
-
const firstLine = renderInline(item.tokens?.[0]?.tokens || [{ text: item.text }]);
|
|
13539
|
-
lines.push(` ${chalk2.dim(bullet)} ${firstLine}`);
|
|
13540
|
-
const restTokens = (item.tokens || []).slice(1);
|
|
13541
|
-
for (const sub of restTokens) {
|
|
13542
|
-
if (sub.type === "list") {
|
|
13543
|
-
const subLines = renderList(sub).split("\n").filter(Boolean).map((l) => ` ${l}`).join("\n");
|
|
13544
|
-
lines.push(subLines);
|
|
13545
|
-
} else if (sub.type === "text") {
|
|
13546
|
-
lines.push(` ${chalk2.dim("\u2022")} ${renderInline(sub.tokens)}`);
|
|
13547
|
-
}
|
|
13548
|
-
}
|
|
13549
|
-
});
|
|
13550
|
-
return lines.join("\n") + "\n\n";
|
|
13551
|
-
}
|
|
13552
|
-
function renderBlockquote(t) {
|
|
13553
|
-
const content = renderTokens(t.tokens || []);
|
|
13554
|
-
const lines = content.split("\n").filter((l) => l.trim()).map((l) => `${chalk2.dim("\u2502 ")}${chalk2.gray(l)}`).join("\n");
|
|
13555
|
-
return `
|
|
13556
|
-
${lines}
|
|
13557
|
-
|
|
13558
|
-
`;
|
|
13559
|
-
}
|
|
13560
|
-
function renderTable(t) {
|
|
13561
|
-
const headers = (t.header || []).map((h) => chalk2.bold(renderInline(h.tokens)));
|
|
13562
|
-
const colWidths = (t.header || []).map((h, i) => {
|
|
13563
|
-
const hLen = (h.text || "").length;
|
|
13564
|
-
const rowLens = (t.rows || []).map((row) => {
|
|
13565
|
-
const cell = row[i];
|
|
13566
|
-
return cell?.text?.length ?? 0;
|
|
13567
|
-
});
|
|
13568
|
-
return Math.max(hLen, ...rowLens) + 2;
|
|
13569
|
-
});
|
|
13570
|
-
const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(chalk2.dim(" \u2502 "));
|
|
13571
|
-
const separator = colWidths.map((w) => "\u2500".repeat(w)).join(chalk2.dim("\u2500\u253C\u2500"));
|
|
13572
|
-
const dataLines = (t.rows || []).map(
|
|
13573
|
-
(row) => row.map((cell, i) => {
|
|
13574
|
-
const text = renderInline(cell.tokens) || cell.text || "";
|
|
13575
|
-
return text.padEnd(colWidths[i]);
|
|
13576
|
-
}).join(chalk2.dim(" \u2502 "))
|
|
13577
|
-
);
|
|
13578
|
-
return `
|
|
13579
|
-
${headerLine}
|
|
13580
|
-
${chalk2.dim(separator)}
|
|
13581
|
-
${dataLines.join("\n")}
|
|
13582
|
-
|
|
13583
|
-
`;
|
|
13584
|
-
}
|
|
13585
|
-
function escapeHtml(text) {
|
|
13586
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
13587
|
-
}
|
|
13588
|
-
function mdToTelegram(text) {
|
|
13589
|
-
let out2 = text;
|
|
13590
|
-
const codeBlocks = [];
|
|
13591
|
-
out2 = out2.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
13592
|
-
const placeholder = `__CODEBLOCK_${codeBlocks.length}__`;
|
|
13593
|
-
codeBlocks.push(`<pre><code class="${lang}">${escapeHtml(code)}</code></pre>`);
|
|
13594
|
-
return placeholder;
|
|
13595
|
-
});
|
|
13596
|
-
const inlineCodes = [];
|
|
13597
|
-
out2 = out2.replace(/`([^`]+)`/g, (_match, code) => {
|
|
13598
|
-
const placeholder = `__INLINECODE_${inlineCodes.length}__`;
|
|
13599
|
-
inlineCodes.push(`<code>${escapeHtml(code)}</code>`);
|
|
13600
|
-
return placeholder;
|
|
13601
|
-
});
|
|
13602
|
-
const links = [];
|
|
13603
|
-
out2 = out2.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
|
|
13604
|
-
const placeholder = `__LINK_${links.length}__`;
|
|
13605
|
-
links.push(`<a href="${escapeHtml(href)}">${escapeHtml(label)}</a>`);
|
|
13606
|
-
return placeholder;
|
|
13607
|
-
});
|
|
13608
|
-
out2 = escapeHtml(out2);
|
|
13609
|
-
out2 = out2.replace(/^### (.+)$/gm, "<b><i>$1</i></b>");
|
|
13610
|
-
out2 = out2.replace(/^## (.+)$/gm, "<b>$1</b>");
|
|
13611
|
-
out2 = out2.replace(/^# (.+)$/gm, "<b>$1</b>");
|
|
13612
|
-
out2 = out2.replace(/\*\*([^*]+)\*\*/g, "<b>$1</b>");
|
|
13613
|
-
out2 = out2.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "<i>$1</i>");
|
|
13614
|
-
out2 = out2.replace(/~~([^~]+)~~/g, "<s>$1</s>");
|
|
13615
|
-
for (let i = 0; i < inlineCodes.length; i++) {
|
|
13616
|
-
out2 = out2.replace(`__INLINECODE_${i}__`, inlineCodes[i]);
|
|
13617
|
-
}
|
|
13618
|
-
for (let i = 0; i < codeBlocks.length; i++) {
|
|
13619
|
-
out2 = out2.replace(`__CODEBLOCK_${i}__`, codeBlocks[i]);
|
|
13620
|
-
}
|
|
13621
|
-
for (let i = 0; i < links.length; i++) {
|
|
13622
|
-
out2 = out2.replace(`__LINK_${i}__`, links[i]);
|
|
13623
|
-
}
|
|
13624
|
-
if (out2.length > 4096) {
|
|
13625
|
-
out2 = out2.slice(0, 4090) + "...";
|
|
13626
|
-
}
|
|
13627
|
-
return out2;
|
|
13628
|
-
}
|
|
13629
|
-
function mdToSignal(text) {
|
|
13630
|
-
let out2 = text;
|
|
13631
|
-
const codeBlocks = [];
|
|
13632
|
-
out2 = out2.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, _lang, code) => {
|
|
13633
|
-
const placeholder = `__CB_${codeBlocks.length}__`;
|
|
13634
|
-
codeBlocks.push(code.trim());
|
|
13635
|
-
return placeholder;
|
|
13636
|
-
});
|
|
13637
|
-
const inlineCodes = [];
|
|
13638
|
-
out2 = out2.replace(/`([^`]+)`/g, (_match, code) => {
|
|
13639
|
-
const placeholder = `__IC_${inlineCodes.length}__`;
|
|
13640
|
-
inlineCodes.push(code);
|
|
13641
|
-
return placeholder;
|
|
13642
|
-
});
|
|
13643
|
-
const links = [];
|
|
13644
|
-
out2 = out2.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
|
|
13645
|
-
const placeholder = `__LK_${links.length}__`;
|
|
13646
|
-
links.push(`${label} (${href})`);
|
|
13647
|
-
return placeholder;
|
|
13648
|
-
});
|
|
13649
|
-
out2 = out2.replace(/^### (.+)$/gm, "*$1*");
|
|
13650
|
-
out2 = out2.replace(/^## (.+)$/gm, "*$1*");
|
|
13651
|
-
out2 = out2.replace(/^# (.+)$/gm, "*$1*");
|
|
13652
|
-
out2 = out2.replace(/\*\*([^*]+)\*\*/g, "*$1*");
|
|
13653
|
-
out2 = out2.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "_$1_");
|
|
13654
|
-
out2 = out2.replace(/~~([^~]+)~~/g, "~$1~");
|
|
13655
|
-
for (let i = 0; i < inlineCodes.length; i++) {
|
|
13656
|
-
out2 = out2.replace(`__IC_${i}__`, inlineCodes[i]);
|
|
13657
|
-
}
|
|
13658
|
-
for (let i = 0; i < codeBlocks.length; i++) {
|
|
13659
|
-
out2 = out2.replace(`__CB_${i}__`, codeBlocks[i]);
|
|
13660
|
-
}
|
|
13661
|
-
for (let i = 0; i < links.length; i++) {
|
|
13662
|
-
out2 = out2.replace(`__LK_${i}__`, links[i]);
|
|
13663
|
-
}
|
|
13664
|
-
if (out2.length > 4e3) {
|
|
13665
|
-
out2 = out2.slice(0, 3990) + "...";
|
|
13666
|
-
}
|
|
13667
|
-
return out2;
|
|
13668
|
-
}
|
|
13669
|
-
function mdToDiscord(text) {
|
|
13670
|
-
let out2 = text;
|
|
13671
|
-
const codeBlocks = [];
|
|
13672
|
-
out2 = out2.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
13673
|
-
const placeholder = `__DCB_${codeBlocks.length}__`;
|
|
13674
|
-
codeBlocks.push(`\`\`\`${lang}
|
|
13675
|
-
${code.trim()}
|
|
13676
|
-
\`\`\``);
|
|
13677
|
-
return placeholder;
|
|
13678
|
-
});
|
|
13679
|
-
const inlineCodes = [];
|
|
13680
|
-
out2 = out2.replace(/`([^`]+)`/g, (_match, code) => {
|
|
13681
|
-
const placeholder = `__DIC_${inlineCodes.length}__`;
|
|
13682
|
-
inlineCodes.push(`\`${code}\``);
|
|
13683
|
-
return placeholder;
|
|
13684
|
-
});
|
|
13685
|
-
const links = [];
|
|
13686
|
-
out2 = out2.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
|
|
13687
|
-
const placeholder = `__DLK_${links.length}__`;
|
|
13688
|
-
links.push(`[${label}](${href})`);
|
|
13689
|
-
return placeholder;
|
|
13690
|
-
});
|
|
13691
|
-
out2 = out2.replace(/^### (.+)$/gm, "__$1__");
|
|
13692
|
-
out2 = out2.replace(/^## (.+)$/gm, "**$1**");
|
|
13693
|
-
out2 = out2.replace(/^# (.+)$/gm, "**$1**");
|
|
13694
|
-
out2 = out2.replace(/~~([^~]+)~~/g, "~~$1~~");
|
|
13695
|
-
for (let i = 0; i < inlineCodes.length; i++) {
|
|
13696
|
-
out2 = out2.replace(`__DIC_${i}__`, inlineCodes[i]);
|
|
13697
|
-
}
|
|
13698
|
-
for (let i = 0; i < codeBlocks.length; i++) {
|
|
13699
|
-
out2 = out2.replace(`__DCB_${i}__`, codeBlocks[i]);
|
|
13700
|
-
}
|
|
13701
|
-
for (let i = 0; i < links.length; i++) {
|
|
13702
|
-
out2 = out2.replace(`__DLK_${i}__`, links[i]);
|
|
13703
|
-
}
|
|
13704
|
-
if (out2.length > 2e3) {
|
|
13705
|
-
out2 = out2.slice(0, 1990) + "...";
|
|
13706
|
-
}
|
|
13707
|
-
return out2;
|
|
13708
|
-
}
|
|
13709
|
-
function mdToSlack(text) {
|
|
13710
|
-
let out2 = text;
|
|
13711
|
-
const codeBlocks = [];
|
|
13712
|
-
out2 = out2.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
13713
|
-
const placeholder = `__SCB_${codeBlocks.length}__`;
|
|
13714
|
-
codeBlocks.push(`\`\`\`${lang}
|
|
13715
|
-
${code.trim()}
|
|
13716
|
-
\`\`\``);
|
|
13717
|
-
return placeholder;
|
|
13718
|
-
});
|
|
13719
|
-
const inlineCodes = [];
|
|
13720
|
-
out2 = out2.replace(/`([^`]+)`/g, (_match, code) => {
|
|
13721
|
-
const placeholder = `__SIC_${inlineCodes.length}__`;
|
|
13722
|
-
inlineCodes.push(`\`${code}\``);
|
|
13723
|
-
return placeholder;
|
|
13724
|
-
});
|
|
13725
|
-
const links = [];
|
|
13726
|
-
out2 = out2.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
|
|
13727
|
-
const placeholder = `__SLK_${links.length}__`;
|
|
13728
|
-
links.push(`<${href}|${label}>`);
|
|
13729
|
-
return placeholder;
|
|
13730
|
-
});
|
|
13731
|
-
out2 = out2.replace(/^### (.+)$/gm, "*$1*");
|
|
13732
|
-
out2 = out2.replace(/^## (.+)$/gm, "*$1*");
|
|
13733
|
-
out2 = out2.replace(/^# (.+)$/gm, "*$1*");
|
|
13734
|
-
out2 = out2.replace(/\*\*([^*]+)\*\*/g, "*$1*");
|
|
13735
|
-
out2 = out2.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "_$1_");
|
|
13736
|
-
out2 = out2.replace(/~~([^~]+)~~/g, "~$1~");
|
|
13737
|
-
out2 = out2.replace(/^- /gm, "\u2022 ");
|
|
13738
|
-
for (let i = 0; i < inlineCodes.length; i++) {
|
|
13739
|
-
out2 = out2.replace(`__SIC_${i}__`, inlineCodes[i]);
|
|
13740
|
-
}
|
|
13741
|
-
for (let i = 0; i < codeBlocks.length; i++) {
|
|
13742
|
-
out2 = out2.replace(`__SCB_${i}__`, codeBlocks[i]);
|
|
13743
|
-
}
|
|
13744
|
-
for (let i = 0; i < links.length; i++) {
|
|
13745
|
-
out2 = out2.replace(`__SLK_${i}__`, links[i]);
|
|
13746
|
-
}
|
|
13747
|
-
if (out2.length > 4e4) {
|
|
13748
|
-
out2 = out2.slice(0, 39990) + "...";
|
|
13749
|
-
}
|
|
13750
|
-
return out2;
|
|
13751
|
-
}
|
|
13752
|
-
|
|
13753
14115
|
// src/ui/terminal-viewport.ts
|
|
13754
14116
|
function normalizeTerminalText(text) {
|
|
13755
14117
|
return text.replace(/\r\n?/g, "\n");
|
|
@@ -13767,10 +14129,9 @@ function getViewportWindow(totalLines, viewportLines, distanceFromBottom) {
|
|
|
13767
14129
|
maxDistanceFromBottom
|
|
13768
14130
|
};
|
|
13769
14131
|
}
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
|
|
13773
|
-
}
|
|
14132
|
+
|
|
14133
|
+
// src/ui/mercury-transcript.ts
|
|
14134
|
+
init_markdown();
|
|
13774
14135
|
|
|
13775
14136
|
// src/ui/pixel-logo.ts
|
|
13776
14137
|
var GLYPHS = {
|
|
@@ -14007,6 +14368,25 @@ function buildMercuryBrandLines(version, cols) {
|
|
|
14007
14368
|
return rows;
|
|
14008
14369
|
}
|
|
14009
14370
|
var CODE_BLOCK_VISIBLE_ROWS = 40;
|
|
14371
|
+
function buildStreamTailLines(message, width, tailChars = 8 * 1024, maxLines = 12) {
|
|
14372
|
+
const content = message.content;
|
|
14373
|
+
if (content.length === 0) return [];
|
|
14374
|
+
const rawStart = Math.max(0, content.length - tailChars);
|
|
14375
|
+
const head2 = content.slice(0, rawStart);
|
|
14376
|
+
const insideFence = (head2.match(/```/g) || []).length % 2 === 1;
|
|
14377
|
+
let start = rawStart === 0 ? 0 : content.indexOf("\n", rawStart) + 1;
|
|
14378
|
+
if (start === 0 && rawStart > 0) start = rawStart;
|
|
14379
|
+
if (insideFence) {
|
|
14380
|
+
const opener = content.lastIndexOf("```", Math.max(0, start - 1));
|
|
14381
|
+
if (opener >= 0 && opener < start) start = opener;
|
|
14382
|
+
}
|
|
14383
|
+
const tailMessage = { ...message, content: content.slice(start) };
|
|
14384
|
+
const lines = buildMercuryMessageLines(tailMessage, width);
|
|
14385
|
+
if (lines.length > maxLines) {
|
|
14386
|
+
return [lines[0], ...lines.slice(-(maxLines - 1))];
|
|
14387
|
+
}
|
|
14388
|
+
return lines;
|
|
14389
|
+
}
|
|
14010
14390
|
function buildMercuryMessageLines(message, width) {
|
|
14011
14391
|
if (message.id.startsWith("heartbeat-")) return [];
|
|
14012
14392
|
const contentWidth = Math.max(12, width - 4);
|
|
@@ -14498,40 +14878,45 @@ function TuiApp({ channel, onInput, onPermissionResolve, onExit, spotifyClient:
|
|
|
14498
14878
|
return;
|
|
14499
14879
|
}
|
|
14500
14880
|
if (key.upArrow) {
|
|
14501
|
-
|
|
14881
|
+
if (inputHistory.length === 0) return;
|
|
14882
|
+
if (historyIndex === -1) {
|
|
14883
|
+
setHistoryDraft(input);
|
|
14884
|
+
const next2 = inputHistory.length - 1;
|
|
14885
|
+
setHistoryIndex(next2);
|
|
14886
|
+
setInputAndCursor(inputHistory[next2] ?? "");
|
|
14887
|
+
return;
|
|
14888
|
+
}
|
|
14889
|
+
const next = Math.max(0, historyIndex - 1);
|
|
14890
|
+
setHistoryIndex(next);
|
|
14891
|
+
setInputAndCursor(inputHistory[next] ?? "");
|
|
14502
14892
|
return;
|
|
14503
14893
|
}
|
|
14504
14894
|
if (key.downArrow) {
|
|
14505
|
-
|
|
14506
|
-
|
|
14507
|
-
|
|
14508
|
-
|
|
14509
|
-
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
|
|
14513
|
-
|
|
14514
|
-
onInput(`/mc scroll -${transcriptPage}`);
|
|
14895
|
+
if (historyIndex === -1) return;
|
|
14896
|
+
const next = historyIndex + 1;
|
|
14897
|
+
if (next >= inputHistory.length) {
|
|
14898
|
+
setHistoryIndex(-1);
|
|
14899
|
+
setInputAndCursor(historyDraft);
|
|
14900
|
+
return;
|
|
14901
|
+
}
|
|
14902
|
+
setHistoryIndex(next);
|
|
14903
|
+
setInputAndCursor(inputHistory[next] ?? "");
|
|
14515
14904
|
return;
|
|
14516
14905
|
}
|
|
14517
14906
|
if (key.home) {
|
|
14518
|
-
|
|
14907
|
+
setCursorPos(0);
|
|
14519
14908
|
return;
|
|
14520
14909
|
}
|
|
14521
14910
|
if (key.end) {
|
|
14522
|
-
|
|
14523
|
-
return;
|
|
14524
|
-
}
|
|
14525
|
-
if (key.ctrl && (ch === "u" || ch === "U")) {
|
|
14526
|
-
onInput(`/mc scroll ${transcriptPage}`);
|
|
14911
|
+
setCursorPos(input.length);
|
|
14527
14912
|
return;
|
|
14528
14913
|
}
|
|
14529
14914
|
if (key.ctrl && (ch === "a" || ch === "A")) {
|
|
14530
|
-
|
|
14915
|
+
setCursorPos(0);
|
|
14531
14916
|
return;
|
|
14532
14917
|
}
|
|
14533
14918
|
if (key.ctrl && (ch === "e" || ch === "E")) {
|
|
14534
|
-
|
|
14919
|
+
setCursorPos(input.length);
|
|
14535
14920
|
return;
|
|
14536
14921
|
}
|
|
14537
14922
|
if (key.backspace || key.delete) {
|
|
@@ -15041,20 +15426,23 @@ function TuiApp({ channel, onInput, onPermissionResolve, onExit, spotifyClient:
|
|
|
15041
15426
|
MercuryCodeView,
|
|
15042
15427
|
{
|
|
15043
15428
|
state,
|
|
15044
|
-
height: Math.max(10, terminalSize.rows),
|
|
15045
15429
|
cols: terminalSize.cols,
|
|
15046
15430
|
input,
|
|
15047
15431
|
cursorPos,
|
|
15048
|
-
onInput,
|
|
15049
|
-
onScrollClamp: (distance) => onInput(`/mc scroll-set ${distance}`),
|
|
15050
15432
|
permIdx
|
|
15051
15433
|
}
|
|
15052
15434
|
) : null,
|
|
15053
15435
|
state.mode === "spotify" ? /* @__PURE__ */ jsx(SpotifyBody, { activeIdx: spotifyIdx, nowPlaying: spotifyNow, status: spotifyStatus, volume: spotifyVolume, albumArtAnsi: spotifyArtAnsi }) : null,
|
|
15054
15436
|
state.mode === "menu" ? /* @__PURE__ */ jsx(MenuBody, { menuIdx }) : null,
|
|
15055
|
-
state.mode === "coding" ? /* @__PURE__ */ jsx(CodingBody, { state, maxDynamicLines: Math.max(3, terminalSize.rows - 14) }) : null,
|
|
15437
|
+
state.mode === "coding" ? /* @__PURE__ */ jsx(CodingBody, { state, maxDynamicLines: Math.min(12, Math.max(3, terminalSize.rows - 14)) }) : null,
|
|
15056
15438
|
state.mode === "workspace" ? /* @__PURE__ */ jsx(WorkspaceBody, { state, gitCursor, height: Math.max(8, terminalSize.rows - 6), cols: terminalSize.cols, onInput }) : null,
|
|
15057
|
-
state.mode === "chat" ?
|
|
15439
|
+
state.mode === "chat" ? (
|
|
15440
|
+
// Live-region cap: only the newest few lines of the streaming message
|
|
15441
|
+
// repaint per frame — finalized content is already in <Static>. A
|
|
15442
|
+
// near-full-screen live region was rewritten every 60ms frame, which
|
|
15443
|
+
// read as a visible "on-off" flicker on long chats.
|
|
15444
|
+
/* @__PURE__ */ jsx(ChatBody, { state, maxDynamicLines: Math.min(12, Math.max(3, terminalSize.rows - 14)) })
|
|
15445
|
+
) : null,
|
|
15058
15446
|
state.permissionPrompt && state.mode !== "mercury-code" && /* @__PURE__ */ jsx(PermPromptView, { prompt: state.permissionPrompt, activeIdx: permIdx }),
|
|
15059
15447
|
showInput && state.mode !== "mercury-code" && /* @__PURE__ */ jsx(
|
|
15060
15448
|
InputBox,
|
|
@@ -16065,120 +16453,6 @@ function InputBox({
|
|
|
16065
16453
|
/* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: inWorkspace ? "Tab switch panels \xB7 Ctrl+J chat \xB7 Ctrl+P Plan \xB7 Ctrl+X Execute \xB7 Esc back/exit" : inCoding ? "Coding chat active. Ctrl+P Plan \xB7 Ctrl+X Execute." : "Enter send \xB7 Ctrl+N newline" }) })
|
|
16066
16454
|
] });
|
|
16067
16455
|
}
|
|
16068
|
-
var mercuryTranscriptIndex = /* @__PURE__ */ new Map();
|
|
16069
|
-
var MERCURY_INDEX_MAX_ENTRIES = 4096;
|
|
16070
|
-
var MERCURY_LINES_MAX_ENTRIES = 64;
|
|
16071
|
-
var MERCURY_LINES_MAX_LINES = 6e3;
|
|
16072
|
-
var mercuryLineCacheEntries = 0;
|
|
16073
|
-
var mercuryLineCacheLines = 0;
|
|
16074
|
-
function mercuryCacheKey(msg, width) {
|
|
16075
|
-
return `${msg.id}|${msg.role}|${msg.content.length}|${msg.timestamp}|${msg.streaming ? 1 : 0}|${width}`;
|
|
16076
|
-
}
|
|
16077
|
-
function evictMercuryLineCache() {
|
|
16078
|
-
while (mercuryLineCacheEntries > MERCURY_LINES_MAX_ENTRIES || mercuryLineCacheLines > MERCURY_LINES_MAX_LINES) {
|
|
16079
|
-
const oldest = mercuryTranscriptIndex.keys().next().value;
|
|
16080
|
-
if (oldest === void 0) break;
|
|
16081
|
-
const entry = mercuryTranscriptIndex.get(oldest);
|
|
16082
|
-
if (entry.lines) {
|
|
16083
|
-
mercuryLineCacheLines -= entry.lines.length;
|
|
16084
|
-
mercuryLineCacheEntries -= 1;
|
|
16085
|
-
entry.lines = void 0;
|
|
16086
|
-
mercuryTranscriptIndex.delete(oldest);
|
|
16087
|
-
mercuryTranscriptIndex.set(oldest, entry);
|
|
16088
|
-
continue;
|
|
16089
|
-
}
|
|
16090
|
-
if (mercuryLineCacheEntries === 0 && mercuryLineCacheLines === 0) break;
|
|
16091
|
-
break;
|
|
16092
|
-
}
|
|
16093
|
-
while (mercuryTranscriptIndex.size > MERCURY_INDEX_MAX_ENTRIES) {
|
|
16094
|
-
const oldest = mercuryTranscriptIndex.keys().next().value;
|
|
16095
|
-
if (oldest === void 0) break;
|
|
16096
|
-
const entry = mercuryTranscriptIndex.get(oldest);
|
|
16097
|
-
if (entry.lines) {
|
|
16098
|
-
mercuryLineCacheLines -= entry.lines.length;
|
|
16099
|
-
mercuryLineCacheEntries -= 1;
|
|
16100
|
-
}
|
|
16101
|
-
mercuryTranscriptIndex.delete(oldest);
|
|
16102
|
-
}
|
|
16103
|
-
}
|
|
16104
|
-
function getMercuryEntry(msg, width, wantLines) {
|
|
16105
|
-
const key = mercuryCacheKey(msg, width);
|
|
16106
|
-
const existing = mercuryTranscriptIndex.get(msg.id);
|
|
16107
|
-
if (existing && existing.key === key) {
|
|
16108
|
-
if (existing.lines) {
|
|
16109
|
-
mercuryTranscriptIndex.delete(msg.id);
|
|
16110
|
-
mercuryTranscriptIndex.set(msg.id, existing);
|
|
16111
|
-
return existing;
|
|
16112
|
-
}
|
|
16113
|
-
if (wantLines) {
|
|
16114
|
-
const lines2 = buildMercuryMessageLines(msg, width);
|
|
16115
|
-
existing.lines = lines2;
|
|
16116
|
-
mercuryLineCacheEntries += 1;
|
|
16117
|
-
mercuryLineCacheLines += lines2.length;
|
|
16118
|
-
evictMercuryLineCache();
|
|
16119
|
-
}
|
|
16120
|
-
return existing;
|
|
16121
|
-
}
|
|
16122
|
-
const lines = buildMercuryMessageLines(msg, width);
|
|
16123
|
-
const entry = { key, count: lines.length };
|
|
16124
|
-
if (existing?.lines) {
|
|
16125
|
-
mercuryLineCacheLines -= existing.lines.length;
|
|
16126
|
-
mercuryLineCacheEntries -= 1;
|
|
16127
|
-
}
|
|
16128
|
-
entry.lines = lines;
|
|
16129
|
-
mercuryLineCacheEntries += 1;
|
|
16130
|
-
mercuryLineCacheLines += lines.length;
|
|
16131
|
-
mercuryTranscriptIndex.set(msg.id, entry);
|
|
16132
|
-
evictMercuryLineCache();
|
|
16133
|
-
return entry;
|
|
16134
|
-
}
|
|
16135
|
-
function buildMercuryTranscriptIndex(messages, width, brandLines = []) {
|
|
16136
|
-
const msgs = [];
|
|
16137
|
-
const counts = [];
|
|
16138
|
-
let total = brandLines.length;
|
|
16139
|
-
for (const msg of messages) {
|
|
16140
|
-
if (typeof msg.content !== "string") continue;
|
|
16141
|
-
const entry = getMercuryEntry(msg, width, false);
|
|
16142
|
-
counts.push(entry.count);
|
|
16143
|
-
msgs.push(msg);
|
|
16144
|
-
total += entry.count;
|
|
16145
|
-
}
|
|
16146
|
-
return { msgs, counts, total, brandLines };
|
|
16147
|
-
}
|
|
16148
|
-
function renderMercuryTranscriptWindow(index, startRow, endRow, width) {
|
|
16149
|
-
const out2 = [];
|
|
16150
|
-
const brandCount = index.brandLines.length;
|
|
16151
|
-
if (startRow < brandCount && endRow > 0) {
|
|
16152
|
-
out2.push(...index.brandLines.slice(startRow, Math.min(brandCount, endRow)));
|
|
16153
|
-
}
|
|
16154
|
-
let offset = brandCount;
|
|
16155
|
-
for (let i = 0; i < index.msgs.length; i++) {
|
|
16156
|
-
const count = index.counts[i];
|
|
16157
|
-
const msgStart = offset;
|
|
16158
|
-
const msgEnd = offset + count;
|
|
16159
|
-
offset = msgEnd;
|
|
16160
|
-
if (msgEnd <= startRow || msgStart >= endRow) continue;
|
|
16161
|
-
const entry = getMercuryEntry(index.msgs[i], width, true);
|
|
16162
|
-
const lines = entry.lines ?? [];
|
|
16163
|
-
const from = Math.max(0, startRow - msgStart);
|
|
16164
|
-
const to = Math.max(0, Math.min(count, endRow - msgStart));
|
|
16165
|
-
if (to > from) out2.push(...lines.slice(from, to));
|
|
16166
|
-
}
|
|
16167
|
-
return out2;
|
|
16168
|
-
}
|
|
16169
|
-
function renderMercuryTranscriptRange(index, tail, startRow, endRow, width) {
|
|
16170
|
-
const finalizedTotal = index.total;
|
|
16171
|
-
const out2 = [];
|
|
16172
|
-
if (startRow < finalizedTotal && endRow > 0) {
|
|
16173
|
-
out2.push(...renderMercuryTranscriptWindow(index, startRow, Math.min(endRow, finalizedTotal), width));
|
|
16174
|
-
}
|
|
16175
|
-
if (endRow > finalizedTotal && tail.length > 0) {
|
|
16176
|
-
const from = Math.max(0, startRow - finalizedTotal);
|
|
16177
|
-
const to = Math.min(tail.length, endRow - finalizedTotal);
|
|
16178
|
-
if (to > from) out2.push(...tail.slice(from, to));
|
|
16179
|
-
}
|
|
16180
|
-
return out2;
|
|
16181
|
-
}
|
|
16182
16456
|
var CODE_HINTS = [
|
|
16183
16457
|
["/code auto", "plan & build automatically \u2014 the default", ""],
|
|
16184
16458
|
["/code plan", "analyze & propose before coding", "ctrl+p"],
|
|
@@ -16250,7 +16524,7 @@ function PlanProgressView({ steps }) {
|
|
|
16250
16524
|
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 2, flexShrink: 0, children: rows });
|
|
16251
16525
|
}
|
|
16252
16526
|
var STREAM_TAIL_CHARS = 8 * 1024;
|
|
16253
|
-
var STREAM_TAIL_MAX_LINES =
|
|
16527
|
+
var STREAM_TAIL_MAX_LINES = 12;
|
|
16254
16528
|
var WORDMARK_LIGHT_BG = (() => {
|
|
16255
16529
|
const fgBg = process.env.COLORFGBG;
|
|
16256
16530
|
if (!fgBg) return false;
|
|
@@ -16390,14 +16664,70 @@ function MercuryCodeExitConfirm({ boxWidth }) {
|
|
|
16390
16664
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Enter/Y exit \xB7 Esc/N stay \xB7 Ctrl+D force" })
|
|
16391
16665
|
] }) });
|
|
16392
16666
|
}
|
|
16667
|
+
var MERCURY_BRAND_ITEM_KEY = "__mercury_code_brand__";
|
|
16668
|
+
function MercuryTranscriptRow({ line }) {
|
|
16669
|
+
const roleColor = line.role === "user" ? "yellow" : line.role === "agent" ? "cyan" : "gray";
|
|
16670
|
+
if (line.kind === "brand") {
|
|
16671
|
+
return /* @__PURE__ */ jsx(Box, { children: line.accent && line.accent.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
16672
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: WORDMARK_COLORS.mercury, children: line.text }),
|
|
16673
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: WORDMARK_COLORS.code, children: line.accent })
|
|
16674
|
+
] }) : /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: line.text }) });
|
|
16675
|
+
}
|
|
16676
|
+
if (line.kind === "spacer") {
|
|
16677
|
+
return /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsx(Text, { children: " " }) });
|
|
16678
|
+
}
|
|
16679
|
+
if (line.kind === "header") {
|
|
16680
|
+
return /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsxs(Text, { bold: true, color: roleColor, children: [
|
|
16681
|
+
"\u25CF ",
|
|
16682
|
+
line.text
|
|
16683
|
+
] }) });
|
|
16684
|
+
}
|
|
16685
|
+
if (line.kind === "code-label") {
|
|
16686
|
+
return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
|
|
16687
|
+
/* @__PURE__ */ jsx(Text, { color: roleColor, children: "\u2502 " }),
|
|
16688
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
16689
|
+
"\u250C\u2500 ",
|
|
16690
|
+
line.text
|
|
16691
|
+
] })
|
|
16692
|
+
] });
|
|
16693
|
+
}
|
|
16694
|
+
if (line.kind === "code") {
|
|
16695
|
+
const highlighted = highlightCodeBlock(line.text, line.lang)[0] ?? line.text;
|
|
16696
|
+
return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
|
|
16697
|
+
/* @__PURE__ */ jsx(Text, { color: roleColor, children: "\u2502 " }),
|
|
16698
|
+
/* @__PURE__ */ jsx(Text, { children: highlighted || " " })
|
|
16699
|
+
] });
|
|
16700
|
+
}
|
|
16701
|
+
if (line.kind === "system") {
|
|
16702
|
+
const complete = line.text.startsWith("Task complete");
|
|
16703
|
+
return /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsxs(Text, { color: complete ? "green" : "gray", bold: complete, children: [
|
|
16704
|
+
"\u2500 ",
|
|
16705
|
+
line.text || " "
|
|
16706
|
+
] }) });
|
|
16707
|
+
}
|
|
16708
|
+
if (line.kind === "file") {
|
|
16709
|
+
return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
|
|
16710
|
+
/* @__PURE__ */ jsx(Text, { color: "green", children: " \u21B3 " }),
|
|
16711
|
+
/* @__PURE__ */ jsx(Text, { children: line.text })
|
|
16712
|
+
] });
|
|
16713
|
+
}
|
|
16714
|
+
return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
|
|
16715
|
+
/* @__PURE__ */ jsx(Text, { color: roleColor, children: "\u2502 " }),
|
|
16716
|
+
/* @__PURE__ */ jsx(Text, { children: line.text || " " })
|
|
16717
|
+
] });
|
|
16718
|
+
}
|
|
16719
|
+
function MercuryBrandBlock({ brandLines }) {
|
|
16720
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexShrink: 0, children: brandLines.map((line) => /* @__PURE__ */ jsx(MercuryTranscriptRow, { line }, line.key)) });
|
|
16721
|
+
}
|
|
16722
|
+
function MercuryMessageBlock({ message, width }) {
|
|
16723
|
+
const lines = buildMercuryMessageLines(message, width);
|
|
16724
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexShrink: 0, children: lines.map((line) => /* @__PURE__ */ jsx(MercuryTranscriptRow, { line }, line.key)) });
|
|
16725
|
+
}
|
|
16393
16726
|
function MercuryCodeView({
|
|
16394
16727
|
state,
|
|
16395
|
-
height,
|
|
16396
16728
|
cols,
|
|
16397
|
-
onInput,
|
|
16398
16729
|
input,
|
|
16399
16730
|
cursorPos,
|
|
16400
|
-
onScrollClamp,
|
|
16401
16731
|
permIdx
|
|
16402
16732
|
}) {
|
|
16403
16733
|
const mc = state.mercuryCode;
|
|
@@ -16408,141 +16738,59 @@ function MercuryCodeView({
|
|
|
16408
16738
|
[state.chatMessages]
|
|
16409
16739
|
);
|
|
16410
16740
|
const streamingMessage = state.chatMessages.find((m) => m.streaming && !m.id.startsWith("heartbeat-"));
|
|
16411
|
-
const
|
|
16412
|
-
() =>
|
|
16413
|
-
[
|
|
16741
|
+
const streamTail = React.useMemo(
|
|
16742
|
+
() => streamingMessage ? buildStreamTailLines(streamingMessage, contentWidth, STREAM_TAIL_CHARS, STREAM_TAIL_MAX_LINES) : [],
|
|
16743
|
+
[streamingMessage, contentWidth]
|
|
16414
16744
|
);
|
|
16415
|
-
const totalLines = transcriptIndex.total;
|
|
16416
16745
|
if (!mc) {
|
|
16417
16746
|
return /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "Mercury Code is not active. Type /code to enter." }) });
|
|
16418
16747
|
}
|
|
16419
|
-
const
|
|
16420
|
-
|
|
16421
|
-
|
|
16422
|
-
|
|
16423
|
-
const liveRows = liveVisible ? 1 + Math.min(2, state.toolSteps.filter((s) => s.status === "done").slice(-2).length) + (state.subAgents.some((a) => a.status === "running") ? 1 + Math.min(4, state.subAgents.filter((a) => a.status === "running").length) : 0) : 0;
|
|
16424
|
-
const statusRows = 1;
|
|
16425
|
-
const planRows = state.planProgress && state.planProgress.length > 0 ? Math.min(7, state.planProgress.length + 1) : 0;
|
|
16426
|
-
const promptRows = state.permissionPrompt ? 2 + (state.permissionPrompt.options?.length ?? 0) : 0;
|
|
16427
|
-
const transcriptHeight = Math.max(3, height - inputRows - 1 - liveRows - confirmRows - planRows - promptRows);
|
|
16428
|
-
const streamTail = React.useMemo(() => {
|
|
16429
|
-
if (!streamingMessage) return [];
|
|
16430
|
-
const content = streamingMessage.content;
|
|
16431
|
-
const tail = content.length > STREAM_TAIL_CHARS ? content.slice(-STREAM_TAIL_CHARS) : content;
|
|
16432
|
-
const lines = [{ key: `${streamingMessage.id}:hdr`, kind: "header", role: streamingMessage.role, text: "MERCURY" }];
|
|
16433
|
-
for (const row of tail.split("\n")) {
|
|
16434
|
-
for (const chunk of wrapMercuryText(row, contentWidth)) {
|
|
16435
|
-
lines.push({ key: `${streamingMessage.id}:${lines.length}`, kind: "text", role: streamingMessage.role, text: chunk });
|
|
16436
|
-
if (lines.length > STREAM_TAIL_MAX_LINES) {
|
|
16437
|
-
lines.splice(1, lines.length - STREAM_TAIL_MAX_LINES);
|
|
16438
|
-
}
|
|
16439
|
-
}
|
|
16440
|
-
}
|
|
16441
|
-
return lines;
|
|
16442
|
-
}, [streamingMessage, contentWidth]);
|
|
16443
|
-
const totalWithTail = totalLines + streamTail.length;
|
|
16444
|
-
const previousLineCount = React.useRef(totalWithTail);
|
|
16445
|
-
const anchoredOffset = anchorViewportDistance(mc.scrollOffset, previousLineCount.current, totalWithTail);
|
|
16446
|
-
const preliminaryViewport = getViewportWindow(totalWithTail, transcriptHeight, anchoredOffset);
|
|
16447
|
-
const wordmarkOnScreen = preliminaryViewport.start < brandLines.length;
|
|
16448
|
-
const effectiveViewportRows = wordmarkOnScreen ? transcriptHeight : transcriptHeight - 1;
|
|
16449
|
-
const viewport = getViewportWindow(totalWithTail, effectiveViewportRows, anchoredOffset);
|
|
16450
|
-
const adjustedVisible = renderMercuryTranscriptRange(transcriptIndex, streamTail, viewport.start, viewport.end, contentWidth);
|
|
16451
|
-
React.useEffect(() => {
|
|
16452
|
-
previousLineCount.current = totalWithTail;
|
|
16453
|
-
if (onScrollClamp && viewport.distanceFromBottom !== mc.scrollOffset) {
|
|
16454
|
-
onScrollClamp(viewport.distanceFromBottom);
|
|
16455
|
-
}
|
|
16456
|
-
}, [totalWithTail, onScrollClamp, mc.scrollOffset, viewport.distanceFromBottom]);
|
|
16748
|
+
const staticItems = [
|
|
16749
|
+
MERCURY_BRAND_ITEM_KEY,
|
|
16750
|
+
...finalizedMessages.slice(-MAX_STATIC_MESSAGES)
|
|
16751
|
+
];
|
|
16457
16752
|
const mode = state.programmingMode;
|
|
16458
16753
|
const modeLabel = mode === "execute" ? "EXECUTE" : mode === "plan" ? "PLAN" : mode === "auto" ? "AUTO" : "CHAT";
|
|
16459
|
-
const modeColor = mode === "execute" ? "green" : mode === "plan" ? "yellow" : "cyan";
|
|
16460
16754
|
const git2 = mc.git;
|
|
16461
|
-
const
|
|
16462
|
-
|
|
16463
|
-
|
|
16755
|
+
const hasGit = git2.branch !== "no-git" && git2.branch !== "not-a-git-repo";
|
|
16756
|
+
const rightSegs = [{ text: mc.dirName, color: "cyan" }];
|
|
16757
|
+
if (hasGit) {
|
|
16758
|
+
const gitBits = [`\u2387 ${git2.branch}`];
|
|
16464
16759
|
if (git2.ahead > 0) gitBits.push(`\u2191${git2.ahead}`);
|
|
16465
16760
|
if (git2.behind > 0) gitBits.push(`\u2193${git2.behind}`);
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
|
|
16469
|
-
|
|
16470
|
-
|
|
16471
|
-
|
|
16761
|
+
if (git2.dirty > 0) gitBits.push(`\xB1${git2.dirty}`);
|
|
16762
|
+
rightSegs.push({ text: gitBits.join(" "), color: "blue" });
|
|
16763
|
+
}
|
|
16764
|
+
rightSegs.push({ text: modeLabel, color: mode === "execute" ? "green" : mode === "plan" ? "yellow" : "cyan" });
|
|
16765
|
+
if (state.provider) rightSegs.push({ text: `${state.provider.name} ${state.provider.model}`, color: "magenta" });
|
|
16766
|
+
if (state.tokenInfo) rightSegs.push({ text: `\u26A1 ${Math.round(state.tokenInfo.percentage)}%`, color: "green" });
|
|
16767
|
+
const SEP = " \xB7 ";
|
|
16768
|
+
let segWidth = 4;
|
|
16769
|
+
rightSegs.forEach((seg, i) => {
|
|
16770
|
+
segWidth += seg.text.length + (i > 0 ? SEP.length : 0);
|
|
16771
|
+
});
|
|
16772
|
+
while (segWidth > cols - 2 && rightSegs.length > 1) {
|
|
16773
|
+
const removed = rightSegs.pop();
|
|
16774
|
+
segWidth -= removed.text.length + SEP.length;
|
|
16472
16775
|
}
|
|
16473
|
-
|
|
16474
|
-
const
|
|
16475
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column",
|
|
16476
|
-
|
|
16477
|
-
|
|
16478
|
-
|
|
16479
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
16480
|
-
" v",
|
|
16481
|
-
state.version
|
|
16482
|
-
] }),
|
|
16483
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
|
|
16484
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: mc.dirName })
|
|
16485
|
-
] }),
|
|
16486
|
-
/* @__PURE__ */ jsx(Box, { flexDirection: "column", height: effectiveViewportRows, overflow: "hidden", children: adjustedVisible.length === 0 && streamTail.length === 0 && totalWithTail === 0 ? /* @__PURE__ */ jsx(MercuryCodeHints, { cols }) : adjustedVisible.map((line) => {
|
|
16487
|
-
const roleColor = line.role === "user" ? "yellow" : line.role === "agent" ? "cyan" : "gray";
|
|
16488
|
-
if (line.kind === "brand") {
|
|
16489
|
-
return /* @__PURE__ */ jsx(Box, { children: line.accent && line.accent.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
16490
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: WORDMARK_COLORS.mercury, children: line.text }),
|
|
16491
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: WORDMARK_COLORS.code, children: line.accent })
|
|
16492
|
-
] }) : /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: line.text }) }, line.key);
|
|
16493
|
-
}
|
|
16494
|
-
if (line.kind === "spacer") {
|
|
16495
|
-
return /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsx(Text, { children: " " }) }, line.key);
|
|
16496
|
-
}
|
|
16497
|
-
if (line.kind === "header") {
|
|
16498
|
-
return /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsxs(Text, { bold: true, color: roleColor, children: [
|
|
16499
|
-
"\u25CF ",
|
|
16500
|
-
line.text
|
|
16501
|
-
] }) }, line.key);
|
|
16502
|
-
}
|
|
16503
|
-
if (line.kind === "code-label") {
|
|
16504
|
-
return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
|
|
16505
|
-
/* @__PURE__ */ jsx(Text, { color: roleColor, children: "\u2502 " }),
|
|
16506
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
16507
|
-
"\u250C\u2500 ",
|
|
16508
|
-
line.text
|
|
16509
|
-
] })
|
|
16510
|
-
] }, line.key);
|
|
16511
|
-
}
|
|
16512
|
-
if (line.kind === "code") {
|
|
16513
|
-
const highlighted = highlightCodeBlock(line.text, line.lang)[0] ?? line.text;
|
|
16514
|
-
return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
|
|
16515
|
-
/* @__PURE__ */ jsx(Text, { color: roleColor, children: "\u2502 " }),
|
|
16516
|
-
/* @__PURE__ */ jsx(Text, { children: highlighted || " " })
|
|
16517
|
-
] }, line.key);
|
|
16518
|
-
}
|
|
16519
|
-
if (line.kind === "system") {
|
|
16520
|
-
const complete = line.text.startsWith("Task complete");
|
|
16521
|
-
return /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsxs(Text, { color: complete ? "green" : "gray", bold: complete, children: [
|
|
16522
|
-
"\u2500 ",
|
|
16523
|
-
line.text || " "
|
|
16524
|
-
] }) }, line.key);
|
|
16525
|
-
}
|
|
16526
|
-
if (line.kind === "file") {
|
|
16527
|
-
return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
|
|
16528
|
-
/* @__PURE__ */ jsx(Text, { color: "green", children: " \u21B3 " }),
|
|
16529
|
-
/* @__PURE__ */ jsx(Text, { children: line.text })
|
|
16530
|
-
] }, line.key);
|
|
16531
|
-
}
|
|
16532
|
-
return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
|
|
16533
|
-
/* @__PURE__ */ jsx(Text, { color: roleColor, children: "\u2502 " }),
|
|
16534
|
-
/* @__PURE__ */ jsx(Text, { children: line.text || " " })
|
|
16535
|
-
] }, line.key);
|
|
16536
|
-
}) }),
|
|
16776
|
+
const leftHint = mc.exitConfirm || state.permissionPrompt ? "" : state.isThinking || state.toolSteps.some((s) => s.status === "running") || state.subAgents.some((a) => a.status === "running") ? "" : "\u21B5 send \xB7 /help";
|
|
16777
|
+
const showHints = finalizedMessages.length === 0 && streamTail.length === 0;
|
|
16778
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [
|
|
16779
|
+
/* @__PURE__ */ jsx(Static, { items: staticItems, itemKey: staticItemKey, children: (item) => typeof item === "string" ? /* @__PURE__ */ jsx(MercuryBrandBlock, { brandLines }, item) : /* @__PURE__ */ jsx(MercuryMessageBlock, { message: item, width: contentWidth }, item.id) }),
|
|
16780
|
+
streamTail.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexShrink: 0, children: streamTail.map((line) => /* @__PURE__ */ jsx(MercuryTranscriptRow, { line }, line.key)) }),
|
|
16781
|
+
showHints && /* @__PURE__ */ jsx(MercuryCodeHints, { cols }),
|
|
16537
16782
|
state.planProgress && state.planProgress.length > 0 && /* @__PURE__ */ jsx(PlanProgressView, { steps: state.planProgress }),
|
|
16538
16783
|
/* @__PURE__ */ jsx(MercuryLiveFeedback, { state }),
|
|
16539
16784
|
state.permissionPrompt && /* @__PURE__ */ jsx(PermPromptView, { prompt: state.permissionPrompt, activeIdx: permIdx ?? 0 }),
|
|
16540
16785
|
mc.exitConfirm && /* @__PURE__ */ jsx(MercuryCodeExitConfirm, { boxWidth: Math.max(40, cols - 4) }),
|
|
16541
16786
|
/* @__PURE__ */ jsx(MercuryCodeInput, { input: input ?? "", cursorPos: cursorPos ?? 0, mode: state.programmingMode, boxWidth: Math.max(40, cols - 4) }),
|
|
16542
|
-
/* @__PURE__ */ jsxs(Box, { paddingX:
|
|
16543
|
-
|
|
16787
|
+
/* @__PURE__ */ jsxs(Box, { height: 1, overflow: "hidden", paddingX: 2, flexShrink: 0, children: [
|
|
16788
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: leftHint }),
|
|
16544
16789
|
/* @__PURE__ */ jsx(Spacer, {}),
|
|
16545
|
-
/* @__PURE__ */ jsx(Text, {
|
|
16790
|
+
/* @__PURE__ */ jsx(Text, { wrap: "truncate-end", children: rightSegs.map((seg, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
16791
|
+
i > 0 && /* @__PURE__ */ jsx(Text, { color: "gray", children: SEP }),
|
|
16792
|
+
/* @__PURE__ */ jsx(Text, { color: seg.color, children: seg.text })
|
|
16793
|
+
] }, seg.text)) })
|
|
16546
16794
|
] })
|
|
16547
16795
|
] });
|
|
16548
16796
|
}
|
|
@@ -17790,9 +18038,10 @@ size: ${sizeStr}`;
|
|
|
17790
18038
|
}
|
|
17791
18039
|
/**
|
|
17792
18040
|
* Enter Mercury Code: full-screen coding TUI bound to `dir`.
|
|
17793
|
-
* Switches to plan mode by default (analyze-first).
|
|
17794
|
-
*
|
|
17795
|
-
*
|
|
18041
|
+
* Switches to plan mode by default (analyze-first). The transcript prints
|
|
18042
|
+
* into the terminal's native scrollback (<Static>), so scrolling and
|
|
18043
|
+
* drag-selection are terminal-native: mouse reporting stays OFF — it would
|
|
18044
|
+
* capture wheel/drag events and break both.
|
|
17796
18045
|
*/
|
|
17797
18046
|
enterMercuryCode(dir, version) {
|
|
17798
18047
|
const target = path.resolve(dir.replace(/^~(?=$|\/)/, process.env.HOME || "~"));
|
|
@@ -17817,10 +18066,6 @@ size: ${sizeStr}`;
|
|
|
17817
18066
|
programmingMode: "auto",
|
|
17818
18067
|
exitEscArmed: false
|
|
17819
18068
|
});
|
|
17820
|
-
this.setMouseEnabled(true, (ev) => {
|
|
17821
|
-
if (ev.wheel === "up") this.scrollMercuryCode(3);
|
|
17822
|
-
else if (ev.wheel === "down") this.scrollMercuryCode(-3);
|
|
17823
|
-
});
|
|
17824
18069
|
try {
|
|
17825
18070
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
17826
18071
|
} catch {
|
|
@@ -18319,6 +18564,7 @@ import { Bot, InputFile, InlineKeyboard } from "grammy";
|
|
|
18319
18564
|
import { autoRetry } from "@grammyjs/auto-retry";
|
|
18320
18565
|
init_config();
|
|
18321
18566
|
init_logger();
|
|
18567
|
+
init_markdown();
|
|
18322
18568
|
init_registry();
|
|
18323
18569
|
init_store();
|
|
18324
18570
|
init_loader();
|
|
@@ -19784,6 +20030,7 @@ import fs4 from "fs";
|
|
|
19784
20030
|
import path4 from "path";
|
|
19785
20031
|
init_config();
|
|
19786
20032
|
init_logger();
|
|
20033
|
+
init_markdown();
|
|
19787
20034
|
init_redact();
|
|
19788
20035
|
init_jsonrpc();
|
|
19789
20036
|
init_binary();
|
|
@@ -20704,6 +20951,7 @@ import {
|
|
|
20704
20951
|
} from "discord.js";
|
|
20705
20952
|
init_config();
|
|
20706
20953
|
init_logger();
|
|
20954
|
+
init_markdown();
|
|
20707
20955
|
var MAX_MESSAGE_LENGTH3 = 2e3;
|
|
20708
20956
|
var DISCORD_DM_PREFIX = "discord:dm";
|
|
20709
20957
|
var DISCORD_GUILD_PREFIX = "discord";
|
|
@@ -21740,6 +21988,7 @@ import path6 from "path";
|
|
|
21740
21988
|
import { App } from "@slack/bolt";
|
|
21741
21989
|
init_config();
|
|
21742
21990
|
init_logger();
|
|
21991
|
+
init_markdown();
|
|
21743
21992
|
var SLACK_DM_PREFIX = "slack:dm";
|
|
21744
21993
|
var SlackChannel = class _SlackChannel extends BaseChannel {
|
|
21745
21994
|
constructor(config2) {
|
|
@@ -23736,6 +23985,10 @@ var IMPLEMENTATION_PATTERN = new RegExp(
|
|
|
23736
23985
|
);
|
|
23737
23986
|
var PURE_CONVERSATION_PATTERN = /^(thanks|thank you|thx|ty|cool|nice|great|awesome|perfect|ok|okay|got it|understood|bye|hi|hey|hello|lol|lgtm|sounds good|well done)[\s!,.?]*$/i;
|
|
23738
23987
|
var QUESTION_PATTERN = /^(what|whats|what's|why|how|when|where|who|which|explain|describe|tell me|walk me through|compare|list)\b/i;
|
|
23988
|
+
var TEXT_DELIVERABLE_PATTERN = /\b(poems?|poetry|haiku|stor(y|ies)|essay|essays|lyrics|song|joke|jokes|slogans?|taglines?|e-?mails?|letters?|blog|blogs|articles?|summaries|summari[sz]e|translat\w*|captions?|tweets?|outlines?|brainstorm|names?|titles?|description|descriptions)\b/i;
|
|
23989
|
+
function isTextDeliverableRequest(taskText) {
|
|
23990
|
+
return TEXT_DELIVERABLE_PATTERN.test(taskText);
|
|
23991
|
+
}
|
|
23739
23992
|
function shouldForceExecuteContinuation(input) {
|
|
23740
23993
|
for (const toolName of input.toolsUsed) {
|
|
23741
23994
|
if (EXECUTE_PAUSE_TOOLS.has(toolName)) return false;
|
|
@@ -26632,7 +26885,7 @@ Per-provider:
|
|
|
26632
26885
|
const preGuardText = (result.text || "").trim() || "(no text response)";
|
|
26633
26886
|
this.markProgress("Finalizing response...");
|
|
26634
26887
|
this.pushLiveActivity("Finalizing response");
|
|
26635
|
-
while (this.programmingMode.isExecute() && !
|
|
26888
|
+
while (!loopAbortController.signal.aborted && (this.programmingMode.isExecute() || this.programmingMode.getState() === "off" && msg.channelType !== "internal" && !isTextDeliverableRequest(msg.content)) && executeGuardRounds < (narrationSecondWind ? MAX_EXECUTE_CONTINUATIONS * 2 : MAX_EXECUTE_CONTINUATIONS) && !responseAsksUser(result.text || "") && shouldForceExecuteContinuation({
|
|
26636
26889
|
taskText: msg.content,
|
|
26637
26890
|
hasApprovedPlan: this.programmingMode.getLastPlan() != null,
|
|
26638
26891
|
toolsUsed: executeTurnToolsUsed,
|
|
@@ -26848,7 +27101,7 @@ This is real, current state. Use it: create/edit files HERE with your tools.`
|
|
|
26848
27101
|
break;
|
|
26849
27102
|
}
|
|
26850
27103
|
}
|
|
26851
|
-
if (!loopAbortController.signal.aborted && turnEnd() === "text-stop" && this.programmingMode.isExecute() && verificationContinuations < MAX_VERIFICATION_CONTINUATIONS && shouldRequireVerification({
|
|
27104
|
+
if (!loopAbortController.signal.aborted && turnEnd() === "text-stop" && (this.programmingMode.isExecute() || this.programmingMode.getState() === "off" && msg.channelType !== "internal") && verificationContinuations < MAX_VERIFICATION_CONTINUATIONS && shouldRequireVerification({
|
|
26852
27105
|
taskText: msg.content,
|
|
26853
27106
|
hasApprovedPlan: this.programmingMode.getLastPlan() != null,
|
|
26854
27107
|
commandsRun: executeCommandsRun,
|
|
@@ -32374,6 +32627,15 @@ init_config();
|
|
|
32374
32627
|
init_logger();
|
|
32375
32628
|
import { existsSync as existsSync21, readFileSync as readFileSync16, writeFileSync as writeFileSync15 } from "fs";
|
|
32376
32629
|
import { join as join19 } from "path";
|
|
32630
|
+
|
|
32631
|
+
// src/utils/format.ts
|
|
32632
|
+
function formatNumber(value) {
|
|
32633
|
+
const n = Number(value);
|
|
32634
|
+
if (!Number.isFinite(n)) return "0";
|
|
32635
|
+
return n.toLocaleString("en-US", { useGrouping: true });
|
|
32636
|
+
}
|
|
32637
|
+
|
|
32638
|
+
// src/utils/tokens.ts
|
|
32377
32639
|
var TOKEN_FILE = "token-usage.json";
|
|
32378
32640
|
function safeNumber(value) {
|
|
32379
32641
|
if (value === null || value === void 0) return 0;
|
|
@@ -32499,7 +32761,7 @@ var TokenBudget = class {
|
|
|
32499
32761
|
const used = this.sanitizeCount(this.dailyUsed);
|
|
32500
32762
|
const pct = Math.round(this.getUsagePercentage());
|
|
32501
32763
|
const remaining = this.getRemaining();
|
|
32502
|
-
return `Token budget: ${used
|
|
32764
|
+
return `Token budget: ${formatNumber(used)} / ${formatNumber(this.dailyBudget)} used (${pct}%), ${formatNumber(remaining)} remaining`;
|
|
32503
32765
|
}
|
|
32504
32766
|
/**
|
|
32505
32767
|
* Record an estimated savings from Token Saver Mode. Updates both the
|
|
@@ -35434,12 +35696,13 @@ init_loader();
|
|
|
35434
35696
|
// src/skills/cli.ts
|
|
35435
35697
|
init_registry();
|
|
35436
35698
|
init_store();
|
|
35699
|
+
init_markdown();
|
|
35700
|
+
init_config();
|
|
35701
|
+
init_open_url();
|
|
35437
35702
|
import { existsSync as existsSync31, readFileSync as readFileSync21 } from "fs";
|
|
35438
35703
|
import { resolve as resolve16 } from "path";
|
|
35439
35704
|
import { homedir as homedir7 } from "os";
|
|
35440
35705
|
import chalk6 from "chalk";
|
|
35441
|
-
init_config();
|
|
35442
|
-
init_open_url();
|
|
35443
35706
|
function inheritedFlags(cmd) {
|
|
35444
35707
|
const opts = {};
|
|
35445
35708
|
let c = cmd;
|
|
@@ -35783,76 +36046,23 @@ function registerSkillsCommand(program2) {
|
|
|
35783
36046
|
|
|
35784
36047
|
// src/index.ts
|
|
35785
36048
|
init_daemon();
|
|
35786
|
-
init_service();
|
|
35787
36049
|
|
|
35788
|
-
// src/cli/
|
|
35789
|
-
|
|
35790
|
-
init_crash_flag();
|
|
35791
|
-
var MAX_RESTARTS = 10;
|
|
35792
|
-
var RESTART_WINDOW_MS = 6e4;
|
|
35793
|
-
var BASE_DELAY_MS = 1e3;
|
|
35794
|
-
async function runWithWatchdog(agentFn) {
|
|
35795
|
-
const restarts = [];
|
|
35796
|
-
async function attempt() {
|
|
35797
|
-
try {
|
|
35798
|
-
await agentFn();
|
|
35799
|
-
} catch (err) {
|
|
35800
|
-
const now = Date.now();
|
|
35801
|
-
restarts.push(now);
|
|
35802
|
-
const recentRestarts = restarts.filter((t) => now - t < RESTART_WINDOW_MS);
|
|
35803
|
-
const restartCount = recentRestarts.length;
|
|
35804
|
-
if (restartCount >= MAX_RESTARTS) {
|
|
35805
|
-
logger.error({ restartCount }, "Max restarts exceeded within 60s. Exiting.");
|
|
35806
|
-
writeCrashFlag({
|
|
35807
|
-
reason: `Max restarts exceeded (${restartCount} crashes in 60s). Last error: ${err instanceof Error ? err.message : String(err)}`.slice(0, 300),
|
|
35808
|
-
timestamp: Date.now()
|
|
35809
|
-
});
|
|
35810
|
-
process.stderr.write(`[mercury] FATAL: Max restarts exceeded (${restartCount} in 60s). Last error: ${err instanceof Error ? err.message : String(err)}
|
|
35811
|
-
`);
|
|
35812
|
-
process.exit(1);
|
|
35813
|
-
}
|
|
35814
|
-
const delay = BASE_DELAY_MS * Math.pow(1.25, restartCount);
|
|
35815
|
-
logger.warn({ err, restartCount, delay }, "Crash detected. Restarting with backoff...");
|
|
35816
|
-
await sleep2(delay);
|
|
35817
|
-
await attempt();
|
|
35818
|
-
}
|
|
35819
|
-
}
|
|
35820
|
-
await attempt();
|
|
35821
|
-
}
|
|
35822
|
-
function sleep2(ms) {
|
|
35823
|
-
return new Promise((resolve19) => setTimeout(resolve19, ms));
|
|
35824
|
-
}
|
|
35825
|
-
|
|
35826
|
-
// src/index.ts
|
|
35827
|
-
init_arrow_select();
|
|
35828
|
-
init_provider_models();
|
|
35829
|
-
init_token_store();
|
|
35830
|
-
init_runtime_status();
|
|
35831
|
-
|
|
35832
|
-
// src/web/server.ts
|
|
35833
|
-
init_logger();
|
|
35834
|
-
import { Hono as Hono12 } from "hono";
|
|
35835
|
-
import { createAdaptorServer } from "@hono/node-server";
|
|
35836
|
-
import { readFileSync as readFileSync30, existsSync as existsSync41, readdirSync as readdirSync8 } from "fs";
|
|
35837
|
-
import { join as join32, dirname as dirname8 } from "path";
|
|
35838
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
35839
|
-
|
|
35840
|
-
// src/web/middleware.ts
|
|
35841
|
-
import { getCookie } from "hono/cookie";
|
|
36050
|
+
// src/cli/attach.ts
|
|
36051
|
+
init_daemon();
|
|
35842
36052
|
|
|
35843
36053
|
// src/web/auth.ts
|
|
35844
36054
|
init_config();
|
|
35845
36055
|
import { compareSync, hashSync, genSaltSync } from "bcryptjs";
|
|
35846
|
-
import { readFileSync as
|
|
35847
|
-
import { join as
|
|
36056
|
+
import { readFileSync as readFileSync23, writeFileSync as writeFileSync22, existsSync as existsSync33, mkdirSync as mkdirSync22, chmodSync as chmodSync3 } from "fs";
|
|
36057
|
+
import { join as join24 } from "path";
|
|
35848
36058
|
import { randomBytes } from "crypto";
|
|
35849
36059
|
var SESSION_COOKIE = "mercury_session";
|
|
35850
36060
|
var SESSION_MAX_AGE = 7 * 24 * 60 * 60;
|
|
35851
36061
|
function getWebConfigPath() {
|
|
35852
|
-
return
|
|
36062
|
+
return join24(getMercuryHome(), "web-config.json");
|
|
35853
36063
|
}
|
|
35854
36064
|
function writeCredentialFile(path9, contents) {
|
|
35855
|
-
|
|
36065
|
+
writeFileSync22(path9, contents, { encoding: "utf-8", mode: 384 });
|
|
35856
36066
|
try {
|
|
35857
36067
|
chmodSync3(path9, 384);
|
|
35858
36068
|
} catch {
|
|
@@ -35870,15 +36080,35 @@ function getWebPort() {
|
|
|
35870
36080
|
}
|
|
35871
36081
|
return 6174;
|
|
35872
36082
|
}
|
|
36083
|
+
var ATTACH_TOKEN_FILE = "attach-token";
|
|
36084
|
+
function getAttachTokenPath() {
|
|
36085
|
+
return join24(getMercuryHome(), ATTACH_TOKEN_FILE);
|
|
36086
|
+
}
|
|
36087
|
+
function writeAttachToken() {
|
|
36088
|
+
try {
|
|
36089
|
+
const home = getMercuryHome();
|
|
36090
|
+
if (!existsSync33(home)) mkdirSync22(home, { recursive: true });
|
|
36091
|
+
writeCredentialFile(getAttachTokenPath(), randomBytes(32).toString("hex"));
|
|
36092
|
+
} catch {
|
|
36093
|
+
}
|
|
36094
|
+
}
|
|
36095
|
+
function readAttachToken() {
|
|
36096
|
+
try {
|
|
36097
|
+
const token = readFileSync23(getAttachTokenPath(), "utf-8").trim();
|
|
36098
|
+
return token.length > 0 ? token : null;
|
|
36099
|
+
} catch {
|
|
36100
|
+
return null;
|
|
36101
|
+
}
|
|
36102
|
+
}
|
|
35873
36103
|
function loadWebAuth() {
|
|
35874
36104
|
const path9 = getWebConfigPath();
|
|
35875
|
-
if (!
|
|
36105
|
+
if (!existsSync33(path9)) return null;
|
|
35876
36106
|
try {
|
|
35877
36107
|
try {
|
|
35878
36108
|
chmodSync3(path9, 384);
|
|
35879
36109
|
} catch {
|
|
35880
36110
|
}
|
|
35881
|
-
const raw =
|
|
36111
|
+
const raw = readFileSync23(path9, "utf-8");
|
|
35882
36112
|
return JSON.parse(raw);
|
|
35883
36113
|
} catch {
|
|
35884
36114
|
return null;
|
|
@@ -35886,8 +36116,8 @@ function loadWebAuth() {
|
|
|
35886
36116
|
}
|
|
35887
36117
|
function saveWebAuth(auth2) {
|
|
35888
36118
|
const dir = getMercuryHome();
|
|
35889
|
-
if (!
|
|
35890
|
-
|
|
36119
|
+
if (!existsSync33(dir)) {
|
|
36120
|
+
mkdirSync22(dir, { recursive: true });
|
|
35891
36121
|
}
|
|
35892
36122
|
writeCredentialFile(getWebConfigPath(), JSON.stringify(auth2, null, 2));
|
|
35893
36123
|
}
|
|
@@ -35951,12 +36181,12 @@ function createSessionToken() {
|
|
|
35951
36181
|
var sessions = /* @__PURE__ */ new Map();
|
|
35952
36182
|
var SESSION_FILE = "web-sessions.json";
|
|
35953
36183
|
function getSessionFilePath() {
|
|
35954
|
-
return
|
|
36184
|
+
return join24(getMercuryHome(), SESSION_FILE);
|
|
35955
36185
|
}
|
|
35956
36186
|
function persistSessions() {
|
|
35957
36187
|
try {
|
|
35958
36188
|
const dir = getMercuryHome();
|
|
35959
|
-
if (!
|
|
36189
|
+
if (!existsSync33(dir)) mkdirSync22(dir, { recursive: true });
|
|
35960
36190
|
const entries = Object.fromEntries(sessions);
|
|
35961
36191
|
writeCredentialFile(getSessionFilePath(), JSON.stringify(entries, null, 2));
|
|
35962
36192
|
} catch {
|
|
@@ -35965,8 +36195,8 @@ function persistSessions() {
|
|
|
35965
36195
|
function restoreSessions() {
|
|
35966
36196
|
try {
|
|
35967
36197
|
const path9 = getSessionFilePath();
|
|
35968
|
-
if (!
|
|
35969
|
-
const raw =
|
|
36198
|
+
if (!existsSync33(path9)) return;
|
|
36199
|
+
const raw = readFileSync23(path9, "utf-8");
|
|
35970
36200
|
const data = JSON.parse(raw);
|
|
35971
36201
|
const now = Date.now();
|
|
35972
36202
|
for (const [key, entry] of Object.entries(data)) {
|
|
@@ -36007,8 +36237,301 @@ function getSessionMaxAge() {
|
|
|
36007
36237
|
return SESSION_MAX_AGE;
|
|
36008
36238
|
}
|
|
36009
36239
|
|
|
36240
|
+
// src/cli/attach.ts
|
|
36241
|
+
function createSseParser() {
|
|
36242
|
+
let buffer = "";
|
|
36243
|
+
let eventName = "";
|
|
36244
|
+
let dataLines = [];
|
|
36245
|
+
const flush = (frames) => {
|
|
36246
|
+
if (eventName || dataLines.length > 0) {
|
|
36247
|
+
frames.push({ event: eventName || "message", data: dataLines.join("\n") });
|
|
36248
|
+
eventName = "";
|
|
36249
|
+
dataLines = [];
|
|
36250
|
+
}
|
|
36251
|
+
};
|
|
36252
|
+
return {
|
|
36253
|
+
push(chunk) {
|
|
36254
|
+
const frames = [];
|
|
36255
|
+
buffer += chunk.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
36256
|
+
let idx;
|
|
36257
|
+
while ((idx = buffer.indexOf("\n")) !== -1) {
|
|
36258
|
+
const line = buffer.slice(0, idx);
|
|
36259
|
+
buffer = buffer.slice(idx + 1);
|
|
36260
|
+
if (line.length === 0) {
|
|
36261
|
+
flush(frames);
|
|
36262
|
+
continue;
|
|
36263
|
+
}
|
|
36264
|
+
if (line.startsWith(":")) continue;
|
|
36265
|
+
if (line.startsWith("event:")) eventName = line.slice(6).trim();
|
|
36266
|
+
else if (line.startsWith("data:")) dataLines.push(line.slice(5).replace(/^ /, ""));
|
|
36267
|
+
}
|
|
36268
|
+
return frames;
|
|
36269
|
+
}
|
|
36270
|
+
};
|
|
36271
|
+
}
|
|
36272
|
+
var AttachClient = class {
|
|
36273
|
+
constructor(baseUrl, token) {
|
|
36274
|
+
this.baseUrl = baseUrl;
|
|
36275
|
+
this.token = token;
|
|
36276
|
+
}
|
|
36277
|
+
baseUrl;
|
|
36278
|
+
token;
|
|
36279
|
+
headers() {
|
|
36280
|
+
return { Authorization: `Bearer ${this.token}` };
|
|
36281
|
+
}
|
|
36282
|
+
async request(path9, init) {
|
|
36283
|
+
return fetch(`${this.baseUrl}${path9}`, {
|
|
36284
|
+
...init,
|
|
36285
|
+
headers: { ...this.headers(), ...init?.headers }
|
|
36286
|
+
});
|
|
36287
|
+
}
|
|
36288
|
+
/** Cheap authenticated GET to verify the runtime + token before opening the TUI. */
|
|
36289
|
+
async healthCheck(timeoutMs = 3e3) {
|
|
36290
|
+
const controller = new AbortController();
|
|
36291
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
36292
|
+
try {
|
|
36293
|
+
const res = await this.request("/api/chat/models", { signal: controller.signal });
|
|
36294
|
+
if (res.status === 401) throw new Error("auth");
|
|
36295
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
36296
|
+
} finally {
|
|
36297
|
+
clearTimeout(timer);
|
|
36298
|
+
}
|
|
36299
|
+
}
|
|
36300
|
+
async listThreads() {
|
|
36301
|
+
const res = await this.request("/api/chat/threads");
|
|
36302
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
36303
|
+
const body = await res.json();
|
|
36304
|
+
return (body.threads || []).map((t) => ({
|
|
36305
|
+
id: String(t.id ?? ""),
|
|
36306
|
+
shortId: String(t.shortId ?? ""),
|
|
36307
|
+
alias: String(t.alias ?? t.title ?? "session"),
|
|
36308
|
+
title: String(t.title ?? ""),
|
|
36309
|
+
updatedAt: String(t.updatedAt ?? "")
|
|
36310
|
+
})).filter((t) => t.id.length > 0);
|
|
36311
|
+
}
|
|
36312
|
+
async getThread(id) {
|
|
36313
|
+
const res = await this.request(`/api/chat/threads/${encodeURIComponent(id)}`);
|
|
36314
|
+
if (!res.ok) return [];
|
|
36315
|
+
const body = await res.json();
|
|
36316
|
+
return (body.messages || []).map((m) => ({
|
|
36317
|
+
id: String(m.id ?? `h_${Math.random().toString(36).slice(2, 8)}`),
|
|
36318
|
+
role: m.role ?? "system",
|
|
36319
|
+
kind: m.kind,
|
|
36320
|
+
content: String(m.content ?? ""),
|
|
36321
|
+
timestamp: String(m.timestamp ?? "")
|
|
36322
|
+
}));
|
|
36323
|
+
}
|
|
36324
|
+
async createThread() {
|
|
36325
|
+
try {
|
|
36326
|
+
const res = await this.request("/api/chat/threads", {
|
|
36327
|
+
method: "POST",
|
|
36328
|
+
headers: { "Content-Type": "application/json" },
|
|
36329
|
+
body: JSON.stringify({})
|
|
36330
|
+
});
|
|
36331
|
+
if (!res.ok) return null;
|
|
36332
|
+
const t = await res.json();
|
|
36333
|
+
if (!t.id) return null;
|
|
36334
|
+
return { id: t.id, shortId: String(t.shortId ?? ""), alias: String(t.alias ?? t.title ?? "session"), title: String(t.title ?? ""), updatedAt: "" };
|
|
36335
|
+
} catch {
|
|
36336
|
+
return null;
|
|
36337
|
+
}
|
|
36338
|
+
}
|
|
36339
|
+
async send(content, sessionId) {
|
|
36340
|
+
try {
|
|
36341
|
+
const res = await this.request("/api/chat/send", {
|
|
36342
|
+
method: "POST",
|
|
36343
|
+
headers: { "Content-Type": "application/json" },
|
|
36344
|
+
body: JSON.stringify({ content, sessionId })
|
|
36345
|
+
});
|
|
36346
|
+
if (!res.ok) {
|
|
36347
|
+
const body2 = await res.json().catch(() => ({}));
|
|
36348
|
+
return { ok: false, error: body2.error || `HTTP ${res.status}` };
|
|
36349
|
+
}
|
|
36350
|
+
const body = await res.json();
|
|
36351
|
+
return { ok: true, sessionId: body.sessionId };
|
|
36352
|
+
} catch (err) {
|
|
36353
|
+
return { ok: false, error: String(err?.message || err) };
|
|
36354
|
+
}
|
|
36355
|
+
}
|
|
36356
|
+
async resolvePermission(id, action) {
|
|
36357
|
+
try {
|
|
36358
|
+
const res = await this.request(`/api/chat/permission/${encodeURIComponent(id)}`, {
|
|
36359
|
+
method: "POST",
|
|
36360
|
+
headers: { "Content-Type": "application/json" },
|
|
36361
|
+
body: JSON.stringify({ action })
|
|
36362
|
+
});
|
|
36363
|
+
return res.ok;
|
|
36364
|
+
} catch {
|
|
36365
|
+
return false;
|
|
36366
|
+
}
|
|
36367
|
+
}
|
|
36368
|
+
/**
|
|
36369
|
+
* Consume the SSE event feed (reconnecting with backoff until aborted).
|
|
36370
|
+
* Emits transport pseudo-events `attach_disconnected` / `attach_auth_error`
|
|
36371
|
+
* — the auth error is terminal (the token rotates on runtime restart, so
|
|
36372
|
+
* reconnecting cannot recover; the client must re-attach).
|
|
36373
|
+
*/
|
|
36374
|
+
async streamEvents(sessionId, onEvent, signal) {
|
|
36375
|
+
const parser = createSseParser();
|
|
36376
|
+
const decoder = new TextDecoder();
|
|
36377
|
+
let backoffMs = 1e3;
|
|
36378
|
+
while (!signal.aborted) {
|
|
36379
|
+
try {
|
|
36380
|
+
const query = sessionId ? `?sessionId=${encodeURIComponent(sessionId)}` : "";
|
|
36381
|
+
const res = await this.request(`/api/chat/events${query}`, {
|
|
36382
|
+
headers: { Accept: "text/event-stream" },
|
|
36383
|
+
signal
|
|
36384
|
+
});
|
|
36385
|
+
if (res.status === 401) {
|
|
36386
|
+
onEvent({ type: "attach_auth_error", data: {} });
|
|
36387
|
+
return;
|
|
36388
|
+
}
|
|
36389
|
+
if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`);
|
|
36390
|
+
backoffMs = 1e3;
|
|
36391
|
+
const reader = res.body.getReader();
|
|
36392
|
+
for (; ; ) {
|
|
36393
|
+
const { done, value } = await reader.read();
|
|
36394
|
+
if (done) break;
|
|
36395
|
+
for (const frame of parser.push(decoder.decode(value, { stream: true }))) {
|
|
36396
|
+
let data = {};
|
|
36397
|
+
try {
|
|
36398
|
+
data = frame.data ? JSON.parse(frame.data) : {};
|
|
36399
|
+
} catch {
|
|
36400
|
+
}
|
|
36401
|
+
onEvent({ type: frame.event, data });
|
|
36402
|
+
}
|
|
36403
|
+
}
|
|
36404
|
+
} catch (err) {
|
|
36405
|
+
if (signal.aborted) return;
|
|
36406
|
+
onEvent({ type: "attach_disconnected", data: { message: String(err?.message || err) } });
|
|
36407
|
+
}
|
|
36408
|
+
const aborted = await new Promise((resolve19) => {
|
|
36409
|
+
const timer = setTimeout(() => resolve19(false), backoffMs);
|
|
36410
|
+
signal.addEventListener("abort", () => {
|
|
36411
|
+
clearTimeout(timer);
|
|
36412
|
+
resolve19(true);
|
|
36413
|
+
}, { once: true });
|
|
36414
|
+
});
|
|
36415
|
+
if (aborted) return;
|
|
36416
|
+
backoffMs = Math.min(backoffMs * 2, 1e4);
|
|
36417
|
+
}
|
|
36418
|
+
}
|
|
36419
|
+
};
|
|
36420
|
+
function findAttachTarget() {
|
|
36421
|
+
const foreground = getForegroundRuntimeStatus();
|
|
36422
|
+
const daemon = getDaemonStatus();
|
|
36423
|
+
const pid = foreground.running ? foreground.pid : daemon.running ? daemon.pid : null;
|
|
36424
|
+
if (pid == null) return null;
|
|
36425
|
+
const token = readAttachToken();
|
|
36426
|
+
if (!token) return null;
|
|
36427
|
+
return { baseUrl: `http://127.0.0.1:${getWebPort()}`, token, pid };
|
|
36428
|
+
}
|
|
36429
|
+
async function runAttach() {
|
|
36430
|
+
const { render: render2 } = await import("ink");
|
|
36431
|
+
const React4 = await import("react");
|
|
36432
|
+
const { AttachTui: AttachTui2 } = await Promise.resolve().then(() => (init_attach_tui(), attach_tui_exports));
|
|
36433
|
+
const chalk11 = (await import("chalk")).default;
|
|
36434
|
+
const target = findAttachTarget();
|
|
36435
|
+
if (!target) {
|
|
36436
|
+
const runtimeRunning = getForegroundRuntimeStatus().running || getDaemonStatus().running;
|
|
36437
|
+
if (!runtimeRunning) {
|
|
36438
|
+
console.error(" Mercury is not running. Start it with `mercury` or `mercury start`.");
|
|
36439
|
+
} else {
|
|
36440
|
+
console.error(" Cannot attach: web is disabled or no attach token was written at boot.");
|
|
36441
|
+
console.error(" Enable web (mercury doctor), or `mercury stop` and start again.");
|
|
36442
|
+
}
|
|
36443
|
+
process.exitCode = 1;
|
|
36444
|
+
return;
|
|
36445
|
+
}
|
|
36446
|
+
const client = new AttachClient(target.baseUrl, target.token);
|
|
36447
|
+
try {
|
|
36448
|
+
await client.healthCheck();
|
|
36449
|
+
} catch (err) {
|
|
36450
|
+
const message = String(err?.message || err);
|
|
36451
|
+
console.error(` Runtime is not responding at ${target.baseUrl} (${message}).`);
|
|
36452
|
+
if (message === "auth") {
|
|
36453
|
+
console.error(" The attach token rotated \u2014 the runtime restarted since the token was read.");
|
|
36454
|
+
console.error(" Run `mercury attach` again.");
|
|
36455
|
+
}
|
|
36456
|
+
process.exitCode = 1;
|
|
36457
|
+
return;
|
|
36458
|
+
}
|
|
36459
|
+
const instance = render2(React4.createElement(AttachTui2, {
|
|
36460
|
+
client,
|
|
36461
|
+
pid: target.pid,
|
|
36462
|
+
onExit: () => {
|
|
36463
|
+
}
|
|
36464
|
+
}));
|
|
36465
|
+
await instance.waitUntilExit();
|
|
36466
|
+
console.log(chalk11.dim(` Detached. Runtime still running${target.pid ? ` (PID ${target.pid})` : ""} \u2014 \`mercury stop\` stops it.`));
|
|
36467
|
+
}
|
|
36468
|
+
|
|
36469
|
+
// src/index.ts
|
|
36470
|
+
init_service();
|
|
36471
|
+
|
|
36472
|
+
// src/cli/watchdog.ts
|
|
36473
|
+
init_logger();
|
|
36474
|
+
init_crash_flag();
|
|
36475
|
+
var MAX_RESTARTS = 10;
|
|
36476
|
+
var RESTART_WINDOW_MS = 6e4;
|
|
36477
|
+
var BASE_DELAY_MS = 1e3;
|
|
36478
|
+
async function runWithWatchdog(agentFn) {
|
|
36479
|
+
const restarts = [];
|
|
36480
|
+
async function attempt() {
|
|
36481
|
+
try {
|
|
36482
|
+
await agentFn();
|
|
36483
|
+
} catch (err) {
|
|
36484
|
+
const now = Date.now();
|
|
36485
|
+
restarts.push(now);
|
|
36486
|
+
const recentRestarts = restarts.filter((t) => now - t < RESTART_WINDOW_MS);
|
|
36487
|
+
const restartCount = recentRestarts.length;
|
|
36488
|
+
if (restartCount >= MAX_RESTARTS) {
|
|
36489
|
+
logger.error({ restartCount }, "Max restarts exceeded within 60s. Exiting.");
|
|
36490
|
+
writeCrashFlag({
|
|
36491
|
+
reason: `Max restarts exceeded (${restartCount} crashes in 60s). Last error: ${err instanceof Error ? err.message : String(err)}`.slice(0, 300),
|
|
36492
|
+
timestamp: Date.now()
|
|
36493
|
+
});
|
|
36494
|
+
process.stderr.write(`[mercury] FATAL: Max restarts exceeded (${restartCount} in 60s). Last error: ${err instanceof Error ? err.message : String(err)}
|
|
36495
|
+
`);
|
|
36496
|
+
process.exit(1);
|
|
36497
|
+
}
|
|
36498
|
+
const delay = BASE_DELAY_MS * Math.pow(1.25, restartCount);
|
|
36499
|
+
logger.warn({ err, restartCount, delay }, "Crash detected. Restarting with backoff...");
|
|
36500
|
+
await sleep2(delay);
|
|
36501
|
+
await attempt();
|
|
36502
|
+
}
|
|
36503
|
+
}
|
|
36504
|
+
await attempt();
|
|
36505
|
+
}
|
|
36506
|
+
function sleep2(ms) {
|
|
36507
|
+
return new Promise((resolve19) => setTimeout(resolve19, ms));
|
|
36508
|
+
}
|
|
36509
|
+
|
|
36510
|
+
// src/index.ts
|
|
36511
|
+
init_arrow_select();
|
|
36512
|
+
init_provider_models();
|
|
36513
|
+
init_token_store();
|
|
36514
|
+
init_runtime_status();
|
|
36515
|
+
|
|
36516
|
+
// src/web/server.ts
|
|
36517
|
+
init_logger();
|
|
36518
|
+
import { Hono as Hono12 } from "hono";
|
|
36519
|
+
import { createAdaptorServer } from "@hono/node-server";
|
|
36520
|
+
import { readFileSync as readFileSync30, existsSync as existsSync41, readdirSync as readdirSync8 } from "fs";
|
|
36521
|
+
import { join as join32, dirname as dirname8 } from "path";
|
|
36522
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
36523
|
+
|
|
36010
36524
|
// src/web/middleware.ts
|
|
36525
|
+
import { getCookie } from "hono/cookie";
|
|
36011
36526
|
var PUBLIC_PATHS = /* @__PURE__ */ new Set(["/login", "/api/auth/login", "/api/auth/logout"]);
|
|
36527
|
+
function isLoopbackRequest(c) {
|
|
36528
|
+
try {
|
|
36529
|
+
const address = c.env?.incoming?.socket?.remoteAddress || "";
|
|
36530
|
+
return ["127.0.0.1", "::1", "::ffff:127.0.0.1"].includes(address);
|
|
36531
|
+
} catch {
|
|
36532
|
+
return false;
|
|
36533
|
+
}
|
|
36534
|
+
}
|
|
36012
36535
|
async function authGuard(c, next) {
|
|
36013
36536
|
const path9 = new URL(c.req.url).pathname;
|
|
36014
36537
|
if (path9.startsWith("/vendor/") || path9.startsWith("/static/") || path9.startsWith("/assets/") || path9.startsWith("/icons/") || path9.endsWith(".css") || path9.endsWith(".js") || path9.endsWith(".png") || path9.endsWith(".ico") || path9.endsWith(".svg") || path9.endsWith(".woff2") || path9.endsWith(".webmanifest")) {
|
|
@@ -36019,10 +36542,13 @@ async function authGuard(c, next) {
|
|
|
36019
36542
|
}
|
|
36020
36543
|
if (path9.startsWith("/api/")) {
|
|
36021
36544
|
const token2 = getCookie(c, getSessionCookieName()) || c.req.header("Authorization")?.replace("Bearer ", "");
|
|
36022
|
-
if (
|
|
36023
|
-
return
|
|
36545
|
+
if (token2 && validateSession(token2)) {
|
|
36546
|
+
return next();
|
|
36024
36547
|
}
|
|
36025
|
-
|
|
36548
|
+
if (token2 && isLoopbackRequest(c) && token2 === readAttachToken()) {
|
|
36549
|
+
return next();
|
|
36550
|
+
}
|
|
36551
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
36026
36552
|
}
|
|
36027
36553
|
const token = getCookie(c, getSessionCookieName());
|
|
36028
36554
|
if (!token || !validateSession(token)) {
|
|
@@ -37879,7 +38405,7 @@ process.on("unhandledRejection", (reason) => {
|
|
|
37879
38405
|
try {
|
|
37880
38406
|
process.stderr.write(`
|
|
37881
38407
|
\u2717 Mercury cannot start: ${message}
|
|
37882
|
-
|
|
38408
|
+
Attach to it with \`mercury attach\`, or stop it with \`mercury stop\` / \`kill <pid>\`.
|
|
37883
38409
|
`);
|
|
37884
38410
|
} catch {
|
|
37885
38411
|
}
|
|
@@ -37922,7 +38448,7 @@ var pkgVersion;
|
|
|
37922
38448
|
try {
|
|
37923
38449
|
pkgVersion = JSON.parse(readFileSync31(join33(__dirname2, "..", "package.json"), "utf8")).version;
|
|
37924
38450
|
} catch {
|
|
37925
|
-
pkgVersion = "1.2.
|
|
38451
|
+
pkgVersion = "1.2.5";
|
|
37926
38452
|
}
|
|
37927
38453
|
function hr() {
|
|
37928
38454
|
console.log(chalk10.dim("\u2500".repeat(50)));
|
|
@@ -39655,7 +40181,7 @@ async function runAgent(isDaemon = false) {
|
|
|
39655
40181
|
try {
|
|
39656
40182
|
process.stderr.write(`
|
|
39657
40183
|
\u2717 Mercury cannot start: ${message}
|
|
39658
|
-
|
|
40184
|
+
Attach to it with \`mercury attach\`, or stop it with \`mercury stop\` / \`kill <pid>\`.
|
|
39659
40185
|
`);
|
|
39660
40186
|
} catch {
|
|
39661
40187
|
}
|
|
@@ -40653,6 +41179,7 @@ Assistant: ${assistantMessage.slice(0, 1500)}`,
|
|
|
40653
41179
|
console.log(chalk10.dim(" Ctrl+C to exit \xB7 /help for commands"));
|
|
40654
41180
|
if (config2.web.enabled) {
|
|
40655
41181
|
startWebServer();
|
|
41182
|
+
writeAttachToken();
|
|
40656
41183
|
updateStatus({
|
|
40657
41184
|
running: true,
|
|
40658
41185
|
pid: process.pid,
|
|
@@ -40701,6 +41228,7 @@ Assistant: ${assistantMessage.slice(0, 1500)}`,
|
|
|
40701
41228
|
await channels.startAll();
|
|
40702
41229
|
if (config2.web.enabled) {
|
|
40703
41230
|
startWebServer();
|
|
41231
|
+
writeAttachToken();
|
|
40704
41232
|
updateStatus({
|
|
40705
41233
|
running: true,
|
|
40706
41234
|
pid: process.pid,
|
|
@@ -40784,6 +41312,12 @@ program.name("mercury").description("Mercury \u2014 Soul-driven AI agent with pe
|
|
|
40784
41312
|
autoDaemonize();
|
|
40785
41313
|
return;
|
|
40786
41314
|
}
|
|
41315
|
+
const foreground = getForegroundRuntimeStatus();
|
|
41316
|
+
if (foreground.running && foreground.pid) {
|
|
41317
|
+
console.log(chalk10.cyan(` \u26BF Mercury is already running (PID: ${foreground.pid}) \u2014 attaching.`));
|
|
41318
|
+
await runAttach();
|
|
41319
|
+
return;
|
|
41320
|
+
}
|
|
40787
41321
|
autoDaemonize();
|
|
40788
41322
|
await runAgent();
|
|
40789
41323
|
});
|
|
@@ -40798,11 +41332,20 @@ program.command("start").description("Start Mercury \u2014 runs as a daemon by d
|
|
|
40798
41332
|
return;
|
|
40799
41333
|
}
|
|
40800
41334
|
if (opts.foreground) {
|
|
41335
|
+
const foreground = getForegroundRuntimeStatus();
|
|
41336
|
+
if (foreground.running && foreground.pid) {
|
|
41337
|
+
console.log(chalk10.cyan(` \u26BF Mercury is already running (PID: ${foreground.pid}) \u2014 attaching.`));
|
|
41338
|
+
await runAttach();
|
|
41339
|
+
return;
|
|
41340
|
+
}
|
|
40801
41341
|
await runAgent();
|
|
40802
41342
|
return;
|
|
40803
41343
|
}
|
|
40804
41344
|
startBackground();
|
|
40805
41345
|
});
|
|
41346
|
+
program.command("attach").description("Attach this terminal to an already-running Mercury runtime (chat + live streaming)").action(async () => {
|
|
41347
|
+
await runAttach();
|
|
41348
|
+
});
|
|
40806
41349
|
program.command("stop").description("Stop a running Mercury process (daemon or foreground)").action(async () => {
|
|
40807
41350
|
const foreground = getForegroundRuntimeStatus();
|
|
40808
41351
|
const daemon = getDaemonStatus();
|