illusion-code 0.1.0__py3-none-any.whl

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 (214) hide show
  1. illusion/__init__.py +24 -0
  2. illusion/__main__.py +15 -0
  3. illusion/_frontend/dist/index.mjs +39208 -0
  4. illusion/_frontend/package.json +27 -0
  5. illusion/_frontend/src/App.tsx +624 -0
  6. illusion/_frontend/src/components/CommandPicker.tsx +98 -0
  7. illusion/_frontend/src/components/Composer.tsx +55 -0
  8. illusion/_frontend/src/components/ComposerController.tsx +128 -0
  9. illusion/_frontend/src/components/ConversationView.tsx +750 -0
  10. illusion/_frontend/src/components/Footer.tsx +25 -0
  11. illusion/_frontend/src/components/MarkdownContent.tsx +537 -0
  12. illusion/_frontend/src/components/MarkdownTable.tsx +245 -0
  13. illusion/_frontend/src/components/ModalHost.tsx +425 -0
  14. illusion/_frontend/src/components/MultilineTextInput.tsx +250 -0
  15. illusion/_frontend/src/components/PromptInput.tsx +64 -0
  16. illusion/_frontend/src/components/SelectModal.tsx +78 -0
  17. illusion/_frontend/src/components/SidePanel.tsx +175 -0
  18. illusion/_frontend/src/components/Spinner.tsx +77 -0
  19. illusion/_frontend/src/components/StatusBar.tsx +142 -0
  20. illusion/_frontend/src/components/SwarmPanel.tsx +141 -0
  21. illusion/_frontend/src/components/TodoPanel.tsx +126 -0
  22. illusion/_frontend/src/components/ToolCallDisplay.tsx +202 -0
  23. illusion/_frontend/src/components/TranscriptPane.tsx +79 -0
  24. illusion/_frontend/src/components/WelcomeBanner.tsx +37 -0
  25. illusion/_frontend/src/hooks/useBackendSession.ts +468 -0
  26. illusion/_frontend/src/hooks/useTerminalSize.ts +9 -0
  27. illusion/_frontend/src/i18n.ts +78 -0
  28. illusion/_frontend/src/index.tsx +42 -0
  29. illusion/_frontend/src/theme/ThemeContext.tsx +19 -0
  30. illusion/_frontend/src/theme/builtinThemes.ts +89 -0
  31. illusion/_frontend/src/types.ts +110 -0
  32. illusion/_frontend/src/utils/markdown.ts +33 -0
  33. illusion/_frontend/src/utils/thinking.ts +191 -0
  34. illusion/_frontend/tsconfig.json +13 -0
  35. illusion/_web_dist/assets/index-BseIw-ik.css +10 -0
  36. illusion/_web_dist/assets/index-C_0ZWMuW.js +82 -0
  37. illusion/_web_dist/index.html +16 -0
  38. illusion/api/__init__.py +36 -0
  39. illusion/api/client.py +568 -0
  40. illusion/api/codex_client.py +563 -0
  41. illusion/api/compat.py +138 -0
  42. illusion/api/effort.py +128 -0
  43. illusion/api/errors.py +57 -0
  44. illusion/api/openai_client.py +819 -0
  45. illusion/api/provider.py +148 -0
  46. illusion/api/registry.py +479 -0
  47. illusion/api/usage.py +45 -0
  48. illusion/auth/__init__.py +50 -0
  49. illusion/auth/copilot.py +419 -0
  50. illusion/auth/external.py +612 -0
  51. illusion/auth/flows.py +58 -0
  52. illusion/auth/manager.py +214 -0
  53. illusion/auth/storage.py +372 -0
  54. illusion/bridge/__init__.py +38 -0
  55. illusion/bridge/manager.py +190 -0
  56. illusion/bridge/session_runner.py +84 -0
  57. illusion/bridge/types.py +113 -0
  58. illusion/bridge/work_secret.py +131 -0
  59. illusion/cli.py +1228 -0
  60. illusion/commands/__init__.py +32 -0
  61. illusion/commands/registry.py +1934 -0
  62. illusion/config/__init__.py +39 -0
  63. illusion/config/i18n.py +522 -0
  64. illusion/config/paths.py +259 -0
  65. illusion/config/settings.py +564 -0
  66. illusion/coordinator/__init__.py +41 -0
  67. illusion/coordinator/agent_definitions.py +1093 -0
  68. illusion/coordinator/coordinator_mode.py +127 -0
  69. illusion/engine/__init__.py +95 -0
  70. illusion/engine/cost_tracker.py +55 -0
  71. illusion/engine/messages.py +369 -0
  72. illusion/engine/query.py +632 -0
  73. illusion/engine/query_engine.py +343 -0
  74. illusion/engine/stream_events.py +169 -0
  75. illusion/hooks/__init__.py +67 -0
  76. illusion/hooks/events.py +43 -0
  77. illusion/hooks/executor.py +397 -0
  78. illusion/hooks/hot_reload.py +74 -0
  79. illusion/hooks/loader.py +133 -0
  80. illusion/hooks/schemas.py +121 -0
  81. illusion/hooks/types.py +86 -0
  82. illusion/mcp/__init__.py +104 -0
  83. illusion/mcp/client.py +377 -0
  84. illusion/mcp/config.py +140 -0
  85. illusion/mcp/types.py +175 -0
  86. illusion/memory/__init__.py +36 -0
  87. illusion/memory/manager.py +94 -0
  88. illusion/memory/memdir.py +58 -0
  89. illusion/memory/paths.py +57 -0
  90. illusion/memory/scan.py +120 -0
  91. illusion/memory/search.py +83 -0
  92. illusion/memory/types.py +43 -0
  93. illusion/output_styles/__init__.py +15 -0
  94. illusion/output_styles/loader.py +64 -0
  95. illusion/permissions/__init__.py +39 -0
  96. illusion/permissions/checker.py +174 -0
  97. illusion/permissions/modes.py +38 -0
  98. illusion/platforms.py +148 -0
  99. illusion/plugins/__init__.py +71 -0
  100. illusion/plugins/bundled/__init__.py +0 -0
  101. illusion/plugins/installer.py +59 -0
  102. illusion/plugins/loader.py +301 -0
  103. illusion/plugins/schemas.py +51 -0
  104. illusion/plugins/types.py +56 -0
  105. illusion/prompts/__init__.py +29 -0
  106. illusion/prompts/claudemd.py +74 -0
  107. illusion/prompts/context.py +187 -0
  108. illusion/prompts/environment.py +189 -0
  109. illusion/prompts/system_prompt.py +155 -0
  110. illusion/py.typed +0 -0
  111. illusion/sandbox/__init__.py +29 -0
  112. illusion/sandbox/adapter.py +174 -0
  113. illusion/services/__init__.py +59 -0
  114. illusion/services/compact/__init__.py +1015 -0
  115. illusion/services/cron.py +338 -0
  116. illusion/services/cron_scheduler.py +715 -0
  117. illusion/services/file_history.py +258 -0
  118. illusion/services/lsp/__init__.py +455 -0
  119. illusion/services/session_storage.py +237 -0
  120. illusion/services/token_estimation.py +72 -0
  121. illusion/skills/__init__.py +60 -0
  122. illusion/skills/bundled/__init__.py +110 -0
  123. illusion/skills/bundled/content/batch.md +86 -0
  124. illusion/skills/bundled/content/coding-guidelines.md +70 -0
  125. illusion/skills/bundled/content/debug.md +38 -0
  126. illusion/skills/bundled/content/loop.md +82 -0
  127. illusion/skills/bundled/content/remember.md +105 -0
  128. illusion/skills/bundled/content/simplify.md +53 -0
  129. illusion/skills/bundled/content/skillify.md +113 -0
  130. illusion/skills/bundled/content/stuck.md +54 -0
  131. illusion/skills/bundled/content/update-config.md +329 -0
  132. illusion/skills/bundled/content/verify.md +74 -0
  133. illusion/skills/loader.py +219 -0
  134. illusion/skills/registry.py +40 -0
  135. illusion/skills/types.py +24 -0
  136. illusion/state/__init__.py +18 -0
  137. illusion/state/app_state.py +67 -0
  138. illusion/state/store.py +93 -0
  139. illusion/swarm/__init__.py +71 -0
  140. illusion/swarm/agent_executor.py +857 -0
  141. illusion/swarm/in_process.py +259 -0
  142. illusion/swarm/subprocess_backend.py +136 -0
  143. illusion/swarm/team_helpers.py +123 -0
  144. illusion/swarm/types.py +159 -0
  145. illusion/swarm/worktree.py +347 -0
  146. illusion/tasks/__init__.py +33 -0
  147. illusion/tasks/local_agent_task.py +42 -0
  148. illusion/tasks/local_shell_task.py +27 -0
  149. illusion/tasks/manager.py +377 -0
  150. illusion/tasks/stop_task.py +21 -0
  151. illusion/tasks/types.py +88 -0
  152. illusion/tools/__init__.py +126 -0
  153. illusion/tools/agent_tool.py +388 -0
  154. illusion/tools/ask_user_question_tool.py +186 -0
  155. illusion/tools/base.py +149 -0
  156. illusion/tools/bash_tool.py +413 -0
  157. illusion/tools/config_tool.py +90 -0
  158. illusion/tools/cron_tool.py +473 -0
  159. illusion/tools/enter_plan_mode_tool.py +147 -0
  160. illusion/tools/enter_worktree_tool.py +188 -0
  161. illusion/tools/exit_plan_mode_tool.py +69 -0
  162. illusion/tools/exit_worktree_tool.py +225 -0
  163. illusion/tools/file_edit_tool.py +283 -0
  164. illusion/tools/file_read_tool.py +294 -0
  165. illusion/tools/file_write_tool.py +184 -0
  166. illusion/tools/glob_tool.py +165 -0
  167. illusion/tools/grep_tool.py +190 -0
  168. illusion/tools/list_mcp_resources_tool.py +80 -0
  169. illusion/tools/lsp_tool.py +333 -0
  170. illusion/tools/mcp_auth_tool.py +100 -0
  171. illusion/tools/mcp_tool.py +75 -0
  172. illusion/tools/notebook_edit_tool.py +242 -0
  173. illusion/tools/powershell_tool.py +334 -0
  174. illusion/tools/read_mcp_resource_tool.py +63 -0
  175. illusion/tools/repl_tool.py +100 -0
  176. illusion/tools/send_message_tool.py +112 -0
  177. illusion/tools/shell_common.py +187 -0
  178. illusion/tools/skill_tool.py +86 -0
  179. illusion/tools/sleep_tool.py +62 -0
  180. illusion/tools/structured_output_tool.py +58 -0
  181. illusion/tools/task_create_tool.py +98 -0
  182. illusion/tools/task_get_tool.py +94 -0
  183. illusion/tools/task_list_tool.py +94 -0
  184. illusion/tools/task_output_tool.py +55 -0
  185. illusion/tools/task_stop_tool.py +52 -0
  186. illusion/tools/task_update_tool.py +224 -0
  187. illusion/tools/team_create_tool.py +236 -0
  188. illusion/tools/team_delete_tool.py +104 -0
  189. illusion/tools/todo_write_tool.py +198 -0
  190. illusion/tools/tool_search_tool.py +156 -0
  191. illusion/tools/web_fetch_tool.py +264 -0
  192. illusion/tools/web_search_tool.py +186 -0
  193. illusion/ui/__init__.py +23 -0
  194. illusion/ui/app.py +258 -0
  195. illusion/ui/backend_host.py +1180 -0
  196. illusion/ui/input.py +86 -0
  197. illusion/ui/output.py +363 -0
  198. illusion/ui/permission_dialog.py +47 -0
  199. illusion/ui/permission_store.py +99 -0
  200. illusion/ui/protocol.py +384 -0
  201. illusion/ui/react_launcher.py +280 -0
  202. illusion/ui/runtime.py +787 -0
  203. illusion/ui/textual_app.py +603 -0
  204. illusion/ui/web/__init__.py +10 -0
  205. illusion/ui/web/server.py +87 -0
  206. illusion/ui/web/ws_host.py +1197 -0
  207. illusion/utils/__init__.py +0 -0
  208. illusion/utils/ripgrep.py +299 -0
  209. illusion/utils/shell.py +248 -0
  210. illusion_code-0.1.0.dist-info/METADATA +1159 -0
  211. illusion_code-0.1.0.dist-info/RECORD +214 -0
  212. illusion_code-0.1.0.dist-info/WHEEL +4 -0
  213. illusion_code-0.1.0.dist-info/entry_points.txt +2 -0
  214. illusion_code-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,564 @@
1
+ """
2
+ Settings 模型和加载逻辑模块
3
+ ===========================
4
+
5
+ 本模块提供 IllusionCode 的设置管理功能,包括:
6
+ - Settings: 主设置模型(env_N 分组格式)
7
+ - EnvConfig: 环境/提供商组配置
8
+ - 各种设置加载和保存函数
9
+
10
+ 设置解析优先级(从高到低):
11
+ 1. CLI 参数
12
+ 2. 环境变量(ANTHROPIC_API_KEY, ANTHROPIC_MODEL 等)
13
+ 3. 配置文件(~/.illusion/settings.json)
14
+ 4. 默认值
15
+
16
+ 类说明:
17
+ - Settings: 主设置模型,使用 env_N 分组管理多个环境配置
18
+ - EnvConfig: 单个环境的配置(api_format, base_url, api_key, model_N 等)
19
+ - PermissionSettings: 权限模式配置
20
+ - MemorySettings: 记忆系统配置
21
+ - SandboxSettings: 沙箱运行时配置
22
+
23
+ 使用示例:
24
+ >>> from illusion.config.settings import load_settings, Settings
25
+ >>> settings = load_settings()
26
+ >>> print(f"当前模型: {settings.active_model_name}")
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import json # 导入 json 模块用于配置文件读写
32
+ import os # 导入 os 模块用于环境变量访问
33
+ from dataclasses import dataclass # 导入 dataclass 用于创建不可变数据结构
34
+ from pathlib import Path # 导入 Path 用于路径处理
35
+ from typing import Any # 导入 Any 类型用于泛型
36
+
37
+ from pydantic import BaseModel, Field # 导入 pydantic 模型基类
38
+
39
+ from illusion.hooks.schemas import HookDefinition # 导入钩子定义
40
+ from illusion.mcp.types import McpServerConfig # 导入 MCP 服务器配置
41
+ from illusion.permissions.modes import PermissionMode # 导入权限模式
42
+
43
+
44
+ class PathRuleConfig(BaseModel):
45
+ """路径权限规则配置
46
+
47
+ 使用 glob 模式定义路径级别的权限规则。
48
+
49
+ Attributes:
50
+ pattern: glob 模式字符串
51
+ allow: 是否允许访问,默认为 True
52
+ """
53
+
54
+ pattern: str # glob 模式,用于匹配路径
55
+ allow: bool = True # 默认为允许访问
56
+
57
+
58
+ class PermissionSettings(BaseModel):
59
+ """权限模式配置
60
+
61
+ 配置系统的权限控制和行为限制。
62
+
63
+ Attributes:
64
+ mode: 权限模式
65
+ allowed_tools: 允许的工具列表
66
+ denied_tools: 拒绝的工具列表
67
+ path_rules: 路径规则列表
68
+ denied_commands: 拒绝的命令列表
69
+ """
70
+
71
+ mode: PermissionMode = PermissionMode.DEFAULT # 权限模式,默认为默认模式
72
+ allowed_tools: list[str] = Field(default_factory=list) # 允许的工具列表
73
+ denied_tools: list[str] = Field(default_factory=list) # 拒绝的工具列表
74
+ path_rules: list[PathRuleConfig] = Field(default_factory=list) # 路径权限规则
75
+ denied_commands: list[str] = Field(default_factory=list) # 拒绝的命令列表
76
+
77
+
78
+ class MemorySettings(BaseModel):
79
+ """记忆系统配置
80
+
81
+ 配置 AI 记忆系统的行为和限制。
82
+
83
+ Attributes:
84
+ enabled: 是否启用记忆功能
85
+ max_files: 最大记忆文件数
86
+ max_entrypoint_lines: 最大入口文件行数
87
+ """
88
+
89
+ enabled: bool = True # 默认启用记忆功能
90
+ max_files: int = 5 # 默认最多记忆 5 个文件
91
+ max_entrypoint_lines: int = 200 # 默认入口文件最多 200 行
92
+
93
+
94
+ class SandboxNetworkSettings(BaseModel):
95
+ """沙箱网络限制配置
96
+
97
+ 传递给沙箱运行时的操作系统级网络限制配置。
98
+
99
+ Attributes:
100
+ allowed_domains: 允许访问的域名列表
101
+ denied_domains: 拒绝访问的域名列表
102
+ """
103
+
104
+ allowed_domains: list[str] = Field(default_factory=list) # 允许的域名
105
+ denied_domains: list[str] = Field(default_factory=list) # 拒绝的域名
106
+
107
+
108
+ class SandboxFilesystemSettings(BaseModel):
109
+ """沙箱文件系统限制配置
110
+
111
+ 传递给沙箱运行时的操作系统级文件系统限制配置。
112
+
113
+ Attributes:
114
+ allow_read: 允许读取的路径列表
115
+ deny_read: 拒绝读取的路径列表
116
+ allow_write: 允许写入的路径列表
117
+ deny_write: 拒绝写入的路径列表
118
+ """
119
+
120
+ allow_read: list[str] = Field(default_factory=list) # 允许读取的路径
121
+ deny_read: list[str] = Field(default_factory=list) # 拒绝读取的路径
122
+ allow_write: list[str] = Field(default_factory=lambda: ["."]) # 默认允许写入当前目录
123
+ deny_write: list[str] = Field(default_factory=list) # 拒绝写入的路径
124
+
125
+
126
+ class SandboxSettings(BaseModel):
127
+ """沙箱运行时集成配置
128
+
129
+ 配置与沙箱运行时的集成选项。
130
+
131
+ Attributes:
132
+ enabled: 是否启用沙箱
133
+ fail_if_unavailable: 沙箱不可用时是否失败
134
+ enabled_platforms: 启用的平台列表
135
+ network: 网络限制配置
136
+ filesystem: 文件系统限制配置
137
+ """
138
+
139
+ enabled: bool = False # 默认不启用沙箱
140
+ fail_if_unavailable: bool = False # 沙箱不可用时不失败
141
+ enabled_platforms: list[str] = Field(default_factory=list) # 启用的平台
142
+ network: SandboxNetworkSettings = Field(default_factory=SandboxNetworkSettings) # 网络配置
143
+ filesystem: SandboxFilesystemSettings = Field(default_factory=SandboxFilesystemSettings) # 文件系统配置
144
+
145
+
146
+ @dataclass(frozen=True)
147
+ class ResolvedAuth:
148
+ """规范化的认证材料
149
+
150
+ 用于构造 API 客户端的标准化认证信息。
151
+
152
+ Attributes:
153
+ provider: 提供商名称
154
+ auth_kind: 认证类型
155
+ value: 认证值
156
+ source: 认证来源
157
+ state: 状态(默认为 "configured")
158
+ """
159
+
160
+ provider: str # 提供商
161
+ auth_kind: str # 认证类型(api_key、oauth 等)
162
+ value: str # 认证值
163
+ source: str # 来源描述
164
+ state: str = "configured" # 配置状态
165
+
166
+
167
+ class EnvConfig(BaseModel):
168
+ """环境/提供商组配置"""
169
+ api_format: str # "anthropic" / "openai"
170
+ base_url: str | None = None
171
+ api_key: str = ""
172
+ system_prompt: str | None = None
173
+
174
+ model_config = {"extra": "allow"} # 允许 model_N 动态字段
175
+
176
+ def get_model(self, model_key: str) -> str | None:
177
+ """获取指定的模型名称,如 model_1, model_2"""
178
+ return getattr(self, model_key, None)
179
+
180
+ def list_models(self) -> dict[str, str]:
181
+ """列出所有 model_N 字段"""
182
+ result = {}
183
+ extras = self.model_extra or {}
184
+ for key, value in extras.items():
185
+ if key.startswith("model_") and isinstance(value, str):
186
+ result[key] = value
187
+ return result
188
+
189
+
190
+ class Settings(BaseModel):
191
+ """IllusionCode 主设置模型(env_N 分组格式)"""
192
+
193
+ model_config = {"extra": "allow"} # 允许 env_N 动态字段
194
+
195
+ # 活跃模型引用(格式:env_N.model_N)
196
+ model: str = "env_1.model_1"
197
+
198
+ # 全局配置
199
+ context_window: int = 200_000
200
+ system_prompt: str | None = None
201
+
202
+ # 保留的非模型字段
203
+ max_tokens: int = 16384
204
+ max_turns: int = 200
205
+ permission: PermissionSettings = Field(default_factory=PermissionSettings)
206
+ hooks: dict[str, list[HookDefinition]] = Field(default_factory=dict)
207
+ memory: MemorySettings = Field(default_factory=MemorySettings)
208
+ sandbox: SandboxSettings = Field(default_factory=SandboxSettings)
209
+ enabled_plugins: dict[str, bool] = Field(default_factory=dict)
210
+ mcp_servers: dict[str, McpServerConfig] = Field(default_factory=dict)
211
+ ui_language: str = "zh-CN"
212
+ output_style: str = "default"
213
+ show_thinking: bool = True
214
+ fast_mode: bool = False
215
+ effort: str = "medium"
216
+ passes: int = 1
217
+ verbose: bool = False
218
+
219
+ # --- env_N 配置辅助方法 ---
220
+
221
+ def get_env(self, env_key: str) -> EnvConfig | None:
222
+ """获取指定的环境配置"""
223
+ value = getattr(self, env_key, None)
224
+ if isinstance(value, dict):
225
+ return EnvConfig.model_validate(value)
226
+ return value if isinstance(value, EnvConfig) else None
227
+
228
+ def list_envs(self) -> dict[str, EnvConfig]:
229
+ """列出所有 env_N 配置"""
230
+ result = {}
231
+ extras = self.model_extra or {}
232
+ for key, value in extras.items():
233
+ if key.startswith("env_"):
234
+ if isinstance(value, EnvConfig):
235
+ result[key] = value
236
+ elif isinstance(value, dict):
237
+ result[key] = EnvConfig.model_validate(value)
238
+ return result
239
+
240
+ @property
241
+ def _active_env_key(self) -> str:
242
+ """解析 model 字段,返回 env key"""
243
+ if "." in self.model:
244
+ return self.model.split(".", 1)[0]
245
+ return "env_1"
246
+
247
+ @property
248
+ def _active_model_key(self) -> str:
249
+ """解析 model 字段,返回 model key"""
250
+ if "." in self.model:
251
+ return self.model.split(".", 1)[1]
252
+ return "model_1"
253
+
254
+ @property
255
+ def _active_env(self) -> EnvConfig:
256
+ """返回当前活跃的环境配置"""
257
+ env = self.get_env(self._active_env_key)
258
+ if env is None:
259
+ envs = self.list_envs()
260
+ if envs:
261
+ return next(iter(envs.values()))
262
+ return EnvConfig(api_format="anthropic")
263
+ return env
264
+
265
+ @property
266
+ def _active_model_name(self) -> str:
267
+ """返回当前活跃的模型名称"""
268
+ env = self._active_env
269
+ model_name = env.get_model(self._active_model_key)
270
+ if model_name is None:
271
+ models = env.list_models()
272
+ if models:
273
+ return next(iter(models.values()))
274
+ return "claude-sonnet-4-6"
275
+ return model_name
276
+
277
+ # --- 兼容性属性 ---
278
+
279
+ @property
280
+ def active_model_name(self) -> str:
281
+ """兼容性属性:当前活跃模型名称"""
282
+ return self._active_model_name
283
+
284
+ @property
285
+ def api_key(self) -> str:
286
+ """兼容性属性:当前活跃环境的 API 密钥"""
287
+ return self._active_env.api_key
288
+
289
+ @property
290
+ def base_url(self) -> str | None:
291
+ """兼容性属性:当前活跃环境的 base URL"""
292
+ return self._active_env.base_url
293
+
294
+ @property
295
+ def provider(self) -> str:
296
+ """兼容性属性:根据 api_format 推断提供商"""
297
+ fmt = self._active_env.api_format
298
+ if fmt == "anthropic":
299
+ return "anthropic"
300
+ return "openai"
301
+
302
+ @property
303
+ def api_format(self) -> str:
304
+ """兼容性属性:当前活跃环境的 API 格式"""
305
+ return self._active_env.api_format
306
+
307
+ def resolve_api_key(self) -> str:
308
+ """解析 API 密钥
309
+
310
+ 优先级:EnvConfig.api_key > 环境变量 > credentials.json(env_N) > 旧格式 credentials.json(provider) > 空
311
+
312
+ Returns:
313
+ str: API 密钥字符串
314
+
315
+ Raises:
316
+ ValueError: 未找到密钥时抛出
317
+ """
318
+ env = self._active_env
319
+
320
+ # 检查 EnvConfig 中的 api_key
321
+ if env.api_key:
322
+ return env.api_key
323
+
324
+ # 检查环境变量
325
+ if env.api_format == "openai":
326
+ env_var_key = os.environ.get("OPENAI_API_KEY", "")
327
+ if env_var_key:
328
+ return env_var_key
329
+ env_var_key = os.environ.get("ANTHROPIC_API_KEY", "")
330
+ if env_var_key:
331
+ return env_var_key
332
+
333
+ # 从 credentials.json 的 env_N 读取
334
+ from illusion.auth.storage import load_env_credential
335
+ env_cred = load_env_credential(self._active_env_key, "api_key")
336
+ if env_cred:
337
+ return env_cred
338
+
339
+ # 兼容旧格式:按 provider 名读取
340
+ from illusion.auth.storage import load_credential
341
+ provider = self.provider
342
+ old_cred = load_credential(provider, "api_key")
343
+ if old_cred:
344
+ return old_cred
345
+
346
+ from illusion.config.i18n import t as _t
347
+ raise ValueError(_t("no_api_key"))
348
+
349
+ def resolve_auth(self) -> ResolvedAuth:
350
+ """解析当前活跃环境的认证信息
351
+
352
+ Returns:
353
+ ResolvedAuth: 解析后的认证对象
354
+
355
+ Raises:
356
+ ValueError: 认证配置错误时抛出
357
+ """
358
+ env = self._active_env
359
+ provider = self.provider
360
+ api_format = env.api_format
361
+
362
+ # 检查 EnvConfig 中的 api_key
363
+ if env.api_key:
364
+ return ResolvedAuth(
365
+ provider=provider,
366
+ auth_kind="api_key",
367
+ value=env.api_key,
368
+ source="env_config",
369
+ state="configured",
370
+ )
371
+
372
+ # 检查环境变量
373
+ if api_format == "openai":
374
+ openai_val = os.environ.get("OPENAI_API_KEY", "")
375
+ if openai_val:
376
+ return ResolvedAuth(
377
+ provider=provider,
378
+ auth_kind="api_key",
379
+ value=openai_val,
380
+ source="env:OPENAI_API_KEY",
381
+ state="configured",
382
+ )
383
+ anthropic_val = os.environ.get("ANTHROPIC_API_KEY", "")
384
+ if anthropic_val:
385
+ return ResolvedAuth(
386
+ provider=provider,
387
+ auth_kind="api_key",
388
+ value=anthropic_val,
389
+ source="env:ANTHROPIC_API_KEY",
390
+ state="configured",
391
+ )
392
+
393
+ # 从 credentials.json 的 env_N 读取
394
+ from illusion.auth.storage import load_env_credential
395
+ env_cred = load_env_credential(self._active_env_key, "api_key")
396
+ if env_cred:
397
+ return ResolvedAuth(
398
+ provider=provider,
399
+ auth_kind="api_key",
400
+ value=env_cred,
401
+ source=f"file:{self._active_env_key}",
402
+ state="configured",
403
+ )
404
+
405
+ # 兼容旧格式:按 provider 名读取
406
+ from illusion.auth.storage import load_credential
407
+ old_cred = load_credential(provider, "api_key")
408
+ if old_cred:
409
+ return ResolvedAuth(
410
+ provider=provider,
411
+ auth_kind="api_key",
412
+ value=old_cred,
413
+ source=f"file:{provider}",
414
+ state="configured",
415
+ )
416
+
417
+ from illusion.config.i18n import t as _t
418
+ raise ValueError(_t("no_auth"))
419
+
420
+ def merge_cli_overrides(self, **overrides: Any) -> Settings:
421
+ """返回应用了 CLI 覆盖的新 Settings(仅非 None 值)
422
+
423
+ Args:
424
+ **overrides: 要覆盖的字段
425
+
426
+ Returns:
427
+ Settings: 应用覆盖后的新实例
428
+ """
429
+ updates = {k: v for k, v in overrides.items() if v is not None}
430
+ if not updates:
431
+ return self
432
+ return self.model_copy(update=updates)
433
+
434
+
435
+ def _apply_env_overrides(settings: Settings) -> Settings:
436
+ """在加载的设置上应用环境变量覆盖到活跃的 EnvConfig
437
+
438
+ 直接修改活跃 env 的 api_key、model、base_url 等字段,
439
+ 而不是设置 Settings 的属性(属性会 shadow extras)。
440
+
441
+ Args:
442
+ settings: 原始设置
443
+
444
+ Returns:
445
+ Settings: 应用环境变量覆盖后的设置
446
+ """
447
+ env = settings._active_env
448
+ env_modified = False
449
+
450
+ model = os.environ.get("ANTHROPIC_MODEL")
451
+ if model:
452
+ env.model = model
453
+ env_modified = True
454
+
455
+ api_key = os.environ.get("ANTHROPIC_API_KEY")
456
+ if api_key:
457
+ env.api_key = api_key
458
+ env_modified = True
459
+
460
+ base_url = os.environ.get("ANTHROPIC_BASE_URL")
461
+ if base_url:
462
+ env.base_url = base_url
463
+ env_modified = True
464
+
465
+ # 非模型相关的全局字段覆盖仍使用 model_copy
466
+ updates: dict[str, Any] = {}
467
+
468
+ max_tokens = os.environ.get("ILLUSION_MAX_TOKENS") or os.environ.get("illusion_MAX_TOKENS")
469
+ if max_tokens:
470
+ updates["max_tokens"] = int(max_tokens)
471
+
472
+ max_turns = os.environ.get("ILLUSION_MAX_TURNS") or os.environ.get("illusion_MAX_TURNS")
473
+ if max_turns:
474
+ updates["max_turns"] = int(max_turns)
475
+
476
+ effort = os.environ.get("ILLUSION_EFFORT") or os.environ.get("illusion_EFFORT")
477
+ if effort:
478
+ updates["effort"] = effort
479
+
480
+ sandbox_enabled = os.environ.get("ILLUSION_SANDBOX_ENABLED") or os.environ.get("illusion_SANDBOX_ENABLED")
481
+ sandbox_fail = os.environ.get("ILLUSION_SANDBOX_FAIL_IF_UNAVAILABLE") or os.environ.get("illusion_SANDBOX_FAIL_IF_UNAVAILABLE")
482
+ sandbox_updates: dict[str, Any] = {}
483
+ if sandbox_enabled is not None:
484
+ sandbox_updates["enabled"] = _parse_bool_env(sandbox_enabled)
485
+ if sandbox_fail is not None:
486
+ sandbox_updates["fail_if_unavailable"] = _parse_bool_env(sandbox_fail)
487
+ if sandbox_updates:
488
+ updates["sandbox"] = settings.sandbox.model_copy(update=sandbox_updates)
489
+
490
+ # 将修改后的 env 写回 settings 的 extras
491
+ if env_modified:
492
+ env_key = settings._active_env_key
493
+ updates[env_key] = env
494
+
495
+ if not updates:
496
+ return settings
497
+ return settings.model_copy(update=updates)
498
+
499
+
500
+ def _parse_bool_env(value: str) -> bool:
501
+ """解析布尔环境变量
502
+
503
+ Args:
504
+ value: 环境变量值字符串
505
+
506
+ Returns:
507
+ bool: 解析后的布尔值
508
+ """
509
+ return value.strip().lower() in {"1", "true", "yes", "on"}
510
+
511
+
512
+ def load_settings(config_path: Path | None = None) -> Settings:
513
+ """从配置文件加载设置
514
+
515
+ Args:
516
+ config_path: settings.json 的路径。如果为 None,使用默认位置。
517
+
518
+ Returns:
519
+ Settings: 应用环境变量覆盖后的 Settings 实例
520
+ """
521
+ if config_path is None:
522
+ from illusion.config.paths import get_config_file_path
523
+
524
+ config_path = get_config_file_path()
525
+
526
+ if config_path.exists():
527
+ raw = json.loads(config_path.read_text(encoding="utf-8"))
528
+ # 兼容 mcpServers(camelCase)键,映射到 mcp_servers(snake_case)
529
+ if "mcpServers" in raw and "mcp_servers" not in raw:
530
+ raw["mcp_servers"] = raw.pop("mcpServers")
531
+ settings = Settings.model_validate(raw)
532
+ return _apply_env_overrides(settings)
533
+
534
+ return _apply_env_overrides(Settings())
535
+
536
+
537
+ def save_settings(settings: Settings, config_path: Path | None = None) -> None:
538
+ """将设置持久化到配置文件
539
+
540
+ Args:
541
+ settings: 要保存的 Settings 实例
542
+ config_path: 写入路径。如果为 None,使用默认位置
543
+ """
544
+ if config_path is None:
545
+ from illusion.config.paths import get_config_file_path
546
+
547
+ config_path = get_config_file_path()
548
+
549
+ config_path.parent.mkdir(parents=True, exist_ok=True)
550
+
551
+ # 序列化并重排字段,env_N 置顶
552
+ data = settings.model_dump()
553
+ ordered: dict[str, object] = {}
554
+ for key in sorted(data):
555
+ if key.startswith("env_"):
556
+ ordered[key] = data[key]
557
+ for key, value in data.items():
558
+ if not key.startswith("env_"):
559
+ ordered[key] = value
560
+
561
+ config_path.write_text(
562
+ json.dumps(ordered, indent=2, ensure_ascii=False) + "\n",
563
+ encoding="utf-8",
564
+ )
@@ -0,0 +1,41 @@
1
+ """
2
+ 协调器模块
3
+ ==========
4
+
5
+ 本模块提供代理定义和任务通知功能。
6
+
7
+ 主要组件:
8
+ - AgentDefinition: 代理定义
9
+ - TaskNotification: 任务通知
10
+ - get_builtin_agent_definitions: 获取内置代理定义
11
+ - get_agent_definition: 获取指定代理定义
12
+ - get_all_agent_definitions: 获取所有代理定义
13
+ - format_task_notification: 序列化任务通知
14
+ - parse_task_notification: 解析任务通知
15
+
16
+ 使用示例:
17
+ >>> from illusion.coordinator import AgentDefinition, get_agent_definition
18
+ >>> from illusion.coordinator import TaskNotification, format_task_notification
19
+ """
20
+
21
+ from illusion.coordinator.agent_definitions import (
22
+ AgentDefinition,
23
+ get_agent_definition,
24
+ get_all_agent_definitions,
25
+ get_builtin_agent_definitions,
26
+ )
27
+ from illusion.coordinator.coordinator_mode import (
28
+ TaskNotification,
29
+ format_task_notification,
30
+ parse_task_notification,
31
+ )
32
+
33
+ __all__ = [
34
+ "AgentDefinition",
35
+ "TaskNotification",
36
+ "format_task_notification",
37
+ "get_agent_definition",
38
+ "get_all_agent_definitions",
39
+ "get_builtin_agent_definitions",
40
+ "parse_task_notification",
41
+ ]