codex-autorunner 1.0.0__py3-none-any.whl → 1.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 (170) hide show
  1. codex_autorunner/__init__.py +12 -1
  2. codex_autorunner/agents/codex/harness.py +1 -1
  3. codex_autorunner/agents/opencode/constants.py +3 -0
  4. codex_autorunner/agents/opencode/harness.py +6 -1
  5. codex_autorunner/agents/opencode/runtime.py +59 -18
  6. codex_autorunner/agents/registry.py +22 -3
  7. codex_autorunner/bootstrap.py +7 -3
  8. codex_autorunner/cli.py +5 -1174
  9. codex_autorunner/codex_cli.py +20 -84
  10. codex_autorunner/core/__init__.py +4 -0
  11. codex_autorunner/core/about_car.py +6 -1
  12. codex_autorunner/core/app_server_ids.py +59 -0
  13. codex_autorunner/core/app_server_threads.py +11 -2
  14. codex_autorunner/core/app_server_utils.py +165 -0
  15. codex_autorunner/core/archive.py +349 -0
  16. codex_autorunner/core/codex_runner.py +6 -2
  17. codex_autorunner/core/config.py +197 -3
  18. codex_autorunner/core/drafts.py +58 -4
  19. codex_autorunner/core/engine.py +1329 -680
  20. codex_autorunner/core/exceptions.py +4 -0
  21. codex_autorunner/core/flows/controller.py +25 -1
  22. codex_autorunner/core/flows/models.py +13 -0
  23. codex_autorunner/core/flows/reasons.py +52 -0
  24. codex_autorunner/core/flows/reconciler.py +131 -0
  25. codex_autorunner/core/flows/runtime.py +35 -4
  26. codex_autorunner/core/flows/store.py +83 -0
  27. codex_autorunner/core/flows/transition.py +5 -0
  28. codex_autorunner/core/flows/ux_helpers.py +257 -0
  29. codex_autorunner/core/git_utils.py +62 -0
  30. codex_autorunner/core/hub.py +121 -7
  31. codex_autorunner/core/notifications.py +14 -2
  32. codex_autorunner/core/ports/__init__.py +28 -0
  33. codex_autorunner/{integrations/agents → core/ports}/agent_backend.py +11 -3
  34. codex_autorunner/core/ports/backend_orchestrator.py +41 -0
  35. codex_autorunner/{integrations/agents → core/ports}/run_event.py +22 -2
  36. codex_autorunner/core/state_roots.py +57 -0
  37. codex_autorunner/core/supervisor_protocol.py +15 -0
  38. codex_autorunner/core/text_delta_coalescer.py +54 -0
  39. codex_autorunner/core/ticket_linter_cli.py +201 -0
  40. codex_autorunner/core/ticket_manager_cli.py +432 -0
  41. codex_autorunner/core/update.py +4 -5
  42. codex_autorunner/core/update_paths.py +28 -0
  43. codex_autorunner/core/usage.py +164 -12
  44. codex_autorunner/core/utils.py +91 -9
  45. codex_autorunner/flows/review/__init__.py +17 -0
  46. codex_autorunner/{core/review.py → flows/review/service.py} +15 -10
  47. codex_autorunner/flows/ticket_flow/definition.py +9 -2
  48. codex_autorunner/integrations/agents/__init__.py +9 -19
  49. codex_autorunner/integrations/agents/backend_orchestrator.py +284 -0
  50. codex_autorunner/integrations/agents/codex_adapter.py +90 -0
  51. codex_autorunner/integrations/agents/codex_backend.py +158 -17
  52. codex_autorunner/integrations/agents/opencode_adapter.py +108 -0
  53. codex_autorunner/integrations/agents/opencode_backend.py +305 -32
  54. codex_autorunner/integrations/agents/runner.py +91 -0
  55. codex_autorunner/integrations/agents/wiring.py +271 -0
  56. codex_autorunner/integrations/app_server/client.py +7 -60
  57. codex_autorunner/integrations/app_server/env.py +2 -107
  58. codex_autorunner/{core/app_server_events.py → integrations/app_server/event_buffer.py} +15 -8
  59. codex_autorunner/integrations/telegram/adapter.py +65 -0
  60. codex_autorunner/integrations/telegram/config.py +46 -0
  61. codex_autorunner/integrations/telegram/constants.py +1 -1
  62. codex_autorunner/integrations/telegram/handlers/callbacks.py +7 -0
  63. codex_autorunner/integrations/telegram/handlers/commands/flows.py +1203 -66
  64. codex_autorunner/integrations/telegram/handlers/commands_runtime.py +4 -3
  65. codex_autorunner/integrations/telegram/handlers/commands_spec.py +8 -2
  66. codex_autorunner/integrations/telegram/handlers/messages.py +1 -0
  67. codex_autorunner/integrations/telegram/handlers/selections.py +61 -1
  68. codex_autorunner/integrations/telegram/helpers.py +24 -1
  69. codex_autorunner/integrations/telegram/service.py +15 -10
  70. codex_autorunner/integrations/telegram/ticket_flow_bridge.py +329 -40
  71. codex_autorunner/integrations/telegram/transport.py +3 -1
  72. codex_autorunner/routes/__init__.py +37 -76
  73. codex_autorunner/routes/agents.py +2 -137
  74. codex_autorunner/routes/analytics.py +2 -238
  75. codex_autorunner/routes/app_server.py +2 -131
  76. codex_autorunner/routes/base.py +2 -596
  77. codex_autorunner/routes/file_chat.py +4 -833
  78. codex_autorunner/routes/flows.py +4 -977
  79. codex_autorunner/routes/messages.py +4 -456
  80. codex_autorunner/routes/repos.py +2 -196
  81. codex_autorunner/routes/review.py +2 -147
  82. codex_autorunner/routes/sessions.py +2 -175
  83. codex_autorunner/routes/settings.py +2 -168
  84. codex_autorunner/routes/shared.py +2 -275
  85. codex_autorunner/routes/system.py +4 -193
  86. codex_autorunner/routes/usage.py +2 -86
  87. codex_autorunner/routes/voice.py +2 -119
  88. codex_autorunner/routes/workspace.py +2 -270
  89. codex_autorunner/server.py +2 -2
  90. codex_autorunner/static/agentControls.js +40 -11
  91. codex_autorunner/static/app.js +11 -3
  92. codex_autorunner/static/archive.js +826 -0
  93. codex_autorunner/static/archiveApi.js +37 -0
  94. codex_autorunner/static/autoRefresh.js +7 -7
  95. codex_autorunner/static/dashboard.js +224 -171
  96. codex_autorunner/static/hub.js +112 -94
  97. codex_autorunner/static/index.html +80 -33
  98. codex_autorunner/static/messages.js +486 -83
  99. codex_autorunner/static/preserve.js +17 -0
  100. codex_autorunner/static/settings.js +125 -6
  101. codex_autorunner/static/smartRefresh.js +52 -0
  102. codex_autorunner/static/styles.css +1373 -101
  103. codex_autorunner/static/tabs.js +152 -11
  104. codex_autorunner/static/terminal.js +18 -0
  105. codex_autorunner/static/ticketEditor.js +99 -5
  106. codex_autorunner/static/tickets.js +760 -87
  107. codex_autorunner/static/utils.js +11 -0
  108. codex_autorunner/static/workspace.js +133 -40
  109. codex_autorunner/static/workspaceFileBrowser.js +9 -9
  110. codex_autorunner/surfaces/__init__.py +5 -0
  111. codex_autorunner/surfaces/cli/__init__.py +6 -0
  112. codex_autorunner/surfaces/cli/cli.py +1224 -0
  113. codex_autorunner/surfaces/cli/codex_cli.py +20 -0
  114. codex_autorunner/surfaces/telegram/__init__.py +3 -0
  115. codex_autorunner/surfaces/web/__init__.py +1 -0
  116. codex_autorunner/surfaces/web/app.py +2019 -0
  117. codex_autorunner/surfaces/web/hub_jobs.py +192 -0
  118. codex_autorunner/surfaces/web/middleware.py +587 -0
  119. codex_autorunner/surfaces/web/pty_session.py +370 -0
  120. codex_autorunner/surfaces/web/review.py +6 -0
  121. codex_autorunner/surfaces/web/routes/__init__.py +78 -0
  122. codex_autorunner/surfaces/web/routes/agents.py +138 -0
  123. codex_autorunner/surfaces/web/routes/analytics.py +277 -0
  124. codex_autorunner/surfaces/web/routes/app_server.py +132 -0
  125. codex_autorunner/surfaces/web/routes/archive.py +357 -0
  126. codex_autorunner/surfaces/web/routes/base.py +615 -0
  127. codex_autorunner/surfaces/web/routes/file_chat.py +836 -0
  128. codex_autorunner/surfaces/web/routes/flows.py +1164 -0
  129. codex_autorunner/surfaces/web/routes/messages.py +459 -0
  130. codex_autorunner/surfaces/web/routes/repos.py +197 -0
  131. codex_autorunner/surfaces/web/routes/review.py +148 -0
  132. codex_autorunner/surfaces/web/routes/sessions.py +176 -0
  133. codex_autorunner/surfaces/web/routes/settings.py +169 -0
  134. codex_autorunner/surfaces/web/routes/shared.py +280 -0
  135. codex_autorunner/surfaces/web/routes/system.py +196 -0
  136. codex_autorunner/surfaces/web/routes/usage.py +89 -0
  137. codex_autorunner/surfaces/web/routes/voice.py +120 -0
  138. codex_autorunner/surfaces/web/routes/workspace.py +271 -0
  139. codex_autorunner/surfaces/web/runner_manager.py +25 -0
  140. codex_autorunner/surfaces/web/schemas.py +417 -0
  141. codex_autorunner/surfaces/web/static_assets.py +490 -0
  142. codex_autorunner/surfaces/web/static_refresh.py +86 -0
  143. codex_autorunner/surfaces/web/terminal_sessions.py +78 -0
  144. codex_autorunner/tickets/__init__.py +8 -1
  145. codex_autorunner/tickets/agent_pool.py +26 -4
  146. codex_autorunner/tickets/files.py +6 -2
  147. codex_autorunner/tickets/models.py +3 -1
  148. codex_autorunner/tickets/outbox.py +12 -0
  149. codex_autorunner/tickets/runner.py +63 -5
  150. codex_autorunner/web/__init__.py +5 -1
  151. codex_autorunner/web/app.py +2 -1949
  152. codex_autorunner/web/hub_jobs.py +2 -191
  153. codex_autorunner/web/middleware.py +2 -586
  154. codex_autorunner/web/pty_session.py +2 -369
  155. codex_autorunner/web/runner_manager.py +2 -24
  156. codex_autorunner/web/schemas.py +2 -376
  157. codex_autorunner/web/static_assets.py +4 -441
  158. codex_autorunner/web/static_refresh.py +2 -85
  159. codex_autorunner/web/terminal_sessions.py +2 -77
  160. codex_autorunner/workspace/paths.py +49 -33
  161. codex_autorunner-1.1.0.dist-info/METADATA +154 -0
  162. codex_autorunner-1.1.0.dist-info/RECORD +308 -0
  163. codex_autorunner/core/static_assets.py +0 -55
  164. codex_autorunner-1.0.0.dist-info/METADATA +0 -246
  165. codex_autorunner-1.0.0.dist-info/RECORD +0 -251
  166. /codex_autorunner/{routes → surfaces/web/routes}/terminal_images.py +0 -0
  167. {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.1.0.dist-info}/WHEEL +0 -0
  168. {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.1.0.dist-info}/entry_points.txt +0 -0
  169. {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.1.0.dist-info}/licenses/LICENSE +0 -0
  170. {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,17 @@
1
+ // GENERATED FILE - do not edit directly. Source: static_src/
2
+ export function preserveScroll(el, render, opts) {
3
+ if (!el) {
4
+ render();
5
+ return;
6
+ }
7
+ const top = el.scrollTop;
8
+ render();
9
+ const restore = () => {
10
+ el.scrollTop = top;
11
+ };
12
+ if (opts?.restoreOnNextFrame && typeof requestAnimationFrame === "function") {
13
+ requestAnimationFrame(restore);
14
+ return;
15
+ }
16
+ restore();
17
+ }
@@ -1,5 +1,5 @@
1
1
  // GENERATED FILE - do not edit directly. Source: static_src/
2
- import { api, confirmModal, flash, resolvePath } from "./utils.js";
2
+ import { api, confirmModal, flash, resolvePath, openModal } from "./utils.js";
3
3
  const ui = {
4
4
  settingsBtn: document.getElementById("repo-settings"),
5
5
  threadList: document.getElementById("thread-tools-list"),
@@ -73,11 +73,9 @@ async function refreshSettings() {
73
73
  await loadThreadTools();
74
74
  }
75
75
  export function initRepoSettingsPanel() {
76
- if (ui.settingsBtn) {
77
- ui.settingsBtn.addEventListener("click", () => {
78
- refreshSettings();
79
- });
80
- }
76
+ window.__CAR_SETTINGS = { loadThreadTools, refreshSettings };
77
+ // Initialize the modal interaction
78
+ initRepoSettingsModal();
81
79
  if (ui.threadNew) {
82
80
  ui.threadNew.addEventListener("click", async () => {
83
81
  try {
@@ -152,3 +150,124 @@ export function initRepoSettingsPanel() {
152
150
  // ignore
153
151
  }
154
152
  }
153
+ const UPDATE_TARGET_LABELS = {
154
+ both: "web + Telegram",
155
+ web: "web only",
156
+ telegram: "Telegram only",
157
+ };
158
+ function normalizeUpdateTarget(value) {
159
+ if (!value)
160
+ return "both";
161
+ if (value === "both" || value === "web" || value === "telegram")
162
+ return value;
163
+ return "both";
164
+ }
165
+ function getUpdateTarget(selectId) {
166
+ const select = selectId ? document.getElementById(selectId) : null;
167
+ return normalizeUpdateTarget(select ? select.value : "both");
168
+ }
169
+ function describeUpdateTarget(target) {
170
+ return UPDATE_TARGET_LABELS[target] || UPDATE_TARGET_LABELS.both;
171
+ }
172
+ async function handleSystemUpdate(btnId, targetSelectId) {
173
+ const btn = document.getElementById(btnId);
174
+ if (!btn)
175
+ return;
176
+ const originalText = btn.textContent;
177
+ btn.disabled = true;
178
+ btn.textContent = "Checking...";
179
+ const updateTarget = getUpdateTarget(targetSelectId);
180
+ const targetLabel = describeUpdateTarget(updateTarget);
181
+ let check;
182
+ try {
183
+ check = await api("/system/update/check");
184
+ }
185
+ catch (err) {
186
+ check = { update_available: true, message: err.message || "Unable to check for updates." };
187
+ }
188
+ if (!check?.update_available) {
189
+ flash(check?.message || "No update available.", "info");
190
+ btn.disabled = false;
191
+ btn.textContent = originalText;
192
+ return;
193
+ }
194
+ const restartNotice = updateTarget === "telegram"
195
+ ? "The Telegram bot will restart."
196
+ : "The service will restart.";
197
+ const confirmed = await confirmModal(`${check?.message || "Update available."} Update Codex Autorunner (${targetLabel})? ${restartNotice}`);
198
+ if (!confirmed) {
199
+ btn.disabled = false;
200
+ btn.textContent = originalText;
201
+ return;
202
+ }
203
+ btn.textContent = "Updating...";
204
+ try {
205
+ const res = await api("/system/update", {
206
+ method: "POST",
207
+ body: { target: updateTarget },
208
+ });
209
+ flash(res.message || `Update started (${targetLabel}).`, "success");
210
+ if (updateTarget === "telegram") {
211
+ btn.disabled = false;
212
+ btn.textContent = originalText;
213
+ return;
214
+ }
215
+ document.body.style.pointerEvents = "none";
216
+ setTimeout(() => {
217
+ const url = new URL(window.location.href);
218
+ url.searchParams.set("v", String(Date.now()));
219
+ window.location.replace(url.toString());
220
+ }, 8000);
221
+ }
222
+ catch (err) {
223
+ flash(err.message || "Update failed", "error");
224
+ btn.disabled = false;
225
+ btn.textContent = originalText;
226
+ }
227
+ }
228
+ let repoSettingsCloseModal = null;
229
+ function hideRepoSettingsModal() {
230
+ if (repoSettingsCloseModal) {
231
+ const close = repoSettingsCloseModal;
232
+ repoSettingsCloseModal = null;
233
+ close();
234
+ }
235
+ }
236
+ export function openRepoSettings(triggerEl) {
237
+ const modal = document.getElementById("repo-settings-modal");
238
+ const closeBtn = document.getElementById("repo-settings-close");
239
+ const updateBtn = document.getElementById("repo-update-btn");
240
+ if (!modal)
241
+ return;
242
+ hideRepoSettingsModal();
243
+ repoSettingsCloseModal = openModal(modal, {
244
+ initialFocus: closeBtn || updateBtn || modal,
245
+ returnFocusTo: triggerEl || null,
246
+ onRequestClose: hideRepoSettingsModal,
247
+ });
248
+ // Trigger settings refresh when modal opens
249
+ const { refreshSettings } = window.__CAR_SETTINGS || {};
250
+ if (typeof refreshSettings === "function") {
251
+ refreshSettings();
252
+ }
253
+ }
254
+ function initRepoSettingsModal() {
255
+ const settingsBtn = document.getElementById("repo-settings");
256
+ const closeBtn = document.getElementById("repo-settings-close");
257
+ const updateBtn = document.getElementById("repo-update-btn");
258
+ const updateTarget = document.getElementById("repo-update-target");
259
+ // If the gear button exists in HTML, wire it up (backwards compatibility)
260
+ if (settingsBtn) {
261
+ settingsBtn.addEventListener("click", () => {
262
+ openRepoSettings(settingsBtn);
263
+ });
264
+ }
265
+ if (closeBtn) {
266
+ closeBtn.addEventListener("click", () => {
267
+ hideRepoSettingsModal();
268
+ });
269
+ }
270
+ if (updateBtn) {
271
+ updateBtn.addEventListener("click", () => handleSystemUpdate("repo-update-btn", updateTarget ? updateTarget.id : null));
272
+ }
273
+ }
@@ -0,0 +1,52 @@
1
+ // GENERATED FILE - do not edit directly. Source: static_src/
2
+ /**
3
+ * Create a signature-aware refresh helper that only calls render when data changes.
4
+ *
5
+ * Usage:
6
+ * const smartRefresh = createSmartRefresh({
7
+ * getSignature: (payload) => payload.items.map((i) => i.id).join("|"),
8
+ * render: (payload) => renderList(payload.items),
9
+ * });
10
+ *
11
+ * await smartRefresh.refresh(loadItems, { reason: "background" });
12
+ */
13
+ export function createSmartRefresh(options) {
14
+ let lastSignature = options.initialSignature ?? null;
15
+ const refresh = async (load, request = {}) => {
16
+ const payload = await load();
17
+ const nextSignature = options.getSignature(payload);
18
+ const previousSignature = lastSignature;
19
+ const isInitial = previousSignature === null || request.reason === "initial";
20
+ const isForced = Boolean(request.force);
21
+ const updated = isForced || isInitial || nextSignature !== previousSignature;
22
+ const reason = request.reason ?? (isInitial ? "initial" : "background");
23
+ const ctx = {
24
+ isInitial,
25
+ isForced,
26
+ previousSignature,
27
+ nextSignature,
28
+ updated,
29
+ reason,
30
+ };
31
+ if (updated) {
32
+ lastSignature = nextSignature;
33
+ await options.render(payload, ctx);
34
+ }
35
+ else if (options.onSkip) {
36
+ await options.onSkip(payload, ctx);
37
+ }
38
+ return {
39
+ updated,
40
+ signature: nextSignature,
41
+ previousSignature,
42
+ reason,
43
+ };
44
+ };
45
+ return {
46
+ refresh,
47
+ reset: () => {
48
+ lastSignature = null;
49
+ },
50
+ getSignature: () => lastSignature,
51
+ };
52
+ }