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.
- illusion/__init__.py +24 -0
- illusion/__main__.py +15 -0
- illusion/_frontend/dist/index.mjs +39208 -0
- illusion/_frontend/package.json +27 -0
- illusion/_frontend/src/App.tsx +624 -0
- illusion/_frontend/src/components/CommandPicker.tsx +98 -0
- illusion/_frontend/src/components/Composer.tsx +55 -0
- illusion/_frontend/src/components/ComposerController.tsx +128 -0
- illusion/_frontend/src/components/ConversationView.tsx +750 -0
- illusion/_frontend/src/components/Footer.tsx +25 -0
- illusion/_frontend/src/components/MarkdownContent.tsx +537 -0
- illusion/_frontend/src/components/MarkdownTable.tsx +245 -0
- illusion/_frontend/src/components/ModalHost.tsx +425 -0
- illusion/_frontend/src/components/MultilineTextInput.tsx +250 -0
- illusion/_frontend/src/components/PromptInput.tsx +64 -0
- illusion/_frontend/src/components/SelectModal.tsx +78 -0
- illusion/_frontend/src/components/SidePanel.tsx +175 -0
- illusion/_frontend/src/components/Spinner.tsx +77 -0
- illusion/_frontend/src/components/StatusBar.tsx +142 -0
- illusion/_frontend/src/components/SwarmPanel.tsx +141 -0
- illusion/_frontend/src/components/TodoPanel.tsx +126 -0
- illusion/_frontend/src/components/ToolCallDisplay.tsx +202 -0
- illusion/_frontend/src/components/TranscriptPane.tsx +79 -0
- illusion/_frontend/src/components/WelcomeBanner.tsx +37 -0
- illusion/_frontend/src/hooks/useBackendSession.ts +468 -0
- illusion/_frontend/src/hooks/useTerminalSize.ts +9 -0
- illusion/_frontend/src/i18n.ts +78 -0
- illusion/_frontend/src/index.tsx +42 -0
- illusion/_frontend/src/theme/ThemeContext.tsx +19 -0
- illusion/_frontend/src/theme/builtinThemes.ts +89 -0
- illusion/_frontend/src/types.ts +110 -0
- illusion/_frontend/src/utils/markdown.ts +33 -0
- illusion/_frontend/src/utils/thinking.ts +191 -0
- illusion/_frontend/tsconfig.json +13 -0
- illusion/_web_dist/assets/index-BseIw-ik.css +10 -0
- illusion/_web_dist/assets/index-C_0ZWMuW.js +82 -0
- illusion/_web_dist/index.html +16 -0
- illusion/api/__init__.py +36 -0
- illusion/api/client.py +568 -0
- illusion/api/codex_client.py +563 -0
- illusion/api/compat.py +138 -0
- illusion/api/effort.py +128 -0
- illusion/api/errors.py +57 -0
- illusion/api/openai_client.py +819 -0
- illusion/api/provider.py +148 -0
- illusion/api/registry.py +479 -0
- illusion/api/usage.py +45 -0
- illusion/auth/__init__.py +50 -0
- illusion/auth/copilot.py +419 -0
- illusion/auth/external.py +612 -0
- illusion/auth/flows.py +58 -0
- illusion/auth/manager.py +214 -0
- illusion/auth/storage.py +372 -0
- illusion/bridge/__init__.py +38 -0
- illusion/bridge/manager.py +190 -0
- illusion/bridge/session_runner.py +84 -0
- illusion/bridge/types.py +113 -0
- illusion/bridge/work_secret.py +131 -0
- illusion/cli.py +1228 -0
- illusion/commands/__init__.py +32 -0
- illusion/commands/registry.py +1934 -0
- illusion/config/__init__.py +39 -0
- illusion/config/i18n.py +522 -0
- illusion/config/paths.py +259 -0
- illusion/config/settings.py +564 -0
- illusion/coordinator/__init__.py +41 -0
- illusion/coordinator/agent_definitions.py +1093 -0
- illusion/coordinator/coordinator_mode.py +127 -0
- illusion/engine/__init__.py +95 -0
- illusion/engine/cost_tracker.py +55 -0
- illusion/engine/messages.py +369 -0
- illusion/engine/query.py +632 -0
- illusion/engine/query_engine.py +343 -0
- illusion/engine/stream_events.py +169 -0
- illusion/hooks/__init__.py +67 -0
- illusion/hooks/events.py +43 -0
- illusion/hooks/executor.py +397 -0
- illusion/hooks/hot_reload.py +74 -0
- illusion/hooks/loader.py +133 -0
- illusion/hooks/schemas.py +121 -0
- illusion/hooks/types.py +86 -0
- illusion/mcp/__init__.py +104 -0
- illusion/mcp/client.py +377 -0
- illusion/mcp/config.py +140 -0
- illusion/mcp/types.py +175 -0
- illusion/memory/__init__.py +36 -0
- illusion/memory/manager.py +94 -0
- illusion/memory/memdir.py +58 -0
- illusion/memory/paths.py +57 -0
- illusion/memory/scan.py +120 -0
- illusion/memory/search.py +83 -0
- illusion/memory/types.py +43 -0
- illusion/output_styles/__init__.py +15 -0
- illusion/output_styles/loader.py +64 -0
- illusion/permissions/__init__.py +39 -0
- illusion/permissions/checker.py +174 -0
- illusion/permissions/modes.py +38 -0
- illusion/platforms.py +148 -0
- illusion/plugins/__init__.py +71 -0
- illusion/plugins/bundled/__init__.py +0 -0
- illusion/plugins/installer.py +59 -0
- illusion/plugins/loader.py +301 -0
- illusion/plugins/schemas.py +51 -0
- illusion/plugins/types.py +56 -0
- illusion/prompts/__init__.py +29 -0
- illusion/prompts/claudemd.py +74 -0
- illusion/prompts/context.py +187 -0
- illusion/prompts/environment.py +189 -0
- illusion/prompts/system_prompt.py +155 -0
- illusion/py.typed +0 -0
- illusion/sandbox/__init__.py +29 -0
- illusion/sandbox/adapter.py +174 -0
- illusion/services/__init__.py +59 -0
- illusion/services/compact/__init__.py +1015 -0
- illusion/services/cron.py +338 -0
- illusion/services/cron_scheduler.py +715 -0
- illusion/services/file_history.py +258 -0
- illusion/services/lsp/__init__.py +455 -0
- illusion/services/session_storage.py +237 -0
- illusion/services/token_estimation.py +72 -0
- illusion/skills/__init__.py +60 -0
- illusion/skills/bundled/__init__.py +110 -0
- illusion/skills/bundled/content/batch.md +86 -0
- illusion/skills/bundled/content/coding-guidelines.md +70 -0
- illusion/skills/bundled/content/debug.md +38 -0
- illusion/skills/bundled/content/loop.md +82 -0
- illusion/skills/bundled/content/remember.md +105 -0
- illusion/skills/bundled/content/simplify.md +53 -0
- illusion/skills/bundled/content/skillify.md +113 -0
- illusion/skills/bundled/content/stuck.md +54 -0
- illusion/skills/bundled/content/update-config.md +329 -0
- illusion/skills/bundled/content/verify.md +74 -0
- illusion/skills/loader.py +219 -0
- illusion/skills/registry.py +40 -0
- illusion/skills/types.py +24 -0
- illusion/state/__init__.py +18 -0
- illusion/state/app_state.py +67 -0
- illusion/state/store.py +93 -0
- illusion/swarm/__init__.py +71 -0
- illusion/swarm/agent_executor.py +857 -0
- illusion/swarm/in_process.py +259 -0
- illusion/swarm/subprocess_backend.py +136 -0
- illusion/swarm/team_helpers.py +123 -0
- illusion/swarm/types.py +159 -0
- illusion/swarm/worktree.py +347 -0
- illusion/tasks/__init__.py +33 -0
- illusion/tasks/local_agent_task.py +42 -0
- illusion/tasks/local_shell_task.py +27 -0
- illusion/tasks/manager.py +377 -0
- illusion/tasks/stop_task.py +21 -0
- illusion/tasks/types.py +88 -0
- illusion/tools/__init__.py +126 -0
- illusion/tools/agent_tool.py +388 -0
- illusion/tools/ask_user_question_tool.py +186 -0
- illusion/tools/base.py +149 -0
- illusion/tools/bash_tool.py +413 -0
- illusion/tools/config_tool.py +90 -0
- illusion/tools/cron_tool.py +473 -0
- illusion/tools/enter_plan_mode_tool.py +147 -0
- illusion/tools/enter_worktree_tool.py +188 -0
- illusion/tools/exit_plan_mode_tool.py +69 -0
- illusion/tools/exit_worktree_tool.py +225 -0
- illusion/tools/file_edit_tool.py +283 -0
- illusion/tools/file_read_tool.py +294 -0
- illusion/tools/file_write_tool.py +184 -0
- illusion/tools/glob_tool.py +165 -0
- illusion/tools/grep_tool.py +190 -0
- illusion/tools/list_mcp_resources_tool.py +80 -0
- illusion/tools/lsp_tool.py +333 -0
- illusion/tools/mcp_auth_tool.py +100 -0
- illusion/tools/mcp_tool.py +75 -0
- illusion/tools/notebook_edit_tool.py +242 -0
- illusion/tools/powershell_tool.py +334 -0
- illusion/tools/read_mcp_resource_tool.py +63 -0
- illusion/tools/repl_tool.py +100 -0
- illusion/tools/send_message_tool.py +112 -0
- illusion/tools/shell_common.py +187 -0
- illusion/tools/skill_tool.py +86 -0
- illusion/tools/sleep_tool.py +62 -0
- illusion/tools/structured_output_tool.py +58 -0
- illusion/tools/task_create_tool.py +98 -0
- illusion/tools/task_get_tool.py +94 -0
- illusion/tools/task_list_tool.py +94 -0
- illusion/tools/task_output_tool.py +55 -0
- illusion/tools/task_stop_tool.py +52 -0
- illusion/tools/task_update_tool.py +224 -0
- illusion/tools/team_create_tool.py +236 -0
- illusion/tools/team_delete_tool.py +104 -0
- illusion/tools/todo_write_tool.py +198 -0
- illusion/tools/tool_search_tool.py +156 -0
- illusion/tools/web_fetch_tool.py +264 -0
- illusion/tools/web_search_tool.py +186 -0
- illusion/ui/__init__.py +23 -0
- illusion/ui/app.py +258 -0
- illusion/ui/backend_host.py +1180 -0
- illusion/ui/input.py +86 -0
- illusion/ui/output.py +363 -0
- illusion/ui/permission_dialog.py +47 -0
- illusion/ui/permission_store.py +99 -0
- illusion/ui/protocol.py +384 -0
- illusion/ui/react_launcher.py +280 -0
- illusion/ui/runtime.py +787 -0
- illusion/ui/textual_app.py +603 -0
- illusion/ui/web/__init__.py +10 -0
- illusion/ui/web/server.py +87 -0
- illusion/ui/web/ws_host.py +1197 -0
- illusion/utils/__init__.py +0 -0
- illusion/utils/ripgrep.py +299 -0
- illusion/utils/shell.py +248 -0
- illusion_code-0.1.0.dist-info/METADATA +1159 -0
- illusion_code-0.1.0.dist-info/RECORD +214 -0
- illusion_code-0.1.0.dist-info/WHEEL +4 -0
- illusion_code-0.1.0.dist-info/entry_points.txt +2 -0
- illusion_code-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""
|
|
2
|
+
桥接会话管理器模块
|
|
3
|
+
=================
|
|
4
|
+
|
|
5
|
+
本模块提供桥接会话的跟踪和管理功能。
|
|
6
|
+
|
|
7
|
+
主要功能:
|
|
8
|
+
- 管理生成的子会话
|
|
9
|
+
- 捕获会话输出
|
|
10
|
+
- 列出和读取会话状态
|
|
11
|
+
|
|
12
|
+
类说明:
|
|
13
|
+
- BridgeSessionRecord: UI安全的会话快照
|
|
14
|
+
- BridgeSessionManager: 桥接会话管理器
|
|
15
|
+
|
|
16
|
+
函数说明:
|
|
17
|
+
- get_bridge_manager: 获取单例会话管理器
|
|
18
|
+
|
|
19
|
+
使用示例:
|
|
20
|
+
>>> from illusion.bridge import get_bridge_manager
|
|
21
|
+
>>> manager = get_bridge_manager()
|
|
22
|
+
>>> sessions = manager.list_sessions()
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import asyncio
|
|
28
|
+
from dataclasses import dataclass
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
|
|
31
|
+
from illusion.config.paths import get_data_dir
|
|
32
|
+
from illusion.bridge.session_runner import SessionHandle, spawn_session
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class BridgeSessionRecord:
|
|
37
|
+
"""UI安全的桥接会话快照
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
session_id: 会话唯一标识符
|
|
41
|
+
command: 执行的命令
|
|
42
|
+
cwd: 工作目录
|
|
43
|
+
pid: 进程ID
|
|
44
|
+
status: 会话状态 (running/completed/failed)
|
|
45
|
+
started_at: 启动时间戳
|
|
46
|
+
output_path: 输出文件路径
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
session_id: str # 会话ID
|
|
50
|
+
command: str # 命令
|
|
51
|
+
cwd: str # 工作目录
|
|
52
|
+
pid: int # 进程ID
|
|
53
|
+
status: str # 状态
|
|
54
|
+
started_at: float # 启动时间
|
|
55
|
+
output_path: str # 输出路径
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class BridgeSessionManager:
|
|
59
|
+
"""管理桥接运行的子会话并捕获其输出
|
|
60
|
+
|
|
61
|
+
Attributes:
|
|
62
|
+
_sessions: 会话ID到句柄的映射
|
|
63
|
+
_commands: 会话ID到命令的映射
|
|
64
|
+
_output_paths: 会话ID到输出文件路径的映射
|
|
65
|
+
_copy_tasks: 会话ID到异步复制任务的映射
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
>>> manager = BridgeSessionManager()
|
|
69
|
+
>>> handle = await manager.spawn(session_id="test", command="echo hello", cwd=".")
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(self) -> None:
|
|
73
|
+
self._sessions: dict[str, SessionHandle] = {} # 会话映射
|
|
74
|
+
self._commands: dict[str, str] = {} # 命令映射
|
|
75
|
+
self._output_paths: dict[str, Path] = {} # 输出路径映射
|
|
76
|
+
self._copy_tasks: dict[str, asyncio.Task[None]] = {} # 复制任务映射
|
|
77
|
+
|
|
78
|
+
async def spawn(self, *, session_id: str, command: str, cwd: str | Path) -> SessionHandle:
|
|
79
|
+
"""生成新的桥接会话
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
session_id: 会话唯一标识符
|
|
83
|
+
command: 要执行的命令
|
|
84
|
+
cwd: 工作目录
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
SessionHandle: 会话句柄
|
|
88
|
+
"""
|
|
89
|
+
handle = await spawn_session(session_id=session_id, command=command, cwd=cwd) # 生成会话
|
|
90
|
+
self._sessions[session_id] = handle # 存储句柄
|
|
91
|
+
self._commands[session_id] = command # 存储命令
|
|
92
|
+
output_dir = get_data_dir() / "bridge" # 输出目录
|
|
93
|
+
output_dir.mkdir(parents=True, exist_ok=True) # 创建目录
|
|
94
|
+
output_path = output_dir / f"{session_id}.log" # 输出文件
|
|
95
|
+
output_path.write_text("", encoding="utf-8") # 初始化文件
|
|
96
|
+
self._output_paths[session_id] = output_path # 存储路径
|
|
97
|
+
self._copy_tasks[session_id] = asyncio.create_task(self._copy_output(session_id, handle)) # 启动输出复制
|
|
98
|
+
return handle
|
|
99
|
+
|
|
100
|
+
def list_sessions(self) -> list[BridgeSessionRecord]:
|
|
101
|
+
"""列出所有会话
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
list[BridgeSessionRecord]: 按启动时间倒序排列的会话列表
|
|
105
|
+
"""
|
|
106
|
+
items: list[BridgeSessionRecord] = [] # 结果列表
|
|
107
|
+
for session_id, handle in self._sessions.items(): # 遍历会话
|
|
108
|
+
process = handle.process # 进程
|
|
109
|
+
if process.returncode is None: # 运行中
|
|
110
|
+
status = "running"
|
|
111
|
+
elif process.returncode == 0: # 正常退出
|
|
112
|
+
status = "completed"
|
|
113
|
+
else: # 异常退出
|
|
114
|
+
status = "failed"
|
|
115
|
+
items.append(
|
|
116
|
+
BridgeSessionRecord(
|
|
117
|
+
session_id=session_id, # 会话ID
|
|
118
|
+
command=self._commands.get(session_id, ""), # 命令
|
|
119
|
+
cwd=str(handle.cwd), # 工作目录
|
|
120
|
+
pid=process.pid or 0, # 进程ID
|
|
121
|
+
status=status, # 状态
|
|
122
|
+
started_at=handle.started_at, # 启动时间
|
|
123
|
+
output_path=str(self._output_paths[session_id]), # 输出路径
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
return sorted(items, key=lambda item: item.started_at, reverse=True) # 按时间倒序
|
|
127
|
+
|
|
128
|
+
def read_output(self, session_id: str, *, max_bytes: int = 12000) -> str:
|
|
129
|
+
"""读取会话输出
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
session_id: 会话ID
|
|
133
|
+
max_bytes: 最大返回字节数
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
str: 输出内容 (如果超过max_bytes,返回最后max_bytes字节)
|
|
137
|
+
"""
|
|
138
|
+
path = self._output_paths.get(session_id) # 获取输出路径
|
|
139
|
+
if path is None or not path.exists(): # 不存在
|
|
140
|
+
return ""
|
|
141
|
+
content = path.read_text(encoding="utf-8", errors="replace") # 读取内容
|
|
142
|
+
if len(content) > max_bytes: # 超过限制
|
|
143
|
+
return content[-max_bytes:] # 返回最后部分
|
|
144
|
+
return content # 返回全部
|
|
145
|
+
|
|
146
|
+
async def stop(self, session_id: str) -> None:
|
|
147
|
+
"""停止会话
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
session_id: 会话ID
|
|
151
|
+
|
|
152
|
+
Raises:
|
|
153
|
+
ValueError: 如果会话不存在
|
|
154
|
+
"""
|
|
155
|
+
handle = self._sessions.get(session_id) # 获取句柄
|
|
156
|
+
if handle is None: # 不存在
|
|
157
|
+
raise ValueError(f"Unknown bridge session: {session_id}")
|
|
158
|
+
await handle.kill() # 终止会话
|
|
159
|
+
|
|
160
|
+
async def _copy_output(self, session_id: str, handle: SessionHandle) -> None:
|
|
161
|
+
"""异步复制会话输出到文件
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
session_id: 会话ID
|
|
165
|
+
handle: 会话句柄
|
|
166
|
+
"""
|
|
167
|
+
path = self._output_paths[session_id] # 输出路径
|
|
168
|
+
if handle.process.stdout is not None: # 有输出
|
|
169
|
+
while True:
|
|
170
|
+
chunk = await handle.process.stdout.read(4096) # 读取块
|
|
171
|
+
if not chunk: # 无数据
|
|
172
|
+
break
|
|
173
|
+
with path.open("ab") as stream: # 追加模式
|
|
174
|
+
stream.write(chunk) # 写入
|
|
175
|
+
await handle.process.wait() # 等待进程结束
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
_DEFAULT_MANAGER: BridgeSessionManager | None = None # 默认管理器单例
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def get_bridge_manager() -> BridgeSessionManager:
|
|
182
|
+
"""获取单例桥接会话管理器
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
BridgeSessionManager: 全局会话管理器实例
|
|
186
|
+
"""
|
|
187
|
+
global _DEFAULT_MANAGER # 声明全局变量
|
|
188
|
+
if _DEFAULT_MANAGER is None: # 未初始化
|
|
189
|
+
_DEFAULT_MANAGER = BridgeSessionManager() # 创建实例
|
|
190
|
+
return _DEFAULT_MANAGER # 返回实例
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
桥接会话运行器模块
|
|
3
|
+
=================
|
|
4
|
+
|
|
5
|
+
本模块提供桥接会话的生成和管理功能。
|
|
6
|
+
|
|
7
|
+
主要功能:
|
|
8
|
+
- 生成子会话进程
|
|
9
|
+
- 管理会话生命周期
|
|
10
|
+
|
|
11
|
+
类说明:
|
|
12
|
+
- SessionHandle: 生成的桥接会话句柄
|
|
13
|
+
|
|
14
|
+
函数说明:
|
|
15
|
+
- spawn_session: 生成新的桥接会话
|
|
16
|
+
|
|
17
|
+
使用示例:
|
|
18
|
+
>>> from illusion.bridge import spawn_session, SessionHandle
|
|
19
|
+
>>> handle = await spawn_session(session_id="test", command="echo hello", cwd=".")
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import asyncio
|
|
25
|
+
import time
|
|
26
|
+
from dataclasses import dataclass, field
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
from illusion.utils.shell import create_shell_subprocess
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class SessionHandle:
|
|
34
|
+
"""生成的桥接会话句柄
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
session_id: 会话唯一标识符
|
|
38
|
+
process: 异步子进程对象
|
|
39
|
+
cwd: 工作目录路径
|
|
40
|
+
started_at: 启动时间戳
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
session_id: str # 会话ID
|
|
44
|
+
process: asyncio.subprocess.Process # 异步进程
|
|
45
|
+
cwd: Path # 工作目录
|
|
46
|
+
started_at: float = field(default_factory=time.time) # 启动时间
|
|
47
|
+
|
|
48
|
+
async def kill(self) -> None:
|
|
49
|
+
"""终止会话进程
|
|
50
|
+
|
|
51
|
+
先尝试优雅终止 (terminate),超时后强制终止 (kill)
|
|
52
|
+
"""
|
|
53
|
+
self.process.terminate() # 发送终止信号
|
|
54
|
+
try:
|
|
55
|
+
await asyncio.wait_for(self.process.wait(), timeout=3) # 等待进程终止
|
|
56
|
+
except asyncio.TimeoutError: # 超时
|
|
57
|
+
self.process.kill() # 强制终止
|
|
58
|
+
await self.process.wait() # 等待进程
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def spawn_session(
|
|
62
|
+
*,
|
|
63
|
+
session_id: str,
|
|
64
|
+
command: str,
|
|
65
|
+
cwd: str | Path,
|
|
66
|
+
) -> SessionHandle:
|
|
67
|
+
"""生成一个桥接管理的子会话
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
session_id: 会话唯一标识符
|
|
71
|
+
command: 要执行的命令
|
|
72
|
+
cwd: 工作目录
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
SessionHandle: 会话句柄
|
|
76
|
+
"""
|
|
77
|
+
resolved_cwd = Path(cwd).resolve() # 解析为绝对路径
|
|
78
|
+
process = await create_shell_subprocess( # 创建子进程
|
|
79
|
+
command, # 命令
|
|
80
|
+
cwd=resolved_cwd, # 工作目录
|
|
81
|
+
stdout=asyncio.subprocess.PIPE, # 标准输出管道
|
|
82
|
+
stderr=asyncio.subprocess.PIPE, # 标准错误管道
|
|
83
|
+
)
|
|
84
|
+
return SessionHandle(session_id=session_id, process=process, cwd=resolved_cwd)
|
illusion/bridge/types.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
桥接配置类型模块
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
本模块定义 Illusion Bridge 所需的配置数据类型,用于在组件之间传递配置信息。
|
|
6
|
+
|
|
7
|
+
主要功能:
|
|
8
|
+
- 定义工作数据类型(WorkData)
|
|
9
|
+
- 定义工作密钥类型(WorkSecret)
|
|
10
|
+
- 定义桥接配置类型(BridgeConfig)
|
|
11
|
+
|
|
12
|
+
类说明:
|
|
13
|
+
- WorkData: 工作项元数据,包含类型和ID
|
|
14
|
+
- WorkSecret: 解码后的工作密钥,包含版本、令牌和API地址
|
|
15
|
+
- BridgeConfig: 桥接配置,包含目录、机器名、会话数等
|
|
16
|
+
|
|
17
|
+
使用示例:
|
|
18
|
+
>>> from illusion.bridge.types import WorkData, WorkSecret, BridgeConfig
|
|
19
|
+
>>>
|
|
20
|
+
>>> # 创建工作数据
|
|
21
|
+
>>> work = WorkData(type="session", id="abc123")
|
|
22
|
+
>>>
|
|
23
|
+
>>> # 创建工作密钥
|
|
24
|
+
>>> secret = WorkSecret(version=1, session_ingress_token="token", api_base_url="https://api.example.com")
|
|
25
|
+
>>>
|
|
26
|
+
>>> # 创建桥接配置
|
|
27
|
+
>>> config = BridgeConfig(dir="/tmp/bridge", machine_name="machine-1")
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from dataclasses import dataclass
|
|
33
|
+
from typing import Literal
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# 默认会话超时时间:24小时(毫秒)
|
|
37
|
+
DEFAULT_SESSION_TIMEOUT_MS = 24 * 60 * 60 * 1000
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class WorkData:
|
|
42
|
+
"""
|
|
43
|
+
工作项元数据
|
|
44
|
+
|
|
45
|
+
用于标识工作项的类型和唯一标识符。
|
|
46
|
+
|
|
47
|
+
Attributes:
|
|
48
|
+
type: 工作类型,可选值为 "session"(会话)或 "healthcheck"(健康检查)
|
|
49
|
+
id: 工作项的唯一标识符
|
|
50
|
+
|
|
51
|
+
使用示例:
|
|
52
|
+
>>> WorkData(type="session", id="abc123")
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
# 工作类型:session 或 healthcheck
|
|
56
|
+
type: Literal["session", "healthcheck"]
|
|
57
|
+
# 工作项唯一标识符
|
|
58
|
+
id: str
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True)
|
|
62
|
+
class WorkSecret:
|
|
63
|
+
"""
|
|
64
|
+
解码后的工作密钥
|
|
65
|
+
|
|
66
|
+
包含与后端服务建立连接所需的认证信息。
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
version: 密钥版本号,目前仅支持版本 1
|
|
70
|
+
session_ingress_token: 会话入口令牌,用于身份验证
|
|
71
|
+
api_base_url: API 基础地址,服务端点
|
|
72
|
+
|
|
73
|
+
使用示例:
|
|
74
|
+
>>> WorkSecret(version=1, session_ingress_token="token", api_base_url="https://api.example.com")
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
# 密钥版本号
|
|
78
|
+
version: int
|
|
79
|
+
# 会话入口令牌
|
|
80
|
+
session_ingress_token: str
|
|
81
|
+
# API 基础地址
|
|
82
|
+
api_base_url: str
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass(frozen=True)
|
|
86
|
+
class BridgeConfig:
|
|
87
|
+
"""
|
|
88
|
+
桥接配置
|
|
89
|
+
|
|
90
|
+
定义桥接服务的基本配置信息。
|
|
91
|
+
|
|
92
|
+
Attributes:
|
|
93
|
+
dir: 桥接目录路径
|
|
94
|
+
machine_name: 机器名称,用于标识当前节点
|
|
95
|
+
max_sessions: 最大会话数,默认为 1
|
|
96
|
+
verbose: 是否启用详细输出,默认为 False
|
|
97
|
+
session_timeout_ms: 会话超时时间(毫秒),默认为 24 小时
|
|
98
|
+
|
|
99
|
+
使用示例:
|
|
100
|
+
>>> config = BridgeConfig(dir="/tmp/bridge", machine_name="machine-1")
|
|
101
|
+
>>> config = BridgeConfig(dir="/tmp/bridge", machine_name="machine-1", max_sessions=5, verbose=True)
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
# 桥接目录路径
|
|
105
|
+
dir: str
|
|
106
|
+
# 机器名称
|
|
107
|
+
machine_name: str
|
|
108
|
+
# 最大会话数
|
|
109
|
+
max_sessions: int = 1
|
|
110
|
+
# 是否启用详细输出
|
|
111
|
+
verbose: bool = False
|
|
112
|
+
# 会话超时时间(毫秒)
|
|
113
|
+
session_timeout_ms: int = DEFAULT_SESSION_TIMEOUT_MS
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
工作密钥处理模块
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
本模块提供工作密钥的编码、解码和URL构建功能,用于客户端与后端服务的安全通信。
|
|
6
|
+
|
|
7
|
+
主要功能:
|
|
8
|
+
- 编码工作密钥为 Base64URL 格式
|
|
9
|
+
- 解码并验证 Base64URL 格式的工作密钥
|
|
10
|
+
- 构建会话入口 WebSocket URL
|
|
11
|
+
|
|
12
|
+
函数说明:
|
|
13
|
+
- encode_work_secret: 将 WorkSecret 对象编码为字符串
|
|
14
|
+
- decode_work_secret: 将字符串解码为 WorkSecret 对象
|
|
15
|
+
- build_sdk_url: 构建会话入口 WebSocket URL
|
|
16
|
+
|
|
17
|
+
使用示例:
|
|
18
|
+
>>> from illusion.bridge.types import WorkSecret
|
|
19
|
+
>>> from illusion.bridge.work_secret import encode_work_secret, decode_work_secret, build_sdk_url
|
|
20
|
+
>>>
|
|
21
|
+
>>> # 编码工作密钥
|
|
22
|
+
>>> secret = WorkSecret(version=1, session_ingress_token="token", api_base_url="https://api.example.com")
|
|
23
|
+
>>> encoded = encode_work_secret(secret)
|
|
24
|
+
>>>
|
|
25
|
+
>>> # 解码工作密钥
|
|
26
|
+
>>> decoded = decode_work_secret(encoded)
|
|
27
|
+
>>>
|
|
28
|
+
>>> # 构建 WebSocket URL
|
|
29
|
+
>>> ws_url = build_sdk_url("https://api.example.com", "session-123")
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import base64
|
|
35
|
+
import json
|
|
36
|
+
|
|
37
|
+
from illusion.bridge.types import WorkSecret
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def encode_work_secret(secret: WorkSecret) -> str:
|
|
41
|
+
"""
|
|
42
|
+
编码工作密钥
|
|
43
|
+
|
|
44
|
+
将 WorkSecret 对象序列化为 JSON 字符串,然后进行 Base64URL 编码。
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
secret: WorkSecret 对象,包含版本、令牌和 API 地址
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
str: Base64URL 编码后的字符串(不包含填充字符)
|
|
51
|
+
|
|
52
|
+
使用示例:
|
|
53
|
+
>>> secret = WorkSecret(version=1, session_ingress_token="token", api_base_url="https://api.example.com")
|
|
54
|
+
>>> encoded = encode_work_secret(secret)
|
|
55
|
+
"""
|
|
56
|
+
# 将 WorkSecret 对象转换为字典并序列化为 JSON 格式
|
|
57
|
+
data = json.dumps(secret.__dict__, separators=(",", ":")).encode("utf-8")
|
|
58
|
+
# 使用 Base64URL 编码并移除填充字符
|
|
59
|
+
return base64.urlsafe_b64encode(data).decode("utf-8").rstrip("=")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def decode_work_secret(secret: str) -> WorkSecret:
|
|
63
|
+
"""
|
|
64
|
+
解码并验证工作密钥
|
|
65
|
+
|
|
66
|
+
将 Base64URL 编码的字符串解码为 WorkSecret 对象,并进行版本和字段验证。
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
secret: Base64URL 编码的工作密钥字符串
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
WorkSecret: 解码后的 WorkSecret 对象
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
ValueError: 当密钥版本不支持或缺少必需字段时
|
|
76
|
+
|
|
77
|
+
使用示例:
|
|
78
|
+
>>> decoded = decode_work_secret("eyJ2ZXJzaW9uIjoxLCJzZXNzaW9uX2luZ3Jlc3NfdG9rZW4iOiJ0b2tlbiIsImFwaV9iYXNlX3VybCI6Imh0dHBzOi8vYXBpLmV4YW1wbGUuY29tIn0")
|
|
79
|
+
"""
|
|
80
|
+
# 计算并添加缺失的填充字符
|
|
81
|
+
padding = "=" * (-len(secret) % 4)
|
|
82
|
+
# 解码 Base64URL 数据
|
|
83
|
+
raw = base64.urlsafe_b64decode((secret + padding).encode("utf-8"))
|
|
84
|
+
# 解析 JSON 数据
|
|
85
|
+
data = json.loads(raw.decode("utf-8"))
|
|
86
|
+
# 验证密钥版本
|
|
87
|
+
if data.get("version") != 1:
|
|
88
|
+
raise ValueError(f"Unsupported work secret version: {data.get('version')}")
|
|
89
|
+
# 验证会话入口令牌
|
|
90
|
+
if not data.get("session_ingress_token"):
|
|
91
|
+
raise ValueError("Invalid work secret: missing session_ingress_token")
|
|
92
|
+
# 验证 API 基础地址
|
|
93
|
+
if not isinstance(data.get("api_base_url"), str):
|
|
94
|
+
raise ValueError("Invalid work secret: missing api_base_url")
|
|
95
|
+
# 创建 WorkSecret 对象
|
|
96
|
+
return WorkSecret(
|
|
97
|
+
version=data["version"],
|
|
98
|
+
session_ingress_token=data["session_ingress_token"],
|
|
99
|
+
api_base_url=data["api_base_url"],
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def build_sdk_url(api_base_url: str, session_id: str) -> str:
|
|
104
|
+
"""
|
|
105
|
+
构建会话入口 WebSocket URL
|
|
106
|
+
|
|
107
|
+
根据 API 基础地址和会话 ID 构建完整的 WebSocket 连接 URL。
|
|
108
|
+
本地环境使用 ws 协议和 v2 版本,生产环境使用 wss 协议和 v1 版本。
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
api_base_url: API 基础地址,如 "https://api.example.com"
|
|
112
|
+
session_id: 会话唯一标识符
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
str: 完整的 WebSocket URL
|
|
116
|
+
|
|
117
|
+
使用示例:
|
|
118
|
+
>>> url = build_sdk_url("https://api.example.com", "session-123")
|
|
119
|
+
>>> # 本地: ws://api.example.com/v2/session_ingress/ws/session-123
|
|
120
|
+
>>> # 生产: wss://api.example.com/v1/session_ingress/ws/session-123
|
|
121
|
+
"""
|
|
122
|
+
# 判断是否为本地环境
|
|
123
|
+
is_local = "localhost" in api_base_url or "127.0.0.1" in api_base_url
|
|
124
|
+
# 根据环境选择 WebSocket 协议
|
|
125
|
+
protocol = "ws" if is_local else "wss"
|
|
126
|
+
# 根据环境选择版本
|
|
127
|
+
version = "v2" if is_local else "v1"
|
|
128
|
+
# 提取主机地址(移除协议前缀和尾部斜杠)
|
|
129
|
+
host = api_base_url.replace("https://", "").replace("http://", "").rstrip("/")
|
|
130
|
+
# 构建完整的 WebSocket URL
|
|
131
|
+
return f"{protocol}://{host}/{version}/session_ingress/ws/{session_id}"
|