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
codex_autorunner/static/hub.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
1
2
|
import { api, flash, statusPill, resolvePath, escapeHtml, confirmModal, inputModal, openModal, } from "./utils.js";
|
|
2
3
|
import { registerAutoRefresh } from "./autoRefresh.js";
|
|
3
4
|
import { HUB_BASE } from "./env.js";
|
|
5
|
+
import { preserveScroll } from "./preserve.js";
|
|
4
6
|
let hubData = { repos: [], last_scan_at: null };
|
|
5
|
-
const repoPrCache = new Map();
|
|
6
|
-
const repoPrFetches = new Set();
|
|
7
7
|
const prefetchedUrls = new Set();
|
|
8
|
+
let hubInboxHydrated = false;
|
|
8
9
|
const HUB_CACHE_TTL_MS = 30000;
|
|
9
10
|
const HUB_CACHE_KEY = `car:hub:${HUB_BASE || "/"}`;
|
|
10
11
|
const HUB_USAGE_CACHE_KEY = `car:hub-usage:${HUB_BASE || "/"}`;
|
|
11
|
-
const PR_CACHE_TTL_MS = 120000;
|
|
12
|
-
const PR_FAILURE_TTL_MS = 15000;
|
|
13
|
-
const PR_FETCH_CONCURRENCY = 3;
|
|
14
|
-
const PR_PREFETCH_MARGIN = "200px";
|
|
15
12
|
const HUB_REFRESH_ACTIVE_MS = 5000;
|
|
16
13
|
const HUB_REFRESH_IDLE_MS = 30000;
|
|
17
14
|
let lastHubAutoRefreshAt = 0;
|
|
@@ -20,13 +17,14 @@ const lastScanEl = document.getElementById("hub-last-scan");
|
|
|
20
17
|
const totalEl = document.getElementById("hub-count-total");
|
|
21
18
|
const runningEl = document.getElementById("hub-count-running");
|
|
22
19
|
const missingEl = document.getElementById("hub-count-missing");
|
|
23
|
-
const hubUsageList = document.getElementById("hub-usage-list");
|
|
24
20
|
const hubUsageMeta = document.getElementById("hub-usage-meta");
|
|
25
21
|
const hubUsageRefresh = document.getElementById("hub-usage-refresh");
|
|
26
22
|
const hubUsageChartCanvas = document.getElementById("hub-usage-chart-canvas");
|
|
27
23
|
const hubUsageChartRange = document.getElementById("hub-usage-chart-range");
|
|
28
24
|
const hubUsageChartSegment = document.getElementById("hub-usage-chart-segment");
|
|
29
25
|
const hubVersionEl = document.getElementById("hub-version");
|
|
26
|
+
const hubInboxList = document.getElementById("hub-inbox-list");
|
|
27
|
+
const hubInboxRefresh = document.getElementById("hub-inbox-refresh");
|
|
30
28
|
const UPDATE_STATUS_SEEN_KEY = "car_update_status_seen";
|
|
31
29
|
const HUB_JOB_POLL_INTERVAL_MS = 1200;
|
|
32
30
|
const HUB_JOB_TIMEOUT_MS = 180000;
|
|
@@ -37,10 +35,8 @@ const hubUsageChartState = {
|
|
|
37
35
|
};
|
|
38
36
|
let hubUsageSeriesRetryTimer = null;
|
|
39
37
|
let hubUsageSummaryRetryTimer = null;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
let repoPrActive = 0;
|
|
43
|
-
let repoPrObserver = null;
|
|
38
|
+
let hubUsageIndex = {};
|
|
39
|
+
let hubUsageUnmatched = null;
|
|
44
40
|
function saveSessionCache(key, value) {
|
|
45
41
|
try {
|
|
46
42
|
const payload = { at: Date.now(), value };
|
|
@@ -183,49 +179,37 @@ function formatTokensAxis(val) {
|
|
|
183
179
|
return `${(num / 1000).toFixed(1)}k`;
|
|
184
180
|
return Math.round(num).toString();
|
|
185
181
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
182
|
+
function getRepoUsage(repoId) {
|
|
183
|
+
const usage = hubUsageIndex[repoId];
|
|
184
|
+
if (!usage)
|
|
185
|
+
return { label: "—", meta: "", hasData: false };
|
|
186
|
+
const totals = usage.totals || {};
|
|
187
|
+
const cached = totals.cached_input_tokens || 0;
|
|
188
|
+
const input = totals.input_tokens || 0;
|
|
189
|
+
const cachePercent = input ? Math.round((cached / input) * 100) : 0;
|
|
190
|
+
const meta = usage.events === undefined
|
|
191
|
+
? ""
|
|
192
|
+
: `${usage.events}ev${input ? ` · ${cachePercent}%↻` : ""}`;
|
|
193
|
+
return {
|
|
194
|
+
label: formatTokensCompact(totals.total_tokens),
|
|
195
|
+
meta,
|
|
196
|
+
hasData: true,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function indexHubUsage(data) {
|
|
200
|
+
hubUsageIndex = {};
|
|
201
|
+
hubUsageUnmatched = data?.unmatched || null;
|
|
202
|
+
if (!data?.repos)
|
|
188
203
|
return;
|
|
204
|
+
data.repos.forEach((repo) => {
|
|
205
|
+
if (repo?.id)
|
|
206
|
+
hubUsageIndex[repo.id] = repo;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function renderHubUsageMeta(data) {
|
|
189
210
|
if (hubUsageMeta) {
|
|
190
211
|
hubUsageMeta.textContent = data?.codex_home || "–";
|
|
191
212
|
}
|
|
192
|
-
if (!data || !data.repos) {
|
|
193
|
-
hubUsageList.innerHTML =
|
|
194
|
-
'<span class="muted small">Usage unavailable</span>';
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
if (!data.repos.length && (!data.unmatched || !data.unmatched.events)) {
|
|
198
|
-
hubUsageList.innerHTML = '<span class="muted small">No token events</span>';
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
hubUsageList.innerHTML = "";
|
|
202
|
-
const entries = [...data.repos].sort((a, b) => (b.totals?.total_tokens || 0) - (a.totals?.total_tokens || 0));
|
|
203
|
-
entries.forEach((repo) => {
|
|
204
|
-
const div = document.createElement("div");
|
|
205
|
-
div.className = "hub-usage-chip";
|
|
206
|
-
const totals = repo.totals || {};
|
|
207
|
-
const cached = totals.cached_input_tokens || 0;
|
|
208
|
-
const cachePercent = totals.input_tokens
|
|
209
|
-
? Math.round((cached / totals.input_tokens) * 100)
|
|
210
|
-
: 0;
|
|
211
|
-
div.innerHTML = `
|
|
212
|
-
<span class="hub-usage-chip-name">${escapeHtml(repo.id)}</span>
|
|
213
|
-
<span class="hub-usage-chip-total">${escapeHtml(formatTokensCompact(totals.total_tokens))}</span>
|
|
214
|
-
<span class="hub-usage-chip-meta">${escapeHtml(`${repo.events ?? 0}ev · ${cachePercent}%↻`)}</span>
|
|
215
|
-
`;
|
|
216
|
-
hubUsageList.appendChild(div);
|
|
217
|
-
});
|
|
218
|
-
if (data.unmatched && data.unmatched.events) {
|
|
219
|
-
const div = document.createElement("div");
|
|
220
|
-
div.className = "hub-usage-chip hub-usage-chip-unmatched";
|
|
221
|
-
const totals = data.unmatched.totals || {};
|
|
222
|
-
div.innerHTML = `
|
|
223
|
-
<span class="hub-usage-chip-name">other</span>
|
|
224
|
-
<span class="hub-usage-chip-total">${escapeHtml(formatTokensCompact(totals.total_tokens))}</span>
|
|
225
|
-
<span class="hub-usage-chip-meta">${escapeHtml(`${data.unmatched.events}ev`)}</span>
|
|
226
|
-
`;
|
|
227
|
-
hubUsageList.appendChild(div);
|
|
228
|
-
}
|
|
229
213
|
}
|
|
230
214
|
function scheduleHubUsageSummaryRetry() {
|
|
231
215
|
clearHubUsageSummaryRetry();
|
|
@@ -241,30 +225,23 @@ function clearHubUsageSummaryRetry() {
|
|
|
241
225
|
}
|
|
242
226
|
function handleHubUsagePayload(data, { cachedUsage, allowRetry }) {
|
|
243
227
|
const hasSummary = data && Array.isArray(data.repos);
|
|
228
|
+
const effective = hasSummary ? data : cachedUsage;
|
|
229
|
+
if (effective) {
|
|
230
|
+
indexHubUsage(effective);
|
|
231
|
+
renderHubUsageMeta(effective);
|
|
232
|
+
renderReposWithScroll(hubData.repos || []);
|
|
233
|
+
}
|
|
244
234
|
if (data?.status === "loading") {
|
|
245
|
-
if (hasSummary) {
|
|
246
|
-
renderHubUsage(data);
|
|
247
|
-
}
|
|
248
|
-
else if (cachedUsage) {
|
|
249
|
-
renderHubUsage(cachedUsage);
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
renderHubUsage(data);
|
|
253
|
-
}
|
|
254
235
|
if (allowRetry)
|
|
255
236
|
scheduleHubUsageSummaryRetry();
|
|
256
|
-
return hasSummary;
|
|
237
|
+
return Boolean(hasSummary);
|
|
257
238
|
}
|
|
258
239
|
if (hasSummary) {
|
|
259
|
-
renderHubUsage(data);
|
|
260
240
|
clearHubUsageSummaryRetry();
|
|
261
241
|
return true;
|
|
262
242
|
}
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
renderHubUsage(null);
|
|
243
|
+
if (!effective && !data) {
|
|
244
|
+
renderReposWithScroll(hubData.repos || []);
|
|
268
245
|
}
|
|
269
246
|
return false;
|
|
270
247
|
}
|
|
@@ -286,10 +263,7 @@ async function loadHubUsage({ silent = false, allowRetry = true } = {}) {
|
|
|
286
263
|
catch (err) {
|
|
287
264
|
const cachedUsage = loadSessionCache(HUB_USAGE_CACHE_KEY, HUB_CACHE_TTL_MS);
|
|
288
265
|
if (cachedUsage) {
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
renderHubUsage(null);
|
|
266
|
+
handleHubUsagePayload(cachedUsage, { cachedUsage, allowRetry: false });
|
|
293
267
|
}
|
|
294
268
|
if (!silent) {
|
|
295
269
|
flash(err.message || "Failed to load usage", "error");
|
|
@@ -758,76 +732,9 @@ function inferBaseId(repo) {
|
|
|
758
732
|
}
|
|
759
733
|
return null;
|
|
760
734
|
}
|
|
761
|
-
function initRepoPrObserver() {
|
|
762
|
-
if (!("IntersectionObserver" in window))
|
|
763
|
-
return null;
|
|
764
|
-
if (repoPrObserver)
|
|
765
|
-
return repoPrObserver;
|
|
766
|
-
repoPrObserver = new IntersectionObserver((entries) => {
|
|
767
|
-
entries.forEach((entry) => {
|
|
768
|
-
if (!entry.isIntersecting)
|
|
769
|
-
return;
|
|
770
|
-
const target = entry.target;
|
|
771
|
-
const repoId = target?.dataset?.repoId;
|
|
772
|
-
if (repoId) {
|
|
773
|
-
const repo = (hubData.repos || []).find((item) => item.id === repoId);
|
|
774
|
-
if (repo)
|
|
775
|
-
scheduleRepoPrFetch(repo);
|
|
776
|
-
}
|
|
777
|
-
if (target)
|
|
778
|
-
repoPrObserver?.unobserve(target);
|
|
779
|
-
});
|
|
780
|
-
}, { rootMargin: PR_PREFETCH_MARGIN });
|
|
781
|
-
return repoPrObserver;
|
|
782
|
-
}
|
|
783
|
-
function scheduleRepoPrFetch(repo) {
|
|
784
|
-
if (!repo || repo.mounted !== true)
|
|
785
|
-
return;
|
|
786
|
-
const cached = repoPrCache.get(repo.id);
|
|
787
|
-
if (cached &&
|
|
788
|
-
typeof cached.fetchedAt === "number" &&
|
|
789
|
-
Date.now() - cached.fetchedAt <
|
|
790
|
-
(cached.failed ? PR_FAILURE_TTL_MS : PR_CACHE_TTL_MS)) {
|
|
791
|
-
return;
|
|
792
|
-
}
|
|
793
|
-
if (repoPrFetches.has(repo.id) || repoPrPending.has(repo.id))
|
|
794
|
-
return;
|
|
795
|
-
repoPrPending.add(repo.id);
|
|
796
|
-
repoPrQueue.push(repo);
|
|
797
|
-
pumpRepoPrQueue();
|
|
798
|
-
}
|
|
799
|
-
function pumpRepoPrQueue() {
|
|
800
|
-
while (repoPrActive < PR_FETCH_CONCURRENCY && repoPrQueue.length) {
|
|
801
|
-
const repo = repoPrQueue.shift();
|
|
802
|
-
if (!repo || repoPrFetches.has(repo.id))
|
|
803
|
-
continue;
|
|
804
|
-
repoPrPending.delete(repo.id);
|
|
805
|
-
repoPrActive += 1;
|
|
806
|
-
repoPrFetches.add(repo.id);
|
|
807
|
-
api(`/repos/${repo.id}/api/github/pr`, { method: "GET" })
|
|
808
|
-
.then((pr) => {
|
|
809
|
-
repoPrCache.set(repo.id, { data: pr, fetchedAt: Date.now() });
|
|
810
|
-
})
|
|
811
|
-
.catch(() => {
|
|
812
|
-
repoPrCache.set(repo.id, {
|
|
813
|
-
data: null,
|
|
814
|
-
fetchedAt: Date.now(),
|
|
815
|
-
failed: true,
|
|
816
|
-
});
|
|
817
|
-
})
|
|
818
|
-
.finally(() => {
|
|
819
|
-
repoPrFetches.delete(repo.id);
|
|
820
|
-
repoPrActive -= 1;
|
|
821
|
-
pumpRepoPrQueue();
|
|
822
|
-
renderRepos(hubData.repos || []);
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
735
|
function renderRepos(repos) {
|
|
827
736
|
if (!repoListEl)
|
|
828
737
|
return;
|
|
829
|
-
if (repoPrObserver)
|
|
830
|
-
repoPrObserver.disconnect();
|
|
831
738
|
repoListEl.innerHTML = "";
|
|
832
739
|
if (!repos.length) {
|
|
833
740
|
repoListEl.innerHTML =
|
|
@@ -902,12 +809,16 @@ function renderRepos(repos) {
|
|
|
902
809
|
const infoLine = infoItems.length > 0
|
|
903
810
|
? `<span class="hub-repo-info-line">${escapeHtml(infoItems.join(" · "))}</span>`
|
|
904
811
|
: "";
|
|
905
|
-
const
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
812
|
+
const usageInfo = getRepoUsage(repo.id);
|
|
813
|
+
const usageLine = `
|
|
814
|
+
<div class="hub-repo-usage-line${usageInfo.hasData ? "" : " muted"}">
|
|
815
|
+
<span class="pill pill-small hub-usage-pill">
|
|
816
|
+
${escapeHtml(usageInfo.label)}
|
|
817
|
+
</span>
|
|
818
|
+
${usageInfo.meta
|
|
819
|
+
? `<span class="hub-usage-pill-meta">${escapeHtml(usageInfo.meta)}</span>`
|
|
820
|
+
: ""}
|
|
821
|
+
</div>`;
|
|
911
822
|
card.innerHTML = `
|
|
912
823
|
<div class="hub-repo-row">
|
|
913
824
|
<div class="hub-repo-left">
|
|
@@ -920,8 +831,8 @@ function renderRepos(repos) {
|
|
|
920
831
|
<span class="hub-repo-title">${escapeHtml(repo.display_name)}</span>
|
|
921
832
|
<div class="hub-repo-subline">
|
|
922
833
|
${infoLine}
|
|
923
|
-
${prPill}
|
|
924
834
|
</div>
|
|
835
|
+
${usageLine}
|
|
925
836
|
</div>
|
|
926
837
|
<div class="hub-repo-right">
|
|
927
838
|
${actions || ""}
|
|
@@ -935,15 +846,6 @@ function renderRepos(repos) {
|
|
|
935
846
|
statusPill(statusEl, repo.status);
|
|
936
847
|
}
|
|
937
848
|
repoListEl.appendChild(card);
|
|
938
|
-
if (repo.mounted === true) {
|
|
939
|
-
const observer = initRepoPrObserver();
|
|
940
|
-
if (observer) {
|
|
941
|
-
observer.observe(card);
|
|
942
|
-
}
|
|
943
|
-
else {
|
|
944
|
-
scheduleRepoPrFetch(repo);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
849
|
};
|
|
948
850
|
orderedGroups.forEach((group) => {
|
|
949
851
|
const repo = group.base;
|
|
@@ -979,25 +881,18 @@ function renderRepos(repos) {
|
|
|
979
881
|
.sort((a, b) => String(a.id).localeCompare(String(b.id)))
|
|
980
882
|
.forEach((wt) => renderRepoCard(wt, { isWorktreeRow: true }));
|
|
981
883
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
if (observer && repoListEl) {
|
|
989
|
-
mounted.forEach((repo) => {
|
|
990
|
-
const card = repoListEl.querySelector(`[data-repo-id="${repo.id}"]`);
|
|
991
|
-
if (card) {
|
|
992
|
-
observer.observe(card);
|
|
993
|
-
}
|
|
994
|
-
else {
|
|
995
|
-
scheduleRepoPrFetch(repo);
|
|
996
|
-
}
|
|
997
|
-
});
|
|
998
|
-
return;
|
|
884
|
+
if (hubUsageUnmatched && hubUsageUnmatched.events) {
|
|
885
|
+
const note = document.createElement("div");
|
|
886
|
+
note.className = "hub-usage-unmatched-note muted small";
|
|
887
|
+
const total = formatTokensCompact(hubUsageUnmatched.totals?.total_tokens);
|
|
888
|
+
note.textContent = `Other: ${total} · ${hubUsageUnmatched.events}ev (unattributed)`;
|
|
889
|
+
repoListEl.appendChild(note);
|
|
999
890
|
}
|
|
1000
|
-
|
|
891
|
+
}
|
|
892
|
+
function renderReposWithScroll(repos) {
|
|
893
|
+
preserveScroll(repoListEl, () => {
|
|
894
|
+
renderRepos(repos);
|
|
895
|
+
}, { restoreOnNextFrame: true });
|
|
1001
896
|
}
|
|
1002
897
|
async function refreshHub() {
|
|
1003
898
|
setButtonLoading(true);
|
|
@@ -1007,9 +902,9 @@ async function refreshHub() {
|
|
|
1007
902
|
markHubRefreshed();
|
|
1008
903
|
saveSessionCache(HUB_CACHE_KEY, hubData);
|
|
1009
904
|
renderSummary(data.repos || []);
|
|
1010
|
-
|
|
1011
|
-
await
|
|
1012
|
-
|
|
905
|
+
renderReposWithScroll(data.repos || []);
|
|
906
|
+
await loadHubInbox().catch(() => { });
|
|
907
|
+
loadHubUsage({ silent: true }).catch(() => { });
|
|
1013
908
|
}
|
|
1014
909
|
catch (err) {
|
|
1015
910
|
flash(err.message || "Hub request failed", "error");
|
|
@@ -1018,6 +913,46 @@ async function refreshHub() {
|
|
|
1018
913
|
setButtonLoading(false);
|
|
1019
914
|
}
|
|
1020
915
|
}
|
|
916
|
+
async function loadHubInbox(ctx) {
|
|
917
|
+
if (!hubInboxList)
|
|
918
|
+
return;
|
|
919
|
+
if (!hubInboxHydrated || ctx?.reason === "manual") {
|
|
920
|
+
hubInboxList.textContent = "Loading…";
|
|
921
|
+
}
|
|
922
|
+
try {
|
|
923
|
+
const payload = (await api("/hub/messages", { method: "GET" }));
|
|
924
|
+
const items = payload?.items || [];
|
|
925
|
+
const html = !items.length
|
|
926
|
+
? '<div class="muted">No paused runs</div>'
|
|
927
|
+
: items
|
|
928
|
+
.map((item) => {
|
|
929
|
+
const title = item.message?.title || item.message?.mode || "Message";
|
|
930
|
+
const excerpt = item.message?.body ? item.message.body.slice(0, 160) : "";
|
|
931
|
+
const repoLabel = item.repo_display_name || item.repo_id;
|
|
932
|
+
const href = item.open_url || `/repos/${item.repo_id}/?tab=messages&run_id=${item.run_id}`;
|
|
933
|
+
return `
|
|
934
|
+
<a class="hub-inbox-item" href="${escapeHtml(resolvePath(href))}">
|
|
935
|
+
<div class="hub-inbox-item-header">
|
|
936
|
+
<span class="hub-inbox-repo">${escapeHtml(repoLabel)}</span>
|
|
937
|
+
<span class="pill pill-small pill-warn">paused</span>
|
|
938
|
+
</div>
|
|
939
|
+
<div class="hub-inbox-title">${escapeHtml(title)}</div>
|
|
940
|
+
<div class="hub-inbox-excerpt muted small">${escapeHtml(excerpt)}</div>
|
|
941
|
+
</a>
|
|
942
|
+
`;
|
|
943
|
+
})
|
|
944
|
+
.join("");
|
|
945
|
+
preserveScroll(hubInboxList, () => {
|
|
946
|
+
hubInboxList.innerHTML = html;
|
|
947
|
+
}, { restoreOnNextFrame: true });
|
|
948
|
+
hubInboxHydrated = true;
|
|
949
|
+
}
|
|
950
|
+
catch (_err) {
|
|
951
|
+
preserveScroll(hubInboxList, () => {
|
|
952
|
+
hubInboxList.innerHTML = "";
|
|
953
|
+
}, { restoreOnNextFrame: true });
|
|
954
|
+
}
|
|
955
|
+
}
|
|
1021
956
|
async function triggerHubScan() {
|
|
1022
957
|
setButtonLoading(true);
|
|
1023
958
|
try {
|
|
@@ -1145,7 +1080,12 @@ async function handleRepoAction(repoId, action) {
|
|
|
1145
1080
|
if (!ok)
|
|
1146
1081
|
return;
|
|
1147
1082
|
await startHubJob("/hub/jobs/worktrees/cleanup", {
|
|
1148
|
-
body: {
|
|
1083
|
+
body: {
|
|
1084
|
+
worktree_repo_id: repoId,
|
|
1085
|
+
archive: true,
|
|
1086
|
+
force_archive: false,
|
|
1087
|
+
archive_note: null,
|
|
1088
|
+
},
|
|
1149
1089
|
startedMessage: "Worktree cleanup queued",
|
|
1150
1090
|
});
|
|
1151
1091
|
flash(`Removed worktree: ${repoId}`, "success");
|
|
@@ -1266,11 +1206,6 @@ function attachHubHandlers() {
|
|
|
1266
1206
|
if (repoListEl) {
|
|
1267
1207
|
repoListEl.addEventListener("click", (event) => {
|
|
1268
1208
|
const target = event.target;
|
|
1269
|
-
const prLink = target instanceof HTMLElement && target.closest("a.hub-pr-pill");
|
|
1270
|
-
if (prLink) {
|
|
1271
|
-
event.stopPropagation();
|
|
1272
|
-
return;
|
|
1273
|
-
}
|
|
1274
1209
|
const btn = target instanceof HTMLElement && target.closest("button[data-action]");
|
|
1275
1210
|
if (btn) {
|
|
1276
1211
|
event.stopPropagation();
|
|
@@ -1325,7 +1260,7 @@ async function silentRefreshHub() {
|
|
|
1325
1260
|
markHubRefreshed();
|
|
1326
1261
|
saveSessionCache(HUB_CACHE_KEY, hubData);
|
|
1327
1262
|
renderSummary(data.repos || []);
|
|
1328
|
-
|
|
1263
|
+
renderReposWithScroll(data.repos || []);
|
|
1329
1264
|
await loadHubUsage({ silent: true, allowRetry: false });
|
|
1330
1265
|
}
|
|
1331
1266
|
catch (err) {
|
|
@@ -1387,22 +1322,29 @@ export function initHub() {
|
|
|
1387
1322
|
return;
|
|
1388
1323
|
attachHubHandlers();
|
|
1389
1324
|
initHubUsageChartControls();
|
|
1325
|
+
hubInboxRefresh?.addEventListener("click", () => {
|
|
1326
|
+
void loadHubInbox({ reason: "manual" });
|
|
1327
|
+
});
|
|
1390
1328
|
const cachedHub = loadSessionCache(HUB_CACHE_KEY, HUB_CACHE_TTL_MS);
|
|
1391
1329
|
if (cachedHub) {
|
|
1392
1330
|
hubData = cachedHub;
|
|
1393
1331
|
renderSummary(cachedHub.repos || []);
|
|
1394
|
-
|
|
1332
|
+
renderReposWithScroll(cachedHub.repos || []);
|
|
1395
1333
|
}
|
|
1396
1334
|
const cachedUsage = loadSessionCache(HUB_USAGE_CACHE_KEY, HUB_CACHE_TTL_MS);
|
|
1397
1335
|
if (cachedUsage) {
|
|
1398
|
-
|
|
1336
|
+
indexHubUsage(cachedUsage);
|
|
1337
|
+
renderHubUsageMeta(cachedUsage);
|
|
1399
1338
|
}
|
|
1400
1339
|
loadHubUsageSeries();
|
|
1401
1340
|
refreshHub();
|
|
1402
1341
|
loadHubVersion();
|
|
1403
1342
|
checkUpdateStatus();
|
|
1404
1343
|
registerAutoRefresh("hub-repos", {
|
|
1405
|
-
callback: async () => {
|
|
1344
|
+
callback: async (ctx) => {
|
|
1345
|
+
void ctx;
|
|
1346
|
+
await dynamicRefreshHub();
|
|
1347
|
+
},
|
|
1406
1348
|
tabId: null,
|
|
1407
1349
|
interval: HUB_REFRESH_ACTIVE_MS,
|
|
1408
1350
|
refreshOnActivation: true,
|
|
@@ -1411,5 +1353,4 @@ export function initHub() {
|
|
|
1411
1353
|
}
|
|
1412
1354
|
export const __hubTest = {
|
|
1413
1355
|
renderRepos,
|
|
1414
|
-
renderHubUsage,
|
|
1415
1356
|
};
|