pi-advisor-flow 0.11.2-dev.2 → 0.11.3-dev.1
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/CHANGELOG.md +10 -0
- package/README.md +4 -2
- package/dist/index.js +863 -238
- package/package.json +10 -10
- package/src/commands/settings-commands.ts +43 -0
- package/src/commands/settings-persistence.ts +9 -2
- package/src/config/types.ts +6 -1
- package/src/jev/client.ts +88 -22
- package/src/jev/decisions-client.ts +124 -0
- package/src/jev/decisions-response.ts +198 -0
- package/src/jev/key-store.ts +1 -1
- package/src/jev/ledger.ts +28 -6
- package/src/jev/openai-key-store.ts +159 -0
- package/src/jev/transport.ts +103 -25
- package/src/tools/jev-filter.ts +2 -2
- package/src/tools/jev-turn-gate.ts +2 -2
- package/src/ui/jev-setup-submenu-view.ts +96 -0
- package/src/ui/jev-setup-submenu.ts +253 -235
- package/src/ui/jev-setup-support.ts +173 -0
- package/src/ui/settings-items.ts +28 -20
- package/src/ui/settings-mutations.ts +0 -5
- package/src/ui/settings-selector.ts +29 -0
- package/src/ui/types.ts +32 -2
|
@@ -1,131 +1,45 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Key, matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
|
|
1
|
+
import { Key, matchesKey } from "@earendil-works/pi-tui";
|
|
3
2
|
import type { Component, Focusable } from "@earendil-works/pi-tui";
|
|
4
|
-
import { noul } from "@typesafe-ai/sdk";
|
|
5
3
|
|
|
6
|
-
import {
|
|
4
|
+
import type { JevCredentials, JevTransportKind } from "../jev/transport.ts";
|
|
5
|
+
import { redactSecrets } from "../redaction.ts";
|
|
6
|
+
import { renderJevSetup, setupActions } from "./jev-setup-submenu-view.ts";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
|
|
8
|
+
canClearCredential,
|
|
9
|
+
canReplaceWithEnteredKey,
|
|
10
|
+
consumeSetupPlaintextWarning,
|
|
11
|
+
defaultSetupDeps,
|
|
12
|
+
fireAndForget,
|
|
13
|
+
} from "./jev-setup-support.ts";
|
|
14
|
+
import type {
|
|
15
|
+
JevSetupSubmenuOptions,
|
|
16
|
+
JevSetupViewState,
|
|
17
|
+
SetupAction,
|
|
18
|
+
SetupMode,
|
|
19
|
+
} from "./jev-setup-support.ts";
|
|
16
20
|
import { MaskedInput } from "./masked-input.ts";
|
|
17
|
-
import type {
|
|
21
|
+
import type { JevSetupDeps, JevSetupSelection } from "./types.ts";
|
|
18
22
|
|
|
19
|
-
export interface JevSetupSubmenuOptions {
|
|
20
|
-
currentValue: string;
|
|
21
|
-
done: (selectedValue?: string) => void;
|
|
22
|
-
theme: Theme;
|
|
23
|
-
tui: RenderRequester;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface JevSetupDeps {
|
|
27
|
-
clearStoredKey?: () => Promise<JevKeyStoreResult>;
|
|
28
|
-
removePlaintextKey?: () => JevKeyStoreResult;
|
|
29
|
-
resolveTransport?: () => Promise<JevCredentials | undefined>;
|
|
30
|
-
verify?: (credentials: JevCredentials) => Promise<JevFailureLike>;
|
|
31
|
-
writeKey?: (key: string) => Promise<JevKeyStoreResult>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface JevFailureLike {
|
|
35
|
-
message?: string;
|
|
36
|
-
ok: boolean;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
type SetupMode = "menu" | "key-entry" | "verifying";
|
|
40
|
-
type SetupAction =
|
|
41
|
-
| "verify-enable"
|
|
42
|
-
| "verify-again"
|
|
43
|
-
| "disable"
|
|
44
|
-
| "disable-clear"
|
|
45
|
-
| "enter-key"
|
|
46
|
-
| "done";
|
|
47
|
-
|
|
48
|
-
const ACTION_LABELS: Record<SetupAction, string> = {
|
|
49
|
-
disable: "Disable",
|
|
50
|
-
"disable-clear": "Disable and clear stored key",
|
|
51
|
-
done: "Done",
|
|
52
|
-
"enter-key": "Enter a TypeSafe API key",
|
|
53
|
-
"verify-again": "Verify again",
|
|
54
|
-
"verify-enable": "Verify and enable",
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const fireAndForget = async (action: Promise<unknown>): Promise<void> => {
|
|
58
|
-
try {
|
|
59
|
-
await action;
|
|
60
|
-
} catch {
|
|
61
|
-
// Background setup actions surface their own notices.
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const transportLabel = (credentials: JevCredentials): string => {
|
|
66
|
-
if (credentials.transport === "openrouter") {
|
|
67
|
-
return "OpenRouter (reusing pi login)";
|
|
68
|
-
}
|
|
69
|
-
switch (credentials.source) {
|
|
70
|
-
case "advisor-json": {
|
|
71
|
-
return "TypeSafe (key: advisor.json — plaintext, not recommended)";
|
|
72
|
-
}
|
|
73
|
-
case "bun-secrets": {
|
|
74
|
-
return "TypeSafe (key: Bun.secrets)";
|
|
75
|
-
}
|
|
76
|
-
case "file": {
|
|
77
|
-
return "TypeSafe (key: stored file, mode 0600)";
|
|
78
|
-
}
|
|
79
|
-
default: {
|
|
80
|
-
return "TypeSafe (key: TYPESAFE_API_KEY)";
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const defaultVerify = async (credentials: JevCredentials) => {
|
|
86
|
-
const client = jevClientFromCredentials(credentials);
|
|
87
|
-
try {
|
|
88
|
-
await client.ask(
|
|
89
|
-
{ purpose: "pi-advisor setup verification" },
|
|
90
|
-
{ verified: noul("Answer yes.") }
|
|
91
|
-
);
|
|
92
|
-
return { ok: true };
|
|
93
|
-
} catch (error) {
|
|
94
|
-
return {
|
|
95
|
-
message: error instanceof Error ? error.message : String(error),
|
|
96
|
-
ok: false,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
/** Guided Jev credentials setup: detects a reusable login, verifies live
|
|
102
|
-
* before enabling, and never echoes an entered key back. */
|
|
103
23
|
export class JevSetupSubmenu implements Component, Focusable {
|
|
104
24
|
private readonly options: JevSetupSubmenuOptions;
|
|
105
|
-
private readonly deps: JevSetupDeps
|
|
106
|
-
private
|
|
25
|
+
private readonly deps: Required<JevSetupDeps>;
|
|
26
|
+
private maskedInput: MaskedInput;
|
|
107
27
|
private credentials: JevCredentials | undefined;
|
|
28
|
+
private selectedTransport: JevTransportKind | undefined;
|
|
108
29
|
private mode: SetupMode = "menu";
|
|
109
30
|
private notice: string | undefined;
|
|
110
31
|
private selectedIndex = 0;
|
|
32
|
+
private filterEnabled: boolean;
|
|
33
|
+
private canEnterKey = false;
|
|
34
|
+
private pendingSelectionRefresh = false;
|
|
111
35
|
private _focused = true;
|
|
112
36
|
|
|
113
37
|
constructor(options: JevSetupSubmenuOptions, deps: JevSetupDeps = {}) {
|
|
114
38
|
this.options = options;
|
|
115
|
-
this.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
resolveTransport: deps.resolveTransport ?? (() => resolveJevTransport()),
|
|
120
|
-
verify: deps.verify ?? defaultVerify,
|
|
121
|
-
writeKey: deps.writeKey ?? writeKeyTypeSafeKey,
|
|
122
|
-
};
|
|
123
|
-
this.maskedInput = new MaskedInput({
|
|
124
|
-
onEscape: () => this.options.done(),
|
|
125
|
-
onSubmit: (value) => this.submitEnteredKey(value),
|
|
126
|
-
placeholder: "Paste a TypeSafe API key",
|
|
127
|
-
});
|
|
128
|
-
void fireAndForget(this.refresh());
|
|
39
|
+
this.filterEnabled = options.currentValue === "On";
|
|
40
|
+
this.deps = { ...defaultSetupDeps, ...options.setupDeps, ...deps };
|
|
41
|
+
this.maskedInput = this.createMaskedInput("Paste a TypeSafe API key");
|
|
42
|
+
void fireAndForget(this.refresh(), this.reportBackgroundError);
|
|
129
43
|
}
|
|
130
44
|
|
|
131
45
|
get focused(): boolean {
|
|
@@ -150,7 +64,7 @@ export class JevSetupSubmenu implements Component, Focusable {
|
|
|
150
64
|
if (this.mode !== "menu") {
|
|
151
65
|
return;
|
|
152
66
|
}
|
|
153
|
-
const actions = this.
|
|
67
|
+
const actions = setupActions(this.viewState());
|
|
154
68
|
if (
|
|
155
69
|
matchesKey(keyData, Key.down) ||
|
|
156
70
|
keyData === "\u001B[B" ||
|
|
@@ -173,184 +87,288 @@ export class JevSetupSubmenu implements Component, Focusable {
|
|
|
173
87
|
}
|
|
174
88
|
|
|
175
89
|
render(width: number): string[] {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (this.credentials) {
|
|
183
|
-
lines.push(
|
|
184
|
-
` Transport: ${transportLabel(this.credentials)}`,
|
|
185
|
-
` Filter: ${enabled ? "On" : "Off"}`
|
|
186
|
-
);
|
|
187
|
-
} else if (this.mode === "verifying") {
|
|
188
|
-
lines.push(" Checking available Jev credentials…");
|
|
189
|
-
} else {
|
|
190
|
-
lines.push(
|
|
191
|
-
" No Jev credentials found. Enter a TypeSafe API key below, or add",
|
|
192
|
-
" an OpenRouter login in pi; it is reused automatically."
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
if (this.notice) {
|
|
196
|
-
lines.push("", theme.fg("warning", ` ${this.notice}`));
|
|
197
|
-
}
|
|
198
|
-
lines.push("");
|
|
199
|
-
if (this.mode === "verifying") {
|
|
200
|
-
lines.push(" Verifying with a live Jev call…");
|
|
201
|
-
} else if (this.mode === "key-entry") {
|
|
202
|
-
lines.push(
|
|
203
|
-
` ${this.maskedInput.render(Math.max(10, width - 4))[0] ?? ""}`,
|
|
204
|
-
theme.fg("dim", " Enter: verify · Esc: cancel")
|
|
205
|
-
);
|
|
206
|
-
} else {
|
|
207
|
-
for (const [index, label] of this.labels().entries()) {
|
|
208
|
-
const prefix = index === this.selectedIndex ? "→ " : " ";
|
|
209
|
-
lines.push(`${prefix}${label}`);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return lines.map((line) => truncateToWidth(line, width));
|
|
90
|
+
return renderJevSetup(
|
|
91
|
+
this.options,
|
|
92
|
+
this.viewState(),
|
|
93
|
+
this.maskedInput,
|
|
94
|
+
width
|
|
95
|
+
);
|
|
213
96
|
}
|
|
214
97
|
|
|
215
|
-
private
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this.credentials.source === "bun-secrets" ||
|
|
226
|
-
this.credentials.source === "file"
|
|
227
|
-
) {
|
|
228
|
-
actions.push("disable-clear");
|
|
229
|
-
}
|
|
230
|
-
actions.push("done");
|
|
231
|
-
return actions;
|
|
98
|
+
private viewState(): JevSetupViewState {
|
|
99
|
+
return {
|
|
100
|
+
canEnterKey: this.canEnterKey,
|
|
101
|
+
credentials: this.credentials,
|
|
102
|
+
filterEnabled: this.filterEnabled,
|
|
103
|
+
mode: this.mode,
|
|
104
|
+
notice: this.notice,
|
|
105
|
+
selectedIndex: this.selectedIndex,
|
|
106
|
+
selectedTransport: this.selectedTransport,
|
|
107
|
+
};
|
|
232
108
|
}
|
|
233
109
|
|
|
234
|
-
private
|
|
235
|
-
return
|
|
110
|
+
private createMaskedInput(placeholder: string): MaskedInput {
|
|
111
|
+
return new MaskedInput({
|
|
112
|
+
onEscape: () => this.cancelKeyEntry(),
|
|
113
|
+
onSubmit: (value) => this.submitEnteredKey(value),
|
|
114
|
+
placeholder,
|
|
115
|
+
});
|
|
236
116
|
}
|
|
237
117
|
|
|
238
118
|
private async refresh(): Promise<void> {
|
|
239
|
-
|
|
240
|
-
this.
|
|
241
|
-
|
|
119
|
+
this.mode = "resolving";
|
|
120
|
+
this.options.tui.requestRender();
|
|
121
|
+
try {
|
|
122
|
+
const credentials = await this.deps.resolveTransport(
|
|
123
|
+
this.options.currentTransport === "auto"
|
|
124
|
+
? undefined
|
|
125
|
+
: this.options.currentTransport
|
|
126
|
+
);
|
|
127
|
+
this.credentials = credentials;
|
|
128
|
+
this.selectedTransport = credentials?.transport;
|
|
129
|
+
this.mode = "menu";
|
|
130
|
+
this.selectedIndex = 0;
|
|
131
|
+
if (credentials?.source === "advisor-json") {
|
|
132
|
+
this.notice = consumeSetupPlaintextWarning();
|
|
133
|
+
}
|
|
242
134
|
this.options.tui.requestRender();
|
|
135
|
+
} catch (error) {
|
|
136
|
+
this.reportBackgroundError(
|
|
137
|
+
error instanceof Error ? error.message : String(error)
|
|
138
|
+
);
|
|
243
139
|
}
|
|
244
|
-
this.credentials = await this.deps.resolveTransport?.();
|
|
245
|
-
this.mode = "menu";
|
|
246
|
-
this.selectedIndex = 0;
|
|
247
|
-
if (this.credentials?.source === "advisor-json") {
|
|
248
|
-
this.notice ??= consumePlaintextKeyWarning();
|
|
249
|
-
}
|
|
250
|
-
this.options.tui.requestRender();
|
|
251
140
|
}
|
|
252
141
|
|
|
253
142
|
private activate(action: SetupAction): void {
|
|
254
143
|
if (action === "done") {
|
|
255
|
-
this.
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
if (action === "enter-key") {
|
|
259
|
-
this.mode = "key-entry";
|
|
144
|
+
this.closeSetup();
|
|
260
145
|
return;
|
|
261
146
|
}
|
|
262
147
|
if (action === "disable") {
|
|
263
148
|
this.notice = undefined;
|
|
264
|
-
|
|
149
|
+
if (
|
|
150
|
+
this.commitSelection(
|
|
151
|
+
{
|
|
152
|
+
enabled: false,
|
|
153
|
+
transport: this.options.currentTransport,
|
|
154
|
+
},
|
|
155
|
+
"Off"
|
|
156
|
+
)
|
|
157
|
+
) {
|
|
158
|
+
this.filterEnabled = false;
|
|
159
|
+
}
|
|
265
160
|
return;
|
|
266
161
|
}
|
|
267
|
-
if (action === "
|
|
268
|
-
void fireAndForget(this.disableAndClear());
|
|
162
|
+
if (action === "clear-stored-key") {
|
|
163
|
+
void fireAndForget(this.disableAndClear(), this.reportBackgroundError);
|
|
269
164
|
return;
|
|
270
165
|
}
|
|
271
|
-
if (action === "
|
|
272
|
-
|
|
166
|
+
if (action === "enter-key") {
|
|
167
|
+
if (this.selectedTransport) {
|
|
168
|
+
this.openKeyEntry(this.selectedTransport);
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
273
171
|
}
|
|
172
|
+
void fireAndForget(this.selectProvider(action), this.reportBackgroundError);
|
|
274
173
|
}
|
|
275
174
|
|
|
276
|
-
private
|
|
277
|
-
|
|
278
|
-
|
|
175
|
+
private reportBackgroundError = (message: string): void => {
|
|
176
|
+
this.mode = "menu";
|
|
177
|
+
this.notice = `Setup failed: ${redactSecrets(message)}`;
|
|
178
|
+
this.options.tui.requestRender();
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
private async selectProvider(transport: JevTransportKind): Promise<void> {
|
|
182
|
+
this.selectedTransport = transport;
|
|
183
|
+
this.mode = "resolving";
|
|
184
|
+
this.notice = undefined;
|
|
185
|
+
this.canEnterKey = false;
|
|
186
|
+
this.options.tui.requestRender();
|
|
187
|
+
let credentials: JevCredentials | undefined;
|
|
188
|
+
try {
|
|
189
|
+
credentials = await this.deps.resolveTransport(transport);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
this.mode = "menu";
|
|
192
|
+
this.notice = `Credential lookup failed: ${redactSecrets(error instanceof Error ? error.message : String(error))}`;
|
|
193
|
+
this.options.tui.requestRender();
|
|
279
194
|
return;
|
|
280
195
|
}
|
|
281
|
-
|
|
282
|
-
this.
|
|
283
|
-
if (!
|
|
196
|
+
this.credentials = credentials;
|
|
197
|
+
this.mode = "menu";
|
|
198
|
+
if (!credentials) {
|
|
199
|
+
if (transport === "typesafe" || transport === "openai-decisions") {
|
|
200
|
+
this.notice = undefined;
|
|
201
|
+
this.openKeyEntry(transport);
|
|
202
|
+
} else {
|
|
203
|
+
this.notice = "No OpenRouter Pi login found. Add one in Pi and retry.";
|
|
204
|
+
}
|
|
284
205
|
this.options.tui.requestRender();
|
|
285
206
|
return;
|
|
286
207
|
}
|
|
287
|
-
|
|
288
|
-
|
|
208
|
+
if (credentials.transport !== transport) {
|
|
209
|
+
this.notice =
|
|
210
|
+
"The selected provider did not resolve matching credentials.";
|
|
211
|
+
this.options.tui.requestRender();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
await this.verifyAndEnable(credentials);
|
|
289
215
|
}
|
|
290
216
|
|
|
291
|
-
private
|
|
292
|
-
|
|
293
|
-
|
|
217
|
+
private openKeyEntry(transport: JevTransportKind): void {
|
|
218
|
+
this.selectedTransport = transport;
|
|
219
|
+
this.mode = "key-entry";
|
|
220
|
+
const placeholder =
|
|
221
|
+
transport === "openai-decisions"
|
|
222
|
+
? "Paste an OpenAI Platform API key"
|
|
223
|
+
: "Paste a TypeSafe API key";
|
|
224
|
+
this.maskedInput = this.createMaskedInput(placeholder);
|
|
225
|
+
this.maskedInput.focused = this._focused;
|
|
226
|
+
this.options.tui.requestRender();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private cancelKeyEntry(): void {
|
|
230
|
+
this.mode = "menu";
|
|
231
|
+
this.selectedTransport = undefined;
|
|
232
|
+
this.maskedInput.setValue("");
|
|
233
|
+
this.options.tui.requestRender();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private async submitEnteredKey(value: string): Promise<void> {
|
|
237
|
+
const transport = this.selectedTransport;
|
|
238
|
+
const key = value.trim();
|
|
239
|
+
this.maskedInput.setValue("");
|
|
240
|
+
if (!transport || !key) {
|
|
294
241
|
return;
|
|
295
242
|
}
|
|
243
|
+
await this.verifyAndEnable({ apiKey: key, transport }, key);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private async verifyAndEnable(
|
|
247
|
+
credentials: JevCredentials,
|
|
248
|
+
enteredKey?: string
|
|
249
|
+
): Promise<void> {
|
|
296
250
|
this.mode = "verifying";
|
|
297
251
|
this.notice = undefined;
|
|
298
252
|
this.options.tui.requestRender();
|
|
299
253
|
const outcome = await this.deps.verify(credentials);
|
|
300
|
-
this.mode = "menu";
|
|
301
254
|
if (!outcome.ok) {
|
|
302
255
|
this.notice = `Verification failed: ${outcome.message ?? "unknown error"}`;
|
|
256
|
+
this.mode = enteredKey ? "key-entry" : "menu";
|
|
257
|
+
this.canEnterKey = !enteredKey && canReplaceWithEnteredKey(credentials);
|
|
303
258
|
this.options.tui.requestRender();
|
|
304
259
|
return;
|
|
305
260
|
}
|
|
306
|
-
|
|
307
|
-
|
|
261
|
+
const keyToStore =
|
|
262
|
+
enteredKey ??
|
|
263
|
+
(credentials.transport === "typesafe" &&
|
|
308
264
|
credentials.source === "advisor-json"
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
265
|
+
? credentials.apiKey
|
|
266
|
+
: undefined);
|
|
267
|
+
if (keyToStore) {
|
|
268
|
+
const stored = await this.deps.writeKey(
|
|
269
|
+
keyToStore,
|
|
270
|
+
credentials.transport
|
|
271
|
+
);
|
|
272
|
+
if (!stored.ok) {
|
|
273
|
+
this.notice = stored.message;
|
|
274
|
+
this.mode = "key-entry";
|
|
275
|
+
this.canEnterKey = false;
|
|
315
276
|
this.options.tui.requestRender();
|
|
316
277
|
return;
|
|
317
278
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
279
|
+
if (
|
|
280
|
+
credentials.transport === "typesafe" &&
|
|
281
|
+
credentials.source === "advisor-json"
|
|
282
|
+
) {
|
|
283
|
+
const removed = this.deps.removePlaintextKey();
|
|
284
|
+
this.notice = removed.ok
|
|
285
|
+
? "Key moved from advisor.json into the secure store."
|
|
286
|
+
: `Stored securely, but ${removed.message}`;
|
|
287
|
+
} else {
|
|
288
|
+
this.notice = `${stored.message} Verification succeeded.`;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
this.credentials = credentials;
|
|
292
|
+
const saved = this.commitSelection(
|
|
293
|
+
{ enabled: true, transport: credentials.transport },
|
|
294
|
+
"On"
|
|
295
|
+
);
|
|
296
|
+
if (!saved) {
|
|
297
|
+
this.mode = "menu";
|
|
298
|
+
this.notice ??= "Provider verified, but settings could not be saved.";
|
|
299
|
+
this.options.tui.requestRender();
|
|
300
|
+
return;
|
|
322
301
|
}
|
|
323
|
-
this.
|
|
302
|
+
this.filterEnabled = true;
|
|
303
|
+
this.mode = "menu";
|
|
304
|
+
this.canEnterKey = false;
|
|
305
|
+
this.options.tui.requestRender();
|
|
324
306
|
}
|
|
325
307
|
|
|
326
|
-
private
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
308
|
+
private commitSelection(
|
|
309
|
+
selection: JevSetupSelection,
|
|
310
|
+
legacyValue: string
|
|
311
|
+
): boolean {
|
|
312
|
+
if (this.options.onSelection) {
|
|
313
|
+
if (!this.options.onSelection(selection)) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
this.closeSetup(true);
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
this.options.done(legacyValue);
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private closeSetup(refreshSettings = false): void {
|
|
324
|
+
this.options.done();
|
|
325
|
+
if (refreshSettings || this.pendingSelectionRefresh) {
|
|
326
|
+
this.options.afterSelection?.();
|
|
330
327
|
}
|
|
331
|
-
|
|
328
|
+
this.pendingSelectionRefresh = false;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private async disableAndClear(): Promise<void> {
|
|
332
|
+
const { credentials } = this;
|
|
333
|
+
if (!(credentials && canClearCredential(credentials))) {
|
|
332
334
|
return;
|
|
333
335
|
}
|
|
334
|
-
this.
|
|
336
|
+
if (this.options.onSelection) {
|
|
337
|
+
const saved = this.options.onSelection({
|
|
338
|
+
enabled: false,
|
|
339
|
+
transport: this.options.currentTransport,
|
|
340
|
+
});
|
|
341
|
+
if (!saved) {
|
|
342
|
+
this.mode = "menu";
|
|
343
|
+
this.notice =
|
|
344
|
+
"Could not save the disabled state; the key was not cleared.";
|
|
345
|
+
this.options.tui.requestRender();
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
this.filterEnabled = false;
|
|
349
|
+
this.pendingSelectionRefresh = true;
|
|
350
|
+
}
|
|
351
|
+
this.mode = "clearing";
|
|
352
|
+
this.notice = undefined;
|
|
335
353
|
this.options.tui.requestRender();
|
|
336
|
-
const
|
|
337
|
-
apiKey: key,
|
|
338
|
-
transport: "typesafe",
|
|
339
|
-
});
|
|
354
|
+
const result = await this.deps.clearStoredKey(credentials.transport);
|
|
340
355
|
this.mode = "menu";
|
|
341
|
-
|
|
342
|
-
|
|
356
|
+
this.notice = result.message;
|
|
357
|
+
if (!result.ok) {
|
|
343
358
|
this.options.tui.requestRender();
|
|
344
359
|
return;
|
|
345
360
|
}
|
|
346
|
-
|
|
347
|
-
if (
|
|
348
|
-
this.
|
|
349
|
-
|
|
350
|
-
|
|
361
|
+
this.credentials = undefined;
|
|
362
|
+
if (this.options.onSelection) {
|
|
363
|
+
this.closeSetup();
|
|
364
|
+
} else if (
|
|
365
|
+
this.commitSelection(
|
|
366
|
+
{ enabled: false, transport: this.options.currentTransport },
|
|
367
|
+
"Off"
|
|
368
|
+
)
|
|
369
|
+
) {
|
|
370
|
+
this.filterEnabled = false;
|
|
351
371
|
}
|
|
352
|
-
this.
|
|
353
|
-
await this.refresh();
|
|
354
|
-
this.options.done("On");
|
|
372
|
+
this.options.tui.requestRender();
|
|
355
373
|
}
|
|
356
374
|
}
|