@mrciphersmith/keryx 0.3.128 → 0.3.130
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/cli.js +990 -324
- package/dist/core.js +1 -7
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -23370,7 +23370,7 @@ var package_default;
|
|
|
23370
23370
|
var init_package = __esm(() => {
|
|
23371
23371
|
package_default = {
|
|
23372
23372
|
name: "@mrciphersmith/keryx",
|
|
23373
|
-
version: "0.3.
|
|
23373
|
+
version: "0.3.130",
|
|
23374
23374
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
23375
23375
|
private: false,
|
|
23376
23376
|
publishConfig: {
|
|
@@ -160929,7 +160929,7 @@ var init_help_groups = __esm(() => {
|
|
|
160929
160929
|
group: "Connect a model provider",
|
|
160930
160930
|
summary: "Category -> model routing table (list, set, unset, trust), the model-profile catalogue (profile list, profile set), and real measured task cost (stats)."
|
|
160931
160931
|
},
|
|
160932
|
-
{ kind: "slash", name: "/connect", group: "Connect a model provider", summary: "
|
|
160932
|
+
{ kind: "slash", name: "/connect", group: "Connect a model provider", summary: "Providers dialog: switch, test or disconnect a connected provider, or add one (Connected / Add provider tabs)." },
|
|
160933
160933
|
{
|
|
160934
160934
|
kind: "slash",
|
|
160935
160935
|
name: "/routing",
|
|
@@ -160948,12 +160948,6 @@ var init_help_groups = __esm(() => {
|
|
|
160948
160948
|
group: "Connect a model provider",
|
|
160949
160949
|
summary: "Select a connected web search provider."
|
|
160950
160950
|
},
|
|
160951
|
-
{
|
|
160952
|
-
kind: "slash",
|
|
160953
|
-
name: "/provider",
|
|
160954
|
-
group: "Connect a model provider",
|
|
160955
|
-
summary: "Switch provider \u2014 /provider <name>, or no arg to re-select."
|
|
160956
|
-
},
|
|
160957
160951
|
{
|
|
160958
160952
|
kind: "cli",
|
|
160959
160953
|
name: "external",
|
|
@@ -163885,6 +163879,9 @@ function mountTab(state, input, tabId) {
|
|
|
163885
163879
|
unmountActiveTab(state);
|
|
163886
163880
|
state.active = tabId;
|
|
163887
163881
|
paintTabs(state);
|
|
163882
|
+
if (input.footerFor !== undefined) {
|
|
163883
|
+
paintFooter(state, input.footerFor(tabId) ?? input.footer);
|
|
163884
|
+
}
|
|
163888
163885
|
const availWidth = resolveModalAvailableWidth(state.chrome);
|
|
163889
163886
|
const size = resolveModalPanelSize(availWidth, state.chrome.renderer.height, input.contentRows);
|
|
163890
163887
|
const panelWidth = Math.min(availWidth, size.width);
|
|
@@ -166962,7 +166959,7 @@ var init_command_gateway = __esm(() => {
|
|
|
166962
166959
|
{ name: "status", kind: "text", description: "Show the session: model, mode, queue, usage" },
|
|
166963
166960
|
{ name: "doctor", kind: "text", description: "Run the health checks" },
|
|
166964
166961
|
{ name: "model", kind: "picker", description: "Pick another model with buttons" },
|
|
166965
|
-
{ name: "connect", kind: "picker", description: "Switch to a connected provider with buttons" },
|
|
166962
|
+
{ name: "connect", kind: "picker", description: "Switch to a connected provider with buttons; also add a provider (device login only) and test it" },
|
|
166966
166963
|
{ name: "resume", kind: "picker", description: "Return to an earlier session with buttons" },
|
|
166967
166964
|
{ name: "sessions", kind: "builtin", description: "List recent sessions" },
|
|
166968
166965
|
{ name: "history", kind: "builtin", description: "Post the last messages of this session here: /history [N], 1 to 20" },
|
|
@@ -166993,7 +166990,6 @@ var init_command_gateway = __esm(() => {
|
|
|
166993
166990
|
exit: "it would close the shell",
|
|
166994
166991
|
quit: "it would close the shell",
|
|
166995
166992
|
channels: "it manages the Telegram connection itself",
|
|
166996
|
-
provider: "it asks for provider keys and addresses, which are never typed into a chat",
|
|
166997
166993
|
"search-provider": "it asks for provider keys, which are never typed into a chat",
|
|
166998
166994
|
"search-connect": "it changes where searches are sent",
|
|
166999
166995
|
"remote-control": "turning remote control off would archive this topic, and its panel is local",
|
|
@@ -167472,11 +167468,18 @@ Now: ${current.label}` : ""}`;
|
|
|
167472
167468
|
return "failed";
|
|
167473
167469
|
}
|
|
167474
167470
|
const providers = await host.listProviders();
|
|
167471
|
+
const canAdd = host.listAddableProviders !== undefined;
|
|
167475
167472
|
if (providers.length === 0) {
|
|
167473
|
+
if (canAdd) {
|
|
167474
|
+
return await this.pickAddableProvider(fromId);
|
|
167475
|
+
}
|
|
167476
167476
|
await this.ctx.reply("No provider is connected yet. Connecting one needs a key, which is never typed into a chat; use the shell.").catch(() => false);
|
|
167477
167477
|
return "failed";
|
|
167478
167478
|
}
|
|
167479
167479
|
const options = providers.map((provider) => ({ id: provider.id, label: provider.current === true ? `* ${provider.label}` : provider.label }));
|
|
167480
|
+
if (canAdd) {
|
|
167481
|
+
options.push({ id: ADD_PROVIDER_ID, label: "Add provider" });
|
|
167482
|
+
}
|
|
167480
167483
|
const chosen = await this.pick("connect", "Connected providers. Pick one to switch to:", options, fromId);
|
|
167481
167484
|
if (chosen === "refused") {
|
|
167482
167485
|
return "failed";
|
|
@@ -167485,8 +167488,92 @@ Now: ${current.label}` : ""}`;
|
|
|
167485
167488
|
this.ctx.record("/connect not answered in time; nothing changed");
|
|
167486
167489
|
return "failed";
|
|
167487
167490
|
}
|
|
167491
|
+
if (chosen.id === ADD_PROVIDER_ID) {
|
|
167492
|
+
return await this.pickAddableProvider(fromId);
|
|
167493
|
+
}
|
|
167488
167494
|
return await this.pickModel(chosen.id, fromId);
|
|
167489
167495
|
}
|
|
167496
|
+
async pickAddableProvider(fromId) {
|
|
167497
|
+
const host = this.ctx.host;
|
|
167498
|
+
if (host.listAddableProviders === undefined) {
|
|
167499
|
+
await this.ctx.reply("/connect is not available from this shell.").catch(() => false);
|
|
167500
|
+
return "failed";
|
|
167501
|
+
}
|
|
167502
|
+
const addable = await host.listAddableProviders();
|
|
167503
|
+
if (addable.length === 0) {
|
|
167504
|
+
await this.ctx.reply("There are no providers to add from this shell.").catch(() => false);
|
|
167505
|
+
return "failed";
|
|
167506
|
+
}
|
|
167507
|
+
const options = addable.map((provider) => ({ id: provider.id, label: provider.connected ? `${provider.label} (connected)` : provider.label }));
|
|
167508
|
+
const chosen = await this.pick("connect", "Add provider. Pick one to test or connect:", options, fromId);
|
|
167509
|
+
if (chosen === "refused") {
|
|
167510
|
+
return "failed";
|
|
167511
|
+
}
|
|
167512
|
+
if (chosen === undefined) {
|
|
167513
|
+
this.ctx.record("/connect not answered in time; nothing changed");
|
|
167514
|
+
return "failed";
|
|
167515
|
+
}
|
|
167516
|
+
const provider = addable.find((entry) => entry.id === chosen.id);
|
|
167517
|
+
if (provider === undefined) {
|
|
167518
|
+
return "failed";
|
|
167519
|
+
}
|
|
167520
|
+
const action = await this.ask("connect", this.ctx.compose(`${provider.label}: test the stored credential, or connect it?`), [["Test", "Connect"]], fromId);
|
|
167521
|
+
if (action === "refused") {
|
|
167522
|
+
return "failed";
|
|
167523
|
+
}
|
|
167524
|
+
if (action === undefined) {
|
|
167525
|
+
this.ctx.record("/connect not answered in time; nothing changed");
|
|
167526
|
+
return "failed";
|
|
167527
|
+
}
|
|
167528
|
+
return action === 0 ? await this.testAddable(provider) : await this.connectAddable(provider);
|
|
167529
|
+
}
|
|
167530
|
+
async testAddable(provider) {
|
|
167531
|
+
const host = this.ctx.host;
|
|
167532
|
+
if (host.testProvider === undefined) {
|
|
167533
|
+
await this.ctx.reply("Testing a provider is not available from this shell.").catch(() => false);
|
|
167534
|
+
return "failed";
|
|
167535
|
+
}
|
|
167536
|
+
const outcome = await host.testProvider(provider.id);
|
|
167537
|
+
this.ctx.record(`/connect test ${provider.id}: ${outcome.ok ? "ok" : "failed"}`);
|
|
167538
|
+
await this.ctx.reply(this.ctx.compose(`${provider.label}: ${outcome.message}`)).catch(() => false);
|
|
167539
|
+
return outcome.ok ? "done" : "failed";
|
|
167540
|
+
}
|
|
167541
|
+
async connectAddable(provider) {
|
|
167542
|
+
const host = this.ctx.host;
|
|
167543
|
+
if (provider.connected) {
|
|
167544
|
+
this.ctx.record(`/connect ${provider.id}: already connected`);
|
|
167545
|
+
await this.ctx.reply(this.ctx.compose(`${provider.label} is already connected. Use Test, or reconnect in the shell (/connect).`)).catch(() => false);
|
|
167546
|
+
return "failed";
|
|
167547
|
+
}
|
|
167548
|
+
if (!provider.deviceLogin) {
|
|
167549
|
+
this.ctx.record(`/connect ${provider.id}: needs a key, entered in the shell`);
|
|
167550
|
+
await this.ctx.reply(this.ctx.compose(`${provider.label} needs a key. The key is entered in the shell with /connect (Add provider tab), never in this chat.`)).catch(() => false);
|
|
167551
|
+
return "failed";
|
|
167552
|
+
}
|
|
167553
|
+
if (host.connectWithDeviceLogin === undefined) {
|
|
167554
|
+
await this.ctx.reply("Connecting a provider is not available from this shell.").catch(() => false);
|
|
167555
|
+
return "failed";
|
|
167556
|
+
}
|
|
167557
|
+
const busy = this.busyReason("/connect", "connect");
|
|
167558
|
+
if (busy !== undefined) {
|
|
167559
|
+
this.ctx.record(`/connect not run: ${busy}`);
|
|
167560
|
+
await this.ctx.reply(`/connect was not run: ${busy}.`).catch(() => false);
|
|
167561
|
+
return "failed";
|
|
167562
|
+
}
|
|
167563
|
+
this.ctx.record(`/connect ${provider.id}: device login started`);
|
|
167564
|
+
const notify = async (text) => {
|
|
167565
|
+
await this.ctx.reply(this.ctx.compose(text)).catch(() => false);
|
|
167566
|
+
};
|
|
167567
|
+
let outcome;
|
|
167568
|
+
try {
|
|
167569
|
+
outcome = await host.connectWithDeviceLogin(provider.id, notify);
|
|
167570
|
+
} catch {
|
|
167571
|
+
outcome = { ok: false, message: "Device login failed. The details are in the shell." };
|
|
167572
|
+
}
|
|
167573
|
+
this.ctx.record(`/connect ${provider.id}: device login ${outcome.ok ? "done" : "failed"}`);
|
|
167574
|
+
await this.ctx.reply(this.ctx.compose(`${provider.label}: ${outcome.message}`)).catch(() => false);
|
|
167575
|
+
return outcome.ok ? "done" : "failed";
|
|
167576
|
+
}
|
|
167490
167577
|
async pickSession(fromId, end) {
|
|
167491
167578
|
const host = this.ctx.host;
|
|
167492
167579
|
if (host.listSessions === undefined || host.resumeSession === undefined) {
|
|
@@ -167535,7 +167622,7 @@ ${lines.join(`
|
|
|
167535
167622
|
`)}`)).catch(() => false);
|
|
167536
167623
|
}
|
|
167537
167624
|
}
|
|
167538
|
-
var DEFAULT_ROUTER_LIMITS, PICKER_PAGE_SIZE = 7, LABEL_CHARS = 56, SESSION_COMMANDS;
|
|
167625
|
+
var DEFAULT_ROUTER_LIMITS, PICKER_PAGE_SIZE = 7, LABEL_CHARS = 56, ADD_PROVIDER_ID = "::add-provider", SESSION_COMMANDS;
|
|
167539
167626
|
var init_command_router = __esm(() => {
|
|
167540
167627
|
init_command_gateway();
|
|
167541
167628
|
init_history2();
|
|
@@ -167916,6 +168003,9 @@ class RemoteBridge {
|
|
|
167916
168003
|
...this.host.busyRefusal !== undefined ? { busyRefusal: (line) => this.host.busyRefusal(line) } : {},
|
|
167917
168004
|
...this.host.listModels !== undefined ? { listModels: (id) => this.host.listModels(id) } : {},
|
|
167918
168005
|
...this.host.listProviders !== undefined ? { listProviders: () => this.host.listProviders() } : {},
|
|
168006
|
+
...this.host.listAddableProviders !== undefined ? { listAddableProviders: () => this.host.listAddableProviders() } : {},
|
|
168007
|
+
...this.host.testProvider !== undefined ? { testProvider: (id) => this.host.testProvider(id) } : {},
|
|
168008
|
+
...this.host.connectWithDeviceLogin !== undefined ? { connectWithDeviceLogin: (id, notify) => this.host.connectWithDeviceLogin(id, notify) } : {},
|
|
167919
168009
|
...this.host.switchModel !== undefined ? { switchModel: (model, provider) => this.host.switchModel(model, provider) } : {},
|
|
167920
168010
|
...this.host.listSessions !== undefined ? { listSessions: () => this.host.listSessions() } : {},
|
|
167921
168011
|
...this.host.resumeSession !== undefined ? { resumeSession: (id) => this.host.resumeSession(id) } : {}
|
|
@@ -168800,6 +168890,9 @@ function stepConnectNavAction(selected, direction) {
|
|
|
168800
168890
|
const nextIdx = direction === "left" ? idx > 0 ? idx - 1 : count - 1 : idx < count - 1 ? idx + 1 : 0;
|
|
168801
168891
|
return CONNECT_NAV_ACTIONS[nextIdx];
|
|
168802
168892
|
}
|
|
168893
|
+
function stepAddNavAction(selected, _direction) {
|
|
168894
|
+
return selected === "connect" ? "test" : "connect";
|
|
168895
|
+
}
|
|
168803
168896
|
var QUEUE_NAV_ACTIONS, CONNECT_NAV_ACTIONS;
|
|
168804
168897
|
var init_queue_nav = __esm(() => {
|
|
168805
168898
|
QUEUE_NAV_ACTIONS = ["force", "edit", "delete"];
|
|
@@ -170449,6 +170542,9 @@ function describeUnavailableCommand(line, mode) {
|
|
|
170449
170542
|
return `${command.name} is only available in ${where} \u2014 this is ${mode} mode. ` + `Type /help for the commands available here.
|
|
170450
170543
|
`;
|
|
170451
170544
|
}
|
|
170545
|
+
function removedCommandHint(line) {
|
|
170546
|
+
return REMOVED_COMMAND_HINTS[commandToken(line)];
|
|
170547
|
+
}
|
|
170452
170548
|
function renderCommandHelp(mode, only, maxColumns) {
|
|
170453
170549
|
const allow = only === undefined ? undefined : new Set(only);
|
|
170454
170550
|
const options = commandsForMode(mode).filter((c) => allow === undefined || allow.has(c.name));
|
|
@@ -170478,7 +170574,7 @@ function wrapWords2(line, budget) {
|
|
|
170478
170574
|
out.push(cur);
|
|
170479
170575
|
return out;
|
|
170480
170576
|
}
|
|
170481
|
-
var CHAT_ONLY, AGENT_ONLY, BOTH, AGENT_SLASH_COMMANDS, DELEGATE_USAGE = "/delegate <agent> <task> (e.g. `/delegate claude <task>`; agents: claude, codex, agy, see `keryx agents external list`; not enabled yet? `/external-agents on`)", DEMOTE_USAGE = "/demote <task_id> (the task_id shell_exec returned)", MIN_HELP_WRAP_COLUMNS = 10;
|
|
170577
|
+
var CHAT_ONLY, AGENT_ONLY, BOTH, AGENT_SLASH_COMMANDS, DELEGATE_USAGE = "/delegate <agent> <task> (e.g. `/delegate claude <task>`; agents: claude, codex, agy, see `keryx agents external list`; not enabled yet? `/external-agents on`)", DEMOTE_USAGE = "/demote <task_id> (the task_id shell_exec returned)", REMOVED_COMMAND_HINTS, MIN_HELP_WRAP_COLUMNS = 10;
|
|
170482
170578
|
var init_agent_commands = __esm(() => {
|
|
170483
170579
|
init_registry2();
|
|
170484
170580
|
CHAT_ONLY = ["chat"];
|
|
@@ -170502,11 +170598,11 @@ var init_agent_commands = __esm(() => {
|
|
|
170502
170598
|
},
|
|
170503
170599
|
{
|
|
170504
170600
|
name: "/connect",
|
|
170505
|
-
description: "
|
|
170601
|
+
description: "Providers \u2014 switch, test or disconnect a connected one, or add a new one",
|
|
170506
170602
|
modes: BOTH,
|
|
170507
170603
|
modeDescriptions: {
|
|
170508
|
-
agent: "
|
|
170509
|
-
chat: "
|
|
170604
|
+
agent: "Providers dialog: Connected (switch, Test, Disconnect) and Add provider (key or login)",
|
|
170605
|
+
chat: "Providers picker: switch to a connected provider or add one (picker when available)"
|
|
170510
170606
|
}
|
|
170511
170607
|
},
|
|
170512
170608
|
{
|
|
@@ -170519,14 +170615,6 @@ var init_agent_commands = __esm(() => {
|
|
|
170519
170615
|
description: "Select a connected web search provider",
|
|
170520
170616
|
modes: AGENT_ONLY
|
|
170521
170617
|
},
|
|
170522
|
-
{
|
|
170523
|
-
name: "/provider",
|
|
170524
|
-
description: "Switch provider \u2014 /provider <name>, or no arg to re-select",
|
|
170525
|
-
modes: BOTH,
|
|
170526
|
-
modeDescriptions: {
|
|
170527
|
-
agent: "Add or configure a provider (interactive picker)"
|
|
170528
|
-
}
|
|
170529
|
-
},
|
|
170530
170618
|
{
|
|
170531
170619
|
name: "/think",
|
|
170532
170620
|
description: "Expand the last reasoning block \u2014 /think [auto|expand|hide|collapse]",
|
|
@@ -170842,6 +170930,9 @@ var init_agent_commands = __esm(() => {
|
|
|
170842
170930
|
modeDescriptions: { agent: "Leave agent mode (/quit works too)" }
|
|
170843
170931
|
}
|
|
170844
170932
|
];
|
|
170933
|
+
REMOVED_COMMAND_HINTS = {
|
|
170934
|
+
"/provider": "/provider was merged into /connect \u2014 run /connect (Connected and Add provider tabs)."
|
|
170935
|
+
};
|
|
170845
170936
|
});
|
|
170846
170937
|
|
|
170847
170938
|
// src/tui/busy-dispatch.ts
|
|
@@ -182874,6 +182965,129 @@ var init_routing_inspector = __esm(() => {
|
|
|
182874
182965
|
];
|
|
182875
182966
|
});
|
|
182876
182967
|
|
|
182968
|
+
// src/tui/remote-provider-hooks.ts
|
|
182969
|
+
function remoteFailureText(failure) {
|
|
182970
|
+
switch (failure.kind) {
|
|
182971
|
+
case "rejected":
|
|
182972
|
+
return `rejected the credential (HTTP ${Number.isInteger(failure.status) ? failure.status : "error"})`;
|
|
182973
|
+
case "http":
|
|
182974
|
+
return `could not list models (HTTP ${Number.isInteger(failure.status) ? failure.status : "error"})`;
|
|
182975
|
+
case "unreachable":
|
|
182976
|
+
return "could not be reached";
|
|
182977
|
+
case "empty":
|
|
182978
|
+
return "reports no models";
|
|
182979
|
+
}
|
|
182980
|
+
}
|
|
182981
|
+
async function testProviderForRemote(providerId, deps) {
|
|
182982
|
+
const outcome = await deps.probe(providerId);
|
|
182983
|
+
switch (outcome.kind) {
|
|
182984
|
+
case "unknown":
|
|
182985
|
+
return { ok: false, message: "unknown provider, cannot test" };
|
|
182986
|
+
case "no-credential":
|
|
182987
|
+
return { ok: false, message: NO_CREDENTIAL_TEXT };
|
|
182988
|
+
case "live":
|
|
182989
|
+
return { ok: true, message: `ok \u2014 ${outcome.modelCount} model(s)` };
|
|
182990
|
+
case "failed":
|
|
182991
|
+
return { ok: false, message: remoteFailureText(outcome.failure) };
|
|
182992
|
+
}
|
|
182993
|
+
}
|
|
182994
|
+
function createRemoteDeviceLogin(deps = {}) {
|
|
182995
|
+
const login = deps.login ?? loginDeviceCode;
|
|
182996
|
+
const applyEnv = deps.applyEnv ?? applyOAuthAccessToEnv;
|
|
182997
|
+
const allows = deps.allows ?? ((id) => catalogAllows(id, "device-code"));
|
|
182998
|
+
const timeoutMs = deps.timeoutMs ?? REMOTE_DEVICE_LOGIN_LIMIT_MS;
|
|
182999
|
+
let current;
|
|
183000
|
+
let stoppedByCaller = false;
|
|
183001
|
+
return {
|
|
183002
|
+
get running() {
|
|
183003
|
+
return current !== undefined;
|
|
183004
|
+
},
|
|
183005
|
+
abort() {
|
|
183006
|
+
if (current !== undefined) {
|
|
183007
|
+
stoppedByCaller = true;
|
|
183008
|
+
current.abort();
|
|
183009
|
+
}
|
|
183010
|
+
},
|
|
183011
|
+
async connect(providerId, notify) {
|
|
183012
|
+
if (!allows(providerId)) {
|
|
183013
|
+
return { ok: false, message: "this provider has no device login; its key is entered in the shell with /connect" };
|
|
183014
|
+
}
|
|
183015
|
+
if (current !== undefined) {
|
|
183016
|
+
return { ok: false, message: "a device login is already running" };
|
|
183017
|
+
}
|
|
183018
|
+
const controller = new AbortController;
|
|
183019
|
+
current = controller;
|
|
183020
|
+
stoppedByCaller = false;
|
|
183021
|
+
let timedOut = false;
|
|
183022
|
+
const timer = setTimeout(() => {
|
|
183023
|
+
timedOut = true;
|
|
183024
|
+
controller.abort();
|
|
183025
|
+
}, timeoutMs);
|
|
183026
|
+
const endedEarly = () => ({ ok: false, message: timedOut ? LOGIN_TIMED_OUT_TEXT : stoppedByCaller ? LOGIN_ABORTED_TEXT : LOGIN_FAILED_TEXT });
|
|
183027
|
+
try {
|
|
183028
|
+
const result = await login({
|
|
183029
|
+
provider: providerId,
|
|
183030
|
+
fetch: (input, init) => (deps.fetch ?? globalThis.fetch)(input, init),
|
|
183031
|
+
signal: controller.signal,
|
|
183032
|
+
...deps.configDir !== undefined ? { dir: deps.configDir } : {},
|
|
183033
|
+
onChallenge: (challenge) => {
|
|
183034
|
+
notify(`Open ${challenge.verificationUri} and enter the code ${challenge.userCode}`).catch(() => {
|
|
183035
|
+
return;
|
|
183036
|
+
});
|
|
183037
|
+
}
|
|
183038
|
+
});
|
|
183039
|
+
if (!result.ok) {
|
|
183040
|
+
return endedEarly();
|
|
183041
|
+
}
|
|
183042
|
+
if (controller.signal.aborted) {
|
|
183043
|
+
return { ok: false, message: LOGIN_NOT_APPLIED_TEXT };
|
|
183044
|
+
}
|
|
183045
|
+
if (deps.isBusy?.() === true) {
|
|
183046
|
+
return { ok: false, message: "signed in, but main is busy, so the login was not applied to this session; run /connect in the shell when it is idle" };
|
|
183047
|
+
}
|
|
183048
|
+
applyEnv(deps.configDir);
|
|
183049
|
+
deps.onConnected?.(providerId);
|
|
183050
|
+
return { ok: true, message: "connected. The session and model are unchanged." };
|
|
183051
|
+
} catch {
|
|
183052
|
+
return endedEarly();
|
|
183053
|
+
} finally {
|
|
183054
|
+
clearTimeout(timer);
|
|
183055
|
+
current = undefined;
|
|
183056
|
+
}
|
|
183057
|
+
}
|
|
183058
|
+
};
|
|
183059
|
+
}
|
|
183060
|
+
function createConnectedProvidersCache(load, options = {}) {
|
|
183061
|
+
const ttlMs = options.ttlMs ?? 15000;
|
|
183062
|
+
const now = options.now ?? Date.now;
|
|
183063
|
+
let entry;
|
|
183064
|
+
return {
|
|
183065
|
+
get() {
|
|
183066
|
+
if (entry !== undefined && now() - entry.at < ttlMs) {
|
|
183067
|
+
return entry.value;
|
|
183068
|
+
}
|
|
183069
|
+
const value = load();
|
|
183070
|
+
const mine = { at: now(), value };
|
|
183071
|
+
entry = mine;
|
|
183072
|
+
value.catch(() => {
|
|
183073
|
+
if (entry === mine)
|
|
183074
|
+
entry = undefined;
|
|
183075
|
+
});
|
|
183076
|
+
return value;
|
|
183077
|
+
},
|
|
183078
|
+
invalidate() {
|
|
183079
|
+
entry = undefined;
|
|
183080
|
+
}
|
|
183081
|
+
};
|
|
183082
|
+
}
|
|
183083
|
+
var NO_CREDENTIAL_TEXT = "no credential, Connect first", REMOTE_DEVICE_LOGIN_LIMIT_MS, LOGIN_FAILED_TEXT = "Device login failed. Try /connect in the shell.", LOGIN_NOT_APPLIED_TEXT = "Signed in, but not applied to this session; run /connect again or restart.", LOGIN_TIMED_OUT_TEXT = "Device login timed out; nothing was saved.", LOGIN_ABORTED_TEXT = "Device login was stopped; nothing was saved.";
|
|
183084
|
+
var init_remote_provider_hooks = __esm(() => {
|
|
183085
|
+
init_catalog3();
|
|
183086
|
+
init_grants();
|
|
183087
|
+
init_login();
|
|
183088
|
+
REMOTE_DEVICE_LOGIN_LIMIT_MS = 10 * 60000;
|
|
183089
|
+
});
|
|
183090
|
+
|
|
182877
183091
|
// src/tui/ci-triage-inspector.ts
|
|
182878
183092
|
function isCiTriageCommand(line) {
|
|
182879
183093
|
const token = line.trim().split(/\s+/)[0] ?? "";
|
|
@@ -190124,7 +190338,7 @@ async function runGameModelTurn(game, state, opts) {
|
|
|
190124
190338
|
return { move: undefined, error: `model error: ${turn.error.message}`, stats };
|
|
190125
190339
|
}
|
|
190126
190340
|
if (!turn.credentialAvailable && opts.providerFactory === undefined) {
|
|
190127
|
-
return { move: undefined, error: "no model credential \u2014 configure a provider first (/
|
|
190341
|
+
return { move: undefined, error: "no model credential \u2014 configure a provider first (/connect)", stats };
|
|
190128
190342
|
}
|
|
190129
190343
|
return { move: game.parseMove(turn.text, state), error: undefined, stats };
|
|
190130
190344
|
}
|
|
@@ -197075,7 +197289,7 @@ async function promptCustomProviderWizard(otui, target) {
|
|
|
197075
197289
|
while (true) {
|
|
197076
197290
|
const nameStep = await promptCustomFieldStep(otui, target, {
|
|
197077
197291
|
title: "Provider name",
|
|
197078
|
-
note: "unique id used in /
|
|
197292
|
+
note: "unique id used in /connect (letters, digits, - _)"
|
|
197079
197293
|
});
|
|
197080
197294
|
if (nameStep.kind === "back") {
|
|
197081
197295
|
return;
|
|
@@ -197656,6 +197870,146 @@ function openStepSurface(otui, target, opts) {
|
|
|
197656
197870
|
}
|
|
197657
197871
|
};
|
|
197658
197872
|
}
|
|
197873
|
+
function tabDigit(key) {
|
|
197874
|
+
const digit = key.sequence.length === 1 ? key.sequence : key.name;
|
|
197875
|
+
return digit.length === 1 && digit >= "1" && digit <= "9" ? Number(digit) : undefined;
|
|
197876
|
+
}
|
|
197877
|
+
function openTabbedSurface(otui, target, opts) {
|
|
197878
|
+
const r = stepRenderer(target);
|
|
197879
|
+
if (isModalChrome(target)) {
|
|
197880
|
+
let closedByStep = false;
|
|
197881
|
+
const tabIds = opts.tabs.map((tab) => tab.id);
|
|
197882
|
+
const handle = openModal(otui, target, {
|
|
197883
|
+
title: opts.title,
|
|
197884
|
+
tabs: opts.tabs,
|
|
197885
|
+
initialTab: opts.initialTab,
|
|
197886
|
+
footer: opts.footerFor(opts.initialTab),
|
|
197887
|
+
footerFor: opts.footerFor,
|
|
197888
|
+
contentRows: opts.contentRows,
|
|
197889
|
+
renderTab: (tabId, tabBody, ctx) => {
|
|
197890
|
+
opts.renderTab(tabId, tabBody, ctx);
|
|
197891
|
+
},
|
|
197892
|
+
onArrowKeys: opts.onArrowKeys,
|
|
197893
|
+
claimKey: (key) => {
|
|
197894
|
+
if (key.name !== "tab" || handle === undefined)
|
|
197895
|
+
return false;
|
|
197896
|
+
const at = tabIds.indexOf(handle.activeTab());
|
|
197897
|
+
const next = tabIds[(at + 1) % tabIds.length];
|
|
197898
|
+
if (next !== undefined)
|
|
197899
|
+
handle.setTab(next);
|
|
197900
|
+
return true;
|
|
197901
|
+
},
|
|
197902
|
+
ownsKeys: opts.ownsKeys,
|
|
197903
|
+
onClose: () => {
|
|
197904
|
+
if (!closedByStep)
|
|
197905
|
+
opts.onEscape();
|
|
197906
|
+
}
|
|
197907
|
+
});
|
|
197908
|
+
if (handle !== undefined) {
|
|
197909
|
+
const opened = handle;
|
|
197910
|
+
return {
|
|
197911
|
+
modal: true,
|
|
197912
|
+
activeTab: () => opened.activeTab(),
|
|
197913
|
+
setTab: (id) => opened.setTab(id),
|
|
197914
|
+
close: () => {
|
|
197915
|
+
if (closedByStep)
|
|
197916
|
+
return;
|
|
197917
|
+
closedByStep = true;
|
|
197918
|
+
opened.close({ restoreFocus: false });
|
|
197919
|
+
}
|
|
197920
|
+
};
|
|
197921
|
+
}
|
|
197922
|
+
}
|
|
197923
|
+
const theme = () => getTheme();
|
|
197924
|
+
const box = overlayBox(otui, r, opts.id);
|
|
197925
|
+
r.root.add(box);
|
|
197926
|
+
box.add(new otui.TextRenderable(r, { id: `${opts.id}-title`, content: otui.t`${boldChunk(otui, opts.title)} ${dimChunk(otui, "(esc close)")}` }));
|
|
197927
|
+
const strip = new otui.BoxRenderable(r, { id: `${opts.id}-tabs`, width: "100%", height: 1, flexShrink: 0, flexDirection: "row" });
|
|
197928
|
+
const body = new otui.BoxRenderable(r, { id: `${opts.id}-body`, width: "100%", flexGrow: 1, minHeight: 0, flexDirection: "column" });
|
|
197929
|
+
const footer = new otui.TextRenderable(r, { id: `${opts.id}-footer`, content: "", flexShrink: 0 });
|
|
197930
|
+
box.add(strip);
|
|
197931
|
+
box.add(body);
|
|
197932
|
+
box.add(footer);
|
|
197933
|
+
let active = opts.initialTab;
|
|
197934
|
+
let closed = false;
|
|
197935
|
+
const paintStrip = () => {
|
|
197936
|
+
clearTranscriptChildren(strip);
|
|
197937
|
+
for (const [index, tab] of opts.tabs.entries()) {
|
|
197938
|
+
const isActive = tab.id === active;
|
|
197939
|
+
const label = isActive ? `[${tab.label}]` : ` ${tab.label} `;
|
|
197940
|
+
const prefix = index === 0 ? "" : " ";
|
|
197941
|
+
strip.add(new otui.TextRenderable(r, {
|
|
197942
|
+
id: `${opts.id}-tab-${tab.id}`,
|
|
197943
|
+
content: isActive ? otui.t`${prefix}${boldChunk(otui, label)}` : otui.t`${prefix}${dimChunk(otui, label)}`,
|
|
197944
|
+
fg: isActive ? theme().focus : theme().muted,
|
|
197945
|
+
onMouseDown: () => {
|
|
197946
|
+
if (!closed && tab.id !== active)
|
|
197947
|
+
mount(tab.id);
|
|
197948
|
+
}
|
|
197949
|
+
}));
|
|
197950
|
+
}
|
|
197951
|
+
};
|
|
197952
|
+
function mount(tabId) {
|
|
197953
|
+
clearTranscriptChildren(body);
|
|
197954
|
+
active = tabId;
|
|
197955
|
+
paintStrip();
|
|
197956
|
+
footer.content = otui.t`${dimChunk(otui, formatModalFooter(opts.footerFor(tabId)))}`;
|
|
197957
|
+
opts.renderTab(tabId, body, { width: Math.max(20, r.width - 4), height: Math.max(4, r.height - 6) });
|
|
197958
|
+
}
|
|
197959
|
+
const cycle = () => {
|
|
197960
|
+
const at = opts.tabs.findIndex((tab) => tab.id === active);
|
|
197961
|
+
const next = opts.tabs[(at + 1) % opts.tabs.length];
|
|
197962
|
+
if (next !== undefined)
|
|
197963
|
+
mount(next.id);
|
|
197964
|
+
};
|
|
197965
|
+
const close = () => {
|
|
197966
|
+
if (closed)
|
|
197967
|
+
return;
|
|
197968
|
+
closed = true;
|
|
197969
|
+
unsub();
|
|
197970
|
+
r.root.remove(box);
|
|
197971
|
+
};
|
|
197972
|
+
const stop = (key) => {
|
|
197973
|
+
key.preventDefault();
|
|
197974
|
+
key.stopPropagation();
|
|
197975
|
+
};
|
|
197976
|
+
const unsub = onKeypress4(r, (key) => {
|
|
197977
|
+
if (closed || opts.ownsKeys())
|
|
197978
|
+
return;
|
|
197979
|
+
if (key.name === "escape" || key.name === "x" || key.sequence === "x") {
|
|
197980
|
+
close();
|
|
197981
|
+
opts.onEscape();
|
|
197982
|
+
stop(key);
|
|
197983
|
+
return;
|
|
197984
|
+
}
|
|
197985
|
+
if ((key.name === "left" || key.name === "right") && opts.onArrowKeys(key, key.name)) {
|
|
197986
|
+
stop(key);
|
|
197987
|
+
return;
|
|
197988
|
+
}
|
|
197989
|
+
if (key.name === "tab") {
|
|
197990
|
+
cycle();
|
|
197991
|
+
stop(key);
|
|
197992
|
+
return;
|
|
197993
|
+
}
|
|
197994
|
+
const jump = tabDigit(key);
|
|
197995
|
+
const to = jump === undefined ? undefined : opts.tabs[jump - 1];
|
|
197996
|
+
if (to !== undefined) {
|
|
197997
|
+
if (to.id !== active)
|
|
197998
|
+
mount(to.id);
|
|
197999
|
+
stop(key);
|
|
198000
|
+
}
|
|
198001
|
+
});
|
|
198002
|
+
mount(opts.initialTab);
|
|
198003
|
+
return {
|
|
198004
|
+
modal: false,
|
|
198005
|
+
activeTab: () => active,
|
|
198006
|
+
setTab: (id) => {
|
|
198007
|
+
if (!closed && id !== active && opts.tabs.some((tab) => tab.id === id))
|
|
198008
|
+
mount(id);
|
|
198009
|
+
},
|
|
198010
|
+
close
|
|
198011
|
+
};
|
|
198012
|
+
}
|
|
197659
198013
|
function selectStepFooter(escLabel) {
|
|
197660
198014
|
return [
|
|
197661
198015
|
{ key: "\u2191/\u2193", label: "select" },
|
|
@@ -198218,102 +198572,151 @@ function pickProviderStep(otui, target, detected) {
|
|
|
198218
198572
|
});
|
|
198219
198573
|
});
|
|
198220
198574
|
}
|
|
198221
|
-
function
|
|
198575
|
+
function clipText(text, max) {
|
|
198576
|
+
if (max <= 1)
|
|
198577
|
+
return "";
|
|
198578
|
+
return text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`;
|
|
198579
|
+
}
|
|
198580
|
+
async function probeProviderConnection(name, deps) {
|
|
198581
|
+
const provider = connectionProviderByName(name, deps.configDir);
|
|
198582
|
+
if (provider === undefined)
|
|
198583
|
+
return { kind: "unknown" };
|
|
198584
|
+
const env = deps.env ?? process.env;
|
|
198585
|
+
if (deps.requireCredential && (provider.requiresApiKey ?? true) && classifyProviderConnection(name, env, deps.configDir).kind === "no-credential") {
|
|
198586
|
+
return { kind: "no-credential" };
|
|
198587
|
+
}
|
|
198588
|
+
deps.onStart?.();
|
|
198589
|
+
let profileSummary;
|
|
198590
|
+
const result = await testProviderConnection(provider, deps.fetch ?? globalThis.fetch, env, {
|
|
198591
|
+
...deps.configDir !== undefined ? { dir: deps.configDir } : {},
|
|
198592
|
+
onSummary: (s) => {
|
|
198593
|
+
profileSummary = s;
|
|
198594
|
+
}
|
|
198595
|
+
});
|
|
198596
|
+
if (result.source === "live") {
|
|
198597
|
+
return { kind: "live", modelCount: result.models.length, ...profileSummary !== undefined ? { profileSummary } : {} };
|
|
198598
|
+
}
|
|
198599
|
+
return { kind: "failed", failure: result.failure ?? { kind: "empty" } };
|
|
198600
|
+
}
|
|
198601
|
+
function pickProvidersTabbedStep(otui, target, opts) {
|
|
198222
198602
|
const r = stepRenderer(target);
|
|
198223
198603
|
return new Promise((resolve) => {
|
|
198224
|
-
|
|
198225
|
-
|
|
198226
|
-
|
|
198604
|
+
const connected = opts.connected;
|
|
198605
|
+
const addRows = () => [...opts.all, CUSTOM_ADD_ENTRY];
|
|
198606
|
+
const rowsOf = (tabId) => tabId === "connected" ? connected : addRows();
|
|
198607
|
+
const isConnected = (name) => connected.some((d) => d.name === name);
|
|
198608
|
+
let tab = connected.length === 0 ? "add" : opts.initialTab;
|
|
198609
|
+
const selected = { connected: 0, add: 0 };
|
|
198610
|
+
const windowStart = { connected: 0, add: 0 };
|
|
198611
|
+
let connectAction = "label";
|
|
198612
|
+
let addAction = "connect";
|
|
198227
198613
|
let pendingDisconnect;
|
|
198228
198614
|
let resolved = false;
|
|
198229
|
-
let
|
|
198615
|
+
let notice = opts.notice;
|
|
198616
|
+
let nextNotice;
|
|
198617
|
+
let rendered = false;
|
|
198618
|
+
let bodyBox;
|
|
198619
|
+
let addHint;
|
|
198620
|
+
const addHintContent = (total) => otui.t`${dimChunk(otui, `Connect saves a key or login \xB7 Test checks it ${total === 0 ? 0 : selected.add + 1}/${total}`)}`;
|
|
198621
|
+
let bodyWidth = 70;
|
|
198622
|
+
let bodyHeight = 15;
|
|
198623
|
+
const statuses = new Map;
|
|
198624
|
+
let blocks = [];
|
|
198230
198625
|
const labelOf = (d) => d.label ?? d.name;
|
|
198231
198626
|
const catalogCache = loadProviderCatalogCache(opts.configDir);
|
|
198232
198627
|
const noteOf = (d) => {
|
|
198233
198628
|
const base = d.note ?? `${d.models.length} model(s)`;
|
|
198234
198629
|
const entry = catalogCache?.providers[d.name];
|
|
198235
|
-
|
|
198236
|
-
|
|
198237
|
-
|
|
198238
|
-
|
|
198239
|
-
|
|
198240
|
-
|
|
198241
|
-
|
|
198242
|
-
|
|
198243
|
-
|
|
198244
|
-
|
|
198245
|
-
|
|
198246
|
-
|
|
198247
|
-
|
|
198248
|
-
|
|
198249
|
-
|
|
198250
|
-
|
|
198251
|
-
|
|
198252
|
-
|
|
198253
|
-
|
|
198254
|
-
|
|
198255
|
-
{
|
|
198256
|
-
|
|
198257
|
-
|
|
198258
|
-
|
|
198259
|
-
|
|
198260
|
-
onEscape: () => finish(undefined)
|
|
198261
|
-
});
|
|
198262
|
-
const body = surface.body;
|
|
198263
|
-
function finish(value) {
|
|
198264
|
-
if (resolved)
|
|
198265
|
-
return;
|
|
198266
|
-
resolved = true;
|
|
198267
|
-
unsubEscapeGuard();
|
|
198268
|
-
unsub();
|
|
198269
|
-
surface.close();
|
|
198270
|
-
resolve(value);
|
|
198630
|
+
const full = entry === undefined ? base : `${base} \xB7 ${describeCatalogStatus(entry.status)}${entry.balance !== undefined ? ` \xB7 ${formatBalance(entry.balance)}` : ""} \xB7 ${formatCatalogAge(entry.fetchedAt)}`;
|
|
198631
|
+
return clipText(full, bodyWidth - 24 - labelOf(d).length);
|
|
198632
|
+
};
|
|
198633
|
+
const addNoteOf = (d) => {
|
|
198634
|
+
if (d.name === CUSTOM_PROVIDER_ADD_ID)
|
|
198635
|
+
return "OpenAI-compatible \xB7 name, URL, key, models";
|
|
198636
|
+
const room = bodyWidth - 24 - labelOf(d).length - (isConnected(d.name) ? 13 : 0);
|
|
198637
|
+
return clipText(d.note ?? `${d.models.length} model(s)`, room);
|
|
198638
|
+
};
|
|
198639
|
+
const statusKey = (tabId, name) => `${tabId}:${name}`;
|
|
198640
|
+
function setStatus(tabId, name, content) {
|
|
198641
|
+
statuses.set(statusKey(tabId, name), content);
|
|
198642
|
+
const block = blocks.find((b) => b.tab === tabId && b.name === name);
|
|
198643
|
+
if (block !== undefined)
|
|
198644
|
+
block.status.content = content;
|
|
198645
|
+
}
|
|
198646
|
+
function labelContent(block, d) {
|
|
198647
|
+
const active = block.index === selected[block.tab];
|
|
198648
|
+
if (block.tab === "connected") {
|
|
198649
|
+
const head = active && connectAction === "label" ? boldChunk(otui, labelOf(d)) : labelOf(d);
|
|
198650
|
+
return otui.t`${head} ${dimChunk(otui, noteOf(d))}`;
|
|
198651
|
+
}
|
|
198652
|
+
const head = active ? boldChunk(otui, labelOf(d)) : labelOf(d);
|
|
198653
|
+
const mark = isConnected(d.name) ? roleChunk(otui, "ok", " \u2713 connected") : "";
|
|
198654
|
+
return otui.t`${head}${mark} ${dimChunk(otui, addNoteOf(d))}`;
|
|
198271
198655
|
}
|
|
198272
198656
|
function clearPending() {
|
|
198273
198657
|
if (pendingDisconnect === undefined)
|
|
198274
198658
|
return;
|
|
198275
|
-
const armed =
|
|
198659
|
+
const armed = pendingDisconnect;
|
|
198276
198660
|
pendingDisconnect = undefined;
|
|
198277
|
-
|
|
198278
|
-
armed.status.content = "";
|
|
198661
|
+
setStatus("connected", armed, "");
|
|
198279
198662
|
}
|
|
198280
198663
|
function applyHighlight() {
|
|
198281
|
-
|
|
198282
|
-
|
|
198283
|
-
|
|
198664
|
+
if (tab === "add" && addHint !== undefined)
|
|
198665
|
+
addHint.content = addHintContent(rowsOf("add").length);
|
|
198666
|
+
for (const block of blocks) {
|
|
198667
|
+
const d = rowsOf(block.tab)[block.index];
|
|
198668
|
+
if (d === undefined)
|
|
198284
198669
|
continue;
|
|
198285
|
-
const active =
|
|
198670
|
+
const active = block.index === selected[block.tab];
|
|
198286
198671
|
block.outer.backgroundColor = active ? getTheme().highlight : undefined;
|
|
198287
|
-
block.labelText.content =
|
|
198288
|
-
block.
|
|
198289
|
-
|
|
198672
|
+
block.labelText.content = labelContent(block, d);
|
|
198673
|
+
if (block.tab === "connected") {
|
|
198674
|
+
block.first.setActive(active && connectAction === "test");
|
|
198675
|
+
block.second?.setActive(active && connectAction === "disconnect");
|
|
198676
|
+
} else {
|
|
198677
|
+
block.first.setActive(active && (addAction === "connect" || block.second === undefined));
|
|
198678
|
+
block.second?.setActive(active && addAction === "test");
|
|
198679
|
+
}
|
|
198290
198680
|
}
|
|
198291
198681
|
}
|
|
198292
|
-
|
|
198293
|
-
|
|
198294
|
-
const provider = connectionProviderByName(name, opts.configDir);
|
|
198295
|
-
if (block === undefined)
|
|
198682
|
+
function finish(value) {
|
|
198683
|
+
if (resolved)
|
|
198296
198684
|
return;
|
|
198297
|
-
|
|
198298
|
-
|
|
198685
|
+
resolved = true;
|
|
198686
|
+
unsub();
|
|
198687
|
+
surface?.close();
|
|
198688
|
+
resolve(value);
|
|
198689
|
+
}
|
|
198690
|
+
async function runTest(tabId, name) {
|
|
198691
|
+
const d = rowsOf(tabId).find((row) => row.name === name);
|
|
198692
|
+
if (d === undefined)
|
|
198299
198693
|
return;
|
|
198300
|
-
|
|
198301
|
-
|
|
198302
|
-
|
|
198303
|
-
|
|
198304
|
-
|
|
198305
|
-
|
|
198306
|
-
profileSummary = s;
|
|
198307
|
-
}
|
|
198694
|
+
const outcome = await probeProviderConnection(name, {
|
|
198695
|
+
...opts.fetch !== undefined ? { fetch: opts.fetch } : {},
|
|
198696
|
+
...opts.env !== undefined ? { env: opts.env } : {},
|
|
198697
|
+
...opts.configDir !== undefined ? { configDir: opts.configDir } : {},
|
|
198698
|
+
requireCredential: tabId === "add",
|
|
198699
|
+
onStart: () => setStatus(tabId, name, otui.t`${dimChunk(otui, "testing\u2026")}`)
|
|
198308
198700
|
});
|
|
198309
|
-
|
|
198310
|
-
if (current === undefined || resolved)
|
|
198701
|
+
if (resolved)
|
|
198311
198702
|
return;
|
|
198312
|
-
|
|
198703
|
+
switch (outcome.kind) {
|
|
198704
|
+
case "unknown":
|
|
198705
|
+
setStatus(tabId, name, otui.t`${roleChunk(otui, "error", "\u2717")} unknown provider \u2014 cannot test`);
|
|
198706
|
+
return;
|
|
198707
|
+
case "no-credential":
|
|
198708
|
+
setStatus(tabId, name, otui.t`${roleChunk(otui, "attention", "\xB7")} ${NO_CREDENTIAL_TEXT}`);
|
|
198709
|
+
return;
|
|
198710
|
+
case "live":
|
|
198711
|
+
setStatus(tabId, name, otui.t`${roleChunk(otui, "ok", "\u2713")} ok \u2014 ${String(outcome.modelCount)} model(s)${formatProfileSummarySuffix(outcome.profileSummary)}`);
|
|
198712
|
+
return;
|
|
198713
|
+
case "failed":
|
|
198714
|
+
setStatus(tabId, name, otui.t`${roleChunk(otui, "error", "\u2717")} ${modelsFailureLine(labelOf(d), outcome.failure)}`);
|
|
198715
|
+
return;
|
|
198716
|
+
}
|
|
198313
198717
|
}
|
|
198314
198718
|
function armOrConfirmDisconnect(name) {
|
|
198315
|
-
|
|
198316
|
-
if (block === undefined)
|
|
198719
|
+
if (!blocks.some((b) => b.tab === "connected" && b.name === name))
|
|
198317
198720
|
return;
|
|
198318
198721
|
if (pendingDisconnect !== name) {
|
|
198319
198722
|
clearPending();
|
|
@@ -198321,157 +198724,510 @@ function pickConnectedProviderStep(otui, target, detected, opts) {
|
|
|
198321
198724
|
const env = opts.env ?? process.env;
|
|
198322
198725
|
const classification = classifyProviderConnection(name, env, opts.configDir);
|
|
198323
198726
|
const warning = sharedCredentialWarning(classification.sharedWith, classification.envKey);
|
|
198324
|
-
const label = labelOf(
|
|
198325
|
-
|
|
198727
|
+
const label = labelOf(connected.find((d) => d.name === name) ?? { name, models: [] });
|
|
198728
|
+
setStatus("connected", name, warning === undefined ? otui.t`${roleChunk(otui, "attention", "?")} disconnect '${label}'? ${dimChunk(otui, "click/Enter Disconnect again to confirm, Esc to cancel")}` : otui.t`${roleChunk(otui, "attention", "?")} disconnect '${label}'? ${roleChunk(otui, "attention", `Note: ${warning}.`)} ${dimChunk(otui, "click/Enter Disconnect again to confirm, Esc to cancel")}`);
|
|
198326
198729
|
return;
|
|
198327
198730
|
}
|
|
198328
198731
|
pendingDisconnect = undefined;
|
|
198329
198732
|
const result = disconnectProvider(name, opts.env ?? process.env, opts.configDir);
|
|
198330
198733
|
if (!result.ok) {
|
|
198331
|
-
|
|
198734
|
+
setStatus("connected", name, otui.t`${roleChunk(otui, "error", "\u2717")} not removed \u2014 ${result.reason ?? "unknown reason"}`);
|
|
198332
198735
|
return;
|
|
198333
198736
|
}
|
|
198334
198737
|
opts.onDisconnected?.(name, result.sharedWith);
|
|
198335
|
-
|
|
198738
|
+
const at = connected.findIndex((d) => d.name === name);
|
|
198739
|
+
if (at >= 0)
|
|
198740
|
+
connected.splice(at, 1);
|
|
198741
|
+
statuses.delete(statusKey("connected", name));
|
|
198742
|
+
if (connected.length === 0) {
|
|
198743
|
+
switchTab("add", { tone: "info", text: "No providers connected. Add one below." });
|
|
198744
|
+
return;
|
|
198745
|
+
}
|
|
198336
198746
|
paint();
|
|
198337
198747
|
}
|
|
198338
|
-
function
|
|
198339
|
-
|
|
198340
|
-
|
|
198341
|
-
|
|
198342
|
-
|
|
198748
|
+
function switchTab(id, withNotice) {
|
|
198749
|
+
if (tab === id)
|
|
198750
|
+
return;
|
|
198751
|
+
nextNotice = withNotice;
|
|
198752
|
+
surface?.setTab(id);
|
|
198753
|
+
}
|
|
198754
|
+
function moveSelection(direction) {
|
|
198755
|
+
const total = rowsOf(tab).length;
|
|
198756
|
+
selected[tab] = stepQueueNavIndex(selected[tab], total, direction);
|
|
198757
|
+
if (addCustomRowSelected())
|
|
198758
|
+
addAction = "connect";
|
|
198759
|
+
const start = windowStart[tab];
|
|
198760
|
+
if (selected[tab] < start || selected[tab] >= start + capacityRows())
|
|
198761
|
+
paint();
|
|
198762
|
+
else
|
|
198763
|
+
applyHighlight();
|
|
198764
|
+
}
|
|
198765
|
+
function addCustomRowSelected() {
|
|
198766
|
+
return tab === "add" && rowsOf("add")[selected.add]?.name === CUSTOM_PROVIDER_ADD_ID;
|
|
198767
|
+
}
|
|
198768
|
+
function capacityRows() {
|
|
198769
|
+
return Math.max(1, Math.floor((bodyHeight - 2 - (notice === undefined ? 0 : 1)) / 2));
|
|
198770
|
+
}
|
|
198771
|
+
function runAddSelection(d) {
|
|
198772
|
+
if (d.name === CUSTOM_PROVIDER_ADD_ID) {
|
|
198773
|
+
finish({ kind: "custom" });
|
|
198774
|
+
return;
|
|
198343
198775
|
}
|
|
198344
|
-
|
|
198345
|
-
|
|
198346
|
-
|
|
198776
|
+
finish({ kind: "connect", provider: d });
|
|
198777
|
+
}
|
|
198778
|
+
function paint() {
|
|
198779
|
+
if (bodyBox === undefined)
|
|
198347
198780
|
return;
|
|
198781
|
+
const body = bodyBox;
|
|
198782
|
+
clearTranscriptChildren(body);
|
|
198783
|
+
blocks = [];
|
|
198784
|
+
const rows = rowsOf(tab);
|
|
198785
|
+
selected[tab] = clampQueueNavIndex(selected[tab], rows.length);
|
|
198786
|
+
const header = new otui.BoxRenderable(r, { id: `prov-${tab}-header`, width: "100%", flexDirection: "row", flexShrink: 0 });
|
|
198787
|
+
if (tab === "connected") {
|
|
198788
|
+
const addButton = smallActionButton(otui, r, "Add provider", "cp-add-provider", getTheme().focus, () => {
|
|
198789
|
+
clearPending();
|
|
198790
|
+
switchTab("add");
|
|
198791
|
+
});
|
|
198792
|
+
addButton.box.marginLeft = 0;
|
|
198793
|
+
header.add(addButton.box);
|
|
198794
|
+
header.add(new otui.TextRenderable(r, { id: "cp-count", marginLeft: 1, content: otui.t`${dimChunk(otui, `${connected.length} connected`)}` }));
|
|
198795
|
+
} else {
|
|
198796
|
+
addHint = new otui.TextRenderable(r, { id: "ap-hint", content: addHintContent(rows.length) });
|
|
198797
|
+
header.add(addHint);
|
|
198798
|
+
}
|
|
198799
|
+
body.add(header);
|
|
198800
|
+
if (notice !== undefined) {
|
|
198801
|
+
const icon = notice.tone === "ok" ? roleChunk(otui, "ok", "\u2713") : notice.tone === "error" ? roleChunk(otui, "error", "\u2717") : dimChunk(otui, "\xB7");
|
|
198802
|
+
body.add(new otui.TextRenderable(r, { id: "prov-notice", content: otui.t`${icon} ${notice.text}`, flexShrink: 0 }));
|
|
198348
198803
|
}
|
|
198349
|
-
|
|
198350
|
-
|
|
198804
|
+
if (rows.length === 0) {
|
|
198805
|
+
body.add(new otui.TextRenderable(r, { id: "cp-empty", content: otui.t`${dimChunk(otui, "Nothing connected yet. Press a, or open Add provider.")}` }));
|
|
198806
|
+
return;
|
|
198807
|
+
}
|
|
198808
|
+
const cap = capacityRows();
|
|
198809
|
+
let start = windowStart[tab];
|
|
198810
|
+
if (selected[tab] < start)
|
|
198811
|
+
start = selected[tab];
|
|
198812
|
+
else if (selected[tab] >= start + cap)
|
|
198813
|
+
start = selected[tab] - cap + 1;
|
|
198814
|
+
start = Math.max(0, Math.min(start, Math.max(0, rows.length - cap)));
|
|
198815
|
+
windowStart[tab] = start;
|
|
198816
|
+
const prefix = tab === "connected" ? "cp" : "ap";
|
|
198817
|
+
for (let i = start;i < Math.min(rows.length, start + cap); i++) {
|
|
198351
198818
|
const d = rows[i];
|
|
198352
|
-
const
|
|
198353
|
-
const
|
|
198354
|
-
const
|
|
198355
|
-
|
|
198356
|
-
|
|
198357
|
-
});
|
|
198819
|
+
const isCustom = d.name === CUSTOM_PROVIDER_ADD_ID;
|
|
198820
|
+
const outer = new otui.BoxRenderable(r, { id: `${prefix}-${d.name}`, width: "100%", flexDirection: "column", flexShrink: 0 });
|
|
198821
|
+
const row = new otui.BoxRenderable(r, { id: `${prefix}-${d.name}-row`, width: "100%", flexDirection: "row" });
|
|
198822
|
+
const base = { tab, index: i, name: d.name };
|
|
198823
|
+
const labelText = new otui.TextRenderable(r, { id: `${prefix}-${d.name}-label-t`, content: labelContent(base, d) });
|
|
198358
198824
|
const label = new otui.BoxRenderable(r, {
|
|
198359
|
-
id:
|
|
198825
|
+
id: `${prefix}-${d.name}-label`,
|
|
198360
198826
|
flexGrow: 1,
|
|
198361
198827
|
minWidth: 0,
|
|
198362
198828
|
onMouseDown: (event) => {
|
|
198363
198829
|
event.stopPropagation();
|
|
198364
198830
|
clearPending();
|
|
198365
|
-
|
|
198366
|
-
|
|
198367
|
-
|
|
198831
|
+
selected[tab] = i;
|
|
198832
|
+
if (tab === "connected") {
|
|
198833
|
+
connectAction = "label";
|
|
198834
|
+
finish({ kind: "pick", provider: d });
|
|
198835
|
+
return;
|
|
198836
|
+
}
|
|
198837
|
+
if (isCustom)
|
|
198838
|
+
addAction = "connect";
|
|
198839
|
+
applyHighlight();
|
|
198368
198840
|
}
|
|
198369
198841
|
});
|
|
198370
198842
|
label.add(labelText);
|
|
198371
198843
|
row.add(label);
|
|
198372
|
-
|
|
198373
|
-
|
|
198374
|
-
|
|
198375
|
-
|
|
198376
|
-
|
|
198377
|
-
|
|
198378
|
-
|
|
198379
|
-
|
|
198380
|
-
|
|
198381
|
-
|
|
198382
|
-
|
|
198383
|
-
|
|
198384
|
-
|
|
198385
|
-
|
|
198386
|
-
|
|
198844
|
+
let first;
|
|
198845
|
+
let second;
|
|
198846
|
+
if (tab === "connected") {
|
|
198847
|
+
first = smallActionButton(otui, r, "Test", `cp-test-${d.name}`, getTheme().focus, () => {
|
|
198848
|
+
clearPending();
|
|
198849
|
+
selected.connected = i;
|
|
198850
|
+
connectAction = "test";
|
|
198851
|
+
applyHighlight();
|
|
198852
|
+
runTest("connected", d.name);
|
|
198853
|
+
});
|
|
198854
|
+
second = smallActionButton(otui, r, "Disconnect", `cp-disc-${d.name}`, getTheme().error, () => {
|
|
198855
|
+
selected.connected = i;
|
|
198856
|
+
connectAction = "disconnect";
|
|
198857
|
+
applyHighlight();
|
|
198858
|
+
armOrConfirmDisconnect(d.name);
|
|
198859
|
+
});
|
|
198860
|
+
} else {
|
|
198861
|
+
first = smallActionButton(otui, r, "Connect", `ap-connect-${d.name}`, getTheme().focus, () => {
|
|
198862
|
+
selected.add = i;
|
|
198863
|
+
addAction = "connect";
|
|
198864
|
+
runAddSelection(d);
|
|
198865
|
+
});
|
|
198866
|
+
if (!isCustom) {
|
|
198867
|
+
second = smallActionButton(otui, r, "Test", `ap-test-${d.name}`, getTheme().focus, () => {
|
|
198868
|
+
selected.add = i;
|
|
198869
|
+
addAction = "test";
|
|
198870
|
+
applyHighlight();
|
|
198871
|
+
runTest("add", d.name);
|
|
198872
|
+
});
|
|
198873
|
+
}
|
|
198874
|
+
}
|
|
198875
|
+
row.add(first.box);
|
|
198876
|
+
if (second !== undefined)
|
|
198877
|
+
row.add(second.box);
|
|
198387
198878
|
outer.add(row);
|
|
198388
|
-
const status = new otui.TextRenderable(r, { id:
|
|
198879
|
+
const status = new otui.TextRenderable(r, { id: `${prefix}-${d.name}-status`, content: statuses.get(statusKey(tab, d.name)) ?? "", marginLeft: 1 });
|
|
198389
198880
|
outer.add(status);
|
|
198390
198881
|
body.add(outer);
|
|
198391
|
-
|
|
198882
|
+
blocks.push({ tab, index: i, name: d.name, outer, labelText, status, first, ...second !== undefined ? { second } : {} });
|
|
198392
198883
|
}
|
|
198393
198884
|
applyHighlight();
|
|
198394
198885
|
}
|
|
198395
|
-
|
|
198396
|
-
|
|
198397
|
-
|
|
198398
|
-
|
|
198399
|
-
|
|
198400
|
-
|
|
198886
|
+
function renderTab(tabId, body, ctx) {
|
|
198887
|
+
const id = tabId === "add" ? "add" : "connected";
|
|
198888
|
+
if (rendered && id !== tab)
|
|
198889
|
+
notice = nextNotice;
|
|
198890
|
+
nextNotice = undefined;
|
|
198891
|
+
rendered = true;
|
|
198892
|
+
if (pendingDisconnect !== undefined) {
|
|
198893
|
+
const armed = pendingDisconnect;
|
|
198894
|
+
pendingDisconnect = undefined;
|
|
198895
|
+
statuses.set(statusKey("connected", armed), "");
|
|
198896
|
+
}
|
|
198897
|
+
tab = id;
|
|
198898
|
+
bodyBox = body;
|
|
198899
|
+
bodyWidth = ctx.width;
|
|
198900
|
+
bodyHeight = ctx.height;
|
|
198901
|
+
if (opts.focusName !== undefined && !focusApplied.has(id)) {
|
|
198902
|
+
focusApplied.add(id);
|
|
198903
|
+
const at = rowsOf(id).findIndex((d) => d.name === opts.focusName);
|
|
198904
|
+
if (at >= 0)
|
|
198905
|
+
selected[id] = at;
|
|
198906
|
+
}
|
|
198907
|
+
paint();
|
|
198908
|
+
}
|
|
198909
|
+
const focusApplied = new Set;
|
|
198910
|
+
const rowCount = Math.max(connected.length, opts.all.length + 1);
|
|
198911
|
+
const surface = openTabbedSurface(otui, target, {
|
|
198912
|
+
id: "providers",
|
|
198913
|
+
title: "Providers",
|
|
198914
|
+
tabs: PROVIDERS_TABS,
|
|
198915
|
+
initialTab: tab,
|
|
198916
|
+
footerFor: (tabId) => PROVIDERS_FOOTERS[tabId === "add" ? "add" : "connected"],
|
|
198917
|
+
contentRows: Math.min(24, rowCount * 2 + 3),
|
|
198918
|
+
renderTab,
|
|
198919
|
+
ownsKeys: () => pendingDisconnect !== undefined,
|
|
198920
|
+
onArrowKeys: (key, direction) => {
|
|
198921
|
+
if (resolved)
|
|
198922
|
+
return false;
|
|
198923
|
+
if (tab === "connected")
|
|
198924
|
+
connectAction = stepConnectNavAction(connectAction, direction);
|
|
198925
|
+
else if (!addCustomRowSelected())
|
|
198926
|
+
addAction = stepAddNavAction(addAction, direction);
|
|
198401
198927
|
applyHighlight();
|
|
198402
198928
|
key.preventDefault();
|
|
198403
198929
|
key.stopPropagation();
|
|
198930
|
+
return true;
|
|
198931
|
+
},
|
|
198932
|
+
onEscape: () => finish({ kind: "close" })
|
|
198933
|
+
});
|
|
198934
|
+
const unsub = onKeypress4(r, (key) => {
|
|
198935
|
+
if (resolved)
|
|
198404
198936
|
return;
|
|
198405
|
-
|
|
198406
|
-
|
|
198937
|
+
if (key.name === "escape") {
|
|
198938
|
+
if (pendingDisconnect === undefined)
|
|
198939
|
+
return;
|
|
198407
198940
|
clearPending();
|
|
198408
|
-
selectedIndex = stepQueueNavIndex(selectedIndex, rows.length, "down");
|
|
198409
198941
|
applyHighlight();
|
|
198410
198942
|
key.preventDefault();
|
|
198411
198943
|
key.stopPropagation();
|
|
198412
198944
|
return;
|
|
198413
198945
|
}
|
|
198414
|
-
if (key.name === "
|
|
198946
|
+
if (key.name === "up" || key.name === "down") {
|
|
198415
198947
|
clearPending();
|
|
198416
|
-
|
|
198417
|
-
applyHighlight();
|
|
198948
|
+
moveSelection(key.name);
|
|
198418
198949
|
key.preventDefault();
|
|
198419
198950
|
key.stopPropagation();
|
|
198420
198951
|
return;
|
|
198421
198952
|
}
|
|
198422
|
-
if (key.name === "right") {
|
|
198953
|
+
if (key.name === "left" || key.name === "right") {
|
|
198423
198954
|
clearPending();
|
|
198424
|
-
|
|
198955
|
+
if (tab === "connected")
|
|
198956
|
+
connectAction = stepConnectNavAction(connectAction, key.name);
|
|
198957
|
+
else if (!addCustomRowSelected())
|
|
198958
|
+
addAction = stepAddNavAction(addAction, key.name);
|
|
198425
198959
|
applyHighlight();
|
|
198426
198960
|
key.preventDefault();
|
|
198427
198961
|
key.stopPropagation();
|
|
198428
198962
|
return;
|
|
198429
198963
|
}
|
|
198430
198964
|
if (key.name === "return" || key.name === "linefeed" || key.name === "kpenter") {
|
|
198431
|
-
const row =
|
|
198965
|
+
const row = rowsOf(tab)[selected[tab]];
|
|
198432
198966
|
if (row === undefined)
|
|
198433
198967
|
return;
|
|
198434
|
-
if (
|
|
198435
|
-
|
|
198436
|
-
|
|
198437
|
-
|
|
198438
|
-
|
|
198439
|
-
|
|
198968
|
+
if (tab === "connected") {
|
|
198969
|
+
if (connectAction === "label") {
|
|
198970
|
+
clearPending();
|
|
198971
|
+
finish({ kind: "pick", provider: row });
|
|
198972
|
+
} else if (connectAction === "test") {
|
|
198973
|
+
clearPending();
|
|
198974
|
+
runTest("connected", row.name);
|
|
198975
|
+
} else {
|
|
198976
|
+
armOrConfirmDisconnect(row.name);
|
|
198977
|
+
}
|
|
198978
|
+
} else if (addAction === "test" && row.name !== CUSTOM_PROVIDER_ADD_ID) {
|
|
198979
|
+
runTest("add", row.name);
|
|
198440
198980
|
} else {
|
|
198441
|
-
|
|
198981
|
+
runAddSelection(row);
|
|
198442
198982
|
}
|
|
198443
198983
|
key.preventDefault();
|
|
198444
198984
|
key.stopPropagation();
|
|
198985
|
+
return;
|
|
198986
|
+
}
|
|
198987
|
+
if (tab === "connected" && key.sequence === "a" && !key.ctrl && !key.meta) {
|
|
198988
|
+
clearPending();
|
|
198989
|
+
switchTab("add");
|
|
198990
|
+
key.preventDefault();
|
|
198991
|
+
key.stopPropagation();
|
|
198445
198992
|
}
|
|
198446
198993
|
});
|
|
198447
|
-
paint();
|
|
198448
198994
|
});
|
|
198449
198995
|
}
|
|
198996
|
+
async function runProviderConnectSequence(otui, target, prov, deps, setup, requireKey = false) {
|
|
198997
|
+
const lockDiscoveredHost = ["github-copilot", "openai", "openai-codex"].includes(prov.name);
|
|
198998
|
+
let selectedBaseUrl = !setup || prov.baseUrl === undefined || lockDiscoveredHost ? prov.baseUrl : await promptBaseUrlStep(otui, target, prov.label ?? prov.name, prov.baseUrl);
|
|
198999
|
+
if (setup && !lockDiscoveredHost && prov.baseUrl !== undefined && selectedBaseUrl === undefined) {
|
|
199000
|
+
return { kind: "back" };
|
|
199001
|
+
}
|
|
199002
|
+
if (setup && !lockDiscoveredHost && selectedBaseUrl !== undefined) {
|
|
199003
|
+
saveProviderBaseUrl(prov.name, selectedBaseUrl, deps.configDir);
|
|
199004
|
+
}
|
|
199005
|
+
let selectedProvider = selectedBaseUrl === undefined ? prov : { ...prov, baseUrl: selectedBaseUrl };
|
|
199006
|
+
const envKey = prov.envKey;
|
|
199007
|
+
const label = prov.label ?? prov.name;
|
|
199008
|
+
const collectCredential = async (notice) => {
|
|
199009
|
+
if (!setup || envKey === undefined && !catalogAllows(prov.name, "device-code")) {
|
|
199010
|
+
return "ok";
|
|
199011
|
+
}
|
|
199012
|
+
if (notice === undefined) {
|
|
199013
|
+
const existingKey = envKey === undefined ? undefined : (deps.env ?? process.env)[envKey] ?? loadShellConfig(deps.configDir).apiKeys?.[envKey];
|
|
199014
|
+
const hasOauth = prov.name !== "openai" && oauthAccessToken(prov.name, deps.configDir) !== undefined;
|
|
199015
|
+
if (existingKey !== undefined && existingKey.length > 0 || hasOauth) {
|
|
199016
|
+
return "ok";
|
|
199017
|
+
}
|
|
199018
|
+
}
|
|
199019
|
+
const offered = [];
|
|
199020
|
+
if (catalogAllows(prov.name, "device-code"))
|
|
199021
|
+
offered.push("device-code");
|
|
199022
|
+
if (catalogMethods(prov.name).includes("api-key"))
|
|
199023
|
+
offered.push("api-key");
|
|
199024
|
+
let method = offered.length === 1 ? offered[0] : undefined;
|
|
199025
|
+
if (offered.length > 1) {
|
|
199026
|
+
method = await pickAuthMethodStep(otui, target, label, offered);
|
|
199027
|
+
if (method === undefined) {
|
|
199028
|
+
return "back";
|
|
199029
|
+
}
|
|
199030
|
+
}
|
|
199031
|
+
if (method === "device-code") {
|
|
199032
|
+
if (!await runDeviceLoginInTui(otui, target, prov.name, deps.configDir, deps.fetch)) {
|
|
199033
|
+
return "back";
|
|
199034
|
+
}
|
|
199035
|
+
const saved = loadShellConfig(deps.configDir).baseUrls?.[prov.name];
|
|
199036
|
+
if (typeof saved === "string" && saved.length > 0) {
|
|
199037
|
+
selectedBaseUrl = saved;
|
|
199038
|
+
selectedProvider = { ...prov, baseUrl: saved };
|
|
199039
|
+
}
|
|
199040
|
+
return "ok";
|
|
199041
|
+
}
|
|
199042
|
+
if (envKey === undefined)
|
|
199043
|
+
return "back";
|
|
199044
|
+
const kr = await promptApiKeyStep(otui, target, {
|
|
199045
|
+
label: prov.name === "openai" ? "OpenAI" : label,
|
|
199046
|
+
envKey,
|
|
199047
|
+
...notice === undefined ? {} : { notice }
|
|
199048
|
+
});
|
|
199049
|
+
if (kr.kind === "back") {
|
|
199050
|
+
return "back";
|
|
199051
|
+
}
|
|
199052
|
+
if (kr.kind === "key") {
|
|
199053
|
+
process.env[envKey] = kr.value;
|
|
199054
|
+
noteSavedCredentialEnv([envKey]);
|
|
199055
|
+
saveApiKey(envKey, kr.value, deps.configDir);
|
|
199056
|
+
}
|
|
199057
|
+
if (kr.kind === "skip" && requireKey && (connectionProviderByName(prov.name, deps.configDir)?.requiresApiKey ?? true) && classifyProviderConnection(prov.name, deps.env ?? process.env, deps.configDir).kind === "no-credential") {
|
|
199058
|
+
return "no-key";
|
|
199059
|
+
}
|
|
199060
|
+
return "ok";
|
|
199061
|
+
};
|
|
199062
|
+
const firstCredential = await collectCredential();
|
|
199063
|
+
if (firstCredential === "back") {
|
|
199064
|
+
return { kind: "back" };
|
|
199065
|
+
}
|
|
199066
|
+
if (firstCredential === "no-key") {
|
|
199067
|
+
return { kind: "back", notice: NO_KEY_ENTERED_TEXT };
|
|
199068
|
+
}
|
|
199069
|
+
const modelDeps = {
|
|
199070
|
+
...deps.fetch === undefined ? {} : { fetch: deps.fetch },
|
|
199071
|
+
...deps.env === undefined ? {} : { env: deps.env },
|
|
199072
|
+
...deps.configDir === undefined ? {} : { configDir: deps.configDir }
|
|
199073
|
+
};
|
|
199074
|
+
let models = await modelsForPicker(selectedProvider, modelDeps);
|
|
199075
|
+
if (models.failure?.kind === "rejected") {
|
|
199076
|
+
const retried = await collectCredential(modelsFailureLine(label, models.failure));
|
|
199077
|
+
if (retried === "back") {
|
|
199078
|
+
return { kind: "back" };
|
|
199079
|
+
}
|
|
199080
|
+
if (retried === "no-key") {
|
|
199081
|
+
return { kind: "back", notice: NO_KEY_ENTERED_TEXT };
|
|
199082
|
+
}
|
|
199083
|
+
models = await modelsForPicker(selectedProvider, modelDeps);
|
|
199084
|
+
}
|
|
199085
|
+
if (setup && !lockDiscoveredHost && selectedProvider.baseUrl !== undefined && endpointMayBeAtFault(models.failure)) {
|
|
199086
|
+
const corrected = await promptBaseUrlStep(otui, target, label, selectedProvider.baseUrl, modelsFailureLine(label, models.failure));
|
|
199087
|
+
if (corrected === undefined) {
|
|
199088
|
+
return { kind: "back" };
|
|
199089
|
+
}
|
|
199090
|
+
if (corrected !== selectedProvider.baseUrl) {
|
|
199091
|
+
saveProviderBaseUrl(prov.name, corrected, deps.configDir);
|
|
199092
|
+
selectedBaseUrl = corrected;
|
|
199093
|
+
selectedProvider = { ...prov, baseUrl: corrected };
|
|
199094
|
+
models = await modelsForPicker(selectedProvider, modelDeps);
|
|
199095
|
+
}
|
|
199096
|
+
}
|
|
199097
|
+
return { kind: "ready", provider: selectedProvider, baseUrl: selectedBaseUrl, models, label };
|
|
199098
|
+
}
|
|
199099
|
+
function saveCustomProviderFromWizard(created, configDir) {
|
|
199100
|
+
saveCustomCompatProvider({
|
|
199101
|
+
name: created.name,
|
|
199102
|
+
label: created.name,
|
|
199103
|
+
baseUrl: created.baseUrl,
|
|
199104
|
+
...created.apiKey !== undefined ? { apiKey: created.apiKey } : {},
|
|
199105
|
+
models: created.models,
|
|
199106
|
+
requiresApiKey: false,
|
|
199107
|
+
...created.temperature !== undefined ? { temperature: created.temperature } : {},
|
|
199108
|
+
...created.maxOutputTokens !== undefined ? { maxOutputTokens: created.maxOutputTokens } : {},
|
|
199109
|
+
...created.timeoutMs !== undefined ? { timeoutMs: created.timeoutMs } : {}
|
|
199110
|
+
}, configDir);
|
|
199111
|
+
return { name: created.name, models: created.models, baseUrl: created.baseUrl, label: created.name, note: "custom" };
|
|
199112
|
+
}
|
|
199113
|
+
function upsertProvider(list, entry) {
|
|
199114
|
+
const at = list.findIndex((d) => d.name === entry.name);
|
|
199115
|
+
if (at >= 0)
|
|
199116
|
+
list[at] = entry;
|
|
199117
|
+
else
|
|
199118
|
+
list.push(entry);
|
|
199119
|
+
}
|
|
199120
|
+
async function pickModelForProvider(otui, target, ready, providerName) {
|
|
199121
|
+
const model = await pickModelInTui(otui, target, ready.models.models, modelPickerNotice(ready.label, ready.models), {
|
|
199122
|
+
escLabel: "back"
|
|
199123
|
+
});
|
|
199124
|
+
if (model === undefined)
|
|
199125
|
+
return;
|
|
199126
|
+
return ready.baseUrl === undefined ? { provider: providerName, model } : { provider: providerName, model, baseUrl: ready.baseUrl };
|
|
199127
|
+
}
|
|
199128
|
+
async function runProvidersModal(otui, target, detected, options) {
|
|
199129
|
+
const deps = {
|
|
199130
|
+
...options.fetch !== undefined ? { fetch: options.fetch } : {},
|
|
199131
|
+
...options.env !== undefined ? { env: options.env } : {},
|
|
199132
|
+
...options.configDir !== undefined ? { configDir: options.configDir } : {}
|
|
199133
|
+
};
|
|
199134
|
+
const all = [...detected];
|
|
199135
|
+
const connected = await filterConnectedDetectedProviders(detected, deps);
|
|
199136
|
+
let tab = connected.length > 0 ? "connected" : "add";
|
|
199137
|
+
let notice;
|
|
199138
|
+
let focusName;
|
|
199139
|
+
while (true) {
|
|
199140
|
+
const action = await pickProvidersTabbedStep(otui, target, {
|
|
199141
|
+
connected,
|
|
199142
|
+
all,
|
|
199143
|
+
initialTab: tab,
|
|
199144
|
+
...notice !== undefined ? { notice } : {},
|
|
199145
|
+
...focusName !== undefined ? { focusName } : {},
|
|
199146
|
+
...deps,
|
|
199147
|
+
...options.onDisconnected !== undefined ? { onDisconnected: options.onDisconnected } : {}
|
|
199148
|
+
});
|
|
199149
|
+
notice = undefined;
|
|
199150
|
+
focusName = undefined;
|
|
199151
|
+
if (action.kind === "close")
|
|
199152
|
+
return;
|
|
199153
|
+
try {
|
|
199154
|
+
if (action.kind === "pick") {
|
|
199155
|
+
const ready = await runProviderConnectSequence(otui, target, action.provider, deps, false);
|
|
199156
|
+
if (ready.kind === "ready") {
|
|
199157
|
+
const selection = await pickModelForProvider(otui, target, ready, action.provider.name);
|
|
199158
|
+
if (selection !== undefined)
|
|
199159
|
+
return selection;
|
|
199160
|
+
}
|
|
199161
|
+
tab = "connected";
|
|
199162
|
+
focusName = action.provider.name;
|
|
199163
|
+
continue;
|
|
199164
|
+
}
|
|
199165
|
+
if (action.kind === "connect") {
|
|
199166
|
+
const result = await runProviderConnectSequence(otui, target, action.provider, deps, true, true);
|
|
199167
|
+
focusName = action.provider.name;
|
|
199168
|
+
if (result.kind === "back") {
|
|
199169
|
+
tab = "add";
|
|
199170
|
+
if (result.notice !== undefined)
|
|
199171
|
+
notice = { tone: "error", text: `${action.provider.label ?? action.provider.name}: ${result.notice}` };
|
|
199172
|
+
continue;
|
|
199173
|
+
}
|
|
199174
|
+
if (result.models.failure === undefined && result.models.models.length > 0) {
|
|
199175
|
+
upsertProvider(connected, { ...result.provider, models: result.models.models });
|
|
199176
|
+
options.onConnected?.(action.provider.name);
|
|
199177
|
+
tab = "connected";
|
|
199178
|
+
notice = { tone: "ok", text: `${result.label} connected \u2014 ${result.models.models.length} model(s)` };
|
|
199179
|
+
} else {
|
|
199180
|
+
tab = "add";
|
|
199181
|
+
notice = {
|
|
199182
|
+
tone: "error",
|
|
199183
|
+
text: result.models.failure !== undefined ? modelsFailureLine(result.label, result.models.failure) : `${result.label}: no models returned`
|
|
199184
|
+
};
|
|
199185
|
+
}
|
|
199186
|
+
continue;
|
|
199187
|
+
}
|
|
199188
|
+
tab = "add";
|
|
199189
|
+
const created = await promptCustomProviderWizard(otui, target);
|
|
199190
|
+
if (created === undefined)
|
|
199191
|
+
continue;
|
|
199192
|
+
const entry = saveCustomProviderFromWizard(created, options.configDir);
|
|
199193
|
+
upsertProvider(all, entry);
|
|
199194
|
+
focusName = entry.name;
|
|
199195
|
+
const probed = await modelsForPicker(entry, deps);
|
|
199196
|
+
if (probed.failure === undefined && probed.models.length > 0) {
|
|
199197
|
+
upsertProvider(connected, { ...entry, models: probed.models });
|
|
199198
|
+
options.onConnected?.(entry.name);
|
|
199199
|
+
tab = "connected";
|
|
199200
|
+
notice = { tone: "ok", text: `${entry.label ?? entry.name} connected \u2014 ${probed.models.length} model(s)` };
|
|
199201
|
+
} else {
|
|
199202
|
+
notice = {
|
|
199203
|
+
tone: "error",
|
|
199204
|
+
text: probed.failure !== undefined ? modelsFailureLine(entry.label ?? entry.name, probed.failure) : `${entry.label ?? entry.name}: no models returned`
|
|
199205
|
+
};
|
|
199206
|
+
}
|
|
199207
|
+
} catch (error) {
|
|
199208
|
+
tab = action.kind === "pick" ? "connected" : "add";
|
|
199209
|
+
notice = { tone: "error", text: `could not finish (${error instanceof Error ? error.name : "unexpected error"}) \u2014 try again` };
|
|
199210
|
+
}
|
|
199211
|
+
}
|
|
199212
|
+
}
|
|
198450
199213
|
function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
|
|
199214
|
+
if (options.onlyConnected) {
|
|
199215
|
+
return runProvidersModal(otui, rOrChrome, detected, options);
|
|
199216
|
+
}
|
|
198451
199217
|
return new Promise((resolve) => {
|
|
198452
199218
|
if (detected.length === 0) {
|
|
198453
199219
|
resolve(undefined);
|
|
198454
199220
|
return;
|
|
198455
199221
|
}
|
|
198456
|
-
const candidatesPromise = options.onlyConnected ? filterConnectedDetectedProviders(detected, {
|
|
198457
|
-
...options.fetch !== undefined ? { fetch: options.fetch } : {},
|
|
198458
|
-
...options.env !== undefined ? { env: options.env } : {},
|
|
198459
|
-
...options.configDir !== undefined ? { configDir: options.configDir } : {}
|
|
198460
|
-
}) : Promise.resolve(detected);
|
|
198461
199222
|
(async () => {
|
|
198462
|
-
const
|
|
198463
|
-
|
|
198464
|
-
|
|
198465
|
-
|
|
198466
|
-
|
|
198467
|
-
|
|
199223
|
+
const allCandidates = [...detected, CUSTOM_ADD_ENTRY];
|
|
199224
|
+
const deps = {
|
|
199225
|
+
...options.fetch !== undefined ? { fetch: options.fetch } : {},
|
|
199226
|
+
...options.env !== undefined ? { env: options.env } : {},
|
|
199227
|
+
...options.configDir !== undefined ? { configDir: options.configDir } : {}
|
|
199228
|
+
};
|
|
198468
199229
|
while (true) {
|
|
198469
|
-
const prov =
|
|
198470
|
-
...options.fetch !== undefined ? { fetch: options.fetch } : {},
|
|
198471
|
-
...options.env !== undefined ? { env: options.env } : {},
|
|
198472
|
-
...options.configDir !== undefined ? { configDir: options.configDir } : {},
|
|
198473
|
-
...options.onDisconnected !== undefined ? { onDisconnected: options.onDisconnected } : {}
|
|
198474
|
-
}) : await pickProviderStep(otui, rOrChrome, allCandidates);
|
|
199230
|
+
const prov = await pickProviderStep(otui, rOrChrome, allCandidates);
|
|
198475
199231
|
if (prov === undefined) {
|
|
198476
199232
|
resolve(undefined);
|
|
198477
199233
|
return;
|
|
@@ -198479,123 +199235,17 @@ function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
|
|
|
198479
199235
|
if (prov.name === CUSTOM_PROVIDER_ADD_ID) {
|
|
198480
199236
|
const created = await promptCustomProviderWizard(otui, rOrChrome);
|
|
198481
199237
|
if (created !== undefined) {
|
|
198482
|
-
|
|
198483
|
-
name: created.name,
|
|
198484
|
-
label: created.name,
|
|
198485
|
-
baseUrl: created.baseUrl,
|
|
198486
|
-
...created.apiKey !== undefined ? { apiKey: created.apiKey } : {},
|
|
198487
|
-
models: created.models,
|
|
198488
|
-
requiresApiKey: false,
|
|
198489
|
-
...created.temperature !== undefined ? { temperature: created.temperature } : {},
|
|
198490
|
-
...created.maxOutputTokens !== undefined ? { maxOutputTokens: created.maxOutputTokens } : {},
|
|
198491
|
-
...created.timeoutMs !== undefined ? { timeoutMs: created.timeoutMs } : {}
|
|
198492
|
-
});
|
|
198493
|
-
allCandidates.push({
|
|
198494
|
-
name: created.name,
|
|
198495
|
-
models: created.models,
|
|
198496
|
-
baseUrl: created.baseUrl,
|
|
198497
|
-
label: created.name,
|
|
198498
|
-
note: "custom"
|
|
198499
|
-
});
|
|
199238
|
+
allCandidates.push(saveCustomProviderFromWizard(created, options.configDir));
|
|
198500
199239
|
}
|
|
198501
199240
|
continue;
|
|
198502
199241
|
}
|
|
198503
|
-
const
|
|
198504
|
-
|
|
198505
|
-
if (!options.onlyConnected && !lockDiscoveredHost && prov.baseUrl !== undefined && selectedBaseUrl === undefined) {
|
|
198506
|
-
continue;
|
|
198507
|
-
}
|
|
198508
|
-
if (!options.onlyConnected && !lockDiscoveredHost && selectedBaseUrl !== undefined) {
|
|
198509
|
-
saveProviderBaseUrl(prov.name, selectedBaseUrl, options.configDir);
|
|
198510
|
-
}
|
|
198511
|
-
let selectedProvider = selectedBaseUrl === undefined ? prov : { ...prov, baseUrl: selectedBaseUrl };
|
|
198512
|
-
const envKey = prov.envKey;
|
|
198513
|
-
const label = prov.label ?? prov.name;
|
|
198514
|
-
const collectCredential = async (notice) => {
|
|
198515
|
-
if (options.onlyConnected || envKey === undefined && !catalogAllows(prov.name, "device-code")) {
|
|
198516
|
-
return "ok";
|
|
198517
|
-
}
|
|
198518
|
-
if (notice === undefined) {
|
|
198519
|
-
const existingKey = envKey === undefined ? undefined : (options.env ?? process.env)[envKey] ?? loadShellConfig(options.configDir).apiKeys?.[envKey];
|
|
198520
|
-
const hasOauth = prov.name !== "openai" && oauthAccessToken(prov.name, options.configDir) !== undefined;
|
|
198521
|
-
if (existingKey !== undefined && existingKey.length > 0 || hasOauth) {
|
|
198522
|
-
return "ok";
|
|
198523
|
-
}
|
|
198524
|
-
}
|
|
198525
|
-
const offered = [];
|
|
198526
|
-
if (catalogAllows(prov.name, "device-code"))
|
|
198527
|
-
offered.push("device-code");
|
|
198528
|
-
if (catalogMethods(prov.name).includes("api-key"))
|
|
198529
|
-
offered.push("api-key");
|
|
198530
|
-
let method = offered.length === 1 ? offered[0] : undefined;
|
|
198531
|
-
if (offered.length > 1) {
|
|
198532
|
-
method = await pickAuthMethodStep(otui, rOrChrome, label, offered);
|
|
198533
|
-
if (method === undefined) {
|
|
198534
|
-
return "back";
|
|
198535
|
-
}
|
|
198536
|
-
}
|
|
198537
|
-
if (method === "device-code") {
|
|
198538
|
-
if (!await runDeviceLoginInTui(otui, rOrChrome, prov.name, options.configDir, options.fetch)) {
|
|
198539
|
-
return "back";
|
|
198540
|
-
}
|
|
198541
|
-
const saved = loadShellConfig(options.configDir).baseUrls?.[prov.name];
|
|
198542
|
-
if (typeof saved === "string" && saved.length > 0) {
|
|
198543
|
-
selectedBaseUrl = saved;
|
|
198544
|
-
selectedProvider = { ...prov, baseUrl: saved };
|
|
198545
|
-
}
|
|
198546
|
-
return "ok";
|
|
198547
|
-
}
|
|
198548
|
-
if (envKey === undefined)
|
|
198549
|
-
return "back";
|
|
198550
|
-
const kr = await promptApiKeyStep(otui, rOrChrome, {
|
|
198551
|
-
label: prov.name === "openai" ? "OpenAI" : label,
|
|
198552
|
-
envKey,
|
|
198553
|
-
...notice === undefined ? {} : { notice }
|
|
198554
|
-
});
|
|
198555
|
-
if (kr.kind === "back") {
|
|
198556
|
-
return "back";
|
|
198557
|
-
}
|
|
198558
|
-
if (kr.kind === "key") {
|
|
198559
|
-
process.env[envKey] = kr.value;
|
|
198560
|
-
noteSavedCredentialEnv([envKey]);
|
|
198561
|
-
saveApiKey(envKey, kr.value, options.configDir);
|
|
198562
|
-
}
|
|
198563
|
-
return "ok";
|
|
198564
|
-
};
|
|
198565
|
-
if (await collectCredential() === "back") {
|
|
199242
|
+
const ready = await runProviderConnectSequence(otui, rOrChrome, prov, deps, true);
|
|
199243
|
+
if (ready.kind === "back")
|
|
198566
199244
|
continue;
|
|
198567
|
-
|
|
198568
|
-
|
|
198569
|
-
...options.fetch === undefined ? {} : { fetch: options.fetch },
|
|
198570
|
-
...options.env === undefined ? {} : { env: options.env },
|
|
198571
|
-
...options.configDir === undefined ? {} : { configDir: options.configDir }
|
|
198572
|
-
};
|
|
198573
|
-
let models = await modelsForPicker(selectedProvider, modelDeps);
|
|
198574
|
-
if (models.failure?.kind === "rejected") {
|
|
198575
|
-
if (await collectCredential(modelsFailureLine(label, models.failure)) === "back") {
|
|
198576
|
-
continue;
|
|
198577
|
-
}
|
|
198578
|
-
models = await modelsForPicker(selectedProvider, modelDeps);
|
|
198579
|
-
}
|
|
198580
|
-
if (!options.onlyConnected && !lockDiscoveredHost && selectedProvider.baseUrl !== undefined && endpointMayBeAtFault(models.failure)) {
|
|
198581
|
-
const corrected = await promptBaseUrlStep(otui, rOrChrome, label, selectedProvider.baseUrl, modelsFailureLine(label, models.failure));
|
|
198582
|
-
if (corrected === undefined) {
|
|
198583
|
-
continue;
|
|
198584
|
-
}
|
|
198585
|
-
if (corrected !== selectedProvider.baseUrl) {
|
|
198586
|
-
saveProviderBaseUrl(prov.name, corrected, options.configDir);
|
|
198587
|
-
selectedBaseUrl = corrected;
|
|
198588
|
-
selectedProvider = { ...prov, baseUrl: corrected };
|
|
198589
|
-
models = await modelsForPicker(selectedProvider, modelDeps);
|
|
198590
|
-
}
|
|
198591
|
-
}
|
|
198592
|
-
const model = await pickModelInTui(otui, rOrChrome, models.models, modelPickerNotice(label, models), {
|
|
198593
|
-
escLabel: "back"
|
|
198594
|
-
});
|
|
198595
|
-
if (model === undefined) {
|
|
199245
|
+
const selection = await pickModelForProvider(otui, rOrChrome, ready, prov.name);
|
|
199246
|
+
if (selection === undefined)
|
|
198596
199247
|
continue;
|
|
198597
|
-
|
|
198598
|
-
resolve(selectedBaseUrl === undefined ? { provider: prov.name, model } : { provider: prov.name, model, baseUrl: selectedBaseUrl });
|
|
199248
|
+
resolve(selection);
|
|
198599
199249
|
return;
|
|
198600
199250
|
}
|
|
198601
199251
|
})();
|
|
@@ -198866,6 +199516,11 @@ async function launchTuiAgentShell(opts) {
|
|
|
198866
199516
|
let liveAutoReview;
|
|
198867
199517
|
let liveReviewsPanel;
|
|
198868
199518
|
let remoteBridge;
|
|
199519
|
+
let remoteDeviceLogin;
|
|
199520
|
+
const connectedProviders = createConnectedProvidersCache(async () => {
|
|
199521
|
+
const detected = opts.redetect !== undefined ? await opts.redetect() : opts.detected;
|
|
199522
|
+
return { detected, connected: await filterConnectedDetectedProviders(detected, {}) };
|
|
199523
|
+
});
|
|
198869
199524
|
let liveRemotePanel;
|
|
198870
199525
|
const operatorCards = new OperatorConfirmationLog;
|
|
198871
199526
|
const operatorCardLines = () => confirmationLines(operatorCards.entries(), Date.now());
|
|
@@ -198909,6 +199564,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
198909
199564
|
liveReviewsPanel?.dispose();
|
|
198910
199565
|
liveRemotePanel?.dispose();
|
|
198911
199566
|
liveChannelsPanel?.dispose();
|
|
199567
|
+
remoteDeviceLogin?.abort();
|
|
198912
199568
|
remoteBridge?.disable();
|
|
198913
199569
|
liveApprovals?.dispose();
|
|
198914
199570
|
liveIntake?.dispose();
|
|
@@ -203184,28 +203840,28 @@ ${formatThemeList(getThemeId())}`);
|
|
|
203184
203840
|
runDelegate(line);
|
|
203185
203841
|
return;
|
|
203186
203842
|
}
|
|
203187
|
-
if (command.name === "/connect"
|
|
203843
|
+
if (command.name === "/connect") {
|
|
203188
203844
|
detachSlashWork((async () => {
|
|
203189
203845
|
const detected = opts.redetect !== undefined ? await opts.redetect() : opts.detected;
|
|
203190
|
-
const ns = await chrome.withOverlay(() =>
|
|
203846
|
+
const ns = await chrome.withOverlay(() => selectProviderModelInTui(otui, chrome, detected, {
|
|
203191
203847
|
onlyConnected: true,
|
|
203192
203848
|
env: process.env,
|
|
203849
|
+
onConnected: () => connectedProviders.invalidate(),
|
|
203193
203850
|
onDisconnected: (name, sharedWith) => {
|
|
203851
|
+
connectedProviders.invalidate();
|
|
203194
203852
|
if (sharedWith.length > 0) {
|
|
203195
203853
|
io.onSystem?.(`\u25C7 ${name} disconnected \u2014 this also disconnected: ${sharedWith.join(", ")}.
|
|
203196
203854
|
`);
|
|
203197
203855
|
}
|
|
203198
203856
|
if (name === currentSel.provider) {
|
|
203199
|
-
io.onSystem?.(name === "openai-codex" ? `\u25C7 ${name} disconnected \u2014 it stays selected, but the next turn requires a new login.
|
|
203857
|
+
io.onSystem?.(name === "openai-codex" ? `\u25C7 ${name} disconnected \u2014 it stays selected, but the next turn requires a new login. Run /connect to log in again or to switch.
|
|
203200
203858
|
` : `\u25C7 ${name} disconnected \u2014 this session keeps its already-loaded credential until you /connect another provider or restart.
|
|
203201
203859
|
`);
|
|
203202
203860
|
}
|
|
203203
203861
|
}
|
|
203204
|
-
})
|
|
203862
|
+
}));
|
|
203205
203863
|
if (ns !== undefined) {
|
|
203206
203864
|
await switchTo(ns);
|
|
203207
|
-
} else if (command.name === "/connect") {
|
|
203208
|
-
chrome.showToast("No connected providers found. Run /provider to configure one first.");
|
|
203209
203865
|
}
|
|
203210
203866
|
input.focus();
|
|
203211
203867
|
})());
|
|
@@ -203215,7 +203871,8 @@ ${formatThemeList(getThemeId())}`);
|
|
|
203215
203871
|
return;
|
|
203216
203872
|
}
|
|
203217
203873
|
if (line.startsWith("/")) {
|
|
203218
|
-
|
|
203874
|
+
const removedHint = removedCommandHint(line);
|
|
203875
|
+
io.onSystem?.(describeUnavailableCommand(line, "agent") ?? `Unknown command: ${line}${removedHint === undefined ? "" : `. ${removedHint}`}
|
|
203219
203876
|
`);
|
|
203220
203877
|
const help = renderCommandHelp("agent");
|
|
203221
203878
|
addBlock({ kind: "output", fullText: help, summary: "Available commands", lineCount: help.trimEnd().split(`
|
|
@@ -203726,11 +204383,12 @@ Turn: ${chrome.isBusy() ? "running" : "idle"} \xB7 queue: ${turnLoop.queue.lengt
|
|
|
203726
204383
|
}
|
|
203727
204384
|
return await captureShellOutput(() => runLine(line, "operator", TG_SOURCE));
|
|
203728
204385
|
};
|
|
204386
|
+
const shellIsBusy = () => chrome.isBusy() || foregroundOperation.isActive || forceHandoff.isAwaitingSettlement || turnLoop.queue.length > 0 || leaseView()?.held() === true;
|
|
203729
204387
|
remoteBridge = new RemoteBridge({
|
|
203730
204388
|
host: {
|
|
203731
204389
|
sessionId: () => liveSession.summary.id,
|
|
203732
204390
|
project: () => sessionCwd,
|
|
203733
|
-
isBusy:
|
|
204391
|
+
isBusy: shellIsBusy,
|
|
203734
204392
|
turnRunning: () => chrome.isBusy() || foregroundOperation.isActive,
|
|
203735
204393
|
sessionState: () => ({ queued: turnLoop.queue.length, mode: formatModeInForce(modeForTurn(true)) }),
|
|
203736
204394
|
runLine: (text) => remoteQueue.runTelegramLine(text),
|
|
@@ -203746,6 +204404,7 @@ Turn: ${chrome.isBusy() ? "running" : "idle"} \xB7 queue: ${turnLoop.queue.lengt
|
|
|
203746
204404
|
} catch {}
|
|
203747
204405
|
},
|
|
203748
204406
|
recordOff: () => {
|
|
204407
|
+
remoteDeviceLogin?.abort();
|
|
203749
204408
|
if (!sessionLease.canPersist())
|
|
203750
204409
|
return;
|
|
203751
204410
|
try {
|
|
@@ -203769,14 +204428,25 @@ Turn: ${chrome.isBusy() ? "running" : "idle"} \xB7 queue: ${turnLoop.queue.lengt
|
|
|
203769
204428
|
return decision === "deferred" || decision === "not-a-command" ? BUSY_REASON : undefined;
|
|
203770
204429
|
},
|
|
203771
204430
|
listProviders: async () => {
|
|
203772
|
-
const
|
|
203773
|
-
const connected = await filterConnectedDetectedProviders(detected, {});
|
|
204431
|
+
const { connected } = await connectedProviders.get();
|
|
203774
204432
|
return connected.map((prov) => ({
|
|
203775
204433
|
id: prov.name,
|
|
203776
204434
|
label: prov.label ?? prov.name,
|
|
203777
204435
|
...prov.name === currentSel.provider ? { current: true } : {}
|
|
203778
204436
|
}));
|
|
203779
204437
|
},
|
|
204438
|
+
listAddableProviders: async () => {
|
|
204439
|
+
const { detected, connected } = await connectedProviders.get();
|
|
204440
|
+
const connectedNames = new Set(connected.map((prov) => prov.name));
|
|
204441
|
+
return detected.filter((prov) => prov.name !== "fake").map((prov) => ({
|
|
204442
|
+
id: prov.name,
|
|
204443
|
+
label: prov.label ?? prov.name,
|
|
204444
|
+
connected: connectedNames.has(prov.name),
|
|
204445
|
+
deviceLogin: catalogAllows(prov.name, "device-code")
|
|
204446
|
+
}));
|
|
204447
|
+
},
|
|
204448
|
+
testProvider: (providerId) => testProviderForRemote(providerId, { probe: (id) => probeProviderConnection(id, { requireCredential: true }) }),
|
|
204449
|
+
connectWithDeviceLogin: (providerId, notify) => (remoteDeviceLogin ??= createRemoteDeviceLogin({ isBusy: shellIsBusy, onConnected: () => connectedProviders.invalidate() })).connect(providerId, notify),
|
|
203780
204450
|
listModels: async (providerId) => {
|
|
203781
204451
|
const detected = opts.redetect !== undefined ? await opts.redetect() : opts.detected;
|
|
203782
204452
|
const name = providerId ?? currentSel.provider;
|
|
@@ -203876,7 +204546,7 @@ Turn: ${chrome.isBusy() ? "running" : "idle"} \xB7 queue: ${turnLoop.queue.lengt
|
|
|
203876
204546
|
`);
|
|
203877
204547
|
}
|
|
203878
204548
|
}
|
|
203879
|
-
var SESSION_PREVIEW_MESSAGE_COUNT = 200, SIDE_WORKER_DENIED_TOOL_NAMES, CUSTOM_PROVIDER_ADD_ID = "__add_custom_provider__", CUSTOM_ADD_ENTRY, REASONING_PREVIEW_THROTTLE_MS = 100, OPENAI_CACHED_INPUT_DISCOUNT = 0.5, ANTHROPIC_CACHE_READ_MULTIPLIER = 0.1, ANTHROPIC_CACHE_WRITE_MULTIPLIER = 1.25, MODE_PICKER_DESCRIPTIONS;
|
|
204549
|
+
var SESSION_PREVIEW_MESSAGE_COUNT = 200, SIDE_WORKER_DENIED_TOOL_NAMES, CUSTOM_PROVIDER_ADD_ID = "__add_custom_provider__", CUSTOM_ADD_ENTRY, REASONING_PREVIEW_THROTTLE_MS = 100, OPENAI_CACHED_INPUT_DISCOUNT = 0.5, ANTHROPIC_CACHE_READ_MULTIPLIER = 0.1, ANTHROPIC_CACHE_WRITE_MULTIPLIER = 1.25, PROVIDERS_TABS, PROVIDERS_FOOTERS, NO_KEY_ENTERED_TEXT = "no key entered", MODE_PICKER_DESCRIPTIONS;
|
|
203880
204550
|
var init_tui_shell = __esm(() => {
|
|
203881
204551
|
init_shell_startup_trace();
|
|
203882
204552
|
init_service5();
|
|
@@ -203925,6 +204595,7 @@ var init_tui_shell = __esm(() => {
|
|
|
203925
204595
|
init_workspace_inspector();
|
|
203926
204596
|
init_review_inspector();
|
|
203927
204597
|
init_routing_inspector();
|
|
204598
|
+
init_remote_provider_hooks();
|
|
203928
204599
|
init_model_profile();
|
|
203929
204600
|
init_task_cost();
|
|
203930
204601
|
init_ci_triage_inspector();
|
|
@@ -204057,6 +204728,23 @@ var init_tui_shell = __esm(() => {
|
|
|
204057
204728
|
label: "\uFF0B Add custom provider (OpenAI-compatible)",
|
|
204058
204729
|
note: "name \xB7 URL \xB7 key \xB7 models \u2192 saved to llm-providers.json"
|
|
204059
204730
|
};
|
|
204731
|
+
PROVIDERS_TABS = [
|
|
204732
|
+
{ id: "connected", label: "Connected" },
|
|
204733
|
+
{ id: "add", label: "Add provider" }
|
|
204734
|
+
];
|
|
204735
|
+
PROVIDERS_FOOTERS = {
|
|
204736
|
+
connected: [
|
|
204737
|
+
{ key: "\u2191/\u2193", label: "row" },
|
|
204738
|
+
{ key: "\u2190/\u2192", label: "action" },
|
|
204739
|
+
{ key: "Enter", label: "run" },
|
|
204740
|
+
{ key: "a", label: "add" }
|
|
204741
|
+
],
|
|
204742
|
+
add: [
|
|
204743
|
+
{ key: "\u2191/\u2193", label: "row" },
|
|
204744
|
+
{ key: "\u2190/\u2192", label: "Connect/Test" },
|
|
204745
|
+
{ key: "Enter", label: "run" }
|
|
204746
|
+
]
|
|
204747
|
+
};
|
|
204060
204748
|
MODE_PICKER_DESCRIPTIONS = {
|
|
204061
204749
|
ask: "Every mutating action asks first (today's default)",
|
|
204062
204750
|
trust: "Auto-approves safe actions; still asks for destructive/credential ones",
|
|
@@ -205494,28 +206182,6 @@ Kept the current session.
|
|
|
205494
206182
|
await selectAndApply({ onlyProvider: providerName });
|
|
205495
206183
|
continue;
|
|
205496
206184
|
}
|
|
205497
|
-
if (command === "/provider") {
|
|
205498
|
-
if (argument.length > 0) {
|
|
205499
|
-
if (argument === "openai-codex") {
|
|
205500
|
-
if (deps.selectProviderModel === undefined) {
|
|
205501
|
-
system("Interactive subscription selection is not available. Run `keryx auth login openai-codex` and select an authorized model.\n");
|
|
205502
|
-
} else {
|
|
205503
|
-
await selectAndApply({ onlyProvider: "openai-codex" });
|
|
205504
|
-
}
|
|
205505
|
-
continue;
|
|
205506
|
-
}
|
|
205507
|
-
providerName = argument;
|
|
205508
|
-
provider = makeActive();
|
|
205509
|
-
continue;
|
|
205510
|
-
}
|
|
205511
|
-
if (deps.selectProviderModel === undefined) {
|
|
205512
|
-
system(`Interactive provider selection is not available in this session.
|
|
205513
|
-
`);
|
|
205514
|
-
continue;
|
|
205515
|
-
}
|
|
205516
|
-
await selectAndApply({});
|
|
205517
|
-
continue;
|
|
205518
|
-
}
|
|
205519
206185
|
if (command === "/connect") {
|
|
205520
206186
|
if (deps.selectProviderModel === undefined) {
|
|
205521
206187
|
system(CONNECT_GUIDANCE);
|
|
@@ -205529,7 +206195,7 @@ Kept the current session.
|
|
|
205529
206195
|
system(wrongMode);
|
|
205530
206196
|
continue;
|
|
205531
206197
|
}
|
|
205532
|
-
system(`Unknown command: ${command}. Type /help for commands.
|
|
206198
|
+
system(`Unknown command: ${command}. ${removedCommandHint(command) ?? "Type /help for commands."}
|
|
205533
206199
|
`);
|
|
205534
206200
|
continue;
|
|
205535
206201
|
}
|
|
@@ -207097,7 +207763,7 @@ ${formatThemeList(getThemeId())}`);
|
|
|
207097
207763
|
const rows = buildSettingsRows(await loadSettingsSnapshot(sessionCwd, { permissionMode, plan: readOnly, reasoningOverride: reasoningSessionOverride }, configDir));
|
|
207098
207764
|
agentIo.onSystem?.(formatSettingsTable(rows, { available: new Set(READLINE_AGENT_COMMANDS) }));
|
|
207099
207765
|
} else {
|
|
207100
|
-
agentIo.onSystem?.(describeUnavailableCommand(command, "agent") ?? `Unknown command: ${command}. Type /help.
|
|
207766
|
+
agentIo.onSystem?.(describeUnavailableCommand(command, "agent") ?? `Unknown command: ${command}. ${removedCommandHint(command) ?? "Type /help."}
|
|
207101
207767
|
`);
|
|
207102
207768
|
}
|
|
207103
207769
|
printPromptWithBusNotice();
|
|
@@ -208063,7 +208729,7 @@ var init_shell = __esm(() => {
|
|
|
208063
208729
|
" export ANTHROPIC_API_KEY=<key>",
|
|
208064
208730
|
"",
|
|
208065
208731
|
"keryx reads ANTHROPIC_API_KEY from the environment only \u2014 it never stores,",
|
|
208066
|
-
"logs, or echoes the key. Then run /
|
|
208732
|
+
"logs, or echoes the key. Then run /connect to pick the anthropic provider.",
|
|
208067
208733
|
""
|
|
208068
208734
|
].join(`
|
|
208069
208735
|
`);
|