api-key-manager 2.1.0__tar.gz → 2.1.2__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 (98) hide show
  1. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/PKG-INFO +23 -3
  2. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/README.md +22 -2
  3. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/api_key_manager.egg-info/PKG-INFO +23 -3
  4. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/api_key_manager.egg-info/SOURCES.txt +7 -0
  5. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/__init__.py +3 -2
  6. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/api_models.py +9 -0
  7. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/core.py +10 -4
  8. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/proxy.py +0 -13
  9. api_key_manager-2.1.2/key_manager/py.typed +1 -0
  10. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/storage.py +10 -10
  11. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/validator.py +1 -1
  12. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/web.py +194 -149
  13. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/pyproject.toml +1 -1
  14. api_key_manager-2.1.2/tests/test_bug_fixes.py +246 -0
  15. api_key_manager-2.1.2/tests/test_core.py +265 -0
  16. api_key_manager-2.1.2/tests/test_logger.py +273 -0
  17. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_providers.py +1 -1
  18. api_key_manager-2.1.2/tests/test_proxy.py +122 -0
  19. api_key_manager-2.1.2/tests/test_tester.py +300 -0
  20. api_key_manager-2.1.2/tests/test_web_fixes.py +161 -0
  21. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/api_key_manager.egg-info/dependency_links.txt +0 -0
  22. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/api_key_manager.egg-info/entry_points.txt +0 -0
  23. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/api_key_manager.egg-info/requires.txt +0 -0
  24. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/api_key_manager.egg-info/top_level.txt +0 -0
  25. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/__main__.py +0 -0
  26. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/checker.py +0 -0
  27. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/cli.py +0 -0
  28. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/config.py +0 -0
  29. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/detector.py +0 -0
  30. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/errors.py +0 -0
  31. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/i18n.py +0 -0
  32. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/logger.py +0 -0
  33. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/model_capabilities.py +0 -0
  34. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/parser.py +0 -0
  35. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/__init__.py +0 -0
  36. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/ai302.py +0 -0
  37. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/anthropic.py +0 -0
  38. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/baichuan.py +0 -0
  39. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/base.py +0 -0
  40. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/cerebras.py +0 -0
  41. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/cohere.py +0 -0
  42. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/cstcloud.py +0 -0
  43. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/dashscope.py +0 -0
  44. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/dashscope_coding.py +0 -0
  45. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/deepseek.py +0 -0
  46. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/dmxapi.py +0 -0
  47. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/doubao.py +0 -0
  48. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/fireworks.py +0 -0
  49. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/google.py +0 -0
  50. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/grok.py +0 -0
  51. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/groq.py +0 -0
  52. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/huggingface.py +0 -0
  53. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/hyperbolic.py +0 -0
  54. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/infini.py +0 -0
  55. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/infini_coding.py +0 -0
  56. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/kimi.py +0 -0
  57. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/kimi_coding.py +0 -0
  58. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/longcat.py +0 -0
  59. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/mimo.py +0 -0
  60. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/mimo_plan.py +0 -0
  61. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/minimax.py +0 -0
  62. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/minimax_plan.py +0 -0
  63. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/mistral.py +0 -0
  64. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/models_registry.py +0 -0
  65. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/modelscope.py +0 -0
  66. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/nvidia.py +0 -0
  67. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/ocoolai.py +0 -0
  68. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/openai.py +0 -0
  69. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/openrouter.py +0 -0
  70. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/perplexity.py +0 -0
  71. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/poe.py +0 -0
  72. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/ppio.py +0 -0
  73. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/replicate.py +0 -0
  74. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/siliconflow.py +0 -0
  75. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/stepfun.py +0 -0
  76. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/tencent_hunyuan.py +0 -0
  77. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/together.py +0 -0
  78. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/yi.py +0 -0
  79. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/zai.py +0 -0
  80. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/zhipu.py +0 -0
  81. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/providers/zhipu_coding.py +0 -0
  82. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/ssrf.py +0 -0
  83. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/tester.py +0 -0
  84. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/url_override.py +0 -0
  85. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/key_manager/webhook.py +0 -0
  86. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/setup.cfg +0 -0
  87. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_checker.py +0 -0
  88. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_detector.py +0 -0
  89. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_e2e.py +0 -0
  90. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_errors.py +0 -0
  91. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_i18n.py +0 -0
  92. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_openapi.py +0 -0
  93. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_parser.py +0 -0
  94. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_provider_detection.py +0 -0
  95. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_security.py +0 -0
  96. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_storage.py +0 -0
  97. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_validator.py +0 -0
  98. {api_key_manager-2.1.0 → api_key_manager-2.1.2}/tests/test_webhook.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: api-key-manager
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Batch manage API keys for 44+ AI providers with CLI and Web interfaces
5
5
  Author: Townrain
6
6
  License: MIT
@@ -192,10 +192,11 @@ python web.py
192
192
  ### 安全防护
193
193
 
194
194
  - **路径遍历防护** - 导入端点验证路径在允许目录内
195
- - **SSRF 防护** - `custom_base_url` 验证域名白名单,阻止私有 IP
195
+ - **SSRF 防护** - `custom_base_url` 验证域名白名单,阻止私有 IP,已接入 `check/single` 和 `balance` 端点
196
196
  - **时序安全认证** - 使用 `hmac.compare_digest()` 防止时序攻击
197
197
  - **认证警告** - 未配置 API Key 时启动警告
198
198
  - **密钥掩码** - API 响应中只返回 `key_masked`,不暴露完整密钥
199
+ - **Webhook 安全** - Webhook 端点使用正确的 API 方法,防止运行时错误
199
200
 
200
201
  ### API 认证
201
202
 
@@ -609,7 +610,7 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
609
610
  | 密钥解析 | `test_parser.py` | 12 |
610
611
  | 验证器 | `test_validator.py` | 5 |
611
612
  | 检查器 | `test_checker.py` | 4 |
612
- | 提供商合约 | `test_providers.py` | 30 |
613
+ | 提供商合约 | `test_providers.py` | 220 |
613
614
  | 安全回归 | `test_security.py` | 12 |
614
615
  | 加密存储 | `test_storage.py` | 26 |
615
616
  | 错误系统 | `test_errors.py` | 28 |
@@ -617,7 +618,12 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
617
618
  | 端到端 | `test_e2e.py` | 17 |
618
619
  | Webhook | `test_webhook.py` | 35 |
619
620
  | OpenAPI | `test_openapi.py` | 26 |
621
+ | 代理检测 | `test_proxy.py` | 19 |
622
+ | 日志系统 | `test_logger.py` | 21 |
623
+ | 能力测试 | `test_tester.py` | 11 |
624
+ | 核心门面 | `test_core.py` | 22 |
620
625
 
626
+ **总测试数**: 583+ | **覆盖率**: 88%
621
627
  ## SDK 使用
622
628
 
623
629
  ### Python SDK
@@ -704,6 +710,20 @@ const result = await client.checkSingleKey({ key: 'sk-xxx', provider: 'openai' }
704
710
 
705
711
  模型列表从 Cherry Studio 同步,每日更新一次。新发布的模型可能需要等待同步后才能被检测到。
706
712
 
713
+
714
+ ## 更新日志
715
+
716
+ ### v2.1.2 (2026-06-11)
717
+
718
+ - **Bug 修复**: 修复 `KeyManager.detect_provider()` 调用异步函数未 await 的问题
719
+ - **测试扩展**: 提供商合约测试扩展至全部 44 个服务商
720
+ - **测试新增**: 新增 `test_proxy.py`、`test_logger.py`、`test_tester.py`、`test_core.py`
721
+ - **覆盖率提升**: 测试覆盖率从 74% 提升至 88%
722
+
723
+ ### v2.1.1
724
+
725
+ - 初始发布版本
726
+
707
727
  ## 许可证
708
728
 
709
729
  MIT License
@@ -159,10 +159,11 @@ python web.py
159
159
  ### 安全防护
160
160
 
161
161
  - **路径遍历防护** - 导入端点验证路径在允许目录内
162
- - **SSRF 防护** - `custom_base_url` 验证域名白名单,阻止私有 IP
162
+ - **SSRF 防护** - `custom_base_url` 验证域名白名单,阻止私有 IP,已接入 `check/single` 和 `balance` 端点
163
163
  - **时序安全认证** - 使用 `hmac.compare_digest()` 防止时序攻击
164
164
  - **认证警告** - 未配置 API Key 时启动警告
165
165
  - **密钥掩码** - API 响应中只返回 `key_masked`,不暴露完整密钥
166
+ - **Webhook 安全** - Webhook 端点使用正确的 API 方法,防止运行时错误
166
167
 
167
168
  ### API 认证
168
169
 
@@ -576,7 +577,7 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
576
577
  | 密钥解析 | `test_parser.py` | 12 |
577
578
  | 验证器 | `test_validator.py` | 5 |
578
579
  | 检查器 | `test_checker.py` | 4 |
579
- | 提供商合约 | `test_providers.py` | 30 |
580
+ | 提供商合约 | `test_providers.py` | 220 |
580
581
  | 安全回归 | `test_security.py` | 12 |
581
582
  | 加密存储 | `test_storage.py` | 26 |
582
583
  | 错误系统 | `test_errors.py` | 28 |
@@ -584,7 +585,12 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
584
585
  | 端到端 | `test_e2e.py` | 17 |
585
586
  | Webhook | `test_webhook.py` | 35 |
586
587
  | OpenAPI | `test_openapi.py` | 26 |
588
+ | 代理检测 | `test_proxy.py` | 19 |
589
+ | 日志系统 | `test_logger.py` | 21 |
590
+ | 能力测试 | `test_tester.py` | 11 |
591
+ | 核心门面 | `test_core.py` | 22 |
587
592
 
593
+ **总测试数**: 583+ | **覆盖率**: 88%
588
594
  ## SDK 使用
589
595
 
590
596
  ### Python SDK
@@ -671,6 +677,20 @@ const result = await client.checkSingleKey({ key: 'sk-xxx', provider: 'openai' }
671
677
 
672
678
  模型列表从 Cherry Studio 同步,每日更新一次。新发布的模型可能需要等待同步后才能被检测到。
673
679
 
680
+
681
+ ## 更新日志
682
+
683
+ ### v2.1.2 (2026-06-11)
684
+
685
+ - **Bug 修复**: 修复 `KeyManager.detect_provider()` 调用异步函数未 await 的问题
686
+ - **测试扩展**: 提供商合约测试扩展至全部 44 个服务商
687
+ - **测试新增**: 新增 `test_proxy.py`、`test_logger.py`、`test_tester.py`、`test_core.py`
688
+ - **覆盖率提升**: 测试覆盖率从 74% 提升至 88%
689
+
690
+ ### v2.1.1
691
+
692
+ - 初始发布版本
693
+
674
694
  ## 许可证
675
695
 
676
696
  MIT License
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: api-key-manager
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Batch manage API keys for 44+ AI providers with CLI and Web interfaces
5
5
  Author: Townrain
6
6
  License: MIT
@@ -192,10 +192,11 @@ python web.py
192
192
  ### 安全防护
193
193
 
194
194
  - **路径遍历防护** - 导入端点验证路径在允许目录内
195
- - **SSRF 防护** - `custom_base_url` 验证域名白名单,阻止私有 IP
195
+ - **SSRF 防护** - `custom_base_url` 验证域名白名单,阻止私有 IP,已接入 `check/single` 和 `balance` 端点
196
196
  - **时序安全认证** - 使用 `hmac.compare_digest()` 防止时序攻击
197
197
  - **认证警告** - 未配置 API Key 时启动警告
198
198
  - **密钥掩码** - API 响应中只返回 `key_masked`,不暴露完整密钥
199
+ - **Webhook 安全** - Webhook 端点使用正确的 API 方法,防止运行时错误
199
200
 
200
201
  ### API 认证
201
202
 
@@ -609,7 +610,7 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
609
610
  | 密钥解析 | `test_parser.py` | 12 |
610
611
  | 验证器 | `test_validator.py` | 5 |
611
612
  | 检查器 | `test_checker.py` | 4 |
612
- | 提供商合约 | `test_providers.py` | 30 |
613
+ | 提供商合约 | `test_providers.py` | 220 |
613
614
  | 安全回归 | `test_security.py` | 12 |
614
615
  | 加密存储 | `test_storage.py` | 26 |
615
616
  | 错误系统 | `test_errors.py` | 28 |
@@ -617,7 +618,12 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
617
618
  | 端到端 | `test_e2e.py` | 17 |
618
619
  | Webhook | `test_webhook.py` | 35 |
619
620
  | OpenAPI | `test_openapi.py` | 26 |
621
+ | 代理检测 | `test_proxy.py` | 19 |
622
+ | 日志系统 | `test_logger.py` | 21 |
623
+ | 能力测试 | `test_tester.py` | 11 |
624
+ | 核心门面 | `test_core.py` | 22 |
620
625
 
626
+ **总测试数**: 583+ | **覆盖率**: 88%
621
627
  ## SDK 使用
622
628
 
623
629
  ### Python SDK
@@ -704,6 +710,20 @@ const result = await client.checkSingleKey({ key: 'sk-xxx', provider: 'openai' }
704
710
 
705
711
  模型列表从 Cherry Studio 同步,每日更新一次。新发布的模型可能需要等待同步后才能被检测到。
706
712
 
713
+
714
+ ## 更新日志
715
+
716
+ ### v2.1.2 (2026-06-11)
717
+
718
+ - **Bug 修复**: 修复 `KeyManager.detect_provider()` 调用异步函数未 await 的问题
719
+ - **测试扩展**: 提供商合约测试扩展至全部 44 个服务商
720
+ - **测试新增**: 新增 `test_proxy.py`、`test_logger.py`、`test_tester.py`、`test_core.py`
721
+ - **覆盖率提升**: 测试覆盖率从 74% 提升至 88%
722
+
723
+ ### v2.1.1
724
+
725
+ - 初始发布版本
726
+
707
727
  ## 许可证
708
728
 
709
729
  MIT License
@@ -20,6 +20,7 @@ key_manager/logger.py
20
20
  key_manager/model_capabilities.py
21
21
  key_manager/parser.py
22
22
  key_manager/proxy.py
23
+ key_manager/py.typed
23
24
  key_manager/ssrf.py
24
25
  key_manager/storage.py
25
26
  key_manager/tester.py
@@ -74,16 +75,22 @@ key_manager/providers/yi.py
74
75
  key_manager/providers/zai.py
75
76
  key_manager/providers/zhipu.py
76
77
  key_manager/providers/zhipu_coding.py
78
+ tests/test_bug_fixes.py
77
79
  tests/test_checker.py
80
+ tests/test_core.py
78
81
  tests/test_detector.py
79
82
  tests/test_e2e.py
80
83
  tests/test_errors.py
81
84
  tests/test_i18n.py
85
+ tests/test_logger.py
82
86
  tests/test_openapi.py
83
87
  tests/test_parser.py
84
88
  tests/test_provider_detection.py
85
89
  tests/test_providers.py
90
+ tests/test_proxy.py
86
91
  tests/test_security.py
87
92
  tests/test_storage.py
93
+ tests/test_tester.py
88
94
  tests/test_validator.py
95
+ tests/test_web_fixes.py
89
96
  tests/test_webhook.py
@@ -1,4 +1,4 @@
1
- """Key Manager - Batch API key management for 44+ AI providers."""
1
+ """Key Manager - Batch API key management for 44+ AI providers."""
2
2
  from key_manager.core import KeyManager
3
3
  from key_manager.providers import PROVIDERS, get_display_name
4
4
  from key_manager.errors import KeyManagerError, ErrorCode
@@ -13,4 +13,5 @@ __all__ = [
13
13
  "KeyStore",
14
14
  ]
15
15
 
16
- __version__ = "2.1.0"
16
+ __version__ = "2.1.2"
17
+
@@ -42,6 +42,7 @@ __all__ = [
42
42
  "OperationEntry",
43
43
  "OperationsResponse",
44
44
  "ProgressResponse",
45
+ "ProxyResponse",
45
46
  ]
46
47
 
47
48
 
@@ -356,3 +357,11 @@ class ProgressResponse(BaseModel):
356
357
  total: int = Field(0, description="Total items to process")
357
358
  status: str = Field("", description="Task status: loading / done / error")
358
359
  results: dict[str, Any] | None = Field(None, description="Final results when complete")
360
+
361
+
362
+ # ── Proxy ─────────────────────────────────────────────────────────────────────
363
+
364
+
365
+ class ProxyResponse(BaseModel):
366
+ proxy: str | None = Field(None, description="Detected proxy URL")
367
+ source: str = Field("", description="Proxy source: config / env / auto / none")
@@ -62,10 +62,16 @@ class KeyManager:
62
62
  """Save keys to encrypted storage."""
63
63
  self._store.save(data)
64
64
 
65
- def detect_provider(self, key: str) -> list[str]:
65
+ async def detect_provider(self, key: str) -> list[str]:
66
66
  """Auto-detect provider from key prefix."""
67
- return detect_provider(key)
68
-
67
+ import httpx
68
+ from key_manager.detector import detect_provider as _detect_provider
69
+
70
+ timeout = self.config.get("check", {}).get("timeout_seconds", 30)
71
+ proxy = self.config.get("proxy") or None
72
+ async with httpx.AsyncClient(timeout=timeout, proxy=proxy) as client:
73
+ result = await _detect_provider(client, key)
74
+ return [result] if result else []
69
75
  def list_keys(
70
76
  self,
71
77
  provider: str | None = None,
@@ -139,7 +145,7 @@ class KeyManager:
139
145
  from key_manager.providers import PROVIDERS
140
146
 
141
147
  if not provider:
142
- providers = self.detect_provider(key)
148
+ providers = await self.detect_provider(key)
143
149
  if not providers:
144
150
  return {"valid": False, "error": "Could not detect provider"}
145
151
  provider = providers[0]
@@ -55,16 +55,3 @@ def get_proxy(config_proxy: str = None) -> str:
55
55
  return ""
56
56
  # Otherwise use the configured proxy
57
57
  return config_proxy
58
- """Get proxy from config or auto-detect."""
59
- # 空字符串表示禁用代理
60
- if config_proxy == "":
61
- return ""
62
- # 有配置则使用配置
63
- if config_proxy:
64
- return config_proxy
65
- # 没有配置则自动检测
66
- return detect_system_proxy()
67
- """Get proxy from config or auto-detect."""
68
- if config_proxy:
69
- return config_proxy
70
- return detect_system_proxy()
@@ -0,0 +1 @@
1
+
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  from cryptography.hazmat.primitives.ciphers.aead import AESGCM
7
7
  from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
8
8
  from cryptography.hazmat.primitives import hashes
9
+ from key_manager.errors import ErrorCode, StorageError
9
10
 
10
11
 
11
12
  _ITERATIONS = 100_000
@@ -33,7 +34,8 @@ def _get_passphrase(config: dict | None = None) -> str:
33
34
  if config and config.get("encryption", {}).get("passphrase"):
34
35
  return config["encryption"]["passphrase"]
35
36
  raise StorageError(
36
- "No passphrase found. Set KEY_MANAGER_SECRET env var or "
37
+ code=ErrorCode.STORAGE_ENCRYPTION_ERROR,
38
+ message="No passphrase found. Set KEY_MANAGER_SECRET env var or "
37
39
  "configure encryption.passphrase in config.yaml"
38
40
  )
39
41
 
@@ -46,8 +48,6 @@ def _b64d(s: str) -> bytes:
46
48
  return base64.b64decode(s)
47
49
 
48
50
 
49
- class StorageError(Exception):
50
- pass
51
51
 
52
52
 
53
53
  class KeyStore:
@@ -58,12 +58,12 @@ class KeyStore:
58
58
 
59
59
  def load(self) -> dict:
60
60
  if not self.path.exists():
61
- raise StorageError(f"File not found: {self.path}")
61
+ raise StorageError(code=ErrorCode.STORAGE_READ_ERROR, message=f"File not found: {self.path}")
62
62
  raw = self.path.read_text(encoding="utf-8")
63
63
  try:
64
64
  data = json.loads(raw)
65
65
  except json.JSONDecodeError as e:
66
- raise StorageError(f"Invalid JSON in {self.path}: {e}") from e
66
+ raise StorageError(code=ErrorCode.STORAGE_READ_ERROR, message=f"Invalid JSON in {self.path}: {e}") from e
67
67
  if isinstance(data, dict) and data.get("encrypted") is True:
68
68
  return self._decrypt(data)
69
69
  return data
@@ -83,12 +83,12 @@ class KeyStore:
83
83
 
84
84
  def rotate_key(self, new_passphrase: str) -> dict:
85
85
  if not self.path.exists():
86
- raise StorageError(f"File not found: {self.path}")
86
+ raise StorageError(code=ErrorCode.STORAGE_READ_ERROR, message=f"File not found: {self.path}")
87
87
  raw = self.path.read_text(encoding="utf-8")
88
88
  try:
89
89
  envelope = json.loads(raw)
90
90
  except json.JSONDecodeError as e:
91
- raise StorageError(f"Invalid JSON in {self.path}: {e}") from e
91
+ raise StorageError(code=ErrorCode.STORAGE_READ_ERROR, message=f"Invalid JSON in {self.path}: {e}") from e
92
92
  if isinstance(envelope, dict) and envelope.get("encrypted") is True:
93
93
  data = self._decrypt(envelope)
94
94
  else:
@@ -120,15 +120,15 @@ class KeyStore:
120
120
  nonce = _b64d(envelope["nonce"])
121
121
  ciphertext = _b64d(envelope["data"])
122
122
  except (KeyError, ValueError) as e:
123
- raise StorageError(f"Malformed encrypted envelope: {e}") from e
123
+ raise StorageError(code=ErrorCode.STORAGE_ENCRYPTION_ERROR, message=f"Malformed encrypted envelope: {e}") from e
124
124
  passphrase = _get_passphrase(self.config)
125
125
  key = _derive_key(passphrase, salt)
126
126
  aes = AESGCM(key)
127
127
  try:
128
128
  plaintext = aes.decrypt(nonce, ciphertext, None)
129
129
  except Exception as e:
130
- raise StorageError(f"Decryption failed (wrong key or tampered data): {e}") from e
130
+ raise StorageError(code=ErrorCode.STORAGE_ENCRYPTION_ERROR, message=f"Decryption failed (wrong key or tampered data): {e}") from e
131
131
  try:
132
132
  return json.loads(plaintext.decode("utf-8"))
133
133
  except json.JSONDecodeError as e:
134
- raise StorageError(f"Decrypted data is not valid JSON: {e}") from e
134
+ raise StorageError(code=ErrorCode.STORAGE_ENCRYPTION_ERROR, message=f"Decrypted data is not valid JSON: {e}") from e
@@ -60,7 +60,7 @@ async def validate_keys(keys_file: str = "./data/keys.json",
60
60
  provider_name = info["provider"].lower()
61
61
  provider = PROVIDERS.get(provider_name)
62
62
  if not provider:
63
- from src.providers.base import CheckResult
63
+ from key_manager.providers.base import CheckResult
64
64
  result = CheckResult(valid=False, status_code=None, latency_ms=0, error=f"unknown provider: {provider_name}")
65
65
  else:
66
66
  result = await provider.check(client, key)