codex-autorunner 0.1.2__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.
- codex_autorunner/__init__.py +12 -1
- codex_autorunner/__main__.py +4 -0
- codex_autorunner/agents/codex/harness.py +1 -1
- codex_autorunner/agents/opencode/client.py +68 -35
- codex_autorunner/agents/opencode/constants.py +3 -0
- codex_autorunner/agents/opencode/harness.py +6 -1
- codex_autorunner/agents/opencode/logging.py +21 -5
- codex_autorunner/agents/opencode/run_prompt.py +1 -0
- codex_autorunner/agents/opencode/runtime.py +176 -47
- codex_autorunner/agents/opencode/supervisor.py +36 -48
- codex_autorunner/agents/registry.py +155 -8
- codex_autorunner/api.py +25 -0
- codex_autorunner/bootstrap.py +22 -37
- codex_autorunner/cli.py +5 -1156
- codex_autorunner/codex_cli.py +20 -84
- codex_autorunner/core/__init__.py +4 -0
- codex_autorunner/core/about_car.py +49 -32
- codex_autorunner/core/adapter_utils.py +21 -0
- codex_autorunner/core/app_server_ids.py +59 -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 +26 -28
- codex_autorunner/core/app_server_utils.py +165 -0
- codex_autorunner/core/archive.py +349 -0
- codex_autorunner/core/codex_runner.py +12 -2
- codex_autorunner/core/config.py +587 -103
- codex_autorunner/core/docs.py +10 -2
- codex_autorunner/core/drafts.py +136 -0
- codex_autorunner/core/engine.py +1531 -866
- codex_autorunner/core/exceptions.py +4 -0
- codex_autorunner/core/flows/__init__.py +25 -0
- codex_autorunner/core/flows/controller.py +202 -0
- codex_autorunner/core/flows/definition.py +82 -0
- codex_autorunner/core/flows/models.py +88 -0
- codex_autorunner/core/flows/reasons.py +52 -0
- codex_autorunner/core/flows/reconciler.py +131 -0
- codex_autorunner/core/flows/runtime.py +382 -0
- codex_autorunner/core/flows/store.py +568 -0
- codex_autorunner/core/flows/transition.py +138 -0
- codex_autorunner/core/flows/ux_helpers.py +257 -0
- codex_autorunner/core/flows/worker_process.py +242 -0
- codex_autorunner/core/git_utils.py +62 -0
- codex_autorunner/core/hub.py +136 -16
- codex_autorunner/core/locks.py +4 -0
- codex_autorunner/core/notifications.py +14 -2
- codex_autorunner/core/ports/__init__.py +28 -0
- codex_autorunner/core/ports/agent_backend.py +150 -0
- codex_autorunner/core/ports/backend_orchestrator.py +41 -0
- codex_autorunner/core/ports/run_event.py +91 -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/state_roots.py +57 -0
- codex_autorunner/core/supervisor_protocol.py +15 -0
- codex_autorunner/core/supervisor_utils.py +67 -0
- codex_autorunner/core/text_delta_coalescer.py +54 -0
- codex_autorunner/core/ticket_linter_cli.py +201 -0
- codex_autorunner/core/ticket_manager_cli.py +432 -0
- codex_autorunner/core/update.py +24 -16
- codex_autorunner/core/update_paths.py +28 -0
- codex_autorunner/core/update_runner.py +2 -0
- codex_autorunner/core/usage.py +164 -12
- codex_autorunner/core/utils.py +120 -11
- codex_autorunner/discovery.py +2 -4
- codex_autorunner/flows/review/__init__.py +17 -0
- codex_autorunner/{core/review.py → flows/review/service.py} +15 -10
- codex_autorunner/flows/ticket_flow/__init__.py +3 -0
- codex_autorunner/flows/ticket_flow/definition.py +98 -0
- codex_autorunner/integrations/agents/__init__.py +17 -0
- codex_autorunner/integrations/agents/backend_orchestrator.py +284 -0
- codex_autorunner/integrations/agents/codex_adapter.py +90 -0
- codex_autorunner/integrations/agents/codex_backend.py +448 -0
- codex_autorunner/integrations/agents/opencode_adapter.py +108 -0
- codex_autorunner/integrations/agents/opencode_backend.py +598 -0
- codex_autorunner/integrations/agents/runner.py +91 -0
- codex_autorunner/integrations/agents/wiring.py +271 -0
- codex_autorunner/integrations/app_server/client.py +583 -152
- codex_autorunner/integrations/app_server/env.py +2 -107
- codex_autorunner/{core/app_server_events.py → integrations/app_server/event_buffer.py} +15 -8
- codex_autorunner/integrations/app_server/supervisor.py +59 -33
- codex_autorunner/integrations/telegram/adapter.py +204 -165
- codex_autorunner/integrations/telegram/api_schemas.py +120 -0
- codex_autorunner/integrations/telegram/config.py +221 -0
- codex_autorunner/integrations/telegram/constants.py +17 -2
- codex_autorunner/integrations/telegram/dispatch.py +17 -0
- codex_autorunner/integrations/telegram/doctor.py +47 -0
- codex_autorunner/integrations/telegram/handlers/callbacks.py +7 -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 +1364 -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 +137 -478
- codex_autorunner/integrations/telegram/handlers/commands_spec.py +17 -4
- codex_autorunner/integrations/telegram/handlers/messages.py +121 -9
- codex_autorunner/integrations/telegram/handlers/selections.py +61 -1
- codex_autorunner/integrations/telegram/helpers.py +111 -16
- codex_autorunner/integrations/telegram/outbox.py +208 -37
- codex_autorunner/integrations/telegram/progress_stream.py +3 -10
- codex_autorunner/integrations/telegram/service.py +221 -42
- codex_autorunner/integrations/telegram/state.py +100 -2
- codex_autorunner/integrations/telegram/ticket_flow_bridge.py +611 -0
- codex_autorunner/integrations/telegram/transport.py +39 -4
- 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 +37 -67
- codex_autorunner/routes/agents.py +2 -137
- codex_autorunner/routes/analytics.py +3 -0
- codex_autorunner/routes/app_server.py +2 -131
- codex_autorunner/routes/base.py +2 -624
- codex_autorunner/routes/file_chat.py +7 -0
- codex_autorunner/routes/flows.py +7 -0
- codex_autorunner/routes/messages.py +7 -0
- codex_autorunner/routes/repos.py +2 -196
- codex_autorunner/routes/review.py +2 -147
- codex_autorunner/routes/sessions.py +2 -175
- codex_autorunner/routes/settings.py +2 -168
- codex_autorunner/routes/shared.py +2 -275
- codex_autorunner/routes/system.py +4 -188
- codex_autorunner/routes/usage.py +3 -0
- codex_autorunner/routes/voice.py +2 -119
- codex_autorunner/routes/workspace.py +3 -0
- codex_autorunner/server.py +3 -2
- codex_autorunner/static/agentControls.js +41 -11
- codex_autorunner/static/agentEvents.js +248 -0
- codex_autorunner/static/app.js +35 -24
- codex_autorunner/static/archive.js +826 -0
- codex_autorunner/static/archiveApi.js +37 -0
- codex_autorunner/static/autoRefresh.js +36 -8
- 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 +344 -325
- 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 +126 -185
- codex_autorunner/static/index.html +839 -863
- codex_autorunner/static/liveUpdates.js +1 -0
- codex_autorunner/static/loader.js +1 -0
- codex_autorunner/static/messages.js +873 -0
- codex_autorunner/static/mobileCompact.js +2 -1
- codex_autorunner/static/preserve.js +17 -0
- codex_autorunner/static/settings.js +149 -217
- codex_autorunner/static/smartRefresh.js +52 -0
- codex_autorunner/static/styles.css +8850 -3876
- codex_autorunner/static/tabs.js +175 -11
- codex_autorunner/static/terminal.js +32 -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 +844 -0
- codex_autorunner/static/ticketVoice.js +9 -0
- codex_autorunner/static/tickets.js +1988 -0
- codex_autorunner/static/utils.js +43 -3
- codex_autorunner/static/voice.js +1 -0
- codex_autorunner/static/workspace.js +765 -0
- codex_autorunner/static/workspaceApi.js +53 -0
- codex_autorunner/static/workspaceFileBrowser.js +504 -0
- codex_autorunner/surfaces/__init__.py +5 -0
- codex_autorunner/surfaces/cli/__init__.py +6 -0
- codex_autorunner/surfaces/cli/cli.py +1224 -0
- codex_autorunner/surfaces/cli/codex_cli.py +20 -0
- codex_autorunner/surfaces/telegram/__init__.py +3 -0
- codex_autorunner/surfaces/web/__init__.py +1 -0
- codex_autorunner/surfaces/web/app.py +2019 -0
- codex_autorunner/surfaces/web/hub_jobs.py +192 -0
- codex_autorunner/surfaces/web/middleware.py +587 -0
- codex_autorunner/surfaces/web/pty_session.py +370 -0
- codex_autorunner/surfaces/web/review.py +6 -0
- codex_autorunner/surfaces/web/routes/__init__.py +78 -0
- codex_autorunner/surfaces/web/routes/agents.py +138 -0
- codex_autorunner/surfaces/web/routes/analytics.py +277 -0
- codex_autorunner/surfaces/web/routes/app_server.py +132 -0
- codex_autorunner/surfaces/web/routes/archive.py +357 -0
- codex_autorunner/surfaces/web/routes/base.py +615 -0
- codex_autorunner/surfaces/web/routes/file_chat.py +836 -0
- codex_autorunner/surfaces/web/routes/flows.py +1164 -0
- codex_autorunner/surfaces/web/routes/messages.py +459 -0
- codex_autorunner/surfaces/web/routes/repos.py +197 -0
- codex_autorunner/surfaces/web/routes/review.py +148 -0
- codex_autorunner/surfaces/web/routes/sessions.py +176 -0
- codex_autorunner/surfaces/web/routes/settings.py +169 -0
- codex_autorunner/surfaces/web/routes/shared.py +280 -0
- codex_autorunner/surfaces/web/routes/system.py +196 -0
- codex_autorunner/surfaces/web/routes/usage.py +89 -0
- codex_autorunner/surfaces/web/routes/voice.py +120 -0
- codex_autorunner/surfaces/web/routes/workspace.py +271 -0
- codex_autorunner/surfaces/web/runner_manager.py +25 -0
- codex_autorunner/surfaces/web/schemas.py +417 -0
- codex_autorunner/surfaces/web/static_assets.py +490 -0
- codex_autorunner/surfaces/web/static_refresh.py +86 -0
- codex_autorunner/surfaces/web/terminal_sessions.py +78 -0
- codex_autorunner/tickets/__init__.py +27 -0
- codex_autorunner/tickets/agent_pool.py +399 -0
- codex_autorunner/tickets/files.py +89 -0
- codex_autorunner/tickets/frontmatter.py +55 -0
- codex_autorunner/tickets/lint.py +102 -0
- codex_autorunner/tickets/models.py +97 -0
- codex_autorunner/tickets/outbox.py +244 -0
- codex_autorunner/tickets/replies.py +179 -0
- codex_autorunner/tickets/runner.py +881 -0
- codex_autorunner/tickets/spec_ingest.py +77 -0
- codex_autorunner/web/__init__.py +5 -1
- codex_autorunner/web/app.py +2 -1771
- codex_autorunner/web/hub_jobs.py +2 -191
- codex_autorunner/web/middleware.py +2 -587
- codex_autorunner/web/pty_session.py +2 -369
- codex_autorunner/web/runner_manager.py +2 -24
- codex_autorunner/web/schemas.py +2 -396
- codex_autorunner/web/static_assets.py +4 -484
- codex_autorunner/web/static_refresh.py +2 -85
- codex_autorunner/web/terminal_sessions.py +2 -77
- codex_autorunner/workspace/__init__.py +40 -0
- codex_autorunner/workspace/paths.py +335 -0
- codex_autorunner-1.1.0.dist-info/METADATA +154 -0
- codex_autorunner-1.1.0.dist-info/RECORD +308 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.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/METADATA +0 -249
- codex_autorunner-0.1.2.dist-info/RECORD +0 -222
- /codex_autorunner/{routes → surfaces/web/routes}/terminal_images.py +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/entry_points.txt +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
2
|
+
import { api, flash, 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 {
|
|
9
|
-
import {
|
|
6
|
+
import { preserveScroll } from "./preserve.js";
|
|
7
|
+
import { createSmartRefresh } from "./smartRefresh.js";
|
|
10
8
|
const UPDATE_STATUS_SEEN_KEY = "car_update_status_seen";
|
|
11
|
-
|
|
9
|
+
const ANALYTICS_SUMMARY_CACHE_KEY = "analytics-summary";
|
|
12
10
|
const usageChartState = {
|
|
13
11
|
segment: "none",
|
|
14
12
|
bucket: "day",
|
|
@@ -16,119 +14,16 @@ const usageChartState = {
|
|
|
16
14
|
};
|
|
17
15
|
let usageSeriesRetryTimer = null;
|
|
18
16
|
let usageSummaryRetryTimer = null;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
}
|
|
17
|
+
const DASHBOARD_REFRESH_REASONS = ["initial", "background", "manual"];
|
|
18
|
+
function normalizeRefreshReason(reason) {
|
|
19
|
+
if (reason && DASHBOARD_REFRESH_REASONS.includes(reason))
|
|
20
|
+
return reason;
|
|
21
|
+
return "manual";
|
|
120
22
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
updateTodoPreview(data?.todo || "");
|
|
126
|
-
}
|
|
127
|
-
catch (err) {
|
|
128
|
-
if (!silent) {
|
|
129
|
-
flash(err.message || "Failed to load TODO preview", "error");
|
|
130
|
-
}
|
|
131
|
-
}
|
|
23
|
+
function mapAutoRefreshReason(ctx) {
|
|
24
|
+
if (ctx.reason === "manual")
|
|
25
|
+
return "manual";
|
|
26
|
+
return "background";
|
|
132
27
|
}
|
|
133
28
|
function setUsageLoading(loading) {
|
|
134
29
|
const btn = document.getElementById("usage-refresh");
|
|
@@ -178,6 +73,27 @@ function renderUsageProgressBar(container, percent, windowMinutes) {
|
|
|
178
73
|
<span class="usage-progress-label">${hasData ? `${pct}%` : "–"}${windowMinutes ? `/${windowMinutes}m` : ""}</span>
|
|
179
74
|
`;
|
|
180
75
|
}
|
|
76
|
+
function usageSummarySignature(data) {
|
|
77
|
+
if (!data)
|
|
78
|
+
return "none";
|
|
79
|
+
const totals = data.totals || {};
|
|
80
|
+
const primary = data.latest_rate_limits?.primary || {};
|
|
81
|
+
const secondary = data.latest_rate_limits?.secondary || {};
|
|
82
|
+
return [
|
|
83
|
+
data.status || "",
|
|
84
|
+
data.events ?? "",
|
|
85
|
+
data.codex_home ?? "",
|
|
86
|
+
totals.total_tokens ?? "",
|
|
87
|
+
totals.input_tokens ?? "",
|
|
88
|
+
totals.cached_input_tokens ?? "",
|
|
89
|
+
totals.output_tokens ?? "",
|
|
90
|
+
totals.reasoning_output_tokens ?? "",
|
|
91
|
+
primary.used_percent ?? "",
|
|
92
|
+
primary.window_minutes ?? "",
|
|
93
|
+
secondary.used_percent ?? "",
|
|
94
|
+
secondary.window_minutes ?? "",
|
|
95
|
+
].join("|");
|
|
96
|
+
}
|
|
181
97
|
function renderUsage(data) {
|
|
182
98
|
if (data)
|
|
183
99
|
saveToCache("usage", data);
|
|
@@ -226,6 +142,235 @@ function renderUsage(data) {
|
|
|
226
142
|
if (metaEl)
|
|
227
143
|
metaEl.textContent = codexHome;
|
|
228
144
|
}
|
|
145
|
+
function analyticsSummarySignature(data) {
|
|
146
|
+
if (!data)
|
|
147
|
+
return "none";
|
|
148
|
+
const run = data.run;
|
|
149
|
+
const tickets = data.tickets;
|
|
150
|
+
const turns = data.turns;
|
|
151
|
+
const agent = data.agent;
|
|
152
|
+
return [
|
|
153
|
+
run?.id ?? "",
|
|
154
|
+
run?.short_id ?? "",
|
|
155
|
+
run?.status ?? "",
|
|
156
|
+
run?.started_at ?? "",
|
|
157
|
+
run?.finished_at ?? "",
|
|
158
|
+
run?.duration_seconds ?? "",
|
|
159
|
+
run?.current_step ?? "",
|
|
160
|
+
tickets?.todo_count ?? "",
|
|
161
|
+
tickets?.done_count ?? "",
|
|
162
|
+
tickets?.total_count ?? "",
|
|
163
|
+
tickets?.current_ticket ?? "",
|
|
164
|
+
turns?.total ?? "",
|
|
165
|
+
turns?.current_ticket ?? "",
|
|
166
|
+
turns?.dispatches ?? "",
|
|
167
|
+
turns?.replies ?? "",
|
|
168
|
+
turns?.diff_stats?.insertions ?? "",
|
|
169
|
+
turns?.diff_stats?.deletions ?? "",
|
|
170
|
+
agent?.id ?? "",
|
|
171
|
+
agent?.model ?? "",
|
|
172
|
+
].join("|");
|
|
173
|
+
}
|
|
174
|
+
const usageSummaryRefresh = createSmartRefresh({
|
|
175
|
+
getSignature: (payload) => usageSummarySignature(payload),
|
|
176
|
+
render: (payload) => {
|
|
177
|
+
renderUsage(payload);
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
const analyticsSummaryRefresh = createSmartRefresh({
|
|
181
|
+
getSignature: (payload) => analyticsSummarySignature(payload),
|
|
182
|
+
render: (payload) => {
|
|
183
|
+
renderTicketAnalytics(payload);
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
function runHistorySignature(runs) {
|
|
187
|
+
if (!runs.length)
|
|
188
|
+
return "none";
|
|
189
|
+
return runs
|
|
190
|
+
.map((run) => [
|
|
191
|
+
run.id,
|
|
192
|
+
run.status || "",
|
|
193
|
+
run.current_step || "",
|
|
194
|
+
run.created_at || "",
|
|
195
|
+
run.started_at || "",
|
|
196
|
+
run.finished_at || "",
|
|
197
|
+
].join(":"))
|
|
198
|
+
.join("|");
|
|
199
|
+
}
|
|
200
|
+
const runHistoryRefresh = createSmartRefresh({
|
|
201
|
+
getSignature: (payload) => runHistorySignature(payload),
|
|
202
|
+
render: (payload) => {
|
|
203
|
+
renderRunHistory(payload);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
async function fetchTicketAnalytics() {
|
|
207
|
+
try {
|
|
208
|
+
const data = (await api("/api/analytics/summary"));
|
|
209
|
+
saveToCache(ANALYTICS_SUMMARY_CACHE_KEY, data);
|
|
210
|
+
return data;
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
const cached = loadFromCache(ANALYTICS_SUMMARY_CACHE_KEY);
|
|
214
|
+
flash(err.message || "Failed to load analytics", "error");
|
|
215
|
+
if (cached)
|
|
216
|
+
return cached;
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async function loadTicketAnalytics(reason = "manual") {
|
|
221
|
+
const resolvedReason = normalizeRefreshReason(reason);
|
|
222
|
+
try {
|
|
223
|
+
await analyticsSummaryRefresh.refresh(fetchTicketAnalytics, { reason: resolvedReason });
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
flash(err.message || "Failed to load analytics", "error");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function formatDuration(seconds) {
|
|
230
|
+
if (seconds === null || Number.isNaN(seconds))
|
|
231
|
+
return "–";
|
|
232
|
+
if (seconds < 60)
|
|
233
|
+
return `${Math.round(seconds)}s`;
|
|
234
|
+
const mins = seconds / 60;
|
|
235
|
+
if (mins < 60)
|
|
236
|
+
return `${Math.round(mins)}m`;
|
|
237
|
+
const hours = mins / 60;
|
|
238
|
+
return `${hours.toFixed(1)}h`;
|
|
239
|
+
}
|
|
240
|
+
function renderTicketAnalytics(data) {
|
|
241
|
+
const run = data?.run;
|
|
242
|
+
const tickets = data?.tickets;
|
|
243
|
+
const turns = data?.turns;
|
|
244
|
+
const agent = data?.agent;
|
|
245
|
+
const statusEl = document.getElementById("runner-status");
|
|
246
|
+
if (statusEl && run) {
|
|
247
|
+
statusPill(statusEl, run.status || "idle");
|
|
248
|
+
statusEl.textContent = run.status || "idle";
|
|
249
|
+
}
|
|
250
|
+
const lastStart = document.getElementById("last-start");
|
|
251
|
+
const lastFinish = document.getElementById("last-finish");
|
|
252
|
+
const lastDuration = document.getElementById("last-duration");
|
|
253
|
+
const todoCount = document.getElementById("todo-count");
|
|
254
|
+
const doneCount = document.getElementById("done-count");
|
|
255
|
+
const ticketActive = document.getElementById("ticket-active");
|
|
256
|
+
const ticketTurns = document.getElementById("ticket-turns");
|
|
257
|
+
const totalTurns = document.getElementById("total-turns");
|
|
258
|
+
const dispatchesEl = document.getElementById("message-dispatches");
|
|
259
|
+
const repliesEl = document.getElementById("message-replies");
|
|
260
|
+
const runIdEl = document.getElementById("last-run-id");
|
|
261
|
+
if (lastStart)
|
|
262
|
+
lastStart.textContent = formatIso(run?.started_at || null);
|
|
263
|
+
if (lastFinish)
|
|
264
|
+
lastFinish.textContent = formatIso(run?.finished_at || null);
|
|
265
|
+
if (lastDuration)
|
|
266
|
+
lastDuration.textContent = formatDuration(run?.duration_seconds ?? null);
|
|
267
|
+
if (todoCount)
|
|
268
|
+
todoCount.textContent = tickets ? String(tickets.todo_count) : "–";
|
|
269
|
+
if (doneCount)
|
|
270
|
+
doneCount.textContent = tickets ? String(tickets.done_count) : "–";
|
|
271
|
+
if (ticketActive) {
|
|
272
|
+
const ticket = tickets?.current_ticket || null;
|
|
273
|
+
ticketActive.textContent = ticket ? ticket.split("/").pop() || ticket : "–";
|
|
274
|
+
}
|
|
275
|
+
if (ticketTurns)
|
|
276
|
+
ticketTurns.textContent = turns?.current_ticket != null ? String(turns.current_ticket) : "–";
|
|
277
|
+
if (totalTurns)
|
|
278
|
+
totalTurns.textContent = turns?.total != null ? String(turns.total) : "–";
|
|
279
|
+
const dispatchCount = turns?.dispatches ?? 0;
|
|
280
|
+
if (dispatchesEl)
|
|
281
|
+
dispatchesEl.textContent = String(dispatchCount);
|
|
282
|
+
if (repliesEl)
|
|
283
|
+
repliesEl.textContent = turns?.replies != null ? String(turns.replies) : "0";
|
|
284
|
+
if (runIdEl)
|
|
285
|
+
runIdEl.textContent = run?.short_id || run?.id || "–";
|
|
286
|
+
// Diff stats (lines changed)
|
|
287
|
+
const diffStatsEl = document.getElementById("lines-changed");
|
|
288
|
+
if (diffStatsEl) {
|
|
289
|
+
const diffStats = turns?.diff_stats;
|
|
290
|
+
if (diffStats && (diffStats.insertions > 0 || diffStats.deletions > 0)) {
|
|
291
|
+
const ins = diffStats.insertions || 0;
|
|
292
|
+
const del = diffStats.deletions || 0;
|
|
293
|
+
diffStatsEl.innerHTML = `<span class="diff-add">+${formatTokensCompact(ins)}</span> <span class="diff-del">-${formatTokensCompact(del)}</span>`;
|
|
294
|
+
diffStatsEl.title = `${ins} insertions, ${del} deletions, ${diffStats.files_changed || 0} files changed`;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
diffStatsEl.textContent = "–";
|
|
298
|
+
diffStatsEl.title = "";
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Agent chip (optional future use)
|
|
302
|
+
const agentEl = document.getElementById("ticket-agent");
|
|
303
|
+
if (agentEl) {
|
|
304
|
+
agentEl.textContent = agent?.id || "–";
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async function fetchRunHistory() {
|
|
308
|
+
const runs = (await api("/api/flows/runs?flow_type=ticket_flow"));
|
|
309
|
+
return Array.isArray(runs) ? runs.slice(0, 10) : [];
|
|
310
|
+
}
|
|
311
|
+
async function loadRunHistory(reason = "manual") {
|
|
312
|
+
const resolvedReason = normalizeRefreshReason(reason);
|
|
313
|
+
try {
|
|
314
|
+
await runHistoryRefresh.refresh(fetchRunHistory, { reason: resolvedReason });
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
flash(err.message || "Failed to load run history", "error");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function formatIso(iso) {
|
|
321
|
+
if (!iso)
|
|
322
|
+
return "–";
|
|
323
|
+
const dt = new Date(iso);
|
|
324
|
+
if (Number.isNaN(dt.getTime()))
|
|
325
|
+
return iso;
|
|
326
|
+
return dt.toLocaleString();
|
|
327
|
+
}
|
|
328
|
+
function calcDurationFromRun(run) {
|
|
329
|
+
const started = run.started_at;
|
|
330
|
+
const finished = run.finished_at;
|
|
331
|
+
if (!started)
|
|
332
|
+
return "–";
|
|
333
|
+
const start = new Date(started).getTime();
|
|
334
|
+
const end = finished && !Number.isNaN(new Date(finished).getTime())
|
|
335
|
+
? new Date(finished).getTime()
|
|
336
|
+
: Date.now();
|
|
337
|
+
if (Number.isNaN(start) || Number.isNaN(end))
|
|
338
|
+
return "–";
|
|
339
|
+
return formatDuration((end - start) / 1000);
|
|
340
|
+
}
|
|
341
|
+
function renderRunHistory(runs) {
|
|
342
|
+
const container = document.getElementById("run-history-list");
|
|
343
|
+
if (!container)
|
|
344
|
+
return;
|
|
345
|
+
preserveScroll(container, () => {
|
|
346
|
+
if (!runs || !runs.length) {
|
|
347
|
+
container.innerHTML = '<div class="muted">No runs yet.</div>';
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const items = runs.map((run) => {
|
|
351
|
+
const shortId = run.id ? run.id.split("-")[0] : "–";
|
|
352
|
+
const status = run.status || "unknown";
|
|
353
|
+
const duration = calcDurationFromRun(run);
|
|
354
|
+
const started = formatIso(run.started_at);
|
|
355
|
+
const current = run.current_step || "–";
|
|
356
|
+
return `
|
|
357
|
+
<div class="run-history-row">
|
|
358
|
+
<div class="run-history-id">${shortId}</div>
|
|
359
|
+
<div class="run-history-status">${status}</div>
|
|
360
|
+
<div class="run-history-duration">${duration}</div>
|
|
361
|
+
<div class="run-history-start">${started}</div>
|
|
362
|
+
<div class="run-history-step">${current}</div>
|
|
363
|
+
</div>
|
|
364
|
+
`;
|
|
365
|
+
});
|
|
366
|
+
container.innerHTML = `
|
|
367
|
+
<div class="run-history-head">
|
|
368
|
+
<div>ID</div><div>Status</div><div>Duration</div><div>Started</div><div>Step</div>
|
|
369
|
+
</div>
|
|
370
|
+
${items.join("")}
|
|
371
|
+
`;
|
|
372
|
+
}, { restoreOnNextFrame: true });
|
|
373
|
+
}
|
|
229
374
|
function buildUsageSeriesQuery() {
|
|
230
375
|
const params = new URLSearchParams();
|
|
231
376
|
const now = new Date();
|
|
@@ -237,6 +382,31 @@ function buildUsageSeriesQuery() {
|
|
|
237
382
|
params.set("segment", usageChartState.segment);
|
|
238
383
|
return params.toString();
|
|
239
384
|
}
|
|
385
|
+
function usageSeriesSignature(data) {
|
|
386
|
+
if (!data)
|
|
387
|
+
return "none";
|
|
388
|
+
const buckets = data.buckets || [];
|
|
389
|
+
const series = data.series || [];
|
|
390
|
+
const seriesSig = series
|
|
391
|
+
.map((entry) => {
|
|
392
|
+
const values = entry.values || [];
|
|
393
|
+
return [
|
|
394
|
+
entry.key ?? "",
|
|
395
|
+
entry.model ?? "",
|
|
396
|
+
entry.token_type ?? "",
|
|
397
|
+
entry.total ?? "",
|
|
398
|
+
values.join(","),
|
|
399
|
+
].join(":");
|
|
400
|
+
})
|
|
401
|
+
.join("|");
|
|
402
|
+
return `${data.status || ""}::${buckets.join(",")}::${seriesSig}`;
|
|
403
|
+
}
|
|
404
|
+
const usageSeriesRefresh = createSmartRefresh({
|
|
405
|
+
getSignature: (payload) => usageSeriesSignature(payload),
|
|
406
|
+
render: (payload) => {
|
|
407
|
+
renderUsageChart(payload);
|
|
408
|
+
},
|
|
409
|
+
});
|
|
240
410
|
function renderUsageChart(data) {
|
|
241
411
|
const container = document.getElementById("usage-chart-canvas");
|
|
242
412
|
if (!container)
|
|
@@ -263,6 +433,7 @@ function renderUsageChart(data) {
|
|
|
263
433
|
"#84d1ff",
|
|
264
434
|
"#9be26f",
|
|
265
435
|
"#f2a0c5",
|
|
436
|
+
"#373",
|
|
266
437
|
];
|
|
267
438
|
const { series: displaySeries } = normalizeSeries(limitSeries(series, 4, "rest").series, buckets.length);
|
|
268
439
|
let scaleMax = 1;
|
|
@@ -445,12 +616,13 @@ function attachUsageChartInteraction(container, state) {
|
|
|
445
616
|
tooltip.style.opacity = "0";
|
|
446
617
|
});
|
|
447
618
|
}
|
|
448
|
-
async function loadUsageSeries() {
|
|
619
|
+
async function loadUsageSeries(reason = "manual") {
|
|
620
|
+
const resolvedReason = normalizeRefreshReason(reason);
|
|
449
621
|
const container = document.getElementById("usage-chart-canvas");
|
|
450
622
|
try {
|
|
451
623
|
const data = await api(`/api/usage/series?${buildUsageSeriesQuery()}`);
|
|
452
624
|
setChartLoading(container, data?.status === "loading");
|
|
453
|
-
|
|
625
|
+
await usageSeriesRefresh.refresh(async () => data, { reason: resolvedReason });
|
|
454
626
|
if (data?.status === "loading") {
|
|
455
627
|
scheduleUsageSeriesRetry();
|
|
456
628
|
}
|
|
@@ -460,7 +632,7 @@ async function loadUsageSeries() {
|
|
|
460
632
|
}
|
|
461
633
|
catch (err) {
|
|
462
634
|
setChartLoading(container, false);
|
|
463
|
-
|
|
635
|
+
await usageSeriesRefresh.refresh(async () => null, { reason: resolvedReason });
|
|
464
636
|
clearUsageSeriesRetry();
|
|
465
637
|
}
|
|
466
638
|
}
|
|
@@ -488,153 +660,52 @@ function clearUsageSummaryRetry() {
|
|
|
488
660
|
usageSummaryRetryTimer = null;
|
|
489
661
|
}
|
|
490
662
|
}
|
|
491
|
-
async function
|
|
492
|
-
setUsageLoading(true);
|
|
663
|
+
async function fetchUsageSummary() {
|
|
493
664
|
try {
|
|
494
665
|
const data = await api("/api/usage");
|
|
495
666
|
const cachedUsage = loadFromCache("usage");
|
|
496
667
|
const hasSummary = data && data.totals && typeof data.events === "number";
|
|
497
668
|
if (data?.status === "loading") {
|
|
498
669
|
if (hasSummary) {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
else if (cachedUsage) {
|
|
502
|
-
renderUsage(cachedUsage);
|
|
670
|
+
scheduleUsageSummaryRetry();
|
|
671
|
+
return data;
|
|
503
672
|
}
|
|
504
|
-
|
|
505
|
-
|
|
673
|
+
if (cachedUsage) {
|
|
674
|
+
scheduleUsageSummaryRetry();
|
|
675
|
+
return cachedUsage;
|
|
506
676
|
}
|
|
507
677
|
scheduleUsageSummaryRetry();
|
|
678
|
+
return data;
|
|
508
679
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
clearUsageSummaryRetry();
|
|
512
|
-
}
|
|
513
|
-
loadUsageSeries();
|
|
680
|
+
clearUsageSummaryRetry();
|
|
681
|
+
return data;
|
|
514
682
|
}
|
|
515
683
|
catch (err) {
|
|
516
684
|
const cachedUsage = loadFromCache("usage");
|
|
517
|
-
if (cachedUsage) {
|
|
518
|
-
renderUsage(cachedUsage);
|
|
519
|
-
}
|
|
520
|
-
else {
|
|
521
|
-
renderUsage(null);
|
|
522
|
-
}
|
|
523
685
|
flash(err.message || "Failed to load usage", "error");
|
|
524
686
|
clearUsageSummaryRetry();
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
687
|
+
if (cachedUsage) {
|
|
688
|
+
return cachedUsage;
|
|
689
|
+
}
|
|
690
|
+
return null;
|
|
528
691
|
}
|
|
529
692
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
function normalizeUpdateTarget(value) {
|
|
536
|
-
if (!value)
|
|
537
|
-
return "both";
|
|
538
|
-
if (value === "both" || value === "web" || value === "telegram")
|
|
539
|
-
return value;
|
|
540
|
-
return "both";
|
|
541
|
-
}
|
|
542
|
-
function getUpdateTarget(selectId) {
|
|
543
|
-
const select = selectId ? document.getElementById(selectId) : null;
|
|
544
|
-
return normalizeUpdateTarget(select ? select.value : "both");
|
|
545
|
-
}
|
|
546
|
-
function describeUpdateTarget(target) {
|
|
547
|
-
return UPDATE_TARGET_LABELS[target] || UPDATE_TARGET_LABELS.both;
|
|
548
|
-
}
|
|
549
|
-
async function handleSystemUpdate(btnId, targetSelectId) {
|
|
550
|
-
const btn = document.getElementById(btnId);
|
|
551
|
-
if (!btn)
|
|
552
|
-
return;
|
|
553
|
-
const originalText = btn.textContent;
|
|
554
|
-
btn.disabled = true;
|
|
555
|
-
btn.textContent = "Checking...";
|
|
556
|
-
const updateTarget = getUpdateTarget(targetSelectId);
|
|
557
|
-
const targetLabel = describeUpdateTarget(updateTarget);
|
|
558
|
-
let check;
|
|
693
|
+
async function loadUsage(reason = "manual") {
|
|
694
|
+
const resolvedReason = normalizeRefreshReason(reason);
|
|
695
|
+
const showLoading = resolvedReason !== "background";
|
|
696
|
+
if (showLoading)
|
|
697
|
+
setUsageLoading(true);
|
|
559
698
|
try {
|
|
560
|
-
|
|
699
|
+
await usageSummaryRefresh.refresh(fetchUsageSummary, { reason: resolvedReason });
|
|
561
700
|
}
|
|
562
701
|
catch (err) {
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
if (!check?.update_available) {
|
|
566
|
-
flash(check?.message || "No update available.", "info");
|
|
567
|
-
btn.disabled = false;
|
|
568
|
-
btn.textContent = originalText;
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
const restartNotice = updateTarget === "telegram"
|
|
572
|
-
? "The Telegram bot will restart."
|
|
573
|
-
: "The service will restart.";
|
|
574
|
-
const confirmed = await confirmModal(`${check?.message || "Update available."} Update Codex Autorunner (${targetLabel})? ${restartNotice}`);
|
|
575
|
-
if (!confirmed) {
|
|
576
|
-
btn.disabled = false;
|
|
577
|
-
btn.textContent = originalText;
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
btn.textContent = "Updating...";
|
|
581
|
-
try {
|
|
582
|
-
const res = await api("/system/update", {
|
|
583
|
-
method: "POST",
|
|
584
|
-
body: { target: updateTarget },
|
|
585
|
-
});
|
|
586
|
-
flash(res.message || `Update started (${targetLabel}).`, "success");
|
|
587
|
-
if (updateTarget === "telegram") {
|
|
588
|
-
btn.disabled = false;
|
|
589
|
-
btn.textContent = originalText;
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
document.body.style.pointerEvents = "none";
|
|
593
|
-
setTimeout(() => {
|
|
594
|
-
const url = new URL(window.location.href);
|
|
595
|
-
url.searchParams.set("v", String(Date.now()));
|
|
596
|
-
window.location.replace(url.toString());
|
|
597
|
-
}, 8000);
|
|
598
|
-
}
|
|
599
|
-
catch (err) {
|
|
600
|
-
flash(err.message || "Update failed", "error");
|
|
601
|
-
btn.disabled = false;
|
|
602
|
-
btn.textContent = originalText;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
function initSettings() {
|
|
606
|
-
const settingsBtn = document.getElementById("repo-settings");
|
|
607
|
-
const modal = document.getElementById("repo-settings-modal");
|
|
608
|
-
const closeBtn = document.getElementById("repo-settings-close");
|
|
609
|
-
const updateBtn = document.getElementById("repo-update-btn");
|
|
610
|
-
const updateTarget = document.getElementById("repo-update-target");
|
|
611
|
-
let closeModal = null;
|
|
612
|
-
const hideModal = () => {
|
|
613
|
-
if (closeModal) {
|
|
614
|
-
const close = closeModal;
|
|
615
|
-
closeModal = null;
|
|
616
|
-
close();
|
|
617
|
-
}
|
|
618
|
-
};
|
|
619
|
-
if (settingsBtn && modal) {
|
|
620
|
-
settingsBtn.addEventListener("click", () => {
|
|
621
|
-
const triggerEl = document.activeElement;
|
|
622
|
-
hideModal();
|
|
623
|
-
closeModal = openModal(modal, {
|
|
624
|
-
initialFocus: closeBtn || updateBtn || modal,
|
|
625
|
-
returnFocusTo: triggerEl,
|
|
626
|
-
onRequestClose: hideModal,
|
|
627
|
-
});
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
if (closeBtn && modal) {
|
|
631
|
-
closeBtn.addEventListener("click", () => {
|
|
632
|
-
hideModal();
|
|
633
|
-
});
|
|
702
|
+
flash(err.message || "Failed to load usage", "error");
|
|
634
703
|
}
|
|
635
|
-
|
|
636
|
-
|
|
704
|
+
finally {
|
|
705
|
+
if (showLoading)
|
|
706
|
+
setUsageLoading(false);
|
|
637
707
|
}
|
|
708
|
+
await loadUsageSeries(resolvedReason);
|
|
638
709
|
}
|
|
639
710
|
function initUsageChartControls() {
|
|
640
711
|
const segmentSelect = document.getElementById("usage-chart-segment");
|
|
@@ -676,97 +747,45 @@ function bindAction(buttonId, action) {
|
|
|
676
747
|
}
|
|
677
748
|
});
|
|
678
749
|
}
|
|
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
750
|
export function initDashboard() {
|
|
688
|
-
initSettings();
|
|
689
751
|
initUsageChartControls();
|
|
690
|
-
initReview();
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
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();
|
|
752
|
+
// initReview(); // Removed - review.ts was deleted
|
|
753
|
+
bindAction("usage-refresh", () => loadUsage("manual"));
|
|
754
|
+
bindAction("analytics-refresh", async () => {
|
|
755
|
+
await loadTicketAnalytics("manual");
|
|
756
|
+
await loadRunHistory("manual");
|
|
730
757
|
});
|
|
731
|
-
bindAction("refresh-state", async () => { await loadState(); });
|
|
732
|
-
bindAction("usage-refresh", loadUsage);
|
|
733
|
-
bindAction("refresh-preview", loadTodoPreview);
|
|
734
|
-
const cachedState = loadFromCache("state");
|
|
735
|
-
if (cachedState)
|
|
736
|
-
renderState(cachedState);
|
|
737
758
|
const cachedUsage = loadFromCache("usage");
|
|
738
759
|
if (cachedUsage)
|
|
739
760
|
renderUsage(cachedUsage);
|
|
740
|
-
const
|
|
741
|
-
if (
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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
|
-
}
|
|
758
|
-
loadUsage();
|
|
759
|
-
loadTodoPreview();
|
|
761
|
+
const cachedAnalytics = loadFromCache(ANALYTICS_SUMMARY_CACHE_KEY);
|
|
762
|
+
if (cachedAnalytics)
|
|
763
|
+
renderTicketAnalytics(cachedAnalytics);
|
|
764
|
+
loadUsage("initial");
|
|
765
|
+
loadTicketAnalytics("initial");
|
|
766
|
+
loadRunHistory("initial");
|
|
760
767
|
loadVersion();
|
|
761
768
|
checkUpdateStatus();
|
|
762
|
-
startStatePolling();
|
|
763
769
|
registerAutoRefresh("dashboard-usage", {
|
|
764
|
-
callback: async () => {
|
|
765
|
-
|
|
770
|
+
callback: async (ctx) => {
|
|
771
|
+
await loadUsage(mapAutoRefreshReason(ctx));
|
|
772
|
+
},
|
|
773
|
+
tabId: "analytics",
|
|
766
774
|
interval: CONSTANTS.UI.AUTO_REFRESH_USAGE_INTERVAL,
|
|
767
775
|
refreshOnActivation: true,
|
|
768
776
|
immediate: false,
|
|
769
777
|
});
|
|
778
|
+
registerAutoRefresh("dashboard-analytics", {
|
|
779
|
+
callback: async (ctx) => {
|
|
780
|
+
const reason = mapAutoRefreshReason(ctx);
|
|
781
|
+
await loadTicketAnalytics(reason);
|
|
782
|
+
await loadRunHistory(reason);
|
|
783
|
+
},
|
|
784
|
+
tabId: "analytics",
|
|
785
|
+
interval: CONSTANTS.UI.AUTO_REFRESH_INTERVAL,
|
|
786
|
+
refreshOnActivation: true,
|
|
787
|
+
immediate: true,
|
|
788
|
+
});
|
|
770
789
|
}
|
|
771
790
|
async function loadVersion() {
|
|
772
791
|
const versionEl = document.getElementById("repo-version");
|