kimi-code-usage 0.1.5__tar.gz → 0.2.0__tar.gz

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.
Files changed (127) hide show
  1. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/.gitignore +16 -0
  2. kimi_code_usage-0.2.0/CHANGELOG.md +40 -0
  3. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/PKG-INFO +10 -4
  4. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/README.md +5 -0
  5. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/README.zh.md +5 -0
  6. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/pyproject.toml +17 -3
  7. kimi_code_usage-0.2.0/src/kimi_code_usage/__main__.py +3 -0
  8. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/src/kimi_code_usage/config.py +42 -12
  9. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/src/kimi_code_usage/main.py +532 -106
  10. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/src/kimi_code_usage/providers/__init__.py +4 -0
  11. kimi_code_usage-0.2.0/src/kimi_code_usage/providers/claude.py +308 -0
  12. kimi_code_usage-0.2.0/src/kimi_code_usage/providers/codex.py +481 -0
  13. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/src/kimi_code_usage/providers/kimi.py +201 -3
  14. kimi_code_usage-0.2.0/src/kimi_code_usage/providers/kimi_membership.py +269 -0
  15. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/src/kimi_code_usage/providers/openrouter.py +8 -8
  16. kimi_code_usage-0.2.0/src/kimi_code_usage/providers/webbridge.py +115 -0
  17. kimi_code_usage-0.2.0/src/kimi_code_usage/server.py +851 -0
  18. kimi_code_usage-0.2.0/tests/conftest.py +15 -0
  19. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/tests/test_config.py +76 -45
  20. kimi_code_usage-0.2.0/tests/test_main.py +1861 -0
  21. kimi_code_usage-0.2.0/tests/test_mcp_smoke.py +48 -0
  22. kimi_code_usage-0.2.0/tests/test_provider_claude.py +467 -0
  23. kimi_code_usage-0.2.0/tests/test_provider_codex.py +656 -0
  24. kimi_code_usage-0.2.0/tests/test_provider_kimi.py +732 -0
  25. kimi_code_usage-0.2.0/tests/test_provider_kimi_membership.py +389 -0
  26. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/tests/test_provider_openrouter.py +28 -0
  27. kimi_code_usage-0.2.0/tests/test_provider_webbridge.py +133 -0
  28. kimi_code_usage-0.2.0/tests/test_server.py +370 -0
  29. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/uv.lock +1268 -665
  30. kimi_code_usage-0.2.0/vscode-extension/package-lock.json +4874 -0
  31. kimi_code_usage-0.1.5/.github/workflows/ci.yml +0 -42
  32. kimi_code_usage-0.1.5/docs/kimi-code-usage.md +0 -54
  33. kimi_code_usage-0.1.5/docs/superpowers/plans/2026-05-09-pace-indicator.md +0 -220
  34. kimi_code_usage-0.1.5/docs/superpowers/plans/2026-05-14-pace-theme-thresholds.md +0 -501
  35. kimi_code_usage-0.1.5/docs/superpowers/plans/2026-06-10-multi-llm-quota.md +0 -108
  36. kimi_code_usage-0.1.5/docs/superpowers/plans/2026-06-10-vscode-extension-ts-strictness.md +0 -679
  37. kimi_code_usage-0.1.5/docs/superpowers/specs/2026-05-09-pace-indicator-design.md +0 -189
  38. kimi_code_usage-0.1.5/docs/superpowers/specs/2026-05-14-pace-theme-thresholds-design.md +0 -106
  39. kimi_code_usage-0.1.5/docs/superpowers/specs/2026-06-10-multi-llm-quota-design.md +0 -307
  40. kimi_code_usage-0.1.5/docs/superpowers/specs/2026-06-10-vscode-extension-multi-provider-design.md +0 -394
  41. kimi_code_usage-0.1.5/docs/superpowers/specs/2026-06-10-vscode-extension-ts-refactor-design.md +0 -115
  42. kimi_code_usage-0.1.5/htmlcov/.gitignore +0 -2
  43. kimi_code_usage-0.1.5/htmlcov/class_index.html +0 -281
  44. kimi_code_usage-0.1.5/htmlcov/coverage_html_cb_dd2e7eb5.js +0 -735
  45. kimi_code_usage-0.1.5/htmlcov/favicon_32_cb_c827f16f.png +0 -0
  46. kimi_code_usage-0.1.5/htmlcov/function_index.html +0 -771
  47. kimi_code_usage-0.1.5/htmlcov/index.html +0 -189
  48. kimi_code_usage-0.1.5/htmlcov/keybd_closed_cb_900cfef5.png +0 -0
  49. kimi_code_usage-0.1.5/htmlcov/status.json +0 -1
  50. kimi_code_usage-0.1.5/htmlcov/style_cb_4667309f.css +0 -391
  51. kimi_code_usage-0.1.5/htmlcov/z_581a990e2fe7e610___init___py.html +0 -97
  52. kimi_code_usage-0.1.5/htmlcov/z_581a990e2fe7e610_config_py.html +0 -298
  53. kimi_code_usage-0.1.5/htmlcov/z_581a990e2fe7e610_main_py.html +0 -1202
  54. kimi_code_usage-0.1.5/htmlcov/z_581a990e2fe7e610_mcp_py.html +0 -272
  55. kimi_code_usage-0.1.5/htmlcov/z_aaf3485d2243f1af___init___py.html +0 -203
  56. kimi_code_usage-0.1.5/htmlcov/z_aaf3485d2243f1af_anthropic_py.html +0 -167
  57. kimi_code_usage-0.1.5/htmlcov/z_aaf3485d2243f1af_kimi_py.html +0 -256
  58. kimi_code_usage-0.1.5/htmlcov/z_aaf3485d2243f1af_openai_py.html +0 -173
  59. kimi_code_usage-0.1.5/htmlcov/z_aaf3485d2243f1af_openrouter_py.html +0 -463
  60. kimi_code_usage-0.1.5/tests/conftest.py +0 -4
  61. kimi_code_usage-0.1.5/tests/test_main.py +0 -973
  62. kimi_code_usage-0.1.5/tests/test_provider_kimi.py +0 -289
  63. kimi_code_usage-0.1.5/vscode-extension/.claude/settings.local.json +0 -8
  64. kimi_code_usage-0.1.5/vscode-extension/.github/workflows/ci.yml +0 -34
  65. kimi_code_usage-0.1.5/vscode-extension/.prettierignore +0 -10
  66. kimi_code_usage-0.1.5/vscode-extension/.prettierrc.json +0 -9
  67. kimi_code_usage-0.1.5/vscode-extension/.vscodeignore +0 -19
  68. kimi_code_usage-0.1.5/vscode-extension/LICENSE +0 -21
  69. kimi_code_usage-0.1.5/vscode-extension/README.md +0 -236
  70. kimi_code_usage-0.1.5/vscode-extension/assets/icon.png +0 -0
  71. kimi_code_usage-0.1.5/vscode-extension/assets/moon.svg +0 -3
  72. kimi_code_usage-0.1.5/vscode-extension/eslint.config.mjs +0 -48
  73. kimi_code_usage-0.1.5/vscode-extension/l10n/bundle.l10n.de.json +0 -151
  74. kimi_code_usage-0.1.5/vscode-extension/l10n/bundle.l10n.en.json +0 -151
  75. kimi_code_usage-0.1.5/vscode-extension/l10n/bundle.l10n.es.json +0 -151
  76. kimi_code_usage-0.1.5/vscode-extension/l10n/bundle.l10n.fr.json +0 -151
  77. kimi_code_usage-0.1.5/vscode-extension/l10n/bundle.l10n.it.json +0 -151
  78. kimi_code_usage-0.1.5/vscode-extension/l10n/bundle.l10n.ja.json +0 -151
  79. kimi_code_usage-0.1.5/vscode-extension/l10n/bundle.l10n.ko.json +0 -151
  80. kimi_code_usage-0.1.5/vscode-extension/l10n/bundle.l10n.pt.json +0 -151
  81. kimi_code_usage-0.1.5/vscode-extension/l10n/bundle.l10n.ru.json +0 -151
  82. kimi_code_usage-0.1.5/vscode-extension/l10n/bundle.l10n.zh-cn.json +0 -151
  83. kimi_code_usage-0.1.5/vscode-extension/package.json +0 -277
  84. kimi_code_usage-0.1.5/vscode-extension/scripts/copy-assets.ts +0 -24
  85. kimi_code_usage-0.1.5/vscode-extension/src/__mocks__/vscode.ts +0 -125
  86. kimi_code_usage-0.1.5/vscode-extension/src/api.test.ts +0 -641
  87. kimi_code_usage-0.1.5/vscode-extension/src/api.ts +0 -339
  88. kimi_code_usage-0.1.5/vscode-extension/src/apiCache.test.ts +0 -123
  89. kimi_code_usage-0.1.5/vscode-extension/src/apiCache.ts +0 -62
  90. kimi_code_usage-0.1.5/vscode-extension/src/config.test.ts +0 -298
  91. kimi_code_usage-0.1.5/vscode-extension/src/config.ts +0 -113
  92. kimi_code_usage-0.1.5/vscode-extension/src/extension.test.ts +0 -581
  93. kimi_code_usage-0.1.5/vscode-extension/src/extension.ts +0 -147
  94. kimi_code_usage-0.1.5/vscode-extension/src/historyPanel.test.ts +0 -359
  95. kimi_code_usage-0.1.5/vscode-extension/src/historyPanel.ts +0 -641
  96. kimi_code_usage-0.1.5/vscode-extension/src/i18n.test.ts +0 -247
  97. kimi_code_usage-0.1.5/vscode-extension/src/i18n.ts +0 -124
  98. kimi_code_usage-0.1.5/vscode-extension/src/notifier.test.ts +0 -213
  99. kimi_code_usage-0.1.5/vscode-extension/src/notifier.ts +0 -101
  100. kimi_code_usage-0.1.5/vscode-extension/src/pace.test.ts +0 -246
  101. kimi_code_usage-0.1.5/vscode-extension/src/pace.ts +0 -141
  102. kimi_code_usage-0.1.5/vscode-extension/src/predict.test.ts +0 -578
  103. kimi_code_usage-0.1.5/vscode-extension/src/predict.ts +0 -180
  104. kimi_code_usage-0.1.5/vscode-extension/src/statusBar.test.ts +0 -947
  105. kimi_code_usage-0.1.5/vscode-extension/src/statusBar.ts +0 -423
  106. kimi_code_usage-0.1.5/vscode-extension/src/storage-unlink.test.ts +0 -36
  107. kimi_code_usage-0.1.5/vscode-extension/src/storage.test.ts +0 -233
  108. kimi_code_usage-0.1.5/vscode-extension/src/storage.ts +0 -137
  109. kimi_code_usage-0.1.5/vscode-extension/src/types.ts +0 -102
  110. kimi_code_usage-0.1.5/vscode-extension/src/vscode-mock.test.ts +0 -106
  111. kimi_code_usage-0.1.5/vscode-extension/tsconfig.eslint.json +0 -10
  112. kimi_code_usage-0.1.5/vscode-extension/tsconfig.json +0 -20
  113. kimi_code_usage-0.1.5/vscode-extension/vitest.config.ts +0 -16
  114. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/.claude/settings.local.json +0 -0
  115. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/.env.example +0 -0
  116. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/AGENTS.md +0 -0
  117. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/kimi_usage.code-workspace +0 -0
  118. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/requirements.txt +0 -0
  119. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/src/kimi_code_usage/__init__.py +0 -0
  120. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/src/kimi_code_usage/mcp.py +0 -0
  121. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/src/kimi_code_usage/providers/anthropic.py +0 -0
  122. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/src/kimi_code_usage/providers/openai.py +0 -0
  123. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/tests/__init__.py +0 -0
  124. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/tests/test_mcp.py +0 -0
  125. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/tests/test_provider_anthropic.py +0 -0
  126. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/tests/test_provider_openai.py +0 -0
  127. {kimi_code_usage-0.1.5 → kimi_code_usage-0.2.0}/tests/test_providers_init.py +0 -0
@@ -14,6 +14,9 @@ build/
14
14
  node_modules/
15
15
  package-lock.json
16
16
 
17
+ # The VS Code extension's lock file is tracked so CI can install reproducibly.
18
+ !vscode-extension/package-lock.json
19
+
17
20
  # VSCode Extension artifacts
18
21
  *.vsix
19
22
  vscode-extension/out/
@@ -44,3 +47,16 @@ vscode-extension/coverage/
44
47
  .vscode-test/
45
48
  **/*.js.map
46
49
  tokscale-export-*.json
50
+
51
+ # Toolchain caches
52
+ .ruff_cache/
53
+ .mypy_cache/
54
+
55
+ # Coverage
56
+ htmlcov/
57
+
58
+ # Isolated development worktrees
59
+ .worktrees/
60
+
61
+ # Oh My OpenAgent local session artifacts
62
+ .omo/
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ Notable changes to the Python package (`kimi-code-usage` on PyPI). The VS Code
4
+ extension has its own changelog: [`vscode-extension/CHANGELOG.md`](vscode-extension/CHANGELOG.md).
5
+
6
+ This file starts at 0.2.0; earlier releases are recorded by the `v0.1.0`–`v0.1.6`
7
+ git tags.
8
+
9
+ ## [0.2.0] — 2026-10-20
10
+
11
+ ### Added
12
+
13
+ - **Extra Usage row.** Kimi's booster wallet is now reported as its own row
14
+ (`Extra Usage`, `加油包` in Chinese) instead of being invisible. A wallet that
15
+ reports no usable amount produces no row rather than a made-up `¥0.00`.
16
+ - **Monthly quota over the CLI OAuth token.** Monthly credits are read through
17
+ the Kimi Code CLI token, preferring that path and falling back to the
18
+ WebBridge browser snapshot when one is available.
19
+ - Parsing of the live `/usages` payload shape, including its `booster_wallet`
20
+ field.
21
+
22
+ ### Changed
23
+
24
+ - **`fastmcp` floor raised from 0.1.0 to 4.1.1** (`mcp` 2). The MCP surface used
25
+ here — `FastMCP(...)`, `@mcp.tool()`, `mcp.run()` — is unchanged, and the
26
+ upgrade is covered by a real stdio smoke test (`initialize` → `tools/list` →
27
+ `call_tool`). Projects that pin `mcp<2` in their own environment will need to
28
+ relax that pin.
29
+
30
+ ### Fixed
31
+
32
+ - Kimi: read the live `/usages` shape and count only emitted windows.
33
+ - Kimi: report OAuth monthly probe failures instead of dropping them silently.
34
+ - Kimi: only treat a wallet as live on an explicit signal, and stay silent when
35
+ it is live but reports no amount.
36
+ - Codex: label each rate-limit window from its own `limit_window_seconds`.
37
+ - Claude: read the Claude Code OAuth token and utilization correctly.
38
+ - OpenRouter: handle `BaseException` from `asyncio.gather` and fix typing.
39
+ - Dependencies: upgraded the vulnerable locked packages; `aiohttp >= 3.14.3` is
40
+ required for the patched parser.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: kimi-code-usage
3
- Version: 0.1.5
3
+ Version: 0.2.0
4
4
  Summary: A curated Kimi Coding Plan usage monitor with aesthetic CLI and MCP server.
5
5
  Project-URL: Homepage, https://github.com/Golden0Voyager/kimi-code-usage
6
6
  Project-URL: Repository, https://github.com/Golden0Voyager/kimi-code-usage
@@ -9,8 +9,9 @@ Author-email: Haining Yu <hainingyu@gmail.com>
9
9
  License: MIT
10
10
  Keywords: curated,kimi,mcp,monitor,moonshot,usage
11
11
  Requires-Python: >=3.10
12
- Requires-Dist: aiohttp>=3.8.0
13
- Requires-Dist: fastmcp>=0.1.0
12
+ Requires-Dist: aiohttp>=3.14.3
13
+ Requires-Dist: cloudscraper>=1.2.0
14
+ Requires-Dist: fastmcp>=4.1.1
14
15
  Requires-Dist: httpx[socks]>=0.20.0
15
16
  Requires-Dist: python-dotenv>=1.0.0
16
17
  Requires-Dist: rich>=12.0.0
@@ -50,6 +51,10 @@ In the era of "Vibecoding," transparency of resources is a prerequisite for flow
50
51
 
51
52
  **Prerequisite:** A [Kimi Coding Plan](https://api.kimi.com/coding/v1) API Key (`sk-kimi-xxx`), set as `KIMI_API_KEY` in your environment or `.env` file.
52
53
 
54
+ > Monthly membership usage is optional. In interactive mode, if an installed Kimi WebBridge daemon is stopped, `kimi-usage` asks on every launch whether to start it. Declining affects only monthly credits. The browser extension must still be connected and Kimi must be logged in; weekly and 5-hour Kimi usage continue working when monthly usage is unavailable.
55
+
56
+ > ChatGPT Plus usage reads the local Codex authentication file. After a 401 response, it refreshes the access token and retries once. If authentication still fails, run `codex login`.
57
+
53
58
  ---
54
59
 
55
60
  ### ⚡ CLI Reporter
@@ -60,6 +65,7 @@ In the era of "Vibecoding," transparency of resources is a prerequisite for flow
60
65
  ```bash
61
66
  pip install kimi-code-usage
62
67
  kimi-usage # Aesthetic Rich panel
68
+ kimi-usage -i # Interactive panel with optional WebBridge startup prompt
63
69
  kimi-usage --json # Machine-readable JSON
64
70
  kimi-usage --plain # Plain text output
65
71
  ```
@@ -32,6 +32,10 @@ In the era of "Vibecoding," transparency of resources is a prerequisite for flow
32
32
 
33
33
  **Prerequisite:** A [Kimi Coding Plan](https://api.kimi.com/coding/v1) API Key (`sk-kimi-xxx`), set as `KIMI_API_KEY` in your environment or `.env` file.
34
34
 
35
+ > Monthly membership usage is optional. In interactive mode, if an installed Kimi WebBridge daemon is stopped, `kimi-usage` asks on every launch whether to start it. Declining affects only monthly credits. The browser extension must still be connected and Kimi must be logged in; weekly and 5-hour Kimi usage continue working when monthly usage is unavailable.
36
+
37
+ > ChatGPT Plus usage reads the local Codex authentication file. After a 401 response, it refreshes the access token and retries once. If authentication still fails, run `codex login`.
38
+
35
39
  ---
36
40
 
37
41
  ### ⚡ CLI Reporter
@@ -42,6 +46,7 @@ In the era of "Vibecoding," transparency of resources is a prerequisite for flow
42
46
  ```bash
43
47
  pip install kimi-code-usage
44
48
  kimi-usage # Aesthetic Rich panel
49
+ kimi-usage -i # Interactive panel with optional WebBridge startup prompt
45
50
  kimi-usage --json # Machine-readable JSON
46
51
  kimi-usage --plain # Plain text output
47
52
  ```
@@ -32,6 +32,10 @@
32
32
 
33
33
  **统一前提:** 在环境变量或 `.env` 文件中设置 `KIMI_API_KEY`([Kimi Coding Plan](https://api.kimi.com/coding/v1) API Key,格式 `sk-kimi-xxx`)。
34
34
 
35
+ > 月度会员额度为可选增强功能。在交互模式中,如果已安装的 Kimi WebBridge daemon 尚未运行,`kimi-usage` 每次启动都会询问是否启动它;拒绝只影响月度额度。浏览器扩展仍须保持连接且 Kimi 必须已登录;月度额度不可用时,周用量和 5 小时用量仍会正常显示。
36
+
37
+ > ChatGPT Plus 用量读取本机 Codex 认证文件。接口返回 401 后会自动刷新 access token 并重试一次;认证仍失败时请运行 `codex login`。
38
+
35
39
  ---
36
40
 
37
41
  ### ⚡ CLI 终端报告器
@@ -42,6 +46,7 @@
42
46
  ```bash
43
47
  pip install kimi-code-usage
44
48
  kimi-usage # 美观的 Rich 面板
49
+ kimi-usage -i # 带可选 WebBridge 启动询问的交互面板
45
50
  kimi-usage --json # 机器可读的 JSON
46
51
  kimi-usage --plain # 纯文本输出
47
52
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kimi-code-usage"
3
- version = "0.1.5"
3
+ version = "0.2.0"
4
4
  description = "A curated Kimi Coding Plan usage monitor with aesthetic CLI and MCP server."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -10,11 +10,12 @@ authors = [
10
10
  license = { text = "MIT" }
11
11
  keywords = ["kimi", "moonshot", "mcp", "usage", "monitor", "curated"]
12
12
  dependencies = [
13
- "aiohttp>=3.8.0",
13
+ "aiohttp>=3.14.3",
14
14
  "rich>=12.0.0",
15
15
  "python-dotenv>=1.0.0",
16
- "fastmcp>=0.1.0",
16
+ "fastmcp>=4.1.1",
17
17
  "httpx[socks]>=0.20.0",
18
+ "cloudscraper>=1.2.0",
18
19
  ]
19
20
 
20
21
  [project.urls]
@@ -59,6 +60,7 @@ ignore = ["E501", "SIM108", "SIM117", "UP035", "N806"]
59
60
 
60
61
  [tool.mypy]
61
62
  ignore_missing_imports = true
63
+ files = ["src/kimi_code_usage/"]
62
64
 
63
65
  [tool.coverage.run]
64
66
  source = ["kimi_code_usage"]
@@ -69,3 +71,15 @@ show_missing = true
69
71
 
70
72
  [tool.bandit]
71
73
  skips = ["B101", "B311", "B404", "B603"]
74
+
75
+ [tool.hatch.build.targets.sdist]
76
+ # Explicit, because the default "whatever VCS says" rule breaks inside a git
77
+ # worktree: `.git` is a file there, detection fails, and hatchling falls back to
78
+ # shipping everything untracked — including vscode-extension/node_modules, which
79
+ # turned the sdist from 0.4 MB into 50 MB.
80
+ exclude = [
81
+ "/vscode-extension",
82
+ "/docs",
83
+ "/.github",
84
+ "/.scratch",
85
+ ]
@@ -0,0 +1,3 @@
1
+ from .main import run_cli
2
+
3
+ run_cli()
@@ -5,6 +5,7 @@ from pathlib import Path
5
5
 
6
6
  DEFAULT_CONFIG_PATH = Path.home() / ".kimi-usage" / "config.json"
7
7
 
8
+
8
9
  @dataclass
9
10
  class ProviderConfig:
10
11
  api_key: str | None = None
@@ -12,6 +13,7 @@ class ProviderConfig:
12
13
  enabled: bool = True
13
14
  management_key: str | None = None
14
15
 
16
+
15
17
  @dataclass
16
18
  class AppConfig:
17
19
  providers: dict[str, ProviderConfig] = field(default_factory=dict)
@@ -28,6 +30,7 @@ class AppConfig:
28
30
  def enabled_providers(self) -> list[str]:
29
31
  return [name for name, p_conf in self.providers.items() if p_conf.api_key and p_conf.enabled]
30
32
 
33
+
31
34
  class ConfigResolver:
32
35
  def __init__(self, config_path: str | None = None):
33
36
  self.config_path = Path(config_path) if config_path else DEFAULT_CONFIG_PATH
@@ -56,9 +59,10 @@ class ConfigResolver:
56
59
  theme = os.getenv("KIMI_USAGE_THEME", "blue-dark")
57
60
  self.config.theme = theme
58
61
 
59
- # Get language and visible providers from JSON
62
+ # Get language, visible providers, and provider order from JSON
60
63
  self.config.language = general_data.get("language")
61
64
  self.config.visible_providers = general_data.get("visibleProviders")
65
+ saved_provider_order = general_data.get("providerOrder")
62
66
 
63
67
  # Get OpenRouter metric preference, default requests
64
68
  or_metric = general_data.get("orMetric", "requests")
@@ -77,7 +81,7 @@ class ConfigResolver:
77
81
  self.config.days_window = days_window
78
82
 
79
83
  # 2. Setup providers
80
- all_providers = ["kimi", "openai", "anthropic", "openrouter"]
84
+ all_providers = ["kimi", "openai", "anthropic", "openrouter", "codex", "claude"]
81
85
 
82
86
  # Envs map
83
87
  env_keys = {
@@ -85,6 +89,8 @@ class ConfigResolver:
85
89
  "openai": ("OPENAI_API_KEY",),
86
90
  "anthropic": ("ANTHROPIC_API_KEY",),
87
91
  "openrouter": ("OPENROUTER_API_KEY", "OPENROUTER_ADMIN_KEY"),
92
+ "codex": ("CODEX_API_KEY",),
93
+ "claude": ("CLAUDE_API_KEY",),
88
94
  }
89
95
 
90
96
  env_urls = {
@@ -92,6 +98,8 @@ class ConfigResolver:
92
98
  "openai": "OPENAI_BASE_URL",
93
99
  "anthropic": "ANTHROPIC_BASE_URL",
94
100
  "openrouter": "OPENROUTER_BASE_URL",
101
+ "codex": "CODEX_BASE_URL",
102
+ "claude": "CLAUDE_BASE_URL",
95
103
  }
96
104
 
97
105
  default_urls = {
@@ -99,6 +107,8 @@ class ConfigResolver:
99
107
  "openai": "https://api.openai.com",
100
108
  "anthropic": "https://api.anthropic.com",
101
109
  "openrouter": "https://openrouter.ai/api",
110
+ "codex": "https://chatgpt.com/backend-api",
111
+ "claude": "https://api.anthropic.com",
102
112
  }
103
113
 
104
114
  for p in all_providers:
@@ -128,14 +138,23 @@ class ConfigResolver:
128
138
  or os.getenv("OPENROUTER_ADMIN_KEY")
129
139
  )
130
140
 
131
- # Get Enabled status from JSON, else ENV, default True
132
- enabled = p_data.get("enabled")
133
- if enabled is None:
134
- env_enabled = os.getenv(f"{p.upper()}_ENABLED")
135
- if env_enabled is not None:
136
- enabled = env_enabled.lower() not in ("false", "0", "no")
137
- else:
138
- enabled = True
141
+ # Codex & Claude: default-off, read local auth files directly
142
+ if p in ("codex", "claude"):
143
+ enabled = p_data.get("enabled")
144
+ if enabled is None:
145
+ env_enabled: str | None = os.getenv(f"{p.upper()}_ENABLED", "").lower()
146
+ enabled = env_enabled in ("true", "1", "yes")
147
+ if enabled:
148
+ api_key = "enabled" # Sentinel: passes enabled_providers check
149
+ else:
150
+ # Get Enabled status from JSON, else ENV, default True
151
+ enabled = p_data.get("enabled")
152
+ if enabled is None:
153
+ env_enabled = os.getenv(f"{p.upper()}_ENABLED")
154
+ if env_enabled is not None:
155
+ enabled = env_enabled.lower() not in ("false", "0", "no")
156
+ else:
157
+ enabled = True
139
158
 
140
159
  self.config.providers[p] = ProviderConfig(
141
160
  api_key=api_key if api_key else None,
@@ -145,7 +164,7 @@ class ConfigResolver:
145
164
  )
146
165
 
147
166
  # 3. Determine provider ordering
148
- default_order = ["anthropic", "openai", "openrouter", "kimi"]
167
+ default_order = ["anthropic", "openai", "openrouter", "kimi", "codex", "claude"]
149
168
  json_providers = list(providers_data.keys())
150
169
 
151
170
  final_order = []
@@ -156,6 +175,14 @@ class ConfigResolver:
156
175
  if p not in final_order:
157
176
  final_order.append(p)
158
177
 
178
+ # Persisted order from settings view takes precedence over derived order
179
+ if saved_provider_order:
180
+ saved_order = [p for p in saved_provider_order if p in all_providers]
181
+ for p in default_order:
182
+ if p not in saved_order:
183
+ saved_order.append(p)
184
+ final_order = saved_order
185
+
159
186
  self.config.provider_order = final_order
160
187
 
161
188
  return self.config
@@ -167,7 +194,8 @@ def save_theme(
167
194
  visible_providers: list[str] | None = None,
168
195
  or_metric: str | None = None,
169
196
  days_window: int | None = None,
170
- config_path: Path | None = None
197
+ provider_order: list[str] | None = None,
198
+ config_path: Path | None = None,
171
199
  ) -> None:
172
200
  """Persist settings into ``general`` in the JSON config file.
173
201
 
@@ -195,6 +223,8 @@ def save_theme(
195
223
  general["orMetric"] = or_metric
196
224
  if days_window is not None:
197
225
  general["daysWindow"] = days_window
226
+ if provider_order is not None:
227
+ general["providerOrder"] = provider_order
198
228
  data["general"] = general
199
229
 
200
230
  with open(path, "w", encoding="utf-8") as fh: