@zhihaohong52/sonata 0.4.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/LICENSE +21 -0
- package/README.md +437 -0
- package/dist/adapters/claude.js +83 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex.js +382 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/index.js +21 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/opencode.js +104 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/adapters/pi.js +152 -0
- package/dist/adapters/pi.js.map +1 -0
- package/dist/adapters/reasonix.js +358 -0
- package/dist/adapters/reasonix.js.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/agent-markers.js +3 -0
- package/dist/agent-markers.js.map +1 -0
- package/dist/aipricing.js +109 -0
- package/dist/aipricing.js.map +1 -0
- package/dist/catalog.js +274 -0
- package/dist/catalog.js.map +1 -0
- package/dist/cli.js +766 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/approve.js +23 -0
- package/dist/commands/approve.js.map +1 -0
- package/dist/commands/auth.js +41 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/catalog.js +172 -0
- package/dist/commands/catalog.js.map +1 -0
- package/dist/commands/code.js +113 -0
- package/dist/commands/code.js.map +1 -0
- package/dist/commands/dispatch.js +148 -0
- package/dist/commands/dispatch.js.map +1 -0
- package/dist/commands/doctor.js +545 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/gc.js +25 -0
- package/dist/commands/gc.js.map +1 -0
- package/dist/commands/init-log.js +83 -0
- package/dist/commands/init-log.js.map +1 -0
- package/dist/commands/init.js +157 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/log.js +24 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/route.js +660 -0
- package/dist/commands/route.js.map +1 -0
- package/dist/commands/run.js +199 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/runs.js +37 -0
- package/dist/commands/runs.js.map +1 -0
- package/dist/commands/serve.js +818 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/status.js +14 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.js +258 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/tail.js +229 -0
- package/dist/commands/tail.js.map +1 -0
- package/dist/commands/usage.js +85 -0
- package/dist/commands/usage.js.map +1 -0
- package/dist/commands/verify.js +69 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/wait.js +38 -0
- package/dist/commands/wait.js.map +1 -0
- package/dist/config.js +588 -0
- package/dist/config.js.map +1 -0
- package/dist/detect.js +445 -0
- package/dist/detect.js.map +1 -0
- package/dist/filelock.js +83 -0
- package/dist/filelock.js.map +1 -0
- package/dist/init/apply.js +96 -0
- package/dist/init/apply.js.map +1 -0
- package/dist/init/candidates.js +115 -0
- package/dist/init/candidates.js.map +1 -0
- package/dist/init/discover.js +216 -0
- package/dist/init/discover.js.map +1 -0
- package/dist/init/helpers.js +429 -0
- package/dist/init/helpers.js.map +1 -0
- package/dist/init/interactive-state.js +128 -0
- package/dist/init/interactive-state.js.map +1 -0
- package/dist/init/plan.js +225 -0
- package/dist/init/plan.js.map +1 -0
- package/dist/init/scripted-state.js +130 -0
- package/dist/init/scripted-state.js.map +1 -0
- package/dist/init/toml.js +83 -0
- package/dist/init/toml.js.map +1 -0
- package/dist/init/validate.js +149 -0
- package/dist/init/validate.js.map +1 -0
- package/dist/ledger.js +123 -0
- package/dist/ledger.js.map +1 -0
- package/dist/mcp/protocol.js +64 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/server.js +96 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.js +210 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mode.js +64 -0
- package/dist/mode.js.map +1 -0
- package/dist/native/claude-shim.js +228 -0
- package/dist/native/claude-shim.js.map +1 -0
- package/dist/native/codex-auth.js +177 -0
- package/dist/native/codex-auth.js.map +1 -0
- package/dist/native/copilot-auth.js +105 -0
- package/dist/native/copilot-auth.js.map +1 -0
- package/dist/native/credentials.js +83 -0
- package/dist/native/credentials.js.map +1 -0
- package/dist/native/hosts.js +60 -0
- package/dist/native/hosts.js.map +1 -0
- package/dist/native/litellm.js +103 -0
- package/dist/native/litellm.js.map +1 -0
- package/dist/native/models.js +96 -0
- package/dist/native/models.js.map +1 -0
- package/dist/native/oauth-login.js +112 -0
- package/dist/native/oauth-login.js.map +1 -0
- package/dist/native/router.js +469 -0
- package/dist/native/router.js.map +1 -0
- package/dist/native/tls.js +73 -0
- package/dist/native/tls.js.map +1 -0
- package/dist/native/usage.js +119 -0
- package/dist/native/usage.js.map +1 -0
- package/dist/normalize.js +98 -0
- package/dist/normalize.js.map +1 -0
- package/dist/pricing.js +86 -0
- package/dist/pricing.js.map +1 -0
- package/dist/roles.js +49 -0
- package/dist/roles.js.map +1 -0
- package/dist/sessions.js +62 -0
- package/dist/sessions.js.map +1 -0
- package/dist/settings.js +131 -0
- package/dist/settings.js.map +1 -0
- package/dist/store.js +70 -0
- package/dist/store.js.map +1 -0
- package/dist/tmux.js +89 -0
- package/dist/tmux.js.map +1 -0
- package/dist/tui-ink/app-state.js +340 -0
- package/dist/tui-ink/app-state.js.map +1 -0
- package/dist/tui-ink/app.js +156 -0
- package/dist/tui-ink/app.js.map +1 -0
- package/dist/tui-ink/components/byok-step.js +102 -0
- package/dist/tui-ink/components/byok-step.js.map +1 -0
- package/dist/tui-ink/components/login-screen.js +80 -0
- package/dist/tui-ink/components/login-screen.js.map +1 -0
- package/dist/tui-ink/components/models-step.js +92 -0
- package/dist/tui-ink/components/models-step.js.map +1 -0
- package/dist/tui-ink/components/multi-select-state.js +56 -0
- package/dist/tui-ink/components/multi-select-state.js.map +1 -0
- package/dist/tui-ink/components/multi-select.js +69 -0
- package/dist/tui-ink/components/multi-select.js.map +1 -0
- package/dist/tui-ink/components/providers-step.js +285 -0
- package/dist/tui-ink/components/providers-step.js.map +1 -0
- package/dist/tui-ink/components/ranked-select-state.js +41 -0
- package/dist/tui-ink/components/ranked-select-state.js.map +1 -0
- package/dist/tui-ink/components/ranked-select.js +61 -0
- package/dist/tui-ink/components/ranked-select.js.map +1 -0
- package/dist/tui-ink/components/search-select.js +47 -0
- package/dist/tui-ink/components/search-select.js.map +1 -0
- package/dist/tui-ink/components/text-input-state.js +42 -0
- package/dist/tui-ink/components/text-input-state.js.map +1 -0
- package/dist/tui-ink/components/text-input.js +48 -0
- package/dist/tui-ink/components/text-input.js.map +1 -0
- package/dist/tui-ink/run.js +38 -0
- package/dist/tui-ink/run.js.map +1 -0
- package/dist/tui-ink/test-compile.js +4 -0
- package/dist/tui-ink/test-compile.js.map +1 -0
- package/dist/tui-ink/types.js +2 -0
- package/dist/tui-ink/types.js.map +1 -0
- package/dist/tui.js +330 -0
- package/dist/tui.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/watchdog.js +85 -0
- package/dist/watchdog.js.map +1 -0
- package/hooks/capture-mode.mjs +56 -0
- package/hooks/ensure-serve.mjs +140 -0
- package/hooks/hooks.json +12 -0
- package/hooks/route-session.mjs +47 -0
- package/hooks/route-subagent.mjs +54 -0
- package/package.json +64 -0
- package/roles/code.md +13 -0
- package/roles/explore.md +9 -0
- package/roles/plan.md +10 -0
- package/roles/review.md +11 -0
- package/skills/loop/SKILL.md +43 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { MultiSelect } from './components/multi-select.js';
|
|
5
|
+
import { RankedSelect } from './components/ranked-select.js';
|
|
6
|
+
import { ProvidersStep } from './components/providers-step.js';
|
|
7
|
+
import { ModelsStep } from './components/models-step.js';
|
|
8
|
+
import { loadAaCatalog, proposeTiers } from '../catalog.js';
|
|
9
|
+
import { applyStep, candidatesForProviders, initialRankedFor, acceptRemainingTiers, tierPickerKeys, } from './app-state.js';
|
|
10
|
+
import { byokCandidateKey, fetchModels as defaultFetchModels } from '../native/models.js';
|
|
11
|
+
import { ROLE_BLURB } from '../roles.js';
|
|
12
|
+
export { applyStep, candidatesForProviders } from './app-state.js';
|
|
13
|
+
function Choice({ title, choices, initial, onSubmit, onBack, onCancel }) {
|
|
14
|
+
const [cursor, setCursor] = useState(() => Math.max(0, choices.findIndex((choice) => choice.value === initial)));
|
|
15
|
+
useInput((_, key) => {
|
|
16
|
+
if (key.escape)
|
|
17
|
+
return onCancel();
|
|
18
|
+
if (key.leftArrow)
|
|
19
|
+
return onBack?.();
|
|
20
|
+
if (key.upArrow)
|
|
21
|
+
return setCursor((current) => (current - 1 + choices.length) % choices.length);
|
|
22
|
+
if (key.downArrow)
|
|
23
|
+
return setCursor((current) => (current + 1) % choices.length);
|
|
24
|
+
if (key.return && choices[cursor])
|
|
25
|
+
onSubmit(choices[cursor].value);
|
|
26
|
+
});
|
|
27
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: title }), choices.map((choice, index) => (_jsxs(Text, { inverse: index === cursor, children: [index === cursor ? '›' : ' ', " ", choice.label, choice.hint ? ` · ${choice.hint}` : ''] }, String(choice.value)))), _jsxs(Text, { dimColor: true, children: ["\u2191\u2193 choose \u00B7 enter confirm", onBack ? ' · ← back' : '', " \u00B7 esc cancel"] })] }));
|
|
28
|
+
}
|
|
29
|
+
function Summary({ state, onDone, onBack }) {
|
|
30
|
+
const hasModels = (state.nativeKeys?.length ?? 0) > 0;
|
|
31
|
+
useInput((_, key) => {
|
|
32
|
+
if (key.escape)
|
|
33
|
+
onDone({ cancelled: true, state });
|
|
34
|
+
if (key.leftArrow)
|
|
35
|
+
onBack();
|
|
36
|
+
if (key.return && hasModels)
|
|
37
|
+
onDone({ cancelled: false, state });
|
|
38
|
+
});
|
|
39
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Summary" }), _jsxs(Text, { children: ["Config scope: ", state.configScope ?? 'none'] }), _jsxs(Text, { children: ["Imported from: ", state.harnesses?.join(', ') || 'no harness (native models only)'] }), _jsxs(Text, { children: ["Providers: ", state.providerKeys?.join(', ') || 'none'] }), _jsxs(Text, { children: ["Models: ", state.nativeKeys?.join(', ') || 'none'] }), !hasModels && _jsx(Text, { color: "red", children: "Select at least one model before continuing." }), _jsxs(Text, { children: ["Roles: ", state.roles?.join(', ') || 'none'] }), (state.roles ?? []).map((role) => {
|
|
40
|
+
const tiers = state.tiers?.[role];
|
|
41
|
+
const line = tiers
|
|
42
|
+
? ['simple', 'complex'].map((tier) => {
|
|
43
|
+
const ranked = tiers[tier] ?? [];
|
|
44
|
+
const [main, ...backups] = ranked;
|
|
45
|
+
return `${tier} → ${main ?? 'none'}${backups.length > 0 ? ` (+${backups.length} backup${backups.length === 1 ? '' : 's'})` : ''}`;
|
|
46
|
+
}).join(' · ')
|
|
47
|
+
: 'none';
|
|
48
|
+
return _jsxs(Text, { children: [" ", role, ": ", line] }, role);
|
|
49
|
+
}), _jsxs(Text, { dimColor: true, children: [hasModels ? 'enter confirm' : '← back to select models', " \u00B7 \u2190 back \u00B7 esc cancel"] })] }));
|
|
50
|
+
}
|
|
51
|
+
export function InitWizard({ data, onDone }) {
|
|
52
|
+
const [step, setStep] = useState(0);
|
|
53
|
+
const [state, setState] = useState(data.initialState ?? {});
|
|
54
|
+
const [tierIndex, setTierIndex] = useState(0);
|
|
55
|
+
const cancel = () => onDone({ cancelled: true, state });
|
|
56
|
+
const next = (value) => {
|
|
57
|
+
setState((current) => applyStep(current, step, value));
|
|
58
|
+
setStep((current) => current + 1);
|
|
59
|
+
};
|
|
60
|
+
const chooseScope = (scope) => {
|
|
61
|
+
setState(data.initialStateByScope?.[scope] ?? { configScope: scope });
|
|
62
|
+
setTierIndex(0);
|
|
63
|
+
setStep(1);
|
|
64
|
+
};
|
|
65
|
+
const back = () => setStep((current) => Math.max(0, current - 1));
|
|
66
|
+
switch (step) {
|
|
67
|
+
case 0:
|
|
68
|
+
// A project config *replaces* the machine one rather than merging, which
|
|
69
|
+
// is the whole consequence of this answer and was nowhere on the screen.
|
|
70
|
+
return _jsx(Choice, { title: "Where should this config live?", choices: [
|
|
71
|
+
{ value: 'project', label: 'Project', hint: './sonata.toml — this repo only, and it replaces the machine config here' },
|
|
72
|
+
{ value: 'global', label: 'Global', hint: '~/.config/sonata/sonata.toml — every project without its own' },
|
|
73
|
+
], initial: state.configScope, onSubmit: chooseScope, onCancel: cancel }, "config-scope");
|
|
74
|
+
case 1: {
|
|
75
|
+
const providers = data.providers;
|
|
76
|
+
return _jsx(ProvidersStep, { home: data.home, harnesses: data.harnesses, providers: providers, byokProviders: data.byokProviders, credentialAvailability: data.credentialAvailability ?? {}, gatewayAuth: data.gatewayAuth ?? {}, storedKeys: data.storedKeys, fetchModels: data.fetchModels ?? defaultFetchModels, state: state, onChange: setState, onContinue: () => {
|
|
77
|
+
const candidates = candidatesForProviders(data.candidates, providers, state.providerKeys);
|
|
78
|
+
setStep(candidates.length > 0 ? 2 : 3);
|
|
79
|
+
}, onBack: back, onCancel: cancel }, "providers-step");
|
|
80
|
+
}
|
|
81
|
+
case 2: {
|
|
82
|
+
const candidates = candidatesForProviders(data.candidates, data.providers, state.providerKeys);
|
|
83
|
+
if (candidates.length === 0)
|
|
84
|
+
return _jsx(Summary, { state: state, onDone: onDone, onBack: back });
|
|
85
|
+
return _jsx(ModelsStep, { candidates: candidates, gatewayBaseUrls: data.gatewayBaseUrls ?? {}, gatewayAuth: data.gatewayAuth ?? {}, keys: { ...data.storedKeys, ...state.byokKeys }, fetchModels: data.fetchModels ?? defaultFetchModels, initialSelected: new Set(state.nativeKeys), onSubmit: (keys, live) => {
|
|
86
|
+
// Keep any BYOK/custom-provider keys already chosen: this step owns
|
|
87
|
+
// the harness candidates only, and a plain overwrite would drop the
|
|
88
|
+
// rest — they were already selected inside ProvidersStep.
|
|
89
|
+
const byokKeys = new Set(Object.entries(state.byokModels ?? {}).flatMap(([provider, ids]) => ids.map((id) => byokCandidateKey(provider, id))));
|
|
90
|
+
// Deduped: a BYOK model can now also appear in this picker (its
|
|
91
|
+
// gateway's live /models answer lists it), so `keys` and `kept`
|
|
92
|
+
// overlap — and a doubled key is written to the config twice.
|
|
93
|
+
const kept = (state.nativeKeys ?? []).filter((key) => byokKeys.has(key));
|
|
94
|
+
setState((current) => ({
|
|
95
|
+
...applyStep(current, 2, [...new Set([...keys, ...kept])]),
|
|
96
|
+
// Carried so cmdInit can mint a candidate for a model only the
|
|
97
|
+
// gateway knew about — otherwise it reaches the tiers but never
|
|
98
|
+
// [models], and the config it writes will not parse.
|
|
99
|
+
liveModels: { ...current.liveModels, ...live },
|
|
100
|
+
}));
|
|
101
|
+
setStep(3);
|
|
102
|
+
}, onBack: back, onCancel: cancel }, "models");
|
|
103
|
+
}
|
|
104
|
+
case 3: {
|
|
105
|
+
const candidates = candidatesForProviders(data.candidates, data.providers, state.providerKeys);
|
|
106
|
+
return _jsx(MultiSelect, { title: "Roles", items: data.roles.map((role) => ({ value: role, label: role, hint: ROLE_BLURB[role] })), initialSelected: new Set(state.roles ?? data.roles), onSubmit: next, onBack: () => setStep(candidates.length > 0 ? 2 : 1), onCancel: cancel, filterable: false }, "roles");
|
|
107
|
+
}
|
|
108
|
+
case 4: {
|
|
109
|
+
const roles = state.roles ?? [];
|
|
110
|
+
const role = roles[Math.floor(tierIndex / 2)];
|
|
111
|
+
const tier = tierIndex % 2 === 0 ? 'simple' : 'complex';
|
|
112
|
+
if (!role)
|
|
113
|
+
return _jsx(Summary, { state: state, onDone: onDone, onBack: back });
|
|
114
|
+
const catalog = loadAaCatalog(data.home);
|
|
115
|
+
// Gateway names come from the candidate set: a model key is
|
|
116
|
+
// `<gateway>-<id>`, and without them the id cannot be recovered, so the
|
|
117
|
+
// model misses its catalog entry and drops out of the simple tier.
|
|
118
|
+
const gateways = [...new Set(data.candidates.map((candidate) => candidate.gateway))];
|
|
119
|
+
// Resolved from the candidate set, not by matching key prefixes: a key
|
|
120
|
+
// only looks like `<gateway>-<id>`.
|
|
121
|
+
const avoid = new Set(data.avoidGateways ?? []);
|
|
122
|
+
const avoided = new Set(data.candidates.filter((c) => avoid.has(c.gateway)).map((c) => c.key));
|
|
123
|
+
const proposal = proposeTiers(state.nativeKeys ?? [], catalog, gateways, avoided);
|
|
124
|
+
const initialRanked = initialRankedFor(state.tiers?.[role]?.[tier], proposal[tier]);
|
|
125
|
+
const footer = catalog
|
|
126
|
+
? `rankings: Artificial Analysis (fetched ${catalog.fetchedAt}) — artificialanalysis.ai`
|
|
127
|
+
: 'rankings: built-in defaults — refresh with sonata catalog update';
|
|
128
|
+
return _jsx(RankedSelect, { title: `${role}: ${tier} models`, items: tierPickerKeys(state.nativeKeys ?? [], initialRanked, data.candidates.map((c) => c.key)).map((key) => ({ value: key, label: key })), initialRanked: initialRanked, footer: footer, onSubmit: (ranked) => {
|
|
129
|
+
setState((current) => applyStep(current, 4, { role, tier, ranked }));
|
|
130
|
+
if (tierIndex + 1 < roles.length * 2)
|
|
131
|
+
setTierIndex((current) => current + 1);
|
|
132
|
+
else
|
|
133
|
+
setStep(5);
|
|
134
|
+
}, onAcceptRest: tierIndex + 1 < roles.length * 2
|
|
135
|
+
? (ranked) => {
|
|
136
|
+
// This screen keeps what the user actually ranked; only the
|
|
137
|
+
// screens after it take the seed they would have opened with.
|
|
138
|
+
setState((current) => acceptRemainingTiers(applyStep(current, 4, { role, tier, ranked }), roles, tierIndex + 1, proposal,
|
|
139
|
+
// The same universe the picker builds its items from, so a
|
|
140
|
+
// deselected-but-natively-routable key is withheld here exactly
|
|
141
|
+
// as it would be on the screen this is standing in for.
|
|
142
|
+
data.candidates.map((c) => c.key)));
|
|
143
|
+
setStep(5);
|
|
144
|
+
}
|
|
145
|
+
: undefined, onBack: () => {
|
|
146
|
+
if (tierIndex === 0)
|
|
147
|
+
back();
|
|
148
|
+
else
|
|
149
|
+
setTierIndex((current) => current - 1);
|
|
150
|
+
}, onCancel: cancel }, `${role}-${tier}`);
|
|
151
|
+
}
|
|
152
|
+
default:
|
|
153
|
+
return _jsx(Summary, { state: state, onDone: onDone, onBack: back });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/tui-ink/app.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { MultiSelect } from './multi-select.js';
|
|
5
|
+
import { TextInput } from './text-input.js';
|
|
6
|
+
import { isAnthropicRoutedName } from '../../config.js';
|
|
7
|
+
function Choice({ title, choices, initial, onSubmit, onBack, onCancel }) {
|
|
8
|
+
const [cursor, setCursor] = useState(() => Math.max(0, choices.findIndex((choice) => choice.value === initial)));
|
|
9
|
+
useInput((_, key) => {
|
|
10
|
+
if (key.escape)
|
|
11
|
+
return onCancel();
|
|
12
|
+
if (key.leftArrow)
|
|
13
|
+
return onBack?.();
|
|
14
|
+
if (key.upArrow)
|
|
15
|
+
return setCursor((current) => (current - 1 + choices.length) % choices.length);
|
|
16
|
+
if (key.downArrow)
|
|
17
|
+
return setCursor((current) => (current + 1) % choices.length);
|
|
18
|
+
if (key.return && choices[cursor])
|
|
19
|
+
onSubmit(choices[cursor].value);
|
|
20
|
+
});
|
|
21
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: title }), choices.map((choice, index) => (_jsxs(Text, { inverse: index === cursor, children: [index === cursor ? '›' : ' ', " ", choice.label] }, String(choice.value)))), _jsxs(Text, { dimColor: true, children: ["\u2191\u2193 choose \u00B7 enter confirm", onBack ? ' · ← back' : '', " \u00B7 esc cancel"] })] }));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One BYOK provider: get a key if we lack one, ask the provider what it serves,
|
|
25
|
+
* and let the user choose — or type ids, when it will not say.
|
|
26
|
+
*
|
|
27
|
+
* This is the wizard's only asynchronous step. The fetch runs in an effect and
|
|
28
|
+
* the reducer never blocks on it; a `cancelled` flag keeps a late response from
|
|
29
|
+
* writing into a screen the user has already left.
|
|
30
|
+
*/
|
|
31
|
+
export function ByokStep(props) {
|
|
32
|
+
const { provider, apiKey, initialIds, fetchModels, onKey, onSubmit, onBack, onCancel } = props;
|
|
33
|
+
const [result, setResult] = useState(undefined);
|
|
34
|
+
const [dropped, setDropped] = useState(0);
|
|
35
|
+
const [attempt, setAttempt] = useState(0);
|
|
36
|
+
const [ignoreRejection, setIgnoreRejection] = useState(false);
|
|
37
|
+
const [retrying, setRetrying] = useState(false);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (apiKey === undefined)
|
|
40
|
+
return;
|
|
41
|
+
let cancelled = false;
|
|
42
|
+
void fetchModels(provider.url, apiKey).then((found) => {
|
|
43
|
+
if (cancelled)
|
|
44
|
+
return;
|
|
45
|
+
if (found.outcome === 'ok') {
|
|
46
|
+
const usable = found.models.filter((model) => !isAnthropicRoutedName(model.id));
|
|
47
|
+
setDropped(found.models.length - usable.length);
|
|
48
|
+
setResult({ outcome: 'ok', models: usable });
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
setResult(found);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return () => { cancelled = true; };
|
|
55
|
+
}, [apiKey, provider.url, fetchModels, attempt]);
|
|
56
|
+
const retryKey = () => {
|
|
57
|
+
setResult(undefined);
|
|
58
|
+
setIgnoreRejection(false);
|
|
59
|
+
setRetrying(false);
|
|
60
|
+
setAttempt((n) => n + 1);
|
|
61
|
+
};
|
|
62
|
+
if (apiKey === undefined || (result?.outcome === 'unauthorized' && retrying)) {
|
|
63
|
+
return (_jsx(TextInput, { title: `API key for ${provider.name}`, hint: `${provider.url} · stored in sonata's key store, not shown again`, mask: true, validate: (value) => value.trim() === '' ? 'A key is required to list this provider\'s models.' : undefined, onSubmit: (value) => { onKey(value.trim()); retryKey(); }, onBack: onBack, onCancel: onCancel }, `byok-key-${provider.name}-${attempt}`));
|
|
64
|
+
}
|
|
65
|
+
if (result === undefined) {
|
|
66
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: provider.name }), _jsxs(Text, { dimColor: true, children: ["fetching models from ", provider.url, "\u2026"] })] }));
|
|
67
|
+
}
|
|
68
|
+
if (result.outcome === 'unauthorized' && !ignoreRejection) {
|
|
69
|
+
return (_jsx(Choice, { title: `${provider.name} rejected that key (HTTP ${result.status})`, choices: [
|
|
70
|
+
{ value: 'retry', label: 'Re-enter the key' },
|
|
71
|
+
{ value: 'manual', label: 'Keep it and type model ids by hand' },
|
|
72
|
+
], initial: 'retry', onSubmit: (choice) => choice === 'retry' ? setRetrying(true) : setIgnoreRejection(true), onBack: onBack, onCancel: onCancel }, `byok-rejected-${provider.name}`));
|
|
73
|
+
}
|
|
74
|
+
if (result.outcome !== 'ok' || result.models.length === 0) {
|
|
75
|
+
return (_jsx(TextInput, { title: `Model ids for ${provider.name} (comma-separated)`, hint: hintFor(result, provider.url), initial: initialIds?.join(', '), validate: validateIds, onSubmit: (value) => onSubmit(parseIds(value)), onBack: onBack, onCancel: onCancel }, `byok-ids-${provider.name}`));
|
|
76
|
+
}
|
|
77
|
+
return (_jsxs(Box, { flexDirection: "column", children: [dropped > 0 && (_jsxs(Text, { dimColor: true, children: [dropped, " claude-* model", dropped === 1 ? '' : 's', " not shown \u2014 the router reserves that prefix"] })), _jsx(MultiSelect, { title: `Models for ${provider.name}`, items: result.models.map((model) => ({ value: model.id, label: model.name ?? model.id, hint: model.id })), initialSelected: new Set(initialIds), onSubmit: onSubmit, onBack: onBack, onCancel: onCancel }, `byok-models-${provider.name}`)] }));
|
|
78
|
+
}
|
|
79
|
+
/** Says what actually happened, so the fallback is not read as a diagnosis. */
|
|
80
|
+
function hintFor(result, url) {
|
|
81
|
+
switch (result.outcome) {
|
|
82
|
+
case 'unauthorized':
|
|
83
|
+
return `${url} rejected the key for listing models — enter ids by hand`;
|
|
84
|
+
case 'unreachable':
|
|
85
|
+
return `could not reach ${url} — enter ids by hand`;
|
|
86
|
+
default:
|
|
87
|
+
return `${url} did not return a model list — enter ids by hand`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function validateIds(value) {
|
|
91
|
+
if (parseIds(value).length > 0)
|
|
92
|
+
return undefined;
|
|
93
|
+
const typed = value.split(',').map((id) => id.trim()).filter((id) => id !== '');
|
|
94
|
+
return typed.length > 0
|
|
95
|
+
? 'None of those can be used: the router reserves `claude-` for Anthropic.'
|
|
96
|
+
: 'Enter at least one model id.';
|
|
97
|
+
}
|
|
98
|
+
function parseIds(value) {
|
|
99
|
+
return [...new Set(value.split(',').map((id) => id.trim()).filter((id) => id !== ''))]
|
|
100
|
+
.filter((id) => !isAnthropicRoutedName(id));
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=byok-step.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"byok-step.js","sourceRoot":"","sources":["../../../src/tui-ink/components/byok-step.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAYxD,SAAS,MAAM,CAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAkB;IACxF,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAEjH,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;QAClB,IAAI,GAAG,CAAC,MAAM;YAAE,OAAO,QAAQ,EAAE,CAAC;QAClC,IAAI,GAAG,CAAC,SAAS;YAAE,OAAO,MAAM,EAAE,EAAE,CAAC;QACrC,IAAI,GAAG,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QAChG,IAAI,GAAG,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QACjF,IAAI,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;YAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,IAAI,IAAC,IAAI,kBAAE,KAAK,GAAQ,EACxB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAC9B,MAAC,IAAI,IAA4B,OAAO,EAAE,KAAK,KAAK,MAAM,aACvD,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,OAAG,MAAM,CAAC,KAAK,KADnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAExB,CACR,CAAC,EACF,MAAC,IAAI,IAAC,QAAQ,+DAA2B,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,0BAAqB,IACnF,CACP,CAAC;AACJ,CAAC;AAcD;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAC/F,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAgC,SAAS,CAAC,CAAC;IAC/E,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEhD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO;QACjC,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,KAAK,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YACpD,IAAI,SAAS;gBAAE,OAAO;YACtB,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;gBAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;gBAChF,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;gBAChD,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;YAC/C,CAAC;iBAAM,CAAC;gBACN,SAAS,CAAC,KAAK,CAAC,CAAC;YACnB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC1B,SAAS,CAAC,SAAS,CAAC,CAAC;QACrB,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC1B,WAAW,CAAC,KAAK,CAAC,CAAC;QACnB,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,CAAC,CAAC;IAEF,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,EAAE,OAAO,KAAK,cAAc,IAAI,QAAQ,CAAC,EAAE,CAAC;QAC7E,OAAO,CACL,KAAC,SAAS,IAER,KAAK,EAAE,eAAe,QAAQ,CAAC,IAAI,EAAE,EACrC,IAAI,EAAE,GAAG,QAAQ,CAAC,GAAG,kDAAkD,EACvE,IAAI,QACJ,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,oDAAoD,CAAC,CAAC,CAAC,SAAS,EAC3G,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EACzD,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,IAPb,YAAY,QAAQ,CAAC,IAAI,IAAI,OAAO,EAAE,CAQ3C,CACH,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,IAAI,IAAC,IAAI,kBAAE,QAAQ,CAAC,IAAI,GAAQ,EACjC,MAAC,IAAI,IAAC,QAAQ,4CAAuB,QAAQ,CAAC,GAAG,cAAS,IACtD,CACP,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,KAAK,cAAc,IAAI,CAAC,eAAe,EAAE,CAAC;QAC1D,OAAO,CACL,KAAC,MAAM,IAEL,KAAK,EAAE,GAAG,QAAQ,CAAC,IAAI,4BAA4B,MAAM,CAAC,MAAM,GAAG,EACnE,OAAO,EAAE;gBACP,EAAE,KAAK,EAAE,OAAgB,EAAE,KAAK,EAAE,kBAAkB,EAAE;gBACtD,EAAE,KAAK,EAAE,QAAiB,EAAE,KAAK,EAAE,oCAAoC,EAAE;aAC1E,EACD,OAAO,EAAE,OAAgB,EACzB,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,EACvF,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,IATb,iBAAiB,QAAQ,CAAC,IAAI,EAAE,CAUrC,CACH,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,OAAO,CACL,KAAC,SAAS,IAER,KAAK,EAAE,iBAAiB,QAAQ,CAAC,IAAI,oBAAoB,EACzD,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,EACnC,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,EAC/B,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAC9C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,IAPb,YAAY,QAAQ,CAAC,IAAI,EAAE,CAQhC,CACH,CAAC;IACJ,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACxB,OAAO,GAAG,CAAC,IAAI,CACd,MAAC,IAAI,IAAC,QAAQ,mBACX,OAAO,qBAAiB,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,yDAC5C,CACR,EACD,KAAC,WAAW,IAEV,KAAK,EAAE,cAAc,QAAQ,CAAC,IAAI,EAAE,EACpC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EACzG,eAAe,EAAE,IAAI,GAAG,CAAC,UAAU,CAAC,EACpC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,IANb,eAAe,QAAQ,CAAC,IAAI,EAAE,CAOnC,IACE,CACP,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,SAAS,OAAO,CAAC,MAAyB,EAAE,GAAW;IACrD,QAAQ,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,KAAK,cAAc;YACjB,OAAO,GAAG,GAAG,0DAA0D,CAAC;QAC1E,KAAK,aAAa;YAChB,OAAO,mBAAmB,GAAG,sBAAsB,CAAC;QACtD;YACE,OAAO,GAAG,GAAG,kDAAkD,CAAC;IACpE,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACjD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAChF,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC;QACrB,CAAC,CAAC,yEAAyE;QAC3E,CAAC,CAAC,8BAA8B,CAAC;AACrC,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa;IAC7B,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;SACnF,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { loginGateway as defaultLoginGateway } from '../../native/oauth-login.js';
|
|
5
|
+
/**
|
|
6
|
+
* The newest URL and code from LiteLLM's printed output.
|
|
7
|
+
*
|
|
8
|
+
* Two shapes, because the two providers print differently: ChatGPT's is a
|
|
9
|
+
* four-line block (chatgpt/authenticator.py:162-168), Copilot's a single line
|
|
10
|
+
* (github_copilot/authenticator.py:358). Never re-derive these URLs from
|
|
11
|
+
* constants of our own -- one LiteLLM upgrade and we would point users at the
|
|
12
|
+
* wrong page.
|
|
13
|
+
*/
|
|
14
|
+
export function latestCode(lines) {
|
|
15
|
+
let url;
|
|
16
|
+
let code;
|
|
17
|
+
for (const line of lines) {
|
|
18
|
+
const oneLine = /visit (\S+) and enter code (\S+?)(?:\s+to authenticate)?[\s.]*$/i.exec(line);
|
|
19
|
+
if (oneLine) {
|
|
20
|
+
url = oneLine[1];
|
|
21
|
+
code = oneLine[2];
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const visit = /Visit (\S+)/.exec(line);
|
|
25
|
+
if (visit)
|
|
26
|
+
url = visit[1];
|
|
27
|
+
const enter = /Enter code:\s*(\S+)/i.exec(line);
|
|
28
|
+
if (enter)
|
|
29
|
+
code = enter[1];
|
|
30
|
+
}
|
|
31
|
+
return { ...(url ? { url } : {}), ...(code ? { code } : {}) };
|
|
32
|
+
}
|
|
33
|
+
export function LoginScreen({ home, gateway, auth, onDone, loginGateway = defaultLoginGateway }) {
|
|
34
|
+
const [lines, setLines] = useState([]);
|
|
35
|
+
const [seconds, setSeconds] = useState(60);
|
|
36
|
+
const [superseded, setSuperseded] = useState(false);
|
|
37
|
+
const controller = useRef(undefined);
|
|
38
|
+
const previousCode = useRef(undefined);
|
|
39
|
+
const onDoneRef = useRef(onDone);
|
|
40
|
+
const { url, code } = latestCode(lines);
|
|
41
|
+
useEffect(() => { onDoneRef.current = onDone; }, [onDone]);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const activeController = new AbortController();
|
|
44
|
+
controller.current = activeController;
|
|
45
|
+
let mounted = true;
|
|
46
|
+
void loginGateway({
|
|
47
|
+
home,
|
|
48
|
+
gateway,
|
|
49
|
+
auth,
|
|
50
|
+
progress: { line: (text) => setLines((current) => [...current, text]) },
|
|
51
|
+
signal: activeController.signal,
|
|
52
|
+
}).then((result) => { if (mounted)
|
|
53
|
+
onDoneRef.current(result); }, () => { if (mounted)
|
|
54
|
+
onDoneRef.current({ ok: false, problem: 'login failed' }); });
|
|
55
|
+
return () => {
|
|
56
|
+
mounted = false;
|
|
57
|
+
activeController.abort();
|
|
58
|
+
};
|
|
59
|
+
}, [auth, gateway, home, loginGateway]);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (code === undefined || code === previousCode.current)
|
|
62
|
+
return;
|
|
63
|
+
setSuperseded(previousCode.current !== undefined);
|
|
64
|
+
previousCode.current = code;
|
|
65
|
+
setSeconds(60);
|
|
66
|
+
}, [code]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (auth !== 'copilot-oauth' || code === undefined)
|
|
69
|
+
return;
|
|
70
|
+
const timer = setInterval(() => setSeconds((current) => Math.max(0, current - 1)), 1000);
|
|
71
|
+
return () => clearInterval(timer);
|
|
72
|
+
}, [auth, code]);
|
|
73
|
+
useInput((_, key) => {
|
|
74
|
+
if (key.escape)
|
|
75
|
+
controller.current?.abort();
|
|
76
|
+
});
|
|
77
|
+
const warning = lines.find((line) => line.includes('Device codes are a common phishing target.'));
|
|
78
|
+
return (_jsxs(Box, { flexDirection: "column", children: [url !== undefined && _jsxs(Text, { children: ["Open ", url, " in your browser, then the code below"] }), code !== undefined && _jsx(Text, { bold: true, color: "cyan", children: code }), auth === 'copilot-oauth' && code !== undefined && _jsxs(Text, { children: [seconds, "s remaining"] }), superseded && _jsx(Text, { color: "yellow", children: "A new code was issued \u2014 use the one above." }), warning !== undefined && _jsx(Text, { children: warning }), code === undefined && _jsx(Text, { dimColor: true, children: "Waiting for a device code..." }), _jsx(Text, { dimColor: true, children: "Esc to cancel" })] }));
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=login-screen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-screen.js","sourceRoot":"","sources":["../../../src/tui-ink/components/login-screen.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import { MultiSelect } from './multi-select.js';
|
|
5
|
+
import { mergeLiveCandidates } from '../app-state.js';
|
|
6
|
+
import { isAnthropicRoutedName, isOauthGatewayAuth } from '../../config.js';
|
|
7
|
+
import { fetchModels as defaultFetchModels } from '../../native/models.js';
|
|
8
|
+
/**
|
|
9
|
+
* Which gateways can be asked what they serve, rather than trusted to still
|
|
10
|
+
* match a harness snapshot.
|
|
11
|
+
*
|
|
12
|
+
* Three things have to hold. A **base URL**, or there is nowhere to ask. A
|
|
13
|
+
* **key**, because listing models means authenticating. And **not OAuth**: a
|
|
14
|
+
* subscription credential is not a bearer key, and those gateways do not serve
|
|
15
|
+
* an OpenAI-shaped `/models` anyway (ChatGPT's is `backend-api/codex`, Copilot
|
|
16
|
+
* needs a token exchange first) — so asking would fail slowly and teach
|
|
17
|
+
* nothing. Everything excluded here keeps its harness list.
|
|
18
|
+
*/
|
|
19
|
+
export function refreshableGateways(candidates, gatewayBaseUrls, gatewayAuth, keys) {
|
|
20
|
+
const gateways = [...new Set(candidates.map((candidate) => candidate.gateway))];
|
|
21
|
+
return gateways.filter((gateway) => {
|
|
22
|
+
const auth = gatewayAuth[gateway];
|
|
23
|
+
if (auth !== undefined && isOauthGatewayAuth(auth))
|
|
24
|
+
return false;
|
|
25
|
+
return gatewayBaseUrls[gateway] !== undefined && keys[gateway] !== undefined;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The model picker, over a catalogue refreshed from the gateways themselves.
|
|
30
|
+
*
|
|
31
|
+
* The wizard's only other asynchronous screen is `ByokStep`, and this follows
|
|
32
|
+
* its shape: the fetch runs in an effect, a `cancelled` flag keeps a late
|
|
33
|
+
* response from writing into a screen the user has left, and a failure is
|
|
34
|
+
* never fatal — `fetchModels` is timeout-bounded and non-throwing, and a
|
|
35
|
+
* gateway that does not answer simply keeps the list the harness gave.
|
|
36
|
+
*/
|
|
37
|
+
export function ModelsStep(props) {
|
|
38
|
+
const { candidates, gatewayBaseUrls, gatewayAuth, keys, initialSelected, fetchModels = defaultFetchModels, onSubmit, onBack, onCancel, } = props;
|
|
39
|
+
const targets = refreshableGateways(candidates, gatewayBaseUrls, gatewayAuth, keys);
|
|
40
|
+
const [live, setLive] = useState(targets.length === 0 ? {} : undefined);
|
|
41
|
+
const [failed, setFailed] = useState([]);
|
|
42
|
+
// The gateway set is derived from props that do not change while this screen
|
|
43
|
+
// is mounted; joining it keeps the effect from re-firing on every render.
|
|
44
|
+
const targetKey = targets.join(',');
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (targets.length === 0)
|
|
47
|
+
return;
|
|
48
|
+
let cancelled = false;
|
|
49
|
+
void Promise.all(targets.map(async (gateway) => {
|
|
50
|
+
const found = await fetchModels(gatewayBaseUrls[gateway], keys[gateway]);
|
|
51
|
+
return { gateway, found };
|
|
52
|
+
})).then((results) => {
|
|
53
|
+
if (cancelled)
|
|
54
|
+
return;
|
|
55
|
+
const fresh = {};
|
|
56
|
+
const problems = [];
|
|
57
|
+
for (const { gateway, found } of results) {
|
|
58
|
+
// The router reserves `claude-` for Anthropic, so parseConfig refuses
|
|
59
|
+
// such an id — offering one would write a config it cannot read back.
|
|
60
|
+
const usable = found.outcome === 'ok'
|
|
61
|
+
? found.models.map((model) => model.id).filter((id) => !isAnthropicRoutedName(id))
|
|
62
|
+
: [];
|
|
63
|
+
// An empty answer is not evidence the gateway serves nothing; treat it
|
|
64
|
+
// as a failed refresh so the harness list survives.
|
|
65
|
+
if (usable.length > 0)
|
|
66
|
+
fresh[gateway] = usable;
|
|
67
|
+
else
|
|
68
|
+
problems.push(gateway);
|
|
69
|
+
}
|
|
70
|
+
setFailed(problems);
|
|
71
|
+
setLive(fresh);
|
|
72
|
+
});
|
|
73
|
+
return () => { cancelled = true; };
|
|
74
|
+
}, [targetKey, fetchModels]);
|
|
75
|
+
if (live === undefined) {
|
|
76
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Models" }), _jsxs(Text, { dimColor: true, children: ["asking ", targets.length, " provider", targets.length === 1 ? '' : 's', " what ", targets.length === 1 ? 'it serves' : 'they serve', "\u2026"] })] }));
|
|
77
|
+
}
|
|
78
|
+
const merged = mergeLiveCandidates(candidates, live);
|
|
79
|
+
const refreshed = Object.keys(live).length;
|
|
80
|
+
return (_jsxs(Box, { flexDirection: "column", children: [refreshed > 0 && (_jsxs(Text, { dimColor: true, children: ["refreshed ", refreshed, " provider", refreshed === 1 ? '' : 's', " from their /models endpoint"] })), failed.length > 0 && (_jsxs(Text, { dimColor: true, children: [failed.join(', '), " did not answer \u2014 showing the harness catalogue"] })), _jsx(MultiSelect, { title: "Models",
|
|
81
|
+
// A harness-imported candidate is already labelled `<gateway>/<id>`, so
|
|
82
|
+
// the id hint repeated it — printing the id twice on one row and
|
|
83
|
+
// wrapping the longer ones past the terminal width. A BYOK candidate is
|
|
84
|
+
// labelled with its display name, where the id is genuinely new
|
|
85
|
+
// information, so the hint is kept there.
|
|
86
|
+
items: merged.map((candidate) => ({
|
|
87
|
+
value: candidate.key,
|
|
88
|
+
label: candidate.label,
|
|
89
|
+
hint: candidate.label.endsWith(candidate.id) ? undefined : candidate.id,
|
|
90
|
+
})), initialSelected: initialSelected, onSubmit: (selected) => onSubmit(selected, live), onBack: onBack, onCancel: onCancel }, "models")] }));
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=models-step.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models-step.js","sourceRoot":"","sources":["../../../src/tui-ink/components/models-step.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAwB,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAA0B,MAAM,iBAAiB,CAAC;AACpG,OAAO,EAAE,WAAW,IAAI,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAgB3E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,UAA6B,EAC7B,eAAuC,EACvC,WAA8C,EAC9C,IAA4B;IAE5B,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAChF,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,IAAI,KAAK,SAAS,IAAI,kBAAkB,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QACjE,OAAO,eAAe,CAAC,OAAO,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC;IAC/E,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,MAAM,EACJ,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,EAC/D,WAAW,GAAG,kBAAkB,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAC7D,GAAG,KAAK,CAAC;IACV,MAAM,OAAO,GAAG,mBAAmB,CAAC,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;IACpF,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAC9B,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CACtC,CAAC;IACF,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAW,EAAE,CAAC,CAAC;IAEnD,6EAA6E;IAC7E,0EAA0E;IAC1E,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACjC,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,KAAK,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YAC7C,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,eAAe,CAAC,OAAO,CAAE,EAAE,IAAI,CAAC,OAAO,CAAE,CAAC,CAAC;YAC3E,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YACnB,IAAI,SAAS;gBAAE,OAAO;YACtB,MAAM,KAAK,GAA6B,EAAE,CAAC;YAC3C,MAAM,QAAQ,GAAa,EAAE,CAAC;YAC9B,KAAK,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;gBACzC,sEAAsE;gBACtE,sEAAsE;gBACtE,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,KAAK,IAAI;oBACnC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;oBAClF,CAAC,CAAC,EAAE,CAAC;gBACP,uEAAuE;gBACvE,oDAAoD;gBACpD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;oBAAE,KAAK,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;;oBAC1C,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC9B,CAAC;YACD,SAAS,CAAC,QAAQ,CAAC,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;IAE7B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,IAAI,IAAC,IAAI,6BAAc,EACxB,MAAC,IAAI,IAAC,QAAQ,8BACJ,OAAO,CAAC,MAAM,eAAW,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,YAAQ,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,cACpH,IACH,CACP,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACxB,SAAS,GAAG,CAAC,IAAI,CAChB,MAAC,IAAI,IAAC,QAAQ,iCACD,SAAS,eAAW,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,oCACpD,CACR,EACA,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CACpB,MAAC,IAAI,IAAC,QAAQ,mBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,4DAAuD,CACzF,EACD,KAAC,WAAW,IAEV,KAAK,EAAC,QAAQ;gBACd,wEAAwE;gBACxE,iEAAiE;gBACjE,wEAAwE;gBACxE,gEAAgE;gBAChE,0CAA0C;gBAC1C,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;oBAChC,KAAK,EAAE,SAAS,CAAC,GAAG;oBACpB,KAAK,EAAE,SAAS,CAAC,KAAK;oBACtB,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE;iBACxE,CAAC,CAAC,EACH,eAAe,EAAE,eAAe,EAChC,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAoB,EAAE,IAAI,CAAC,EAC5D,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,IAfd,QAAQ,CAgBZ,IACE,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function msInitial(itemCount, initialSelected) {
|
|
2
|
+
return {
|
|
3
|
+
cursor: 0,
|
|
4
|
+
selected: new Set([...initialSelected].filter((index) => index >= 0 && index < itemCount)),
|
|
5
|
+
filter: '',
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export function msVisible(labels, filter) {
|
|
9
|
+
const query = filter.toLowerCase();
|
|
10
|
+
return labels.flatMap((label, index) => (query.length === 0 || label.toLowerCase().includes(query) ? [index] : []));
|
|
11
|
+
}
|
|
12
|
+
function cursorFor(state, labels, filter) {
|
|
13
|
+
const rowCount = msVisible(labels, filter).length + 1;
|
|
14
|
+
return Math.min(state.cursor, rowCount - 1);
|
|
15
|
+
}
|
|
16
|
+
export function msReduce(state, action, labels) {
|
|
17
|
+
const visible = msVisible(labels, state.filter);
|
|
18
|
+
const rowCount = visible.length + 1;
|
|
19
|
+
switch (action.kind) {
|
|
20
|
+
case 'up':
|
|
21
|
+
return { ...state, cursor: (state.cursor - 1 + rowCount) % rowCount };
|
|
22
|
+
case 'down':
|
|
23
|
+
return { ...state, cursor: (state.cursor + 1) % rowCount };
|
|
24
|
+
case 'space': {
|
|
25
|
+
const selected = new Set(state.selected);
|
|
26
|
+
if (state.cursor === 0) {
|
|
27
|
+
const allVisibleSelected = visible.length > 0 && visible.every((index) => selected.has(index));
|
|
28
|
+
for (const index of visible) {
|
|
29
|
+
if (allVisibleSelected)
|
|
30
|
+
selected.delete(index);
|
|
31
|
+
else
|
|
32
|
+
selected.add(index);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const index = visible[state.cursor - 1];
|
|
37
|
+
if (index !== undefined) {
|
|
38
|
+
if (selected.has(index))
|
|
39
|
+
selected.delete(index);
|
|
40
|
+
else
|
|
41
|
+
selected.add(index);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { ...state, selected };
|
|
45
|
+
}
|
|
46
|
+
case 'char': {
|
|
47
|
+
const filter = state.filter + action.value;
|
|
48
|
+
return { ...state, filter, cursor: cursorFor(state, labels, filter) };
|
|
49
|
+
}
|
|
50
|
+
case 'backspace': {
|
|
51
|
+
const filter = state.filter.slice(0, -1);
|
|
52
|
+
return { ...state, filter, cursor: cursorFor(state, labels, filter) };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=multi-select-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-select-state.js","sourceRoot":"","sources":["../../../src/tui-ink/components/multi-select-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useReducer } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { msInitial, msReduce, msVisible } from './multi-select-state.js';
|
|
5
|
+
const WINDOW_ROWS = 12;
|
|
6
|
+
function initialIndices(items, initialSelected) {
|
|
7
|
+
if (!initialSelected)
|
|
8
|
+
return new Set();
|
|
9
|
+
return new Set(items.flatMap((item, index) => initialSelected.has(item.value) ? [index] : []));
|
|
10
|
+
}
|
|
11
|
+
export function MultiSelect(props) {
|
|
12
|
+
const { title, items, initialSelected, onSubmit, onBack, onCancel, filterable = true } = props;
|
|
13
|
+
const labels = items.map((item) => item.label);
|
|
14
|
+
const [state, dispatch] = useReducer((current, action) => msReduce(current, action, labels), undefined, () => msInitial(items.length, initialIndices(items, initialSelected)));
|
|
15
|
+
const visible = msVisible(labels, state.filter);
|
|
16
|
+
const allVisibleSelected = visible.length > 0 && visible.every((index) => state.selected.has(index));
|
|
17
|
+
const rowCount = visible.length + 1;
|
|
18
|
+
const start = Math.max(0, Math.min(state.cursor - Math.floor(WINDOW_ROWS / 2), rowCount - WINDOW_ROWS));
|
|
19
|
+
const end = Math.min(rowCount, start + WINDOW_ROWS);
|
|
20
|
+
useInput((input, key) => {
|
|
21
|
+
if (key.escape) {
|
|
22
|
+
onCancel?.();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (key.leftArrow) {
|
|
26
|
+
onBack?.();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (key.return) {
|
|
30
|
+
onSubmit(items.filter((_, index) => state.selected.has(index)).map((item) => item.value));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (key.upArrow || (!filterable && input === 'k')) {
|
|
34
|
+
dispatch({ kind: 'up' });
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (key.downArrow || (!filterable && input === 'j')) {
|
|
38
|
+
dispatch({ kind: 'down' });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (input === ' ') {
|
|
42
|
+
dispatch({ kind: 'space' });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (filterable && key.backspace) {
|
|
46
|
+
dispatch({ kind: 'backspace' });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (filterable && input.length === 1 && /[a-zA-Z0-9._\-\/]/.test(input)) {
|
|
50
|
+
dispatch({ kind: 'char', value: input });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: title }), filterable && _jsxs(Text, { children: ["Filter: ", state.filter] }), _jsxs(Text, { dimColor: true, children: [visible.length, " of ", items.length, " shown \u00B7 ", state.selected.size, " selected"] }), start > 0 && _jsxs(Text, { dimColor: true, children: [" \u2191 ", start, " more"] }), Array.from({ length: end - start }, (_, offset) => {
|
|
54
|
+
const row = start + offset;
|
|
55
|
+
const isToggle = row === 0;
|
|
56
|
+
const index = isToggle ? undefined : visible[row - 1];
|
|
57
|
+
const item = index === undefined ? undefined : items[index];
|
|
58
|
+
const checked = isToggle ? allVisibleSelected : index !== undefined && state.selected.has(index);
|
|
59
|
+
// Naming the count is what makes this row safe to press. It acts on the
|
|
60
|
+
// *filtered* rows, so on an unfiltered 396-model list "Select All"
|
|
61
|
+
// really does mean all 396 — and with a filter active it means only
|
|
62
|
+
// the matches, which the bare label never distinguished.
|
|
63
|
+
const label = isToggle
|
|
64
|
+
? `[ ${allVisibleSelected ? 'Deselect' : 'Select'} all ${visible.length}${state.filter === '' ? '' : ' matching'} ]`
|
|
65
|
+
: item?.label ?? '';
|
|
66
|
+
return (_jsxs(Text, { inverse: row === state.cursor, children: [checked ? '◉' : '○', " ", label, item?.hint ? ` · ${item.hint}` : ''] }, isToggle ? 'toggle' : index));
|
|
67
|
+
}), end < rowCount && _jsxs(Text, { dimColor: true, children: [" \u2193 ", rowCount - end, " more"] }), _jsxs(Text, { dimColor: true, children: ["\u2191\u2193 choose \u00B7 space toggle", filterable ? ' · type to filter' : '', " \u00B7 enter confirm", onBack ? ' · ← back' : '', onCancel ? ' · esc cancel' : ''] })] }));
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=multi-select.js.map
|