@compilr-dev/cli 0.4.0 → 0.5.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/README.md +254 -68
- package/dist/.tsbuildinfo.app +1 -0
- package/dist/.tsbuildinfo.data +1 -0
- package/dist/.tsbuildinfo.domain +1 -0
- package/dist/.tsbuildinfo.foundation +1 -0
- package/dist/agent.d.ts +134 -4
- package/dist/agent.js +345 -166
- package/dist/anchors/index.d.ts +9 -0
- package/dist/anchors/index.js +9 -0
- package/dist/anchors/project-anchors.d.ts +79 -0
- package/dist/anchors/project-anchors.js +202 -0
- package/dist/auth/api-client.d.ts +124 -0
- package/dist/auth/api-client.js +261 -0
- package/dist/auth/index.d.ts +172 -0
- package/dist/auth/index.js +545 -0
- package/dist/auth/storage.d.ts +52 -0
- package/dist/auth/storage.js +118 -0
- package/dist/changelog/index.d.ts +16 -0
- package/dist/changelog/index.js +24 -0
- package/dist/changelog/releases.d.ts +17 -0
- package/dist/changelog/releases.js +63 -0
- package/dist/commands/index.d.ts +2 -1
- package/dist/commands-v2/handlers/auth.d.ts +10 -0
- package/dist/commands-v2/handlers/auth.js +118 -0
- package/dist/commands-v2/handlers/background.d.ts +14 -0
- package/dist/commands-v2/handlers/background.js +276 -0
- package/dist/commands-v2/handlers/context.d.ts +13 -0
- package/dist/commands-v2/handlers/context.js +533 -0
- package/dist/commands-v2/handlers/core.d.ts +14 -0
- package/dist/commands-v2/handlers/core.js +290 -0
- package/dist/commands-v2/handlers/debug.d.ts +11 -0
- package/dist/commands-v2/handlers/debug.js +177 -0
- package/dist/commands-v2/handlers/delegations.d.ts +8 -0
- package/dist/commands-v2/handlers/delegations.js +29 -0
- package/dist/commands-v2/handlers/files.d.ts +8 -0
- package/dist/commands-v2/handlers/files.js +162 -0
- package/dist/commands-v2/handlers/filter.d.ts +9 -0
- package/dist/commands-v2/handlers/filter.js +130 -0
- package/dist/commands-v2/handlers/games.d.ts +7 -0
- package/dist/commands-v2/handlers/games.js +57 -0
- package/dist/commands-v2/handlers/index.d.ts +25 -0
- package/dist/commands-v2/handlers/index.js +63 -0
- package/dist/commands-v2/handlers/mcp.d.ts +8 -0
- package/dist/commands-v2/handlers/mcp.js +39 -0
- package/dist/commands-v2/handlers/notifications.d.ts +9 -0
- package/dist/commands-v2/handlers/notifications.js +34 -0
- package/dist/commands-v2/handlers/project.d.ts +22 -0
- package/dist/commands-v2/handlers/project.js +1035 -0
- package/dist/commands-v2/handlers/reset.d.ts +11 -0
- package/dist/commands-v2/handlers/reset.js +118 -0
- package/dist/commands-v2/handlers/session.d.ts +161 -0
- package/dist/commands-v2/handlers/session.js +805 -0
- package/dist/commands-v2/handlers/settings.d.ts +17 -0
- package/dist/commands-v2/handlers/settings.js +417 -0
- package/dist/commands-v2/handlers/tasks.d.ts +5 -0
- package/dist/commands-v2/handlers/tasks.js +36 -0
- package/dist/commands-v2/handlers/team.d.ts +9 -0
- package/dist/commands-v2/handlers/team.js +549 -0
- package/dist/commands-v2/handlers/terminals.d.ts +9 -0
- package/dist/commands-v2/handlers/terminals.js +34 -0
- package/dist/commands-v2/index.d.ts +14 -0
- package/dist/commands-v2/index.js +18 -0
- package/dist/commands-v2/registry.d.ts +52 -0
- package/dist/commands-v2/registry.js +114 -0
- package/dist/commands-v2/types.d.ts +153 -0
- package/dist/commands-v2/types.js +7 -0
- package/dist/commands.js +123 -7
- package/dist/compilr-diff-companion.vsix +0 -0
- package/dist/db/index.js +98 -4
- package/dist/db/repositories/document-repository.d.ts +2 -0
- package/dist/db/repositories/document-repository.js +5 -1
- package/dist/db/repositories/index.d.ts +2 -0
- package/dist/db/repositories/index.js +1 -0
- package/dist/db/repositories/plan-repository.d.ts +101 -0
- package/dist/db/repositories/plan-repository.js +275 -0
- package/dist/db/repositories/project-repository.d.ts +6 -0
- package/dist/db/repositories/project-repository.js +41 -0
- package/dist/db/repositories/work-item-repository.d.ts +15 -0
- package/dist/db/repositories/work-item-repository.js +69 -4
- package/dist/db/schema.d.ts +40 -3
- package/dist/db/schema.js +66 -3
- package/dist/episodes/index.d.ts +20 -0
- package/dist/episodes/index.js +27 -0
- package/dist/episodes/recorder.d.ts +51 -0
- package/dist/episodes/recorder.js +195 -0
- package/dist/episodes/significant-work.d.ts +21 -0
- package/dist/episodes/significant-work.js +56 -0
- package/dist/episodes/store.d.ts +38 -0
- package/dist/episodes/store.js +199 -0
- package/dist/episodes/types.d.ts +35 -0
- package/dist/episodes/types.js +6 -0
- package/dist/episodes/work-at-risk.d.ts +12 -0
- package/dist/episodes/work-at-risk.js +38 -0
- package/dist/episodes/work-summary-anchor.d.ts +23 -0
- package/dist/episodes/work-summary-anchor.js +73 -0
- package/dist/games/coins.d.ts +66 -0
- package/dist/games/coins.js +165 -0
- package/dist/games/game-base.d.ts +84 -0
- package/dist/games/game-base.js +204 -0
- package/dist/games/index.d.ts +16 -0
- package/dist/games/index.js +49 -0
- package/dist/games/scores.d.ts +69 -0
- package/dist/games/scores.js +191 -0
- package/dist/games/tetris/board.d.ts +59 -0
- package/dist/games/tetris/board.js +170 -0
- package/dist/games/tetris/index.d.ts +109 -0
- package/dist/games/tetris/index.js +610 -0
- package/dist/games/tetris/pieces.d.ts +44 -0
- package/dist/games/tetris/pieces.js +271 -0
- package/dist/games/tetris/renderer.d.ts +26 -0
- package/dist/games/tetris/renderer.js +77 -0
- package/dist/guide/guide-content.d.ts +23 -0
- package/dist/guide/guide-content.js +196 -0
- package/dist/guide/index.d.ts +8 -0
- package/dist/guide/index.js +7 -0
- package/dist/guide/shared-content.d.ts +37 -0
- package/dist/guide/shared-content.js +1272 -0
- package/dist/guide/tutorial-helpers.d.ts +57 -0
- package/dist/guide/tutorial-helpers.js +147 -0
- package/dist/handlers/ask-user-handlers.d.ts +32 -0
- package/dist/handlers/ask-user-handlers.js +104 -0
- package/dist/handlers/delegation-handlers.d.ts +34 -0
- package/dist/handlers/delegation-handlers.js +291 -0
- package/dist/handlers/permission-handler.d.ts +30 -0
- package/dist/handlers/permission-handler.js +205 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +615 -179
- package/dist/input-handlers/index.d.ts +7 -0
- package/dist/input-handlers/index.js +7 -0
- package/dist/input-handlers/memory-handler.d.ts +26 -0
- package/dist/input-handlers/memory-handler.js +69 -0
- package/dist/models/index.d.ts +10 -0
- package/dist/models/index.js +12 -0
- package/dist/models/model-registry.d.ts +38 -0
- package/dist/models/model-registry.js +69 -0
- package/dist/models/model-tiers.d.ts +28 -0
- package/dist/models/model-tiers.js +71 -0
- package/dist/models/model-validation.d.ts +25 -0
- package/dist/models/model-validation.js +291 -0
- package/dist/models/ollama-models.d.ts +73 -0
- package/dist/models/ollama-models.js +178 -0
- package/dist/models/provider-types.d.ts +6 -0
- package/dist/models/provider-types.js +1 -0
- package/dist/models/providers.d.ts +35 -0
- package/dist/models/providers.js +58 -0
- package/dist/models/types.d.ts +4 -0
- package/dist/models/types.js +4 -0
- package/dist/multi-agent/activity.d.ts +21 -0
- package/dist/multi-agent/activity.js +34 -0
- package/dist/multi-agent/agent-selection.d.ts +55 -0
- package/dist/multi-agent/agent-selection.js +90 -0
- package/dist/multi-agent/artifacts.d.ts +197 -0
- package/dist/multi-agent/artifacts.js +379 -0
- package/dist/multi-agent/checkpointer.d.ts +138 -0
- package/dist/multi-agent/checkpointer.js +471 -0
- package/dist/multi-agent/collision-utils.d.ts +16 -0
- package/dist/multi-agent/collision-utils.js +28 -0
- package/dist/multi-agent/context-resolver.d.ts +97 -0
- package/dist/multi-agent/context-resolver.js +316 -0
- package/dist/multi-agent/custom-agents.d.ts +83 -0
- package/dist/multi-agent/custom-agents.js +227 -0
- package/dist/multi-agent/delegation-tracker.d.ts +157 -0
- package/dist/multi-agent/delegation-tracker.js +243 -0
- package/dist/multi-agent/file-lock-hook.d.ts +29 -0
- package/dist/multi-agent/file-lock-hook.js +97 -0
- package/dist/multi-agent/file-locks.d.ts +58 -0
- package/dist/multi-agent/file-locks.js +194 -0
- package/dist/multi-agent/index.d.ts +24 -0
- package/dist/multi-agent/index.js +30 -0
- package/dist/multi-agent/mention-parser.d.ts +64 -0
- package/dist/multi-agent/mention-parser.js +146 -0
- package/dist/multi-agent/notification-manager.d.ts +84 -0
- package/dist/multi-agent/notification-manager.js +224 -0
- package/dist/multi-agent/pending-requests.d.ts +122 -0
- package/dist/multi-agent/pending-requests.js +155 -0
- package/dist/multi-agent/session-registry.d.ts +139 -0
- package/dist/multi-agent/session-registry.js +514 -0
- package/dist/multi-agent/shared-context.d.ts +293 -0
- package/dist/multi-agent/shared-context.js +671 -0
- package/dist/multi-agent/skill-requirements.d.ts +66 -0
- package/dist/multi-agent/skill-requirements.js +178 -0
- package/dist/multi-agent/task-assignment.d.ts +69 -0
- package/dist/multi-agent/task-assignment.js +123 -0
- package/dist/multi-agent/task-suggestion.d.ts +31 -0
- package/dist/multi-agent/task-suggestion.js +72 -0
- package/dist/multi-agent/team-agent.d.ts +201 -0
- package/dist/multi-agent/team-agent.js +488 -0
- package/dist/multi-agent/team.d.ts +286 -0
- package/dist/multi-agent/team.js +610 -0
- package/dist/multi-agent/tool-config.d.ts +110 -0
- package/dist/multi-agent/tool-config.js +661 -0
- package/dist/multi-agent/types.d.ts +211 -0
- package/dist/multi-agent/types.js +617 -0
- package/dist/prompts/plan-mode-prompt.d.ts +11 -0
- package/dist/prompts/plan-mode-prompt.js +95 -0
- package/dist/repl-helpers.d.ts +63 -0
- package/dist/repl-helpers.js +319 -0
- package/dist/repl-v2.d.ts +554 -0
- package/dist/repl-v2.js +3286 -0
- package/dist/session/index.d.ts +6 -0
- package/dist/session/index.js +6 -0
- package/dist/session/project-session-manager.d.ts +158 -0
- package/dist/session/project-session-manager.js +650 -0
- package/dist/settings/index.d.ts +156 -13
- package/dist/settings/index.js +377 -24
- package/dist/settings/mcp-config.d.ts +76 -0
- package/dist/settings/mcp-config.js +143 -0
- package/dist/settings/paths.d.ts +114 -0
- package/dist/settings/paths.js +270 -0
- package/dist/shared-handlers.d.ts +62 -0
- package/dist/shared-handlers.js +48 -0
- package/dist/system-prompt/builder.d.ts +5 -0
- package/dist/system-prompt/builder.js +4 -0
- package/dist/system-prompt/index.d.ts +18 -0
- package/dist/system-prompt/index.js +18 -0
- package/dist/system-prompt/modules.d.ts +5 -0
- package/dist/system-prompt/modules.js +4 -0
- package/dist/tabbed-menu.js +2 -1
- package/dist/templates/compilr-md-import.d.ts +16 -0
- package/dist/templates/compilr-md-import.js +241 -0
- package/dist/templates/compilr-md.js +10 -58
- package/dist/templates/config-json.d.ts +1 -25
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.js +35 -75
- package/dist/themes/colors.js +3 -1
- package/dist/themes/registry.d.ts +5 -36
- package/dist/themes/registry.js +11 -95
- package/dist/themes/types.d.ts +3 -38
- package/dist/themes/types.js +2 -2
- package/dist/tool-names.d.ts +108 -0
- package/dist/tool-names.js +227 -0
- package/dist/tools/anchor-tools.d.ts +31 -0
- package/dist/tools/anchor-tools.js +255 -0
- package/dist/tools/artifact-tools.d.ts +42 -0
- package/dist/tools/artifact-tools.js +328 -0
- package/dist/tools/ask-user-simple.d.ts +1 -1
- package/dist/tools/ask-user-simple.js +2 -1
- package/dist/tools/ask-user.d.ts +1 -1
- package/dist/tools/ask-user.js +2 -1
- package/dist/tools/backlog-wrappers.d.ts +56 -0
- package/dist/tools/backlog-wrappers.js +353 -0
- package/dist/tools/backlog.d.ts +2 -2
- package/dist/tools/backlog.js +2 -2
- package/dist/tools/db-tools.d.ts +12 -0
- package/dist/tools/db-tools.js +14 -0
- package/dist/tools/delegate-background.d.ts +27 -0
- package/dist/tools/delegate-background.js +115 -0
- package/dist/tools/delegate.d.ts +22 -0
- package/dist/tools/delegate.js +97 -0
- package/dist/tools/delegation-status.d.ts +16 -0
- package/dist/tools/delegation-status.js +128 -0
- package/dist/tools/document-db.d.ts +43 -0
- package/dist/tools/document-db.js +220 -0
- package/dist/tools/guide-tool.d.ts +12 -0
- package/dist/tools/guide-tool.js +59 -0
- package/dist/tools/handoff.d.ts +25 -0
- package/dist/tools/handoff.js +99 -0
- package/dist/tools/meta-tools.d.ts +26 -0
- package/dist/tools/meta-tools.js +47 -0
- package/dist/tools/plan-tools.d.ts +54 -0
- package/dist/tools/plan-tools.js +338 -0
- package/dist/tools/platform-adapter.d.ts +29 -0
- package/dist/tools/platform-adapter.js +394 -0
- package/dist/tools/project-db.d.ts +34 -0
- package/dist/tools/project-db.js +39 -0
- package/dist/tools/recall-work-tool.d.ts +18 -0
- package/dist/tools/recall-work-tool.js +82 -0
- package/dist/tools/workitem-db.d.ts +135 -0
- package/dist/tools/workitem-db.js +730 -0
- package/dist/tools.d.ts +67 -2
- package/dist/tools.js +238 -38
- package/dist/ui/ask-user-overlay.d.ts +2 -2
- package/dist/ui/ask-user-overlay.js +443 -535
- package/dist/ui/ask-user-simple-overlay.d.ts +2 -2
- package/dist/ui/ask-user-simple-overlay.js +182 -209
- package/dist/ui/autocomplete-controller.d.ts +42 -0
- package/dist/ui/autocomplete-controller.js +384 -0
- package/dist/ui/base/index.d.ts +26 -0
- package/dist/ui/base/index.js +33 -0
- package/dist/ui/base/inline-overlay-utils.d.ts +217 -0
- package/dist/ui/base/inline-overlay-utils.js +320 -0
- package/dist/ui/base/inline-overlay.d.ts +159 -0
- package/dist/ui/base/inline-overlay.js +257 -0
- package/dist/ui/base/key-utils.d.ts +15 -0
- package/dist/ui/base/key-utils.js +30 -0
- package/dist/ui/base/overlay-base-v2.d.ts +203 -0
- package/dist/ui/base/overlay-base-v2.js +260 -0
- package/dist/ui/base/overlay-base.d.ts +156 -0
- package/dist/ui/base/overlay-base.js +238 -0
- package/dist/ui/base/overlay-lifecycle.d.ts +65 -0
- package/dist/ui/base/overlay-lifecycle.js +159 -0
- package/dist/ui/base/overlay-types.d.ts +185 -0
- package/dist/ui/base/overlay-types.js +7 -0
- package/dist/ui/base/render-utils.d.ts +27 -0
- package/dist/ui/base/render-utils.js +36 -0
- package/dist/ui/base/screen-stack.d.ts +148 -0
- package/dist/ui/base/screen-stack.js +184 -0
- package/dist/ui/base/tabbed-list-overlay-v2.d.ts +118 -0
- package/dist/ui/base/tabbed-list-overlay-v2.js +335 -0
- package/dist/ui/base/tabbed-list-overlay.d.ts +153 -0
- package/dist/ui/base/tabbed-list-overlay.js +369 -0
- package/dist/ui/constants/labels.d.ts +14 -0
- package/dist/ui/constants/labels.js +51 -0
- package/dist/ui/conversation-store.d.ts +55 -0
- package/dist/ui/conversation-store.js +107 -0
- package/dist/ui/conversation.d.ts +75 -4
- package/dist/ui/conversation.js +376 -165
- package/dist/ui/diff.d.ts +7 -1
- package/dist/ui/diff.js +85 -48
- package/dist/ui/ephemeral.d.ts +1 -1
- package/dist/ui/ephemeral.js +4 -10
- package/dist/ui/features/index.d.ts +34 -0
- package/dist/ui/features/index.js +34 -0
- package/dist/ui/features/input-feature.d.ts +85 -0
- package/dist/ui/features/input-feature.js +238 -0
- package/dist/ui/features/list-feature.d.ts +155 -0
- package/dist/ui/features/list-feature.js +244 -0
- package/dist/ui/features/pagination-feature.d.ts +154 -0
- package/dist/ui/features/pagination-feature.js +238 -0
- package/dist/ui/features/search-feature.d.ts +148 -0
- package/dist/ui/features/search-feature.js +185 -0
- package/dist/ui/features/tab-feature.d.ts +194 -0
- package/dist/ui/features/tab-feature.js +307 -0
- package/dist/ui/file-autocomplete.d.ts +24 -0
- package/dist/ui/file-autocomplete.js +56 -0
- package/dist/ui/footer-renderer.d.ts +69 -0
- package/dist/ui/footer-renderer.js +431 -0
- package/dist/ui/footer.d.ts +181 -7
- package/dist/ui/footer.js +523 -74
- package/dist/ui/guardrail-overlay.d.ts +29 -0
- package/dist/ui/guardrail-overlay.js +145 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -3
- package/dist/ui/input-controller.d.ts +51 -0
- package/dist/ui/input-controller.js +176 -0
- package/dist/ui/input-prompt.d.ts +135 -33
- package/dist/ui/input-prompt.js +728 -337
- package/dist/ui/iteration-limit-overlay.d.ts +2 -2
- package/dist/ui/iteration-limit-overlay.js +92 -128
- package/dist/ui/keyboard-handler.d.ts +57 -0
- package/dist/ui/keyboard-handler.js +557 -0
- package/dist/ui/keys-overlay.d.ts +1 -0
- package/dist/ui/keys-overlay.js +203 -141
- package/dist/ui/line-utils.d.ts +88 -0
- package/dist/ui/line-utils.js +150 -0
- package/dist/ui/live-region-facade.d.ts +42 -0
- package/dist/ui/live-region-facade.js +205 -0
- package/dist/ui/live-region.d.ts +157 -0
- package/dist/ui/live-region.js +379 -0
- package/dist/ui/mascot/expressions.d.ts +32 -0
- package/dist/ui/mascot/expressions.js +213 -0
- package/dist/ui/mascot/index.d.ts +8 -0
- package/dist/ui/mascot/index.js +8 -0
- package/dist/ui/mascot/renderer.d.ts +19 -0
- package/dist/ui/mascot/renderer.js +132 -0
- package/dist/ui/overlay/data/tutorial-content.d.ts +9 -0
- package/dist/ui/overlay/data/tutorial-content.js +9 -0
- package/dist/ui/overlay/data/tutorial-registry.d.ts +12 -0
- package/dist/ui/overlay/data/tutorial-registry.js +116 -0
- package/dist/ui/overlay/data/tutorial-types.d.ts +35 -0
- package/dist/ui/overlay/data/tutorial-types.js +6 -0
- package/dist/ui/overlay/data/tutorials/basics/first-conversation.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/basics/first-conversation.js +220 -0
- package/dist/ui/overlay/data/tutorials/basics/first-project.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/basics/first-project.js +284 -0
- package/dist/ui/overlay/data/tutorials/basics/navigation.d.ts +8 -0
- package/dist/ui/overlay/data/tutorials/basics/navigation.js +22 -0
- package/dist/ui/overlay/data/tutorials/basics/welcome.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/basics/welcome.js +174 -0
- package/dist/ui/overlay/data/tutorials/config/context-management.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/config/context-management.js +158 -0
- package/dist/ui/overlay/data/tutorials/config/mcp-servers.d.ts +8 -0
- package/dist/ui/overlay/data/tutorials/config/mcp-servers.js +155 -0
- package/dist/ui/overlay/data/tutorials/config/model-selection.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/config/model-selection.js +162 -0
- package/dist/ui/overlay/data/tutorials/config/permissions-safety.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/config/permissions-safety.js +163 -0
- package/dist/ui/overlay/data/tutorials/config/settings-config.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/config/settings-config.js +166 -0
- package/dist/ui/overlay/data/tutorials/planning/arch.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/planning/arch.js +168 -0
- package/dist/ui/overlay/data/tutorials/planning/backlog.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/planning/backlog.js +103 -0
- package/dist/ui/overlay/data/tutorials/planning/build.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/planning/build.js +173 -0
- package/dist/ui/overlay/data/tutorials/planning/design.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/planning/design.js +205 -0
- package/dist/ui/overlay/data/tutorials/planning/docs.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/planning/docs.js +143 -0
- package/dist/ui/overlay/data/tutorials/planning/prd.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/planning/prd.js +173 -0
- package/dist/ui/overlay/data/tutorials/planning/scaffold.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/planning/scaffold.js +164 -0
- package/dist/ui/overlay/data/tutorials/planning/sketch.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/planning/sketch.js +58 -0
- package/dist/ui/overlay/data/tutorials/projects/anchors.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/projects/anchors.js +248 -0
- package/dist/ui/overlay/data/tutorials/projects/import-project.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/projects/import-project.js +172 -0
- package/dist/ui/overlay/data/tutorials/projects/managing-projects.d.ts +8 -0
- package/dist/ui/overlay/data/tutorials/projects/managing-projects.js +212 -0
- package/dist/ui/overlay/data/tutorials/projects/new-project.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/projects/new-project.js +251 -0
- package/dist/ui/overlay/data/tutorials/projects/session-management.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/projects/session-management.js +169 -0
- package/dist/ui/overlay/data/tutorials/teams/background-execution.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/teams/background-execution.js +171 -0
- package/dist/ui/overlay/data/tutorials/teams/multi-terminal.d.ts +8 -0
- package/dist/ui/overlay/data/tutorials/teams/multi-terminal.js +147 -0
- package/dist/ui/overlay/data/tutorials/teams/task-assignment.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/teams/task-assignment.js +204 -0
- package/dist/ui/overlay/data/tutorials/teams/team-overview.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/teams/team-overview.js +165 -0
- package/dist/ui/overlay/data/tutorials/teams/working-with-agents.d.ts +7 -0
- package/dist/ui/overlay/data/tutorials/teams/working-with-agents.js +172 -0
- package/dist/ui/overlay/impl/agents-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/agents-overlay-v2.js +814 -0
- package/dist/ui/overlay/impl/anchors-overlay-v2.d.ts +47 -0
- package/dist/ui/overlay/impl/anchors-overlay-v2.js +749 -0
- package/dist/ui/overlay/impl/arch-type-overlay-v2.d.ts +37 -0
- package/dist/ui/overlay/impl/arch-type-overlay-v2.js +240 -0
- package/dist/ui/overlay/impl/artifact-detail-overlay-v2.d.ts +43 -0
- package/dist/ui/overlay/impl/artifact-detail-overlay-v2.js +232 -0
- package/dist/ui/overlay/impl/artifact-overlay-v2.d.ts +40 -0
- package/dist/ui/overlay/impl/artifact-overlay-v2.js +115 -0
- package/dist/ui/overlay/impl/ask-user-overlay-v2.d.ts +72 -0
- package/dist/ui/overlay/impl/ask-user-overlay-v2.js +581 -0
- package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.d.ts +46 -0
- package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.js +204 -0
- package/dist/ui/overlay/impl/background-overlay-v2.d.ts +40 -0
- package/dist/ui/overlay/impl/background-overlay-v2.js +147 -0
- package/dist/ui/overlay/impl/backlog-overlay-v2.d.ts +52 -0
- package/dist/ui/overlay/impl/backlog-overlay-v2.js +681 -0
- package/dist/ui/overlay/impl/changelog-overlay-v2.d.ts +44 -0
- package/dist/ui/overlay/impl/changelog-overlay-v2.js +165 -0
- package/dist/ui/overlay/impl/commands-overlay-v2.d.ts +33 -0
- package/dist/ui/overlay/impl/commands-overlay-v2.js +439 -0
- package/dist/ui/overlay/impl/config-overlay-v2.d.ts +111 -0
- package/dist/ui/overlay/impl/config-overlay-v2.js +718 -0
- package/dist/ui/overlay/impl/custom-agent-form-overlay-v2.d.ts +83 -0
- package/dist/ui/overlay/impl/custom-agent-form-overlay-v2.js +711 -0
- package/dist/ui/overlay/impl/dashboard-overlay-v2.d.ts +57 -0
- package/dist/ui/overlay/impl/dashboard-overlay-v2.js +382 -0
- package/dist/ui/overlay/impl/delegations-overlay-v2.d.ts +28 -0
- package/dist/ui/overlay/impl/delegations-overlay-v2.js +279 -0
- package/dist/ui/overlay/impl/docs-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/docs-overlay-v2.js +117 -0
- package/dist/ui/overlay/impl/document-detail-overlay-v2.d.ts +84 -0
- package/dist/ui/overlay/impl/document-detail-overlay-v2.js +1112 -0
- package/dist/ui/overlay/impl/filter-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/filter-overlay-v2.js +110 -0
- package/dist/ui/overlay/impl/games-overlay-v2.d.ts +31 -0
- package/dist/ui/overlay/impl/games-overlay-v2.js +135 -0
- package/dist/ui/overlay/impl/guardrail-overlay-v2.d.ts +43 -0
- package/dist/ui/overlay/impl/guardrail-overlay-v2.js +114 -0
- package/dist/ui/overlay/impl/help-overlay-v2.d.ts +57 -0
- package/dist/ui/overlay/impl/help-overlay-v2.js +287 -0
- package/dist/ui/overlay/impl/init-setup-overlay-v2.d.ts +25 -0
- package/dist/ui/overlay/impl/init-setup-overlay-v2.js +97 -0
- package/dist/ui/overlay/impl/iteration-limit-overlay-v2.d.ts +35 -0
- package/dist/ui/overlay/impl/iteration-limit-overlay-v2.js +105 -0
- package/dist/ui/overlay/impl/keys-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/keys-overlay-v2.js +248 -0
- package/dist/ui/overlay/impl/login-overlay-v2.d.ts +49 -0
- package/dist/ui/overlay/impl/login-overlay-v2.js +277 -0
- package/dist/ui/overlay/impl/mascot-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/mascot-overlay-v2.js +138 -0
- package/dist/ui/overlay/impl/mcp-overlay-v2.d.ts +63 -0
- package/dist/ui/overlay/impl/mcp-overlay-v2.js +907 -0
- package/dist/ui/overlay/impl/model-overlay-v2.d.ts +93 -0
- package/dist/ui/overlay/impl/model-overlay-v2.js +1143 -0
- package/dist/ui/overlay/impl/model-warning-overlay-v2.d.ts +46 -0
- package/dist/ui/overlay/impl/model-warning-overlay-v2.js +132 -0
- package/dist/ui/overlay/impl/new-overlay-v2.d.ts +108 -0
- package/dist/ui/overlay/impl/new-overlay-v2.js +1243 -0
- package/dist/ui/overlay/impl/notifications-overlay-v2.d.ts +20 -0
- package/dist/ui/overlay/impl/notifications-overlay-v2.js +116 -0
- package/dist/ui/overlay/impl/onboarding-wizard-overlay-v2.d.ts +76 -0
- package/dist/ui/overlay/impl/onboarding-wizard-overlay-v2.js +728 -0
- package/dist/ui/overlay/impl/pending-overlay-v2.d.ts +51 -0
- package/dist/ui/overlay/impl/pending-overlay-v2.js +445 -0
- package/dist/ui/overlay/impl/permission-overlay-v2.d.ts +36 -0
- package/dist/ui/overlay/impl/permission-overlay-v2.js +380 -0
- package/dist/ui/overlay/impl/permissions-overlay-v2.d.ts +85 -0
- package/dist/ui/overlay/impl/permissions-overlay-v2.js +820 -0
- package/dist/ui/overlay/impl/plan-approval-overlay-v2.d.ts +35 -0
- package/dist/ui/overlay/impl/plan-approval-overlay-v2.js +181 -0
- package/dist/ui/overlay/impl/project-edit-overlay-v2.d.ts +36 -0
- package/dist/ui/overlay/impl/project-edit-overlay-v2.js +195 -0
- package/dist/ui/overlay/impl/projects-overlay-v2.d.ts +37 -0
- package/dist/ui/overlay/impl/projects-overlay-v2.js +733 -0
- package/dist/ui/overlay/impl/reset-overlay-v2.d.ts +39 -0
- package/dist/ui/overlay/impl/reset-overlay-v2.js +107 -0
- package/dist/ui/overlay/impl/resume-overlay-v2.d.ts +60 -0
- package/dist/ui/overlay/impl/resume-overlay-v2.js +414 -0
- package/dist/ui/overlay/impl/session-mode-overlay-v2.d.ts +43 -0
- package/dist/ui/overlay/impl/session-mode-overlay-v2.js +124 -0
- package/dist/ui/overlay/impl/tasks-overlay-v2.d.ts +28 -0
- package/dist/ui/overlay/impl/tasks-overlay-v2.js +283 -0
- package/dist/ui/overlay/impl/team-overlay-v2.d.ts +86 -0
- package/dist/ui/overlay/impl/team-overlay-v2.js +692 -0
- package/dist/ui/overlay/impl/terminals-overlay-v2.d.ts +26 -0
- package/dist/ui/overlay/impl/terminals-overlay-v2.js +217 -0
- package/dist/ui/overlay/impl/theme-overlay-v2.d.ts +42 -0
- package/dist/ui/overlay/impl/theme-overlay-v2.js +135 -0
- package/dist/ui/overlay/impl/tools-overlay-v2.d.ts +47 -0
- package/dist/ui/overlay/impl/tools-overlay-v2.js +214 -0
- package/dist/ui/overlay/impl/tutorial-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/tutorial-overlay-v2.js +212 -0
- package/dist/ui/overlay/impl/workflow-overlay-v2.d.ts +80 -0
- package/dist/ui/overlay/impl/workflow-overlay-v2.js +641 -0
- package/dist/ui/overlay/index.d.ts +52 -0
- package/dist/ui/overlay/index.js +54 -0
- package/dist/ui/overlay/key-utils.d.ts +6 -0
- package/dist/ui/overlay/key-utils.js +6 -0
- package/dist/ui/overlay/types.d.ts +140 -0
- package/dist/ui/overlay/types.js +22 -0
- package/dist/ui/overlay-manager.d.ts +43 -0
- package/dist/ui/overlay-manager.js +238 -0
- package/dist/ui/overlays.d.ts +0 -4
- package/dist/ui/overlays.js +4 -460
- package/dist/ui/permission-overlay.d.ts +1 -1
- package/dist/ui/permission-overlay.js +189 -300
- package/dist/ui/providers/types.d.ts +178 -0
- package/dist/ui/providers/types.js +9 -0
- package/dist/ui/render-modes.d.ts +36 -0
- package/dist/ui/render-modes.js +44 -0
- package/dist/ui/startup-menu.d.ts +36 -0
- package/dist/ui/startup-menu.js +236 -0
- package/dist/ui/status-bar-controller.d.ts +33 -0
- package/dist/ui/status-bar-controller.js +99 -0
- package/dist/ui/subagent-renderer.d.ts +117 -0
- package/dist/ui/subagent-renderer.js +318 -0
- package/dist/ui/terminal-autocomplete-utils.d.ts +23 -0
- package/dist/ui/terminal-autocomplete-utils.js +83 -0
- package/dist/ui/terminal-codes.d.ts +94 -0
- package/dist/ui/terminal-codes.js +124 -0
- package/dist/ui/terminal-line-builders.d.ts +17 -0
- package/dist/ui/terminal-line-builders.js +42 -0
- package/dist/ui/terminal-render-item.d.ts +16 -0
- package/dist/ui/terminal-render-item.js +267 -0
- package/dist/ui/terminal-renderer.d.ts +220 -0
- package/dist/ui/terminal-renderer.js +750 -0
- package/dist/ui/terminal-types.d.ts +179 -0
- package/dist/ui/terminal-types.js +34 -0
- package/dist/ui/terminal-ui.d.ts +331 -0
- package/dist/ui/terminal-ui.js +819 -0
- package/dist/ui/terminal.d.ts +20 -0
- package/dist/ui/terminal.js +72 -0
- package/dist/ui/todo-zone.d.ts +19 -1
- package/dist/ui/todo-zone.js +124 -38
- package/dist/ui/tool-formatters.d.ts +16 -0
- package/dist/ui/tool-formatters.js +1027 -0
- package/dist/ui/turn-metrics.d.ts +56 -0
- package/dist/ui/turn-metrics.js +75 -0
- package/dist/ui/types.d.ts +28 -0
- package/dist/ui/types.js +1 -0
- package/dist/ui/vscode-diff-ipc.d.ts +102 -0
- package/dist/ui/vscode-diff-ipc.js +385 -0
- package/dist/utils/credentials.d.ts +24 -5
- package/dist/utils/credentials.js +123 -9
- package/dist/utils/debug-log.d.ts +28 -0
- package/dist/utils/debug-log.js +57 -0
- package/dist/utils/format-tokens.d.ts +13 -0
- package/dist/utils/format-tokens.js +18 -0
- package/dist/utils/git-config.d.ts +26 -0
- package/dist/utils/git-config.js +54 -0
- package/dist/utils/message-utils.d.ts +61 -0
- package/dist/utils/message-utils.js +72 -0
- package/dist/utils/model-tiers.d.ts +8 -1
- package/dist/utils/model-tiers.js +39 -17
- package/dist/utils/open-browser.d.ts +5 -0
- package/dist/utils/open-browser.js +32 -0
- package/dist/utils/path-safety.d.ts +56 -0
- package/dist/utils/path-safety.js +240 -0
- package/dist/utils/project-detection.d.ts +58 -0
- package/dist/utils/project-detection.js +424 -0
- package/dist/utils/project-memory.js +2 -1
- package/dist/utils/project-status.d.ts +2 -2
- package/dist/utils/startup-perf.d.ts +18 -0
- package/dist/utils/startup-perf.js +60 -0
- package/dist/utils/token-tracker.d.ts +62 -0
- package/dist/utils/token-tracker.js +150 -0
- package/dist/utils/token-types.d.ts +23 -0
- package/dist/utils/token-types.js +18 -0
- package/dist/utils/types/config-types.d.ts +32 -0
- package/dist/utils/types/config-types.js +8 -0
- package/dist/utils/update-checker.d.ts +28 -0
- package/dist/utils/update-checker.js +106 -0
- package/dist/utils/version.d.ts +7 -0
- package/dist/utils/version.js +10 -0
- package/dist/utils/vscode-detect.d.ts +39 -0
- package/dist/utils/vscode-detect.js +137 -0
- package/dist/workflow/guided-mode-injector.d.ts +42 -0
- package/dist/workflow/guided-mode-injector.js +191 -0
- package/dist/workflow/index.d.ts +8 -0
- package/dist/workflow/index.js +8 -0
- package/dist/workflow/step-criteria.d.ts +62 -0
- package/dist/workflow/step-criteria.js +150 -0
- package/dist/workflow/step-tracker.d.ts +92 -0
- package/dist/workflow/step-tracker.js +141 -0
- package/package.json +32 -12
- package/dist/index.old.d.ts +0 -7
- package/dist/index.old.js +0 -1014
- package/dist/repl.d.ts +0 -121
- package/dist/repl.js +0 -1878
- package/dist/templates/claude-md.d.ts +0 -7
- package/dist/templates/claude-md.js +0 -189
- package/dist/test-autocomplete.d.ts +0 -7
- package/dist/test-autocomplete.js +0 -85
- package/dist/test-tabbed-menu.d.ts +0 -7
- package/dist/test-tabbed-menu.js +0 -25
- package/dist/tool-selector.d.ts +0 -71
- package/dist/tool-selector.js +0 -184
- package/dist/ui/agents-overlay.d.ts +0 -12
- package/dist/ui/agents-overlay.js +0 -501
- package/dist/ui/arch-type-overlay.d.ts +0 -20
- package/dist/ui/arch-type-overlay.js +0 -229
- package/dist/ui/backlog-overlay.d.ts +0 -17
- package/dist/ui/backlog-overlay.js +0 -786
- package/dist/ui/commands-overlay.d.ts +0 -11
- package/dist/ui/commands-overlay.js +0 -410
- package/dist/ui/config-overlay.d.ts +0 -34
- package/dist/ui/config-overlay.js +0 -977
- package/dist/ui/init-overlay.d.ts +0 -24
- package/dist/ui/init-overlay.js +0 -525
- package/dist/ui/input-prompt-v2.d.ts +0 -179
- package/dist/ui/input-prompt-v2.js +0 -991
- package/dist/ui/model-warning-overlay.d.ts +0 -30
- package/dist/ui/model-warning-overlay.js +0 -171
- package/dist/ui/tools-overlay.d.ts +0 -26
- package/dist/ui/tools-overlay.js +0 -278
- package/dist/ui/tutorial-overlay.d.ts +0 -10
- package/dist/ui/tutorial-overlay.js +0 -936
|
@@ -0,0 +1,1143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Overlay V2
|
|
3
|
+
*
|
|
4
|
+
* Model selection overlay using TabbedListOverlayV2 for the new Overlay interface.
|
|
5
|
+
* Works with TerminalUI's overlay management.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Tabs by provider (Claude, OpenAI, Gemini, Ollama)
|
|
9
|
+
* - Detail view for each model with:
|
|
10
|
+
* - Tier info, provider, model name
|
|
11
|
+
* - Editable model ID
|
|
12
|
+
* - Test model (t), Set active (a), Reset default (d)
|
|
13
|
+
* - API key status checking
|
|
14
|
+
* - Persists selection to settings
|
|
15
|
+
*/
|
|
16
|
+
import { TabbedListOverlayV2, BaseScreen, pushScreen, popScreen, closeOverlay, stay, renderBorder, } from '../../base/index.js';
|
|
17
|
+
import { getSetting, setSetting } from '../../../settings/index.js';
|
|
18
|
+
import { hasApiKey, settingsProviderToCredentialKey } from '../../../utils/credentials.js';
|
|
19
|
+
import { TIER_INFO, getModelForTier, setTierOverride, clearTierOverride, validateModel, checkApiKeyStatus, getDefaultModelForTier, getModelInfo, getOthersProviders, getModelsForOthersProvider, getModelsForProvider, PROVIDER_METADATA,
|
|
20
|
+
// Ollama auto-detection
|
|
21
|
+
listOllamaModels, } from '../../../models/index.js';
|
|
22
|
+
import { hasApiKey as hasApiKeyForProvider, getMaskedKey, setApiKey, } from '../../../utils/credentials.js';
|
|
23
|
+
import { isCtrlC, isEscape, isClose, isEnter } from '../../base/key-utils.js';
|
|
24
|
+
import * as terminal from '../../terminal.js';
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// Constants
|
|
27
|
+
// =============================================================================
|
|
28
|
+
const PAGE_SIZE = 10;
|
|
29
|
+
const TIER_WIDTH = 14;
|
|
30
|
+
const LABEL_WIDTH = 14;
|
|
31
|
+
const MODEL_NAME_WIDTH = 28;
|
|
32
|
+
/** Primary providers that show tier-based selection */
|
|
33
|
+
const PRIMARY_PROVIDERS = ['claude', 'openai', 'gemini', 'ollama'];
|
|
34
|
+
const PROVIDER_TABS = [
|
|
35
|
+
{ id: 'claude', label: 'Claude' },
|
|
36
|
+
{ id: 'openai', label: 'OpenAI' },
|
|
37
|
+
{ id: 'gemini', label: 'Gemini' },
|
|
38
|
+
{ id: 'ollama', label: 'Ollama (local)' },
|
|
39
|
+
{ id: 'others', label: 'Others' },
|
|
40
|
+
];
|
|
41
|
+
/** All model tiers in display order */
|
|
42
|
+
const ALL_TIERS = ['fast', 'balanced', 'powerful'];
|
|
43
|
+
/**
|
|
44
|
+
* Get list items - tier items for primary providers, provider items for Others.
|
|
45
|
+
*/
|
|
46
|
+
function getListItems() {
|
|
47
|
+
const items = [];
|
|
48
|
+
// For each primary provider, add 3 tier items
|
|
49
|
+
for (const provider of PRIMARY_PROVIDERS) {
|
|
50
|
+
for (const tier of ALL_TIERS) {
|
|
51
|
+
// Get default model from registry
|
|
52
|
+
const defaultModel = getDefaultModelForTier(provider, tier);
|
|
53
|
+
// Get current model (may have user override)
|
|
54
|
+
const currentModelId = getModelForTier(provider, tier);
|
|
55
|
+
const currentModelInfo = getModelInfo(currentModelId);
|
|
56
|
+
items.push({
|
|
57
|
+
type: 'tier',
|
|
58
|
+
tier,
|
|
59
|
+
provider,
|
|
60
|
+
defaultModelId: defaultModel?.id ?? currentModelId,
|
|
61
|
+
defaultModelName: defaultModel?.displayName ?? currentModelId,
|
|
62
|
+
currentModelId,
|
|
63
|
+
currentModelName: currentModelInfo?.displayName ?? currentModelId,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Get providers for "Others" tab
|
|
68
|
+
const providers = getOthersProviders();
|
|
69
|
+
for (const p of providers) {
|
|
70
|
+
items.push({
|
|
71
|
+
type: 'provider',
|
|
72
|
+
metadata: p,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return items;
|
|
76
|
+
}
|
|
77
|
+
// =============================================================================
|
|
78
|
+
// Key Input Screen (for setting API keys from main list)
|
|
79
|
+
// =============================================================================
|
|
80
|
+
/**
|
|
81
|
+
* Simple screen for entering an API key.
|
|
82
|
+
* Used when pressing 's' from the main model list.
|
|
83
|
+
*/
|
|
84
|
+
class KeyInputScreen extends BaseScreen {
|
|
85
|
+
provider;
|
|
86
|
+
providerName;
|
|
87
|
+
styles;
|
|
88
|
+
onKeySaved;
|
|
89
|
+
keyInput = '';
|
|
90
|
+
cursorPos = 0;
|
|
91
|
+
constructor(provider, providerName, styles, onKeySaved) {
|
|
92
|
+
super();
|
|
93
|
+
this.provider = provider;
|
|
94
|
+
this.providerName = providerName;
|
|
95
|
+
this.styles = styles;
|
|
96
|
+
this.onKeySaved = onKeySaved;
|
|
97
|
+
}
|
|
98
|
+
render() {
|
|
99
|
+
const s = this.styles;
|
|
100
|
+
const cols = terminal.getTerminalWidth();
|
|
101
|
+
const border = renderBorder(cols, s);
|
|
102
|
+
const lines = [];
|
|
103
|
+
lines.push(border);
|
|
104
|
+
lines.push(` ${s.primaryBold('Set API Key')}`);
|
|
105
|
+
lines.push(border);
|
|
106
|
+
lines.push('');
|
|
107
|
+
lines.push(` Set API key for ${s.primary(this.providerName)}:`);
|
|
108
|
+
lines.push('');
|
|
109
|
+
// Input field
|
|
110
|
+
const beforeCursor = this.keyInput.slice(0, this.cursorPos);
|
|
111
|
+
const afterCursor = this.keyInput.slice(this.cursorPos);
|
|
112
|
+
const cursor = s.primary('█');
|
|
113
|
+
const placeholder = this.keyInput ? '' : s.muted('Paste or type your API key...');
|
|
114
|
+
lines.push(` > ${beforeCursor}${cursor}${afterCursor}${placeholder}`);
|
|
115
|
+
lines.push('');
|
|
116
|
+
// Help text
|
|
117
|
+
const metadata = PROVIDER_METADATA[this.provider];
|
|
118
|
+
if (metadata.keyUrl) {
|
|
119
|
+
lines.push(` ${s.muted(`Get key: ${metadata.keyUrl}`)}`);
|
|
120
|
+
lines.push('');
|
|
121
|
+
}
|
|
122
|
+
lines.push(border);
|
|
123
|
+
lines.push(` ${s.muted('Enter Save · Esc Cancel')}`);
|
|
124
|
+
lines.push(border);
|
|
125
|
+
return lines;
|
|
126
|
+
}
|
|
127
|
+
handleKey(data) {
|
|
128
|
+
// Ctrl+C closes overlay
|
|
129
|
+
if (isCtrlC(data)) {
|
|
130
|
+
return closeOverlay();
|
|
131
|
+
}
|
|
132
|
+
// Escape goes back
|
|
133
|
+
if (isEscape(data)) {
|
|
134
|
+
return popScreen();
|
|
135
|
+
}
|
|
136
|
+
// Enter saves the key
|
|
137
|
+
if (isEnter(data)) {
|
|
138
|
+
const value = this.keyInput.trim();
|
|
139
|
+
if (value) {
|
|
140
|
+
const metadata = PROVIDER_METADATA[this.provider];
|
|
141
|
+
setApiKey(metadata.credentialKey, value);
|
|
142
|
+
this.onKeySaved();
|
|
143
|
+
}
|
|
144
|
+
return popScreen();
|
|
145
|
+
}
|
|
146
|
+
// Handle text input
|
|
147
|
+
const char = data.toString();
|
|
148
|
+
// Backspace
|
|
149
|
+
if (data[0] === 0x7f || data[0] === 0x08) {
|
|
150
|
+
if (this.cursorPos > 0) {
|
|
151
|
+
this.keyInput = this.keyInput.slice(0, this.cursorPos - 1) + this.keyInput.slice(this.cursorPos);
|
|
152
|
+
this.cursorPos--;
|
|
153
|
+
}
|
|
154
|
+
return stay(true);
|
|
155
|
+
}
|
|
156
|
+
// Left arrow
|
|
157
|
+
if (data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x44) {
|
|
158
|
+
if (this.cursorPos > 0)
|
|
159
|
+
this.cursorPos--;
|
|
160
|
+
return stay(true);
|
|
161
|
+
}
|
|
162
|
+
// Right arrow
|
|
163
|
+
if (data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x43) {
|
|
164
|
+
if (this.cursorPos < this.keyInput.length)
|
|
165
|
+
this.cursorPos++;
|
|
166
|
+
return stay(true);
|
|
167
|
+
}
|
|
168
|
+
// Printable characters
|
|
169
|
+
if (char.length === 1 && char.charCodeAt(0) >= 32 && char.charCodeAt(0) < 127) {
|
|
170
|
+
this.keyInput = this.keyInput.slice(0, this.cursorPos) + char + this.keyInput.slice(this.cursorPos);
|
|
171
|
+
this.cursorPos++;
|
|
172
|
+
return stay(true);
|
|
173
|
+
}
|
|
174
|
+
// Handle paste (multiple characters)
|
|
175
|
+
if (char.length > 1) {
|
|
176
|
+
const printable = char.replace(/[^\x20-\x7e]/g, '');
|
|
177
|
+
this.keyInput = this.keyInput.slice(0, this.cursorPos) + printable + this.keyInput.slice(this.cursorPos);
|
|
178
|
+
this.cursorPos += printable.length;
|
|
179
|
+
return stay(true);
|
|
180
|
+
}
|
|
181
|
+
return stay(false);
|
|
182
|
+
}
|
|
183
|
+
getMinHeight() {
|
|
184
|
+
return 12;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// =============================================================================
|
|
188
|
+
// Others Tab - Provider Detail Screen (with 3-tier models)
|
|
189
|
+
// =============================================================================
|
|
190
|
+
/**
|
|
191
|
+
* Screen showing a specific "Others" provider's details and 3-tier models.
|
|
192
|
+
*/
|
|
193
|
+
class OthersProviderDetailScreen extends BaseScreen {
|
|
194
|
+
provider;
|
|
195
|
+
styles;
|
|
196
|
+
activeModel;
|
|
197
|
+
activeProvider;
|
|
198
|
+
requestRender;
|
|
199
|
+
selectedIndex = 0;
|
|
200
|
+
models;
|
|
201
|
+
editingKey = false;
|
|
202
|
+
keyInput = '';
|
|
203
|
+
keyCursorPos = 0;
|
|
204
|
+
message = null;
|
|
205
|
+
messageType = null;
|
|
206
|
+
constructor(provider, styles, activeModel, activeProvider, requestRender) {
|
|
207
|
+
super();
|
|
208
|
+
this.provider = provider;
|
|
209
|
+
this.styles = styles;
|
|
210
|
+
this.activeModel = activeModel;
|
|
211
|
+
this.activeProvider = activeProvider;
|
|
212
|
+
this.requestRender = requestRender;
|
|
213
|
+
this.models = getModelsForOthersProvider(provider.type);
|
|
214
|
+
}
|
|
215
|
+
render() {
|
|
216
|
+
const s = this.styles;
|
|
217
|
+
const cols = terminal.getTerminalWidth();
|
|
218
|
+
const border = renderBorder(cols, s);
|
|
219
|
+
const lines = [];
|
|
220
|
+
// Header
|
|
221
|
+
lines.push(border);
|
|
222
|
+
lines.push(` ${s.primaryBold(this.provider.displayName)}`);
|
|
223
|
+
lines.push(border);
|
|
224
|
+
lines.push('');
|
|
225
|
+
// Provider info
|
|
226
|
+
const hasKey = hasApiKeyForProvider(this.provider.credentialKey);
|
|
227
|
+
const keyIndicator = hasKey ? s.success('● Set') : s.error('○ Not set');
|
|
228
|
+
const maskedKey = hasKey ? getMaskedKey(this.provider.credentialKey) : null;
|
|
229
|
+
const keyDisplay = maskedKey ? s.muted(` (${maskedKey})`) : '';
|
|
230
|
+
if (this.editingKey) {
|
|
231
|
+
const prefix = s.primary(' > ');
|
|
232
|
+
const label = 'API Key:'.padEnd(12);
|
|
233
|
+
const beforeCursor = this.keyInput.slice(0, this.keyCursorPos);
|
|
234
|
+
const afterCursor = this.keyInput.slice(this.keyCursorPos);
|
|
235
|
+
const cursor = s.primary('█');
|
|
236
|
+
lines.push(`${prefix}${s.muted(label)}${beforeCursor}${cursor}${afterCursor}`);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
lines.push(` ${s.muted('API Key:'.padEnd(12))}${keyIndicator}${keyDisplay}`);
|
|
240
|
+
}
|
|
241
|
+
lines.push(` ${s.muted('Endpoint:'.padEnd(12))}${s.muted(this.provider.endpoint)}`);
|
|
242
|
+
lines.push(` ${s.muted('Env Var:'.padEnd(12))}${s.muted(this.provider.envVar)}`);
|
|
243
|
+
lines.push('');
|
|
244
|
+
// Models section
|
|
245
|
+
lines.push(` ${s.muted('─── Models ───')}`);
|
|
246
|
+
lines.push('');
|
|
247
|
+
// Tier-first column layout (as per spec)
|
|
248
|
+
lines.push(` ${s.muted('Tier'.padEnd(12))}${s.muted('Model')}`);
|
|
249
|
+
lines.push(` ${s.muted('─'.repeat(40))}`);
|
|
250
|
+
for (let i = 0; i < this.models.length; i++) {
|
|
251
|
+
const model = this.models[i];
|
|
252
|
+
const isSelected = i === this.selectedIndex && !this.editingKey;
|
|
253
|
+
const prefix = isSelected ? s.primary(' > ') : ' ';
|
|
254
|
+
// Check if this model is active
|
|
255
|
+
const isActive = model.id === this.activeModel && this.provider.type === this.activeProvider;
|
|
256
|
+
const tierLabel = model.tier.padEnd(12);
|
|
257
|
+
const modelName = model.displayName;
|
|
258
|
+
const activeBadge = isActive ? s.success(' (active)') : '';
|
|
259
|
+
const tierStyle = isSelected ? s.primary : (str) => str;
|
|
260
|
+
const nameStyle = isSelected ? s.primary : (str) => str;
|
|
261
|
+
lines.push(`${prefix}${tierStyle(tierLabel)}${nameStyle(modelName)}${activeBadge}`);
|
|
262
|
+
}
|
|
263
|
+
lines.push('');
|
|
264
|
+
// Message display
|
|
265
|
+
if (this.message) {
|
|
266
|
+
const messageStyle = this.messageType === 'error'
|
|
267
|
+
? s.error
|
|
268
|
+
: this.messageType === 'success'
|
|
269
|
+
? s.success
|
|
270
|
+
: s.muted;
|
|
271
|
+
lines.push(` ${messageStyle(this.message)}`);
|
|
272
|
+
lines.push('');
|
|
273
|
+
}
|
|
274
|
+
// Footer
|
|
275
|
+
lines.push(border);
|
|
276
|
+
if (this.editingKey) {
|
|
277
|
+
lines.push(` ${s.muted('Enter Save · Esc Cancel')}`);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
lines.push(` ${s.muted('↑↓ Select · Enter Details · s Set Key · Esc Back')}`);
|
|
281
|
+
}
|
|
282
|
+
lines.push(border);
|
|
283
|
+
return lines;
|
|
284
|
+
}
|
|
285
|
+
handleKey(data) {
|
|
286
|
+
// Clear message on any key
|
|
287
|
+
if (this.message && !this.editingKey) {
|
|
288
|
+
this.message = null;
|
|
289
|
+
this.messageType = null;
|
|
290
|
+
}
|
|
291
|
+
if (this.editingKey) {
|
|
292
|
+
return this.handleKeyEdit(data);
|
|
293
|
+
}
|
|
294
|
+
// Ctrl+C closes overlay
|
|
295
|
+
if (isCtrlC(data)) {
|
|
296
|
+
return closeOverlay();
|
|
297
|
+
}
|
|
298
|
+
// Esc goes back
|
|
299
|
+
if (isEscape(data) || isClose(data)) {
|
|
300
|
+
return popScreen();
|
|
301
|
+
}
|
|
302
|
+
// Check for arrow keys
|
|
303
|
+
if (data[0] === 0x1b && data[1] === 0x5b) {
|
|
304
|
+
// Up arrow
|
|
305
|
+
if (data[2] === 0x41) {
|
|
306
|
+
if (this.selectedIndex > 0) {
|
|
307
|
+
this.selectedIndex--;
|
|
308
|
+
}
|
|
309
|
+
return stay(true);
|
|
310
|
+
}
|
|
311
|
+
// Down arrow
|
|
312
|
+
if (data[2] === 0x42) {
|
|
313
|
+
if (this.selectedIndex < this.models.length - 1) {
|
|
314
|
+
this.selectedIndex++;
|
|
315
|
+
}
|
|
316
|
+
return stay(true);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const char = data.toString();
|
|
320
|
+
// j/k vim navigation
|
|
321
|
+
if (char === 'j' && this.selectedIndex < this.models.length - 1) {
|
|
322
|
+
this.selectedIndex++;
|
|
323
|
+
return stay(true);
|
|
324
|
+
}
|
|
325
|
+
if (char === 'k' && this.selectedIndex > 0) {
|
|
326
|
+
this.selectedIndex--;
|
|
327
|
+
return stay(true);
|
|
328
|
+
}
|
|
329
|
+
// 's' - set API key
|
|
330
|
+
if (char === 's') {
|
|
331
|
+
this.editingKey = true;
|
|
332
|
+
this.keyInput = '';
|
|
333
|
+
this.keyCursorPos = 0;
|
|
334
|
+
return stay(true);
|
|
335
|
+
}
|
|
336
|
+
// Enter - open model detail screen (consistent with other provider tabs)
|
|
337
|
+
if (isEnter(data)) {
|
|
338
|
+
const selectedModel = this.models[this.selectedIndex];
|
|
339
|
+
// Convert OthersProviderModel to TierItem for detail screen
|
|
340
|
+
const tierItem = {
|
|
341
|
+
type: 'tier',
|
|
342
|
+
tier: selectedModel.tier,
|
|
343
|
+
provider: this.provider.type,
|
|
344
|
+
defaultModelId: selectedModel.id,
|
|
345
|
+
defaultModelName: selectedModel.displayName,
|
|
346
|
+
currentModelId: getModelForTier(this.provider.type, selectedModel.tier),
|
|
347
|
+
currentModelName: selectedModel.displayName,
|
|
348
|
+
};
|
|
349
|
+
return pushScreen(new TierDetailScreen(tierItem, this.styles, this.activeModel, this.activeProvider, { status: 'offline', availableModels: new Set() }, // Others providers don't use Ollama state
|
|
350
|
+
this.requestRender));
|
|
351
|
+
}
|
|
352
|
+
return stay(false);
|
|
353
|
+
}
|
|
354
|
+
handleKeyEdit(data) {
|
|
355
|
+
// Escape - cancel edit
|
|
356
|
+
if (isEscape(data) || isCtrlC(data)) {
|
|
357
|
+
this.editingKey = false;
|
|
358
|
+
this.keyInput = '';
|
|
359
|
+
this.keyCursorPos = 0;
|
|
360
|
+
return stay(true);
|
|
361
|
+
}
|
|
362
|
+
// Enter - save key
|
|
363
|
+
if (isEnter(data)) {
|
|
364
|
+
const key = this.keyInput.trim();
|
|
365
|
+
if (key) {
|
|
366
|
+
// Import setApiKey dynamically to avoid circular deps
|
|
367
|
+
import('../../../utils/credentials.js').then(({ setApiKey }) => {
|
|
368
|
+
setApiKey(this.provider.credentialKey, key);
|
|
369
|
+
this.message = 'API key saved';
|
|
370
|
+
this.messageType = 'success';
|
|
371
|
+
this.requestRender();
|
|
372
|
+
}).catch(() => {
|
|
373
|
+
this.message = 'Failed to save API key';
|
|
374
|
+
this.messageType = 'error';
|
|
375
|
+
this.requestRender();
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
this.editingKey = false;
|
|
379
|
+
this.keyInput = '';
|
|
380
|
+
this.keyCursorPos = 0;
|
|
381
|
+
return stay(true);
|
|
382
|
+
}
|
|
383
|
+
// Left arrow
|
|
384
|
+
if (data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x44) {
|
|
385
|
+
if (this.keyCursorPos > 0) {
|
|
386
|
+
this.keyCursorPos--;
|
|
387
|
+
}
|
|
388
|
+
return stay(true);
|
|
389
|
+
}
|
|
390
|
+
// Right arrow
|
|
391
|
+
if (data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x43) {
|
|
392
|
+
if (this.keyCursorPos < this.keyInput.length) {
|
|
393
|
+
this.keyCursorPos++;
|
|
394
|
+
}
|
|
395
|
+
return stay(true);
|
|
396
|
+
}
|
|
397
|
+
// Backspace
|
|
398
|
+
if (data[0] === 127 || data[0] === 8) {
|
|
399
|
+
if (this.keyCursorPos > 0) {
|
|
400
|
+
const before = this.keyInput.slice(0, this.keyCursorPos - 1);
|
|
401
|
+
const after = this.keyInput.slice(this.keyCursorPos);
|
|
402
|
+
this.keyInput = before + after;
|
|
403
|
+
this.keyCursorPos--;
|
|
404
|
+
}
|
|
405
|
+
return stay(true);
|
|
406
|
+
}
|
|
407
|
+
// Printable character
|
|
408
|
+
const char = data.toString();
|
|
409
|
+
if (char.length === 1 && char.charCodeAt(0) >= 32 && char.charCodeAt(0) < 127) {
|
|
410
|
+
const before = this.keyInput.slice(0, this.keyCursorPos);
|
|
411
|
+
const after = this.keyInput.slice(this.keyCursorPos);
|
|
412
|
+
this.keyInput = before + char + after;
|
|
413
|
+
this.keyCursorPos++;
|
|
414
|
+
return stay(true);
|
|
415
|
+
}
|
|
416
|
+
return stay(false);
|
|
417
|
+
}
|
|
418
|
+
getMinHeight() {
|
|
419
|
+
return 22;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const DEFAULT_OLLAMA_URL = 'http://localhost:11434';
|
|
423
|
+
class TierDetailScreen extends BaseScreen {
|
|
424
|
+
tierItem;
|
|
425
|
+
styles;
|
|
426
|
+
state;
|
|
427
|
+
currentModelId;
|
|
428
|
+
currentOllamaUrl;
|
|
429
|
+
activeModel;
|
|
430
|
+
activeProvider;
|
|
431
|
+
ollamaState;
|
|
432
|
+
requestRenderFn;
|
|
433
|
+
/** Available models for cycling */
|
|
434
|
+
availableModels;
|
|
435
|
+
constructor(tierItem, styles, activeModel, activeProvider, ollamaState, requestRender) {
|
|
436
|
+
super();
|
|
437
|
+
this.tierItem = tierItem;
|
|
438
|
+
this.styles = styles;
|
|
439
|
+
this.activeModel = activeModel;
|
|
440
|
+
this.activeProvider = activeProvider;
|
|
441
|
+
this.ollamaState = ollamaState;
|
|
442
|
+
this.requestRenderFn = requestRender;
|
|
443
|
+
this.currentModelId = tierItem.currentModelId;
|
|
444
|
+
const settingUrl = getSetting('ollamaBaseUrl');
|
|
445
|
+
this.currentOllamaUrl = typeof settingUrl === 'string' ? settingUrl : DEFAULT_OLLAMA_URL;
|
|
446
|
+
this.state = {
|
|
447
|
+
editMode: false,
|
|
448
|
+
// For Ollama, start with URL field; otherwise just model
|
|
449
|
+
selectedField: tierItem.provider === 'ollama' ? 'url' : 'model',
|
|
450
|
+
editValue: '',
|
|
451
|
+
cursorPos: 0,
|
|
452
|
+
testing: false,
|
|
453
|
+
message: null,
|
|
454
|
+
messageType: null,
|
|
455
|
+
};
|
|
456
|
+
// Build available models list for cycling
|
|
457
|
+
this.availableModels = this.buildAvailableModels();
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Build list of available models for this tier from registry.
|
|
461
|
+
* For Ollama: also includes locally installed models.
|
|
462
|
+
*/
|
|
463
|
+
buildAvailableModels() {
|
|
464
|
+
const models = [];
|
|
465
|
+
const registryModels = getModelsForProvider(this.tierItem.provider);
|
|
466
|
+
// Add registry models (supported ones)
|
|
467
|
+
for (const model of registryModels) {
|
|
468
|
+
if (model.status !== 'supported')
|
|
469
|
+
continue;
|
|
470
|
+
const isDefault = model.id === this.tierItem.defaultModelId;
|
|
471
|
+
// For Ollama: check if locally available
|
|
472
|
+
let isLocal;
|
|
473
|
+
if (this.tierItem.provider === 'ollama') {
|
|
474
|
+
const modelName = model.id.toLowerCase();
|
|
475
|
+
const baseName = modelName.split(':')[0];
|
|
476
|
+
isLocal = this.ollamaState.availableModels.has(modelName) ||
|
|
477
|
+
this.ollamaState.availableModels.has(baseName);
|
|
478
|
+
}
|
|
479
|
+
models.push({
|
|
480
|
+
id: model.id,
|
|
481
|
+
displayName: model.displayName,
|
|
482
|
+
isDefault,
|
|
483
|
+
isLocal,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
// For Ollama: add any local models not in registry
|
|
487
|
+
if (this.tierItem.provider === 'ollama') {
|
|
488
|
+
const registryIds = new Set(models.map(m => m.id.toLowerCase()));
|
|
489
|
+
for (const localModel of this.ollamaState.availableModels) {
|
|
490
|
+
// Skip base names (we only want full model:tag names)
|
|
491
|
+
if (!localModel.includes(':') && this.ollamaState.availableModels.has(localModel + ':latest')) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
if (!registryIds.has(localModel)) {
|
|
495
|
+
models.push({
|
|
496
|
+
id: localModel,
|
|
497
|
+
displayName: localModel,
|
|
498
|
+
isDefault: false,
|
|
499
|
+
isLocal: true,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
// Sort: default first, then by name
|
|
505
|
+
models.sort((a, b) => {
|
|
506
|
+
if (a.isDefault && !b.isDefault)
|
|
507
|
+
return -1;
|
|
508
|
+
if (!a.isDefault && b.isDefault)
|
|
509
|
+
return 1;
|
|
510
|
+
return a.displayName.localeCompare(b.displayName);
|
|
511
|
+
});
|
|
512
|
+
return models;
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Get current model index in available models list.
|
|
516
|
+
* Returns -1 if current model is custom (not in list).
|
|
517
|
+
*/
|
|
518
|
+
getCurrentModelIndex() {
|
|
519
|
+
return this.availableModels.findIndex(m => m.id === this.currentModelId);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Cycle to next available model.
|
|
523
|
+
*/
|
|
524
|
+
cycleToNextModel() {
|
|
525
|
+
if (this.availableModels.length === 0)
|
|
526
|
+
return;
|
|
527
|
+
const currentIndex = this.getCurrentModelIndex();
|
|
528
|
+
const nextIndex = (currentIndex + 1) % this.availableModels.length;
|
|
529
|
+
const nextModel = this.availableModels[nextIndex];
|
|
530
|
+
this.currentModelId = nextModel.id;
|
|
531
|
+
// Persist the change
|
|
532
|
+
if (nextModel.isDefault) {
|
|
533
|
+
clearTierOverride(this.tierItem.provider, this.tierItem.tier);
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
setTierOverride(this.tierItem.provider, this.tierItem.tier, nextModel.id);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Cycle to previous available model.
|
|
541
|
+
*/
|
|
542
|
+
cycleToPrevModel() {
|
|
543
|
+
if (this.availableModels.length === 0)
|
|
544
|
+
return;
|
|
545
|
+
const currentIndex = this.getCurrentModelIndex();
|
|
546
|
+
const prevIndex = currentIndex <= 0
|
|
547
|
+
? this.availableModels.length - 1
|
|
548
|
+
: currentIndex - 1;
|
|
549
|
+
const prevModel = this.availableModels[prevIndex];
|
|
550
|
+
this.currentModelId = prevModel.id;
|
|
551
|
+
// Persist the change
|
|
552
|
+
if (prevModel.isDefault) {
|
|
553
|
+
clearTierOverride(this.tierItem.provider, this.tierItem.tier);
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
setTierOverride(this.tierItem.provider, this.tierItem.tier, prevModel.id);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
render() {
|
|
560
|
+
const s = this.styles;
|
|
561
|
+
const cols = terminal.getTerminalWidth();
|
|
562
|
+
const border = renderBorder(cols, s);
|
|
563
|
+
const lines = [];
|
|
564
|
+
// Header
|
|
565
|
+
lines.push(border);
|
|
566
|
+
lines.push(` ${s.primaryBold('Tier Configuration')}`);
|
|
567
|
+
lines.push(border);
|
|
568
|
+
lines.push('');
|
|
569
|
+
// Provider
|
|
570
|
+
const providerLabel = this.tierItem.provider.charAt(0).toUpperCase() + this.tierItem.provider.slice(1);
|
|
571
|
+
lines.push(` ${s.muted('Provider:'.padEnd(LABEL_WIDTH))}${providerLabel}`);
|
|
572
|
+
// Tier
|
|
573
|
+
const tierInfo = TIER_INFO[this.tierItem.tier];
|
|
574
|
+
lines.push(` ${s.muted('Tier:'.padEnd(LABEL_WIDTH))}${tierInfo.label} ${s.muted(`- ${tierInfo.shortDescription}`)}`);
|
|
575
|
+
lines.push('');
|
|
576
|
+
// API Key Status (not shown for Ollama - no auth needed)
|
|
577
|
+
if (this.tierItem.provider !== 'ollama') {
|
|
578
|
+
const keyStatus = checkApiKeyStatus(this.tierItem.provider);
|
|
579
|
+
const keyIndicator = keyStatus.hasKey ? s.success('● Set') : s.error('○ Not set');
|
|
580
|
+
const keyHint = !keyStatus.hasKey && keyStatus.hint ? s.warning(` (${keyStatus.hint})`) : '';
|
|
581
|
+
lines.push(` ${s.muted('API Key:'.padEnd(LABEL_WIDTH))}${keyIndicator}${keyHint}`);
|
|
582
|
+
lines.push('');
|
|
583
|
+
}
|
|
584
|
+
// Ollama Server URL (only for Ollama provider)
|
|
585
|
+
if (this.tierItem.provider === 'ollama') {
|
|
586
|
+
const isUrlSelected = this.state.editMode && this.state.selectedField === 'url';
|
|
587
|
+
const sectionHeader = isUrlSelected ? s.primary('─── Server URL ───') : s.muted('─── Server URL ───');
|
|
588
|
+
lines.push(` ${sectionHeader}`);
|
|
589
|
+
lines.push('');
|
|
590
|
+
// Default URL first (consistent order)
|
|
591
|
+
lines.push(` ${s.muted('Default:'.padEnd(LABEL_WIDTH))}${s.muted(DEFAULT_OLLAMA_URL)}`);
|
|
592
|
+
// Current URL (editable)
|
|
593
|
+
const isDefaultUrl = this.currentOllamaUrl === DEFAULT_OLLAMA_URL;
|
|
594
|
+
if (isUrlSelected) {
|
|
595
|
+
const prefix = s.primary(' > ');
|
|
596
|
+
const beforeCursor = this.state.editValue.slice(0, this.state.cursorPos);
|
|
597
|
+
const afterCursor = this.state.editValue.slice(this.state.cursorPos);
|
|
598
|
+
const cursor = s.primary('█');
|
|
599
|
+
lines.push(`${prefix}${s.muted('Current:'.padEnd(LABEL_WIDTH - 3))}${beforeCursor}${cursor}${afterCursor}`);
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
const urlStyle = isDefaultUrl ? (str) => str : s.success;
|
|
603
|
+
const urlBadge = isDefaultUrl ? '' : s.success(' (custom)');
|
|
604
|
+
lines.push(` ${s.muted('Current:'.padEnd(LABEL_WIDTH))}${urlStyle(this.currentOllamaUrl)}${urlBadge}`);
|
|
605
|
+
}
|
|
606
|
+
lines.push('');
|
|
607
|
+
}
|
|
608
|
+
// Model ID section
|
|
609
|
+
const isModelSelected = this.state.editMode && this.state.selectedField === 'model';
|
|
610
|
+
const modelHeader = isModelSelected ? s.primary('─── Model ID ───') : s.muted('─── Model ID ───');
|
|
611
|
+
lines.push(` ${modelHeader}`);
|
|
612
|
+
lines.push('');
|
|
613
|
+
// Default Model ID first (consistent order)
|
|
614
|
+
lines.push(` ${s.muted('Default:'.padEnd(LABEL_WIDTH))}${s.muted(this.tierItem.defaultModelId)}`);
|
|
615
|
+
// Current Model ID (with cycling indicator)
|
|
616
|
+
const isOverride = this.currentModelId !== this.tierItem.defaultModelId;
|
|
617
|
+
const currentIndex = this.getCurrentModelIndex();
|
|
618
|
+
const isCustomModel = currentIndex === -1;
|
|
619
|
+
if (isModelSelected) {
|
|
620
|
+
// Edit mode - show text input
|
|
621
|
+
const prefix = s.primary(' > ');
|
|
622
|
+
const beforeCursor = this.state.editValue.slice(0, this.state.cursorPos);
|
|
623
|
+
const afterCursor = this.state.editValue.slice(this.state.cursorPos);
|
|
624
|
+
const cursor = s.primary('█');
|
|
625
|
+
lines.push(`${prefix}${s.muted('Current:'.padEnd(LABEL_WIDTH - 3))}${beforeCursor}${cursor}${afterCursor}`);
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
// Normal mode - show current with cycling info
|
|
629
|
+
const currentStyle = isOverride ? s.success : (str) => str;
|
|
630
|
+
// Build badges
|
|
631
|
+
const badges = [];
|
|
632
|
+
if (isOverride && !isCustomModel)
|
|
633
|
+
badges.push(s.success('custom'));
|
|
634
|
+
if (isCustomModel)
|
|
635
|
+
badges.push(s.warning('manual'));
|
|
636
|
+
// Cycling indicator: (2/5) or (manual)
|
|
637
|
+
let cycleIndicator = '';
|
|
638
|
+
if (this.availableModels.length > 0) {
|
|
639
|
+
if (isCustomModel) {
|
|
640
|
+
cycleIndicator = s.muted(` [manual]`);
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
cycleIndicator = s.muted(` [${String(currentIndex + 1)}/${String(this.availableModels.length)}]`);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
// Get display name for current model
|
|
647
|
+
const currentModelInfo = this.availableModels.find(m => m.id === this.currentModelId);
|
|
648
|
+
const displayName = currentModelInfo?.displayName ?? this.currentModelId;
|
|
649
|
+
// For Ollama: show local availability
|
|
650
|
+
let localBadge = '';
|
|
651
|
+
if (this.tierItem.provider === 'ollama' && currentModelInfo) {
|
|
652
|
+
localBadge = currentModelInfo.isLocal ? s.success(' ●') : s.warning(' ○');
|
|
653
|
+
}
|
|
654
|
+
lines.push(` ${s.muted('Current:'.padEnd(LABEL_WIDTH))}${currentStyle(displayName)}${localBadge}${cycleIndicator}`);
|
|
655
|
+
}
|
|
656
|
+
lines.push('');
|
|
657
|
+
// Active status
|
|
658
|
+
const isActive = this.currentModelId === this.activeModel && this.tierItem.provider === this.activeProvider;
|
|
659
|
+
if (isActive) {
|
|
660
|
+
lines.push(` ${s.success('✓ This is the active model')}`);
|
|
661
|
+
}
|
|
662
|
+
lines.push('');
|
|
663
|
+
// Message display
|
|
664
|
+
if (this.state.message) {
|
|
665
|
+
const messageStyle = this.state.messageType === 'error'
|
|
666
|
+
? s.error
|
|
667
|
+
: this.state.messageType === 'success'
|
|
668
|
+
? s.success
|
|
669
|
+
: s.muted;
|
|
670
|
+
lines.push(` ${messageStyle(this.state.message)}`);
|
|
671
|
+
lines.push('');
|
|
672
|
+
}
|
|
673
|
+
// Footer
|
|
674
|
+
lines.push(border);
|
|
675
|
+
if (this.state.testing) {
|
|
676
|
+
lines.push(` ${s.warning('Testing model...')}`);
|
|
677
|
+
}
|
|
678
|
+
else if (this.state.editMode) {
|
|
679
|
+
const arrowHint = this.tierItem.provider === 'ollama' ? '↑↓ Switch field · ' : '';
|
|
680
|
+
lines.push(` ${s.muted(`${arrowHint}Enter Save · Esc Cancel · Clear = Reset to default`)}`);
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
// Show cycling hint if multiple models available
|
|
684
|
+
const cycleHint = this.availableModels.length > 1 ? '←→/Enter Cycle · ' : '';
|
|
685
|
+
lines.push(` ${s.muted(`${cycleHint}e Manual · t Test · a Set Active · d Reset · Esc Back`)}`);
|
|
686
|
+
}
|
|
687
|
+
lines.push(border);
|
|
688
|
+
return lines;
|
|
689
|
+
}
|
|
690
|
+
handleKey(data) {
|
|
691
|
+
// Clear message on any key (except while testing)
|
|
692
|
+
if (this.state.message && !this.state.testing) {
|
|
693
|
+
this.state.message = null;
|
|
694
|
+
this.state.messageType = null;
|
|
695
|
+
}
|
|
696
|
+
if (this.state.testing) {
|
|
697
|
+
// Ignore keys while testing
|
|
698
|
+
return stay(false);
|
|
699
|
+
}
|
|
700
|
+
if (this.state.editMode) {
|
|
701
|
+
return this.handleEditKey(data);
|
|
702
|
+
}
|
|
703
|
+
return this.handleNavigateKey(data);
|
|
704
|
+
}
|
|
705
|
+
handleNavigateKey(data) {
|
|
706
|
+
// Ctrl+C closes overlay completely
|
|
707
|
+
if (isCtrlC(data)) {
|
|
708
|
+
return closeOverlay();
|
|
709
|
+
}
|
|
710
|
+
// Esc or q goes back to list
|
|
711
|
+
if (isEscape(data) || isClose(data)) {
|
|
712
|
+
return popScreen();
|
|
713
|
+
}
|
|
714
|
+
// Arrow keys for cycling through models
|
|
715
|
+
if (data[0] === 0x1b && data[1] === 0x5b) {
|
|
716
|
+
// Right arrow - cycle to next model
|
|
717
|
+
if (data[2] === 0x43) {
|
|
718
|
+
this.cycleToNextModel();
|
|
719
|
+
return stay(true);
|
|
720
|
+
}
|
|
721
|
+
// Left arrow - cycle to previous model
|
|
722
|
+
if (data[2] === 0x44) {
|
|
723
|
+
this.cycleToPrevModel();
|
|
724
|
+
return stay(true);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const char = data.toString();
|
|
728
|
+
// Enter/Space - cycle to next model (like config overlay)
|
|
729
|
+
if (isEnter(data) || char === ' ') {
|
|
730
|
+
if (this.availableModels.length > 1) {
|
|
731
|
+
this.cycleToNextModel();
|
|
732
|
+
return stay(true);
|
|
733
|
+
}
|
|
734
|
+
return stay(false);
|
|
735
|
+
}
|
|
736
|
+
// 'e' - enter manual edit mode
|
|
737
|
+
if (char === 'e') {
|
|
738
|
+
this.state.editMode = true;
|
|
739
|
+
// For Ollama, start with URL field; otherwise just model
|
|
740
|
+
if (this.tierItem.provider === 'ollama') {
|
|
741
|
+
this.state.selectedField = 'url';
|
|
742
|
+
this.state.editValue = this.currentOllamaUrl;
|
|
743
|
+
this.state.cursorPos = this.currentOllamaUrl.length;
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
this.state.selectedField = 'model';
|
|
747
|
+
this.state.editValue = this.currentModelId;
|
|
748
|
+
this.state.cursorPos = this.currentModelId.length;
|
|
749
|
+
}
|
|
750
|
+
return stay(true);
|
|
751
|
+
}
|
|
752
|
+
// 't' - test model
|
|
753
|
+
if (char === 't') {
|
|
754
|
+
// Start async test (fire-and-forget)
|
|
755
|
+
void this.testModel();
|
|
756
|
+
return stay(true);
|
|
757
|
+
}
|
|
758
|
+
// 'a' - set as active
|
|
759
|
+
if (char === 'a') {
|
|
760
|
+
setSetting('defaultModel', this.currentModelId);
|
|
761
|
+
setSetting('defaultProvider', this.tierItem.provider);
|
|
762
|
+
const tierInfo = TIER_INFO[this.tierItem.tier];
|
|
763
|
+
this.state.message = `Set ${tierInfo.label} tier (${this.currentModelId}) as active`;
|
|
764
|
+
this.state.messageType = 'success';
|
|
765
|
+
return stay(true);
|
|
766
|
+
}
|
|
767
|
+
// 'd' - reset to default
|
|
768
|
+
if (char === 'd') {
|
|
769
|
+
if (this.currentModelId !== this.tierItem.defaultModelId) {
|
|
770
|
+
clearTierOverride(this.tierItem.provider, this.tierItem.tier);
|
|
771
|
+
this.currentModelId = this.tierItem.defaultModelId;
|
|
772
|
+
this.state.message = 'Reset to default';
|
|
773
|
+
this.state.messageType = 'info';
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
this.state.message = 'Already using default';
|
|
777
|
+
this.state.messageType = 'info';
|
|
778
|
+
}
|
|
779
|
+
return stay(true);
|
|
780
|
+
}
|
|
781
|
+
return stay(false);
|
|
782
|
+
}
|
|
783
|
+
handleEditKey(data) {
|
|
784
|
+
// Escape - cancel edit
|
|
785
|
+
if (isEscape(data) || isCtrlC(data)) {
|
|
786
|
+
this.state.editMode = false;
|
|
787
|
+
this.state.editValue = '';
|
|
788
|
+
this.state.cursorPos = 0;
|
|
789
|
+
return stay(true);
|
|
790
|
+
}
|
|
791
|
+
// Check for escape sequences (arrow keys)
|
|
792
|
+
if (data[0] === 0x1b && data[1] === 0x5b) {
|
|
793
|
+
// Left arrow - move cursor left
|
|
794
|
+
if (data[2] === 0x44) {
|
|
795
|
+
if (this.state.cursorPos > 0) {
|
|
796
|
+
this.state.cursorPos--;
|
|
797
|
+
}
|
|
798
|
+
return stay(true);
|
|
799
|
+
}
|
|
800
|
+
// Right arrow - move cursor right
|
|
801
|
+
if (data[2] === 0x43) {
|
|
802
|
+
if (this.state.cursorPos < this.state.editValue.length) {
|
|
803
|
+
this.state.cursorPos++;
|
|
804
|
+
}
|
|
805
|
+
return stay(true);
|
|
806
|
+
}
|
|
807
|
+
// Up/Down arrows - switch between fields (only for Ollama)
|
|
808
|
+
if (this.tierItem.provider === 'ollama') {
|
|
809
|
+
// Up arrow
|
|
810
|
+
if (data[2] === 0x41) {
|
|
811
|
+
if (this.state.selectedField === 'model') {
|
|
812
|
+
this.state.selectedField = 'url';
|
|
813
|
+
this.state.editValue = this.currentOllamaUrl;
|
|
814
|
+
this.state.cursorPos = this.currentOllamaUrl.length;
|
|
815
|
+
}
|
|
816
|
+
return stay(true);
|
|
817
|
+
}
|
|
818
|
+
// Down arrow
|
|
819
|
+
if (data[2] === 0x42) {
|
|
820
|
+
if (this.state.selectedField === 'url') {
|
|
821
|
+
this.state.selectedField = 'model';
|
|
822
|
+
this.state.editValue = this.currentModelId;
|
|
823
|
+
this.state.cursorPos = this.currentModelId.length;
|
|
824
|
+
}
|
|
825
|
+
return stay(true);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
// Enter - save current field
|
|
830
|
+
if (isEnter(data)) {
|
|
831
|
+
const newValue = this.state.editValue.trim();
|
|
832
|
+
if (this.state.selectedField === 'model') {
|
|
833
|
+
// Editing model ID
|
|
834
|
+
if (!newValue) {
|
|
835
|
+
// Empty = reset to default
|
|
836
|
+
clearTierOverride(this.tierItem.provider, this.tierItem.tier);
|
|
837
|
+
this.currentModelId = this.tierItem.defaultModelId;
|
|
838
|
+
this.state.message = 'Reset to default model';
|
|
839
|
+
this.state.messageType = 'info';
|
|
840
|
+
}
|
|
841
|
+
else if (newValue !== this.currentModelId) {
|
|
842
|
+
// Save new value
|
|
843
|
+
setTierOverride(this.tierItem.provider, this.tierItem.tier, newValue);
|
|
844
|
+
this.currentModelId = newValue;
|
|
845
|
+
this.state.message = `Saved model. Press 't' to test.`;
|
|
846
|
+
this.state.messageType = 'info';
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
// Editing Ollama URL (selectedField === 'url')
|
|
851
|
+
if (!newValue || newValue === DEFAULT_OLLAMA_URL) {
|
|
852
|
+
// Empty or default = clear override
|
|
853
|
+
setSetting('ollamaBaseUrl', undefined);
|
|
854
|
+
this.currentOllamaUrl = DEFAULT_OLLAMA_URL;
|
|
855
|
+
this.state.message = 'Reset to default URL';
|
|
856
|
+
this.state.messageType = 'info';
|
|
857
|
+
}
|
|
858
|
+
else if (newValue !== this.currentOllamaUrl) {
|
|
859
|
+
// Save new URL
|
|
860
|
+
setSetting('ollamaBaseUrl', newValue);
|
|
861
|
+
this.currentOllamaUrl = newValue;
|
|
862
|
+
this.state.message = `Saved URL. Press 't' to test.`;
|
|
863
|
+
this.state.messageType = 'info';
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
this.state.editMode = false;
|
|
867
|
+
this.state.editValue = '';
|
|
868
|
+
this.state.cursorPos = 0;
|
|
869
|
+
return stay(true);
|
|
870
|
+
}
|
|
871
|
+
// Backspace - delete char before cursor
|
|
872
|
+
if (data[0] === 127 || data[0] === 8) {
|
|
873
|
+
if (this.state.cursorPos > 0) {
|
|
874
|
+
const before = this.state.editValue.slice(0, this.state.cursorPos - 1);
|
|
875
|
+
const after = this.state.editValue.slice(this.state.cursorPos);
|
|
876
|
+
this.state.editValue = before + after;
|
|
877
|
+
this.state.cursorPos--;
|
|
878
|
+
}
|
|
879
|
+
return stay(true);
|
|
880
|
+
}
|
|
881
|
+
// Printable character - insert at cursor position
|
|
882
|
+
const char = data.toString();
|
|
883
|
+
if (char.length === 1 && char.charCodeAt(0) >= 32 && char.charCodeAt(0) < 127) {
|
|
884
|
+
const before = this.state.editValue.slice(0, this.state.cursorPos);
|
|
885
|
+
const after = this.state.editValue.slice(this.state.cursorPos);
|
|
886
|
+
this.state.editValue = before + char + after;
|
|
887
|
+
this.state.cursorPos++;
|
|
888
|
+
return stay(true);
|
|
889
|
+
}
|
|
890
|
+
return stay(false);
|
|
891
|
+
}
|
|
892
|
+
async testModel() {
|
|
893
|
+
this.state.testing = true;
|
|
894
|
+
this.state.message = `Testing ${this.currentModelId}...`;
|
|
895
|
+
this.state.messageType = 'info';
|
|
896
|
+
try {
|
|
897
|
+
const result = await validateModel(this.tierItem.provider, this.currentModelId);
|
|
898
|
+
this.state.testing = false;
|
|
899
|
+
if (result.valid) {
|
|
900
|
+
this.state.message = `✓ Model '${this.currentModelId}' is valid and working`;
|
|
901
|
+
this.state.messageType = 'success';
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
this.state.message = result.error || 'Model validation failed';
|
|
905
|
+
this.state.messageType = 'error';
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
catch (error) {
|
|
909
|
+
this.state.testing = false;
|
|
910
|
+
this.state.message = `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
|
|
911
|
+
this.state.messageType = 'error';
|
|
912
|
+
}
|
|
913
|
+
// Trigger re-render after async operation
|
|
914
|
+
this.requestRenderFn();
|
|
915
|
+
}
|
|
916
|
+
getMinHeight() {
|
|
917
|
+
// Ollama needs more height for the URL section
|
|
918
|
+
return this.tierItem.provider === 'ollama' ? 26 : 22;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
// =============================================================================
|
|
922
|
+
// Model Overlay V2 Class
|
|
923
|
+
// =============================================================================
|
|
924
|
+
/**
|
|
925
|
+
* Model selection overlay using the new Overlay interface.
|
|
926
|
+
* Displays available models in a tabbed list by provider.
|
|
927
|
+
* Others tab shows providers; selecting a provider shows its models.
|
|
928
|
+
*/
|
|
929
|
+
export class ModelOverlayV2 extends TabbedListOverlayV2 {
|
|
930
|
+
type = 'inline';
|
|
931
|
+
id = 'model-overlay-v2';
|
|
932
|
+
activeModel;
|
|
933
|
+
activeProvider;
|
|
934
|
+
/** Ollama auto-detection state */
|
|
935
|
+
ollamaState = {
|
|
936
|
+
status: 'loading',
|
|
937
|
+
availableModels: new Set(),
|
|
938
|
+
};
|
|
939
|
+
minHeight = 18;
|
|
940
|
+
constructor(options = {}) {
|
|
941
|
+
const activeModel = options.currentModel ?? 'claude-sonnet-4-5-20250929';
|
|
942
|
+
const activeProvider = options.currentProvider ?? 'claude';
|
|
943
|
+
// Find the initial tab based on current provider
|
|
944
|
+
const initialTabIndex = PROVIDER_TABS.findIndex((t) => t.id === activeProvider);
|
|
945
|
+
// Get list items - tier items for primary providers, provider items for Others
|
|
946
|
+
const listItems = getListItems();
|
|
947
|
+
super({
|
|
948
|
+
title: 'Select Model',
|
|
949
|
+
tabs: PROVIDER_TABS,
|
|
950
|
+
items: listItems,
|
|
951
|
+
pageSize: PAGE_SIZE,
|
|
952
|
+
filterByTab: (item, tabId) => {
|
|
953
|
+
if (tabId === 'others') {
|
|
954
|
+
// "Others" tab shows provider items
|
|
955
|
+
return item.type === 'provider';
|
|
956
|
+
}
|
|
957
|
+
// Primary provider tabs show tier items for that provider
|
|
958
|
+
return item.type === 'tier' && item.provider === tabId;
|
|
959
|
+
},
|
|
960
|
+
getSearchText: (item) => {
|
|
961
|
+
if (item.type === 'provider') {
|
|
962
|
+
return `${item.metadata.displayName} ${item.metadata.description}`;
|
|
963
|
+
}
|
|
964
|
+
// TierItem
|
|
965
|
+
const tierInfo = TIER_INFO[item.tier];
|
|
966
|
+
return `${tierInfo.label} ${item.currentModelName} ${item.currentModelId}`;
|
|
967
|
+
},
|
|
968
|
+
renderItem: (item, isSelected, styles) => {
|
|
969
|
+
const prefix = isSelected ? styles.primary(' > ') : ' ';
|
|
970
|
+
if (item.type === 'provider') {
|
|
971
|
+
// Render provider item for Others tab
|
|
972
|
+
const provider = item.metadata;
|
|
973
|
+
const hasKey = hasApiKeyForProvider(provider.credentialKey);
|
|
974
|
+
const keyIndicator = hasKey ? styles.success('●') : styles.error('○');
|
|
975
|
+
const keyStatus = hasKey ? styles.muted('Configured') : styles.muted('Not configured');
|
|
976
|
+
const name = isSelected
|
|
977
|
+
? styles.primary(provider.displayName.padEnd(20))
|
|
978
|
+
: provider.displayName.padEnd(20);
|
|
979
|
+
const desc = styles.muted(provider.description.substring(0, 30));
|
|
980
|
+
return `${prefix}${keyIndicator} ${name}${desc} ${keyStatus}`;
|
|
981
|
+
}
|
|
982
|
+
// Render tier item (tier-centric view)
|
|
983
|
+
const tierInfo = TIER_INFO[item.tier];
|
|
984
|
+
const isCustom = item.currentModelId !== item.defaultModelId;
|
|
985
|
+
// Check if this tier is active
|
|
986
|
+
const isActive = item.currentModelId === activeModel && item.provider === activeProvider;
|
|
987
|
+
// For Ollama, show availability status; for others, show API key status
|
|
988
|
+
let statusIndicator;
|
|
989
|
+
if (item.provider === 'ollama') {
|
|
990
|
+
const isLocallyAvailable = this.ollamaState.availableModels.has(item.currentModelId.toLowerCase()) ||
|
|
991
|
+
this.ollamaState.availableModels.has(item.currentModelId.split(':')[0].toLowerCase());
|
|
992
|
+
statusIndicator = isLocallyAvailable ? styles.success('●') : styles.warning('○');
|
|
993
|
+
}
|
|
994
|
+
else {
|
|
995
|
+
const credKey = settingsProviderToCredentialKey(item.provider);
|
|
996
|
+
const hasKey = hasApiKey(credKey);
|
|
997
|
+
statusIndicator = hasKey ? styles.success('●') : styles.error('○');
|
|
998
|
+
}
|
|
999
|
+
// Format: Tier | Model Name | Badges
|
|
1000
|
+
const tierLabel = isSelected
|
|
1001
|
+
? styles.primary(tierInfo.label.padEnd(TIER_WIDTH))
|
|
1002
|
+
: tierInfo.label.padEnd(TIER_WIDTH);
|
|
1003
|
+
const modelName = item.currentModelName.padEnd(MODEL_NAME_WIDTH);
|
|
1004
|
+
const activeBadge = isActive ? styles.success(' (active)') : '';
|
|
1005
|
+
const customBadge = isCustom && !isActive ? styles.warning(' *') : '';
|
|
1006
|
+
return `${prefix}${statusIndicator} ${tierLabel}${modelName}${activeBadge}${customBadge}`;
|
|
1007
|
+
},
|
|
1008
|
+
footerHints: (searchMode) => {
|
|
1009
|
+
if (searchMode) {
|
|
1010
|
+
return 'Type to filter · ↑↓/jk Navigate · Enter Details · Esc Exit search';
|
|
1011
|
+
}
|
|
1012
|
+
const currentTabId = PROVIDER_TABS[this.state.currentTab]?.id;
|
|
1013
|
+
// "Others" tab doesn't have inline key setting (handled in provider detail)
|
|
1014
|
+
if (currentTabId === 'others') {
|
|
1015
|
+
return 'Tab · ↑↓ · / Search · Enter Details · q Close';
|
|
1016
|
+
}
|
|
1017
|
+
// Ollama doesn't need API key
|
|
1018
|
+
if (currentTabId === 'ollama') {
|
|
1019
|
+
return 'Tab · ↑↓ · / Search · Enter Details · q Close';
|
|
1020
|
+
}
|
|
1021
|
+
// Standard providers: show s Set Key option
|
|
1022
|
+
return 'Tab · ↑↓ · / Search · Enter Details · s Set Key · q Close';
|
|
1023
|
+
},
|
|
1024
|
+
// Show API key status below tab bar for standard providers
|
|
1025
|
+
renderSubHeader: (tabId, styles) => {
|
|
1026
|
+
// Skip for "Others" tab (has its own detail screen)
|
|
1027
|
+
if (tabId === 'others')
|
|
1028
|
+
return [];
|
|
1029
|
+
// Ollama: show connection status
|
|
1030
|
+
if (tabId === 'ollama') {
|
|
1031
|
+
const status = this.ollamaState.status;
|
|
1032
|
+
let indicator;
|
|
1033
|
+
switch (status) {
|
|
1034
|
+
case 'loading':
|
|
1035
|
+
indicator = styles.muted('◐ Detecting local models...');
|
|
1036
|
+
break;
|
|
1037
|
+
case 'connected': {
|
|
1038
|
+
const count = this.ollamaState.availableModels.size;
|
|
1039
|
+
indicator = styles.success(`● Ollama: ${String(count)} model${count !== 1 ? 's' : ''} available`);
|
|
1040
|
+
break;
|
|
1041
|
+
}
|
|
1042
|
+
case 'offline':
|
|
1043
|
+
indicator = styles.error('○ Ollama: Not running (start with: ollama serve)');
|
|
1044
|
+
break;
|
|
1045
|
+
case 'error':
|
|
1046
|
+
indicator = styles.error(`○ Ollama: ${this.ollamaState.error ?? 'Error'}`);
|
|
1047
|
+
break;
|
|
1048
|
+
}
|
|
1049
|
+
return [` ${indicator}`, ''];
|
|
1050
|
+
}
|
|
1051
|
+
const keyStatus = checkApiKeyStatus(tabId);
|
|
1052
|
+
const indicator = keyStatus.hasKey
|
|
1053
|
+
? styles.success('● API Key: Set')
|
|
1054
|
+
: styles.error('○ API Key: Not set');
|
|
1055
|
+
return [` ${indicator}`, ''];
|
|
1056
|
+
},
|
|
1057
|
+
// Handle 's' key to set API key for current provider
|
|
1058
|
+
handleCustomKey: (key, tabId) => {
|
|
1059
|
+
const char = key.toString();
|
|
1060
|
+
if (char === 's' && tabId !== 'others' && tabId !== 'ollama') {
|
|
1061
|
+
const providerName = PROVIDER_TABS.find(t => t.id === tabId)?.label ?? tabId;
|
|
1062
|
+
return {
|
|
1063
|
+
handled: true,
|
|
1064
|
+
pushScreen: new KeyInputScreen(tabId, providerName, this.getStyles(), () => { this.requestRender(); }),
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
return { handled: false };
|
|
1068
|
+
},
|
|
1069
|
+
emptyMessage: 'No models available for this provider.',
|
|
1070
|
+
noResultsMessage: 'No models match the search.',
|
|
1071
|
+
});
|
|
1072
|
+
this.activeModel = activeModel;
|
|
1073
|
+
this.activeProvider = activeProvider;
|
|
1074
|
+
// Set initial tab based on current provider
|
|
1075
|
+
if (initialTabIndex >= 0) {
|
|
1076
|
+
this.state.currentTab = initialTabIndex;
|
|
1077
|
+
// Re-filter items for the selected tab
|
|
1078
|
+
const tabId = PROVIDER_TABS[initialTabIndex]?.id;
|
|
1079
|
+
this.state.filteredItems = listItems.filter((item) => {
|
|
1080
|
+
if (tabId === 'others') {
|
|
1081
|
+
return item.type === 'provider';
|
|
1082
|
+
}
|
|
1083
|
+
// Primary provider tabs show tier items for that provider
|
|
1084
|
+
return item.type === 'tier' && item.provider === tabId;
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
// Start fetching Ollama models in background
|
|
1088
|
+
this.fetchOllamaModels();
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Fetch available Ollama models and update state.
|
|
1092
|
+
*/
|
|
1093
|
+
fetchOllamaModels() {
|
|
1094
|
+
listOllamaModels().then((result) => {
|
|
1095
|
+
this.updateOllamaState(result);
|
|
1096
|
+
}).catch(() => {
|
|
1097
|
+
this.ollamaState.status = 'error';
|
|
1098
|
+
this.ollamaState.error = 'Failed to fetch models';
|
|
1099
|
+
this.requestRender();
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Update Ollama state based on fetch result.
|
|
1104
|
+
*/
|
|
1105
|
+
updateOllamaState(result) {
|
|
1106
|
+
if (!result.ollamaRunning) {
|
|
1107
|
+
this.ollamaState.status = 'offline';
|
|
1108
|
+
this.ollamaState.availableModels.clear();
|
|
1109
|
+
this.requestRender();
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
if (!result.success) {
|
|
1113
|
+
this.ollamaState.status = 'error';
|
|
1114
|
+
this.ollamaState.error = result.error;
|
|
1115
|
+
this.ollamaState.availableModels.clear();
|
|
1116
|
+
this.requestRender();
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
this.ollamaState.status = 'connected';
|
|
1120
|
+
this.ollamaState.availableModels.clear();
|
|
1121
|
+
// Add all available model names to the set
|
|
1122
|
+
for (const model of result.models) {
|
|
1123
|
+
const modelName = model.name.toLowerCase();
|
|
1124
|
+
const baseName = modelName.split(':')[0];
|
|
1125
|
+
this.ollamaState.availableModels.add(modelName);
|
|
1126
|
+
this.ollamaState.availableModels.add(baseName);
|
|
1127
|
+
}
|
|
1128
|
+
this.requestRender();
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Create detail screen for a list item.
|
|
1132
|
+
* For tiers: shows TierDetailScreen to select model for that tier
|
|
1133
|
+
* For providers: shows OthersProviderDetailScreen with provider's tier/models
|
|
1134
|
+
*/
|
|
1135
|
+
createDetailScreen(item) {
|
|
1136
|
+
if (item.type === 'provider') {
|
|
1137
|
+
// Show provider detail screen with its tiers
|
|
1138
|
+
return new OthersProviderDetailScreen(item.metadata, this.getStyles(), this.activeModel, this.activeProvider, () => { this.requestRender(); });
|
|
1139
|
+
}
|
|
1140
|
+
// TierItem - show tier detail screen
|
|
1141
|
+
return new TierDetailScreen(item, this.getStyles(), this.activeModel, this.activeProvider, this.ollamaState, () => { this.requestRender(); });
|
|
1142
|
+
}
|
|
1143
|
+
}
|