codex-autorunner 1.0.0__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codex_autorunner/__init__.py +12 -1
- codex_autorunner/agents/codex/harness.py +1 -1
- codex_autorunner/agents/opencode/client.py +113 -4
- codex_autorunner/agents/opencode/constants.py +3 -0
- codex_autorunner/agents/opencode/harness.py +6 -1
- codex_autorunner/agents/opencode/runtime.py +59 -18
- codex_autorunner/agents/opencode/supervisor.py +4 -0
- codex_autorunner/agents/registry.py +36 -7
- codex_autorunner/bootstrap.py +226 -4
- codex_autorunner/cli.py +5 -1174
- codex_autorunner/codex_cli.py +20 -84
- codex_autorunner/core/__init__.py +20 -0
- codex_autorunner/core/about_car.py +119 -1
- codex_autorunner/core/app_server_ids.py +59 -0
- codex_autorunner/core/app_server_threads.py +17 -2
- codex_autorunner/core/app_server_utils.py +165 -0
- codex_autorunner/core/archive.py +349 -0
- codex_autorunner/core/codex_runner.py +6 -2
- codex_autorunner/core/config.py +433 -4
- codex_autorunner/core/context_awareness.py +38 -0
- codex_autorunner/core/docs.py +0 -122
- codex_autorunner/core/drafts.py +58 -4
- codex_autorunner/core/exceptions.py +4 -0
- codex_autorunner/core/filebox.py +265 -0
- codex_autorunner/core/flows/controller.py +96 -2
- codex_autorunner/core/flows/models.py +13 -0
- codex_autorunner/core/flows/reasons.py +52 -0
- codex_autorunner/core/flows/reconciler.py +134 -0
- codex_autorunner/core/flows/runtime.py +57 -4
- codex_autorunner/core/flows/store.py +142 -7
- codex_autorunner/core/flows/transition.py +27 -15
- codex_autorunner/core/flows/ux_helpers.py +272 -0
- codex_autorunner/core/flows/worker_process.py +32 -6
- codex_autorunner/core/git_utils.py +62 -0
- codex_autorunner/core/hub.py +291 -20
- codex_autorunner/core/lifecycle_events.py +253 -0
- codex_autorunner/core/notifications.py +14 -2
- codex_autorunner/core/path_utils.py +2 -1
- codex_autorunner/core/pma_audit.py +224 -0
- codex_autorunner/core/pma_context.py +496 -0
- codex_autorunner/core/pma_dispatch_interceptor.py +284 -0
- codex_autorunner/core/pma_lifecycle.py +527 -0
- codex_autorunner/core/pma_queue.py +367 -0
- codex_autorunner/core/pma_safety.py +221 -0
- codex_autorunner/core/pma_state.py +115 -0
- codex_autorunner/core/ports/__init__.py +28 -0
- codex_autorunner/{integrations/agents → core/ports}/agent_backend.py +13 -8
- codex_autorunner/core/ports/backend_orchestrator.py +41 -0
- codex_autorunner/{integrations/agents → core/ports}/run_event.py +23 -6
- codex_autorunner/core/prompt.py +0 -80
- codex_autorunner/core/prompts.py +56 -172
- codex_autorunner/core/redaction.py +0 -4
- codex_autorunner/core/review_context.py +11 -9
- codex_autorunner/core/runner_controller.py +35 -33
- codex_autorunner/core/runner_state.py +147 -0
- codex_autorunner/core/runtime.py +829 -0
- codex_autorunner/core/sqlite_utils.py +13 -4
- codex_autorunner/core/state.py +7 -10
- codex_autorunner/core/state_roots.py +62 -0
- codex_autorunner/core/supervisor_protocol.py +15 -0
- codex_autorunner/core/templates/__init__.py +39 -0
- codex_autorunner/core/templates/git_mirror.py +234 -0
- codex_autorunner/core/templates/provenance.py +56 -0
- codex_autorunner/core/templates/scan_cache.py +120 -0
- codex_autorunner/core/text_delta_coalescer.py +54 -0
- codex_autorunner/core/ticket_linter_cli.py +218 -0
- codex_autorunner/core/ticket_manager_cli.py +494 -0
- codex_autorunner/core/time_utils.py +11 -0
- codex_autorunner/core/types.py +18 -0
- codex_autorunner/core/update.py +4 -5
- codex_autorunner/core/update_paths.py +28 -0
- codex_autorunner/core/usage.py +164 -12
- codex_autorunner/core/utils.py +125 -15
- codex_autorunner/flows/review/__init__.py +17 -0
- codex_autorunner/{core/review.py → flows/review/service.py} +37 -34
- codex_autorunner/flows/ticket_flow/definition.py +52 -3
- codex_autorunner/integrations/agents/__init__.py +11 -19
- codex_autorunner/integrations/agents/backend_orchestrator.py +302 -0
- codex_autorunner/integrations/agents/codex_adapter.py +90 -0
- codex_autorunner/integrations/agents/codex_backend.py +177 -25
- codex_autorunner/integrations/agents/opencode_adapter.py +108 -0
- codex_autorunner/integrations/agents/opencode_backend.py +305 -32
- codex_autorunner/integrations/agents/runner.py +86 -0
- codex_autorunner/integrations/agents/wiring.py +279 -0
- codex_autorunner/integrations/app_server/client.py +7 -60
- 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/telegram/adapter.py +65 -0
- codex_autorunner/integrations/telegram/config.py +46 -0
- codex_autorunner/integrations/telegram/constants.py +1 -1
- codex_autorunner/integrations/telegram/doctor.py +228 -6
- codex_autorunner/integrations/telegram/handlers/callbacks.py +7 -0
- codex_autorunner/integrations/telegram/handlers/commands/execution.py +236 -74
- codex_autorunner/integrations/telegram/handlers/commands/files.py +314 -75
- codex_autorunner/integrations/telegram/handlers/commands/flows.py +1496 -71
- codex_autorunner/integrations/telegram/handlers/commands/workspace.py +498 -37
- codex_autorunner/integrations/telegram/handlers/commands_runtime.py +206 -48
- codex_autorunner/integrations/telegram/handlers/commands_spec.py +20 -3
- codex_autorunner/integrations/telegram/handlers/messages.py +27 -1
- codex_autorunner/integrations/telegram/handlers/selections.py +61 -1
- codex_autorunner/integrations/telegram/helpers.py +22 -1
- codex_autorunner/integrations/telegram/runtime.py +9 -4
- codex_autorunner/integrations/telegram/service.py +45 -10
- codex_autorunner/integrations/telegram/state.py +38 -0
- codex_autorunner/integrations/telegram/ticket_flow_bridge.py +338 -43
- codex_autorunner/integrations/telegram/transport.py +13 -4
- codex_autorunner/integrations/templates/__init__.py +27 -0
- codex_autorunner/integrations/templates/scan_agent.py +312 -0
- codex_autorunner/routes/__init__.py +37 -76
- codex_autorunner/routes/agents.py +2 -137
- codex_autorunner/routes/analytics.py +2 -238
- codex_autorunner/routes/app_server.py +2 -131
- codex_autorunner/routes/base.py +2 -596
- codex_autorunner/routes/file_chat.py +4 -833
- codex_autorunner/routes/flows.py +4 -977
- codex_autorunner/routes/messages.py +4 -456
- 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 -193
- codex_autorunner/routes/usage.py +2 -86
- codex_autorunner/routes/voice.py +2 -119
- codex_autorunner/routes/workspace.py +2 -270
- codex_autorunner/server.py +4 -4
- codex_autorunner/static/agentControls.js +61 -16
- codex_autorunner/static/app.js +126 -14
- codex_autorunner/static/archive.js +826 -0
- codex_autorunner/static/archiveApi.js +37 -0
- codex_autorunner/static/autoRefresh.js +7 -7
- codex_autorunner/static/chatUploads.js +137 -0
- codex_autorunner/static/dashboard.js +224 -171
- codex_autorunner/static/docChatCore.js +185 -13
- codex_autorunner/static/fileChat.js +68 -40
- codex_autorunner/static/fileboxUi.js +159 -0
- codex_autorunner/static/hub.js +114 -131
- codex_autorunner/static/index.html +375 -49
- codex_autorunner/static/messages.js +568 -87
- codex_autorunner/static/notifications.js +255 -0
- codex_autorunner/static/pma.js +1167 -0
- codex_autorunner/static/preserve.js +17 -0
- codex_autorunner/static/settings.js +128 -6
- codex_autorunner/static/smartRefresh.js +52 -0
- codex_autorunner/static/streamUtils.js +57 -0
- codex_autorunner/static/styles.css +9798 -6143
- codex_autorunner/static/tabs.js +152 -11
- codex_autorunner/static/templateReposSettings.js +225 -0
- codex_autorunner/static/terminal.js +18 -0
- codex_autorunner/static/ticketChatActions.js +165 -3
- codex_autorunner/static/ticketChatStream.js +17 -119
- codex_autorunner/static/ticketEditor.js +137 -15
- codex_autorunner/static/ticketTemplates.js +798 -0
- codex_autorunner/static/tickets.js +821 -98
- codex_autorunner/static/turnEvents.js +27 -0
- codex_autorunner/static/turnResume.js +33 -0
- codex_autorunner/static/utils.js +39 -0
- codex_autorunner/static/workspace.js +389 -82
- codex_autorunner/static/workspaceFileBrowser.js +15 -13
- codex_autorunner/surfaces/__init__.py +5 -0
- codex_autorunner/surfaces/cli/__init__.py +6 -0
- codex_autorunner/surfaces/cli/cli.py +2534 -0
- codex_autorunner/surfaces/cli/codex_cli.py +20 -0
- codex_autorunner/surfaces/cli/pma_cli.py +817 -0
- codex_autorunner/surfaces/telegram/__init__.py +3 -0
- codex_autorunner/surfaces/web/__init__.py +1 -0
- codex_autorunner/surfaces/web/app.py +2223 -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 +82 -0
- codex_autorunner/surfaces/web/routes/agents.py +138 -0
- codex_autorunner/surfaces/web/routes/analytics.py +284 -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 +1117 -0
- codex_autorunner/surfaces/web/routes/filebox.py +227 -0
- codex_autorunner/surfaces/web/routes/flows.py +1354 -0
- codex_autorunner/surfaces/web/routes/messages.py +490 -0
- codex_autorunner/surfaces/web/routes/pma.py +1652 -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 +277 -0
- codex_autorunner/surfaces/web/routes/system.py +196 -0
- codex_autorunner/surfaces/web/routes/templates.py +634 -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 +469 -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 +8 -1
- codex_autorunner/tickets/agent_pool.py +53 -4
- codex_autorunner/tickets/files.py +37 -16
- codex_autorunner/tickets/lint.py +50 -0
- codex_autorunner/tickets/models.py +6 -1
- codex_autorunner/tickets/outbox.py +50 -2
- codex_autorunner/tickets/runner.py +396 -57
- codex_autorunner/web/__init__.py +5 -1
- codex_autorunner/web/app.py +2 -1949
- codex_autorunner/web/hub_jobs.py +2 -191
- codex_autorunner/web/middleware.py +2 -586
- codex_autorunner/web/pty_session.py +2 -369
- codex_autorunner/web/runner_manager.py +2 -24
- codex_autorunner/web/schemas.py +2 -376
- codex_autorunner/web/static_assets.py +4 -441
- codex_autorunner/web/static_refresh.py +2 -85
- codex_autorunner/web/terminal_sessions.py +2 -77
- codex_autorunner/workspace/paths.py +49 -33
- codex_autorunner-1.2.0.dist-info/METADATA +150 -0
- codex_autorunner-1.2.0.dist-info/RECORD +339 -0
- codex_autorunner/core/adapter_utils.py +0 -21
- codex_autorunner/core/engine.py +0 -2653
- codex_autorunner/core/static_assets.py +0 -55
- codex_autorunner-1.0.0.dist-info/METADATA +0 -246
- codex_autorunner-1.0.0.dist-info/RECORD +0 -251
- /codex_autorunner/{routes → surfaces/web/routes}/terminal_images.py +0 -0
- {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/WHEEL +0 -0
- {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/entry_points.txt +0 -0
- {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {codex_autorunner-1.0.0.dist-info → codex_autorunner-1.2.0.dist-info}/top_level.txt +0 -0
codex_autorunner/static/hub.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { api, flash, statusPill, resolvePath, escapeHtml, confirmModal, inputModal, openModal, } from "./utils.js";
|
|
3
3
|
import { registerAutoRefresh } from "./autoRefresh.js";
|
|
4
4
|
import { HUB_BASE } from "./env.js";
|
|
5
|
+
import { preserveScroll } from "./preserve.js";
|
|
5
6
|
let hubData = { repos: [], last_scan_at: null };
|
|
6
7
|
const prefetchedUrls = new Set();
|
|
7
8
|
const HUB_CACHE_TTL_MS = 30000;
|
|
@@ -12,18 +13,17 @@ const HUB_REFRESH_IDLE_MS = 30000;
|
|
|
12
13
|
let lastHubAutoRefreshAt = 0;
|
|
13
14
|
const repoListEl = document.getElementById("hub-repo-list");
|
|
14
15
|
const lastScanEl = document.getElementById("hub-last-scan");
|
|
16
|
+
const pmaLastScanEl = document.getElementById("pma-last-scan");
|
|
15
17
|
const totalEl = document.getElementById("hub-count-total");
|
|
16
18
|
const runningEl = document.getElementById("hub-count-running");
|
|
17
19
|
const missingEl = document.getElementById("hub-count-missing");
|
|
18
|
-
const hubUsageList = document.getElementById("hub-usage-list");
|
|
19
20
|
const hubUsageMeta = document.getElementById("hub-usage-meta");
|
|
20
21
|
const hubUsageRefresh = document.getElementById("hub-usage-refresh");
|
|
21
22
|
const hubUsageChartCanvas = document.getElementById("hub-usage-chart-canvas");
|
|
22
23
|
const hubUsageChartRange = document.getElementById("hub-usage-chart-range");
|
|
23
24
|
const hubUsageChartSegment = document.getElementById("hub-usage-chart-segment");
|
|
24
25
|
const hubVersionEl = document.getElementById("hub-version");
|
|
25
|
-
const
|
|
26
|
-
const hubInboxRefresh = document.getElementById("hub-inbox-refresh");
|
|
26
|
+
const pmaVersionEl = document.getElementById("pma-version");
|
|
27
27
|
const UPDATE_STATUS_SEEN_KEY = "car_update_status_seen";
|
|
28
28
|
const HUB_JOB_POLL_INTERVAL_MS = 1200;
|
|
29
29
|
const HUB_JOB_TIMEOUT_MS = 180000;
|
|
@@ -34,6 +34,8 @@ const hubUsageChartState = {
|
|
|
34
34
|
};
|
|
35
35
|
let hubUsageSeriesRetryTimer = null;
|
|
36
36
|
let hubUsageSummaryRetryTimer = null;
|
|
37
|
+
let hubUsageIndex = {};
|
|
38
|
+
let hubUsageUnmatched = null;
|
|
37
39
|
function saveSessionCache(key, value) {
|
|
38
40
|
try {
|
|
39
41
|
const payload = { at: Date.now(), value };
|
|
@@ -81,9 +83,7 @@ function formatLastActivity(repo) {
|
|
|
81
83
|
}
|
|
82
84
|
function setButtonLoading(scanning) {
|
|
83
85
|
const buttons = [
|
|
84
|
-
document.getElementById("hub-scan"),
|
|
85
86
|
document.getElementById("hub-quick-scan"),
|
|
86
|
-
document.getElementById("hub-refresh"),
|
|
87
87
|
];
|
|
88
88
|
buttons.forEach((btn) => {
|
|
89
89
|
if (!btn)
|
|
@@ -153,6 +153,9 @@ function renderSummary(repos) {
|
|
|
153
153
|
if (lastScanEl) {
|
|
154
154
|
lastScanEl.textContent = formatTimeCompact(hubData.last_scan_at);
|
|
155
155
|
}
|
|
156
|
+
if (pmaLastScanEl) {
|
|
157
|
+
pmaLastScanEl.textContent = formatTimeCompact(hubData.last_scan_at);
|
|
158
|
+
}
|
|
156
159
|
}
|
|
157
160
|
function formatTokensCompact(val) {
|
|
158
161
|
if (val === null || val === undefined)
|
|
@@ -176,49 +179,30 @@ function formatTokensAxis(val) {
|
|
|
176
179
|
return `${(num / 1000).toFixed(1)}k`;
|
|
177
180
|
return Math.round(num).toString();
|
|
178
181
|
}
|
|
179
|
-
function
|
|
180
|
-
|
|
182
|
+
function getRepoUsage(repoId) {
|
|
183
|
+
const usage = hubUsageIndex[repoId];
|
|
184
|
+
if (!usage)
|
|
185
|
+
return { label: "—", hasData: false };
|
|
186
|
+
const totals = usage.totals || {};
|
|
187
|
+
return {
|
|
188
|
+
label: formatTokensCompact(totals.total_tokens),
|
|
189
|
+
hasData: true,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function indexHubUsage(data) {
|
|
193
|
+
hubUsageIndex = {};
|
|
194
|
+
hubUsageUnmatched = data?.unmatched || null;
|
|
195
|
+
if (!data?.repos)
|
|
181
196
|
return;
|
|
197
|
+
data.repos.forEach((repo) => {
|
|
198
|
+
if (repo?.id)
|
|
199
|
+
hubUsageIndex[repo.id] = repo;
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function renderHubUsageMeta(data) {
|
|
182
203
|
if (hubUsageMeta) {
|
|
183
204
|
hubUsageMeta.textContent = data?.codex_home || "–";
|
|
184
205
|
}
|
|
185
|
-
if (!data || !data.repos) {
|
|
186
|
-
hubUsageList.innerHTML =
|
|
187
|
-
'<span class="muted small">Usage unavailable</span>';
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
if (!data.repos.length && (!data.unmatched || !data.unmatched.events)) {
|
|
191
|
-
hubUsageList.innerHTML = '<span class="muted small">No token events</span>';
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
hubUsageList.innerHTML = "";
|
|
195
|
-
const entries = [...data.repos].sort((a, b) => (b.totals?.total_tokens || 0) - (a.totals?.total_tokens || 0));
|
|
196
|
-
entries.forEach((repo) => {
|
|
197
|
-
const div = document.createElement("div");
|
|
198
|
-
div.className = "hub-usage-chip";
|
|
199
|
-
const totals = repo.totals || {};
|
|
200
|
-
const cached = totals.cached_input_tokens || 0;
|
|
201
|
-
const cachePercent = totals.input_tokens
|
|
202
|
-
? Math.round((cached / totals.input_tokens) * 100)
|
|
203
|
-
: 0;
|
|
204
|
-
div.innerHTML = `
|
|
205
|
-
<span class="hub-usage-chip-name">${escapeHtml(repo.id)}</span>
|
|
206
|
-
<span class="hub-usage-chip-total">${escapeHtml(formatTokensCompact(totals.total_tokens))}</span>
|
|
207
|
-
<span class="hub-usage-chip-meta">${escapeHtml(`${repo.events ?? 0}ev · ${cachePercent}%↻`)}</span>
|
|
208
|
-
`;
|
|
209
|
-
hubUsageList.appendChild(div);
|
|
210
|
-
});
|
|
211
|
-
if (data.unmatched && data.unmatched.events) {
|
|
212
|
-
const div = document.createElement("div");
|
|
213
|
-
div.className = "hub-usage-chip hub-usage-chip-unmatched";
|
|
214
|
-
const totals = data.unmatched.totals || {};
|
|
215
|
-
div.innerHTML = `
|
|
216
|
-
<span class="hub-usage-chip-name">other</span>
|
|
217
|
-
<span class="hub-usage-chip-total">${escapeHtml(formatTokensCompact(totals.total_tokens))}</span>
|
|
218
|
-
<span class="hub-usage-chip-meta">${escapeHtml(`${data.unmatched.events}ev`)}</span>
|
|
219
|
-
`;
|
|
220
|
-
hubUsageList.appendChild(div);
|
|
221
|
-
}
|
|
222
206
|
}
|
|
223
207
|
function scheduleHubUsageSummaryRetry() {
|
|
224
208
|
clearHubUsageSummaryRetry();
|
|
@@ -234,30 +218,23 @@ function clearHubUsageSummaryRetry() {
|
|
|
234
218
|
}
|
|
235
219
|
function handleHubUsagePayload(data, { cachedUsage, allowRetry }) {
|
|
236
220
|
const hasSummary = data && Array.isArray(data.repos);
|
|
221
|
+
const effective = hasSummary ? data : cachedUsage;
|
|
222
|
+
if (effective) {
|
|
223
|
+
indexHubUsage(effective);
|
|
224
|
+
renderHubUsageMeta(effective);
|
|
225
|
+
renderReposWithScroll(hubData.repos || []);
|
|
226
|
+
}
|
|
237
227
|
if (data?.status === "loading") {
|
|
238
|
-
if (hasSummary) {
|
|
239
|
-
renderHubUsage(data);
|
|
240
|
-
}
|
|
241
|
-
else if (cachedUsage) {
|
|
242
|
-
renderHubUsage(cachedUsage);
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
renderHubUsage(data);
|
|
246
|
-
}
|
|
247
228
|
if (allowRetry)
|
|
248
229
|
scheduleHubUsageSummaryRetry();
|
|
249
|
-
return hasSummary;
|
|
230
|
+
return Boolean(hasSummary);
|
|
250
231
|
}
|
|
251
232
|
if (hasSummary) {
|
|
252
|
-
renderHubUsage(data);
|
|
253
233
|
clearHubUsageSummaryRetry();
|
|
254
234
|
return true;
|
|
255
235
|
}
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
renderHubUsage(null);
|
|
236
|
+
if (!effective && !data) {
|
|
237
|
+
renderReposWithScroll(hubData.repos || []);
|
|
261
238
|
}
|
|
262
239
|
return false;
|
|
263
240
|
}
|
|
@@ -279,10 +256,7 @@ async function loadHubUsage({ silent = false, allowRetry = true } = {}) {
|
|
|
279
256
|
catch (err) {
|
|
280
257
|
const cachedUsage = loadSessionCache(HUB_USAGE_CACHE_KEY, HUB_CACHE_TTL_MS);
|
|
281
258
|
if (cachedUsage) {
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
else {
|
|
285
|
-
renderHubUsage(null);
|
|
259
|
+
handleHubUsagePayload(cachedUsage, { cachedUsage, allowRetry: false });
|
|
286
260
|
}
|
|
287
261
|
if (!silent) {
|
|
288
262
|
flash(err.message || "Failed to load usage", "error");
|
|
@@ -635,7 +609,7 @@ async function handleSystemUpdate(btnId, targetSelectId) {
|
|
|
635
609
|
}
|
|
636
610
|
}
|
|
637
611
|
function initHubSettings() {
|
|
638
|
-
const
|
|
612
|
+
const settingsBtns = Array.from(document.querySelectorAll("#hub-settings, #pma-settings"));
|
|
639
613
|
const modal = document.getElementById("hub-settings-modal");
|
|
640
614
|
const closeBtn = document.getElementById("hub-settings-close");
|
|
641
615
|
const updateBtn = document.getElementById("hub-update-btn");
|
|
@@ -648,14 +622,16 @@ function initHubSettings() {
|
|
|
648
622
|
close();
|
|
649
623
|
}
|
|
650
624
|
};
|
|
651
|
-
if (
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
625
|
+
if (modal && settingsBtns.length > 0) {
|
|
626
|
+
settingsBtns.forEach((settingsBtn) => {
|
|
627
|
+
settingsBtn.addEventListener("click", () => {
|
|
628
|
+
const triggerEl = document.activeElement;
|
|
629
|
+
hideModal();
|
|
630
|
+
closeModal = openModal(modal, {
|
|
631
|
+
initialFocus: closeBtn || updateBtn || modal,
|
|
632
|
+
returnFocusTo: triggerEl,
|
|
633
|
+
onRequestClose: hideModal,
|
|
634
|
+
});
|
|
659
635
|
});
|
|
660
636
|
});
|
|
661
637
|
}
|
|
@@ -828,6 +804,32 @@ function renderRepos(repos) {
|
|
|
828
804
|
const infoLine = infoItems.length > 0
|
|
829
805
|
? `<span class="hub-repo-info-line">${escapeHtml(infoItems.join(" · "))}</span>`
|
|
830
806
|
: "";
|
|
807
|
+
const usageInfo = getRepoUsage(repo.id);
|
|
808
|
+
const usageLine = `
|
|
809
|
+
<div class="hub-repo-usage-line${usageInfo.hasData ? "" : " muted"}">
|
|
810
|
+
<span class="pill pill-small hub-usage-pill">
|
|
811
|
+
${escapeHtml(usageInfo.label)}
|
|
812
|
+
</span>
|
|
813
|
+
</div>`;
|
|
814
|
+
// Ticket flow progress line
|
|
815
|
+
let ticketFlowLine = "";
|
|
816
|
+
const tf = repo.ticket_flow;
|
|
817
|
+
if (tf && tf.total_count > 0) {
|
|
818
|
+
const percent = Math.round((tf.done_count / tf.total_count) * 100);
|
|
819
|
+
const isActive = tf.status === "running" || tf.status === "paused";
|
|
820
|
+
const statusSuffix = tf.status === "paused"
|
|
821
|
+
? " · paused"
|
|
822
|
+
: tf.current_step
|
|
823
|
+
? ` · step ${tf.current_step}`
|
|
824
|
+
: "";
|
|
825
|
+
ticketFlowLine = `
|
|
826
|
+
<div class="hub-repo-flow-line${isActive ? " active" : ""}">
|
|
827
|
+
<div class="hub-flow-bar">
|
|
828
|
+
<div class="hub-flow-fill" style="width:${percent}%"></div>
|
|
829
|
+
</div>
|
|
830
|
+
<span class="hub-flow-text">${tf.done_count}/${tf.total_count}${statusSuffix}</span>
|
|
831
|
+
</div>`;
|
|
832
|
+
}
|
|
831
833
|
card.innerHTML = `
|
|
832
834
|
<div class="hub-repo-row">
|
|
833
835
|
<div class="hub-repo-left">
|
|
@@ -841,6 +843,8 @@ function renderRepos(repos) {
|
|
|
841
843
|
<div class="hub-repo-subline">
|
|
842
844
|
${infoLine}
|
|
843
845
|
</div>
|
|
846
|
+
${usageLine}
|
|
847
|
+
${ticketFlowLine}
|
|
844
848
|
</div>
|
|
845
849
|
<div class="hub-repo-right">
|
|
846
850
|
${actions || ""}
|
|
@@ -889,6 +893,18 @@ function renderRepos(repos) {
|
|
|
889
893
|
.sort((a, b) => String(a.id).localeCompare(String(b.id)))
|
|
890
894
|
.forEach((wt) => renderRepoCard(wt, { isWorktreeRow: true }));
|
|
891
895
|
}
|
|
896
|
+
if (hubUsageUnmatched && hubUsageUnmatched.events) {
|
|
897
|
+
const note = document.createElement("div");
|
|
898
|
+
note.className = "hub-usage-unmatched-note muted small";
|
|
899
|
+
const total = formatTokensCompact(hubUsageUnmatched.totals?.total_tokens);
|
|
900
|
+
note.textContent = `Other: ${total} · ${hubUsageUnmatched.events}ev (unattributed)`;
|
|
901
|
+
repoListEl.appendChild(note);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
function renderReposWithScroll(repos) {
|
|
905
|
+
preserveScroll(repoListEl, () => {
|
|
906
|
+
renderRepos(repos);
|
|
907
|
+
}, { restoreOnNextFrame: true });
|
|
892
908
|
}
|
|
893
909
|
async function refreshHub() {
|
|
894
910
|
setButtonLoading(true);
|
|
@@ -898,9 +914,8 @@ async function refreshHub() {
|
|
|
898
914
|
markHubRefreshed();
|
|
899
915
|
saveSessionCache(HUB_CACHE_KEY, hubData);
|
|
900
916
|
renderSummary(data.repos || []);
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
await loadHubUsage().catch(() => { });
|
|
917
|
+
renderReposWithScroll(data.repos || []);
|
|
918
|
+
loadHubUsage({ silent: true }).catch(() => { });
|
|
904
919
|
}
|
|
905
920
|
catch (err) {
|
|
906
921
|
flash(err.message || "Hub request failed", "error");
|
|
@@ -909,40 +924,6 @@ async function refreshHub() {
|
|
|
909
924
|
setButtonLoading(false);
|
|
910
925
|
}
|
|
911
926
|
}
|
|
912
|
-
async function loadHubInbox() {
|
|
913
|
-
if (!hubInboxList)
|
|
914
|
-
return;
|
|
915
|
-
hubInboxList.innerHTML = "Loading…";
|
|
916
|
-
try {
|
|
917
|
-
const payload = (await api("/hub/messages", { method: "GET" }));
|
|
918
|
-
const items = payload?.items || [];
|
|
919
|
-
if (!items.length) {
|
|
920
|
-
hubInboxList.innerHTML = '<div class="muted">No paused runs</div>';
|
|
921
|
-
return;
|
|
922
|
-
}
|
|
923
|
-
hubInboxList.innerHTML = items
|
|
924
|
-
.map((item) => {
|
|
925
|
-
const title = item.message?.title || item.message?.mode || "Message";
|
|
926
|
-
const excerpt = item.message?.body ? item.message.body.slice(0, 160) : "";
|
|
927
|
-
const repoLabel = item.repo_display_name || item.repo_id;
|
|
928
|
-
const href = item.open_url || `/repos/${item.repo_id}/?tab=messages&run_id=${item.run_id}`;
|
|
929
|
-
return `
|
|
930
|
-
<a class="hub-inbox-item" href="${escapeHtml(resolvePath(href))}">
|
|
931
|
-
<div class="hub-inbox-item-header">
|
|
932
|
-
<span class="hub-inbox-repo">${escapeHtml(repoLabel)}</span>
|
|
933
|
-
<span class="pill pill-small pill-warn">paused</span>
|
|
934
|
-
</div>
|
|
935
|
-
<div class="hub-inbox-title">${escapeHtml(title)}</div>
|
|
936
|
-
<div class="hub-inbox-excerpt muted small">${escapeHtml(excerpt)}</div>
|
|
937
|
-
</a>
|
|
938
|
-
`;
|
|
939
|
-
})
|
|
940
|
-
.join("");
|
|
941
|
-
}
|
|
942
|
-
catch (_err) {
|
|
943
|
-
hubInboxList.innerHTML = '';
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
927
|
async function triggerHubScan() {
|
|
947
928
|
setButtonLoading(true);
|
|
948
929
|
try {
|
|
@@ -1070,7 +1051,12 @@ async function handleRepoAction(repoId, action) {
|
|
|
1070
1051
|
if (!ok)
|
|
1071
1052
|
return;
|
|
1072
1053
|
await startHubJob("/hub/jobs/worktrees/cleanup", {
|
|
1073
|
-
body: {
|
|
1054
|
+
body: {
|
|
1055
|
+
worktree_repo_id: repoId,
|
|
1056
|
+
archive: true,
|
|
1057
|
+
force_archive: false,
|
|
1058
|
+
archive_note: null,
|
|
1059
|
+
},
|
|
1074
1060
|
startedMessage: "Worktree cleanup queued",
|
|
1075
1061
|
});
|
|
1076
1062
|
flash(`Removed worktree: ${repoId}`, "success");
|
|
@@ -1152,22 +1138,14 @@ async function handleRepoAction(repoId, action) {
|
|
|
1152
1138
|
}
|
|
1153
1139
|
function attachHubHandlers() {
|
|
1154
1140
|
initHubSettings();
|
|
1155
|
-
const scanBtn = document.getElementById("hub-scan");
|
|
1156
|
-
const refreshBtn = document.getElementById("hub-refresh");
|
|
1157
1141
|
const quickScanBtn = document.getElementById("hub-quick-scan");
|
|
1158
1142
|
const newRepoBtn = document.getElementById("hub-new-repo");
|
|
1159
1143
|
const createCancelBtn = document.getElementById("create-repo-cancel");
|
|
1160
1144
|
const createSubmitBtn = document.getElementById("create-repo-submit");
|
|
1161
1145
|
const createRepoId = document.getElementById("create-repo-id");
|
|
1162
|
-
if (scanBtn) {
|
|
1163
|
-
scanBtn.addEventListener("click", () => triggerHubScan());
|
|
1164
|
-
}
|
|
1165
1146
|
if (quickScanBtn) {
|
|
1166
1147
|
quickScanBtn.addEventListener("click", () => triggerHubScan());
|
|
1167
1148
|
}
|
|
1168
|
-
if (refreshBtn) {
|
|
1169
|
-
refreshBtn.addEventListener("click", () => refreshHub());
|
|
1170
|
-
}
|
|
1171
1149
|
if (hubUsageRefresh) {
|
|
1172
1150
|
hubUsageRefresh.addEventListener("click", () => loadHubUsage());
|
|
1173
1151
|
}
|
|
@@ -1245,7 +1223,7 @@ async function silentRefreshHub() {
|
|
|
1245
1223
|
markHubRefreshed();
|
|
1246
1224
|
saveSessionCache(HUB_CACHE_KEY, hubData);
|
|
1247
1225
|
renderSummary(data.repos || []);
|
|
1248
|
-
|
|
1226
|
+
renderReposWithScroll(data.repos || []);
|
|
1249
1227
|
await loadHubUsage({ silent: true, allowRetry: false });
|
|
1250
1228
|
}
|
|
1251
1229
|
catch (err) {
|
|
@@ -1267,15 +1245,20 @@ async function dynamicRefreshHub() {
|
|
|
1267
1245
|
await silentRefreshHub();
|
|
1268
1246
|
}
|
|
1269
1247
|
async function loadHubVersion() {
|
|
1270
|
-
if (!hubVersionEl)
|
|
1271
|
-
return;
|
|
1272
1248
|
try {
|
|
1273
1249
|
const data = await api("/hub/version", { method: "GET" });
|
|
1274
1250
|
const version = data.asset_version || "";
|
|
1275
|
-
|
|
1251
|
+
const formatted = version ? `v${version}` : "v–";
|
|
1252
|
+
if (hubVersionEl)
|
|
1253
|
+
hubVersionEl.textContent = formatted;
|
|
1254
|
+
if (pmaVersionEl)
|
|
1255
|
+
pmaVersionEl.textContent = formatted;
|
|
1276
1256
|
}
|
|
1277
1257
|
catch (_err) {
|
|
1278
|
-
hubVersionEl
|
|
1258
|
+
if (hubVersionEl)
|
|
1259
|
+
hubVersionEl.textContent = "v–";
|
|
1260
|
+
if (pmaVersionEl)
|
|
1261
|
+
pmaVersionEl.textContent = "v–";
|
|
1279
1262
|
}
|
|
1280
1263
|
}
|
|
1281
1264
|
async function checkUpdateStatus() {
|
|
@@ -1307,25 +1290,26 @@ export function initHub() {
|
|
|
1307
1290
|
return;
|
|
1308
1291
|
attachHubHandlers();
|
|
1309
1292
|
initHubUsageChartControls();
|
|
1310
|
-
hubInboxRefresh?.addEventListener("click", () => {
|
|
1311
|
-
void loadHubInbox();
|
|
1312
|
-
});
|
|
1313
1293
|
const cachedHub = loadSessionCache(HUB_CACHE_KEY, HUB_CACHE_TTL_MS);
|
|
1314
1294
|
if (cachedHub) {
|
|
1315
1295
|
hubData = cachedHub;
|
|
1316
1296
|
renderSummary(cachedHub.repos || []);
|
|
1317
|
-
|
|
1297
|
+
renderReposWithScroll(cachedHub.repos || []);
|
|
1318
1298
|
}
|
|
1319
1299
|
const cachedUsage = loadSessionCache(HUB_USAGE_CACHE_KEY, HUB_CACHE_TTL_MS);
|
|
1320
1300
|
if (cachedUsage) {
|
|
1321
|
-
|
|
1301
|
+
indexHubUsage(cachedUsage);
|
|
1302
|
+
renderHubUsageMeta(cachedUsage);
|
|
1322
1303
|
}
|
|
1323
1304
|
loadHubUsageSeries();
|
|
1324
1305
|
refreshHub();
|
|
1325
1306
|
loadHubVersion();
|
|
1326
1307
|
checkUpdateStatus();
|
|
1327
1308
|
registerAutoRefresh("hub-repos", {
|
|
1328
|
-
callback: async () => {
|
|
1309
|
+
callback: async (ctx) => {
|
|
1310
|
+
void ctx;
|
|
1311
|
+
await dynamicRefreshHub();
|
|
1312
|
+
},
|
|
1329
1313
|
tabId: null,
|
|
1330
1314
|
interval: HUB_REFRESH_ACTIVE_MS,
|
|
1331
1315
|
refreshOnActivation: true,
|
|
@@ -1334,5 +1318,4 @@ export function initHub() {
|
|
|
1334
1318
|
}
|
|
1335
1319
|
export const __hubTest = {
|
|
1336
1320
|
renderRepos,
|
|
1337
|
-
renderHubUsage,
|
|
1338
1321
|
};
|