@cosmicstack/mercury-agent 1.2.4 → 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/dist/index.js +1406 -895
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- 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,
|
|
@@ -35443,12 +35696,13 @@ init_loader();
|
|
|
35443
35696
|
// src/skills/cli.ts
|
|
35444
35697
|
init_registry();
|
|
35445
35698
|
init_store();
|
|
35699
|
+
init_markdown();
|
|
35700
|
+
init_config();
|
|
35701
|
+
init_open_url();
|
|
35446
35702
|
import { existsSync as existsSync31, readFileSync as readFileSync21 } from "fs";
|
|
35447
35703
|
import { resolve as resolve16 } from "path";
|
|
35448
35704
|
import { homedir as homedir7 } from "os";
|
|
35449
35705
|
import chalk6 from "chalk";
|
|
35450
|
-
init_config();
|
|
35451
|
-
init_open_url();
|
|
35452
35706
|
function inheritedFlags(cmd) {
|
|
35453
35707
|
const opts = {};
|
|
35454
35708
|
let c = cmd;
|
|
@@ -35792,76 +36046,23 @@ function registerSkillsCommand(program2) {
|
|
|
35792
36046
|
|
|
35793
36047
|
// src/index.ts
|
|
35794
36048
|
init_daemon();
|
|
35795
|
-
init_service();
|
|
35796
36049
|
|
|
35797
|
-
// src/cli/
|
|
35798
|
-
|
|
35799
|
-
init_crash_flag();
|
|
35800
|
-
var MAX_RESTARTS = 10;
|
|
35801
|
-
var RESTART_WINDOW_MS = 6e4;
|
|
35802
|
-
var BASE_DELAY_MS = 1e3;
|
|
35803
|
-
async function runWithWatchdog(agentFn) {
|
|
35804
|
-
const restarts = [];
|
|
35805
|
-
async function attempt() {
|
|
35806
|
-
try {
|
|
35807
|
-
await agentFn();
|
|
35808
|
-
} catch (err) {
|
|
35809
|
-
const now = Date.now();
|
|
35810
|
-
restarts.push(now);
|
|
35811
|
-
const recentRestarts = restarts.filter((t) => now - t < RESTART_WINDOW_MS);
|
|
35812
|
-
const restartCount = recentRestarts.length;
|
|
35813
|
-
if (restartCount >= MAX_RESTARTS) {
|
|
35814
|
-
logger.error({ restartCount }, "Max restarts exceeded within 60s. Exiting.");
|
|
35815
|
-
writeCrashFlag({
|
|
35816
|
-
reason: `Max restarts exceeded (${restartCount} crashes in 60s). Last error: ${err instanceof Error ? err.message : String(err)}`.slice(0, 300),
|
|
35817
|
-
timestamp: Date.now()
|
|
35818
|
-
});
|
|
35819
|
-
process.stderr.write(`[mercury] FATAL: Max restarts exceeded (${restartCount} in 60s). Last error: ${err instanceof Error ? err.message : String(err)}
|
|
35820
|
-
`);
|
|
35821
|
-
process.exit(1);
|
|
35822
|
-
}
|
|
35823
|
-
const delay = BASE_DELAY_MS * Math.pow(1.25, restartCount);
|
|
35824
|
-
logger.warn({ err, restartCount, delay }, "Crash detected. Restarting with backoff...");
|
|
35825
|
-
await sleep2(delay);
|
|
35826
|
-
await attempt();
|
|
35827
|
-
}
|
|
35828
|
-
}
|
|
35829
|
-
await attempt();
|
|
35830
|
-
}
|
|
35831
|
-
function sleep2(ms) {
|
|
35832
|
-
return new Promise((resolve19) => setTimeout(resolve19, ms));
|
|
35833
|
-
}
|
|
35834
|
-
|
|
35835
|
-
// src/index.ts
|
|
35836
|
-
init_arrow_select();
|
|
35837
|
-
init_provider_models();
|
|
35838
|
-
init_token_store();
|
|
35839
|
-
init_runtime_status();
|
|
35840
|
-
|
|
35841
|
-
// src/web/server.ts
|
|
35842
|
-
init_logger();
|
|
35843
|
-
import { Hono as Hono12 } from "hono";
|
|
35844
|
-
import { createAdaptorServer } from "@hono/node-server";
|
|
35845
|
-
import { readFileSync as readFileSync30, existsSync as existsSync41, readdirSync as readdirSync8 } from "fs";
|
|
35846
|
-
import { join as join32, dirname as dirname8 } from "path";
|
|
35847
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
35848
|
-
|
|
35849
|
-
// src/web/middleware.ts
|
|
35850
|
-
import { getCookie } from "hono/cookie";
|
|
36050
|
+
// src/cli/attach.ts
|
|
36051
|
+
init_daemon();
|
|
35851
36052
|
|
|
35852
36053
|
// src/web/auth.ts
|
|
35853
36054
|
init_config();
|
|
35854
36055
|
import { compareSync, hashSync, genSaltSync } from "bcryptjs";
|
|
35855
|
-
import { readFileSync as
|
|
35856
|
-
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";
|
|
35857
36058
|
import { randomBytes } from "crypto";
|
|
35858
36059
|
var SESSION_COOKIE = "mercury_session";
|
|
35859
36060
|
var SESSION_MAX_AGE = 7 * 24 * 60 * 60;
|
|
35860
36061
|
function getWebConfigPath() {
|
|
35861
|
-
return
|
|
36062
|
+
return join24(getMercuryHome(), "web-config.json");
|
|
35862
36063
|
}
|
|
35863
36064
|
function writeCredentialFile(path9, contents) {
|
|
35864
|
-
|
|
36065
|
+
writeFileSync22(path9, contents, { encoding: "utf-8", mode: 384 });
|
|
35865
36066
|
try {
|
|
35866
36067
|
chmodSync3(path9, 384);
|
|
35867
36068
|
} catch {
|
|
@@ -35881,11 +36082,19 @@ function getWebPort() {
|
|
|
35881
36082
|
}
|
|
35882
36083
|
var ATTACH_TOKEN_FILE = "attach-token";
|
|
35883
36084
|
function getAttachTokenPath() {
|
|
35884
|
-
return
|
|
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
|
+
}
|
|
35885
36094
|
}
|
|
35886
36095
|
function readAttachToken() {
|
|
35887
36096
|
try {
|
|
35888
|
-
const token =
|
|
36097
|
+
const token = readFileSync23(getAttachTokenPath(), "utf-8").trim();
|
|
35889
36098
|
return token.length > 0 ? token : null;
|
|
35890
36099
|
} catch {
|
|
35891
36100
|
return null;
|
|
@@ -35893,13 +36102,13 @@ function readAttachToken() {
|
|
|
35893
36102
|
}
|
|
35894
36103
|
function loadWebAuth() {
|
|
35895
36104
|
const path9 = getWebConfigPath();
|
|
35896
|
-
if (!
|
|
36105
|
+
if (!existsSync33(path9)) return null;
|
|
35897
36106
|
try {
|
|
35898
36107
|
try {
|
|
35899
36108
|
chmodSync3(path9, 384);
|
|
35900
36109
|
} catch {
|
|
35901
36110
|
}
|
|
35902
|
-
const raw =
|
|
36111
|
+
const raw = readFileSync23(path9, "utf-8");
|
|
35903
36112
|
return JSON.parse(raw);
|
|
35904
36113
|
} catch {
|
|
35905
36114
|
return null;
|
|
@@ -35907,8 +36116,8 @@ function loadWebAuth() {
|
|
|
35907
36116
|
}
|
|
35908
36117
|
function saveWebAuth(auth2) {
|
|
35909
36118
|
const dir = getMercuryHome();
|
|
35910
|
-
if (!
|
|
35911
|
-
|
|
36119
|
+
if (!existsSync33(dir)) {
|
|
36120
|
+
mkdirSync22(dir, { recursive: true });
|
|
35912
36121
|
}
|
|
35913
36122
|
writeCredentialFile(getWebConfigPath(), JSON.stringify(auth2, null, 2));
|
|
35914
36123
|
}
|
|
@@ -35972,12 +36181,12 @@ function createSessionToken() {
|
|
|
35972
36181
|
var sessions = /* @__PURE__ */ new Map();
|
|
35973
36182
|
var SESSION_FILE = "web-sessions.json";
|
|
35974
36183
|
function getSessionFilePath() {
|
|
35975
|
-
return
|
|
36184
|
+
return join24(getMercuryHome(), SESSION_FILE);
|
|
35976
36185
|
}
|
|
35977
36186
|
function persistSessions() {
|
|
35978
36187
|
try {
|
|
35979
36188
|
const dir = getMercuryHome();
|
|
35980
|
-
if (!
|
|
36189
|
+
if (!existsSync33(dir)) mkdirSync22(dir, { recursive: true });
|
|
35981
36190
|
const entries = Object.fromEntries(sessions);
|
|
35982
36191
|
writeCredentialFile(getSessionFilePath(), JSON.stringify(entries, null, 2));
|
|
35983
36192
|
} catch {
|
|
@@ -35986,8 +36195,8 @@ function persistSessions() {
|
|
|
35986
36195
|
function restoreSessions() {
|
|
35987
36196
|
try {
|
|
35988
36197
|
const path9 = getSessionFilePath();
|
|
35989
|
-
if (!
|
|
35990
|
-
const raw =
|
|
36198
|
+
if (!existsSync33(path9)) return;
|
|
36199
|
+
const raw = readFileSync23(path9, "utf-8");
|
|
35991
36200
|
const data = JSON.parse(raw);
|
|
35992
36201
|
const now = Date.now();
|
|
35993
36202
|
for (const [key, entry] of Object.entries(data)) {
|
|
@@ -36028,7 +36237,292 @@ function getSessionMaxAge() {
|
|
|
36028
36237
|
return SESSION_MAX_AGE;
|
|
36029
36238
|
}
|
|
36030
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
|
+
|
|
36031
36524
|
// src/web/middleware.ts
|
|
36525
|
+
import { getCookie } from "hono/cookie";
|
|
36032
36526
|
var PUBLIC_PATHS = /* @__PURE__ */ new Set(["/login", "/api/auth/login", "/api/auth/logout"]);
|
|
36033
36527
|
function isLoopbackRequest(c) {
|
|
36034
36528
|
try {
|
|
@@ -37911,7 +38405,7 @@ process.on("unhandledRejection", (reason) => {
|
|
|
37911
38405
|
try {
|
|
37912
38406
|
process.stderr.write(`
|
|
37913
38407
|
\u2717 Mercury cannot start: ${message}
|
|
37914
|
-
|
|
38408
|
+
Attach to it with \`mercury attach\`, or stop it with \`mercury stop\` / \`kill <pid>\`.
|
|
37915
38409
|
`);
|
|
37916
38410
|
} catch {
|
|
37917
38411
|
}
|
|
@@ -37954,7 +38448,7 @@ var pkgVersion;
|
|
|
37954
38448
|
try {
|
|
37955
38449
|
pkgVersion = JSON.parse(readFileSync31(join33(__dirname2, "..", "package.json"), "utf8")).version;
|
|
37956
38450
|
} catch {
|
|
37957
|
-
pkgVersion = "1.2.
|
|
38451
|
+
pkgVersion = "1.2.5";
|
|
37958
38452
|
}
|
|
37959
38453
|
function hr() {
|
|
37960
38454
|
console.log(chalk10.dim("\u2500".repeat(50)));
|
|
@@ -39687,7 +40181,7 @@ async function runAgent(isDaemon = false) {
|
|
|
39687
40181
|
try {
|
|
39688
40182
|
process.stderr.write(`
|
|
39689
40183
|
\u2717 Mercury cannot start: ${message}
|
|
39690
|
-
|
|
40184
|
+
Attach to it with \`mercury attach\`, or stop it with \`mercury stop\` / \`kill <pid>\`.
|
|
39691
40185
|
`);
|
|
39692
40186
|
} catch {
|
|
39693
40187
|
}
|
|
@@ -40685,6 +41179,7 @@ Assistant: ${assistantMessage.slice(0, 1500)}`,
|
|
|
40685
41179
|
console.log(chalk10.dim(" Ctrl+C to exit \xB7 /help for commands"));
|
|
40686
41180
|
if (config2.web.enabled) {
|
|
40687
41181
|
startWebServer();
|
|
41182
|
+
writeAttachToken();
|
|
40688
41183
|
updateStatus({
|
|
40689
41184
|
running: true,
|
|
40690
41185
|
pid: process.pid,
|
|
@@ -40733,6 +41228,7 @@ Assistant: ${assistantMessage.slice(0, 1500)}`,
|
|
|
40733
41228
|
await channels.startAll();
|
|
40734
41229
|
if (config2.web.enabled) {
|
|
40735
41230
|
startWebServer();
|
|
41231
|
+
writeAttachToken();
|
|
40736
41232
|
updateStatus({
|
|
40737
41233
|
running: true,
|
|
40738
41234
|
pid: process.pid,
|
|
@@ -40816,6 +41312,12 @@ program.name("mercury").description("Mercury \u2014 Soul-driven AI agent with pe
|
|
|
40816
41312
|
autoDaemonize();
|
|
40817
41313
|
return;
|
|
40818
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
|
+
}
|
|
40819
41321
|
autoDaemonize();
|
|
40820
41322
|
await runAgent();
|
|
40821
41323
|
});
|
|
@@ -40830,11 +41332,20 @@ program.command("start").description("Start Mercury \u2014 runs as a daemon by d
|
|
|
40830
41332
|
return;
|
|
40831
41333
|
}
|
|
40832
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
|
+
}
|
|
40833
41341
|
await runAgent();
|
|
40834
41342
|
return;
|
|
40835
41343
|
}
|
|
40836
41344
|
startBackground();
|
|
40837
41345
|
});
|
|
41346
|
+
program.command("attach").description("Attach this terminal to an already-running Mercury runtime (chat + live streaming)").action(async () => {
|
|
41347
|
+
await runAttach();
|
|
41348
|
+
});
|
|
40838
41349
|
program.command("stop").description("Stop a running Mercury process (daemon or foreground)").action(async () => {
|
|
40839
41350
|
const foreground = getForegroundRuntimeStatus();
|
|
40840
41351
|
const daemon = getDaemonStatus();
|