@mrciphersmith/keryx 0.2.116 → 0.2.118
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/cli.js +582 -279
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -50252,7 +50252,7 @@ import path47 from "path";
|
|
|
50252
50252
|
// package.json
|
|
50253
50253
|
var package_default = {
|
|
50254
50254
|
name: "@mrciphersmith/keryx",
|
|
50255
|
-
version: "0.2.
|
|
50255
|
+
version: "0.2.118",
|
|
50256
50256
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
50257
50257
|
private: false,
|
|
50258
50258
|
publishConfig: {
|
|
@@ -74138,6 +74138,15 @@ function parseGrokToml(file, text) {
|
|
|
74138
74138
|
}
|
|
74139
74139
|
continue;
|
|
74140
74140
|
}
|
|
74141
|
+
const arrayHeader = /^\[\[([^[\]]+)\]\]$/.exec(line);
|
|
74142
|
+
if (arrayHeader !== null) {
|
|
74143
|
+
const first = arrayHeader[1].split(".")[0]?.trim().replace(/^(["'])(.*)\1$/, "$2");
|
|
74144
|
+
current = undefined;
|
|
74145
|
+
if (first === "mcp_servers") {
|
|
74146
|
+
problems.push({ file, message: `line ${lineNo}: "${line}" is not a table header this reader understands` });
|
|
74147
|
+
}
|
|
74148
|
+
continue;
|
|
74149
|
+
}
|
|
74141
74150
|
if (line.startsWith("[")) {
|
|
74142
74151
|
problems.push({ file, message: `line ${lineNo}: "${line}" is not a table header this reader understands` });
|
|
74143
74152
|
current = undefined;
|
|
@@ -82502,8 +82511,6 @@ function realSpawner() {
|
|
|
82502
82511
|
env,
|
|
82503
82512
|
detached: true
|
|
82504
82513
|
});
|
|
82505
|
-
let dataCb;
|
|
82506
|
-
let exitCb;
|
|
82507
82514
|
let netClosed = false;
|
|
82508
82515
|
const closeNetOnce = async () => {
|
|
82509
82516
|
if (netClosed)
|
|
@@ -82511,43 +82518,81 @@ function realSpawner() {
|
|
|
82511
82518
|
netClosed = true;
|
|
82512
82519
|
await netClose();
|
|
82513
82520
|
};
|
|
82514
|
-
|
|
82515
|
-
const errDecoder = new TextDecoder;
|
|
82516
|
-
const pump = async (stream, kind, decoder) => {
|
|
82517
|
-
if (stream === undefined)
|
|
82518
|
-
return;
|
|
82519
|
-
const reader = stream.getReader();
|
|
82520
|
-
try {
|
|
82521
|
-
for (;; ) {
|
|
82522
|
-
const chunk = await reader.read();
|
|
82523
|
-
if (chunk.done)
|
|
82524
|
-
break;
|
|
82525
|
-
if (chunk.value !== undefined)
|
|
82526
|
-
dataCb?.(decoder.decode(chunk.value, { stream: true }), kind);
|
|
82527
|
-
}
|
|
82528
|
-
} catch {} finally {
|
|
82529
|
-
reader.releaseLock();
|
|
82530
|
-
}
|
|
82531
|
-
};
|
|
82532
|
-
Promise.all([pump(proc.stdout, "stdout", outDecoder), pump(proc.stderr, "stderr", errDecoder)]);
|
|
82533
|
-
proc.exited.then(async (exitCode) => {
|
|
82534
|
-
exitCb?.({ exitCode });
|
|
82535
|
-
await closeNetOnce();
|
|
82536
|
-
});
|
|
82537
|
-
return {
|
|
82538
|
-
pid: proc.pid,
|
|
82539
|
-
onOutput: (cb) => {
|
|
82540
|
-
dataCb = cb;
|
|
82541
|
-
},
|
|
82542
|
-
onExit: (cb) => {
|
|
82543
|
-
exitCb = cb;
|
|
82544
|
-
},
|
|
82521
|
+
return superviseSpawnedProcess(proc, {
|
|
82545
82522
|
kill: (signal) => {
|
|
82546
82523
|
try {
|
|
82547
82524
|
process.kill(-proc.pid, signal);
|
|
82548
82525
|
} catch {}
|
|
82526
|
+
},
|
|
82527
|
+
afterExit: closeNetOnce
|
|
82528
|
+
});
|
|
82529
|
+
};
|
|
82530
|
+
}
|
|
82531
|
+
var EXIT_OUTPUT_DRAIN_MS = 2000;
|
|
82532
|
+
function superviseSpawnedProcess(proc, opts) {
|
|
82533
|
+
const drainMs = opts.drainMs ?? EXIT_OUTPUT_DRAIN_MS;
|
|
82534
|
+
let dataCb;
|
|
82535
|
+
let exitCb;
|
|
82536
|
+
const pendingChunks = [];
|
|
82537
|
+
let pendingExit;
|
|
82538
|
+
const emitData = (chunk, stream) => {
|
|
82539
|
+
if (dataCb === undefined)
|
|
82540
|
+
pendingChunks.push({ chunk, stream });
|
|
82541
|
+
else
|
|
82542
|
+
dataCb(chunk, stream);
|
|
82543
|
+
};
|
|
82544
|
+
const outDecoder = new TextDecoder;
|
|
82545
|
+
const errDecoder = new TextDecoder;
|
|
82546
|
+
const pump = async (stream, kind, decoder) => {
|
|
82547
|
+
if (stream === undefined)
|
|
82548
|
+
return;
|
|
82549
|
+
const reader = stream.getReader();
|
|
82550
|
+
try {
|
|
82551
|
+
for (;; ) {
|
|
82552
|
+
const chunk = await reader.read();
|
|
82553
|
+
if (chunk.done)
|
|
82554
|
+
break;
|
|
82555
|
+
if (chunk.value !== undefined)
|
|
82556
|
+
emitData(decoder.decode(chunk.value, { stream: true }), kind);
|
|
82549
82557
|
}
|
|
82550
|
-
}
|
|
82558
|
+
} catch {} finally {
|
|
82559
|
+
reader.releaseLock();
|
|
82560
|
+
}
|
|
82561
|
+
};
|
|
82562
|
+
const drained = Promise.all([
|
|
82563
|
+
pump(proc.stdout, "stdout", outDecoder),
|
|
82564
|
+
pump(proc.stderr, "stderr", errDecoder)
|
|
82565
|
+
]);
|
|
82566
|
+
proc.exited.then(async (exitCode) => {
|
|
82567
|
+
let timer;
|
|
82568
|
+
await Promise.race([
|
|
82569
|
+
drained,
|
|
82570
|
+
new Promise((resolve) => {
|
|
82571
|
+
timer = setTimeout(resolve, drainMs);
|
|
82572
|
+
timer.unref?.();
|
|
82573
|
+
})
|
|
82574
|
+
]);
|
|
82575
|
+
if (timer !== undefined)
|
|
82576
|
+
clearTimeout(timer);
|
|
82577
|
+
if (exitCb === undefined)
|
|
82578
|
+
pendingExit = { exitCode };
|
|
82579
|
+
else
|
|
82580
|
+
exitCb({ exitCode });
|
|
82581
|
+
await opts.afterExit?.();
|
|
82582
|
+
});
|
|
82583
|
+
return {
|
|
82584
|
+
pid: proc.pid,
|
|
82585
|
+
onOutput: (cb) => {
|
|
82586
|
+
dataCb = cb;
|
|
82587
|
+
for (const { chunk, stream } of pendingChunks.splice(0))
|
|
82588
|
+
cb(chunk, stream);
|
|
82589
|
+
},
|
|
82590
|
+
onExit: (cb) => {
|
|
82591
|
+
exitCb = cb;
|
|
82592
|
+
if (pendingExit !== undefined)
|
|
82593
|
+
cb(pendingExit);
|
|
82594
|
+
},
|
|
82595
|
+
kill: opts.kill
|
|
82551
82596
|
};
|
|
82552
82597
|
}
|
|
82553
82598
|
function createJobRegistry(options) {
|
|
@@ -89158,6 +89203,7 @@ function resolveInitialTab(tabs, initialTab) {
|
|
|
89158
89203
|
}
|
|
89159
89204
|
return first.id;
|
|
89160
89205
|
}
|
|
89206
|
+
var MODAL_BODY_SCROLLBAR_COLS = 1;
|
|
89161
89207
|
function innerWidthOf(state) {
|
|
89162
89208
|
const panelWidth = state.panel.width;
|
|
89163
89209
|
if (typeof panelWidth === "number" && panelWidth > MODAL_PANEL_CHROME_X) {
|
|
@@ -89210,7 +89256,7 @@ function mountTab(state, input, tabId) {
|
|
|
89210
89256
|
const size = resolveModalPanelSize(availWidth, state.chrome.renderer.height, input.contentRows);
|
|
89211
89257
|
const panelWidth = Math.min(availWidth, size.width);
|
|
89212
89258
|
const cleanup = input.renderTab(tabId, state.body, {
|
|
89213
|
-
width: resolveModalInnerWidth(panelWidth),
|
|
89259
|
+
width: resolveModalInnerWidth(panelWidth) - MODAL_BODY_SCROLLBAR_COLS,
|
|
89214
89260
|
height: modalBodyRows(size.height)
|
|
89215
89261
|
});
|
|
89216
89262
|
state.tabCleanup = typeof cleanup === "function" ? cleanup : undefined;
|
|
@@ -89387,6 +89433,9 @@ function ensureHost(otui, chrome) {
|
|
|
89387
89433
|
if (key.name === "right" && claimArrow("right")) {
|
|
89388
89434
|
return;
|
|
89389
89435
|
}
|
|
89436
|
+
if (state.tabs.length < 2 && (key.name === "left" || key.name === "right")) {
|
|
89437
|
+
return;
|
|
89438
|
+
}
|
|
89390
89439
|
if (key.name === "left" || onStrip && key.name === "tab" && key.shift === true) {
|
|
89391
89440
|
const prev = idx > 0 ? state.tabs[idx - 1] : undefined;
|
|
89392
89441
|
if (prev !== undefined) {
|
|
@@ -89428,11 +89477,11 @@ function ensureHost(otui, chrome) {
|
|
|
89428
89477
|
}
|
|
89429
89478
|
function makeHandle(state, generation) {
|
|
89430
89479
|
return {
|
|
89431
|
-
close() {
|
|
89480
|
+
close(opts) {
|
|
89432
89481
|
if (state.generation !== generation) {
|
|
89433
89482
|
return;
|
|
89434
89483
|
}
|
|
89435
|
-
closeHost(state, { restoreFocus: true, runOnClose: true });
|
|
89484
|
+
closeHost(state, { restoreFocus: opts?.restoreFocus ?? true, runOnClose: true });
|
|
89436
89485
|
},
|
|
89437
89486
|
setTab(id) {
|
|
89438
89487
|
if (state.generation !== generation || !state.open || state.input === undefined) {
|
|
@@ -89481,6 +89530,12 @@ function openModal(otui, chrome, input) {
|
|
|
89481
89530
|
if (focused === null || !containsNode(state.scroll, focused)) {
|
|
89482
89531
|
state.tabStrip.focus();
|
|
89483
89532
|
}
|
|
89533
|
+
const r = chrome.renderer;
|
|
89534
|
+
setTimeout(() => {
|
|
89535
|
+
if (state.generation === generation && state.open) {
|
|
89536
|
+
r.requestRender();
|
|
89537
|
+
}
|
|
89538
|
+
}, 0);
|
|
89484
89539
|
return makeHandle(state, generation);
|
|
89485
89540
|
}
|
|
89486
89541
|
|
|
@@ -90475,7 +90530,7 @@ function classifyBusyDispatch(params) {
|
|
|
90475
90530
|
// src/tui/boot-animation.ts
|
|
90476
90531
|
var DEFAULT_BOOT_DURATION_MS = 350;
|
|
90477
90532
|
var WORDMARK = ["========================", " K E R Y X ", "========================"];
|
|
90478
|
-
var
|
|
90533
|
+
var SPLASH_HINT = "type a message to start \xB7 / for commands";
|
|
90479
90534
|
async function playBootAnimation(otui, r, opts) {
|
|
90480
90535
|
if (process.env.KERYX_SKIP_BOOT === "1" || opts.skip === true) {
|
|
90481
90536
|
return;
|
|
@@ -90500,29 +90555,14 @@ async function playBootAnimation(otui, r, opts) {
|
|
|
90500
90555
|
for (const [index, line] of WORDMARK.entries()) {
|
|
90501
90556
|
logoBox.add(new otui.TextRenderable(r, { id: `boot-logo-${index}`, content: otui.t`${otui.bold(line)}` }));
|
|
90502
90557
|
}
|
|
90503
|
-
const statusText = new otui.TextRenderable(r, {
|
|
90504
|
-
id: "boot-status",
|
|
90505
|
-
content: otui.t`${otui.dim(STEPS[0] ?? "")}`,
|
|
90506
|
-
marginTop: 1
|
|
90507
|
-
});
|
|
90508
|
-
box.add(statusText);
|
|
90509
90558
|
box.add(new otui.TextRenderable(r, { id: "boot-hint", content: otui.t`${otui.dim("any key to skip")}`, marginTop: 1 }));
|
|
90510
90559
|
await new Promise((resolve) => {
|
|
90511
90560
|
let settled = false;
|
|
90512
|
-
let stepIndex = 0;
|
|
90513
|
-
const stepTimer = setInterval(() => {
|
|
90514
|
-
stepIndex = Math.min(stepIndex + 1, STEPS.length - 1);
|
|
90515
|
-
const step = STEPS[stepIndex];
|
|
90516
|
-
if (step !== undefined) {
|
|
90517
|
-
statusText.content = otui.t`${otui.dim(step)}`;
|
|
90518
|
-
}
|
|
90519
|
-
}, Math.max(1, Math.floor(durationMs / STEPS.length)));
|
|
90520
90561
|
const finish = () => {
|
|
90521
90562
|
if (settled) {
|
|
90522
90563
|
return;
|
|
90523
90564
|
}
|
|
90524
90565
|
settled = true;
|
|
90525
|
-
clearInterval(stepTimer);
|
|
90526
90566
|
clearTimeout(doneTimer);
|
|
90527
90567
|
unsubscribe();
|
|
90528
90568
|
r.root.remove(box);
|
|
@@ -90532,6 +90572,31 @@ async function playBootAnimation(otui, r, opts) {
|
|
|
90532
90572
|
const unsubscribe = opts.onKeypress(() => finish());
|
|
90533
90573
|
});
|
|
90534
90574
|
}
|
|
90575
|
+
var SHELL_CHROME_ROWS = 8;
|
|
90576
|
+
function mountEmptyTranscriptSplash(otui, r, transcript) {
|
|
90577
|
+
const rows = WORDMARK.length + 2;
|
|
90578
|
+
const paneRows = Math.max(0, r.height - SHELL_CHROME_ROWS);
|
|
90579
|
+
const box = new otui.BoxRenderable(r, {
|
|
90580
|
+
id: "transcript-splash",
|
|
90581
|
+
width: "100%",
|
|
90582
|
+
flexDirection: "column",
|
|
90583
|
+
alignItems: "center",
|
|
90584
|
+
flexShrink: 0,
|
|
90585
|
+
marginTop: Math.max(1, Math.floor((paneRows - rows) / 2))
|
|
90586
|
+
});
|
|
90587
|
+
for (const [index, line] of WORDMARK.entries()) {
|
|
90588
|
+
box.add(new otui.TextRenderable(r, { id: `splash-logo-${index}`, content: otui.t`${otui.bold(line)}` }));
|
|
90589
|
+
}
|
|
90590
|
+
box.add(new otui.TextRenderable(r, { id: "splash-hint", content: otui.t`${otui.dim(SPLASH_HINT)}`, marginTop: 1 }));
|
|
90591
|
+
transcript.add(box);
|
|
90592
|
+
let removed = false;
|
|
90593
|
+
return () => {
|
|
90594
|
+
if (removed)
|
|
90595
|
+
return;
|
|
90596
|
+
removed = true;
|
|
90597
|
+
transcript.remove(box);
|
|
90598
|
+
};
|
|
90599
|
+
}
|
|
90535
90600
|
|
|
90536
90601
|
// src/tui/workspace-inspector.ts
|
|
90537
90602
|
var WORKSPACE_COMMAND = "/workspace";
|
|
@@ -91270,13 +91335,11 @@ async function declineProposalViaShell(run, workspaceId, proposalId) {
|
|
|
91270
91335
|
|
|
91271
91336
|
// src/tui/mcp-inspector.ts
|
|
91272
91337
|
var MCP_INSPECTOR_FOOTER = [
|
|
91273
|
-
{ key: "\u2191/\u2193", label: "select" },
|
|
91274
|
-
{ key: "click", label: "row: select/act" },
|
|
91275
|
-
{ key: "c/d", label: "connect/disconnect" },
|
|
91276
|
-
{ key: "y", label: "confirm" },
|
|
91338
|
+
{ key: "\u2191/\u2193", label: "scroll/select" },
|
|
91277
91339
|
{ key: "\u2190/\u2192", label: "tabs" },
|
|
91278
91340
|
{ key: "esc", label: "close" }
|
|
91279
91341
|
];
|
|
91342
|
+
var MCP_TAB_KEYS = "keys: c/d connect/disconnect \xB7 y confirm \xB7 click a row to arm, again to confirm";
|
|
91280
91343
|
var MCP_TOOLS_COMMAND = "/integrations";
|
|
91281
91344
|
function isMcpToolsCommand(line) {
|
|
91282
91345
|
return (line.trim().split(/\s+/)[0] ?? "") === MCP_TOOLS_COMMAND;
|
|
@@ -91294,10 +91357,87 @@ var RUNTIME_LABELS = {
|
|
|
91294
91357
|
function runtimeLabel(id) {
|
|
91295
91358
|
return RUNTIME_LABELS[id] ?? id;
|
|
91296
91359
|
}
|
|
91297
|
-
|
|
91298
|
-
|
|
91299
|
-
|
|
91300
|
-
|
|
91360
|
+
var MIN_HANGING_BUDGET = 24;
|
|
91361
|
+
var FALLBACK_INDENT = 4;
|
|
91362
|
+
function splitLongWord(word, budget) {
|
|
91363
|
+
const pieces = [];
|
|
91364
|
+
let rest = word;
|
|
91365
|
+
while (rest.length > budget) {
|
|
91366
|
+
const slash = rest.lastIndexOf("/", budget);
|
|
91367
|
+
const cut = slash > 0 ? slash : budget;
|
|
91368
|
+
pieces.push(rest.slice(0, cut));
|
|
91369
|
+
rest = rest.slice(cut);
|
|
91370
|
+
}
|
|
91371
|
+
pieces.push(rest);
|
|
91372
|
+
return pieces;
|
|
91373
|
+
}
|
|
91374
|
+
function wrapHangingRow(head, text, width, indent = head.length) {
|
|
91375
|
+
if (width === undefined) {
|
|
91376
|
+
return [`${head}${text}`.trimEnd()];
|
|
91377
|
+
}
|
|
91378
|
+
const hang = width - indent >= MIN_HANGING_BUDGET ? indent : Math.min(indent, FALLBACK_INDENT);
|
|
91379
|
+
const pad = " ".repeat(hang);
|
|
91380
|
+
const budget = Math.max(1, width - hang);
|
|
91381
|
+
const out = [];
|
|
91382
|
+
let current = head;
|
|
91383
|
+
let fresh = true;
|
|
91384
|
+
const units = [];
|
|
91385
|
+
for (const match of text.matchAll(/(\S+)( *)/g)) {
|
|
91386
|
+
const word = match[1];
|
|
91387
|
+
const last = units.at(-1);
|
|
91388
|
+
if (last !== undefined && last.gap === " " && last.word.includes("/") && word.includes("/") && !/^[/~-]/.test(word)) {
|
|
91389
|
+
last.word += ` ${word}`;
|
|
91390
|
+
last.gap = match[2];
|
|
91391
|
+
continue;
|
|
91392
|
+
}
|
|
91393
|
+
units.push({ word, gap: match[2] });
|
|
91394
|
+
}
|
|
91395
|
+
let gap = "";
|
|
91396
|
+
for (const unit of units) {
|
|
91397
|
+
for (const [i, piece] of splitLongWord(unit.word, budget).entries()) {
|
|
91398
|
+
const sep = fresh || i > 0 ? "" : gap;
|
|
91399
|
+
if (current.length + sep.length + piece.length <= width || fresh && current === pad) {
|
|
91400
|
+
current += sep + piece;
|
|
91401
|
+
} else {
|
|
91402
|
+
out.push(current.trimEnd());
|
|
91403
|
+
current = pad + piece;
|
|
91404
|
+
}
|
|
91405
|
+
fresh = false;
|
|
91406
|
+
}
|
|
91407
|
+
gap = unit.gap;
|
|
91408
|
+
}
|
|
91409
|
+
out.push(current.trimEnd());
|
|
91410
|
+
return out;
|
|
91411
|
+
}
|
|
91412
|
+
var TOOL_NAME_COLUMN = 28;
|
|
91413
|
+
var TOOL_APPROVAL_COLUMN = 8;
|
|
91414
|
+
function approvalLabel(risk) {
|
|
91415
|
+
const value = risk ?? "read";
|
|
91416
|
+
return value === "read" ? "none" : value;
|
|
91417
|
+
}
|
|
91418
|
+
var TOOLS_COLUMN_HEADER = `${"tool".padEnd(TOOL_NAME_COLUMN)} ${"approval".padEnd(TOOL_APPROVAL_COLUMN)} description`;
|
|
91419
|
+
function formatToolRowLines(tool, width) {
|
|
91420
|
+
const head = `${tool.name.padEnd(TOOL_NAME_COLUMN)} ${approvalLabel(tool.risk).padEnd(TOOL_APPROVAL_COLUMN)} `;
|
|
91421
|
+
return wrapHangingRow(head, tool.description ?? "", width, TOOL_NAME_COLUMN + TOOL_APPROVAL_COLUMN + 2);
|
|
91422
|
+
}
|
|
91423
|
+
function fitRowsByLines(lineCounts, start, rows) {
|
|
91424
|
+
if (lineCounts.length === 0) {
|
|
91425
|
+
return { start: 0, end: 0 };
|
|
91426
|
+
}
|
|
91427
|
+
let maxStart = lineCounts.length - 1;
|
|
91428
|
+
let tail = lineCounts[maxStart] ?? 1;
|
|
91429
|
+
while (maxStart > 0 && tail + (lineCounts[maxStart - 1] ?? 1) <= rows) {
|
|
91430
|
+
maxStart -= 1;
|
|
91431
|
+
tail += lineCounts[maxStart] ?? 1;
|
|
91432
|
+
}
|
|
91433
|
+
const from = Math.min(maxStart, Math.max(0, start));
|
|
91434
|
+
let end = from + 1;
|
|
91435
|
+
let used = lineCounts[from] ?? 1;
|
|
91436
|
+
while (end < lineCounts.length && used + (lineCounts[end] ?? 1) <= rows) {
|
|
91437
|
+
used += lineCounts[end] ?? 1;
|
|
91438
|
+
end += 1;
|
|
91439
|
+
}
|
|
91440
|
+
return { start: from, end };
|
|
91301
91441
|
}
|
|
91302
91442
|
function isActionable(id) {
|
|
91303
91443
|
return id !== "generic";
|
|
@@ -91312,7 +91452,7 @@ function formatOtherServers(otherServers) {
|
|
|
91312
91452
|
const list = rest > 0 ? `${shown.join(", ")}, +${rest} more` : shown.join(", ");
|
|
91313
91453
|
return ` \xB7 also has: ${list}`;
|
|
91314
91454
|
}
|
|
91315
|
-
function formatMcpRowLine(runtime, isSelected, status) {
|
|
91455
|
+
function formatMcpRowLine(runtime, isSelected, status, width) {
|
|
91316
91456
|
const mark = isSelected ? ">" : " ";
|
|
91317
91457
|
const label = runtimeLabel(runtime.id).padEnd(20);
|
|
91318
91458
|
const statusText = runtime.connected ? "\u25CF keryx connected" : "\u25CB keryx not connected";
|
|
@@ -91328,7 +91468,8 @@ function formatMcpRowLine(runtime, isSelected, status) {
|
|
|
91328
91468
|
} else {
|
|
91329
91469
|
action = runtime.connected ? " [d] disconnect" : " [c] connect";
|
|
91330
91470
|
}
|
|
91331
|
-
return `${mark} ${label}
|
|
91471
|
+
return wrapHangingRow(`${mark} ${label} `, `${statusText}${action}${formatOtherServers(runtime.otherServers)}`, width).join(`
|
|
91472
|
+
`);
|
|
91332
91473
|
}
|
|
91333
91474
|
function asRowTarget(body) {
|
|
91334
91475
|
const parent = body;
|
|
@@ -91352,6 +91493,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
|
|
|
91352
91493
|
let rowCtor;
|
|
91353
91494
|
let activeRenderer;
|
|
91354
91495
|
let unsubscribeKey;
|
|
91496
|
+
let bodyWidth;
|
|
91355
91497
|
const rendererHint = options.renderer ?? chrome?.renderer;
|
|
91356
91498
|
const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
|
|
91357
91499
|
const paintToolsRows = () => {
|
|
@@ -91364,11 +91506,23 @@ function presentMcpTools(openModal, otui, chrome, options) {
|
|
|
91364
91506
|
toolsBody.add(new rowCtor(activeRenderer, { id: "mcp-tools-empty", content: "No tools available." }));
|
|
91365
91507
|
return;
|
|
91366
91508
|
}
|
|
91367
|
-
|
|
91368
|
-
|
|
91369
|
-
|
|
91509
|
+
toolsBody.add(new rowCtor(activeRenderer, { id: "mcp-tools-columns", content: TOOLS_COLUMN_HEADER }));
|
|
91510
|
+
const rows = options.tools.map((tool) => formatToolRowLines(tool, bodyWidth));
|
|
91511
|
+
const window = toolsWindow(rows);
|
|
91512
|
+
toolsScroll = window.start;
|
|
91513
|
+
for (let index = window.start;index < window.end; index++) {
|
|
91514
|
+
toolsBody.add(new rowCtor(activeRenderer, {
|
|
91515
|
+
id: `mcp-tool-row-${index}`,
|
|
91516
|
+
content: (rows[index] ?? []).join(`
|
|
91517
|
+
`)
|
|
91518
|
+
}));
|
|
91370
91519
|
}
|
|
91371
91520
|
};
|
|
91521
|
+
const toolsRowBudget = () => {
|
|
91522
|
+
const captionLines = wrapHangingRow("", TOOLS_TAB_HEADER, bodyWidth, 0).length;
|
|
91523
|
+
return Math.max(1, bodyRows - captionLines - 1);
|
|
91524
|
+
};
|
|
91525
|
+
const toolsWindow = (rows) => fitRowsByLines(rows.map((lines) => lines.length), toolsScroll, toolsRowBudget());
|
|
91372
91526
|
const paintMcpRows = () => {
|
|
91373
91527
|
if (mcpBody === undefined || rowCtor === undefined) {
|
|
91374
91528
|
return;
|
|
@@ -91380,18 +91534,19 @@ function presentMcpTools(openModal, otui, chrome, options) {
|
|
|
91380
91534
|
mcpBody.add(new rowCtor(activeRenderer, { id: "mcp-mcp-empty", content: "No MCP client runtimes registered." }));
|
|
91381
91535
|
return;
|
|
91382
91536
|
}
|
|
91537
|
+
mcpBody.add(new rowCtor(activeRenderer, { id: "mcp-mcp-keys", content: MCP_TAB_KEYS }));
|
|
91383
91538
|
const start = clampScroll3(mcpScroll, runtimes.length, bodyRows);
|
|
91384
91539
|
for (const [i, runtime] of runtimes.slice(start, start + bodyRows).entries()) {
|
|
91385
91540
|
const index = start + i;
|
|
91386
91541
|
mcpBody.add(new rowCtor(activeRenderer, {
|
|
91387
91542
|
id: `mcp-row-${runtime.id}`,
|
|
91388
|
-
content: formatMcpRowLine(runtime, index === mcpSelected, status),
|
|
91543
|
+
content: formatMcpRowLine(runtime, index === mcpSelected, status, bodyWidth),
|
|
91389
91544
|
onMouseDown: () => handleRowClick(runtime.id, index)
|
|
91390
91545
|
}));
|
|
91391
91546
|
}
|
|
91392
91547
|
};
|
|
91393
91548
|
const paint = () => {
|
|
91394
|
-
toolsScroll =
|
|
91549
|
+
toolsScroll = Math.max(0, toolsScroll);
|
|
91395
91550
|
mcpScroll = scrollToReveal3(mcpSelected, mcpScroll, bodyRows);
|
|
91396
91551
|
mcpScroll = clampScroll3(mcpScroll, runtimes.length, bodyRows);
|
|
91397
91552
|
paintToolsRows();
|
|
@@ -91474,9 +91629,10 @@ function presentMcpTools(openModal, otui, chrome, options) {
|
|
|
91474
91629
|
}
|
|
91475
91630
|
rowCtor = ctor;
|
|
91476
91631
|
activeRenderer = renderer;
|
|
91632
|
+
bodyWidth = ctx?.width;
|
|
91477
91633
|
if (tabId === "tools") {
|
|
91478
91634
|
toolsBody = target;
|
|
91479
|
-
toolsScroll =
|
|
91635
|
+
toolsScroll = Math.max(0, toolsScroll);
|
|
91480
91636
|
paintToolsRows();
|
|
91481
91637
|
return;
|
|
91482
91638
|
}
|
|
@@ -91524,7 +91680,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
|
|
|
91524
91680
|
if (onMcp) {
|
|
91525
91681
|
moveMcpSelection(mcpSelected - 1);
|
|
91526
91682
|
} else {
|
|
91527
|
-
toolsScroll =
|
|
91683
|
+
toolsScroll = Math.max(0, toolsScroll - 1);
|
|
91528
91684
|
paint();
|
|
91529
91685
|
}
|
|
91530
91686
|
return;
|
|
@@ -91533,7 +91689,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
|
|
|
91533
91689
|
if (onMcp) {
|
|
91534
91690
|
moveMcpSelection(mcpSelected + 1);
|
|
91535
91691
|
} else {
|
|
91536
|
-
toolsScroll
|
|
91692
|
+
toolsScroll += 1;
|
|
91537
91693
|
paint();
|
|
91538
91694
|
}
|
|
91539
91695
|
return;
|
|
@@ -91543,7 +91699,7 @@ function presentMcpTools(openModal, otui, chrome, options) {
|
|
|
91543
91699
|
if (onMcp) {
|
|
91544
91700
|
mcpScroll = clampScroll3(mcpScroll + step, runtimes.length, bodyRows);
|
|
91545
91701
|
} else {
|
|
91546
|
-
toolsScroll =
|
|
91702
|
+
toolsScroll = Math.max(0, toolsScroll + step);
|
|
91547
91703
|
}
|
|
91548
91704
|
paint();
|
|
91549
91705
|
}
|
|
@@ -91556,6 +91712,7 @@ function openMcpTools(otui, chrome, options) {
|
|
|
91556
91712
|
}
|
|
91557
91713
|
|
|
91558
91714
|
// src/tui/mcp-consumer.ts
|
|
91715
|
+
import { homedir as homedir9 } from "os";
|
|
91559
91716
|
var MCP_CONSUMER_COMMAND = "/mcp";
|
|
91560
91717
|
function isMcpConsumerCommand(line) {
|
|
91561
91718
|
return (line.trim().split(/\s+/)[0] ?? "") === MCP_CONSUMER_COMMAND;
|
|
@@ -91584,12 +91741,21 @@ function credentialNames(server) {
|
|
|
91584
91741
|
}
|
|
91585
91742
|
return [...names].sort();
|
|
91586
91743
|
}
|
|
91587
|
-
|
|
91744
|
+
var MAX_TARGET_CHARS = 240;
|
|
91745
|
+
function collapseHome2(text, home) {
|
|
91746
|
+
const trimmed = home.replace(/\/+$/, "");
|
|
91747
|
+
if (trimmed === "")
|
|
91748
|
+
return text;
|
|
91749
|
+
const escaped = trimmed.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
91750
|
+
return text.replace(new RegExp(`(^|[\\s="'])${escaped}(?=/|$|[\\s"'])`, "g"), "$1~");
|
|
91751
|
+
}
|
|
91752
|
+
function targetOf(server, home) {
|
|
91588
91753
|
const raw = server.raw;
|
|
91589
|
-
const target = typeof raw.url === "string" && raw.url.length > 0 ? displayUrl(raw.url) : [raw.command, ...raw.args ?? []].filter(Boolean).join(" ");
|
|
91590
|
-
return sanitiseIdentifier(target);
|
|
91754
|
+
const target = typeof raw.url === "string" && raw.url.length > 0 ? displayUrl(raw.url) : collapseHome2([raw.command, ...raw.args ?? []].filter(Boolean).join(" "), home);
|
|
91755
|
+
return sanitiseIdentifier(target, MAX_TARGET_CHARS);
|
|
91591
91756
|
}
|
|
91592
91757
|
function buildConsumerModel(input) {
|
|
91758
|
+
const home = input.home ?? homedir9();
|
|
91593
91759
|
const stateByName = new Map(input.states.map((s) => [s.name, s]));
|
|
91594
91760
|
const rows = input.configured.map((server) => {
|
|
91595
91761
|
const state = stateByName.get(server.name);
|
|
@@ -91601,7 +91767,7 @@ function buildConsumerModel(input) {
|
|
|
91601
91767
|
transport: transportOf(server),
|
|
91602
91768
|
status,
|
|
91603
91769
|
toolCount: state?.toolCount ?? 0,
|
|
91604
|
-
target: targetOf(server),
|
|
91770
|
+
target: targetOf(server, home),
|
|
91605
91771
|
credentials: credentialNames(server),
|
|
91606
91772
|
detail,
|
|
91607
91773
|
action: status === "needs-approval" ? `keryx mcp trust ${server.name}` : status === "needs_auth" ? "set the variable named above, then restart the session" : undefined
|
|
@@ -91609,10 +91775,10 @@ function buildConsumerModel(input) {
|
|
|
91609
91775
|
});
|
|
91610
91776
|
return {
|
|
91611
91777
|
rows,
|
|
91612
|
-
problems: input.problems.map((p) => `${p.file}: ${sanitiseForDisplay(p.message)}`),
|
|
91778
|
+
problems: input.problems.map((p) => `${collapseHome2(p.file, home)}: ${sanitiseForDisplay(p.message)}`),
|
|
91613
91779
|
emptyHint: rows.length > 0 ? undefined : `No MCP servers configured.
|
|
91614
|
-
user: ${input.userFile}
|
|
91615
|
-
project: ${input.projectFile}`
|
|
91780
|
+
user: ${collapseHome2(input.userFile, home)}
|
|
91781
|
+
project: ${collapseHome2(input.projectFile, home)}`
|
|
91616
91782
|
};
|
|
91617
91783
|
}
|
|
91618
91784
|
var MCP_CONSUMER_FOOTER = [
|
|
@@ -91641,7 +91807,7 @@ function consumerActionFor(status) {
|
|
|
91641
91807
|
function statusGlyph(status) {
|
|
91642
91808
|
return STATUS_GLYPH[status] ?? status;
|
|
91643
91809
|
}
|
|
91644
|
-
function formatConsumerModalRow(row, isSelected, status) {
|
|
91810
|
+
function formatConsumerModalRow(row, isSelected, status, width) {
|
|
91645
91811
|
const mark = isSelected ? ">" : " ";
|
|
91646
91812
|
const name = row.name.padEnd(18);
|
|
91647
91813
|
const source = row.source.padEnd(10);
|
|
@@ -91666,7 +91832,8 @@ function formatConsumerModalRow(row, isSelected, status) {
|
|
|
91666
91832
|
action = "";
|
|
91667
91833
|
}
|
|
91668
91834
|
const creds = row.credentials.length > 0 ? ` [reads ${row.credentials.join(", ")}]` : "";
|
|
91669
|
-
return `${mark} ${name} ${source} ${transport} ${glyph}
|
|
91835
|
+
return wrapHangingRow(`${mark} ${name} ${source} ${transport} ${glyph}`, `${tools}${action} ${row.target}${creds}`, width).join(`
|
|
91836
|
+
`);
|
|
91670
91837
|
}
|
|
91671
91838
|
function formatConsumerModalLines(model, selected, status) {
|
|
91672
91839
|
if (model === undefined) {
|
|
@@ -91707,6 +91874,7 @@ function presentMcpConsumer(openModalFn, otui, chrome, options) {
|
|
|
91707
91874
|
let rowCtor;
|
|
91708
91875
|
let activeRenderer;
|
|
91709
91876
|
let unsubscribeKey;
|
|
91877
|
+
let bodyWidth;
|
|
91710
91878
|
const rendererHint = options.renderer ?? chrome?.renderer;
|
|
91711
91879
|
const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
|
|
91712
91880
|
const rowsOf = () => options.snapshot()?.rows ?? [];
|
|
@@ -91739,7 +91907,7 @@ function presentMcpConsumer(openModalFn, otui, chrome, options) {
|
|
|
91739
91907
|
const index = start + i;
|
|
91740
91908
|
body.add(new rowCtor(activeRenderer, {
|
|
91741
91909
|
id: `mcp-consumer-row-${row.name}`,
|
|
91742
|
-
content: formatConsumerModalRow(row, index === selected, status),
|
|
91910
|
+
content: formatConsumerModalRow(row, index === selected, status, bodyWidth),
|
|
91743
91911
|
onMouseDown: () => handleRowClick(row.name, index)
|
|
91744
91912
|
}));
|
|
91745
91913
|
}
|
|
@@ -91810,6 +91978,7 @@ function presentMcpConsumer(openModalFn, otui, chrome, options) {
|
|
|
91810
91978
|
rowCtor = ctor;
|
|
91811
91979
|
activeRenderer = renderer;
|
|
91812
91980
|
body = target;
|
|
91981
|
+
bodyWidth = ctx?.width;
|
|
91813
91982
|
paint();
|
|
91814
91983
|
},
|
|
91815
91984
|
onClose: () => {
|
|
@@ -97618,30 +97787,31 @@ var CUSTOM_ADD_ENTRY = {
|
|
|
97618
97787
|
label: "\uFF0B Add custom provider (OpenAI-compatible)",
|
|
97619
97788
|
note: "name \xB7 URL \xB7 key \xB7 models \u2192 saved to llm-providers.json"
|
|
97620
97789
|
};
|
|
97621
|
-
function promptCustomFieldStep(otui,
|
|
97790
|
+
function promptCustomFieldStep(otui, target, opts) {
|
|
97791
|
+
const r = stepRenderer(target);
|
|
97622
97792
|
return new Promise((resolve) => {
|
|
97623
|
-
const
|
|
97624
|
-
|
|
97625
|
-
|
|
97793
|
+
const surface = openStepSurface(otui, target, {
|
|
97794
|
+
id: "custom-field-picker",
|
|
97795
|
+
title: opts.title,
|
|
97796
|
+
tab: "Custom provider",
|
|
97797
|
+
hint: "(Enter \xB7 Esc to go back)",
|
|
97798
|
+
footer: inputStepFooter("back"),
|
|
97799
|
+
contentRows: opts.note !== undefined ? 4 : 2,
|
|
97800
|
+
onEscape: () => {
|
|
97801
|
+
input.blur();
|
|
97802
|
+
resolve({ kind: "back" });
|
|
97803
|
+
}
|
|
97804
|
+
});
|
|
97626
97805
|
if (opts.note !== undefined) {
|
|
97627
|
-
|
|
97806
|
+
surface.body.add(new otui.TextRenderable(r, { id: "cf-note", content: otui.t`${otui.dim(opts.note)}`, marginTop: 1 }));
|
|
97628
97807
|
}
|
|
97629
97808
|
const input = new otui.InputRenderable(r, { id: "cf-input", value: opts.value ?? "", marginTop: 1 });
|
|
97630
|
-
|
|
97809
|
+
surface.body.add(input);
|
|
97631
97810
|
input.focus();
|
|
97632
97811
|
const cleanup = () => {
|
|
97633
|
-
unsub();
|
|
97634
97812
|
input.blur();
|
|
97635
|
-
|
|
97813
|
+
surface.close();
|
|
97636
97814
|
};
|
|
97637
|
-
const unsub = onKeypress4(r, (key) => {
|
|
97638
|
-
if (key.name === "escape") {
|
|
97639
|
-
cleanup();
|
|
97640
|
-
resolve({ kind: "back" });
|
|
97641
|
-
key.preventDefault();
|
|
97642
|
-
key.stopPropagation();
|
|
97643
|
-
}
|
|
97644
|
-
});
|
|
97645
97815
|
input.on(otui.InputRenderableEvents.ENTER, () => {
|
|
97646
97816
|
const entered = input.value.trim();
|
|
97647
97817
|
cleanup();
|
|
@@ -97661,9 +97831,9 @@ function parseOptionalPositiveWizardNumber(raw) {
|
|
|
97661
97831
|
const parsed = parseOptionalWizardNumber(raw);
|
|
97662
97832
|
return parsed !== undefined && parsed > 0 ? parsed : undefined;
|
|
97663
97833
|
}
|
|
97664
|
-
async function promptCustomProviderWizard(otui,
|
|
97834
|
+
async function promptCustomProviderWizard(otui, target) {
|
|
97665
97835
|
while (true) {
|
|
97666
|
-
const nameStep = await promptCustomFieldStep(otui,
|
|
97836
|
+
const nameStep = await promptCustomFieldStep(otui, target, {
|
|
97667
97837
|
title: "Provider name",
|
|
97668
97838
|
note: "unique id used in /provider (letters, digits, - _)"
|
|
97669
97839
|
});
|
|
@@ -97675,7 +97845,7 @@ async function promptCustomProviderWizard(otui, r) {
|
|
|
97675
97845
|
continue;
|
|
97676
97846
|
}
|
|
97677
97847
|
while (true) {
|
|
97678
|
-
const urlStep = await promptCustomFieldStep(otui,
|
|
97848
|
+
const urlStep = await promptCustomFieldStep(otui, target, {
|
|
97679
97849
|
title: "Base URL",
|
|
97680
97850
|
note: "before the chat path \u2014 /v1/chat/completions is appended, e.g. http://10.110.43.19:8080",
|
|
97681
97851
|
value: "http://localhost:8000"
|
|
@@ -97697,7 +97867,7 @@ async function promptCustomProviderWizard(otui, r) {
|
|
|
97697
97867
|
continue;
|
|
97698
97868
|
}
|
|
97699
97869
|
const baseUrl = baseUrlRaw.replace(/\/+$/, "");
|
|
97700
|
-
const keyStep = await promptCustomFieldStep(otui,
|
|
97870
|
+
const keyStep = await promptCustomFieldStep(otui, target, {
|
|
97701
97871
|
title: "API key (optional)",
|
|
97702
97872
|
note: "empty = no key \xB7 saved owner-only (0600) in your keryx config dir"
|
|
97703
97873
|
});
|
|
@@ -97705,7 +97875,7 @@ async function promptCustomProviderWizard(otui, r) {
|
|
|
97705
97875
|
continue;
|
|
97706
97876
|
}
|
|
97707
97877
|
const apiKey = keyStep.value.length > 0 ? keyStep.value : undefined;
|
|
97708
|
-
const modelsStep = await promptCustomFieldStep(otui,
|
|
97878
|
+
const modelsStep = await promptCustomFieldStep(otui, target, {
|
|
97709
97879
|
title: "Models (optional)",
|
|
97710
97880
|
note: "comma-separated ids \xB7 empty = fetch the live /v1/models list"
|
|
97711
97881
|
});
|
|
@@ -97713,7 +97883,7 @@ async function promptCustomProviderWizard(otui, r) {
|
|
|
97713
97883
|
continue;
|
|
97714
97884
|
}
|
|
97715
97885
|
const models = modelsStep.value.split(",").map((m) => m.trim()).filter((m) => m.length > 0);
|
|
97716
|
-
const temperatureStep = await promptCustomFieldStep(otui,
|
|
97886
|
+
const temperatureStep = await promptCustomFieldStep(otui, target, {
|
|
97717
97887
|
title: "Temperature (optional)",
|
|
97718
97888
|
note: "empty = provider default \xB7 e.g. 0.2"
|
|
97719
97889
|
});
|
|
@@ -97721,7 +97891,7 @@ async function promptCustomProviderWizard(otui, r) {
|
|
|
97721
97891
|
continue;
|
|
97722
97892
|
}
|
|
97723
97893
|
const temperature = parseOptionalWizardNumber(temperatureStep.value);
|
|
97724
|
-
const maxOutputTokensStep = await promptCustomFieldStep(otui,
|
|
97894
|
+
const maxOutputTokensStep = await promptCustomFieldStep(otui, target, {
|
|
97725
97895
|
title: "Max output tokens (optional)",
|
|
97726
97896
|
note: "empty = default 1024 \xB7 e.g. 4096"
|
|
97727
97897
|
});
|
|
@@ -97729,7 +97899,7 @@ async function promptCustomProviderWizard(otui, r) {
|
|
|
97729
97899
|
continue;
|
|
97730
97900
|
}
|
|
97731
97901
|
const maxOutputTokens = parseOptionalPositiveWizardNumber(maxOutputTokensStep.value);
|
|
97732
|
-
const timeoutMsStep = await promptCustomFieldStep(otui,
|
|
97902
|
+
const timeoutMsStep = await promptCustomFieldStep(otui, target, {
|
|
97733
97903
|
title: "Request timeout ms (optional)",
|
|
97734
97904
|
note: "empty = no engine-internal timeout \xB7 e.g. 180000"
|
|
97735
97905
|
});
|
|
@@ -98084,6 +98254,78 @@ function onKeypress4(r, handler) {
|
|
|
98084
98254
|
r._internalKeyInput.onInternal("keypress", handler);
|
|
98085
98255
|
return () => r._internalKeyInput.offInternal("keypress", handler);
|
|
98086
98256
|
}
|
|
98257
|
+
function isModalChrome(target) {
|
|
98258
|
+
return target !== null && typeof target === "object" && "renderer" in target && "focusComposer" in target;
|
|
98259
|
+
}
|
|
98260
|
+
function stepRenderer(target) {
|
|
98261
|
+
return isModalChrome(target) ? target.renderer : target;
|
|
98262
|
+
}
|
|
98263
|
+
function openStepSurface(otui, target, opts) {
|
|
98264
|
+
const r = stepRenderer(target);
|
|
98265
|
+
if (!isModalChrome(target)) {
|
|
98266
|
+
const box = overlayBox(otui, r, opts.id);
|
|
98267
|
+
r.root.add(box);
|
|
98268
|
+
box.add(new otui.TextRenderable(r, { id: `${opts.id}-title`, content: otui.t`${otui.bold(opts.title)} ${otui.dim(opts.hint)}` }));
|
|
98269
|
+
let closed = false;
|
|
98270
|
+
const close = () => {
|
|
98271
|
+
if (closed)
|
|
98272
|
+
return;
|
|
98273
|
+
closed = true;
|
|
98274
|
+
unsub();
|
|
98275
|
+
r.root.remove(box);
|
|
98276
|
+
};
|
|
98277
|
+
const unsub = onKeypress4(r, (key) => {
|
|
98278
|
+
if (key.name === "escape") {
|
|
98279
|
+
close();
|
|
98280
|
+
opts.onEscape();
|
|
98281
|
+
key.preventDefault();
|
|
98282
|
+
key.stopPropagation();
|
|
98283
|
+
}
|
|
98284
|
+
});
|
|
98285
|
+
return { body: box, modal: false, close };
|
|
98286
|
+
}
|
|
98287
|
+
let body;
|
|
98288
|
+
let closedByStep = false;
|
|
98289
|
+
const handle = openModal(otui, target, {
|
|
98290
|
+
title: opts.title,
|
|
98291
|
+
tabs: [{ id: opts.id, label: opts.tab }],
|
|
98292
|
+
footer: opts.footer,
|
|
98293
|
+
contentRows: opts.contentRows,
|
|
98294
|
+
renderTab: (_tabId, tabBody) => {
|
|
98295
|
+
body = tabBody;
|
|
98296
|
+
},
|
|
98297
|
+
onClose: () => {
|
|
98298
|
+
if (!closedByStep)
|
|
98299
|
+
opts.onEscape();
|
|
98300
|
+
}
|
|
98301
|
+
});
|
|
98302
|
+
if (handle === undefined || body === undefined) {
|
|
98303
|
+
return openStepSurface(otui, r, opts);
|
|
98304
|
+
}
|
|
98305
|
+
return {
|
|
98306
|
+
body,
|
|
98307
|
+
modal: true,
|
|
98308
|
+
close: () => {
|
|
98309
|
+
if (closedByStep)
|
|
98310
|
+
return;
|
|
98311
|
+
closedByStep = true;
|
|
98312
|
+
handle.close({ restoreFocus: false });
|
|
98313
|
+
}
|
|
98314
|
+
};
|
|
98315
|
+
}
|
|
98316
|
+
function selectStepFooter(escLabel) {
|
|
98317
|
+
return [
|
|
98318
|
+
{ key: "\u2191/\u2193", label: "select" },
|
|
98319
|
+
{ key: "Enter", label: "confirm" },
|
|
98320
|
+
{ key: "esc", label: escLabel }
|
|
98321
|
+
];
|
|
98322
|
+
}
|
|
98323
|
+
function inputStepFooter(escLabel) {
|
|
98324
|
+
return [
|
|
98325
|
+
{ key: "Enter", label: "confirm" },
|
|
98326
|
+
{ key: "esc", label: escLabel }
|
|
98327
|
+
];
|
|
98328
|
+
}
|
|
98087
98329
|
function parseSearchProviderArgs(line) {
|
|
98088
98330
|
const parts = line.trim().length === 0 ? [] : line.trim().split(/\s+/);
|
|
98089
98331
|
const [providerId, ...tail] = parts;
|
|
@@ -98130,27 +98372,28 @@ async function selectSearchProviderAndReport(controller, onSystem, providerId) {
|
|
|
98130
98372
|
onSystem?.(`Search provider '${providerId}' selected.
|
|
98131
98373
|
`);
|
|
98132
98374
|
}
|
|
98133
|
-
function promptSearchFieldStep(otui,
|
|
98375
|
+
function promptSearchFieldStep(otui, target, field, value) {
|
|
98376
|
+
const r = stepRenderer(target);
|
|
98134
98377
|
return new Promise((resolve) => {
|
|
98135
|
-
const
|
|
98136
|
-
|
|
98137
|
-
|
|
98378
|
+
const surface = openStepSurface(otui, target, {
|
|
98379
|
+
id: "search-field-picker",
|
|
98380
|
+
title: field.label,
|
|
98381
|
+
tab: field.required ? "Required" : "Optional",
|
|
98382
|
+
hint: `${field.required ? "required" : "optional"} \xB7 Enter \xB7 Esc to go back`,
|
|
98383
|
+
footer: inputStepFooter("back"),
|
|
98384
|
+
contentRows: 2,
|
|
98385
|
+
onEscape: () => {
|
|
98386
|
+
input.blur();
|
|
98387
|
+
resolve({ kind: "back" });
|
|
98388
|
+
}
|
|
98389
|
+
});
|
|
98138
98390
|
const input = new otui.InputRenderable(r, { id: "sf-input", value, marginTop: 1 });
|
|
98139
|
-
|
|
98391
|
+
surface.body.add(input);
|
|
98140
98392
|
input.focus();
|
|
98141
98393
|
const cleanup = () => {
|
|
98142
|
-
unsub();
|
|
98143
98394
|
input.blur();
|
|
98144
|
-
|
|
98395
|
+
surface.close();
|
|
98145
98396
|
};
|
|
98146
|
-
const unsub = onKeypress4(r, (key) => {
|
|
98147
|
-
if (key.name === "escape") {
|
|
98148
|
-
cleanup();
|
|
98149
|
-
resolve({ kind: "back" });
|
|
98150
|
-
key.preventDefault();
|
|
98151
|
-
key.stopPropagation();
|
|
98152
|
-
}
|
|
98153
|
-
});
|
|
98154
98397
|
input.on(otui.InputRenderableEvents.ENTER, () => {
|
|
98155
98398
|
const entered = input.value.trim();
|
|
98156
98399
|
cleanup();
|
|
@@ -98158,32 +98401,33 @@ function promptSearchFieldStep(otui, r, field, value) {
|
|
|
98158
98401
|
});
|
|
98159
98402
|
});
|
|
98160
98403
|
}
|
|
98161
|
-
function promptSearchCredentialStep(otui,
|
|
98404
|
+
function promptSearchCredentialStep(otui, target, opts) {
|
|
98405
|
+
const r = stepRenderer(target);
|
|
98162
98406
|
return new Promise((resolve) => {
|
|
98163
|
-
const
|
|
98164
|
-
|
|
98165
|
-
|
|
98166
|
-
|
|
98407
|
+
const surface = openStepSurface(otui, target, {
|
|
98408
|
+
id: "search-credential-picker",
|
|
98409
|
+
title: `Paste your ${opts.label}`,
|
|
98410
|
+
tab: "Credential",
|
|
98411
|
+
hint: "(Enter \xB7 Esc to go back)",
|
|
98412
|
+
footer: inputStepFooter("back"),
|
|
98413
|
+
contentRows: 4,
|
|
98414
|
+
onEscape: () => {
|
|
98415
|
+
keyInput.blur();
|
|
98416
|
+
resolve({ kind: "back" });
|
|
98417
|
+
}
|
|
98418
|
+
});
|
|
98419
|
+
surface.body.add(new otui.TextRenderable(r, {
|
|
98167
98420
|
id: "sc-note",
|
|
98168
98421
|
content: otui.t`${otui.dim("Saved to your keryx config dir (owner-only, 0600)")}`,
|
|
98169
98422
|
marginTop: 1
|
|
98170
98423
|
}));
|
|
98171
98424
|
const keyInput = new otui.InputRenderable(r, { id: "sc-input", placeholder: "...", marginTop: 1 });
|
|
98172
|
-
|
|
98425
|
+
surface.body.add(keyInput);
|
|
98173
98426
|
keyInput.focus();
|
|
98174
98427
|
const cleanup = () => {
|
|
98175
|
-
unsub();
|
|
98176
98428
|
keyInput.blur();
|
|
98177
|
-
|
|
98429
|
+
surface.close();
|
|
98178
98430
|
};
|
|
98179
|
-
const unsub = onKeypress4(r, (key) => {
|
|
98180
|
-
if (key.name === "escape") {
|
|
98181
|
-
cleanup();
|
|
98182
|
-
resolve({ kind: "back" });
|
|
98183
|
-
key.preventDefault();
|
|
98184
|
-
key.stopPropagation();
|
|
98185
|
-
}
|
|
98186
|
-
});
|
|
98187
98431
|
keyInput.on(otui.InputRenderableEvents.ENTER, () => {
|
|
98188
98432
|
const value = keyInput.value.trim();
|
|
98189
98433
|
cleanup();
|
|
@@ -98191,14 +98435,24 @@ function promptSearchCredentialStep(otui, r, opts) {
|
|
|
98191
98435
|
});
|
|
98192
98436
|
});
|
|
98193
98437
|
}
|
|
98194
|
-
function promptSetActiveProviderStep(otui,
|
|
98438
|
+
function promptSetActiveProviderStep(otui, target) {
|
|
98439
|
+
const r = stepRenderer(target);
|
|
98195
98440
|
return new Promise((resolve) => {
|
|
98196
|
-
const
|
|
98197
|
-
|
|
98198
|
-
|
|
98441
|
+
const surface = openStepSurface(otui, target, {
|
|
98442
|
+
id: "search-active-picker",
|
|
98443
|
+
title: "Set as active provider after a successful test?",
|
|
98444
|
+
tab: "Active",
|
|
98445
|
+
hint: "(\u2191/\u2193, Enter \xB7 Esc to go back)",
|
|
98446
|
+
footer: selectStepFooter("back"),
|
|
98447
|
+
contentRows: selectBoxHeight(2, true),
|
|
98448
|
+
onEscape: () => {
|
|
98449
|
+
select.blur();
|
|
98450
|
+
resolve(undefined);
|
|
98451
|
+
}
|
|
98452
|
+
});
|
|
98199
98453
|
const select = new otui.SelectRenderable(r, {
|
|
98200
98454
|
id: "sa-select",
|
|
98201
|
-
width: 60,
|
|
98455
|
+
width: surface.modal ? "100%" : 60,
|
|
98202
98456
|
height: selectBoxHeight(2, true),
|
|
98203
98457
|
options: [
|
|
98204
98458
|
{ name: "Yes", description: "select it once the test passes" },
|
|
@@ -98206,21 +98460,12 @@ function promptSetActiveProviderStep(otui, r) {
|
|
|
98206
98460
|
],
|
|
98207
98461
|
selectedTextColor: "#ffd166"
|
|
98208
98462
|
});
|
|
98209
|
-
|
|
98463
|
+
surface.body.add(select);
|
|
98210
98464
|
select.focus();
|
|
98211
98465
|
const cleanup = () => {
|
|
98212
|
-
unsub();
|
|
98213
98466
|
select.blur();
|
|
98214
|
-
|
|
98467
|
+
surface.close();
|
|
98215
98468
|
};
|
|
98216
|
-
const unsub = onKeypress4(r, (key) => {
|
|
98217
|
-
if (key.name === "escape") {
|
|
98218
|
-
cleanup();
|
|
98219
|
-
resolve(undefined);
|
|
98220
|
-
key.preventDefault();
|
|
98221
|
-
key.stopPropagation();
|
|
98222
|
-
}
|
|
98223
|
-
});
|
|
98224
98469
|
select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
|
|
98225
98470
|
const chosen = select.getSelectedOption();
|
|
98226
98471
|
cleanup();
|
|
@@ -98228,35 +98473,36 @@ function promptSetActiveProviderStep(otui, r) {
|
|
|
98228
98473
|
});
|
|
98229
98474
|
});
|
|
98230
98475
|
}
|
|
98231
|
-
function pickSearchProviderStep(otui,
|
|
98476
|
+
function pickSearchProviderStep(otui, target, providers) {
|
|
98477
|
+
const r = stepRenderer(target);
|
|
98232
98478
|
return new Promise((resolve) => {
|
|
98233
|
-
const
|
|
98234
|
-
|
|
98235
|
-
|
|
98479
|
+
const surface = openStepSurface(otui, target, {
|
|
98480
|
+
id: "search-provider-picker",
|
|
98481
|
+
title: "Select a search provider",
|
|
98482
|
+
tab: "Search providers",
|
|
98483
|
+
hint: "(\u2191/\u2193, Enter \xB7 Esc to cancel)",
|
|
98484
|
+
footer: selectStepFooter("cancel"),
|
|
98485
|
+
contentRows: selectBoxHeight(providers.length, true),
|
|
98486
|
+
onEscape: () => {
|
|
98487
|
+
select.blur();
|
|
98488
|
+
resolve(undefined);
|
|
98489
|
+
}
|
|
98490
|
+
});
|
|
98236
98491
|
const labelOf = (p) => `${p.id} (${p.displayName})`;
|
|
98237
98492
|
const select = new otui.SelectRenderable(r, {
|
|
98238
98493
|
id: "spp-select",
|
|
98239
|
-
width: 60,
|
|
98494
|
+
width: surface.modal ? "100%" : 60,
|
|
98240
98495
|
height: selectBoxHeight(providers.length, true),
|
|
98241
98496
|
showScrollIndicator: true,
|
|
98242
98497
|
options: providers.map((p) => ({ name: labelOf(p), description: p.kind })),
|
|
98243
98498
|
selectedTextColor: "#ffd166"
|
|
98244
98499
|
});
|
|
98245
|
-
|
|
98500
|
+
surface.body.add(select);
|
|
98246
98501
|
select.focus();
|
|
98247
98502
|
const cleanup = () => {
|
|
98248
|
-
unsub();
|
|
98249
98503
|
select.blur();
|
|
98250
|
-
|
|
98504
|
+
surface.close();
|
|
98251
98505
|
};
|
|
98252
|
-
const unsub = onKeypress4(r, (key) => {
|
|
98253
|
-
if (key.name === "escape") {
|
|
98254
|
-
cleanup();
|
|
98255
|
-
resolve(undefined);
|
|
98256
|
-
key.preventDefault();
|
|
98257
|
-
key.stopPropagation();
|
|
98258
|
-
}
|
|
98259
|
-
});
|
|
98260
98506
|
select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
|
|
98261
98507
|
const chosen = select.getSelectedOption();
|
|
98262
98508
|
cleanup();
|
|
@@ -98316,19 +98562,46 @@ async function runSearchProviderFieldsStep(otui, r, provider, seed) {
|
|
|
98316
98562
|
}
|
|
98317
98563
|
return { kind: "done", fields, credential, setActive };
|
|
98318
98564
|
}
|
|
98319
|
-
function runSearchProviderTestStep(otui,
|
|
98565
|
+
function runSearchProviderTestStep(otui, target, controller, provider, fields, credential, setActive) {
|
|
98566
|
+
const r = stepRenderer(target);
|
|
98320
98567
|
return new Promise((resolve) => {
|
|
98321
|
-
|
|
98322
|
-
|
|
98568
|
+
let settled;
|
|
98569
|
+
let left = false;
|
|
98570
|
+
const modal = isModalChrome(target);
|
|
98571
|
+
let surface;
|
|
98572
|
+
if (modal) {
|
|
98573
|
+
surface = openStepSurface(otui, target, {
|
|
98574
|
+
id: "search-test-picker",
|
|
98575
|
+
title: `Test '${provider.id}'`,
|
|
98576
|
+
tab: "Test",
|
|
98577
|
+
hint: "",
|
|
98578
|
+
footer: [
|
|
98579
|
+
{ key: "Enter", label: "close after success" },
|
|
98580
|
+
{ key: "esc", label: "back" }
|
|
98581
|
+
],
|
|
98582
|
+
contentRows: 2,
|
|
98583
|
+
onEscape: () => {
|
|
98584
|
+
left = true;
|
|
98585
|
+
unsub();
|
|
98586
|
+
resolve(settled === "success" ? "done" : "retry");
|
|
98587
|
+
}
|
|
98588
|
+
});
|
|
98589
|
+
}
|
|
98590
|
+
const box = surface?.body ?? overlayBox(otui, r, "search-test-picker");
|
|
98591
|
+
if (surface === undefined)
|
|
98592
|
+
r.root.add(box);
|
|
98323
98593
|
const status = new otui.TextRenderable(r, { id: "st-title", content: otui.t`${otui.bold(`Testing '${provider.id}'`)} ${otui.dim("...")}` });
|
|
98324
98594
|
box.add(status);
|
|
98325
|
-
let settled;
|
|
98326
98595
|
const cleanup = () => {
|
|
98596
|
+
left = true;
|
|
98327
98597
|
unsub();
|
|
98328
|
-
|
|
98598
|
+
if (surface !== undefined)
|
|
98599
|
+
surface.close();
|
|
98600
|
+
else
|
|
98601
|
+
r.root.remove(box);
|
|
98329
98602
|
};
|
|
98330
98603
|
const unsub = onKeypress4(r, (key) => {
|
|
98331
|
-
if (settled === "failure" && key.name === "escape") {
|
|
98604
|
+
if (!modal && settled === "failure" && key.name === "escape") {
|
|
98332
98605
|
cleanup();
|
|
98333
98606
|
resolve("retry");
|
|
98334
98607
|
key.preventDefault();
|
|
@@ -98343,6 +98616,9 @@ function runSearchProviderTestStep(otui, r, controller, provider, fields, creden
|
|
|
98343
98616
|
(async () => {
|
|
98344
98617
|
controller.configure(provider.id, { ...provider.defaults, ...fields }, credential);
|
|
98345
98618
|
const tested = await controller.test(provider.id);
|
|
98619
|
+
if (left) {
|
|
98620
|
+
return;
|
|
98621
|
+
}
|
|
98346
98622
|
if (!tested.ok) {
|
|
98347
98623
|
settled = "failure";
|
|
98348
98624
|
const reason = tested.reason === "missing-credential" ? "missing credential" : "connection validation failed";
|
|
@@ -98355,6 +98631,9 @@ function runSearchProviderTestStep(otui, r, controller, provider, fields, creden
|
|
|
98355
98631
|
return;
|
|
98356
98632
|
}
|
|
98357
98633
|
const selected = await controller.select(provider.id);
|
|
98634
|
+
if (left) {
|
|
98635
|
+
return;
|
|
98636
|
+
}
|
|
98358
98637
|
status.content = selected.ok ? otui.t`${otui.green("\u2713")} ${otui.bold(`'${provider.id}' configured, tested, and set as active`)} ${otui.dim("(Enter to close)")}` : otui.t`${otui.green("\u2713")} ${otui.bold(`'${provider.id}' configured and tested`)} ${otui.dim(`but could not be set active (${selected.reason ?? "unknown"})`)} ${otui.dim("(Enter to close)")}`;
|
|
98359
98638
|
})();
|
|
98360
98639
|
});
|
|
@@ -98381,11 +98660,22 @@ async function searchProviderWizardInTui(otui, r, controller) {
|
|
|
98381
98660
|
}
|
|
98382
98661
|
}
|
|
98383
98662
|
}
|
|
98384
|
-
function promptBaseUrlStep(otui,
|
|
98663
|
+
function promptBaseUrlStep(otui, target, label, baseUrl, notice) {
|
|
98664
|
+
const r = stepRenderer(target);
|
|
98385
98665
|
return new Promise((resolve) => {
|
|
98386
|
-
const
|
|
98387
|
-
|
|
98388
|
-
|
|
98666
|
+
const surface = openStepSurface(otui, target, {
|
|
98667
|
+
id: "base-url-picker",
|
|
98668
|
+
title: `${label} endpoint URL`,
|
|
98669
|
+
tab: "Endpoint",
|
|
98670
|
+
hint: "(Enter \xB7 Esc to go back)",
|
|
98671
|
+
footer: inputStepFooter("back"),
|
|
98672
|
+
contentRows: notice !== undefined ? 6 : 4,
|
|
98673
|
+
onEscape: () => {
|
|
98674
|
+
input.blur();
|
|
98675
|
+
resolve(undefined);
|
|
98676
|
+
}
|
|
98677
|
+
});
|
|
98678
|
+
const box = surface.body;
|
|
98389
98679
|
if (notice !== undefined) {
|
|
98390
98680
|
box.add(new otui.TextRenderable(r, { id: "bp-notice", content: otui.t`${otui.red("\u2717")} ${notice}`, marginTop: 1 }));
|
|
98391
98681
|
}
|
|
@@ -98394,17 +98684,9 @@ function promptBaseUrlStep(otui, r, label, baseUrl, notice) {
|
|
|
98394
98684
|
box.add(input);
|
|
98395
98685
|
input.focus();
|
|
98396
98686
|
const cleanup = () => {
|
|
98397
|
-
|
|
98398
|
-
|
|
98687
|
+
input.blur();
|
|
98688
|
+
surface.close();
|
|
98399
98689
|
};
|
|
98400
|
-
const unsub = onKeypress4(r, (key) => {
|
|
98401
|
-
if (key.name === "escape") {
|
|
98402
|
-
cleanup();
|
|
98403
|
-
resolve(undefined);
|
|
98404
|
-
key.preventDefault();
|
|
98405
|
-
key.stopPropagation();
|
|
98406
|
-
}
|
|
98407
|
-
});
|
|
98408
98690
|
input.on(otui.InputRenderableEvents.ENTER, () => {
|
|
98409
98691
|
const value = input.value.trim();
|
|
98410
98692
|
cleanup();
|
|
@@ -98412,11 +98694,25 @@ function promptBaseUrlStep(otui, r, label, baseUrl, notice) {
|
|
|
98412
98694
|
});
|
|
98413
98695
|
});
|
|
98414
98696
|
}
|
|
98415
|
-
function promptApiKeyStep(otui,
|
|
98697
|
+
function promptApiKeyStep(otui, target, opts) {
|
|
98698
|
+
const r = stepRenderer(target);
|
|
98416
98699
|
return new Promise((resolve) => {
|
|
98417
|
-
const
|
|
98418
|
-
|
|
98419
|
-
|
|
98700
|
+
const surface = openStepSurface(otui, target, {
|
|
98701
|
+
id: "key-picker",
|
|
98702
|
+
title: `Paste your ${opts.label} API key`,
|
|
98703
|
+
tab: "API key",
|
|
98704
|
+
hint: "(Enter \xB7 Esc to go back)",
|
|
98705
|
+
footer: [
|
|
98706
|
+
{ key: "Enter", label: "save (empty = skip)" },
|
|
98707
|
+
{ key: "esc", label: "back" }
|
|
98708
|
+
],
|
|
98709
|
+
contentRows: opts.notice !== undefined ? 6 : 4,
|
|
98710
|
+
onEscape: () => {
|
|
98711
|
+
keyInput.blur();
|
|
98712
|
+
resolve({ kind: "back" });
|
|
98713
|
+
}
|
|
98714
|
+
});
|
|
98715
|
+
const box = surface.body;
|
|
98420
98716
|
if (opts.notice !== undefined) {
|
|
98421
98717
|
box.add(new otui.TextRenderable(r, { id: "kp-notice", content: otui.t`${otui.red("\u2717")} ${opts.notice}`, marginTop: 1 }));
|
|
98422
98718
|
}
|
|
@@ -98428,18 +98724,9 @@ function promptApiKeyStep(otui, r, opts) {
|
|
|
98428
98724
|
const keyInput = new otui.InputRenderable(r, { id: "kp-input", placeholder: opts.placeholder ?? "sk-...", marginTop: 1 });
|
|
98429
98725
|
box.add(keyInput);
|
|
98430
98726
|
keyInput.focus();
|
|
98431
|
-
const onKey = (key) => {
|
|
98432
|
-
if (key.name === "escape") {
|
|
98433
|
-
cleanup();
|
|
98434
|
-
resolve({ kind: "back" });
|
|
98435
|
-
key.preventDefault();
|
|
98436
|
-
key.stopPropagation();
|
|
98437
|
-
}
|
|
98438
|
-
};
|
|
98439
|
-
const unsub = onKeypress4(r, onKey);
|
|
98440
98727
|
const cleanup = () => {
|
|
98441
|
-
|
|
98442
|
-
|
|
98728
|
+
keyInput.blur();
|
|
98729
|
+
surface.close();
|
|
98443
98730
|
};
|
|
98444
98731
|
keyInput.on(otui.InputRenderableEvents.ENTER, () => {
|
|
98445
98732
|
const value = keyInput.value.trim();
|
|
@@ -98448,38 +98735,39 @@ function promptApiKeyStep(otui, r, opts) {
|
|
|
98448
98735
|
});
|
|
98449
98736
|
});
|
|
98450
98737
|
}
|
|
98451
|
-
function pickAuthMethodStep(otui,
|
|
98738
|
+
function pickAuthMethodStep(otui, target, providerLabel, methods) {
|
|
98739
|
+
const r = stepRenderer(target);
|
|
98452
98740
|
return new Promise((resolve) => {
|
|
98453
|
-
const
|
|
98454
|
-
|
|
98455
|
-
|
|
98741
|
+
const surface = openStepSurface(otui, target, {
|
|
98742
|
+
id: "auth-method-picker",
|
|
98743
|
+
title: `How to connect ${providerLabel}`,
|
|
98744
|
+
tab: "Sign-in",
|
|
98745
|
+
hint: "(\u2191/\u2193, Enter \xB7 Esc to go back)",
|
|
98746
|
+
footer: selectStepFooter("back"),
|
|
98747
|
+
contentRows: selectBoxHeight(methods.length, true),
|
|
98748
|
+
onEscape: () => {
|
|
98749
|
+
select.blur();
|
|
98750
|
+
resolve(undefined);
|
|
98751
|
+
}
|
|
98752
|
+
});
|
|
98456
98753
|
const descriptions = {
|
|
98457
98754
|
"device-code": deviceCodeMethodLabel(providerLabel === "GitHub Copilot" ? "github-copilot" : providerLabel === "OpenAI" ? "openai" : "grok"),
|
|
98458
98755
|
"api-key": "Manually enter API Key"
|
|
98459
98756
|
};
|
|
98460
98757
|
const select = new otui.SelectRenderable(r, {
|
|
98461
98758
|
id: "amp-select",
|
|
98462
|
-
width: 60,
|
|
98759
|
+
width: surface.modal ? "100%" : 60,
|
|
98463
98760
|
height: selectBoxHeight(methods.length, true),
|
|
98464
98761
|
showScrollIndicator: true,
|
|
98465
98762
|
options: methods.map((m) => ({ name: m, description: descriptions[m] })),
|
|
98466
98763
|
selectedTextColor: "#ffd166"
|
|
98467
98764
|
});
|
|
98468
|
-
|
|
98765
|
+
surface.body.add(select);
|
|
98469
98766
|
select.focus();
|
|
98470
98767
|
const cleanup = () => {
|
|
98471
|
-
unsub();
|
|
98472
98768
|
select.blur();
|
|
98473
|
-
|
|
98769
|
+
surface.close();
|
|
98474
98770
|
};
|
|
98475
|
-
const unsub = onKeypress4(r, (key) => {
|
|
98476
|
-
if (key.name === "escape") {
|
|
98477
|
-
cleanup();
|
|
98478
|
-
resolve(undefined);
|
|
98479
|
-
key.preventDefault();
|
|
98480
|
-
key.stopPropagation();
|
|
98481
|
-
}
|
|
98482
|
-
});
|
|
98483
98771
|
select.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
|
|
98484
98772
|
const chosen = select.getSelectedOption();
|
|
98485
98773
|
cleanup();
|
|
@@ -98487,30 +98775,27 @@ function pickAuthMethodStep(otui, r, providerLabel, methods) {
|
|
|
98487
98775
|
});
|
|
98488
98776
|
});
|
|
98489
98777
|
}
|
|
98490
|
-
function runDeviceLoginInTui(otui,
|
|
98778
|
+
function runDeviceLoginInTui(otui, target, provider, dir) {
|
|
98779
|
+
const r = stepRenderer(target);
|
|
98491
98780
|
return new Promise((resolve) => {
|
|
98492
|
-
const box = overlayBox(otui, r, "device-login");
|
|
98493
|
-
r.root.add(box);
|
|
98494
|
-
box.add(new otui.TextRenderable(r, {
|
|
98495
|
-
id: "dl-title",
|
|
98496
|
-
content: otui.t`${otui.bold(deviceCodeMethodLabel(provider))} ${otui.dim("(Esc to cancel)")}`
|
|
98497
|
-
}));
|
|
98498
|
-
const status = new otui.TextRenderable(r, { id: "dl-status", content: otui.t`${otui.dim("Requesting a device code\u2026")}`, marginTop: 1 });
|
|
98499
|
-
box.add(status);
|
|
98500
98781
|
const controller = new AbortController;
|
|
98501
|
-
const
|
|
98502
|
-
|
|
98503
|
-
|
|
98504
|
-
|
|
98505
|
-
|
|
98506
|
-
|
|
98782
|
+
const surface = openStepSurface(otui, target, {
|
|
98783
|
+
id: "device-login",
|
|
98784
|
+
title: deviceCodeMethodLabel(provider),
|
|
98785
|
+
tab: "Device login",
|
|
98786
|
+
hint: "(Esc to cancel)",
|
|
98787
|
+
footer: [{ key: "esc", label: "cancel" }],
|
|
98788
|
+
contentRows: 4,
|
|
98789
|
+
onEscape: () => {
|
|
98507
98790
|
controller.abort();
|
|
98508
|
-
cleanup();
|
|
98509
98791
|
resolve(false);
|
|
98510
|
-
key.preventDefault();
|
|
98511
|
-
key.stopPropagation();
|
|
98512
98792
|
}
|
|
98513
98793
|
});
|
|
98794
|
+
const status = new otui.TextRenderable(r, { id: "dl-status", content: otui.t`${otui.dim("Requesting a device code\u2026")}`, marginTop: 1 });
|
|
98795
|
+
surface.body.add(status);
|
|
98796
|
+
const cleanup = () => {
|
|
98797
|
+
surface.close();
|
|
98798
|
+
};
|
|
98514
98799
|
loginDeviceCode({
|
|
98515
98800
|
provider,
|
|
98516
98801
|
fetch: (input, init) => globalThis.fetch(input, init),
|
|
@@ -98552,15 +98837,26 @@ function modelPickerNotice(label, result) {
|
|
|
98552
98837
|
}
|
|
98553
98838
|
return modelsFailureLine(label, result.failure);
|
|
98554
98839
|
}
|
|
98555
|
-
function pickProviderStep(otui,
|
|
98840
|
+
function pickProviderStep(otui, target, detected) {
|
|
98841
|
+
const r = stepRenderer(target);
|
|
98556
98842
|
return new Promise((resolve) => {
|
|
98557
|
-
const
|
|
98558
|
-
|
|
98559
|
-
|
|
98843
|
+
const surface = openStepSurface(otui, target, {
|
|
98844
|
+
id: "picker",
|
|
98845
|
+
title: "Select a provider",
|
|
98846
|
+
tab: "Providers",
|
|
98847
|
+
hint: "(\u2191/\u2193, Enter \xB7 Esc to cancel)",
|
|
98848
|
+
footer: selectStepFooter("cancel"),
|
|
98849
|
+
contentRows: selectBoxHeight(detected.length, true),
|
|
98850
|
+
onEscape: () => {
|
|
98851
|
+
provSelect.blur();
|
|
98852
|
+
resolve(undefined);
|
|
98853
|
+
}
|
|
98854
|
+
});
|
|
98855
|
+
const box = surface.body;
|
|
98560
98856
|
const labelOf = (d) => d.label ?? d.name;
|
|
98561
98857
|
const provSelect = new otui.SelectRenderable(r, {
|
|
98562
98858
|
id: "picker-provider",
|
|
98563
|
-
width: 60,
|
|
98859
|
+
width: surface.modal ? "100%" : 60,
|
|
98564
98860
|
height: selectBoxHeight(detected.length, true),
|
|
98565
98861
|
showScrollIndicator: true,
|
|
98566
98862
|
options: detected.map((d) => ({ name: labelOf(d), description: d.note ?? `${d.models.length} model(s)` })),
|
|
@@ -98568,18 +98864,9 @@ function pickProviderStep(otui, r, detected) {
|
|
|
98568
98864
|
});
|
|
98569
98865
|
box.add(provSelect);
|
|
98570
98866
|
provSelect.focus();
|
|
98571
|
-
const onKey = (key) => {
|
|
98572
|
-
if (key.name === "escape") {
|
|
98573
|
-
cleanup();
|
|
98574
|
-
resolve(undefined);
|
|
98575
|
-
key.preventDefault();
|
|
98576
|
-
key.stopPropagation();
|
|
98577
|
-
}
|
|
98578
|
-
};
|
|
98579
|
-
const unsub = onKeypress4(r, onKey);
|
|
98580
98867
|
const cleanup = () => {
|
|
98581
|
-
|
|
98582
|
-
|
|
98868
|
+
provSelect.blur();
|
|
98869
|
+
surface.close();
|
|
98583
98870
|
};
|
|
98584
98871
|
provSelect.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
|
|
98585
98872
|
const chosen = provSelect.getSelectedOption();
|
|
@@ -98588,12 +98875,7 @@ function pickProviderStep(otui, r, detected) {
|
|
|
98588
98875
|
});
|
|
98589
98876
|
});
|
|
98590
98877
|
}
|
|
98591
|
-
function isModalChrome(target) {
|
|
98592
|
-
return target !== null && typeof target === "object" && "renderer" in target && "focusComposer" in target;
|
|
98593
|
-
}
|
|
98594
98878
|
function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
|
|
98595
|
-
const chrome = isModalChrome(rOrChrome) ? rOrChrome : undefined;
|
|
98596
|
-
const r = chrome !== undefined ? chrome.renderer : rOrChrome;
|
|
98597
98879
|
return new Promise((resolve) => {
|
|
98598
98880
|
if (detected.length === 0) {
|
|
98599
98881
|
resolve(undefined);
|
|
@@ -98611,13 +98893,13 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
|
|
|
98611
98893
|
}
|
|
98612
98894
|
const allCandidates = options.onlyConnected ? candidates : [...candidates, CUSTOM_ADD_ENTRY];
|
|
98613
98895
|
while (true) {
|
|
98614
|
-
const prov = await pickProviderStep(otui,
|
|
98896
|
+
const prov = await pickProviderStep(otui, rOrChrome, allCandidates);
|
|
98615
98897
|
if (prov === undefined) {
|
|
98616
98898
|
resolve(undefined);
|
|
98617
98899
|
return;
|
|
98618
98900
|
}
|
|
98619
98901
|
if (prov.name === CUSTOM_PROVIDER_ADD_ID) {
|
|
98620
|
-
const created = await promptCustomProviderWizard(otui,
|
|
98902
|
+
const created = await promptCustomProviderWizard(otui, rOrChrome);
|
|
98621
98903
|
if (created !== undefined) {
|
|
98622
98904
|
saveCustomCompatProvider({
|
|
98623
98905
|
name: created.name,
|
|
@@ -98641,7 +98923,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
|
|
|
98641
98923
|
continue;
|
|
98642
98924
|
}
|
|
98643
98925
|
const lockDiscoveredHost = prov.name === "github-copilot";
|
|
98644
|
-
let selectedBaseUrl = options.onlyConnected || prov.baseUrl === undefined || lockDiscoveredHost ? prov.baseUrl : await promptBaseUrlStep(otui,
|
|
98926
|
+
let selectedBaseUrl = options.onlyConnected || prov.baseUrl === undefined || lockDiscoveredHost ? prov.baseUrl : await promptBaseUrlStep(otui, rOrChrome, prov.label ?? prov.name, prov.baseUrl);
|
|
98645
98927
|
if (!options.onlyConnected && !lockDiscoveredHost && prov.baseUrl !== undefined && selectedBaseUrl === undefined) {
|
|
98646
98928
|
continue;
|
|
98647
98929
|
}
|
|
@@ -98669,13 +98951,13 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
|
|
|
98669
98951
|
offered.push("api-key");
|
|
98670
98952
|
let method = offered.length === 1 ? offered[0] : undefined;
|
|
98671
98953
|
if (offered.length > 1) {
|
|
98672
|
-
method = await pickAuthMethodStep(otui,
|
|
98954
|
+
method = await pickAuthMethodStep(otui, rOrChrome, label, offered);
|
|
98673
98955
|
if (method === undefined) {
|
|
98674
98956
|
return "back";
|
|
98675
98957
|
}
|
|
98676
98958
|
}
|
|
98677
98959
|
if (method === "device-code") {
|
|
98678
|
-
if (!await runDeviceLoginInTui(otui,
|
|
98960
|
+
if (!await runDeviceLoginInTui(otui, rOrChrome, prov.name, options.configDir)) {
|
|
98679
98961
|
return "back";
|
|
98680
98962
|
}
|
|
98681
98963
|
const saved = loadShellConfig(options.configDir).baseUrls?.[prov.name];
|
|
@@ -98685,7 +98967,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
|
|
|
98685
98967
|
}
|
|
98686
98968
|
return "ok";
|
|
98687
98969
|
}
|
|
98688
|
-
const kr = await promptApiKeyStep(otui,
|
|
98970
|
+
const kr = await promptApiKeyStep(otui, rOrChrome, {
|
|
98689
98971
|
label,
|
|
98690
98972
|
envKey,
|
|
98691
98973
|
...notice === undefined ? {} : { notice }
|
|
@@ -98715,7 +98997,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
|
|
|
98715
98997
|
models = await modelsForPicker(selectedProvider, modelDeps);
|
|
98716
98998
|
}
|
|
98717
98999
|
if (!options.onlyConnected && selectedProvider.baseUrl !== undefined && endpointMayBeAtFault(models.failure)) {
|
|
98718
|
-
const corrected = await promptBaseUrlStep(otui,
|
|
99000
|
+
const corrected = await promptBaseUrlStep(otui, rOrChrome, label, selectedProvider.baseUrl, modelsFailureLine(label, models.failure));
|
|
98719
99001
|
if (corrected === undefined) {
|
|
98720
99002
|
continue;
|
|
98721
99003
|
}
|
|
@@ -98726,7 +99008,7 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
|
|
|
98726
99008
|
models = await modelsForPicker(selectedProvider, modelDeps);
|
|
98727
99009
|
}
|
|
98728
99010
|
}
|
|
98729
|
-
const model = await pickModelInTui(otui,
|
|
99011
|
+
const model = await pickModelInTui(otui, rOrChrome, models.models, modelPickerNotice(label, models), {
|
|
98730
99012
|
escLabel: "back"
|
|
98731
99013
|
});
|
|
98732
99014
|
if (model === undefined) {
|
|
@@ -98962,6 +99244,9 @@ function pickSessionInTui(otui, rOrChrome, sessions) {
|
|
|
98962
99244
|
});
|
|
98963
99245
|
});
|
|
98964
99246
|
}
|
|
99247
|
+
function describeModeRow(mode, readOnly) {
|
|
99248
|
+
return readOnly ? { mode, readOnly: "\xB7 read-only" } : { mode };
|
|
99249
|
+
}
|
|
98965
99250
|
var MODE_PICKER_DESCRIPTIONS = {
|
|
98966
99251
|
ask: "Every mutating action asks first (today's default)",
|
|
98967
99252
|
trust: "Auto-approves safe actions; still asks for destructive/credential ones",
|
|
@@ -99073,6 +99358,8 @@ async function launchTuiAgentShell(opts) {
|
|
|
99073
99358
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-model-k", content: otui.t`${otui.dim("Model")}`, marginTop: 1 }));
|
|
99074
99359
|
const sbModelV = new otui.TextRenderable(r, { id: "sb-model-v", content: otui.t`${otui.dim(`${sel.provider}/${sel.model}`)}` });
|
|
99075
99360
|
sidebar.add(sbModelV);
|
|
99361
|
+
const sbModeV = new otui.TextRenderable(r, { id: "sb-mode-v", content: "" });
|
|
99362
|
+
sidebar.add(sbModeV);
|
|
99076
99363
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-usage-k", content: otui.t`${otui.dim("Usage")}`, marginTop: 1 }));
|
|
99077
99364
|
const sbUsageV = new otui.TextRenderable(r, { id: "sb-usage-v", content: otui.t`${otui.dim("\u21910 \u21930")}` });
|
|
99078
99365
|
sidebar.add(sbUsageV);
|
|
@@ -99689,6 +99976,11 @@ async function launchTuiAgentShell(opts) {
|
|
|
99689
99976
|
io.permissionMode = () => permissionMode;
|
|
99690
99977
|
let readOnly = false;
|
|
99691
99978
|
io.readOnly = () => readOnly;
|
|
99979
|
+
const paintModeRow = () => {
|
|
99980
|
+
const row = describeModeRow(permissionMode, readOnly);
|
|
99981
|
+
sbModeV.content = row.readOnly === undefined ? otui.t`${otui.dim(`mode ${row.mode}`)}` : otui.t`${otui.dim(`mode ${row.mode}`)} ${otui.yellow(row.readOnly)}`;
|
|
99982
|
+
};
|
|
99983
|
+
paintModeRow();
|
|
99692
99984
|
io.onAutoApproved = (tool, input, meta) => {
|
|
99693
99985
|
const preview = tool === "shell_exec" ? parseShellExecCommand(input) : tool;
|
|
99694
99986
|
const label = meta.destructive ? `\u25C7 auto-approved (${permissionMode}) [destructive]` : `\u25C7 auto-approved (${permissionMode})`;
|
|
@@ -99716,7 +100008,12 @@ async function launchTuiAgentShell(opts) {
|
|
|
99716
100008
|
};
|
|
99717
100009
|
deps = { ...deps, onContextCompaction };
|
|
99718
100010
|
liveDeps = deps;
|
|
100011
|
+
let removeSplash;
|
|
99719
100012
|
const applyOpened = (opened, previewHistory) => {
|
|
100013
|
+
if (opened.history.length > 0) {
|
|
100014
|
+
removeSplash?.();
|
|
100015
|
+
removeSplash = undefined;
|
|
100016
|
+
}
|
|
99720
100017
|
liveSession = opened.handle;
|
|
99721
100018
|
history = previewHistory === true ? opened.history.slice(-SESSION_PREVIEW_MESSAGE_COUNT) : opened.history;
|
|
99722
100019
|
archive = opened.archive.length > 0 ? [...opened.archive] : [...opened.history];
|
|
@@ -99832,6 +100129,9 @@ async function launchTuiAgentShell(opts) {
|
|
|
99832
100129
|
}));
|
|
99833
100130
|
}
|
|
99834
100131
|
slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
|
|
100132
|
+
if (history.length === 0) {
|
|
100133
|
+
removeSplash = mountEmptyTranscriptSplash(otui, r, transcript);
|
|
100134
|
+
}
|
|
99835
100135
|
refreshWorkspaceSidebar();
|
|
99836
100136
|
refreshReviewSidebar();
|
|
99837
100137
|
const paintSessionHeader = () => {
|
|
@@ -100128,6 +100428,7 @@ Staying in the current session.
|
|
|
100128
100428
|
}
|
|
100129
100429
|
}
|
|
100130
100430
|
permissionMode = next;
|
|
100431
|
+
paintModeRow();
|
|
100131
100432
|
chrome.showToast(`Permission mode: ${next}`);
|
|
100132
100433
|
if (saveFlag) {
|
|
100133
100434
|
const saved = setProjectPermissionMode(sessionCwd, next);
|
|
@@ -100185,11 +100486,13 @@ Staying in the current session.
|
|
|
100185
100486
|
}
|
|
100186
100487
|
if (wanted === "on") {
|
|
100187
100488
|
readOnly = true;
|
|
100489
|
+
paintModeRow();
|
|
100188
100490
|
chrome.showToast("Read-only mode: on");
|
|
100189
100491
|
return;
|
|
100190
100492
|
}
|
|
100191
100493
|
if (wanted === "off") {
|
|
100192
100494
|
readOnly = false;
|
|
100495
|
+
paintModeRow();
|
|
100193
100496
|
chrome.showToast(`Read-only mode: off (permission mode stays: ${permissionMode})`);
|
|
100194
100497
|
return;
|
|
100195
100498
|
}
|
|
@@ -100569,6 +100872,8 @@ Staying in the current session.
|
|
|
100569
100872
|
suggestionGate.cancel("new turn started");
|
|
100570
100873
|
if (origin === "operator") {
|
|
100571
100874
|
consecutiveAutoWakes = 0;
|
|
100875
|
+
removeSplash?.();
|
|
100876
|
+
removeSplash = undefined;
|
|
100572
100877
|
}
|
|
100573
100878
|
const displayLine = summarizeSubmittedLine(line);
|
|
100574
100879
|
if (chrome.isBusy()) {
|
|
@@ -100874,7 +101179,7 @@ Staying in the current session.
|
|
|
100874
101179
|
const args = parseSearchProviderArgs(line.slice(16));
|
|
100875
101180
|
const all = searchProviderController.configurable();
|
|
100876
101181
|
if (args.providerId === undefined) {
|
|
100877
|
-
await chrome.withOverlay(() => searchProviderWizardInTui(otui,
|
|
101182
|
+
await chrome.withOverlay(() => searchProviderWizardInTui(otui, chrome, searchProviderController));
|
|
100878
101183
|
input.focus();
|
|
100879
101184
|
return;
|
|
100880
101185
|
}
|
|
@@ -100910,7 +101215,7 @@ Staying in the current session.
|
|
|
100910
101215
|
`);
|
|
100911
101216
|
return;
|
|
100912
101217
|
}
|
|
100913
|
-
const picked = await chrome.withOverlay(() => pickSearchProviderStep(otui,
|
|
101218
|
+
const picked = await chrome.withOverlay(() => pickSearchProviderStep(otui, chrome, selectable));
|
|
100914
101219
|
input.focus();
|
|
100915
101220
|
if (picked === undefined) {
|
|
100916
101221
|
return;
|
|
@@ -101100,12 +101405,10 @@ Usage: /reasoning <${REASONING_EFFORT_LEVELS.join("|")}>
|
|
|
101100
101405
|
const ns = await chrome.withOverlay(() => command.name === "/connect" ? selectProviderModelInTui(otui, chrome, detected, { onlyConnected: true, env: process.env }) : selectProviderModelInTui(otui, chrome, detected));
|
|
101101
101406
|
if (ns !== undefined) {
|
|
101102
101407
|
await switchTo(ns);
|
|
101103
|
-
} else {
|
|
101104
|
-
|
|
101105
|
-
chrome.showToast("No connected providers found. Run /provider to configure one first.");
|
|
101106
|
-
}
|
|
101107
|
-
input.focus();
|
|
101408
|
+
} else if (command.name === "/connect") {
|
|
101409
|
+
chrome.showToast("No connected providers found. Run /provider to configure one first.");
|
|
101108
101410
|
}
|
|
101411
|
+
input.focus();
|
|
101109
101412
|
})();
|
|
101110
101413
|
return;
|
|
101111
101414
|
}
|
|
@@ -106920,7 +107223,7 @@ import { readFileSync as readFileSync9 } from "fs";
|
|
|
106920
107223
|
|
|
106921
107224
|
// src/agents/bootstrap.ts
|
|
106922
107225
|
import { mkdir as mkdir64, readFile as readFile101, writeFile as writeFile55 } from "fs/promises";
|
|
106923
|
-
import { homedir as
|
|
107226
|
+
import { homedir as homedir10 } from "os";
|
|
106924
107227
|
import path200 from "path";
|
|
106925
107228
|
init_fs();
|
|
106926
107229
|
var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
|
|
@@ -106989,7 +107292,7 @@ function resolveAgentBootstrapRuntimes(ids) {
|
|
|
106989
107292
|
}
|
|
106990
107293
|
return { runtimes, unknown };
|
|
106991
107294
|
}
|
|
106992
|
-
async function agentBootstrapStatus(runtime, homeRoot =
|
|
107295
|
+
async function agentBootstrapStatus(runtime, homeRoot = homedir10()) {
|
|
106993
107296
|
const filePath = runtime.filePath(homeRoot);
|
|
106994
107297
|
const exists = await pathExists(filePath);
|
|
106995
107298
|
const content = exists ? await readFile101(filePath, "utf8") : "";
|
|
@@ -106999,7 +107302,7 @@ async function agentBootstrapStatus(runtime, homeRoot = homedir9()) {
|
|
|
106999
107302
|
return { runtime: runtime.id, label: runtime.label, filePath, exists, installed, current };
|
|
107000
107303
|
}
|
|
107001
107304
|
async function installAgentBootstrap(runtime, options = {}) {
|
|
107002
|
-
const homeRoot = options.homeRoot ??
|
|
107305
|
+
const homeRoot = options.homeRoot ?? homedir10();
|
|
107003
107306
|
const filePath = runtime.filePath(homeRoot);
|
|
107004
107307
|
const exists = await pathExists(filePath);
|
|
107005
107308
|
const current = exists ? await readFile101(filePath, "utf8") : "";
|
|
@@ -107014,7 +107317,7 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
107014
107317
|
return { ...status, wrote, dryRun };
|
|
107015
107318
|
}
|
|
107016
107319
|
async function uninstallAgentBootstrap(runtime, options = {}) {
|
|
107017
|
-
const homeRoot = options.homeRoot ??
|
|
107320
|
+
const homeRoot = options.homeRoot ?? homedir10();
|
|
107018
107321
|
const filePath = runtime.filePath(homeRoot);
|
|
107019
107322
|
const exists = await pathExists(filePath);
|
|
107020
107323
|
const current = exists ? await readFile101(filePath, "utf8") : "";
|