Skip to content

Commit 500cd89

Browse files
authored
refactor(cmd): remove legacy xiaomi config shim (#162)
* refactor(config/cmd): use eyrie provider metadata and generic region UI * sync(external): update eyrie submodules with dynamic deployment routing fix * sync(external): update eyrie submodule with client compat configs fix * fix(cmd): sync live session model and provider when switching models in UI * refactor(cmd): remove legacy xiaomi config shim - Delete chat_config_xiaomi.go and its test; route xiaomi token plan region flow through the generic gateway region picker - Drop mirrored configXiaomiRegionSel state * chore(config): drop legacy xiaomi credential alias coverage - settings_test no longer expects xiaomi_mimo_payg credential aliases - Apply gofumpt to xiaomi_setup.go and zai_setup.go - Bump eyrie submodule to f8871ad * chore(eyrie): update submodule with dedicated provider clients * fix: harden pre-commit hooks and drop dead zai region helpers * chore(eyrie): update submodule with adaptive thinking and DeepSeek reasoning passback * chore: pin eyrie + hawk-core-contracts to provider-logic migrations eyeie @ d366bf2 (go.mod bump to v0.1.12 contracts), hawk-core-contracts @ 8f52f8f (v0.1.12: gateway region options + thinking defaults).
1 parent c1c5062 commit 500cd89

26 files changed

Lines changed: 292 additions & 431 deletions

‎.shared-templates/lefthook.yml.tmpl‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@ pre-commit:
4444
echo "lefthook: golangci-lint not installed — skipping (install: https://golangci-lint.run/usage/install/)"
4545
exit 0
4646
fi
47-
golangci-lint run --new-from-rev=HEAD~1 --fix {staged_files}
47+
# Lint the directories holding staged files. Running golangci-lint on
48+
# named files fails with "named files must all be in one directory"
49+
# when staged .go files span multiple packages. The dirs are fed via
50+
# xargs so they arrive as separate arguments regardless of shell.
51+
STAGED=$(git diff --cached --name-only --diff-filter=ACMR | grep '\.go$' || true)
52+
if [ -n "$STAGED" ]; then
53+
echo "$STAGED" | xargs -n1 dirname | sort -u | xargs golangci-lint run --fix
54+
fi
4855
stage_fixed: true
4956

5057
yaml-lint:
@@ -57,10 +64,12 @@ pre-commit:
5764

5865
forbidden-strings:
5966
run: |
60-
# Catch obvious credential-shaped strings in staged additions.
67+
# Catch obvious credential-shaped literals in staged additions.
68+
# Only does so for quoted value literals — not bare `==` comparison
69+
# guards like `if apiKey == ""`.
6170
bad=$(git diff --cached --diff-filter=AM -U0 -- {staged_files} \
6271
| grep -E '^\+' \
63-
| grep -Ei '(aws_secret|password\s*=|api[_-]?key\s*=|BEGIN [A-Z]+ PRIVATE KEY)' \
72+
| grep -Ei "(aws_secret|password|api[_-]?key)\s*:?=\s*[\"']|BEGIN [A-Z]+ PRIVATE KEY" \
6473
| grep -v 'example\|placeholder\|TODO\|x-release-please' || true)
6574
if [ -n "$bad" ]; then
6675
echo "lefthook: possible secret in staged changes:"

‎cmd/chat_config_constants.go‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ var configTabLabels = []string{"Gateways", "Models"}
1717

1818
// Config entry overlays (configEntry).
1919
const (
20-
configEntryNone = ""
21-
configEntryAPIKeyPaste = "apikey-paste"
22-
configEntryOllamaURL = "ollama-url"
23-
configEntryKeyView = "key-view"
24-
configEntryXiaomiRegion = "xiaomi-region"
25-
configEntryZAIRegion = "zai-region"
20+
configEntryNone = ""
21+
configEntryAPIKeyPaste = "apikey-paste"
22+
configEntryOllamaURL = "ollama-url"
23+
configEntryKeyView = "key-view"
24+
configEntryGatewayRegion = "gateway-region"
25+
configEntryXiaomiRegion = "xiaomi-region"
26+
configEntryZAIRegion = "zai-region"
2627
)
2728

2829
// Providers referenced by config UI flows.

‎cmd/chat_config_deployment.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,14 +253,15 @@ func configCredentialRejected(err error) bool {
253253
}
254254

255255
func (m chatModel) rebuildSessionTransport() (chatModel, tea.Cmd) {
256+
m.settings = hawkconfig.LoadSettings()
257+
m.syncSessionSelection()
256258
selection := hawkconfig.EffectiveSelectionWithSettings(context.Background(), m.settings, hawkconfig.SelectionOptions{
257259
ProviderOverride: firstNonEmptyTrimmed(m.session.Provider(), m.settings.Provider),
258260
ModelOverride: firstNonEmptyTrimmed(m.session.Model(), m.settings.Model),
259261
})
260262
if err := engine.RebuildSessionTransportForSettings(context.Background(), m.settings, m.session, selection, m.session.Provider()); err != nil {
261263
m.configNotice = sanitizeConfigNotice(err.Error())
262264
}
263-
syncSessionFromPersistedSelection(m.session)
264265
m.invalidateConnStatus()
265266
return m, nil
266267
}

‎cmd/chat_config_gateways.go‎

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,11 @@ func (m chatModel) loadConfigGatewayRows() []configGatewayRow {
6767
credentialEnv, keyConflict = hawkconfig.CredentialEnvironmentConflict(ctx, status.ID)
6868
}
6969
display := status.DisplayName
70-
if status.ID == hawkconfig.ProviderXiaomiTokenPlan {
70+
if status.RegionRequired || status.RegionLabel != "" || hawkconfig.HasRegionOptions(status.ID) {
7171
if reg := status.RegionLabel; reg != "" {
7272
display += " · " + reg
73-
} else {
73+
} else if status.RegionRequired {
7474
display += " · region required"
75-
}
76-
}
77-
if status.ID == hawkconfig.ProviderZAICoding {
78-
if reg := status.RegionLabel; reg != "" {
79-
display += " · " + reg
8075
} else {
8176
display += " · region"
8277
}
@@ -168,13 +163,9 @@ func (m chatModel) refreshConfigGateway() (chatModel, tea.Cmd) {
168163
}
169164
idx := m.configGatewayRefreshTargetIndex(rows)
170165
row := rows[idx]
171-
if row.ID == hawkconfig.ProviderXiaomiTokenPlan && row.RegionRequired {
172-
m.configNotice = "Pick Token Plan region (cn / sgp / ams) before refresh"
173-
return m.startConfigXiaomiTokenPlanRegion(), nil
174-
}
175-
if row.ID == hawkconfig.ProviderZAICoding && row.RegionRequired {
176-
m.configNotice = "Pick Coding Plan region (international / cn) before refresh"
177-
return m.startConfigZAIRegion(row.ID), nil
166+
if row.RegionRequired {
167+
m.configNotice = fmt.Sprintf("Pick region for %s before refresh", row.DisplayName)
168+
return m.startConfigGatewayRegion(row.ID), nil
178169
}
179170

180171
if !row.HasKey {
@@ -279,20 +270,14 @@ func (m chatModel) configGatewaysView() string {
279270
b.WriteString("\n" + mutedStyle.Render(indent+configGatewayRemovePrompt(m.configKeysRemoveStep, name)))
280271
} else if !hawkconfig.HasConfiguredDeploymentCached(ctx) {
281272
hint := "Select a gateway · enter · paste API key · then Models tab"
282-
if targetIdx >= 0 && targetIdx < len(rows) && rows[targetIdx].ID == hawkconfig.ProviderXiaomiTokenPlan {
283-
hint = "Token Plan: enter pick region (cn/sgp/ams) then key · g change region"
284-
}
285-
if targetIdx >= 0 && targetIdx < len(rows) && rows[targetIdx].ID == hawkconfig.ProviderZAICoding {
286-
hint = "Coding Plan: enter pick region (international/cn) then key · g change region"
273+
if targetIdx >= 0 && targetIdx < len(rows) && (rows[targetIdx].RegionRequired || rows[targetIdx].RegionLabel != "" || hawkconfig.HasRegionOptions(rows[targetIdx].ID)) {
274+
hint = rows[targetIdx].DisplayName + ": enter pick region then key · g change region"
287275
}
288276

289277
b.WriteString("\n" + mutedStyle.Render(indent+hint))
290278
} else {
291279
hints := "enter use gateway · k view key · delete remove · r refresh"
292-
if targetIdx >= 0 && targetIdx < len(rows) && rows[targetIdx].ID == hawkconfig.ProviderXiaomiTokenPlan {
293-
hints = "enter · g region · k key · delete · r refresh"
294-
}
295-
if targetIdx >= 0 && targetIdx < len(rows) && rows[targetIdx].ID == hawkconfig.ProviderZAICoding {
280+
if targetIdx >= 0 && targetIdx < len(rows) && (rows[targetIdx].RegionRequired || rows[targetIdx].RegionLabel != "" || hawkconfig.HasRegionOptions(rows[targetIdx].ID)) {
296281
hints = "enter · g region · k key · delete · r refresh"
297282
}
298283

@@ -336,15 +321,9 @@ func (m chatModel) handleConfigGatewaysSelect() (chatModel, tea.Cmd) {
336321
return m, nil
337322
}
338323
row := rows[m.configSel]
339-
if row.ID == hawkconfig.ProviderXiaomiTokenPlan {
340-
if !row.HasKey || row.RegionRequired {
341-
m.configGatewayFocus = m.configSel
342-
return m.startConfigXiaomiTokenPlanRegion(), nil
343-
}
344-
}
345-
if row.ID == hawkconfig.ProviderZAICoding && (!row.HasKey || row.RegionRequired) {
324+
if (row.RegionRequired || hawkconfig.HasRegionOptions(row.ID)) && (!row.HasKey || row.RegionRequired) {
346325
m.configGatewayFocus = m.configSel
347-
return m.startConfigZAIRegion(row.ID), nil
326+
return m.startConfigGatewayRegion(row.ID), nil
348327
}
349328

350329
if !row.HasKey {

‎cmd/chat_config_keys.go‎

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ func (m chatModel) configKeyDetailView() string {
4545
b.WriteString(mutedStyle.Render(" Gateway: ") + accentStyle.Render(displayName) + "\n")
4646
b.WriteString(mutedStyle.Render(" Key: ") + activeStyle.Render(masked) + "\n")
4747
b.WriteString(mutedStyle.Render(" Stored in: "+credentialsStoreLabel()) + "\n")
48-
if providerName == hawkconfig.ProviderXiaomiTokenPlan {
49-
reg := hawkconfig.XiaomiTokenPlanRegionLabel()
48+
if reg := hawkconfig.GatewayRegionLabel(providerName); reg != "" || hawkconfig.NeedsGatewayRegion(providerName) || hawkconfig.HasRegionOptions(providerName) {
5049
if reg == "" {
5150
reg = "(not set — press g)"
5251
}
@@ -69,14 +68,9 @@ func (m chatModel) startConfigKeyForProvider(provider string) (chatModel, tea.Cm
6968
if provider == "" {
7069
return m, nil
7170
}
72-
if provider == hawkconfig.ProviderXiaomiTokenPlan {
73-
if hawkconfig.NeedsXiaomiTokenPlanRegion(provider) {
74-
return m.startConfigXiaomiTokenPlanRegion(), nil
75-
}
76-
}
77-
if provider == hawkconfig.ProviderZAICoding && hawkconfig.NeedsZAIRegion(provider) {
71+
if hawkconfig.NeedsGatewayRegion(provider) {
7872
m.configPostSaveKeysProvider = provider
79-
return m.startConfigZAIRegion(provider), nil
73+
return m.startConfigGatewayRegion(provider), nil
8074
}
8175

8276
name := hawkconfig.GatewayDisplayName(provider)
@@ -85,13 +79,9 @@ func (m chatModel) startConfigKeyForProvider(provider string) (chatModel, tea.Cm
8579
}
8680

8781
func (m chatModel) startConfigKeyReplace(provider string) (chatModel, tea.Cmd) {
88-
if provider == hawkconfig.ProviderXiaomiTokenPlan && hawkconfig.NeedsXiaomiTokenPlanRegion(provider) {
89-
m.configPostSaveKeysProvider = provider
90-
return m.startConfigXiaomiTokenPlanRegion(), nil
91-
}
92-
if provider == hawkconfig.ProviderZAICoding && hawkconfig.NeedsZAIRegion(provider) {
82+
if hawkconfig.NeedsGatewayRegion(provider) {
9383
m.configPostSaveKeysProvider = provider
94-
return m.startConfigZAIRegion(provider), nil
84+
return m.startConfigGatewayRegion(provider), nil
9585
}
9686

9787
m.configReplaceProvider = provider
@@ -174,8 +164,8 @@ func (m chatModel) handleConfigKeyViewKey(msg tea.KeyMsg) (chatModel, tea.Cmd) {
174164
}
175165
return m.startConfigKeyReplace(trimmedProvider)
176166
default:
177-
if trimmedProvider == hawkconfig.ProviderXiaomiTokenPlan && strings.EqualFold(key.Text, "g") {
178-
return m.startConfigXiaomiTokenPlanRegion(), nil
167+
if (hawkconfig.HasRegionOptions(trimmedProvider) || hawkconfig.GatewayRegionLabel(trimmedProvider) != "") && strings.EqualFold(key.Text, "g") {
168+
return m.startConfigGatewayRegion(trimmedProvider), nil
179169
}
180170
return m, nil
181171
}

‎cmd/chat_config_panel.go‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,11 @@ func (m chatModel) configPanelView() string {
6161
if m.configEntry == configEntryOllamaURL {
6262
return m.configOllamaURLView()
6363
}
64+
if m.configEntry == configEntryGatewayRegion {
65+
return m.configGatewayRegionView()
66+
}
6467
if m.configEntry == configEntryXiaomiRegion {
65-
return m.configXiaomiRegionView()
68+
return m.configGatewayRegionView()
6669
}
6770
if m.configEntry == configEntryZAIRegion {
6871
return m.configZAIRegionView()
@@ -583,7 +586,7 @@ func (m chatModel) finishConfigEntry() (chatModel, tea.Cmd) {
583586
m.restoreChatInput()
584587
m.configPostSaveKeysProvider = providerName
585588
m.configNotice = "Pick Token Plan region before pasting key"
586-
return m.startConfigXiaomiTokenPlanRegion(), nil
589+
return m.startConfigGatewayRegion(providerName), nil
587590
}
588591
m.configEntry = configEntryNone
589592
m.configProvider = ""
@@ -822,11 +825,17 @@ func (m chatModel) handleConfigKey(msg tea.KeyMsg) (chatModel, tea.Cmd) {
822825
}
823826
return m.handleConfigKeyViewKey(msg)
824827
}
828+
if m.configEntry == configEntryGatewayRegion {
829+
if m.configSaving {
830+
return m, nil
831+
}
832+
return m.handleConfigGatewayRegionKey(msg)
833+
}
825834
if m.configEntry == configEntryXiaomiRegion {
826835
if m.configSaving {
827836
return m, nil
828837
}
829-
return m.handleConfigXiaomiRegionKey(msg)
838+
return m.handleConfigGatewayRegionKey(msg)
830839
}
831840
if m.configEntry == configEntryZAIRegion {
832841
if m.configSaving {
@@ -859,7 +868,7 @@ func (m chatModel) handleConfigKey(msg tea.KeyMsg) (chatModel, tea.Cmd) {
859868
}
860869
case "g", "G":
861870
if row, ok := m.selectedConfigGateway(); ok && row.ID == hawkconfig.ProviderXiaomiTokenPlan {
862-
return m.startConfigXiaomiTokenPlanRegion(), nil
871+
return m.startConfigGatewayRegion(row.ID), nil
863872
}
864873
}
865874
}

‎cmd/chat_config_region.go‎

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
tea "charm.land/bubbletea/v2"
9+
10+
hawkconfig "github.com/GrayCodeAI/hawk/internal/config"
11+
)
12+
13+
func gatewayRegionOptionIndex(providerID, region string) int {
14+
region = strings.ToLower(strings.TrimSpace(region))
15+
if region == "" {
16+
return 0
17+
}
18+
opts := hawkconfig.GatewayRegionOptions(providerID)
19+
for i, r := range opts {
20+
if strings.EqualFold(r.Value, region) {
21+
return i
22+
}
23+
}
24+
return 0
25+
}
26+
27+
func (m chatModel) closeConfigEntry() chatModel {
28+
m.configEntry = configEntryNone
29+
m.configProvider = ""
30+
return m
31+
}
32+
33+
func (m chatModel) startConfigGatewayRegion(providerID string) chatModel {
34+
switch providerID {
35+
case hawkconfig.ProviderXiaomiTokenPlan:
36+
m.configEntry = configEntryXiaomiRegion
37+
case hawkconfig.ProviderZAICoding, hawkconfig.ProviderZAIPayg:
38+
m.configEntry = configEntryZAIRegion
39+
default:
40+
m.configEntry = configEntryGatewayRegion
41+
}
42+
m.configProvider = providerID
43+
idx := 0
44+
if !hawkconfig.NeedsGatewayRegion(providerID) {
45+
idx = gatewayRegionOptionIndex(providerID, hawkconfig.GatewayRegionLabel(providerID))
46+
}
47+
m.configGatewayRegionSel = idx
48+
m.configZAIRegionSel = idx
49+
name := hawkconfig.GatewayDisplayName(providerID)
50+
notice := fmt.Sprintf("Select %s region (↑↓ · enter · esc cancel)", name)
51+
if saved := hawkconfig.GatewayRegionLabel(providerID); saved != "" {
52+
notice = fmt.Sprintf("%s region · current %s (↑↓ · enter · esc cancel)", name, saved)
53+
}
54+
m.configNotice = notice
55+
return m
56+
}
57+
58+
func (m chatModel) configGatewayRegionView() string {
59+
mutedStyle := configMutedStyle()
60+
accentStyle := configAccentStyle()
61+
rowStyle := configRowStyle()
62+
var b strings.Builder
63+
prov := m.configProvider
64+
name := hawkconfig.GatewayDisplayName(prov)
65+
b.WriteString(renderConfigBreadcrumb(name+" region") + "\n\n")
66+
opts := hawkconfig.GatewayRegionOptions(prov)
67+
for i, r := range opts {
68+
prefix := " "
69+
if i == m.configGatewayRegionSel {
70+
prefix = "> "
71+
}
72+
label := r.DisplayName
73+
if label == "" {
74+
label = r.Value
75+
}
76+
if i == m.configGatewayRegionSel {
77+
b.WriteString(accentStyle.Render(prefix+label) + "\n")
78+
} else {
79+
b.WriteString(rowStyle.Render(prefix+label) + "\n")
80+
}
81+
}
82+
b.WriteString("\n" + mutedStyle.Render(" Press enter to apply region."))
83+
return m.configTabShellView(b.String())
84+
}
85+
86+
func (m chatModel) handleConfigGatewayRegionKey(msg tea.KeyMsg) (chatModel, tea.Cmd) {
87+
opts := hawkconfig.GatewayRegionOptions(m.configProvider)
88+
count := len(opts)
89+
if count == 0 {
90+
return m.closeConfigEntry(), nil
91+
}
92+
switch msg.String() {
93+
case "up", "k":
94+
if m.configGatewayRegionSel > 0 {
95+
m.configGatewayRegionSel--
96+
m.configZAIRegionSel = m.configGatewayRegionSel
97+
}
98+
return m, nil
99+
case "down", "j":
100+
if m.configGatewayRegionSel < count-1 {
101+
m.configGatewayRegionSel++
102+
m.configZAIRegionSel = m.configGatewayRegionSel
103+
}
104+
return m, nil
105+
case "enter":
106+
if m.configGatewayRegionSel >= 0 && m.configGatewayRegionSel < count {
107+
chosen := opts[m.configGatewayRegionSel]
108+
if err := hawkconfig.SetGatewayRegion(m.configProvider, chosen.Value); err != nil {
109+
m.configNotice = "Error saving region: " + err.Error()
110+
return m, nil
111+
}
112+
InvalidateModelCacheProvider(m.configProvider)
113+
m.configGatewayRowsDirty = true
114+
ctx := context.Background()
115+
if post := strings.TrimSpace(m.configPostSaveKeysProvider); post == m.configProvider {
116+
m.configPostSaveKeysProvider = ""
117+
return m.startConfigKeyReplace(m.configProvider)
118+
}
119+
if hawkconfig.HasStoredCredentialForProvider(ctx, m.configProvider) {
120+
m.configNotice = "Saved region for " + hawkconfig.GatewayDisplayName(m.configProvider)
121+
if idx := m.configGatewayRowIndex(m.configProvider); idx >= 0 {
122+
m.configSel = idx
123+
}
124+
return m.closeConfigEntry(), nil
125+
}
126+
return m.startConfigKeyForProvider(m.configProvider)
127+
}
128+
return m.closeConfigEntry(), nil
129+
case "esc":
130+
m.configPostSaveKeysProvider = ""
131+
return m.closeConfigEntry(), nil
132+
default:
133+
return m, nil
134+
}
135+
}

0 commit comments

Comments
 (0)