codex-autorunner 1.0.0__py3-none-any.whl → 1.2.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 (227) hide show
  1. codex_autorunner/__init__.py +12 -1
  2. codex_autorunner/agents/codex/harness.py +1 -1
  3. codex_autorunner/agents/opencode/client.py +113 -4
  4. codex_autorunner/agents/opencode/constants.py +3 -0
  5. codex_autorunner/agents/opencode/harness.py +6 -1
  6. codex_autorunner/agents/opencode/runtime.py +59 -18
  7. codex_autorunner/agents/opencode/supervisor.py +4 -0
  8. codex_autorunner/agents/registry.py +36 -7
  9. codex_autorunner/bootstrap.py +226 -4
  10. codex_autorunner/cli.py +5 -1174
  11. codex_autorunner/codex_cli.py +20 -84
  12. codex_autorunner/core/__init__.py +20 -0
  13. codex_autorunner/core/about_car.py +119 -1
  14. codex_autorunner/core/app_server_ids.py +59 -0
  15. codex_autorunner/core/app_server_threads.py +17 -2
  16. codex_autorunner/core/app_server_utils.py +165 -0
  17. codex_autorunner/core/archive.py +349 -0
  18. codex_autorunner/core/codex_runner.py +6 -2
  19. codex_autorunner/core/config.py +433 -4
  20. codex_autorunner/core/context_awareness.py +38 -0
  21. codex_autorunner/core/docs.py +0 -122
  22. codex_autorunner/core/drafts.py +58 -4
  23. codex_autorunner/core/exceptions.py +4 -0
  24. codex_autorunner/core/filebox.py +265 -0
  25. codex_autorunner/core/flows/controller.py +96 -2
  26. codex_autorunner/core/flows/models.py +13 -0
  27. codex_autorunner/core/flows/reasons.py +52 -0
  28. codex_autorunner/core/flows/reconciler.py +134 -0
  29. codex_autorunner/core/flows/runtime.py +57 -4
  30. codex_autorunner/core/flows/store.py +142 -7
  31. codex_autorunner/core/flows/transition.py +27 -15
  32. codex_autorunner/core/flows/ux_helpers.py +272 -0
  33. codex_autorunner/core/flows/worker_process.py +32 -6
  34. codex_autorunner/core/git_utils.py +62 -0
  35. codex_autorunner/core/hub.py +291 -20
  36. codex_autorunner/core/lifecycle_events.py +253 -0
  37. codex_autorunner/core/notifications.py +14 -2
  38. codex_autorunner/core/path_utils.py +2 -1
  39. codex_autorunner/core/pma_audit.py +224 -0
  40. codex_autorunner/core/pma_context.py +496 -0
  41. codex_autorunner/core/pma_dispatch_interceptor.py +284 -0
  42. codex_autorunner/core/pma_lifecycle.py +527 -0
  43. codex_autorunner/core/pma_queue.py +367 -0
  44. codex_autorunner/core/pma_safety.py +221 -0
  45. codex_autorunner/core/pma_state.py +115 -0
  46. codex_autorunner/core/ports/__init__.py +28 -0
  47. codex_autorunner/{integrations/agents → core/ports}/agent_backend.py +13 -8
  48. codex_autorunner/core/ports/backend_orchestrator.py +41 -0
  49. codex_autorunner/{integrations/agents → core/ports}/run_event.py +23 -6
  50. codex_autorunner/core/prompt.py +0 -80
  51. codex_autorunner/core/prompts.py +56 -172
  52. codex_autorunner/core/redaction.py +0 -4
  53. codex_autorunner/core/review_context.py +11 -9
  54. codex_autorunner/core/runner_controller.py +35 -33
  55. codex_autorunner/core/runner_state.py +147 -0
  56. codex_autorunner/core/runtime.py +829 -0
  57. codex_autorunner/core/sqlite_utils.py +13 -4
  58. codex_autorunner/core/state.py +7 -10
  59. codex_autorunner/core/state_roots.py +62 -0
  60. codex_autorunner/core/supervisor_protocol.py +15 -0
  61. codex_autorunner/core/templates/__init__.py +39 -0
  62. codex_autorunner/core/templates/git_mirror.py +234 -0
  63. codex_autorunner/core/templates/provenance.py +56 -0
  64. codex_autorunner/core/templates/scan_cache.py +120 -0
  65. codex_autorunner/core/text_delta_coalescer.py +54 -0
  66. codex_autorunner/core/ticket_linter_cli.py +218 -0
  67. codex_autorunner/core/ticket_manager_cli.py +494 -0
  68. codex_autorunner/core/time_utils.py +11 -0
  69. codex_autorunner/core/types.py +18 -0
  70. codex_autorunner/core/update.py +4 -5
  71. codex_autorunner/core/update_paths.py +28 -0
  72. codex_autorunner/core/usage.py +164 -12
  73. codex_autorunner/core/utils.py +125 -15
  74. codex_autorunner/flows/review/__init__.py +17 -0
  75. codex_autorunner/{core/review.py → flows/review/service.py} +37 -34
  76. codex_autorunner/flows/ticket_flow/definition.py +52 -3
  77. codex_autorunner/integrations/agents/__init__.py +11 -19
  78. codex_autorunner/integrations/agents/backend_orchestrator.py +302 -0
  79. codex_autorunner/integrations/agents/codex_adapter.py +90 -0
  80. codex_autorunner/integrations/agents/codex_backend.py +177 -25
  81. codex_autorunner/integrations/agents/opencode_adapter.py +108 -0
  82. codex_autorunner/integrations/agents/opencode_backend.py +305 -32
  83. codex_autorunner/integrations/agents/runner.py +86 -0
  84. codex_autorunner/integrations/agents/wiring.py +279 -0
  85. codex_autorunner/integrations/app_server/client.py +7 -60
  86. codex_autorunner/integrations/app_server/env.py +2 -107
  87. codex_autorunner/{core/app_server_events.py → integrations/app_server/event_buffer.py} +15 -8
  88. codex_autorunner/integrations/telegram/adapter.py +65 -0
  89. codex_autorunner/integrations/telegram/config.py +46 -0
  90. codex_autorunner/integrations/telegram/constants.py +1 -1
  91. codex_autorunner/integrations/telegram/doctor.py +228 -6
  92. codex_autorunner/integrations/telegram/handlers/callbacks.py +7 -0
  93. codex_autorunner/integrations/telegram/handlers/commands/execution.py +236 -74
  94. codex_autorunner/integrations/telegram/handlers/commands/files.py +314 -75
  95. codex_autorunner/integrations/telegram/handlers/commands/flows.py +1496 -71
  96. codex_autorunner/integrations/telegram/handlers/commands/workspace.py +498 -37
  97. codex_autorunner/integrations/telegram/handlers/commands_runtime.py +206 -48
  98. codex_autorunner/integrations/telegram/handlers/commands_spec.py +20 -3
  99. codex_autorunner/integrations/telegram/handlers/messages.py +27 -1
  100. codex_autorunner/integrations/telegram/handlers/selections.py +61 -1
  101. codex_autorunner/integrations/telegram/helpers.py +22 -1
  102. codex_autorunner/integrations/telegram/runtime.py +9 -4
  103. codex_autorunner/integrations/telegram/service.py +45 -10
  104. codex_autorunner/integrations/telegram/state.py +38 -0
  105. codex_autorunner/integrations/telegram/ticket_flow_bridge.py +338 -43
  106. codex_autorunner/integrations/telegram/transport.py +13 -4
  107. codex_autorunner/integrations/templates/__init__.py +27 -0
  108. codex_autorunner/integrations/templates/scan_agent.py +312 -0
  109. codex_autorunner/routes/__init__.py +37 -76
  110. codex_autorunner/routes/agents.py +2 -137
  111. codex_autorunner/routes/analytics.py +2 -238
  112. codex_autorunner/routes/app_server.py +2 -131
  113. codex_autorunner/routes/base.py +2 -596
  114. codex_autorunner/routes/file_chat.py +4 -833
  115. codex_autorunner/routes/flows.py +4 -977
  116. codex_autorunner/routes/messages.py +4 -456
  117. codex_autorunner/routes/repos.py +2 -196
  118. codex_autorunner/routes/review.py +2 -147
  119. codex_autorunner/routes/sessions.py +2 -175
  120. codex_autorunner/routes/settings.py +2 -168
  121. codex_autorunner/routes/shared.py +2 -275
  122. codex_autorunner/routes/system.py +4 -193
  123. codex_autorunner/routes/usage.py +2 -86
  124. codex_autorunner/routes/voice.py +2 -119
  125. codex_autorunner/routes/workspace.py +2 -270
  126. codex_autorunner/server.py +4 -4
  127. codex_autorunner/static/agentControls.js +61 -16
  128. codex_autorunner/static/app.js +126 -14
  129. codex_autorunner/static/archive.js +826 -0
  130. codex_autorunner/static/archiveApi.js +37 -0
  131. codex_autorunner/static/autoRefresh.js +7 -7
  132. codex_autorunner/static/chatUploads.js +137 -0
  133. codex_autorunner/static/dashboard.js +224 -171
  134. codex_autorunner/static/docChatCore.js +185 -13
  135. codex_autorunner/static/fileChat.js +68 -40
  136. codex_autorunner/static/fileboxUi.js +159 -0
  137. codex_autorunner/static/hub.js +114 -131
  138. codex_autorunner/static/index.html +375 -49
  139. codex_autorunner/static/messages.js +568 -87
  140. codex_autorunner/static/notifications.js +255 -0
  141. codex_autorunner/static/pma.js +1167 -0
  142. codex_autorunner/static/preserve.js +17 -0
  143. codex_autorunner/static/settings.js +128 -6
  144. codex_autorunner/static/smartRefresh.js +52 -0
  145. codex_autorunner/static/streamUtils.js +57 -0
  146. codex_autorunner/static/styles.css +9798 -6143
  147. codex_autorunner/static/tabs.js +152 -11
  148. codex_autorunner/static/templateReposSettings.js +225 -0
  149. codex_autorunner/static/terminal.js +18 -0
  150. codex_autorunner/static/ticketChatActions.js +165 -3
  151. codex_autorunner/static/ticketChatStream.js +17 -119
  152. codex_autorunner/static/ticketEditor.js +137 -15
  153. codex_autorunner/static/ticketTemplates.js +798 -0
  154. codex_autorunner/static/tickets.js +821 -98
  155. codex_autorunner/static/turnEvents.js +27 -0
  156. codex_autorunner/static/turnResume.js +33 -0
  157. codex_autorunner/static/utils.js +39 -0
  158. codex_autorunner/static/workspace.js +389 -82
  159. codex_autorunner/static/workspaceFileBrowser.js +15 -13
  160. codex_autorunner/surfaces/__init__.py +5 -0
  161. codex_autorunner/surfaces/cli/__init__.py +6 -0
  162. codex_autorunner/surfaces/cli/cli.py +2534 -0
  163. codex_autorunner/surfaces/cli/codex_cli.py +20 -0
  164. codex_autorunner/surfaces/cli/pma_cli.py +817 -0
  165. codex_autorunner/surfaces/telegram/__init__.py +3 -0
  166. codex_autorunner/surfaces/web/__init__.py +1 -0
  167. codex_autorunner/surfaces/web/app.py +2223 -0
  168. codex_autorunner/surfaces/web/hub_jobs.py +192 -0
  169. codex_autorunner/surfaces/web/middleware.py +587 -0
  170. codex_autorunner/surfaces/web/pty_session.py +370 -0
  171. codex_autorunner/surfaces/web/review.py +6 -0
  172. codex_autorunner/surfaces/web/routes/__init__.py +82 -0
  173. codex_autorunner/surfaces/web/routes/agents.py +138 -0
  174. codex_autorunner/surfaces/web/routes/analytics.py +284 -0
  175. codex_autorunner/surfaces/web/routes/app_server.py +132 -0
  176. codex_autorunner/surfaces/web/routes/archive.py +357 -0
  177. codex_autorunner/surfaces/web/routes/base.py +615 -0
  178. codex_autorunner/surfaces/web/routes/file_chat.py +1117 -0
  179. codex_autorunner/surfaces/web/routes/filebox.py +227 -0
  180. codex_autorunner/surfaces/web/routes/flows.py +1354 -0
  181. codex_autorunner/surfaces/web/routes/messages.py +490 -0
  182. codex_autorunner/surfaces/web/routes/pma.py +1652 -0
  183. codex_autorunner/surfaces/web/routes/repos.py +197 -0
  184. codex_autorunner/surfaces/web/routes/review.py +148 -0
  185. codex_autorunner/surfaces/web/routes/sessions.py +176 -0
  186. codex_autorunner/surfaces/web/routes/settings.py +169 -0
  187. codex_autorunner/surfaces/web/routes/shared.py +277 -0
  188. codex_autorunner/surfaces/web/routes/system.py +196 -0
  189. codex_autorunner/surfaces/web/routes/templates.py +634 -0
  190. codex_autorunner/surfaces/web/routes/usage.py +89 -0
  191. codex_autorunner/surfaces/web/routes/voice.py +120 -0
  192. codex_autorunner/surfaces/web/routes/workspace.py +271 -0
  193. codex_autorunner/surfaces/web/runner_manager.py +25 -0
  194. codex_autorunner/surfaces/web/schemas.py +469 -0
  195. codex_autorunner/surfaces/web/static_assets.py +490 -0
  196. codex_autorunner/surfaces/web/static_refresh.py +86 -0
  197. codex_autorunner/surfaces/web/terminal_sessions.py +78 -0
  198. codex_autorunner/tickets/__init__.py +8 -1
  199. codex_autorunner/tickets/agent_pool.py +53 -4
  200. codex_autorunner/tickets/files.py +37 -16
  201. codex_autorunner/tickets/lint.py +50 -0
  202. codex_autorunner/tickets/models.py +6 -1
  203. codex_autorunner/tickets/outbox.py +50 -2
  204. codex_autorunner/tickets/runner.py +396 -57
  205. codex_autorunner/web/__init__.py +5 -1
  206. codex_autorunner/web/app.py +2 -1949
  207. codex_autorunner/web/hub_jobs.py +2 -191
  208. codex_autorunner/web/middleware.py +2 -586
  209. codex_autorunner/web/pty_session.py +2 -369
  210. codex_autorunner/web/runner_manager.py +2 -24
  211. codex_autorunner/web/schemas.py +2 -376
  212. codex_autorunner/web/static_assets.py +4 -441
  213. codex_autorunner/web/static_refresh.py +2 -85
  214. codex_autorunner/web/terminal_sessions.py +2 -77
  215. codex_autorunner/workspace/paths.py +49 -33
  216. codex_autorunner-1.2.0.dist-info/METADATA +150 -0
  217. codex_autorunner-1.2.0.dist-info/RECORD +339 -0
  218. codex_autorunner/core/adapter_utils.py +0 -21
  219. codex_autorunner/core/engine.py +0 -2653
  220. codex_autorunner/core/static_assets.py +0 -55
  221. codex_autorunner-1.0.0.dist-info/METADATA +0 -246
  222. codex_autorunner-1.0.0.dist-info/RECORD +0 -251
  223. /codex_autorunner/{routes → surfaces/web/routes}/terminal_images.py +0 -0
  224. {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/WHEEL +0 -0
  225. {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/entry_points.txt +0 -0
  226. {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/licenses/LICENSE +0 -0
  227. {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/top_level.txt +0 -0
@@ -1,239 +1,3 @@
1
- """Analytics summary routes.
1
+ """Backward-compatible analytics routes."""
2
2
 
3
- This module aggregates run/ticket/message metadata for the analytics dashboard
4
- without relying on legacy autorunner endpoints. It intentionally reads from the
5
- filesystem-backed ticket_flow store and ticket files to keep the UI consistent
6
- with the rest of the app.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- from datetime import datetime, timezone
12
- from pathlib import Path
13
- from typing import Any, Dict, Optional
14
-
15
- from fastapi import APIRouter
16
-
17
- from ..core.flows.models import FlowRunRecord, FlowRunStatus
18
- from ..core.flows.store import FlowStore
19
- from ..core.utils import find_repo_root
20
- from ..tickets.files import list_ticket_paths, read_ticket, ticket_is_done
21
- from ..tickets.outbox import resolve_outbox_paths
22
- from ..tickets.replies import resolve_reply_paths
23
-
24
-
25
- def _flows_db_path(repo_root: Path) -> Path:
26
- return repo_root / ".codex-autorunner" / "flows.db"
27
-
28
-
29
- def _load_flow_store(repo_root: Path) -> Optional[FlowStore]:
30
- db_path = _flows_db_path(repo_root)
31
- if not db_path.exists():
32
- return None
33
- store = FlowStore(db_path)
34
- try:
35
- store.initialize()
36
- except Exception:
37
- return None
38
- return store
39
-
40
-
41
- def _select_primary_run(records: list[FlowRunRecord]) -> Optional[FlowRunRecord]:
42
- """Select the primary run for analytics display.
43
-
44
- Only considers the newest run (records[0]). If it's active or paused, return it.
45
- If the newest run is terminal (completed/stopped/failed), return None to show idle.
46
- This matches the backend's _active_or_paused_run() logic and prevents showing
47
- stale data from old paused runs when newer runs have completed.
48
- """
49
- if not records:
50
- return None
51
- newest = records[0]
52
- if (
53
- FlowRunStatus(newest.status).is_active()
54
- or FlowRunStatus(newest.status).is_paused()
55
- ):
56
- return newest
57
- return None
58
-
59
-
60
- def _parse_timestamp(value: Optional[str]) -> Optional[datetime]:
61
- if not value:
62
- return None
63
- try:
64
- if value.endswith("Z"):
65
- return datetime.fromisoformat(value.replace("Z", "+00:00"))
66
- return datetime.fromisoformat(value)
67
- except ValueError:
68
- return None
69
-
70
-
71
- def _duration_seconds(
72
- started_at: Optional[str], finished_at: Optional[str], status: str
73
- ) -> Optional[float]:
74
- start_dt = _parse_timestamp(started_at)
75
- if not start_dt:
76
- return None
77
- end_dt = _parse_timestamp(finished_at)
78
- if not end_dt and status in {
79
- FlowRunStatus.RUNNING.value,
80
- FlowRunStatus.PAUSED.value,
81
- FlowRunStatus.PENDING.value,
82
- }:
83
- end_dt = datetime.now(timezone.utc)
84
- if not end_dt:
85
- return None
86
- return (end_dt - start_dt).total_seconds()
87
-
88
-
89
- def _ticket_counts(ticket_dir: Path) -> dict[str, int]:
90
- total = 0
91
- done = 0
92
- for path in list_ticket_paths(ticket_dir):
93
- total += 1
94
- try:
95
- if ticket_is_done(path):
96
- done += 1
97
- except Exception:
98
- # Treat unreadable/invalid tickets as not-done but still count them.
99
- continue
100
- todo = max(total - done, 0)
101
- return {"todo": todo, "done": done, "total": total}
102
-
103
-
104
- def _count_history_dirs(history_dir: Path) -> int:
105
- if not history_dir.exists() or not history_dir.is_dir():
106
- return 0
107
- count = 0
108
- try:
109
- for child in history_dir.iterdir():
110
- try:
111
- if child.is_dir() and len(child.name) == 4 and child.name.isdigit():
112
- count += 1
113
- except OSError:
114
- continue
115
- except OSError:
116
- return count
117
- return count
118
-
119
-
120
- def _build_summary(repo_root: Path) -> Dict[str, Any]:
121
- ticket_dir = repo_root / ".codex-autorunner" / "tickets"
122
- store = _load_flow_store(repo_root)
123
- records: list[FlowRunRecord] = []
124
- if store:
125
- try:
126
- records = store.list_flow_runs(flow_type="ticket_flow")
127
- except Exception:
128
- records = []
129
- finally:
130
- try:
131
- store.close()
132
- except Exception:
133
- pass
134
-
135
- run_record = _select_primary_run(records)
136
-
137
- default_run = {
138
- "id": None,
139
- "short_id": None,
140
- "status": "idle",
141
- "started_at": None,
142
- "finished_at": None,
143
- "duration_seconds": None,
144
- "current_step": None,
145
- "created_at": None,
146
- }
147
-
148
- run_data: Dict[str, Any] = default_run
149
- turns: Dict[str, Optional[int]] = {
150
- "total": None,
151
- "current_ticket": None,
152
- "dispatches": 0,
153
- "replies": 0,
154
- }
155
- current_ticket: Optional[str] = None
156
- agent_id: Optional[str] = None
157
-
158
- if run_record:
159
- run_data = {
160
- "id": run_record.id,
161
- "short_id": run_record.id.split("-")[0] if run_record.id else None,
162
- "status": run_record.status.value,
163
- "started_at": run_record.started_at,
164
- "finished_at": run_record.finished_at,
165
- "duration_seconds": _duration_seconds(
166
- run_record.started_at, run_record.finished_at, run_record.status.value
167
- ),
168
- "current_step": run_record.current_step,
169
- "created_at": run_record.created_at,
170
- }
171
-
172
- state = run_record.state if isinstance(run_record.state, dict) else {}
173
- ticket_state = state.get("ticket_engine") if isinstance(state, dict) else {}
174
- if isinstance(ticket_state, dict):
175
- turns["total"] = ticket_state.get("total_turns") # type: ignore[index]
176
- turns["current_ticket"] = ticket_state.get("ticket_turns") # type: ignore[index]
177
- current_ticket = ticket_state.get("current_ticket") # type: ignore[assignment]
178
- agent_id = ticket_state.get("last_agent_id") # type: ignore[assignment]
179
-
180
- workspace_value = run_record.input_data.get("workspace_root")
181
- workspace_root = Path(workspace_value) if workspace_value else repo_root
182
- runs_dir = Path(
183
- run_record.input_data.get("runs_dir") or ".codex-autorunner/runs"
184
- )
185
- outbox_paths = resolve_outbox_paths(
186
- workspace_root=workspace_root, runs_dir=runs_dir, run_id=run_record.id
187
- )
188
- reply_paths = resolve_reply_paths(
189
- workspace_root=workspace_root, runs_dir=runs_dir, run_id=run_record.id
190
- )
191
- turns["dispatches"] = _count_history_dirs(outbox_paths.dispatch_history_dir)
192
- turns["replies"] = _count_history_dirs(reply_paths.reply_history_dir)
193
-
194
- # If current ticket is known, read its frontmatter to pick agent id when available.
195
- if current_ticket:
196
- current_path = (workspace_root / current_ticket).resolve()
197
- try:
198
- doc, _errors = read_ticket(current_path)
199
- if doc and doc.frontmatter and getattr(doc.frontmatter, "agent", None):
200
- agent_id = doc.frontmatter.agent
201
- except Exception:
202
- pass
203
-
204
- ticket_counts = _ticket_counts(ticket_dir)
205
-
206
- return {
207
- "run": run_data,
208
- "tickets": {
209
- "todo_count": ticket_counts["todo"],
210
- "done_count": ticket_counts["done"],
211
- "total_count": ticket_counts["total"],
212
- "current_ticket": current_ticket,
213
- },
214
- "turns": {
215
- "total": turns.get("total"),
216
- "current_ticket": turns.get("current_ticket"),
217
- "dispatches": turns.get("dispatches"),
218
- "replies": turns.get("replies"),
219
- },
220
- "agent": {
221
- "id": agent_id,
222
- "model": None,
223
- },
224
- }
225
-
226
-
227
- def build_analytics_routes() -> APIRouter:
228
- router = APIRouter(prefix="/api/analytics", tags=["analytics"])
229
-
230
- @router.get("/summary")
231
- def get_analytics_summary():
232
- repo_root = find_repo_root()
233
- data = _build_summary(repo_root)
234
- return data
235
-
236
- return router
237
-
238
-
239
- __all__ = ["build_analytics_routes"]
3
+ from ..surfaces.web.routes.analytics import * # noqa: F401,F403
@@ -1,132 +1,3 @@
1
- """
2
- App-server support routes (thread registry).
3
- """
1
+ """Backward-compatible app-server routes."""
4
2
 
5
- from pathlib import Path
6
-
7
- from fastapi import APIRouter, HTTPException, Request
8
- from fastapi.responses import FileResponse, StreamingResponse
9
-
10
- from ..core.app_server_threads import normalize_feature_key
11
- from ..core.utils import is_within
12
- from ..integrations.app_server.client import CodexAppServerError
13
- from ..web.schemas import (
14
- AppServerThreadArchiveRequest,
15
- AppServerThreadArchiveResponse,
16
- AppServerThreadResetAllResponse,
17
- AppServerThreadResetRequest,
18
- AppServerThreadResetResponse,
19
- AppServerThreadsResponse,
20
- )
21
- from .shared import SSE_HEADERS
22
-
23
-
24
- def build_app_server_routes() -> APIRouter:
25
- router = APIRouter()
26
-
27
- @router.get("/api/app-server/turns/{turn_id}/events")
28
- async def stream_app_server_turn_events(
29
- turn_id: str, request: Request, thread_id: str
30
- ):
31
- events = getattr(request.app.state, "app_server_events", None)
32
- if events is None:
33
- raise HTTPException(status_code=404, detail="App-server events unavailable")
34
- if not thread_id:
35
- raise HTTPException(status_code=400, detail="thread_id is required")
36
- return StreamingResponse(
37
- events.stream(thread_id, turn_id),
38
- media_type="text/event-stream",
39
- headers=SSE_HEADERS,
40
- )
41
-
42
- @router.get("/api/app-server/threads", response_model=AppServerThreadsResponse)
43
- def app_server_threads(request: Request):
44
- registry = request.app.state.app_server_threads
45
- return registry.feature_map()
46
-
47
- @router.get("/api/app-server/models")
48
- async def app_server_models(request: Request):
49
- engine = request.app.state.engine
50
- supervisor = request.app.state.app_server_supervisor
51
- try:
52
- client = await supervisor.get_client(engine.repo_root)
53
- return await client.model_list()
54
- except CodexAppServerError as exc:
55
- raise HTTPException(status_code=502, detail=str(exc)) from exc
56
-
57
- @router.post(
58
- "/api/app-server/threads/reset", response_model=AppServerThreadResetResponse
59
- )
60
- def reset_app_server_thread(request: Request, payload: AppServerThreadResetRequest):
61
- registry = request.app.state.app_server_threads
62
- try:
63
- key = normalize_feature_key(payload.key)
64
- except ValueError as exc:
65
- raise HTTPException(status_code=400, detail=str(exc)) from exc
66
- cleared = registry.reset_thread(key)
67
- return {"status": "ok", "key": key, "cleared": cleared}
68
-
69
- @router.post(
70
- "/api/app-server/threads/archive",
71
- response_model=AppServerThreadArchiveResponse,
72
- )
73
- async def archive_app_server_thread(
74
- request: Request, payload: AppServerThreadArchiveRequest
75
- ):
76
- thread_id = payload.thread_id.strip()
77
- if not thread_id:
78
- raise HTTPException(status_code=400, detail="thread_id is required")
79
- engine = request.app.state.engine
80
- supervisor = request.app.state.app_server_supervisor
81
- try:
82
- client = await supervisor.get_client(engine.repo_root)
83
- await client.thread_archive(thread_id)
84
- except CodexAppServerError as exc:
85
- raise HTTPException(status_code=502, detail=str(exc)) from exc
86
- return {"status": "ok", "thread_id": thread_id, "archived": True}
87
-
88
- @router.post(
89
- "/api/app-server/threads/reset-all",
90
- response_model=AppServerThreadResetAllResponse,
91
- )
92
- def reset_app_server_threads(request: Request):
93
- registry = request.app.state.app_server_threads
94
- registry.reset_all()
95
- return {"status": "ok", "cleared": True}
96
-
97
- @router.get("/api/app-server/threads/backup")
98
- def download_app_server_threads_backup(request: Request):
99
- registry = request.app.state.app_server_threads
100
- notice = registry.corruption_notice() or {}
101
- backup_path = notice.get("backup_path")
102
- if not isinstance(backup_path, str) or not backup_path:
103
- raise HTTPException(status_code=404, detail="No backup available")
104
- path = Path(backup_path)
105
- engine = request.app.state.engine
106
- if not is_within(engine.repo_root, path):
107
- raise HTTPException(status_code=400, detail="Invalid backup path")
108
- if not path.exists():
109
- raise HTTPException(status_code=404, detail="Backup not found")
110
- return FileResponse(path, filename=path.name)
111
-
112
- @router.get("/api/app-server/account")
113
- async def app_server_account(request: Request):
114
- engine = request.app.state.engine
115
- supervisor = request.app.state.app_server_supervisor
116
- try:
117
- client = await supervisor.get_client(engine.repo_root)
118
- return await client.account_read()
119
- except CodexAppServerError as exc:
120
- raise HTTPException(status_code=502, detail=str(exc)) from exc
121
-
122
- @router.get("/api/app-server/rate-limits")
123
- async def app_server_rate_limits(request: Request):
124
- engine = request.app.state.engine
125
- supervisor = request.app.state.app_server_supervisor
126
- try:
127
- client = await supervisor.get_client(engine.repo_root)
128
- return await client.rate_limits_read()
129
- except CodexAppServerError as exc:
130
- raise HTTPException(status_code=502, detail=str(exc)) from exc
131
-
132
- return router
3
+ from ..surfaces.web.routes.app_server import * # noqa: F401,F403