@oh-my-pi/pi-tui 18.2.11 → 18.3.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/CHANGELOG.md +20 -0
- package/THIRD-PARTY-NOTICES.txt +1 -1
- package/dist/types/apps/ps-data.d.ts +1 -1
- package/dist/types/apps/ps-top.d.ts +1 -1
- package/dist/types/chat/chat-transcript-builder.d.ts +1 -1
- package/dist/types/overlays/annotation-overlay.d.ts +30 -0
- package/dist/types/overlays/annotation-types.d.ts +84 -0
- package/dist/types/overlays/copy-selector.d.ts +30 -2
- package/dist/types/overlays/copy-targets.d.ts +2 -0
- package/dist/types/overlays/model-hub.d.ts +2 -2
- package/dist/types/overlays/oauth-selector.d.ts +3 -6
- package/dist/types/overlays/usage-dashboard.d.ts +2 -0
- package/dist/types/prompt/image-source.d.ts +9 -8
- package/dist/types/status-line/context-usage.d.ts +3 -1
- package/dist/types/theme/mermaid-cache.d.ts +4 -6
- package/dist/types/tools/bash.d.ts +7 -0
- package/dist/types/tools/daemon.d.ts +46 -0
- package/dist/types/tools/irc.d.ts +30 -0
- package/dist/types/tools/line-ranges.d.ts +1 -1
- package/dist/types/tools/proc-render.d.ts +34 -0
- package/dist/types/tools/read.d.ts +4 -1
- package/dist/types/tools/wait.d.ts +131 -0
- package/dist/types/tools/write.d.ts +4 -0
- package/dist/types/tools/xd-url.d.ts +8 -0
- package/dist/types/windows-altgr.d.ts +54 -0
- package/package.json +13 -10
- package/src/apps/debug/protocol-probe.ts +1 -1
- package/src/apps/ps-data.ts +1 -1
- package/src/apps/ps-top.ts +1 -1
- package/src/chat/chat-transcript-builder.ts +4 -4
- package/src/chat/tool-execution.ts +8 -8
- package/src/chat/transcript-render-helpers.ts +3 -3
- package/src/components/markdown.ts +1 -1
- package/src/desktop-notify.ts +1 -1
- package/src/overlays/annotation-overlay.ts +1145 -0
- package/src/overlays/annotation-types.ts +78 -0
- package/src/overlays/copy-selector.ts +45 -19
- package/src/overlays/copy-targets.ts +3 -3
- package/src/overlays/model-hub.ts +5 -4
- package/src/overlays/oauth-selector.ts +8 -12
- package/src/overlays/plan-review-overlay.ts +213 -40
- package/src/overlays/usage-dashboard.ts +22 -1
- package/src/prompt/image-source.ts +9 -8
- package/src/setup/scenes/sign-in.ts +1 -1
- package/src/status-line/context-usage.ts +15 -3
- package/src/terminal-capabilities.ts +2 -2
- package/src/terminal.ts +8 -1
- package/src/theme/mermaid-cache.ts +83 -25
- package/src/tools/bash.ts +14 -1
- package/src/tools/daemon.ts +51 -0
- package/src/tools/index.ts +2 -2
- package/src/tools/irc.ts +33 -0
- package/src/tools/line-ranges.ts +13 -3
- package/src/tools/proc-render.ts +294 -0
- package/src/tools/read.ts +22 -1
- package/src/tools/wait.ts +595 -0
- package/src/tools/write.ts +76 -0
- package/src/tools/xd-url.ts +9 -0
- package/src/windows-altgr.ts +227 -0
- package/dist/types/tools/hub.d.ts +0 -342
- package/src/tools/hub.ts +0 -1492
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export type ReviewSourceRowKind = "context" | "added" | "removed";
|
|
2
|
+
|
|
3
|
+
export interface ReviewSourceRow {
|
|
4
|
+
kind: ReviewSourceRowKind;
|
|
5
|
+
raw: string;
|
|
6
|
+
content: string;
|
|
7
|
+
oldLine?: number;
|
|
8
|
+
newLine?: number;
|
|
9
|
+
hunkHeader: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ReviewDiffRow =
|
|
13
|
+
| ReviewSourceRow
|
|
14
|
+
| { kind: "hunk"; raw: string; hunkHeader: string }
|
|
15
|
+
| { kind: "no-newline"; raw: string; hunkHeader: string };
|
|
16
|
+
|
|
17
|
+
export interface ReviewDiffFile {
|
|
18
|
+
path: string;
|
|
19
|
+
oldPath?: string;
|
|
20
|
+
newPath?: string;
|
|
21
|
+
occurrence: number;
|
|
22
|
+
rawDiff: string;
|
|
23
|
+
rows: ReviewDiffRow[];
|
|
24
|
+
linesAdded: number;
|
|
25
|
+
linesRemoved: number;
|
|
26
|
+
isBinary: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface CodeReviewAnnotationBase {
|
|
30
|
+
path: string;
|
|
31
|
+
oldPath?: string;
|
|
32
|
+
newPath?: string;
|
|
33
|
+
occurrence: number;
|
|
34
|
+
note: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type CodeReviewAnnotation =
|
|
38
|
+
| (CodeReviewAnnotationBase & {
|
|
39
|
+
scope: "file";
|
|
40
|
+
})
|
|
41
|
+
| (CodeReviewAnnotationBase & {
|
|
42
|
+
scope: "line";
|
|
43
|
+
hunkHeader: string;
|
|
44
|
+
oldLine?: number;
|
|
45
|
+
newLine?: number;
|
|
46
|
+
rawLine: string;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export interface CodeReviewOverlayResult {
|
|
50
|
+
action: "review" | "paste";
|
|
51
|
+
annotations: CodeReviewAnnotation[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type TextReviewSourceProvenance =
|
|
55
|
+
| { kind: "latest-assistant"; entryId: string }
|
|
56
|
+
| { kind: "session"; entryId: string }
|
|
57
|
+
| { kind: "file"; path: string }
|
|
58
|
+
| { kind: "prompt" };
|
|
59
|
+
|
|
60
|
+
export interface TextReviewSource {
|
|
61
|
+
/** Identifies the source within this annotation run. */
|
|
62
|
+
id: string;
|
|
63
|
+
kind: "message" | "code" | "quote" | "command" | "file" | "prompt";
|
|
64
|
+
label: string;
|
|
65
|
+
text: string;
|
|
66
|
+
/** Identifies the active session entry that supplied this source, when applicable. */
|
|
67
|
+
provenance?: TextReviewSourceProvenance;
|
|
68
|
+
sessionId?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type TextReviewAnnotation =
|
|
72
|
+
| { scope: "text"; note: string }
|
|
73
|
+
| { scope: "line"; line: number; quote: string; note: string };
|
|
74
|
+
|
|
75
|
+
export interface TextReviewOverlayResult {
|
|
76
|
+
action: "paste";
|
|
77
|
+
annotations: TextReviewAnnotation[];
|
|
78
|
+
}
|
|
@@ -49,19 +49,37 @@ export interface CopySelectorDeps {
|
|
|
49
49
|
proseOnlyThinking?: () => boolean;
|
|
50
50
|
linkTargets?: ReadonlyMap<string, string>;
|
|
51
51
|
requestRender: () => void;
|
|
52
|
-
/**
|
|
53
|
-
|
|
52
|
+
/** Replaces the "Copy" header when the picker is reused for another purpose. */
|
|
53
|
+
title?: string;
|
|
54
|
+
/** Verb shown for the pick action in hints and block controls (default "copy"). */
|
|
55
|
+
actionLabel?: string;
|
|
56
|
+
/**
|
|
57
|
+
* The outlined content was chosen — copy it. `label` feeds the status line;
|
|
58
|
+
* `source` names the transcript entry (and inner block, when descended) it came from.
|
|
59
|
+
*/
|
|
60
|
+
onPick: (content: string, label: string, source: CopyPickSource) => void;
|
|
54
61
|
/** `o` on a link block — open `href` with the system opener. Absent: `o` is ignored. */
|
|
55
62
|
onOpen?: (href: string, label: string) => void;
|
|
56
63
|
onCancel: () => void;
|
|
57
64
|
}
|
|
58
65
|
|
|
66
|
+
/** Where picked content came from in the transcript. */
|
|
67
|
+
export interface CopyPickSource {
|
|
68
|
+
entry: TranscriptEntry;
|
|
69
|
+
/** The inner block, when the pick happened in the descended block view. */
|
|
70
|
+
block?: CopyBlock;
|
|
71
|
+
}
|
|
72
|
+
|
|
59
73
|
/** One copyable inner block of a transcript turn. */
|
|
60
|
-
interface CopyBlock {
|
|
74
|
+
export interface CopyBlock {
|
|
61
75
|
/** Short kind label ("code · ts", "bash command", "read result", …). */
|
|
62
76
|
label: string;
|
|
63
77
|
/** Exact text placed on the clipboard. */
|
|
64
78
|
content: string;
|
|
79
|
+
/** Transcript entry that produced this block. */
|
|
80
|
+
entry: TranscriptEntry;
|
|
81
|
+
/** Markdown code/quote block, or a bash/eval tool-call command. */
|
|
82
|
+
kind?: "code" | "quote" | "command";
|
|
65
83
|
/** Highlight language for the block preview. */
|
|
66
84
|
language?: string;
|
|
67
85
|
/** Set for link blocks: the URL `o` opens. `content` is the same URL. */
|
|
@@ -245,13 +263,13 @@ export class CopySelectorComponent implements Component {
|
|
|
245
263
|
if (matchesKey(data, "enter") || matchesKey(data, "return") || data === "\n") {
|
|
246
264
|
if (this.#blocks) {
|
|
247
265
|
const block = this.#blocks[this.#blockSelected];
|
|
248
|
-
if (block) this.deps.onPick(block.content, block.label);
|
|
266
|
+
if (block) this.deps.onPick(block.content, block.label, { entry: block.entry, block });
|
|
249
267
|
return;
|
|
250
268
|
}
|
|
251
269
|
const target = this.#targets[this.#selected];
|
|
252
270
|
if (!target) return;
|
|
253
271
|
const item = targetCopy(target, this.#blocksFor(target));
|
|
254
|
-
this.deps.onPick(item.content, item.label);
|
|
272
|
+
this.deps.onPick(item.content, item.label, { entry: target.entries[0]! });
|
|
255
273
|
return;
|
|
256
274
|
}
|
|
257
275
|
// Page/home/end/shift+arrow scrolling without moving the selection.
|
|
@@ -283,7 +301,7 @@ export class CopySelectorComponent implements Component {
|
|
|
283
301
|
if (block.href && this.deps.onOpen) this.deps.onOpen(block.href, block.label);
|
|
284
302
|
return;
|
|
285
303
|
}
|
|
286
|
-
this.deps.onPick(block.content, block.label);
|
|
304
|
+
this.deps.onPick(block.content, block.label, { entry: block.entry, block });
|
|
287
305
|
}
|
|
288
306
|
|
|
289
307
|
#moveVertical(delta: -1 | 1): void {
|
|
@@ -363,9 +381,10 @@ export class CopySelectorComponent implements Component {
|
|
|
363
381
|
|
|
364
382
|
const selectedBlock = this.#blocks?.[this.#blockSelected];
|
|
365
383
|
const openHint = selectedBlock?.href && this.deps.onOpen ? " o open" : "";
|
|
384
|
+
const action = this.deps.actionLabel ?? "copy";
|
|
366
385
|
const hint = this.#blocks
|
|
367
|
-
? `${this.#blockSelected + 1}/${this.#blocks.length} ↑/↓ block ←/esc back enter
|
|
368
|
-
: `${this.#targets.length > 0 ? `${this.#selected + 1}/${this.#targets.length} ` : ""}↑/↓ step ${blocks.length > 0 ? "→ blocks " : ""}enter
|
|
386
|
+
? `${this.#blockSelected + 1}/${this.#blocks.length} ↑/↓ block ←/esc back enter ${action}${openHint} click ${theme.cmd.copy}/${theme.cmd.share}`
|
|
387
|
+
: `${this.#targets.length > 0 ? `${this.#selected + 1}/${this.#targets.length} ` : ""}↑/↓ step ${blocks.length > 0 ? "→ blocks " : ""}enter ${action} ${this.#truncated ? "a earlier turns " : ""}ctrl+o expand esc close`;
|
|
369
388
|
const anchorId = target
|
|
370
389
|
? this.#blocks
|
|
371
390
|
? `copy:${target.turnId}:block:${this.#blockSelected}`
|
|
@@ -373,7 +392,9 @@ export class CopySelectorComponent implements Component {
|
|
|
373
392
|
: undefined;
|
|
374
393
|
return {
|
|
375
394
|
header: [
|
|
376
|
-
|
|
395
|
+
this.deps.title
|
|
396
|
+
? theme.bold(this.deps.title)
|
|
397
|
+
: `${theme.cmd.copy} ${theme.bold("Copy")}${theme.sep.dot}${theme.fg("dim", "pick what to put on the clipboard")}`,
|
|
377
398
|
],
|
|
378
399
|
body: {
|
|
379
400
|
lines: composed.lines,
|
|
@@ -409,7 +430,7 @@ export class CopySelectorComponent implements Component {
|
|
|
409
430
|
const selected = index === this.#blockSelected;
|
|
410
431
|
const captionColor: ThemeColor = selected ? OUTLINE_COLOR : "dim";
|
|
411
432
|
const controls: Array<{ action: ControlRegion["action"]; text: string }> = [
|
|
412
|
-
{ action: "copy", text: `${theme.cmd.copy} copy` },
|
|
433
|
+
{ action: "copy", text: `${theme.cmd.copy} ${this.deps.actionLabel ?? "copy"}` },
|
|
413
434
|
];
|
|
414
435
|
if (block.href && this.deps.onOpen) controls.push({ action: "open", text: `${theme.cmd.share} open` });
|
|
415
436
|
const controlsWidth = controls.reduce((sum, control) => sum + visibleWidth(control.text) + 2, 0);
|
|
@@ -497,16 +518,18 @@ function toolResultText(message: Extract<SessionMessageEntry["message"], { role:
|
|
|
497
518
|
.trim();
|
|
498
519
|
}
|
|
499
520
|
|
|
500
|
-
function pushMarkdownBlocks(blocks: CopyBlock[], text: string): void {
|
|
521
|
+
function pushMarkdownBlocks(blocks: CopyBlock[], text: string, entry: TranscriptEntry): void {
|
|
501
522
|
for (const block of extractBlocks(text)) {
|
|
502
523
|
if (block.kind === "code") {
|
|
503
524
|
blocks.push({
|
|
504
525
|
label: block.lang ? `${block.lang} code` : "code",
|
|
505
526
|
content: block.code,
|
|
527
|
+
entry,
|
|
528
|
+
kind: "code",
|
|
506
529
|
language: block.lang || undefined,
|
|
507
530
|
});
|
|
508
531
|
} else {
|
|
509
|
-
blocks.push({ label: "quote", content: block.text });
|
|
532
|
+
blocks.push({ label: "quote", content: block.text, entry, kind: "quote" });
|
|
510
533
|
}
|
|
511
534
|
}
|
|
512
535
|
// Links follow the message's blocks. The preview shows the whole URL on one
|
|
@@ -515,6 +538,7 @@ function pushMarkdownBlocks(blocks: CopyBlock[], text: string): void {
|
|
|
515
538
|
blocks.push({
|
|
516
539
|
label: link.text !== link.href ? `link${theme.sep.dot}${link.text}` : "link",
|
|
517
540
|
content: link.href,
|
|
541
|
+
entry,
|
|
518
542
|
href: link.href,
|
|
519
543
|
});
|
|
520
544
|
}
|
|
@@ -528,10 +552,10 @@ function collectBlocks(entries: readonly TranscriptEntry[]): CopyBlock[] {
|
|
|
528
552
|
if (!message) continue;
|
|
529
553
|
switch (message.role) {
|
|
530
554
|
case "user":
|
|
531
|
-
pushMarkdownBlocks(blocks, rawUserText(message));
|
|
555
|
+
pushMarkdownBlocks(blocks, rawUserText(message), entry);
|
|
532
556
|
break;
|
|
533
557
|
case "assistant": {
|
|
534
|
-
pushMarkdownBlocks(blocks, assistantVisibleText(message));
|
|
558
|
+
pushMarkdownBlocks(blocks, assistantVisibleText(message), entry);
|
|
535
559
|
for (const content of message.content) {
|
|
536
560
|
if (content.type !== "toolCall") continue;
|
|
537
561
|
const command = commandFromToolCall(content);
|
|
@@ -539,6 +563,8 @@ function collectBlocks(entries: readonly TranscriptEntry[]): CopyBlock[] {
|
|
|
539
563
|
blocks.push({
|
|
540
564
|
label: command.kind === "bash" ? "bash command" : "eval code",
|
|
541
565
|
content: command.code,
|
|
566
|
+
entry,
|
|
567
|
+
kind: "command",
|
|
542
568
|
language: command.language,
|
|
543
569
|
});
|
|
544
570
|
}
|
|
@@ -547,16 +573,16 @@ function collectBlocks(entries: readonly TranscriptEntry[]): CopyBlock[] {
|
|
|
547
573
|
}
|
|
548
574
|
case "toolResult": {
|
|
549
575
|
const text = toolResultText(message);
|
|
550
|
-
if (text) blocks.push({ label: `${message.toolName} result`, content: text });
|
|
576
|
+
if (text) blocks.push({ label: `${message.toolName} result`, content: text, entry });
|
|
551
577
|
break;
|
|
552
578
|
}
|
|
553
579
|
case "bashExecution":
|
|
554
|
-
blocks.push({ label: "command", content: message.command, language: "bash" });
|
|
555
|
-
if (message.output.trim()) blocks.push({ label: "output", content: message.output });
|
|
580
|
+
blocks.push({ label: "command", content: message.command, entry, language: "bash" });
|
|
581
|
+
if (message.output.trim()) blocks.push({ label: "output", content: message.output, entry });
|
|
556
582
|
break;
|
|
557
583
|
case "pythonExecution":
|
|
558
|
-
blocks.push({ label: "eval code", content: message.code, language: "python" });
|
|
559
|
-
if (message.output.trim()) blocks.push({ label: "output", content: message.output });
|
|
584
|
+
blocks.push({ label: "eval code", content: message.code, entry, language: "python" });
|
|
585
|
+
if (message.output.trim()) blocks.push({ label: "output", content: message.output, entry });
|
|
560
586
|
break;
|
|
561
587
|
default:
|
|
562
588
|
break;
|
|
@@ -199,12 +199,12 @@ export function extractLastCommand(messages: readonly AgentMessage[]): LastComma
|
|
|
199
199
|
return undefined;
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
/** Concatenated visible text of an assistant message,
|
|
203
|
-
function assistantText(msg: AgentMessage): string | undefined {
|
|
202
|
+
/** Concatenated visible text of an assistant message, verbatim; undefined when blank. */
|
|
203
|
+
export function assistantText(msg: AgentMessage): string | undefined {
|
|
204
204
|
if (msg.role !== "assistant") return undefined;
|
|
205
205
|
let text = "";
|
|
206
206
|
for (const content of msg.content) {
|
|
207
207
|
if (content.type === "text") text += content.text;
|
|
208
208
|
}
|
|
209
|
-
return text.trim()
|
|
209
|
+
return text.trim() ? text : undefined;
|
|
210
210
|
}
|
|
@@ -11,7 +11,7 @@ import { parseModelString, splitUpstreamRouting, formatModelSelectorValue } from
|
|
|
11
11
|
* in the compact alt+p picker ({@link ./model-picker}).
|
|
12
12
|
*/
|
|
13
13
|
import { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
14
|
-
import type { Model } from "@oh-my-pi/pi-ai";
|
|
14
|
+
import type { KeysApi, Model } from "@oh-my-pi/pi-ai";
|
|
15
15
|
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
16
16
|
import { getSupportedEfforts } from "@oh-my-pi/pi-catalog/model-thinking";
|
|
17
17
|
import { providerEntry } from "@oh-my-pi/pi-catalog/compat/providers";
|
|
@@ -89,7 +89,7 @@ export interface ModelHubSource extends ModelBrowserSource {
|
|
|
89
89
|
|
|
90
90
|
/** Catalog capabilities required by the model hub. */
|
|
91
91
|
export interface ModelHubRegistry extends ModelBrowserRegistry {
|
|
92
|
-
readonly authStorage: {
|
|
92
|
+
readonly authStorage: { readonly keys: Pick<KeysApi, "source"> };
|
|
93
93
|
getDiscoverableProviders(): string[];
|
|
94
94
|
getProviderDiscoveryState(provider: string):
|
|
95
95
|
| {
|
|
@@ -451,7 +451,8 @@ export class ModelHubComponent implements Component {
|
|
|
451
451
|
// Discoverable without stored auth: catalog-backed providers stay
|
|
452
452
|
// locked; keyless/custom endpoints (ollama, vllm, …) surface as
|
|
453
453
|
// selectable so discovery can populate them.
|
|
454
|
-
|
|
454
|
+
const authenticated = authStorage.keys.source(provider) !== undefined;
|
|
455
|
+
if (authenticated || !locked.has(provider)) {
|
|
455
456
|
// #2761: implicit local endpoints (optional: true) stay hidden
|
|
456
457
|
// until discovery actually reaches a server. "idle" means never
|
|
457
458
|
// probed; "unavailable" means the endpoint is unreachable; both
|
|
@@ -459,7 +460,7 @@ export class ModelHubComponent implements Component {
|
|
|
459
460
|
// configured. models.yml discovery providers (optional: false)
|
|
460
461
|
// and providers with stored auth keep their entry so
|
|
461
462
|
// misconfigurations stay visible and diagnosable.
|
|
462
|
-
if (!
|
|
463
|
+
if (!authenticated) {
|
|
463
464
|
const discovery = this.#registry.getProviderDiscoveryState(provider);
|
|
464
465
|
if (discovery?.optional && (discovery.status === "idle" || discovery.status === "unavailable")) {
|
|
465
466
|
continue;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CredentialsApi, KeysApi } from "@oh-my-pi/pi-ai";
|
|
1
2
|
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
2
3
|
import type { OAuthProviderInfo } from "@oh-my-pi/pi-ai/oauth/types";
|
|
3
4
|
import {
|
|
@@ -19,14 +20,8 @@ const OAUTH_SELECTOR_MAX_VISIBLE = 10;
|
|
|
19
20
|
|
|
20
21
|
/** Credential presence and provenance needed by the provider picker. */
|
|
21
22
|
export interface OAuthSelectorAuthSource {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
getCredentialOrigin(providerId: string):
|
|
25
|
-
| {
|
|
26
|
-
kind: "runtime" | "config" | "oauth" | "api_key" | "env" | "fallback";
|
|
27
|
-
envVar?: string;
|
|
28
|
-
}
|
|
29
|
-
| undefined;
|
|
23
|
+
readonly credentials: Pick<CredentialsApi, "has">;
|
|
24
|
+
readonly keys: Pick<KeysApi, "source">;
|
|
30
25
|
}
|
|
31
26
|
|
|
32
27
|
/**
|
|
@@ -42,7 +37,6 @@ const ORIGIN_LABELS = {
|
|
|
42
37
|
oauth: "login",
|
|
43
38
|
api_key: "api key",
|
|
44
39
|
env: "env",
|
|
45
|
-
fallback: "custom provider",
|
|
46
40
|
};
|
|
47
41
|
/**
|
|
48
42
|
* Component that renders an OAuth provider selector.
|
|
@@ -122,7 +116,9 @@ export class OAuthSelectorComponent extends OverlayPanel {
|
|
|
122
116
|
this.#updateList();
|
|
123
117
|
}
|
|
124
118
|
#hasSelectableAuth(providerId: string): boolean {
|
|
125
|
-
return this.#mode === "logout"
|
|
119
|
+
return this.#mode === "logout"
|
|
120
|
+
? this.#authStorage.credentials.has(providerId)
|
|
121
|
+
: this.#authStorage.keys.source(providerId) !== undefined;
|
|
126
122
|
}
|
|
127
123
|
|
|
128
124
|
#loadProviders(disabledProviders: readonly string[] = []): void {
|
|
@@ -212,7 +208,7 @@ export class OAuthSelectorComponent extends OverlayPanel {
|
|
|
212
208
|
* the list distinguishes a real login from an env var aliasing the provider.
|
|
213
209
|
*/
|
|
214
210
|
#getSourceLabel(providerId: string): string {
|
|
215
|
-
const origin = this.#authStorage.
|
|
211
|
+
const origin = this.#authStorage.keys.source(providerId);
|
|
216
212
|
if (!origin) return "";
|
|
217
213
|
const detail = origin.kind === "env" && origin.envVar ? `env: ${origin.envVar}` : ORIGIN_LABELS[origin.kind];
|
|
218
214
|
return theme.fg("muted", ` (${detail})`);
|
|
@@ -253,7 +249,7 @@ export class OAuthSelectorComponent extends OverlayPanel {
|
|
|
253
249
|
|
|
254
250
|
#getProviderSearchText(provider: OAuthProviderInfo): string {
|
|
255
251
|
let text = `${provider.name} ${provider.id}`;
|
|
256
|
-
const origin = this.#authStorage.
|
|
252
|
+
const origin = this.#authStorage.keys.source(provider.id);
|
|
257
253
|
if (origin) {
|
|
258
254
|
text += ` logged in authenticated ${ORIGIN_LABELS[origin.kind]}`;
|
|
259
255
|
if (origin.envVar) text += ` ${origin.envVar}`;
|