codex-autorunner 0.1.2__py3-none-any.whl → 1.0.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.
- codex_autorunner/__main__.py +4 -0
- codex_autorunner/agents/opencode/client.py +68 -35
- codex_autorunner/agents/opencode/logging.py +21 -5
- codex_autorunner/agents/opencode/run_prompt.py +1 -0
- codex_autorunner/agents/opencode/runtime.py +118 -30
- codex_autorunner/agents/opencode/supervisor.py +36 -48
- codex_autorunner/agents/registry.py +136 -8
- codex_autorunner/api.py +25 -0
- codex_autorunner/bootstrap.py +16 -35
- codex_autorunner/cli.py +157 -139
- codex_autorunner/core/about_car.py +44 -32
- codex_autorunner/core/adapter_utils.py +21 -0
- codex_autorunner/core/app_server_logging.py +7 -3
- codex_autorunner/core/app_server_prompts.py +27 -260
- codex_autorunner/core/app_server_threads.py +15 -26
- codex_autorunner/core/codex_runner.py +6 -0
- codex_autorunner/core/config.py +390 -100
- codex_autorunner/core/docs.py +10 -2
- codex_autorunner/core/drafts.py +82 -0
- codex_autorunner/core/engine.py +278 -262
- codex_autorunner/core/flows/__init__.py +25 -0
- codex_autorunner/core/flows/controller.py +178 -0
- codex_autorunner/core/flows/definition.py +82 -0
- codex_autorunner/core/flows/models.py +75 -0
- codex_autorunner/core/flows/runtime.py +351 -0
- codex_autorunner/core/flows/store.py +485 -0
- codex_autorunner/core/flows/transition.py +133 -0
- codex_autorunner/core/flows/worker_process.py +242 -0
- codex_autorunner/core/hub.py +15 -9
- codex_autorunner/core/locks.py +4 -0
- codex_autorunner/core/prompt.py +15 -7
- codex_autorunner/core/redaction.py +29 -0
- codex_autorunner/core/review_context.py +5 -8
- codex_autorunner/core/run_index.py +6 -0
- codex_autorunner/core/runner_process.py +5 -2
- codex_autorunner/core/state.py +0 -88
- codex_autorunner/core/static_assets.py +55 -0
- codex_autorunner/core/supervisor_utils.py +67 -0
- codex_autorunner/core/update.py +20 -11
- codex_autorunner/core/update_runner.py +2 -0
- codex_autorunner/core/utils.py +29 -2
- codex_autorunner/discovery.py +2 -4
- codex_autorunner/flows/ticket_flow/__init__.py +3 -0
- codex_autorunner/flows/ticket_flow/definition.py +91 -0
- codex_autorunner/integrations/agents/__init__.py +27 -0
- codex_autorunner/integrations/agents/agent_backend.py +142 -0
- codex_autorunner/integrations/agents/codex_backend.py +307 -0
- codex_autorunner/integrations/agents/opencode_backend.py +325 -0
- codex_autorunner/integrations/agents/run_event.py +71 -0
- codex_autorunner/integrations/app_server/client.py +576 -92
- codex_autorunner/integrations/app_server/supervisor.py +59 -33
- codex_autorunner/integrations/telegram/adapter.py +141 -167
- codex_autorunner/integrations/telegram/api_schemas.py +120 -0
- codex_autorunner/integrations/telegram/config.py +175 -0
- codex_autorunner/integrations/telegram/constants.py +16 -1
- codex_autorunner/integrations/telegram/dispatch.py +17 -0
- codex_autorunner/integrations/telegram/doctor.py +47 -0
- codex_autorunner/integrations/telegram/handlers/callbacks.py +0 -4
- codex_autorunner/integrations/telegram/handlers/commands/__init__.py +2 -0
- codex_autorunner/integrations/telegram/handlers/commands/execution.py +53 -57
- codex_autorunner/integrations/telegram/handlers/commands/files.py +2 -6
- codex_autorunner/integrations/telegram/handlers/commands/flows.py +227 -0
- codex_autorunner/integrations/telegram/handlers/commands/formatting.py +1 -1
- codex_autorunner/integrations/telegram/handlers/commands/github.py +41 -582
- codex_autorunner/integrations/telegram/handlers/commands/workspace.py +8 -8
- codex_autorunner/integrations/telegram/handlers/commands_runtime.py +133 -475
- codex_autorunner/integrations/telegram/handlers/commands_spec.py +11 -4
- codex_autorunner/integrations/telegram/handlers/messages.py +120 -9
- codex_autorunner/integrations/telegram/helpers.py +88 -16
- codex_autorunner/integrations/telegram/outbox.py +208 -37
- codex_autorunner/integrations/telegram/progress_stream.py +3 -10
- codex_autorunner/integrations/telegram/service.py +214 -40
- codex_autorunner/integrations/telegram/state.py +100 -2
- codex_autorunner/integrations/telegram/ticket_flow_bridge.py +322 -0
- codex_autorunner/integrations/telegram/transport.py +36 -3
- codex_autorunner/integrations/telegram/trigger_mode.py +53 -0
- codex_autorunner/manifest.py +2 -0
- codex_autorunner/plugin_api.py +22 -0
- codex_autorunner/routes/__init__.py +23 -14
- codex_autorunner/routes/analytics.py +239 -0
- codex_autorunner/routes/base.py +81 -109
- codex_autorunner/routes/file_chat.py +836 -0
- codex_autorunner/routes/flows.py +980 -0
- codex_autorunner/routes/messages.py +459 -0
- codex_autorunner/routes/system.py +6 -1
- codex_autorunner/routes/usage.py +87 -0
- codex_autorunner/routes/workspace.py +271 -0
- codex_autorunner/server.py +2 -1
- codex_autorunner/static/agentControls.js +1 -0
- codex_autorunner/static/agentEvents.js +248 -0
- codex_autorunner/static/app.js +25 -22
- codex_autorunner/static/autoRefresh.js +29 -1
- codex_autorunner/static/bootstrap.js +1 -0
- codex_autorunner/static/bus.js +1 -0
- codex_autorunner/static/cache.js +1 -0
- codex_autorunner/static/constants.js +20 -4
- codex_autorunner/static/dashboard.js +162 -196
- codex_autorunner/static/diffRenderer.js +37 -0
- codex_autorunner/static/docChatCore.js +324 -0
- codex_autorunner/static/docChatStorage.js +65 -0
- codex_autorunner/static/docChatVoice.js +65 -0
- codex_autorunner/static/docEditor.js +133 -0
- codex_autorunner/static/env.js +1 -0
- codex_autorunner/static/eventSummarizer.js +166 -0
- codex_autorunner/static/fileChat.js +182 -0
- codex_autorunner/static/health.js +155 -0
- codex_autorunner/static/hub.js +41 -118
- codex_autorunner/static/index.html +787 -858
- codex_autorunner/static/liveUpdates.js +1 -0
- codex_autorunner/static/loader.js +1 -0
- codex_autorunner/static/messages.js +470 -0
- codex_autorunner/static/mobileCompact.js +2 -1
- codex_autorunner/static/settings.js +24 -211
- codex_autorunner/static/styles.css +7567 -3865
- codex_autorunner/static/tabs.js +28 -5
- codex_autorunner/static/terminal.js +14 -0
- codex_autorunner/static/terminalManager.js +34 -59
- codex_autorunner/static/ticketChatActions.js +333 -0
- codex_autorunner/static/ticketChatEvents.js +16 -0
- codex_autorunner/static/ticketChatStorage.js +16 -0
- codex_autorunner/static/ticketChatStream.js +264 -0
- codex_autorunner/static/ticketEditor.js +750 -0
- codex_autorunner/static/ticketVoice.js +9 -0
- codex_autorunner/static/tickets.js +1315 -0
- codex_autorunner/static/utils.js +32 -3
- codex_autorunner/static/voice.js +1 -0
- codex_autorunner/static/workspace.js +672 -0
- codex_autorunner/static/workspaceApi.js +53 -0
- codex_autorunner/static/workspaceFileBrowser.js +504 -0
- codex_autorunner/tickets/__init__.py +20 -0
- codex_autorunner/tickets/agent_pool.py +377 -0
- codex_autorunner/tickets/files.py +85 -0
- codex_autorunner/tickets/frontmatter.py +55 -0
- codex_autorunner/tickets/lint.py +102 -0
- codex_autorunner/tickets/models.py +95 -0
- codex_autorunner/tickets/outbox.py +232 -0
- codex_autorunner/tickets/replies.py +179 -0
- codex_autorunner/tickets/runner.py +823 -0
- codex_autorunner/tickets/spec_ingest.py +77 -0
- codex_autorunner/web/app.py +269 -91
- codex_autorunner/web/middleware.py +3 -4
- codex_autorunner/web/schemas.py +89 -109
- codex_autorunner/web/static_assets.py +1 -44
- codex_autorunner/workspace/__init__.py +40 -0
- codex_autorunner/workspace/paths.py +319 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/METADATA +18 -21
- codex_autorunner-1.0.0.dist-info/RECORD +251 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/WHEEL +1 -1
- codex_autorunner/agents/execution/policy.py +0 -292
- codex_autorunner/agents/factory.py +0 -52
- codex_autorunner/agents/orchestrator.py +0 -358
- codex_autorunner/core/doc_chat.py +0 -1446
- codex_autorunner/core/snapshot.py +0 -580
- codex_autorunner/integrations/github/chatops.py +0 -268
- codex_autorunner/integrations/github/pr_flow.py +0 -1314
- codex_autorunner/routes/docs.py +0 -381
- codex_autorunner/routes/github.py +0 -327
- codex_autorunner/routes/runs.py +0 -250
- codex_autorunner/spec_ingest.py +0 -812
- codex_autorunner/static/docChatActions.js +0 -287
- codex_autorunner/static/docChatEvents.js +0 -300
- codex_autorunner/static/docChatRender.js +0 -205
- codex_autorunner/static/docChatStream.js +0 -361
- codex_autorunner/static/docs.js +0 -20
- codex_autorunner/static/docsClipboard.js +0 -69
- codex_autorunner/static/docsCrud.js +0 -257
- codex_autorunner/static/docsDocUpdates.js +0 -62
- codex_autorunner/static/docsDrafts.js +0 -16
- codex_autorunner/static/docsElements.js +0 -69
- codex_autorunner/static/docsInit.js +0 -285
- codex_autorunner/static/docsParse.js +0 -160
- codex_autorunner/static/docsSnapshot.js +0 -87
- codex_autorunner/static/docsSpecIngest.js +0 -263
- codex_autorunner/static/docsState.js +0 -127
- codex_autorunner/static/docsThreadRegistry.js +0 -44
- codex_autorunner/static/docsUi.js +0 -153
- codex_autorunner/static/docsVoice.js +0 -56
- codex_autorunner/static/github.js +0 -504
- codex_autorunner/static/logs.js +0 -678
- codex_autorunner/static/review.js +0 -157
- codex_autorunner/static/runs.js +0 -418
- codex_autorunner/static/snapshot.js +0 -124
- codex_autorunner/static/state.js +0 -94
- codex_autorunner/static/todoPreview.js +0 -27
- codex_autorunner/workspace.py +0 -16
- codex_autorunner-0.1.2.dist-info/RECORD +0 -222
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/entry_points.txt +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
1
2
|
/**
|
|
2
3
|
* Auto-refresh utility for managing periodic data fetching.
|
|
3
4
|
*
|
|
@@ -16,6 +17,9 @@ const refreshers = new Map();
|
|
|
16
17
|
let activeTab = null;
|
|
17
18
|
// Track page visibility
|
|
18
19
|
let pageVisible = true;
|
|
20
|
+
// Global enable/disable toggle (e.g., when repo server is offline)
|
|
21
|
+
let globallyEnabled = true;
|
|
22
|
+
let globalPauseReason = null;
|
|
19
23
|
/**
|
|
20
24
|
* Register a component for auto-refresh.
|
|
21
25
|
*
|
|
@@ -43,7 +47,7 @@ export function registerAutoRefresh(id, options) {
|
|
|
43
47
|
// Start timer if applicable
|
|
44
48
|
maybeStartTimer(id, refresher);
|
|
45
49
|
// Immediate refresh if requested
|
|
46
|
-
if (immediate) {
|
|
50
|
+
if (immediate && globallyEnabled) {
|
|
47
51
|
doRefresh(id, refresher);
|
|
48
52
|
}
|
|
49
53
|
return () => unregisterAutoRefresh(id);
|
|
@@ -69,10 +73,32 @@ export function triggerRefresh(id) {
|
|
|
69
73
|
doRefresh(id, refresher);
|
|
70
74
|
}
|
|
71
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Globally enable/disable all auto-refresh timers.
|
|
78
|
+
*/
|
|
79
|
+
export function setAutoRefreshEnabled(enabled, reason) {
|
|
80
|
+
globallyEnabled = enabled;
|
|
81
|
+
globalPauseReason = enabled ? null : reason || null;
|
|
82
|
+
refreshers.forEach((refresher, id) => {
|
|
83
|
+
if (!enabled) {
|
|
84
|
+
if (refresher.timerId) {
|
|
85
|
+
clearInterval(refresher.timerId);
|
|
86
|
+
refresher.timerId = null;
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
maybeStartTimer(id, refresher);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
export function getAutoRefreshPauseReason() {
|
|
94
|
+
return globalPauseReason;
|
|
95
|
+
}
|
|
72
96
|
/**
|
|
73
97
|
* Check if conditions allow refresh for this refresher.
|
|
74
98
|
*/
|
|
75
99
|
function canRefresh(refresher) {
|
|
100
|
+
if (!globallyEnabled)
|
|
101
|
+
return false;
|
|
76
102
|
// Don't refresh if page is hidden
|
|
77
103
|
if (!pageVisible)
|
|
78
104
|
return false;
|
|
@@ -112,6 +138,8 @@ function maybeStartTimer(id, refresher) {
|
|
|
112
138
|
refresher.timerId = null;
|
|
113
139
|
}
|
|
114
140
|
// Only start timer if page is visible and tab is active (or global)
|
|
141
|
+
if (!globallyEnabled)
|
|
142
|
+
return;
|
|
115
143
|
if (!pageVisible)
|
|
116
144
|
return;
|
|
117
145
|
if (refresher.tabId && refresher.tabId !== activeTab)
|
codex_autorunner/static/bus.js
CHANGED
codex_autorunner/static/cache.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
1
2
|
export const CONSTANTS = {
|
|
2
3
|
UI: {
|
|
3
|
-
TOAST_DURATION:
|
|
4
|
+
TOAST_DURATION: 5000,
|
|
4
5
|
POLLING_INTERVAL: 15000,
|
|
5
6
|
LOG_SCROLL_THRESHOLD: 50,
|
|
6
7
|
MAX_LOG_LINES_IN_DOM: 2000,
|
|
@@ -36,11 +37,26 @@ export const CONSTANTS = {
|
|
|
36
37
|
},
|
|
37
38
|
PROMPTS: {
|
|
38
39
|
VOICE_TRANSCRIPT_DISCLAIMER: "Note: transcribed from user voice. If confusing or possibly inaccurate and you cannot infer the intention please clarify before proceeding.",
|
|
40
|
+
CAR_CONTEXT_HINT: "Context: read .codex-autorunner/ABOUT_CAR.md for repo-specific rules.",
|
|
41
|
+
},
|
|
42
|
+
KEYWORDS: {
|
|
43
|
+
CAR_CONTEXT: [
|
|
44
|
+
"car",
|
|
45
|
+
"codex",
|
|
46
|
+
"spec",
|
|
47
|
+
"autorunner",
|
|
48
|
+
"workspace",
|
|
49
|
+
"ticket",
|
|
50
|
+
"tickets",
|
|
51
|
+
"context",
|
|
52
|
+
"decision",
|
|
53
|
+
"decisions",
|
|
54
|
+
"handoff",
|
|
55
|
+
"dispatch",
|
|
56
|
+
"inbox",
|
|
57
|
+
],
|
|
39
58
|
},
|
|
40
59
|
API: {
|
|
41
|
-
STATE_ENDPOINT: "/api/state",
|
|
42
|
-
LOGS_ENDPOINT: "/api/logs",
|
|
43
|
-
DOCS_ENDPOINT: "/api/docs",
|
|
44
60
|
TERMINAL_ENDPOINT: "/api/terminal",
|
|
45
61
|
TERMINAL_IMAGE_ENDPOINT: "/api/terminal/image",
|
|
46
62
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
2
|
+
import { api, flash, confirmModal, openModal, statusPill } from "./utils.js";
|
|
3
3
|
import { saveToCache, loadFromCache } from "./cache.js";
|
|
4
|
-
import { renderTodoPreview } from "./todoPreview.js";
|
|
5
|
-
import { loadState, startRun, stopRun, killRun, resetRun, clearLock, startStatePolling, } from "./state.js";
|
|
6
4
|
import { registerAutoRefresh } from "./autoRefresh.js";
|
|
7
5
|
import { CONSTANTS } from "./constants.js";
|
|
8
|
-
import { initAgentControls } from "./agentControls.js";
|
|
9
|
-
import { initReview } from "./review.js";
|
|
10
6
|
const UPDATE_STATUS_SEEN_KEY = "car_update_status_seen";
|
|
11
|
-
|
|
7
|
+
const ANALYTICS_SUMMARY_CACHE_KEY = "analytics-summary";
|
|
12
8
|
const usageChartState = {
|
|
13
9
|
segment: "none",
|
|
14
10
|
bucket: "day",
|
|
@@ -16,120 +12,6 @@ const usageChartState = {
|
|
|
16
12
|
};
|
|
17
13
|
let usageSeriesRetryTimer = null;
|
|
18
14
|
let usageSummaryRetryTimer = null;
|
|
19
|
-
function renderState(state) {
|
|
20
|
-
if (!state)
|
|
21
|
-
return;
|
|
22
|
-
saveToCache("state", state);
|
|
23
|
-
const statusEl = document.getElementById("runner-status");
|
|
24
|
-
if (statusEl) {
|
|
25
|
-
statusPill(statusEl, state.status);
|
|
26
|
-
}
|
|
27
|
-
const lastRunIdEl = document.getElementById("last-run-id");
|
|
28
|
-
if (lastRunIdEl) {
|
|
29
|
-
lastRunIdEl.textContent = String(state.last_run_id ?? "–");
|
|
30
|
-
}
|
|
31
|
-
const lastExitCodeEl = document.getElementById("last-exit-code");
|
|
32
|
-
if (lastExitCodeEl) {
|
|
33
|
-
lastExitCodeEl.textContent = String(state.last_exit_code ?? "–");
|
|
34
|
-
}
|
|
35
|
-
const lastStartEl = document.getElementById("last-start");
|
|
36
|
-
if (lastStartEl) {
|
|
37
|
-
lastStartEl.textContent = state.last_run_started_at ?? "–";
|
|
38
|
-
}
|
|
39
|
-
const lastFinishEl = document.getElementById("last-finish");
|
|
40
|
-
if (lastFinishEl) {
|
|
41
|
-
lastFinishEl.textContent = state.last_run_finished_at ?? "–";
|
|
42
|
-
}
|
|
43
|
-
const todoCountEl = document.getElementById("todo-count");
|
|
44
|
-
if (todoCountEl) {
|
|
45
|
-
todoCountEl.textContent = String(state.outstanding_count ?? "–");
|
|
46
|
-
}
|
|
47
|
-
const doneCountEl = document.getElementById("done-count");
|
|
48
|
-
if (doneCountEl) {
|
|
49
|
-
doneCountEl.textContent = String(state.done_count ?? "–");
|
|
50
|
-
}
|
|
51
|
-
const runnerPidEl = document.getElementById("runner-pid");
|
|
52
|
-
if (runnerPidEl) {
|
|
53
|
-
runnerPidEl.textContent = `Runner pid: ${state.runner_pid ?? "–"}`;
|
|
54
|
-
}
|
|
55
|
-
const summaryBtn = document.getElementById("open-summary");
|
|
56
|
-
if (summaryBtn) {
|
|
57
|
-
const done = Number(state.outstanding_count ?? NaN) === 0;
|
|
58
|
-
summaryBtn.classList.toggle("hidden", !done);
|
|
59
|
-
}
|
|
60
|
-
const clearLockBtn = document.getElementById("clear-lock");
|
|
61
|
-
if (clearLockBtn) {
|
|
62
|
-
const freeable = Boolean(state.lock_freeable);
|
|
63
|
-
clearLockBtn.classList.toggle("hidden", !freeable);
|
|
64
|
-
if (state.lock_freeable_reason) {
|
|
65
|
-
clearLockBtn.title = state.lock_freeable_reason;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
const status = state.status || "idle";
|
|
69
|
-
const startBtn = document.getElementById("start-run");
|
|
70
|
-
const stopBtn = document.getElementById("stop-run");
|
|
71
|
-
const killBtn = document.getElementById("kill-run");
|
|
72
|
-
const resetBtn = document.getElementById("reset-runner");
|
|
73
|
-
if (status === "idle" || status === "stopped" || status === "completed") {
|
|
74
|
-
if (startBtn)
|
|
75
|
-
startBtn.classList.remove("hidden");
|
|
76
|
-
if (stopBtn)
|
|
77
|
-
stopBtn.classList.add("hidden");
|
|
78
|
-
if (killBtn)
|
|
79
|
-
killBtn.classList.add("hidden");
|
|
80
|
-
if (resetBtn)
|
|
81
|
-
resetBtn.classList.remove("hidden");
|
|
82
|
-
}
|
|
83
|
-
else if (status === "running") {
|
|
84
|
-
if (startBtn)
|
|
85
|
-
startBtn.classList.add("hidden");
|
|
86
|
-
if (stopBtn)
|
|
87
|
-
stopBtn.classList.remove("hidden");
|
|
88
|
-
if (killBtn)
|
|
89
|
-
killBtn.classList.remove("hidden");
|
|
90
|
-
if (resetBtn)
|
|
91
|
-
resetBtn.classList.add("hidden");
|
|
92
|
-
}
|
|
93
|
-
else if (status === "error") {
|
|
94
|
-
if (startBtn)
|
|
95
|
-
startBtn.classList.remove("hidden");
|
|
96
|
-
if (stopBtn)
|
|
97
|
-
stopBtn.classList.add("hidden");
|
|
98
|
-
if (killBtn)
|
|
99
|
-
killBtn.classList.remove("hidden");
|
|
100
|
-
if (resetBtn)
|
|
101
|
-
resetBtn.classList.remove("hidden");
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function updateTodoPreview(content) {
|
|
105
|
-
renderTodoPreview(content || "");
|
|
106
|
-
if (content !== undefined) {
|
|
107
|
-
saveToCache("todo-doc", content || "");
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function handleDocsEvent(payload) {
|
|
111
|
-
if (!payload)
|
|
112
|
-
return;
|
|
113
|
-
if (payload.kind === "todo") {
|
|
114
|
-
updateTodoPreview(payload.content || "");
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
if (typeof payload.todo === "string") {
|
|
118
|
-
updateTodoPreview(payload.todo);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
async function loadTodoPreview(options = {}) {
|
|
122
|
-
const { silent = false } = options;
|
|
123
|
-
try {
|
|
124
|
-
const data = await api("/api/docs");
|
|
125
|
-
updateTodoPreview(data?.todo || "");
|
|
126
|
-
}
|
|
127
|
-
catch (err) {
|
|
128
|
-
if (!silent) {
|
|
129
|
-
flash(err.message || "Failed to load TODO preview", "error");
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
15
|
function setUsageLoading(loading) {
|
|
134
16
|
const btn = document.getElementById("usage-refresh");
|
|
135
17
|
if (!btn)
|
|
@@ -226,6 +108,144 @@ function renderUsage(data) {
|
|
|
226
108
|
if (metaEl)
|
|
227
109
|
metaEl.textContent = codexHome;
|
|
228
110
|
}
|
|
111
|
+
async function loadTicketAnalytics() {
|
|
112
|
+
try {
|
|
113
|
+
const data = (await api("/api/analytics/summary"));
|
|
114
|
+
saveToCache(ANALYTICS_SUMMARY_CACHE_KEY, data);
|
|
115
|
+
renderTicketAnalytics(data);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const cached = loadFromCache(ANALYTICS_SUMMARY_CACHE_KEY);
|
|
119
|
+
if (cached)
|
|
120
|
+
renderTicketAnalytics(cached);
|
|
121
|
+
flash(err.message || "Failed to load analytics", "error");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function formatDuration(seconds) {
|
|
125
|
+
if (seconds === null || Number.isNaN(seconds))
|
|
126
|
+
return "–";
|
|
127
|
+
if (seconds < 60)
|
|
128
|
+
return `${Math.round(seconds)}s`;
|
|
129
|
+
const mins = seconds / 60;
|
|
130
|
+
if (mins < 60)
|
|
131
|
+
return `${Math.round(mins)}m`;
|
|
132
|
+
const hours = mins / 60;
|
|
133
|
+
return `${hours.toFixed(1)}h`;
|
|
134
|
+
}
|
|
135
|
+
function renderTicketAnalytics(data) {
|
|
136
|
+
const run = data?.run;
|
|
137
|
+
const tickets = data?.tickets;
|
|
138
|
+
const turns = data?.turns;
|
|
139
|
+
const agent = data?.agent;
|
|
140
|
+
const statusEl = document.getElementById("runner-status");
|
|
141
|
+
if (statusEl && run) {
|
|
142
|
+
statusPill(statusEl, run.status || "idle");
|
|
143
|
+
statusEl.textContent = run.status || "idle";
|
|
144
|
+
}
|
|
145
|
+
const lastStart = document.getElementById("last-start");
|
|
146
|
+
const lastFinish = document.getElementById("last-finish");
|
|
147
|
+
const lastDuration = document.getElementById("last-duration");
|
|
148
|
+
const todoCount = document.getElementById("todo-count");
|
|
149
|
+
const doneCount = document.getElementById("done-count");
|
|
150
|
+
const ticketActive = document.getElementById("ticket-active");
|
|
151
|
+
const ticketTurns = document.getElementById("ticket-turns");
|
|
152
|
+
const totalTurns = document.getElementById("total-turns");
|
|
153
|
+
const dispatchesEl = document.getElementById("message-dispatches");
|
|
154
|
+
const repliesEl = document.getElementById("message-replies");
|
|
155
|
+
const runIdEl = document.getElementById("last-run-id");
|
|
156
|
+
if (lastStart)
|
|
157
|
+
lastStart.textContent = formatIso(run?.started_at || null);
|
|
158
|
+
if (lastFinish)
|
|
159
|
+
lastFinish.textContent = formatIso(run?.finished_at || null);
|
|
160
|
+
if (lastDuration)
|
|
161
|
+
lastDuration.textContent = formatDuration(run?.duration_seconds ?? null);
|
|
162
|
+
if (todoCount)
|
|
163
|
+
todoCount.textContent = tickets ? String(tickets.todo_count) : "–";
|
|
164
|
+
if (doneCount)
|
|
165
|
+
doneCount.textContent = tickets ? String(tickets.done_count) : "–";
|
|
166
|
+
if (ticketActive) {
|
|
167
|
+
const ticket = tickets?.current_ticket || null;
|
|
168
|
+
ticketActive.textContent = ticket ? ticket.split("/").pop() || ticket : "–";
|
|
169
|
+
}
|
|
170
|
+
if (ticketTurns)
|
|
171
|
+
ticketTurns.textContent = turns?.current_ticket != null ? String(turns.current_ticket) : "–";
|
|
172
|
+
if (totalTurns)
|
|
173
|
+
totalTurns.textContent = turns?.total != null ? String(turns.total) : "–";
|
|
174
|
+
const dispatchCount = turns?.dispatches ?? 0;
|
|
175
|
+
if (dispatchesEl)
|
|
176
|
+
dispatchesEl.textContent = String(dispatchCount);
|
|
177
|
+
if (repliesEl)
|
|
178
|
+
repliesEl.textContent = turns?.replies != null ? String(turns.replies) : "0";
|
|
179
|
+
if (runIdEl)
|
|
180
|
+
runIdEl.textContent = run?.short_id || run?.id || "–";
|
|
181
|
+
// Agent chip (optional future use)
|
|
182
|
+
const agentEl = document.getElementById("ticket-agent");
|
|
183
|
+
if (agentEl) {
|
|
184
|
+
agentEl.textContent = agent?.id || "–";
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async function loadRunHistory() {
|
|
188
|
+
try {
|
|
189
|
+
const runs = (await api("/api/flows/runs?flow_type=ticket_flow"));
|
|
190
|
+
const runHistory = Array.isArray(runs) ? runs.slice(0, 10) : [];
|
|
191
|
+
renderRunHistory(runHistory);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
flash(err.message || "Failed to load run history", "error");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function formatIso(iso) {
|
|
198
|
+
if (!iso)
|
|
199
|
+
return "–";
|
|
200
|
+
const dt = new Date(iso);
|
|
201
|
+
if (Number.isNaN(dt.getTime()))
|
|
202
|
+
return iso;
|
|
203
|
+
return dt.toLocaleString();
|
|
204
|
+
}
|
|
205
|
+
function calcDurationFromRun(run) {
|
|
206
|
+
const started = run.started_at;
|
|
207
|
+
const finished = run.finished_at;
|
|
208
|
+
if (!started)
|
|
209
|
+
return "–";
|
|
210
|
+
const start = new Date(started).getTime();
|
|
211
|
+
const end = finished && !Number.isNaN(new Date(finished).getTime())
|
|
212
|
+
? new Date(finished).getTime()
|
|
213
|
+
: Date.now();
|
|
214
|
+
if (Number.isNaN(start) || Number.isNaN(end))
|
|
215
|
+
return "–";
|
|
216
|
+
return formatDuration((end - start) / 1000);
|
|
217
|
+
}
|
|
218
|
+
function renderRunHistory(runs) {
|
|
219
|
+
const container = document.getElementById("run-history-list");
|
|
220
|
+
if (!container)
|
|
221
|
+
return;
|
|
222
|
+
if (!runs || !runs.length) {
|
|
223
|
+
container.innerHTML = '<div class="muted">No runs yet.</div>';
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const items = runs.map((run) => {
|
|
227
|
+
const shortId = run.id ? run.id.split("-")[0] : "–";
|
|
228
|
+
const status = run.status || "unknown";
|
|
229
|
+
const duration = calcDurationFromRun(run);
|
|
230
|
+
const started = formatIso(run.started_at);
|
|
231
|
+
const current = run.current_step || "–";
|
|
232
|
+
return `
|
|
233
|
+
<div class="run-history-row">
|
|
234
|
+
<div class="run-history-id">${shortId}</div>
|
|
235
|
+
<div class="run-history-status">${status}</div>
|
|
236
|
+
<div class="run-history-duration">${duration}</div>
|
|
237
|
+
<div class="run-history-start">${started}</div>
|
|
238
|
+
<div class="run-history-step">${current}</div>
|
|
239
|
+
</div>
|
|
240
|
+
`;
|
|
241
|
+
});
|
|
242
|
+
container.innerHTML = `
|
|
243
|
+
<div class="run-history-head">
|
|
244
|
+
<div>ID</div><div>Status</div><div>Duration</div><div>Started</div><div>Step</div>
|
|
245
|
+
</div>
|
|
246
|
+
${items.join("")}
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
229
249
|
function buildUsageSeriesQuery() {
|
|
230
250
|
const params = new URLSearchParams();
|
|
231
251
|
const now = new Date();
|
|
@@ -676,97 +696,43 @@ function bindAction(buttonId, action) {
|
|
|
676
696
|
}
|
|
677
697
|
});
|
|
678
698
|
}
|
|
679
|
-
function isDocsReady() {
|
|
680
|
-
return document.body?.dataset?.docsReady === "true";
|
|
681
|
-
}
|
|
682
|
-
function openSummaryDoc() {
|
|
683
|
-
const summaryChip = document.querySelector('.chip[data-doc="summary"]');
|
|
684
|
-
if (summaryChip)
|
|
685
|
-
summaryChip.click();
|
|
686
|
-
}
|
|
687
699
|
export function initDashboard() {
|
|
688
700
|
initSettings();
|
|
689
701
|
initUsageChartControls();
|
|
690
|
-
initReview();
|
|
691
|
-
const agentSelect = document.getElementById("dashboard-agent-select");
|
|
692
|
-
const modelSelect = document.getElementById("dashboard-model-select");
|
|
693
|
-
const reasoningSelect = document.getElementById("dashboard-reasoning-select");
|
|
694
|
-
initAgentControls({ agentSelect, modelSelect, reasoningSelect });
|
|
695
|
-
const buildRunOverrides = () => {
|
|
696
|
-
const overrides = {};
|
|
697
|
-
if (agentSelect)
|
|
698
|
-
overrides.agent = agentSelect.value;
|
|
699
|
-
if (modelSelect)
|
|
700
|
-
overrides.model = modelSelect.value;
|
|
701
|
-
if (reasoningSelect)
|
|
702
|
-
overrides.reasoning = reasoningSelect.value;
|
|
703
|
-
return overrides;
|
|
704
|
-
};
|
|
705
|
-
subscribe("state:update", renderState);
|
|
706
|
-
subscribe("todo:invalidate", () => {
|
|
707
|
-
void loadTodoPreview({ silent: true });
|
|
708
|
-
});
|
|
709
|
-
subscribe("docs:updated", handleDocsEvent);
|
|
710
|
-
subscribe("docs:loaded", handleDocsEvent);
|
|
711
|
-
subscribe("docs:ready", () => {
|
|
712
|
-
if (!isDocsReady()) {
|
|
713
|
-
if (document.body) {
|
|
714
|
-
document.body.dataset.docsReady = "true";
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
if (pendingSummaryOpen) {
|
|
718
|
-
pendingSummaryOpen = false;
|
|
719
|
-
openSummaryDoc();
|
|
720
|
-
}
|
|
721
|
-
});
|
|
722
|
-
bindAction("start-run", () => startRun(false, buildRunOverrides()));
|
|
723
|
-
bindAction("stop-run", stopRun);
|
|
724
|
-
bindAction("kill-run", killRun);
|
|
725
|
-
bindAction("clear-lock", clearLock);
|
|
726
|
-
bindAction("reset-runner", async () => {
|
|
727
|
-
const confirmed = await confirmModal("Reset runner? This will clear all logs and reset run ID to 1.");
|
|
728
|
-
if (confirmed)
|
|
729
|
-
await resetRun();
|
|
730
|
-
});
|
|
731
|
-
bindAction("refresh-state", async () => { await loadState(); });
|
|
702
|
+
// initReview(); // Removed - review.ts was deleted
|
|
732
703
|
bindAction("usage-refresh", loadUsage);
|
|
733
|
-
bindAction("refresh
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
704
|
+
bindAction("analytics-refresh", async () => {
|
|
705
|
+
await loadTicketAnalytics();
|
|
706
|
+
await loadRunHistory();
|
|
707
|
+
});
|
|
737
708
|
const cachedUsage = loadFromCache("usage");
|
|
738
709
|
if (cachedUsage)
|
|
739
710
|
renderUsage(cachedUsage);
|
|
740
|
-
const
|
|
741
|
-
if (
|
|
742
|
-
|
|
743
|
-
}
|
|
744
|
-
const summaryBtn = document.getElementById("open-summary");
|
|
745
|
-
if (summaryBtn) {
|
|
746
|
-
summaryBtn.addEventListener("click", () => {
|
|
747
|
-
const docsTab = document.querySelector('.tab[data-target="docs"]');
|
|
748
|
-
if (docsTab)
|
|
749
|
-
docsTab.click();
|
|
750
|
-
if (isDocsReady()) {
|
|
751
|
-
requestAnimationFrame(openSummaryDoc);
|
|
752
|
-
}
|
|
753
|
-
else {
|
|
754
|
-
pendingSummaryOpen = true;
|
|
755
|
-
}
|
|
756
|
-
});
|
|
757
|
-
}
|
|
711
|
+
const cachedAnalytics = loadFromCache(ANALYTICS_SUMMARY_CACHE_KEY);
|
|
712
|
+
if (cachedAnalytics)
|
|
713
|
+
renderTicketAnalytics(cachedAnalytics);
|
|
758
714
|
loadUsage();
|
|
759
|
-
|
|
715
|
+
loadTicketAnalytics();
|
|
716
|
+
loadRunHistory();
|
|
760
717
|
loadVersion();
|
|
761
718
|
checkUpdateStatus();
|
|
762
|
-
startStatePolling();
|
|
763
719
|
registerAutoRefresh("dashboard-usage", {
|
|
764
720
|
callback: async () => { await loadUsage(); },
|
|
765
|
-
tabId: "
|
|
721
|
+
tabId: "analytics",
|
|
766
722
|
interval: CONSTANTS.UI.AUTO_REFRESH_USAGE_INTERVAL,
|
|
767
723
|
refreshOnActivation: true,
|
|
768
724
|
immediate: false,
|
|
769
725
|
});
|
|
726
|
+
registerAutoRefresh("dashboard-analytics", {
|
|
727
|
+
callback: async () => {
|
|
728
|
+
await loadTicketAnalytics();
|
|
729
|
+
await loadRunHistory();
|
|
730
|
+
},
|
|
731
|
+
tabId: "analytics",
|
|
732
|
+
interval: CONSTANTS.UI.AUTO_REFRESH_INTERVAL,
|
|
733
|
+
refreshOnActivation: true,
|
|
734
|
+
immediate: true,
|
|
735
|
+
});
|
|
770
736
|
}
|
|
771
737
|
async function loadVersion() {
|
|
772
738
|
const versionEl = document.getElementById("repo-version");
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
2
|
+
export function renderDiff(patch, container) {
|
|
3
|
+
if (!container)
|
|
4
|
+
return;
|
|
5
|
+
container.innerHTML = "";
|
|
6
|
+
const lines = (patch || "").split("\n");
|
|
7
|
+
if (!lines.length)
|
|
8
|
+
return;
|
|
9
|
+
for (const line of lines) {
|
|
10
|
+
const row = document.createElement("div");
|
|
11
|
+
row.classList.add("diff-line");
|
|
12
|
+
if (line.startsWith("@@")) {
|
|
13
|
+
row.classList.add("diff-hunk");
|
|
14
|
+
}
|
|
15
|
+
else if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
16
|
+
row.classList.add("diff-add");
|
|
17
|
+
}
|
|
18
|
+
else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
19
|
+
row.classList.add("diff-del");
|
|
20
|
+
}
|
|
21
|
+
else if (line.startsWith("---") || line.startsWith("+++")) {
|
|
22
|
+
row.classList.add("diff-file");
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
row.classList.add("diff-ctx");
|
|
26
|
+
}
|
|
27
|
+
const sign = document.createElement("span");
|
|
28
|
+
sign.classList.add("diff-sign");
|
|
29
|
+
sign.textContent = line.charAt(0) || " ";
|
|
30
|
+
const content = document.createElement("span");
|
|
31
|
+
content.classList.add("diff-content");
|
|
32
|
+
content.textContent = line.slice(1);
|
|
33
|
+
row.appendChild(sign);
|
|
34
|
+
row.appendChild(content);
|
|
35
|
+
container.appendChild(row);
|
|
36
|
+
}
|
|
37
|
+
}
|