koneck 2.72.0 → 2.73.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/clipboard.d.ts +46 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +81 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +73 -6
- package/dist/ink-chat.js.map +1 -1
- package/dist/web/ui-client.d.ts.map +1 -1
- package/dist/web/ui-client.js +44 -10
- package/dist/web/ui-client.js.map +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +16 -1
- package/dist/web/ui.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the system clipboard, for when the terminal's own paste will not do.
|
|
3
|
+
*
|
|
4
|
+
* Pasting into a terminal is not one mechanism, it is several: bracketed paste if the terminal
|
|
5
|
+
* offers it, a plain burst of characters if not, a middle-click that KONECK may have taken over for
|
|
6
|
+
* its own mouse handling, and on Windows a Ctrl+V that some hosts deliver as a control code rather
|
|
7
|
+
* than as text. Somebody trying to put an API key into a prompt does not care which of those their
|
|
8
|
+
* terminal does, and telling them to type a sixty-character secret by hand is not an answer.
|
|
9
|
+
*
|
|
10
|
+
* So the prompt can go and fetch it. Every desktop already has a command for this, and KONECK
|
|
11
|
+
* already shells out to their write-side counterparts for /copy; this is the same list read the
|
|
12
|
+
* other way round.
|
|
13
|
+
*/
|
|
14
|
+
export interface ClipboardRead {
|
|
15
|
+
ok: boolean;
|
|
16
|
+
text: string;
|
|
17
|
+
/** Which command answered, so it can be named. */
|
|
18
|
+
by?: string;
|
|
19
|
+
/** Why nothing came back, when nothing did. */
|
|
20
|
+
reason?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* What is on the clipboard, or why it could not be read.
|
|
24
|
+
*
|
|
25
|
+
* Each reader gets a short deadline: a clipboard tool that hangs — xclip waiting on an X server
|
|
26
|
+
* that is not there is the usual way — must not hold a keystroke open. Failure is ordinary and is
|
|
27
|
+
* reported rather than thrown: a machine reached over SSH has no clipboard, and that is a fact
|
|
28
|
+
* about the machine rather than an error in the program.
|
|
29
|
+
*/
|
|
30
|
+
export declare function readClipboard(readers?: ReadonlyArray<{
|
|
31
|
+
file: string;
|
|
32
|
+
args: string[];
|
|
33
|
+
}>, timeoutMs?: number): Promise<ClipboardRead>;
|
|
34
|
+
/**
|
|
35
|
+
* A pasted secret, made safe to use as one.
|
|
36
|
+
*
|
|
37
|
+
* Three things go, each for its own reason. Surrounding whitespace, because a key copied out of a
|
|
38
|
+
* file or a password manager carries a trailing newline that is not part of it. Bracketed paste
|
|
39
|
+
* markers, because a terminal that announces a paste mid-chunk can leave the literal text "[200~"
|
|
40
|
+
* in the field, and a key with that in front of it fails at the provider with an authentication
|
|
41
|
+
* error that says nothing about where it came from. And control characters, which are invalid in an
|
|
42
|
+
* HTTP header — a key carrying one is rejected by the transport before the provider ever sees it,
|
|
43
|
+
* which is the least informative way for this to go wrong.
|
|
44
|
+
*/
|
|
45
|
+
export declare function cleanPastedSecret(text: string): string;
|
|
46
|
+
//# sourceMappingURL=clipboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../src/clipboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAW,EAClE,SAAS,SAAQ,GAChB,OAAO,CAAC,aAAa,CAAC,CAmBxB;AAeD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKtD"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the system clipboard, for when the terminal's own paste will not do.
|
|
3
|
+
*
|
|
4
|
+
* Pasting into a terminal is not one mechanism, it is several: bracketed paste if the terminal
|
|
5
|
+
* offers it, a plain burst of characters if not, a middle-click that KONECK may have taken over for
|
|
6
|
+
* its own mouse handling, and on Windows a Ctrl+V that some hosts deliver as a control code rather
|
|
7
|
+
* than as text. Somebody trying to put an API key into a prompt does not care which of those their
|
|
8
|
+
* terminal does, and telling them to type a sixty-character secret by hand is not an answer.
|
|
9
|
+
*
|
|
10
|
+
* So the prompt can go and fetch it. Every desktop already has a command for this, and KONECK
|
|
11
|
+
* already shells out to their write-side counterparts for /copy; this is the same list read the
|
|
12
|
+
* other way round.
|
|
13
|
+
*/
|
|
14
|
+
/** The readers worth trying, in order. The first that exists and answers wins. */
|
|
15
|
+
const READERS = [
|
|
16
|
+
// Wayland, then X11's two common tools, then macOS.
|
|
17
|
+
{ file: 'wl-paste', args: ['--no-newline'] },
|
|
18
|
+
{ file: 'xclip', args: ['-selection', 'clipboard', '-o'] },
|
|
19
|
+
{ file: 'xsel', args: ['--clipboard', '--output'] },
|
|
20
|
+
{ file: 'pbpaste', args: [] },
|
|
21
|
+
// Windows, and WSL — where the Windows clipboard is the one holding what was copied.
|
|
22
|
+
{ file: 'powershell.exe', args: ['-NoProfile', '-Command', 'Get-Clipboard'] },
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* What is on the clipboard, or why it could not be read.
|
|
26
|
+
*
|
|
27
|
+
* Each reader gets a short deadline: a clipboard tool that hangs — xclip waiting on an X server
|
|
28
|
+
* that is not there is the usual way — must not hold a keystroke open. Failure is ordinary and is
|
|
29
|
+
* reported rather than thrown: a machine reached over SSH has no clipboard, and that is a fact
|
|
30
|
+
* about the machine rather than an error in the program.
|
|
31
|
+
*/
|
|
32
|
+
export async function readClipboard(readers = READERS, timeoutMs = 1_500) {
|
|
33
|
+
const { execa } = await import('execa');
|
|
34
|
+
for (const reader of readers) {
|
|
35
|
+
try {
|
|
36
|
+
const done = await execa(reader.file, [...reader.args], {
|
|
37
|
+
reject: false, timeout: timeoutMs, stripFinalNewline: true,
|
|
38
|
+
});
|
|
39
|
+
if (done.exitCode !== 0)
|
|
40
|
+
continue;
|
|
41
|
+
// An empty clipboard is a successful read of nothing, and worth reporting as its own thing:
|
|
42
|
+
// it means the copy did not happen, not that KONECK cannot see the clipboard.
|
|
43
|
+
return { ok: true, text: String(done.stdout ?? ''), by: reader.file };
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// ENOENT for a tool that is not installed, which is the ordinary case for all but one of
|
|
47
|
+
// these on any given machine.
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { ok: false, text: '', reason: 'No clipboard tool answered. On Linux that is usually the wl-clipboard package (Wayland) or '
|
|
51
|
+
+ 'xclip (X11); over SSH there is no clipboard to read at all.' };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Bracketed-paste markers, as a terminal writes them and as Ink sometimes forwards them.
|
|
55
|
+
*
|
|
56
|
+
* Built from escapes rather than written literally, so this file stays free of raw control
|
|
57
|
+
* characters — a source file carrying one reads as binary to some tools and is invisible to a
|
|
58
|
+
* reviewer in all of them. The escape is optional in the pattern because Ink strips it from the
|
|
59
|
+
* opening marker before handing input over, so the literal that reaches a field is "[200~".
|
|
60
|
+
*/
|
|
61
|
+
const PASTE_MARKERS = new RegExp('\\u001b?\\[2(?:00|01)~', 'g');
|
|
62
|
+
/** Anything below space, plus DEL. No credential holds one, and an HTTP header cannot carry one. */
|
|
63
|
+
const CONTROL_CHARS = new RegExp('[\\u0000-\\u001f\\u007f]', 'g');
|
|
64
|
+
/**
|
|
65
|
+
* A pasted secret, made safe to use as one.
|
|
66
|
+
*
|
|
67
|
+
* Three things go, each for its own reason. Surrounding whitespace, because a key copied out of a
|
|
68
|
+
* file or a password manager carries a trailing newline that is not part of it. Bracketed paste
|
|
69
|
+
* markers, because a terminal that announces a paste mid-chunk can leave the literal text "[200~"
|
|
70
|
+
* in the field, and a key with that in front of it fails at the provider with an authentication
|
|
71
|
+
* error that says nothing about where it came from. And control characters, which are invalid in an
|
|
72
|
+
* HTTP header — a key carrying one is rejected by the transport before the provider ever sees it,
|
|
73
|
+
* which is the least informative way for this to go wrong.
|
|
74
|
+
*/
|
|
75
|
+
export function cleanPastedSecret(text) {
|
|
76
|
+
return String(text ?? '')
|
|
77
|
+
.replace(PASTE_MARKERS, '')
|
|
78
|
+
.replace(CONTROL_CHARS, '')
|
|
79
|
+
.trim();
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=clipboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../src/clipboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,kFAAkF;AAClF,MAAM,OAAO,GAAoD;IAC/D,oDAAoD;IACpD,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,cAAc,CAAC,EAAE;IAC5C,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE;IAC1D,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,aAAa,EAAE,UAAU,CAAC,EAAE;IACnD,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE;IAC7B,qFAAqF;IACrF,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,YAAY,EAAE,UAAU,EAAE,eAAe,CAAC,EAAE;CAC9E,CAAC;AAWF;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAA2D,OAAO,EAClE,SAAS,GAAG,KAAK;IAEjB,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE;gBACtD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,IAAI;aAC3D,CAAC,CAAC;YACH,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC;gBAAE,SAAS;YAClC,4FAA4F;YAC5F,8EAA8E;YAC9E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;QACxE,CAAC;QAAC,MAAM,CAAC;YACP,yFAAyF;YACzF,8BAA8B;QAChC,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAClC,6FAA6F;cAC3F,6DAA6D,EAAE,CAAC;AACtE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG,IAAI,MAAM,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;AAEhE,oGAAoG;AACpG,MAAM,aAAa,GAAG,IAAI,MAAM,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAAC;AAElE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;SACtB,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;SAC1B,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;SAC1B,IAAI,EAAE,CAAC;AACZ,CAAC"}
|
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
|
@@ -11,6 +11,7 @@ import { PROVIDERS, resolveProvider, getApiKey, addUserProvider, removeUserProvi
|
|
|
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';
|
|
14
|
+
import { readClipboard, cleanPastedSecret } from './clipboard.js';
|
|
14
15
|
import { paceFrom } from './pace.js';
|
|
15
16
|
import { policyFrom, FAILOVER_OFF } from './failover.js';
|
|
16
17
|
import { listCheckpoints, revertCheckpoint, previewCheckpoint, snapshotTree, filesTouchedBy, createWorkspaceCheckpoint, checkpointScope } from './checkpoint.js';
|
|
@@ -2752,6 +2753,45 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
2752
2753
|
}
|
|
2753
2754
|
}
|
|
2754
2755
|
/** Applies a key typed at the prompt. Takes effect immediately — no restart. */
|
|
2756
|
+
/**
|
|
2757
|
+
* Puts whatever is on the clipboard into the key field.
|
|
2758
|
+
*
|
|
2759
|
+
* The result is reported in the panel rather than as a system message, because the panel is where
|
|
2760
|
+
* the person is looking and because the alternative — silence — is exactly what made pasting feel
|
|
2761
|
+
* broken in the first place. Nothing about the key itself is shown, only how many characters
|
|
2762
|
+
* arrived, which is enough to tell a successful paste from an empty clipboard.
|
|
2763
|
+
*/
|
|
2764
|
+
async function pasteFromClipboard() {
|
|
2765
|
+
setKeyPrompt(p => (p ? { ...p, note: 'reading the clipboard…' } : p));
|
|
2766
|
+
const got = await readClipboard();
|
|
2767
|
+
if (!got.ok) {
|
|
2768
|
+
setKeyPrompt(p => (p ? { ...p, note: got.reason ?? 'the clipboard could not be read' } : p));
|
|
2769
|
+
return;
|
|
2770
|
+
}
|
|
2771
|
+
const clean = cleanPastedSecret(got.text);
|
|
2772
|
+
if (clean === '') {
|
|
2773
|
+
setKeyPrompt(p => (p ? { ...p, note: 'the clipboard is empty — copy the key first' } : p));
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
setKeyPrompt(p => (p ? {
|
|
2777
|
+
...p, value: p.value + clean,
|
|
2778
|
+
note: `${clean.length} characters from the clipboard · enter to use them`,
|
|
2779
|
+
} : p));
|
|
2780
|
+
}
|
|
2781
|
+
/** The same, for the field being filled in while declaring a provider. */
|
|
2782
|
+
async function pasteIntoAddProvider() {
|
|
2783
|
+
const got = await readClipboard();
|
|
2784
|
+
if (!got.ok) {
|
|
2785
|
+
setAddProv(p => (p ? { ...p, error: got.reason ?? 'the clipboard could not be read' } : p));
|
|
2786
|
+
return;
|
|
2787
|
+
}
|
|
2788
|
+
const clean = cleanPastedSecret(got.text);
|
|
2789
|
+
if (clean === '') {
|
|
2790
|
+
setAddProv(p => (p ? { ...p, error: 'the clipboard is empty' } : p));
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
setAddProv(p => (p ? { ...p, value: p.value + clean, error: undefined } : p));
|
|
2794
|
+
}
|
|
2755
2795
|
async function applyTypedKey() {
|
|
2756
2796
|
if (!keyPrompt)
|
|
2757
2797
|
return;
|
|
@@ -4289,9 +4329,18 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
4289
4329
|
setAddProv(p => (p ? { ...p, value: p.value.slice(0, -1) } : p));
|
|
4290
4330
|
return;
|
|
4291
4331
|
}
|
|
4332
|
+
// The same clipboard escape hatch the key prompt has. An endpoint is the other thing here
|
|
4333
|
+
// nobody wants to retype, and it is the field most likely to arrive from a browser's address
|
|
4334
|
+
// bar with a newline attached.
|
|
4335
|
+
if (key.ctrl && input.toLowerCase() === 'v') {
|
|
4336
|
+
void pasteIntoAddProvider();
|
|
4337
|
+
return;
|
|
4338
|
+
}
|
|
4292
4339
|
// A pasted endpoint arrives as one chunk, so it is taken whole rather than a letter at a time.
|
|
4293
|
-
if (!key.
|
|
4294
|
-
|
|
4340
|
+
if (!key.meta && input) {
|
|
4341
|
+
const clean = cleanPastedSecret(input);
|
|
4342
|
+
if (clean !== '')
|
|
4343
|
+
setAddProv(p => (p ? { ...p, value: p.value + clean } : p));
|
|
4295
4344
|
}
|
|
4296
4345
|
return;
|
|
4297
4346
|
}
|
|
@@ -4309,9 +4358,27 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
4309
4358
|
setKeyPrompt(p => (p ? { ...p, value: p.value.slice(0, -1) } : p));
|
|
4310
4359
|
return;
|
|
4311
4360
|
}
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4361
|
+
/*
|
|
4362
|
+
* Ctrl+V fetches the clipboard itself, rather than waiting for the terminal to deliver it.
|
|
4363
|
+
*
|
|
4364
|
+
* Pasting into a terminal is several different mechanisms and not all of them arrive here:
|
|
4365
|
+
* bracketed paste does, a plain burst does, a middle-click may have been taken over by
|
|
4366
|
+
* KONECK's own mouse handling, and on Windows some hosts deliver Ctrl+V as a control code
|
|
4367
|
+
* with no text attached. Somebody putting an API key into this box does not care which of
|
|
4368
|
+
* those their terminal does, and typing a sixty-character secret by hand is not an answer. So
|
|
4369
|
+
* this asks the operating system instead, which works the same way everywhere it works at all.
|
|
4370
|
+
*/
|
|
4371
|
+
if (key.ctrl && input.toLowerCase() === 'v') {
|
|
4372
|
+
void pasteFromClipboard();
|
|
4373
|
+
return;
|
|
4374
|
+
}
|
|
4375
|
+
// A paste arrives as one chunk, so it is taken whole rather than a character at a time — and
|
|
4376
|
+
// cleaned, because a key copied out of a file carries a newline and a terminal announcing a
|
|
4377
|
+
// paste mid-chunk can leave its marker text behind.
|
|
4378
|
+
if (!key.meta && input) {
|
|
4379
|
+
const clean = cleanPastedSecret(input);
|
|
4380
|
+
if (clean !== '')
|
|
4381
|
+
setKeyPrompt(p => (p ? { ...p, value: p.value + clean } : p));
|
|
4315
4382
|
}
|
|
4316
4383
|
return;
|
|
4317
4384
|
}
|
|
@@ -5357,7 +5424,7 @@ export function App({ config: initialConfig, clearFrame }) {
|
|
|
5357
5424
|
: { label: 'repoint ' + addProv.name + '? (y/n)',
|
|
5358
5425
|
hint: `it ships with KONECK, pointing at ${shadowed?.baseURL}` };
|
|
5359
5426
|
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" })] })] }));
|
|
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 && (() => {
|
|
5427
|
+
})(), 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, or ctrl+v to read the clipboard. Held in memory for this session only; esc to cancel." }), keyPrompt.note && _jsx(Text, { color: CYAN, children: keyPrompt.note }), _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" }), keyPrompt.value.length > 0 && (_jsxs(Text, { color: DIM, children: [" ", keyPrompt.value.length, " characters"] }))] })] })), ask && (() => {
|
|
5361
5428
|
let detail = '';
|
|
5362
5429
|
try {
|
|
5363
5430
|
const parsed = JSON.parse(ask.args);
|