koneck 2.71.2 → 2.72.0
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/ink-chat.d.ts +26 -0
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +228 -9
- package/dist/ink-chat.js.map +1 -1
- package/package.json +1 -1
package/dist/ink-chat.d.ts
CHANGED
|
@@ -19,6 +19,32 @@ export declare function pickerLabel(text: string, width: number): string;
|
|
|
19
19
|
*/
|
|
20
20
|
export declare function wrapPreserving(text: string, width: number): string[];
|
|
21
21
|
/** Beyond this a paste is summarised in the transcript; it is still sent in full. */
|
|
22
|
+
/**
|
|
23
|
+
* The picker row that declares a provider rather than choosing one.
|
|
24
|
+
*
|
|
25
|
+
* A sentinel rather than a name, so it can never collide with a real provider — somebody is entitled
|
|
26
|
+
* to declare one called "add".
|
|
27
|
+
*/
|
|
28
|
+
export declare const ADD_PROVIDER = "\0add-provider";
|
|
29
|
+
/** Declaring a provider, a field at a time. */
|
|
30
|
+
export interface AddProviderPrompt {
|
|
31
|
+
step: 'name' | 'url' | 'model' | 'confirm';
|
|
32
|
+
name: string;
|
|
33
|
+
url: string;
|
|
34
|
+
model: string;
|
|
35
|
+
/** What is being typed into the current field. */
|
|
36
|
+
value: string;
|
|
37
|
+
/** Why the last entry was refused, shown above the field. */
|
|
38
|
+
error?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Checks one field and says what is wrong with it, or nothing.
|
|
42
|
+
*
|
|
43
|
+
* Separate from the component so the rules can be tested without mounting an Ink app — and they are
|
|
44
|
+
* worth testing: the name rule is what keeps a provider addressable from a command line, and the key
|
|
45
|
+
* variable rule is the one that stops a pasted credential being written into a JSON file.
|
|
46
|
+
*/
|
|
47
|
+
export declare function addProviderProblem(step: AddProviderPrompt['step'], value: string): string | null;
|
|
22
48
|
export declare const MAX_PASTE_ROWS = 80;
|
|
23
49
|
/**
|
|
24
50
|
* The pasted text as it should appear once submitted: what was copied, structure intact.
|
package/dist/ink-chat.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ink-chat.d.ts","sourceRoot":"","sources":["../src/ink-chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ink-chat.d.ts","sourceRoot":"","sources":["../src/ink-chat.tsx"],"names":[],"mappings":"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"}
|
package/dist/ink-chat.js
CHANGED
|
@@ -7,7 +7,7 @@ import { estimateCost, formatCost } from './pricing.js';
|
|
|
7
7
|
import { generateSessionId, saveSession, listSessions, loadSession, relativeAge, renameSession, forkSession, archiveSession, findSession, } from './session.js';
|
|
8
8
|
import { loadMemory } from './memory.js';
|
|
9
9
|
import { loadKoneckConfig, saveKoneckConfig, setConfigKey, configKeyDescriptions, CONFIG_FILE, CONFIG_SCHEMA, stepValue, displayValue } from './config-store.js';
|
|
10
|
-
import { PROVIDERS, resolveProvider, getApiKey, addUserProvider, userProvidersPath, servesLocally, servesOwnWeights, endpointForProvider } from './providers.js';
|
|
10
|
+
import { PROVIDERS, resolveProvider, getApiKey, addUserProvider, removeUserProvider, userProvidersPath, keyEnvFor, BUILT_INS as BUILT_IN_PROVIDERS, servesLocally, servesOwnWeights, endpointForProvider } from './providers.js';
|
|
11
11
|
import { normaliseEndpoint, withEndpoint, savedEndpoints, shortSource, resolveModelChoice, withModel } from './endpoints.js';
|
|
12
12
|
import { loadProjectConfig, loadGlobalConfig, saveGlobalConfig, withoutStoredApiKey } from './config.js';
|
|
13
13
|
import { rememberKey, forgetAllKeys } from './credentials.js';
|
|
@@ -200,6 +200,37 @@ export function wrapPreserving(text, width) {
|
|
|
200
200
|
return out;
|
|
201
201
|
}
|
|
202
202
|
/** Beyond this a paste is summarised in the transcript; it is still sent in full. */
|
|
203
|
+
/**
|
|
204
|
+
* The picker row that declares a provider rather than choosing one.
|
|
205
|
+
*
|
|
206
|
+
* A sentinel rather than a name, so it can never collide with a real provider — somebody is entitled
|
|
207
|
+
* to declare one called "add".
|
|
208
|
+
*/
|
|
209
|
+
export const ADD_PROVIDER = 'add-provider';
|
|
210
|
+
/**
|
|
211
|
+
* Checks one field and says what is wrong with it, or nothing.
|
|
212
|
+
*
|
|
213
|
+
* Separate from the component so the rules can be tested without mounting an Ink app — and they are
|
|
214
|
+
* worth testing: the name rule is what keeps a provider addressable from a command line, and the key
|
|
215
|
+
* variable rule is the one that stops a pasted credential being written into a JSON file.
|
|
216
|
+
*/
|
|
217
|
+
export function addProviderProblem(step, value) {
|
|
218
|
+
const said = value.trim();
|
|
219
|
+
if (step === 'name') {
|
|
220
|
+
if (said === '')
|
|
221
|
+
return 'A name is needed — it is how you refer to this provider afterwards.';
|
|
222
|
+
if (!/^[a-z0-9][a-z0-9._-]{0,40}$/.test(said.toLowerCase())) {
|
|
223
|
+
return 'Letters, digits, dot, dash and underscore only, starting with a letter or digit.';
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
if (step === 'url') {
|
|
228
|
+
if (!/^https?:\/\/.+/i.test(said))
|
|
229
|
+
return 'That is not an http or https URL.';
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
203
234
|
export const MAX_PASTE_ROWS = 80;
|
|
204
235
|
/**
|
|
205
236
|
* The pasted text as it should appear once submitted: what was copied, structure intact.
|
|
@@ -1776,6 +1807,16 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
1776
1807
|
// to the client as a runtime override — KONECK never writes it to disk, and it is never put
|
|
1777
1808
|
// into a transcript row, so it cannot end up in a saved session file.
|
|
1778
1809
|
const [keyPrompt, setKeyPrompt] = useState(null);
|
|
1810
|
+
/**
|
|
1811
|
+
* Declaring a provider, one field at a time.
|
|
1812
|
+
*
|
|
1813
|
+
* A step at a time rather than one long command, because the command form already existed and was
|
|
1814
|
+
* the problem: `/connect openrouter https://openrouter.ai/api/v1 anthropic/claude-sonnet-4.5` is
|
|
1815
|
+
* three things to know before you can type anything at all. Asked one at a time, each question
|
|
1816
|
+
* carries its own example and its own refusal, and the endpoint is checked before it is written
|
|
1817
|
+
* rather than failing on the first message afterwards.
|
|
1818
|
+
*/
|
|
1819
|
+
const [addProv, setAddProv] = useState(null);
|
|
1779
1820
|
const [picker, setPicker] = useState(null);
|
|
1780
1821
|
const [pickIndex, setPickIndex] = useState(0);
|
|
1781
1822
|
const [pickQuery, setPickQuery] = useState('');
|
|
@@ -2429,13 +2470,103 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
2429
2470
|
current: m.id === cfg.model,
|
|
2430
2471
|
}));
|
|
2431
2472
|
}
|
|
2473
|
+
/**
|
|
2474
|
+
* The providers to choose from, with declaring one as the first choice.
|
|
2475
|
+
*
|
|
2476
|
+
* Declaring has been possible from the terminal all along — `/connect <name> <url>` writes it to
|
|
2477
|
+
* disk and makes it a first-class choice — but nothing said so. The picker listed what existed and
|
|
2478
|
+
* stopped there, `/provider` with no argument printed the same list and a usage line about
|
|
2479
|
+
* switching, and the browser meanwhile had a visible "+ add a provider" button. So the answer to
|
|
2480
|
+
* "how do I add one here" was a command form you had to already know. It is an entry in the list
|
|
2481
|
+
* now, where somebody looking for it is looking.
|
|
2482
|
+
*/
|
|
2432
2483
|
function providerItems() {
|
|
2433
|
-
return
|
|
2434
|
-
value:
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2484
|
+
return [
|
|
2485
|
+
{ value: ADD_PROVIDER, label: '+ add a provider',
|
|
2486
|
+
desc: 'an OpenAI-compatible endpoint KONECK does not ship with' },
|
|
2487
|
+
...Object.values(PROVIDERS).map(p => ({
|
|
2488
|
+
value: p.name,
|
|
2489
|
+
label: p.name,
|
|
2490
|
+
desc: `${p.displayName} - ${p.defaultModel}` + (p.userDefined ? ' (yours)' : ''),
|
|
2491
|
+
current: p.name === cfg.provider,
|
|
2492
|
+
})),
|
|
2493
|
+
];
|
|
2494
|
+
}
|
|
2495
|
+
/** Opens the declare-a-provider prompt, optionally with fields already known. */
|
|
2496
|
+
function startAddProvider(seed = {}) {
|
|
2497
|
+
setAddProv({ step: 'name', name: '', url: '', model: '', value: '', ...seed });
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* Takes what was typed into the current field and moves on, or refuses it.
|
|
2501
|
+
*
|
|
2502
|
+
* The built-in check sits between the endpoint and the model rather than at the start, because it
|
|
2503
|
+
* is not about the name being invalid: repointing a name KONECK ships with is a legitimate thing
|
|
2504
|
+
* to want — a company proxy in front of a vendor is exactly this — and the only requirement is
|
|
2505
|
+
* that it cannot happen by accident. Same reasoning as the browser, and the same wording.
|
|
2506
|
+
*/
|
|
2507
|
+
async function advanceAddProvider() {
|
|
2508
|
+
const at = addProv;
|
|
2509
|
+
if (!at)
|
|
2510
|
+
return;
|
|
2511
|
+
const said = at.value.trim();
|
|
2512
|
+
const problem = addProviderProblem(at.step, said);
|
|
2513
|
+
if (problem) {
|
|
2514
|
+
setAddProv({ ...at, error: problem });
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
if (at.step === 'name') {
|
|
2518
|
+
const name = said.toLowerCase();
|
|
2519
|
+
setAddProv({ ...at, name, step: 'url', value: '', error: undefined });
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
if (at.step === 'url') {
|
|
2523
|
+
const url = said.replace(/\/+$/, '');
|
|
2524
|
+
setAddProv({ ...at, url, step: 'model', value: '', error: undefined });
|
|
2525
|
+
return;
|
|
2526
|
+
}
|
|
2527
|
+
if (at.step === 'model') {
|
|
2528
|
+
const next = { ...at, model: said, value: '', error: undefined };
|
|
2529
|
+
if (BUILT_IN_PROVIDERS[at.name]) {
|
|
2530
|
+
setAddProv({ ...next, step: 'confirm' });
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2533
|
+
await saveDeclaredProvider(next);
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
// confirm
|
|
2537
|
+
if (/^y(es)?$/i.test(said)) {
|
|
2538
|
+
await saveDeclaredProvider(at);
|
|
2539
|
+
return;
|
|
2540
|
+
}
|
|
2541
|
+
setAddProv(null);
|
|
2542
|
+
addSystem(`Left ${at.name} pointing at ${BUILT_IN_PROVIDERS[at.name]?.baseURL}.`);
|
|
2543
|
+
}
|
|
2544
|
+
/** Writes the declaration and switches to it, which is what somebody adding one meant to do. */
|
|
2545
|
+
async function saveDeclaredProvider(from) {
|
|
2546
|
+
const shadowed = BUILT_IN_PROVIDERS[from.name];
|
|
2547
|
+
let def;
|
|
2548
|
+
try {
|
|
2549
|
+
def = addUserProvider({
|
|
2550
|
+
name: from.name,
|
|
2551
|
+
displayName: from.name,
|
|
2552
|
+
baseURL: from.url,
|
|
2553
|
+
apiKeyEnv: keyEnvFor(from.name),
|
|
2554
|
+
defaultModel: from.model.trim() || 'default',
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2557
|
+
catch (e) {
|
|
2558
|
+
setAddProv({ ...from, error: e instanceof Error ? e.message : String(e) });
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2561
|
+
setAddProv(null);
|
|
2562
|
+
addSystem((shadowed
|
|
2563
|
+
? `⚠️ Repointed **${def.name}** → ${def.baseURL} (was ${shadowed.baseURL})\n`
|
|
2564
|
+
+ 'Everything that uses this name now goes there, in the browser as well as here.\n'
|
|
2565
|
+
: `Declared **${def.name}** → ${def.baseURL}\n`)
|
|
2566
|
+
+ `Saved to ${userProvidersPath()}. Its key is read from ${def.apiKeyEnv} `
|
|
2567
|
+
+ '(or KONECK_API_KEY).\n'
|
|
2568
|
+
+ `Remove it again with \`/provider remove ${def.name}\`.`);
|
|
2569
|
+
await choosePick({ value: def.name, label: def.name, desc: '' }, 'provider');
|
|
2439
2570
|
}
|
|
2440
2571
|
/**
|
|
2441
2572
|
* Offers workspace paths for an `@` reference.
|
|
@@ -2575,6 +2706,10 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
2575
2706
|
addSystem(`Model → ${item.value} · remembered for ${cfg.provider} (session reset)`);
|
|
2576
2707
|
return;
|
|
2577
2708
|
}
|
|
2709
|
+
if (kind === 'provider' && item.value === ADD_PROVIDER) {
|
|
2710
|
+
startAddProvider();
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2578
2713
|
if (kind === 'provider') {
|
|
2579
2714
|
const def = PROVIDERS[item.value];
|
|
2580
2715
|
// The endpoint saved for the provider being switched to, not its built-in address. Passing
|
|
@@ -2975,8 +3110,64 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
2975
3110
|
return;
|
|
2976
3111
|
}
|
|
2977
3112
|
case '/provider': {
|
|
3113
|
+
const [verb, ...restArgs] = arg.trim().split(/\s+/).filter(Boolean);
|
|
3114
|
+
/*
|
|
3115
|
+
* add and remove, named as what they are.
|
|
3116
|
+
*
|
|
3117
|
+
* Declaring was only ever reachable as `/connect <name> <url>`, which is a thing you have to
|
|
3118
|
+
* already know — and somebody looking for how to add a provider looks at `/provider`. The
|
|
3119
|
+
* old form still works; this is where it can be found.
|
|
3120
|
+
*/
|
|
3121
|
+
if (verb?.toLowerCase() === 'add') {
|
|
3122
|
+
const [name, url, ...model] = restArgs;
|
|
3123
|
+
// With arguments it is one step; without, it asks. Both end in the same place.
|
|
3124
|
+
startAddProvider({
|
|
3125
|
+
...(name ? { name: name.toLowerCase(), step: 'url' } : {}),
|
|
3126
|
+
...(name && url ? { url: url.replace(/\/+$/, ''), step: 'model' } : {}),
|
|
3127
|
+
...(model.length ? { value: model.join(' ') } : {}),
|
|
3128
|
+
});
|
|
3129
|
+
return;
|
|
3130
|
+
}
|
|
3131
|
+
if (verb?.toLowerCase() === 'remove' || verb?.toLowerCase() === 'forget') {
|
|
3132
|
+
const target = (restArgs[0] ?? '').toLowerCase();
|
|
3133
|
+
if (!target) {
|
|
3134
|
+
const mine = Object.values(PROVIDERS).filter(p => p.userDefined).map(p => p.name);
|
|
3135
|
+
addSystem(mine.length
|
|
3136
|
+
? `Usage: /provider remove <name>\nYours: ${mine.join(', ')}`
|
|
3137
|
+
: 'You have not declared any providers, so there is nothing to remove.');
|
|
3138
|
+
return;
|
|
3139
|
+
}
|
|
3140
|
+
const wasShadowing = BUILT_IN_PROVIDERS[target] !== undefined;
|
|
3141
|
+
if (!removeUserProvider(target)) {
|
|
3142
|
+
addSystem(PROVIDERS[target]
|
|
3143
|
+
? `"${target}" ships with KONECK — it was not declared by you, so there is nothing to remove.`
|
|
3144
|
+
: `No declared provider called "${target}".`);
|
|
3145
|
+
return;
|
|
3146
|
+
}
|
|
3147
|
+
addSystem(wasShadowing
|
|
3148
|
+
? `Removed your "${target}". The built-in one is back, pointing at ${BUILT_IN_PROVIDERS[target].baseURL}.`
|
|
3149
|
+
: `Removed "${target}".`);
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
2978
3152
|
if (!arg) {
|
|
2979
|
-
|
|
3153
|
+
/*
|
|
3154
|
+
* Says where things stand, then opens the picker.
|
|
3155
|
+
*
|
|
3156
|
+
* The list scrolls into the transcript where it can be read back; the picker is for doing
|
|
3157
|
+
* something about it, and carries "+ add a provider" as its first row. An earlier version
|
|
3158
|
+
* of this printed the summary and stopped, and the summary said the picker would open —
|
|
3159
|
+
* which it did not, because nothing opened it. A help line that describes behaviour the
|
|
3160
|
+
* program does not have is worse than no help line.
|
|
3161
|
+
*/
|
|
3162
|
+
const mine = Object.values(PROVIDERS).filter(p => p.userDefined).map(p => p.name);
|
|
3163
|
+
addSystem(`Current provider: ${cfg.provider}\n`
|
|
3164
|
+
+ `Available: ${Object.keys(PROVIDERS).join(', ')}\n`
|
|
3165
|
+
+ (mine.length ? `Declared by you: ${mine.join(', ')}\n` : '')
|
|
3166
|
+
+ '\nSwitch: /provider <name>, or pick one below\n'
|
|
3167
|
+
+ 'Add: /provider add — asks for a name, an endpoint and a default model\n'
|
|
3168
|
+
+ ' /provider add <name> <url> [model] if you have them to hand\n'
|
|
3169
|
+
+ 'Remove: /provider remove <name>');
|
|
3170
|
+
openPicker('provider', providerItems());
|
|
2980
3171
|
return;
|
|
2981
3172
|
}
|
|
2982
3173
|
const name = arg.trim().toLowerCase();
|
|
@@ -2985,7 +3176,7 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
2985
3176
|
// Switching to a name that does not resolve used to reset the session anyway, leaving it
|
|
2986
3177
|
// pointed at nothing and failing on the next message instead of on the command.
|
|
2987
3178
|
addSystem(`Unknown provider "${arg}". Known: ${Object.keys(PROVIDERS).join(', ')}\n` +
|
|
2988
|
-
`Declare
|
|
3179
|
+
`Declare it: /provider add ${name} https://endpoint/v1`);
|
|
2989
3180
|
return;
|
|
2990
3181
|
}
|
|
2991
3182
|
// Keep the typed command and picker on one implementation path. The picker clears a
|
|
@@ -4084,6 +4275,26 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
4084
4275
|
answer(typed);
|
|
4085
4276
|
return;
|
|
4086
4277
|
}
|
|
4278
|
+
if (addProv) {
|
|
4279
|
+
if (key.escape) {
|
|
4280
|
+
setAddProv(null);
|
|
4281
|
+
addSystem('Cancelled — nothing was declared.');
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
if (key.return) {
|
|
4285
|
+
void advanceAddProvider();
|
|
4286
|
+
return;
|
|
4287
|
+
}
|
|
4288
|
+
if (key.backspace || key.delete) {
|
|
4289
|
+
setAddProv(p => (p ? { ...p, value: p.value.slice(0, -1) } : p));
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4292
|
+
// A pasted endpoint arrives as one chunk, so it is taken whole rather than a letter at a time.
|
|
4293
|
+
if (!key.ctrl && !key.meta && input) {
|
|
4294
|
+
setAddProv(p => (p ? { ...p, value: p.value + input } : p));
|
|
4295
|
+
}
|
|
4296
|
+
return;
|
|
4297
|
+
}
|
|
4087
4298
|
if (keyPrompt) {
|
|
4088
4299
|
if (key.escape) {
|
|
4089
4300
|
setKeyPrompt(null);
|
|
@@ -5138,6 +5349,14 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
5138
5349
|
return (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { backgroundColor: selected ? CYAN : undefined, color: selected ? '#10222A' : INK, bold: selected, children: fitCells(`${selected ? G.caret + ' ' : ' '}${label}` +
|
|
5139
5350
|
`${item.current ? '(current) ' : ''}${header ? `${header} · ` : ''}${item.desc}`, width) }) }, item.value + absolute));
|
|
5140
5351
|
}), list.length > pickerRows && (_jsxs(Text, { color: DIM, children: ["\u2191\u2193 navigate \u00B7 pgup/pgdn jump \u00B7 enter select \u00B7 showing ", Math.max(0, start) + 1, "\u2013", Math.max(0, start) + shown.length, " of ", list.length] }))] }));
|
|
5352
|
+
})(), addProv && (() => {
|
|
5353
|
+
const shadowed = BUILT_IN_PROVIDERS[addProv.name];
|
|
5354
|
+
const ask = addProv.step === 'name' ? { label: 'name', hint: 'short and lowercase, e.g. openrouter' }
|
|
5355
|
+
: addProv.step === 'url' ? { label: 'endpoint', hint: 'OpenAI-compatible, e.g. https://openrouter.ai/api/v1' }
|
|
5356
|
+
: addProv.step === 'model' ? { label: 'default model', hint: 'optional — enter to skip' }
|
|
5357
|
+
: { label: 'repoint ' + addProv.name + '? (y/n)',
|
|
5358
|
+
hint: `it ships with KONECK, pointing at ${shadowed?.baseURL}` };
|
|
5359
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: CYAN, paddingX: 1, marginTop: 1, width: barWidth, children: [_jsx(Text, { color: CYAN, bold: true, children: "Declare a provider" }), _jsxs(Text, { color: MUTED, children: [ask.hint, " \u00B7 esc to cancel"] }), addProv.name !== '' && (_jsxs(Text, { color: DIM, children: [addProv.name, addProv.url ? ' ' + addProv.url : ''] })), addProv.error && _jsx(Text, { color: CRIMSON, children: addProv.error }), _jsxs(Box, { marginTop: 1, children: [_jsxs(Text, { color: MUTED, children: [ask.label, ": "] }), _jsx(Text, { color: INK, children: addProv.value }), _jsx(Text, { color: CYAN, children: "\u2588" })] })] }));
|
|
5141
5360
|
})(), keyPrompt && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AMBER, paddingX: 1, marginTop: 1, width: barWidth, children: [_jsxs(Text, { color: AMBER, bold: true, children: ["API key for ", keyPrompt.provider] }), _jsx(Text, { color: MUTED, children: "Paste it and press enter. Held in memory for this session only; esc to cancel." }), _jsxs(Box, { marginTop: 1, children: [_jsxs(Text, { color: MUTED, children: [keyPrompt.env, ": "] }), _jsx(Text, { color: INK, children: '*'.repeat(Math.min(keyPrompt.value.length, 48)) }), _jsx(Text, { color: CYAN, children: "\u2588" })] })] })), ask && (() => {
|
|
5142
5361
|
let detail = '';
|
|
5143
5362
|
try {
|