@oh-my-pi/pi-coding-agent 15.10.0 → 15.10.2
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 +142 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/cli/startup-cwd.d.ts +2 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-provider-priority.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +17 -1
- package/dist/types/config/model-resolver.d.ts +4 -1
- package/dist/types/config/settings-schema.d.ts +9 -0
- package/dist/types/config/settings.d.ts +7 -2
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/debug/report-bundle.d.ts +3 -0
- package/dist/types/edit/file-snapshot-store.d.ts +18 -10
- package/dist/types/eval/py/__tests__/prelude.test.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +4 -1
- package/dist/types/lsp/client.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/main.d.ts +3 -9
- package/dist/types/mcp/tool-bridge.d.ts +2 -0
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +4 -1
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/status-line.d.ts +2 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +17 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +16 -5
- package/dist/types/modes/magic-keywords.d.ts +1 -1
- package/dist/types/modes/markdown-prose.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +21 -5
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/modes/workflow.d.ts +3 -3
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +8 -3
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +17 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/eval.d.ts +8 -0
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/gh-cache-invalidation.d.ts +6 -0
- package/dist/types/tools/github-cache.d.ts +12 -0
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +5 -9
- package/dist/types/tools/search.d.ts +6 -2
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +3 -0
- package/dist/types/tools/yield.d.ts +8 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/package.json +9 -9
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/args.ts +3 -1
- package/src/cli/dry-balance-cli.ts +54 -21
- package/src/cli/gallery-cli.ts +4 -1
- package/src/cli/gallery-fixtures/misc.ts +29 -0
- package/src/cli/startup-cwd.ts +68 -0
- package/src/commands/launch.ts +3 -0
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +36 -11
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/model-provider-priority.ts +55 -0
- package/src/config/model-registry.ts +29 -24
- package/src/config/model-resolver.ts +39 -7
- package/src/config/settings-schema.ts +10 -0
- package/src/config/settings.ts +106 -43
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +47 -53
- package/src/debug/raw-sse-buffer.ts +7 -4
- package/src/debug/report-bundle.ts +9 -0
- package/src/edit/file-snapshot-store.ts +33 -1
- package/src/edit/hashline/filesystem.ts +2 -1
- package/src/edit/renderer.ts +82 -78
- package/src/eval/__tests__/llm-bridge.test.ts +110 -31
- package/src/eval/js/context-manager.ts +32 -15
- package/src/eval/llm-bridge.ts +22 -6
- package/src/eval/py/__tests__/prelude.test.ts +19 -0
- package/src/eval/py/executor.ts +23 -11
- package/src/eval/py/prelude.py +1 -1
- package/src/extensibility/extensions/types.ts +10 -1
- package/src/goals/tools/goal-tool.ts +36 -26
- package/src/internal-urls/docs-index.generated.ts +8 -8
- package/src/lsp/client.ts +23 -11
- package/src/lsp/config.ts +11 -1
- package/src/lsp/index.ts +61 -9
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +100 -72
- package/src/mcp/tool-bridge.ts +2 -0
- package/src/memories/index.ts +14 -7
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +164 -109
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/model-selector.ts +59 -13
- package/src/modes/components/oauth-selector.ts +33 -7
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/status-line.ts +19 -4
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +68 -88
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +2 -3
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +57 -55
- package/src/modes/controllers/event-controller.ts +67 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +170 -126
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +23 -25
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/interactive-mode.ts +274 -112
- package/src/modes/magic-keywords.ts +1 -1
- package/src/modes/markdown-prose.ts +1 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/shimmer.ts +20 -9
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +21 -7
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/modes/workflow.ts +10 -10
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/bash.md +9 -0
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/eval.md +2 -1
- package/src/prompts/tools/read.md +2 -2
- package/src/sdk.ts +37 -46
- package/src/session/agent-session.ts +119 -18
- package/src/session/auth-storage.ts +2 -0
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +109 -28
- package/src/slash-commands/builtin-registry.ts +36 -9
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +76 -38
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +211 -147
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +57 -6
- package/src/tools/browser/tab-supervisor.ts +13 -1
- package/src/tools/browser/tab-worker.ts +33 -4
- package/src/tools/debug.ts +20 -8
- package/src/tools/eval.ts +13 -2
- package/src/tools/fetch.ts +297 -7
- package/src/tools/find.ts +51 -30
- package/src/tools/gh-cache-invalidation.ts +200 -0
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/github-cache.ts +25 -0
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +10 -3
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/path-utils.ts +28 -2
- package/src/tools/plan-mode-guard.ts +66 -39
- package/src/tools/read.ts +48 -28
- package/src/tools/render-utils.ts +21 -37
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +118 -81
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +118 -52
- package/src/tools/write.ts +83 -64
- package/src/tools/yield.ts +10 -1
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +11 -3
- package/src/utils/enhanced-paste.ts +230 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/codex.ts +37 -8
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import { formatNumber } from "@oh-my-pi/pi-utils";
|
|
18
18
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
19
19
|
import { getKnownRoleIds, getRoleInfo, MODEL_ROLE_IDS, MODEL_ROLES } from "../../config/model-registry";
|
|
20
|
-
import { resolveModelRoleValue } from "../../config/model-resolver";
|
|
20
|
+
import { getModelMatchPreferences, resolveModelRoleValue } from "../../config/model-resolver";
|
|
21
21
|
import type { Settings } from "../../config/settings";
|
|
22
22
|
import { type ThemeColor, theme } from "../../modes/theme/theme";
|
|
23
23
|
import { matchesSelectDown, matchesSelectUp } from "../../modes/utils/keybinding-matchers";
|
|
@@ -31,6 +31,25 @@ function makeInvertedBadge(label: string, color: ThemeColor): string {
|
|
|
31
31
|
return `${bgAnsi}\x1b[30m ${label} \x1b[39m\x1b[49m`;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function makeAutoSelectedBadge(label: string, color: ThemeColor): string {
|
|
35
|
+
return `${theme.fg("dim", "[")}${theme.fg(color, label)}${theme.fg("dim", " auto]")}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function makeRoleBadgeToken(label: string, color: ThemeColor, assigned: RoleAssignment): string {
|
|
39
|
+
if (assigned.autoSelected) {
|
|
40
|
+
const badge = makeAutoSelectedBadge(label, color);
|
|
41
|
+
if (assigned.thinkingLevel === ThinkingLevel.Inherit) {
|
|
42
|
+
return badge;
|
|
43
|
+
}
|
|
44
|
+
const thinkingLabel = getConfiguredThinkingLevelMetadata(assigned.thinkingLevel).label;
|
|
45
|
+
return `${badge} ${theme.fg("dim", `(${thinkingLabel})`)}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const badge = makeInvertedBadge(label, color);
|
|
49
|
+
const thinkingLabel = getConfiguredThinkingLevelMetadata(assigned.thinkingLevel).label;
|
|
50
|
+
return `${badge} ${theme.fg("dim", `(${thinkingLabel})`)}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
34
53
|
function normalizeSearchText(value: string): string {
|
|
35
54
|
return value
|
|
36
55
|
.toLowerCase()
|
|
@@ -86,6 +105,7 @@ interface ScopedModelItem {
|
|
|
86
105
|
interface RoleAssignment {
|
|
87
106
|
model: Model;
|
|
88
107
|
thinkingLevel: ConfiguredThinkingLevel;
|
|
108
|
+
autoSelected: boolean;
|
|
89
109
|
}
|
|
90
110
|
|
|
91
111
|
type RoleSelectCallback = (
|
|
@@ -271,12 +291,17 @@ export class ModelSelectorComponent extends Container {
|
|
|
271
291
|
});
|
|
272
292
|
}
|
|
273
293
|
|
|
274
|
-
#loadRoleModels(): void {
|
|
294
|
+
#loadRoleModels(autoCandidateModels?: ReadonlyArray<Model>): void {
|
|
295
|
+
const nextRoles = {} as Record<string, RoleAssignment | undefined>;
|
|
275
296
|
const allModels = this.#modelRegistry.getAll();
|
|
276
|
-
const matchPreferences =
|
|
277
|
-
|
|
297
|
+
const matchPreferences = getModelMatchPreferences(this.#settings);
|
|
298
|
+
const knownRoles = getKnownRoleIds(this.#settings);
|
|
299
|
+
const configuredRoles = new Set<string>();
|
|
300
|
+
|
|
301
|
+
for (const role of knownRoles) {
|
|
278
302
|
const roleValue = this.#settings.getModelRole(role);
|
|
279
303
|
if (!roleValue) continue;
|
|
304
|
+
configuredRoles.add(role);
|
|
280
305
|
|
|
281
306
|
const resolved = resolveModelRoleValue(roleValue, allModels, {
|
|
282
307
|
settings: this.#settings,
|
|
@@ -284,15 +309,39 @@ export class ModelSelectorComponent extends Container {
|
|
|
284
309
|
modelRegistry: this.#modelRegistry,
|
|
285
310
|
});
|
|
286
311
|
if (resolved.model) {
|
|
287
|
-
|
|
312
|
+
nextRoles[role] = {
|
|
288
313
|
model: resolved.model,
|
|
289
314
|
thinkingLevel:
|
|
290
315
|
resolved.explicitThinkingLevel && resolved.thinkingLevel !== undefined
|
|
291
316
|
? resolved.thinkingLevel
|
|
292
317
|
: ThinkingLevel.Inherit,
|
|
318
|
+
autoSelected: false,
|
|
293
319
|
};
|
|
294
320
|
}
|
|
295
321
|
}
|
|
322
|
+
|
|
323
|
+
if (autoCandidateModels && autoCandidateModels.length > 0) {
|
|
324
|
+
const candidates = [...autoCandidateModels];
|
|
325
|
+
for (const role of knownRoles) {
|
|
326
|
+
if (configuredRoles.has(role)) continue;
|
|
327
|
+
const resolved = resolveModelRoleValue(`pi/${role}`, candidates, {
|
|
328
|
+
settings: this.#settings,
|
|
329
|
+
matchPreferences,
|
|
330
|
+
modelRegistry: this.#modelRegistry,
|
|
331
|
+
});
|
|
332
|
+
if (!resolved.model) continue;
|
|
333
|
+
nextRoles[role] = {
|
|
334
|
+
model: resolved.model,
|
|
335
|
+
thinkingLevel:
|
|
336
|
+
resolved.explicitThinkingLevel && resolved.thinkingLevel !== undefined
|
|
337
|
+
? resolved.thinkingLevel
|
|
338
|
+
: ThinkingLevel.Inherit,
|
|
339
|
+
autoSelected: true,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
this.#roles = nextRoles;
|
|
296
345
|
}
|
|
297
346
|
|
|
298
347
|
/**
|
|
@@ -427,6 +476,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
427
476
|
}
|
|
428
477
|
|
|
429
478
|
const candidates = models.map(item => item.model);
|
|
479
|
+
this.#loadRoleModels(candidates);
|
|
430
480
|
const canonicalRecords = this.#modelRegistry.getCanonicalModels({
|
|
431
481
|
availableOnly: this.#scopedModels.length === 0,
|
|
432
482
|
candidates,
|
|
@@ -871,25 +921,21 @@ export class ModelSelectorComponent extends Container {
|
|
|
871
921
|
const isDisabled = this.#isItemDisabled(item);
|
|
872
922
|
const disabledSuffix = this.#formatContextLimitSuffix(item.model);
|
|
873
923
|
|
|
874
|
-
// Build role badges
|
|
924
|
+
// Build role badges. Solid badges are configured; outlined badges are auto-selected defaults.
|
|
875
925
|
const roleBadgeTokens: string[] = [];
|
|
876
926
|
for (const role of MODEL_ROLE_IDS) {
|
|
877
927
|
const { tag, color } = getRoleInfo(role, this.#settings);
|
|
878
928
|
const assigned = this.#roles[role];
|
|
879
929
|
if (!tag || !assigned || !modelsAreEqual(assigned.model, item.model)) continue;
|
|
880
930
|
|
|
881
|
-
|
|
882
|
-
const thinkingLabel = getConfiguredThinkingLevelMetadata(assigned.thinkingLevel).label;
|
|
883
|
-
roleBadgeTokens.push(`${badge} ${theme.fg("dim", `(${thinkingLabel})`)}`);
|
|
931
|
+
roleBadgeTokens.push(makeRoleBadgeToken(tag, color ?? "success", assigned));
|
|
884
932
|
}
|
|
885
933
|
// Custom role badges
|
|
886
934
|
for (const [role, assigned] of Object.entries(this.#roles)) {
|
|
887
935
|
if (role in MODEL_ROLES || !assigned || !modelsAreEqual(assigned.model, item.model)) continue;
|
|
888
936
|
const roleInfo = getRoleInfo(role, this.#settings);
|
|
889
937
|
const badgeLabel = roleInfo.tag ?? roleInfo.name;
|
|
890
|
-
|
|
891
|
-
const thinkingLabel = getConfiguredThinkingLevelMetadata(assigned.thinkingLevel).label;
|
|
892
|
-
roleBadgeTokens.push(`${badge} ${theme.fg("dim", `(${thinkingLabel})`)}`);
|
|
938
|
+
roleBadgeTokens.push(makeRoleBadgeToken(badgeLabel, roleInfo.color ?? "muted", assigned));
|
|
893
939
|
}
|
|
894
940
|
const badgeText = roleBadgeTokens.length > 0 ? ` ${roleBadgeTokens.join(" ")}` : "";
|
|
895
941
|
|
|
@@ -1184,7 +1230,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
1184
1230
|
const selectedThinkingLevel = thinkingLevel ?? this.#getCurrentRoleThinkingLevel(role);
|
|
1185
1231
|
|
|
1186
1232
|
// Update local state for UI
|
|
1187
|
-
this.#roles[role] = { model: item.model, thinkingLevel: selectedThinkingLevel };
|
|
1233
|
+
this.#roles[role] = { model: item.model, thinkingLevel: selectedThinkingLevel, autoSelected: false };
|
|
1188
1234
|
|
|
1189
1235
|
// Notify caller (for updating agent state if needed)
|
|
1190
1236
|
this.#onSelectCallback(item.model, role, selectedThinkingLevel, item.selector);
|
|
@@ -11,10 +11,20 @@ import {
|
|
|
11
11
|
} from "@oh-my-pi/pi-tui";
|
|
12
12
|
import { theme } from "../../modes/theme/theme";
|
|
13
13
|
import { matchesSelectCancel, matchesSelectDown, matchesSelectUp } from "../../modes/utils/keybinding-matchers";
|
|
14
|
-
import type { AuthStorage } from "../../session/auth-storage";
|
|
14
|
+
import type { AuthStorage, CredentialOriginKind } from "../../session/auth-storage";
|
|
15
15
|
import { DynamicBorder } from "./dynamic-border";
|
|
16
16
|
|
|
17
17
|
const OAUTH_SELECTOR_MAX_VISIBLE = 10;
|
|
18
|
+
|
|
19
|
+
/** Compact, human-readable tag for each credential-origin leg. */
|
|
20
|
+
const ORIGIN_LABELS: Record<CredentialOriginKind, string> = {
|
|
21
|
+
runtime: "--api-key",
|
|
22
|
+
config: "config",
|
|
23
|
+
oauth: "login",
|
|
24
|
+
api_key: "api key",
|
|
25
|
+
env: "env",
|
|
26
|
+
fallback: "custom provider",
|
|
27
|
+
};
|
|
18
28
|
/**
|
|
19
29
|
* Component that renders an OAuth provider selector.
|
|
20
30
|
*/
|
|
@@ -146,20 +156,34 @@ export class OAuthSelectorComponent extends Container {
|
|
|
146
156
|
}
|
|
147
157
|
}
|
|
148
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Muted provenance suffix (" (env: COPILOT_GITHUB_TOKEN)", " (login)", …) so
|
|
161
|
+
* the list distinguishes a real login from an env var aliasing the provider.
|
|
162
|
+
*/
|
|
163
|
+
#getSourceLabel(providerId: string): string {
|
|
164
|
+
const origin = this.#authStorage.getCredentialOrigin(providerId);
|
|
165
|
+
if (!origin) return "";
|
|
166
|
+
const detail = origin.kind === "env" && origin.envVar ? `env: ${origin.envVar}` : ORIGIN_LABELS[origin.kind];
|
|
167
|
+
return theme.fg("muted", ` (${detail})`);
|
|
168
|
+
}
|
|
169
|
+
|
|
149
170
|
#getStatusIndicator(providerId: string): string {
|
|
150
171
|
const state = this.#authState.get(providerId);
|
|
172
|
+
const source = this.#getSourceLabel(providerId);
|
|
151
173
|
if (state === "checking") {
|
|
152
174
|
const frameCount = theme.spinnerFrames.length;
|
|
153
175
|
const spinner = frameCount > 0 ? theme.spinnerFrames[this.#spinnerFrame % frameCount] : theme.status.pending;
|
|
154
|
-
return theme.fg("warning", ` ${spinner} checking`);
|
|
176
|
+
return theme.fg("warning", ` ${spinner} checking`) + source;
|
|
155
177
|
}
|
|
156
178
|
if (state === "invalid") {
|
|
157
|
-
return theme.fg("error", ` ${theme.status.error} invalid`);
|
|
179
|
+
return theme.fg("error", ` ${theme.status.error} invalid`) + source;
|
|
158
180
|
}
|
|
159
181
|
if (state === "valid") {
|
|
160
|
-
return theme.fg("success", ` ${theme.status.success} logged in`);
|
|
182
|
+
return theme.fg("success", ` ${theme.status.success} logged in`) + source;
|
|
161
183
|
}
|
|
162
|
-
return this.#hasSelectableAuth(providerId)
|
|
184
|
+
return this.#hasSelectableAuth(providerId)
|
|
185
|
+
? theme.fg("success", ` ${theme.status.success} logged in`) + source
|
|
186
|
+
: "";
|
|
163
187
|
}
|
|
164
188
|
|
|
165
189
|
#isSearchEnabled(): boolean {
|
|
@@ -178,8 +202,10 @@ export class OAuthSelectorComponent extends Container {
|
|
|
178
202
|
|
|
179
203
|
#getProviderSearchText(provider: OAuthProviderInfo): string {
|
|
180
204
|
let text = `${provider.name} ${provider.id}`;
|
|
181
|
-
|
|
182
|
-
|
|
205
|
+
const origin = this.#authStorage.getCredentialOrigin(provider.id);
|
|
206
|
+
if (origin) {
|
|
207
|
+
text += ` logged in authenticated ${ORIGIN_LABELS[origin.kind]}`;
|
|
208
|
+
if (origin.envVar) text += ` ${origin.envVar}`;
|
|
183
209
|
}
|
|
184
210
|
if (!provider.available) {
|
|
185
211
|
text += " unavailable";
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared box-drawing chrome for fullscreen overlays (the `/copy` picker, the
|
|
3
|
+
* plan-review overlay, …). Every helper paints with `theme.boxSharp` glyphs and
|
|
4
|
+
* the `border`/`accent` theme colors so all outlined overlays read identically.
|
|
5
|
+
*/
|
|
6
|
+
import { padding, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
7
|
+
import { theme } from "../theme/theme";
|
|
8
|
+
|
|
9
|
+
/** Pad or truncate a (possibly ANSI-styled) string to exactly `width` columns. */
|
|
10
|
+
export function fit(text: string, width: number): string {
|
|
11
|
+
if (width <= 0) return "";
|
|
12
|
+
const w = visibleWidth(text);
|
|
13
|
+
if (w === width) return text;
|
|
14
|
+
if (w < width) return text + padding(width - w);
|
|
15
|
+
const cut = truncateToWidth(text, width);
|
|
16
|
+
const cw = visibleWidth(cut);
|
|
17
|
+
return cw < width ? cut + padding(width - cw) : cut;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function paint(s: string): string {
|
|
21
|
+
return theme.fg("border", s);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Top border with an optional accent-colored title inset into the rule. */
|
|
25
|
+
export function topBorder(width: number, title: string): string {
|
|
26
|
+
const box = theme.boxSharp;
|
|
27
|
+
const inner = Math.max(0, width - 2);
|
|
28
|
+
if (!title) return paint(box.topLeft + box.horizontal.repeat(inner) + box.topRight);
|
|
29
|
+
const shown = truncateToWidth(` ${title} `, Math.max(0, inner - 2));
|
|
30
|
+
const fillWidth = Math.max(0, inner - 1 - visibleWidth(shown));
|
|
31
|
+
return (
|
|
32
|
+
paint(box.topLeft + box.horizontal) +
|
|
33
|
+
theme.bold(theme.fg("accent", shown)) +
|
|
34
|
+
paint(box.horizontal.repeat(fillWidth) + box.topRight)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A horizontal rule with left/right tees, splitting overlay sections. */
|
|
39
|
+
export function divider(width: number): string {
|
|
40
|
+
const box = theme.boxSharp;
|
|
41
|
+
return paint(box.teeRight + box.horizontal.repeat(Math.max(0, width - 2)) + box.teeLeft);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function bottomBorder(width: number): string {
|
|
45
|
+
const box = theme.boxSharp;
|
|
46
|
+
return paint(box.bottomLeft + box.horizontal.repeat(Math.max(0, width - 2)) + box.bottomRight);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Wrap pre-styled content in vertical borders with single-column insets. */
|
|
50
|
+
export function row(content: string, width: number): string {
|
|
51
|
+
const box = theme.boxSharp;
|
|
52
|
+
return `${paint(box.vertical)} ${fit(content, Math.max(0, width - 4))} ${paint(box.vertical)}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Column index (0-based) of the inner divider for a two-column layout whose
|
|
57
|
+
* sidebar content area is `sidebarWidth` columns wide. The layout is
|
|
58
|
+
* `│ sidebar │ body │` with a single-column inset on every side, so the divider
|
|
59
|
+
* vertical sits at `sidebarWidth + 3` and the body content area is
|
|
60
|
+
* {@link splitBodyWidth} columns.
|
|
61
|
+
*/
|
|
62
|
+
function splitDividerCol(sidebarWidth: number): number {
|
|
63
|
+
return sidebarWidth + 3;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Body content width for a two-column overlay of total `width`. */
|
|
67
|
+
export function splitBodyWidth(width: number, sidebarWidth: number): number {
|
|
68
|
+
return Math.max(0, width - sidebarWidth - 7);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Top border carrying the title, split by a `┬` over the column divider. */
|
|
72
|
+
export function topBorderSplit(width: number, title: string, sidebarWidth: number): string {
|
|
73
|
+
const box = theme.boxSharp;
|
|
74
|
+
const dividerCol = splitDividerCol(sidebarWidth);
|
|
75
|
+
const leftLen = Math.max(0, dividerCol - 1);
|
|
76
|
+
const rightLen = Math.max(0, width - 2 - dividerCol);
|
|
77
|
+
let left: string;
|
|
78
|
+
if (!title) {
|
|
79
|
+
left = paint(box.topLeft + box.horizontal.repeat(leftLen));
|
|
80
|
+
} else {
|
|
81
|
+
const shown = truncateToWidth(` ${title} `, Math.max(0, leftLen - 1));
|
|
82
|
+
const fillWidth = Math.max(0, leftLen - 1 - visibleWidth(shown));
|
|
83
|
+
left =
|
|
84
|
+
paint(box.topLeft + box.horizontal) +
|
|
85
|
+
theme.bold(theme.fg("accent", shown)) +
|
|
86
|
+
paint(box.horizontal.repeat(fillWidth));
|
|
87
|
+
}
|
|
88
|
+
return left + paint(box.teeDown + box.horizontal.repeat(rightLen) + box.topRight);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Section rule that closes the sidebar column with a `┴` over the divider. */
|
|
92
|
+
export function dividerSplit(width: number, sidebarWidth: number): string {
|
|
93
|
+
const box = theme.boxSharp;
|
|
94
|
+
const dividerCol = splitDividerCol(sidebarWidth);
|
|
95
|
+
const leftLen = Math.max(0, dividerCol - 1);
|
|
96
|
+
const rightLen = Math.max(0, width - 2 - dividerCol);
|
|
97
|
+
return paint(
|
|
98
|
+
box.teeRight + box.horizontal.repeat(leftLen) + box.teeUp + box.horizontal.repeat(rightLen) + box.teeLeft,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** A two-column content row: `│ sidebar │ body │`, each inset by one column. */
|
|
103
|
+
export function splitRow(sidebar: string, body: string, width: number, sidebarWidth: number): string {
|
|
104
|
+
const box = theme.boxSharp;
|
|
105
|
+
const bodyWidth = splitBodyWidth(width, sidebarWidth);
|
|
106
|
+
const bar = paint(box.vertical);
|
|
107
|
+
return `${bar} ${fit(sidebar, sidebarWidth)} ${bar} ${fit(body, bodyWidth)} ${bar}`;
|
|
108
|
+
}
|