@overdeck/desktop 0.43.0 → 0.44.0
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.
- package/package.json +1 -1
- package/resources/icon.ico +0 -0
- package/resources/icon.png +0 -0
- package/server/public/assets/{architectureDiagram-3BPJPVTR-CDm4DtgX.js → architectureDiagram-3BPJPVTR-D1cX9STH.js} +1 -1
- package/server/public/assets/{blockDiagram-GPEHLZMM-B0CHnHbL.js → blockDiagram-GPEHLZMM-_U_GESDt.js} +1 -1
- package/server/public/assets/{c4Diagram-AAUBKEIU-CwHAm-Sh.js → c4Diagram-AAUBKEIU-DNF40ymm.js} +1 -1
- package/server/public/assets/channel-Dz_N3rZ1.js +1 -0
- package/server/public/assets/{chunk-2J33WTMH-BIWv54To.js → chunk-2J33WTMH-CphN4I1h.js} +1 -1
- package/server/public/assets/{chunk-3OPIFGDE-BIj0tSkO.js → chunk-3OPIFGDE-pHasFymF.js} +1 -1
- package/server/public/assets/{chunk-4BX2VUAB-xpGve5oU.js → chunk-4BX2VUAB-CDe6cmmN.js} +1 -1
- package/server/public/assets/chunk-55IACEB6-CB9aM4Zf.js +1 -0
- package/server/public/assets/{chunk-5ZQYHXKU-DYb235_b.js → chunk-5ZQYHXKU-CKfVKs9N.js} +1 -1
- package/server/public/assets/{chunk-727SXJPM-BYV0QAgl.js → chunk-727SXJPM-D4mt-WN2.js} +1 -1
- package/server/public/assets/{chunk-AQP2D5EJ-Cb8WTwxH.js → chunk-AQP2D5EJ-DKi5ScG8.js} +1 -1
- package/server/public/assets/chunk-BO2N2NFS-DurskaRQ.js +103 -0
- package/server/public/assets/{chunk-BSJP7CBP-DNZJE3LD.js → chunk-BSJP7CBP-B1PFLITi.js} +1 -1
- package/server/public/assets/{chunk-CSCIHK7Q-Cd19qyMq.js → chunk-CSCIHK7Q-BnyS1BnJ.js} +1 -1
- package/server/public/assets/{chunk-FMBD7UC4-BCMOtjng.js → chunk-FMBD7UC4-BB-NMiJi.js} +1 -1
- package/server/public/assets/{chunk-K2UTITRG-BDx74R_n.js → chunk-K2UTITRG-CopEAH22.js} +3 -3
- package/server/public/assets/{chunk-KSCS5N6A-C9ZcXuPw.js → chunk-KSCS5N6A-BF9pAvxk.js} +1 -1
- package/server/public/assets/{chunk-L5ZTLDWV-DERZu8R6.js → chunk-L5ZTLDWV-CSCc1dpF.js} +1 -1
- package/server/public/assets/{chunk-LZXEDZCA-r8NSoc_I.js → chunk-LZXEDZCA-AFwhyAh-.js} +2 -2
- package/server/public/assets/{chunk-ND2GUHAM-U8N4hXOQ.js → chunk-ND2GUHAM-C63t7vfP.js} +1 -1
- package/server/public/assets/{chunk-NZK2D7GU-Cdikaqy1.js → chunk-NZK2D7GU-D1aXVmQS.js} +1 -1
- package/server/public/assets/{chunk-O5CBEL6O-NPUwuba7.js → chunk-O5CBEL6O-U50Oge0P.js} +1 -1
- package/server/public/assets/chunk-QZHKN3VN-DTNr_gzK.js +1 -0
- package/server/public/assets/chunk-WU5MYG2G-CI9Sd9Ej.js +1 -0
- package/server/public/assets/{chunk-XPW4576I-D4sOPQ12.js → chunk-XPW4576I-TDS4g8F-.js} +1 -1
- package/server/public/assets/classDiagram-4FO5ZUOK-ZZslPdIZ.js +1 -0
- package/server/public/assets/classDiagram-v2-Q7XG4LA2-ClxLOE6-.js +1 -0
- package/server/public/assets/{cose-bilkent-S5V4N54A-DbIsCp9l.js → cose-bilkent-S5V4N54A-CXK4TeG6.js} +1 -1
- package/server/public/assets/{dagre-BM42HDAG-BjoYNKH1.js → dagre-BM42HDAG-CekTT-9t.js} +1 -1
- package/server/public/assets/{diagram-2AECGRRQ-BQDDtbjI.js → diagram-2AECGRRQ-C-1uR13O.js} +1 -1
- package/server/public/assets/{diagram-5GNKFQAL-B5QGvpdq.js → diagram-5GNKFQAL-B-7wXipw.js} +1 -1
- package/server/public/assets/{diagram-KO2AKTUF-BPB4GnYb.js → diagram-KO2AKTUF-BkDyAgsg.js} +1 -1
- package/server/public/assets/{diagram-LMA3HP47-ChM6kUb7.js → diagram-LMA3HP47-D9jVbxR4.js} +1 -1
- package/server/public/assets/{diagram-OG6HWLK6-CILjxFV5.js → diagram-OG6HWLK6-Dk7-KYG7.js} +1 -1
- package/server/public/assets/{dist-WopvNJhW.js → dist-CaEw8nYm.js} +5 -5
- package/server/public/assets/{dist-BBtIkNyE.js → dist-Dm4--nOM.js} +1 -1
- package/server/public/assets/{erDiagram-TEJ5UH35-CHXlL8Uu.js → erDiagram-TEJ5UH35-BCzAAnUJ.js} +1 -1
- package/server/public/assets/{flowDiagram-I6XJVG4X-ChzzyfU2.js → flowDiagram-I6XJVG4X-WjKltgsz.js} +1 -1
- package/server/public/assets/ganttDiagram-6RSMTGT7-CFh4T1Yl.js +292 -0
- package/server/public/assets/{gitGraphDiagram-PVQCEYII-Ba1IlQOc.js → gitGraphDiagram-PVQCEYII-DQNkTLKB.js} +1 -1
- package/server/public/assets/{highlighted-body-OFNGDK62-BTG45eDk.js → highlighted-body-OFNGDK62--ggf-1jf.js} +1 -1
- package/server/public/assets/image-GAAHSSAO-rafcvWHh.js +1 -0
- package/server/public/assets/index-B-m-cSFg.css +1 -0
- package/server/public/assets/index-CuIG3f59.js +796 -0
- package/server/public/assets/{infoDiagram-5YYISTIA-Du9oCbUR.js → infoDiagram-5YYISTIA-kt8p6J3W.js} +1 -1
- package/server/public/assets/{ishikawaDiagram-YF4QCWOH-BQBblkcl.js → ishikawaDiagram-YF4QCWOH-CXB66ltc.js} +1 -1
- package/server/public/assets/{journeyDiagram-JHISSGLW-DalMBcZT.js → journeyDiagram-JHISSGLW-CSevbeC_.js} +1 -1
- package/server/public/assets/{kanban-definition-UN3LZRKU-ZZJcEe1c.js → kanban-definition-UN3LZRKU-f3SXWdY0.js} +1 -1
- package/server/public/assets/{line-DSWSgYSV.js → line-D5OFHSZd.js} +1 -1
- package/server/public/assets/linear-B_aOIp4o.js +1 -0
- package/server/public/assets/mermaid-GHXKKRXX-DvZawyn9.js +1 -0
- package/server/public/assets/{mermaid-parser.core-Yy_VTKKl.js → mermaid-parser.core-Cww26sHC.js} +1 -1
- package/server/public/assets/{mindmap-definition-RKZ34NQL-CZN2bKCU.js → mindmap-definition-RKZ34NQL-DCCvz6n-.js} +1 -1
- package/server/public/assets/{pieDiagram-4H26LBE5-C9XG5bKT.js → pieDiagram-4H26LBE5-_QEBgJ8d.js} +1 -1
- package/server/public/assets/{prod-BjCGQWZg.js → prod-oCB7Fr_u.js} +5 -5
- package/server/public/assets/{quadrantDiagram-W4KKPZXB-BqyPPpJS.js → quadrantDiagram-W4KKPZXB-CAbpwVBa.js} +3 -3
- package/server/public/assets/{requirementDiagram-4Y6WPE33-qEFp8qJ8.js → requirementDiagram-4Y6WPE33-B5jvIMD7.js} +1 -1
- package/server/public/assets/{sankeyDiagram-5OEKKPKP-DOiWA53d.js → sankeyDiagram-5OEKKPKP-mPlHirkS.js} +1 -1
- package/server/public/assets/{sequenceDiagram-3UESZ5HK-DtNkSqZR.js → sequenceDiagram-3UESZ5HK-CM_I9wrD.js} +1 -1
- package/server/public/assets/{src-Csh0f24o.js → src-BYanRXvC.js} +1 -1
- package/server/public/assets/src-CdPLAdYJ.js +1 -0
- package/server/public/assets/{stateDiagram-AJRCARHV-DgaB8R7z.js → stateDiagram-AJRCARHV-C6Dqck84.js} +1 -1
- package/server/public/assets/stateDiagram-v2-BHNVJYJU-Bz3dgE_B.js +1 -0
- package/server/public/assets/{timeline-definition-PNZ67QCA-vhc6Bdap.js → timeline-definition-PNZ67QCA-B7tFs4Cq.js} +1 -1
- package/server/public/assets/{vennDiagram-CIIHVFJN-D7yEMTpU.js → vennDiagram-CIIHVFJN-DH3LC-SZ.js} +1 -1
- package/server/public/assets/{wardleyDiagram-YWT4CUSO-DAFN50tK.js → wardleyDiagram-YWT4CUSO-CeJXk7lo.js} +1 -1
- package/server/public/assets/{xychartDiagram-2RQKCTM6-BkfiVkrb.js → xychartDiagram-2RQKCTM6-m6Efea7b.js} +1 -1
- package/server/public/favicon.svg +4 -4
- package/server/public/index.html +5 -5
- package/server/server.js +1844 -424
- package/server/public/assets/channel-cOUdzmRM.js +0 -1
- package/server/public/assets/chunk-55IACEB6-_2lFlbAe.js +0 -1
- package/server/public/assets/chunk-BO2N2NFS-DT2hpoe9.js +0 -103
- package/server/public/assets/chunk-QZHKN3VN-zpNB_C8l.js +0 -1
- package/server/public/assets/chunk-WU5MYG2G-DDr09vEJ.js +0 -1
- package/server/public/assets/classDiagram-4FO5ZUOK-ezwkz8ms.js +0 -1
- package/server/public/assets/classDiagram-v2-Q7XG4LA2-DAphqbn4.js +0 -1
- package/server/public/assets/ganttDiagram-6RSMTGT7-CByisuCB.js +0 -292
- package/server/public/assets/image-GAAHSSAO-CCe2yzQe.js +0 -1
- package/server/public/assets/index-etdSnOxp.js +0 -818
- package/server/public/assets/index-qrDOFwsw.css +0 -1
- package/server/public/assets/linear-BlUaWFF8.js +0 -1
- package/server/public/assets/mermaid-GHXKKRXX-7G86hyh-.js +0 -1
- package/server/public/assets/src-CsT-wIyx.js +0 -1
- package/server/public/assets/stateDiagram-v2-BHNVJYJU-52jRo40M.js +0 -1
- /package/server/public/assets/{html-void-elements-QR3T3anA.js → html-void-elements-DIyBxxO2.js} +0 -0
- /package/server/public/assets/{preload-helper-Dd-HcVz_.js → preload-helper-DzyYoeor.js} +0 -0
package/server/server.js
CHANGED
|
@@ -1,103 +1,101 @@
|
|
|
1
1
|
import { a as __toCommonJS, i as __require, o as __toESM, t as __commonJSMin } from "./rolldown-runtime-DNCzzKnI.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { _ as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { n as
|
|
9
|
-
import {
|
|
10
|
-
import { a as
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { a as
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { n as
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import { t as
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import { i as
|
|
29
|
-
import { i as
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import { E as
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
import { n as
|
|
53
|
-
import { i as
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
57
|
-
import {
|
|
58
|
-
import {
|
|
59
|
-
import {
|
|
60
|
-
import {
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
63
|
-
import {
|
|
64
|
-
import { t as
|
|
65
|
-
import {
|
|
66
|
-
import { a as
|
|
67
|
-
import {
|
|
68
|
-
import {
|
|
69
|
-
import { a as
|
|
70
|
-
import {
|
|
71
|
-
import {
|
|
72
|
-
import {
|
|
73
|
-
import {
|
|
74
|
-
import {
|
|
75
|
-
import {
|
|
76
|
-
import {
|
|
77
|
-
import {
|
|
78
|
-
import {
|
|
79
|
-
import {
|
|
80
|
-
import {
|
|
81
|
-
import {
|
|
82
|
-
import { t as
|
|
83
|
-
import { n as
|
|
84
|
-
import { r as
|
|
85
|
-
import {
|
|
86
|
-
import { a as
|
|
87
|
-
import {
|
|
88
|
-
import {
|
|
89
|
-
import {
|
|
90
|
-
import { r as
|
|
91
|
-
import
|
|
92
|
-
import {
|
|
93
|
-
import "./supervisor-CNcM3RIc.js";
|
|
94
|
-
import { i as listTranscriptCheckpoints, r as getTranscriptCheckpoint$1 } from "./checkpoints-0C9dS0-x.js";
|
|
2
|
+
import { $ as getModelEffortLevelsSync, D as init_harness_policy, Dn as resolveProjectFromIssue, Dr as SYNC_SOURCES, Dt as init_record, Fr as packageRoot, G as DEFAULT_ROLES, Gn as init_auto_commit, I as getProviderForModelSync, In as extractPrefixSync, Ir as resolveOhmypiExtensionPath, K as DEFAULT_WORKHORSES, L as init_providers, Ln as init_issue_id, Lr as sessionFilePath, Mn as setProjectAutoMergeDefaultSync, Mr as getOverdeckHome, N as getBuiltInDefaultHarness, Nr as init_paths, On as resolveProjectFromIssueSync, P as getKimiAnthropicBaseUrl, Pr as isDevMode, R as piProviderForModel, S as resolveTieredExecutionEnabled, T as canUseHarnessSync, Tn as registerProjectSync, Tt as getIssueWorkspacePath, U as defaultBackgroundAiFeatures, Un as flushAllPendingAutoCommits, Vn as require_dist$1, W as init_registry, Wn as flushAutoCommits, X as MODEL_CAPABILITIES, Y as resolveModel, Z as MODEL_DEPRECATIONS, _ as mergeTtsDaemonConfigs, an as findProjectByTeamSync, b as TieredExecutionConfigError, bn as listProjectsSync, br as OVERDECK_HOME, cr as TrackerError, ct as PARENT_MODEL_REF, d as loadConfigSync, dr as AGENTS_DIR, er as FsError, et as hasModelCapabilitySync, f as updateConversationsConfig, g as getDefaultTtsDaemonConfig, gn as init_projects, gr as CLAUDE_DIR, h as mergeConfigs, i as getConversationsConfig, j as UnknownModelError, jr as getLegacyHome, kr as encodeClaudeProjectDir, l as isTldrEnabledSync, lt as ROLE_EFFORTS, m as jsYaml, n as clearConfigCache, nr as GitHubApiError, nt as modelSupportsImagesSync, o as getGlobalConfigPath, or as ProcessSpawnError, p as init_js_yaml, q as computeModelOrigin, r as getConversationSearchConfigSync, rt as resolveModelIdSync, s as isClaudeCodeChannelsEnabled, sn as getIssuePrefix, st as COMPLIANCE_MODES, t as init_config_yaml, tn as extractTeamPrefix, tt as init_model_capabilities, u as loadConfigNoMigration, un as getProjectSync, ur as init_errors, w as validateTieredExecutionConfig, x as init_tier_table, yn as listProjects, yr as LOGS_DIR, zn as parseIssueIdSync } from "./config-yaml-D0t3y9AC.js";
|
|
3
|
+
import { _ as getDashboardIdentity, c as startUatTrainReconciler, d as openFlywheelRunReportForDashboard, f as pauseFlywheelRunForDashboard, g as runDashboardDbJob, h as startFlywheelRunForDashboard, l as stopUatTrainReconciler, m as resumeFlywheelRunForDashboard, p as readCurrentFlywheelStatusForDashboard, u as abortFlywheelRunForDashboard, v as readHostDashboardApiPort, y as shouldRefuseHostDashboardPort } from "./uat-train-CpEtMAyD.js";
|
|
4
|
+
import { A as jsonResponse, D as validateOriginHeaders, E as validateOrigin$1, O as checkCodexAuthStatus, S as getHeaderFromMap, _ as resolveAgentHarness, a as agentsRouteLayer, b as resolveJsonlPath, c as evaluateSpawnGuardrails, d as isSmeeConfiguredSync, f as startSmeeProcessSync, g as readLauncherPinnedSessionId, j as jsonStringResponse, k as httpHandler, l as invalidateAgentsCache, m as resolveConversationGitInfo, o as saveAgentStateAndEmitEvent, p as resolveAgentGitInfo, s as saveAgentStateAndEmitEventProgram, t as specialistsRouteLayer, u as getSystemHealthSnapshot, v as resolveClaudeSessionId, w as isTrustedOriginForHost, x as resolvePiSessionPath, y as resolveCodexRolloutPath } from "./specialists-CWh4WUGp.js";
|
|
5
|
+
import { a as init_activity_logger, i as emitDashboardLifecycleSync, n as emitActivityEntrySync, r as emitActivityTtsSync } from "./activity-logger-Dm95Ua07.js";
|
|
6
|
+
import { A as init_paths$1, O as OVERDECK_MIGRATION_PATH, d as getOverdeckDatabaseSync, f as init_infra, i as DbLive, k as getOverdeckDatabasePath, u as TmuxLive } from "./infra-D6hW7oZO.js";
|
|
7
|
+
import { n as openDatabase, t as init_driver } from "./driver-D-HBOQa_.js";
|
|
8
|
+
import { E as listPaneValues, F as sendKeysAsync, G as buildChildEnvSync, H as init_ui_theme, I as sendRawKeystroke, K as buildChildEnvWithoutTmuxSync, L as sessionExists, M as resizeWindow, O as listSessionNames, P as sendKeys, S as isHarnessProcessAlive, U as setUiTheme, W as BLANKED_PROVIDER_ENV, b as getWindowDimensions, c as ensureManagedTmuxContextOnce, d as exactPaneTarget, i as capturePane, k as listSessions, n as buildTmuxArgs, o as createSession, p as findManagedServerPidSync, q as init_child_env, w as killSession, x as init_tmux, z as setOption } from "./tmux-Cuf3Wudb.js";
|
|
9
|
+
import { a as init_event_store, i as initEventStore, n as getEventStore, r as getSharedDb } from "./event-store-B35NfIvv.js";
|
|
10
|
+
import { _ as FlywheelRunId, a as getHarnessBehavior, b as DomainEvent, c as FlywheelStats, g as WS_METHODS, h as PanRpcGroup, m as PanRpcError, o as ContextLayerSaveRequest, s as ContextPreviewRequest, t as init_src, v as FlywheelStatus, x as MemoryObservation, y as EDITORS } from "./src-CgBTaItZ.js";
|
|
11
|
+
import { a as fixStuckReadyForMerge, c as init_review_status, i as fixStuckCommentedReviews, l as loadReadyForMergeFlags, n as clearReviewStatus, o as getReviewStatus, r as clearStuckMergeStatuses, s as getReviewStatusSync, u as loadReviewStatuses, v as setReviewStatus, y as setReviewStatusSync } from "./review-status-BRzbY-uJ.js";
|
|
12
|
+
import { a as EventStoreService, c as ReadModelService, d as init_read_model, f as toAgentResolution, i as init_issue_lifecycle, l as ReadModelServiceLive, m as toRole, n as IssueLifecycleLive, o as EventStoreServiceLive, p as toAgentStatus, s as init_domain_services, t as IssueLifecycle } from "./issue-lifecycle-BrYCFw67.js";
|
|
13
|
+
import { d as init_agents, i as recordBriefingSessionStart, n as ensureSessionContextBriefingFile, p as listAllAgentsSync, r as init_briefing_freshness, s as AgentsResolverLive, t as appendFreshBriefingUpdate } from "./briefing-freshness-CKbYolyA.js";
|
|
14
|
+
import { i as init_paths$2, l as resolveObservationsFile, r as ensureParentDir, s as resolveIssueMemoryRoot, t as assertMemorySafeSegment } from "./paths-gB-AoCbP.js";
|
|
15
|
+
import { n as listFeatureRegistryEntries, t as init_feature_registry_storage } from "./feature-registry-storage-DzHcsyeM.js";
|
|
16
|
+
import { a as diffAgainstMainFiles, c as diffFilesAgainstHead, d as findCommitAtTime, i as diffAgainstMain, l as diffPatchFilesAgainstHead, m as listCheckpoints, o as diffCheckpointFiles, p as init_checkpoint_manager, s as diffCheckpoints, t as captureCheckpoint, u as diffPatchSinceCommit } from "./checkpoint-manager-DCSBbepE.js";
|
|
17
|
+
import { $n as clearReadySignal, $t as getClaudeAuthStatus, Bt as getAgentRuntimeBaseCommand, Cn as getAgentDir, Ct as init_transcript_landing, Dn as getAgentStateSync, En as getAgentStateFilePath, Et as writeBridgeTokenSync, J as listAgentStates, Kt as injectPiConversationMemory, M as init_operator_interventions, N as operatorInterventionEvent, Qn as writePtyToken, Qt as ohmypiFifoPaths, St as captureTranscriptUserRecordSnapshot, Tn as getAgentState, Tt as init_bridge_token, Ut as getProviderAuthMode, Vn as saveAgentStateSync, X as listRunningAgentsSync, Xn as readAgentHarnessModelRecordSync, Xt as piFifoPaths, Y as listRunningAgents, Yn as init_agent_record_sync, Yt as init_pi_fifo, Zn as init_pty_token, Zt as init_ohmypi_fifo, _t as deliverAgentMessage, an as getProviderExportsForModel, ar as getAgentRuntimeState, c as init_tier_supervisor, en as init_claude_auth, f as supervisorAgentId, ft as init_config, gt as shouldAutoStart, hn as writeChannelsBridgeMcpConfig, ht as saveCloisterConfigSync, in as getProviderEnvForModel, ir as waitForReadySignal, j as messageAgent, ln as getOpenAIAuthStatus, mn as dismissDevChannelsDialog, mt as loadCloisterConfigSync, or as getAgentRuntimeStateSync, pt as loadCloisterConfig, rt as init_concurrency, s as deliverCommitForReview, t as init_agents$1, tr as normalizeAgentId, tt as emergencyBrake, un as init_openai_auth, wt as probeTranscriptSince } from "./agents-BODDwjMW.js";
|
|
18
|
+
import { t as init_behavior } from "./behavior-DqR8vtRW.js";
|
|
19
|
+
import { a as getCostForIssueAggregateSync, c as getDailyTrendsSync, d as init_cost_sync, l as getModelRollupSync, n as getBackgroundCostBySourceSync, p as queryCostEventsSync, r as getCavemanExperimentDataSync, s as getCostsByIssueSync, u as getTodayCostSync } from "./cost-sync-COCS9jfM.js";
|
|
20
|
+
import { c as getAgentsNeedingAttention, d as CostResolverLive, f as CostWriter, l as CostDoorLive, n as readCloisterStateFile, o as generateHealthSummary, s as getAgentHealth, t as getCloisterService, u as CostResolver } from "./service-yJX2ZDTb.js";
|
|
21
|
+
import { n as AgentStateServiceLive } from "./agent-state-service-BDYwkuao.js";
|
|
22
|
+
import { a as init_tracker_config, i as initTrackerConfigCache, n as getLinearApiKey, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-Bp-m78D_.js";
|
|
23
|
+
import { d as init_typed_errors, l as WorkspaceCreateError, n as AgentStartError, r as BeadsNotInitialized, s as TrackerApiError, t as AgentAlreadyRunning, u as WorkspaceNotFound } from "./typed-errors-BRDFMoss.js";
|
|
24
|
+
import { i as init_linear_client, r as LinearClientOptionalLive, t as LinearClient } from "./linear-client-BbxK-2hQ.js";
|
|
25
|
+
import { i as init_github_client, r as GitHubClientOptionalLive, t as GitHubClient } from "./github-client-ClT-9ZHn.js";
|
|
26
|
+
import { i as init_rally_client, r as RallyClientOptionalLive, t as RallyClient } from "./rally-client-DoecXLs_.js";
|
|
27
|
+
import { a as resolveTrackerTypeSync, i as resolveGitHubIssueSync, n as init_tracker_utils } from "./tracker-utils-BGlCcZn1.js";
|
|
28
|
+
import { a as CacheService, i as startSharedIssueService, n as init_issue_service_singleton, o as init_cache_service, r as issue_service_singleton_exports, s as parseIntegerHeader, t as getSharedIssueService } from "./issue-service-singleton-Driy3b99.js";
|
|
29
|
+
import { i as ensureInternalTokenSync, o as getInternalTokenSync, s as init_internal_token, t as INTERNAL_TOKEN_HEADER } from "./internal-token-DR2busqD.js";
|
|
30
|
+
import { a as setPipelineHandlerSync, t as init_pipeline_notifier } from "./pipeline-notifier-DpikL6KI.js";
|
|
31
|
+
import { c as init_review_status_sync, i as getMergeBlockerReconcileCandidates } from "./review-status-sync-TCRUOOb0.js";
|
|
32
|
+
import { C as transitionToMerging, _ as markFailed, c as isFlywheelGloballyPaused, d as listDuePendingAutoMerges, f as listProblemAutoMerges, g as markBlocked, n as getActionableAutoMerge, o as init_merge_sync, t as cancelPending, u as listActiveAutoMerges, v as markMerged, x as scheduleAutoMergeWithResult, y as requeueToPending } from "./merge-sync-CNvpnmSV.js";
|
|
33
|
+
import { B as asPanSpecDocument, C as findSpecByIssue, F as PAN_CONTINUE_FILENAME, I as PAN_DIRNAME, O as writeSpec, T as init_specs, V as init_types$1, a as findWorkspaceDraftPlan, b as init_types, k as writeSpecForIssue, m as readWorkspacePlanSync, o as init_io, p as readWorkspacePlan, r as findPlan, s as isPlanningComplete, u as readPlan, x as subItemsOf } from "./io-DyjLqqzR.js";
|
|
34
|
+
import { C as restartCliproxy, S as readPidFile, T as stopCliproxy, b as isCliproxyInstalled, d as getCliproxyAuthDir, o as bridgeCodexAuthToCliproxy, v as init_cliproxy, w as startCliproxy, x as isCliproxyRunning, y as installCliproxy } from "./cliproxy-BRAY5Min.js";
|
|
35
|
+
import { f as init_config$1, m as loadConfigSync$1, s as getDashboardApiUrlSync } from "./config-C83PzLrX.js";
|
|
36
|
+
import { n as require_auth_errors, r as require_token_error, t as require_token_util } from "./token-util-DEP-WK8V.js";
|
|
37
|
+
import { $ as updateConversationTitle, A as listArchivedConversationsWithEnrichment, C as getConversationLedgerCosts, D as incrementForkRetryCount, E as importLegacyConversation, F as markConversationRunning, G as setConversationModel, H as setConversationClaudeSessionId, J as setImportedConversationLinks, K as setFavorite, M as listFavoritedIds, N as markConversationActive, O as init_conversations, P as markConversationEnded, Q as updateConversationForkFallbackReason, R as recordConversationHandoff, S as getConversationByTmuxSession, T as hasOtherActiveConversationOnTmuxSession, U as setConversationEffort, V as setClearedToConvId, W as setConversationHarness, X as updateConversationCost, Y as unarchiveConversation, Z as updateConversationDeliveryMethod, _ as canReplaceTitle, b as getConversationById, et as updateForkStatus, g as backfillConversationModel, h as archiveConversation, ht as getDiscoveredSessionBySessionId, j as listConversations, k as isAgentConversationName, nt as updateSpawnError, q as setForkRequest, tt as updateLastAttached, v as createConversation, vt as init_discovered_sessions, w as getStuckForks, x as getConversationByName, y as getConversationByClaudeSessionId, z as removeFavorite } from "./conversations-DqMw7hzO.js";
|
|
38
|
+
import { _ as init_codex_conversation_parser, a as contextUsageFromParseResult, c as parseEntireConversation, d as init_pi_conversation_parser, f as isPiSessionFile, g as parseOhmypiConversationMessages, h as isOhmypiSessionFile, i as computeContextUsage, l as parseFromLastCompactBoundary, m as init_ohmypi_conversation_parser, n as gateSnapshotEmission, o as summarizeConversationActivity, p as parsePiConversationMessages, r as watchConversation, s as parseConversationMessages, t as init_conversation_service, v as parseCodexConversationMessages } from "./conversation-service-DMXhLnSO.js";
|
|
39
|
+
import { n as isBackgroundFeatureEnabled, t as init_features } from "./features-DwF4LaSW.js";
|
|
40
|
+
import { i as init_settings, r as getMemoryWorkerConcurrency, t as areMemoryObservationsEnabled } from "./settings-DWLiz0Nr.js";
|
|
41
|
+
import { i as updateMemoryHealth, n as extractWithProviderPolicy, r as init_health, t as init_providers$1 } from "./providers-cLWAz3kQ.js";
|
|
42
|
+
import { n as init_launcher_generator, t as generateLauncherScriptSync } from "./launcher-generator-sMFvWgWt.js";
|
|
43
|
+
import { a as init_claude_permissions, o as resolvePermissionModeSync, r as getClaudePermissionFlagsStringSync, t as BYPASS_PERMISSION_MODE } from "./claude-permissions-C0EMsI4l.js";
|
|
44
|
+
import { a as globalContextFile$1, d as workspaceContextFile, l as projectContextFile, n as codexGlobalContextFile, o as init_layers, r as ensureProjectLayer, s as piGlobalContextFile } from "./layers-DoUs5i4O.js";
|
|
45
|
+
import { l as init_codex, s as findRolloutPath } from "./codex-DlkETOc-.js";
|
|
46
|
+
import { C as setMergeTrainEnabled, S as setFlywheelRequireUatBeforeMerge, b as setFlywheelAutoPickupBacklog, c as isCloisterSpawnsPausedSync, d as isFlywheelAutoPickupBacklog, f as isFlywheelGloballyPaused$1, g as setCloisterSpawnsPausedSync, h as setBootReconciliationDecision, m as isMergeTrainEnabled, o as getSetting, p as isFlywheelRequireUatBeforeMerge, r as getBootReconciliationState, s as init_control_settings, u as isDeaconGloballyPausedSync, v as setDeaconGloballyPausedSync, w as setSetting } from "./control-settings-BTPRGd5d.js";
|
|
47
|
+
import { c as init_docker_stats } from "./stack-health-C_JMDfQa.js";
|
|
48
|
+
import { n as resolveHarness, t as init_harness_resolve } from "./harness-resolve-CYFbeWOz.js";
|
|
49
|
+
import { i as checkPrdGateSync, t as init_pan_dir } from "./pan-dir-DB2mqeFg.js";
|
|
50
|
+
import { a as init_lifecycle_io, t as appendContinueSessionEntryForIssue } from "./lifecycle-io-CKyWyVWt.js";
|
|
51
|
+
import { r as init_swarm_readiness, t as analyzeSwarmReadiness } from "./swarm-readiness-lyw-buM5.js";
|
|
52
|
+
import { B as createInFlightGuard, Dt as countPendingAskUserQuestionsForAgent, Et as computeAgentEnrichment, F as setAgentStatusChangedNotifier, I as setAgentStoppedNotifier, It as parseCodexApprovalPrompt, J as startBootReconciliation, K as isBootReconciliationCandidate, L as setMergeReadyNotifier, Mt as scanPendingInputsPromise, Nt as detectAwaitingInputForAgent, O as loadConfig, Ot as getAgentJsonlMtime, Pt as detectAwaitingInputFromPaneSync, Tt as getRuntimeForAgent, at as withConcurrencyLimit, k as loadState, n as assessDeaconPatrolFreshness, q as listBootReconciliationCandidates, t as applyBootReconciliationDecision, x as getDeaconLogs } from "./deacon-Cx7hYXMe.js";
|
|
53
|
+
import { i as CostThresholdError, l as embed, s as parseRelativeTime } from "./scanner-bjduWTuH.js";
|
|
54
|
+
import { n as deduplicateEventsSync, o as readEventsSync, s as tailEventsSync } from "./events-CiEGnZ53.js";
|
|
55
|
+
import { n as recordBackgroundAiCost, t as init_cost } from "./cost-DHjgeEgA.js";
|
|
56
|
+
import { S as writeLatestFlywheelStatus, _ as readCurrentLatestFlywheelStatus, b as subscribeLatestFlywheelStatus, f as getFlywheelRunDetail, h as listFlywheelRuns, l as buildClassifyLookups, m as isFlywheelRunId, y as resolveLiveFlywheelRunId } from "./flywheel-CkiZj2Rx.js";
|
|
57
|
+
import { C as rejectUnauthorizedDashboardRequest, E as getWorkspacePathForIssue, S as hasDashboardInternalToken, T as rejectUnsafeDashboardMutationRequest, _ as processPendingFeedbackDeliveries, b as dashboardCsrfToken, c as resumeQueuedMerges, d as reconcilePiTranscripts, f as reclassifyUnknownCostEventsSync, g as syncCacheSync, h as rebuildCacheSync, l as syncWalFromAllProjects, m as getCostsForIssueSync, p as migrateAllSessionsSync, t as workspacesRouteLayer, u as reconcile, v as getCachedRunningAgents, w as rejectUnauthorizedDashboardSessionMintRequest, x as dashboardSessionCookieHeader, y as resolveIssueHeadlineCost } from "./workspaces-DjApnCJp.js";
|
|
58
|
+
import { l as detectPlatform, u as init_platform } from "./workspace-manager-DV4u07t9.js";
|
|
59
|
+
import { O as init_specialists, b as getReviewerSessionName } from "./specialists-DW-1CAK5.js";
|
|
60
|
+
import { t as withBdMutex } from "./bd-mutex-Bqr8TTH0.js";
|
|
61
|
+
import { n as findPrdAtStatusSync, r as init_prd_locations } from "./prd-locations-BDN-xDNK.js";
|
|
62
|
+
import { a as getShadowState, s as init_shadow_state } from "./shadow-state-a0THplpo.js";
|
|
63
|
+
import { T as verifyAppCanMerge, d as getPullRequestState, f as init_github_app, l as getMergeBackendStatus, p as isGitHubAppConfigured } from "./github-app-Soz3tZh_.js";
|
|
64
|
+
import { t as clearIssueClosedCache } from "./issue-closed-7IHQ8IJf.js";
|
|
65
|
+
import { a as writeFeedbackFile, i as init_feedback_writer } from "./feedback-writer-DQqQJkgs.js";
|
|
66
|
+
import { a as init_boot_gates, i as formatBootGateState, o as resolveBootGates } from "./no-resume-mode-Hd08C18Q.js";
|
|
67
|
+
import { n as parseSequenceMd, r as writeSequenceMd, t as init_sequence_io } from "./sequence-io-MtiwR4U7.js";
|
|
68
|
+
import { C as selectNeedsPlanning, S as normalizeGate, _ as computeCohort, b as computeStats, d as OBJECTION_LABEL, f as PARKED_LABEL, g as classifyIssue, h as VETOED_LABEL, m as RELEASED_LABEL, p as READY_LABEL, u as BLOCKS_MAIN_LABEL, v as computeEpicGroups, x as computeWaves, y as computeLanes } from "./flywheel-merge-order-B-03AdML.js";
|
|
69
|
+
import { a as loadWorkspaceMetadataSync } from "./workspace-metadata-DYF0ScVu.js";
|
|
70
|
+
import { i as resolveAutoSpawnOnFinalize, o as spawnPlanningSession } from "./spawn-planning-session-Dgxpkfaj.js";
|
|
71
|
+
import { _ as sendToRemoteAgent, a as getRemoteAgentOutput, n as checkRemoteSpendCap, o as init_remote_agents, p as loadRemoteAgentState, u as killRemoteAgent, v as spawnRemoteAgent } from "./remote-agents-DeiRtncQ.js";
|
|
72
|
+
import { r as spawnInspectAgent } from "./inspect-agent-CmBmiWSp.js";
|
|
73
|
+
import { n as init_github_pr_lookup, r as lookupPullRequestNumberForBranch, t as githubPrLookupSource } from "./github-pr-lookup-C-YqEBQA.js";
|
|
74
|
+
import { a as init_pull_requests, c as getIssuePrTabCacheGeneration, i as fetchIssuePullRequestDiff, l as init_pr_tab_cache, n as fetchIssuePullRequest, o as bumpIssuePrTabCacheGeneration, r as fetchIssuePullRequestDetails, s as getCachedIssuePrTabResponse, t as fetchIssueCheckRuns, u as setCachedIssuePrTabResponse } from "./pull-requests-V9zbOnM7.js";
|
|
75
|
+
import { t as enrichReviewStatus } from "./review-status-enrichment-BK-Peqqa.js";
|
|
76
|
+
import { i as runMemoryFtsTransaction, n as init_fts_db, r as runMemoryFtsStatement, t as closeMemoryFtsDatabases } from "./fts-db-Bk_iUuHK.js";
|
|
77
|
+
import { c as init_pending, l as maybeTriggerStatusRollup, o as readRecentObservations, r as init_rollup, u as writePendingTurn } from "./rollup-BlVsp5Zh.js";
|
|
78
|
+
import { n as getTldrDaemonServiceSync, r as init_tldr_daemon } from "./tldr-daemon-Ddo1RYXH.js";
|
|
79
|
+
import { C as init_git_activity, w as listGitOperationsSync } from "./merge-agent-DcC21G-0.js";
|
|
80
|
+
import { t as getAgentCommandSync } from "./settings-DqPuXxT5.js";
|
|
81
|
+
import { n as init_review_agent, r as isExtendedReviewEnabled } from "./review-agent-CzUNH73m.js";
|
|
82
|
+
import { r as isRemoteAvailable, t as createFlyProviderFromConfig } from "./remote-DRwGjxzx.js";
|
|
83
|
+
import { n as init_claude_settings_overlay, t as detectProviderEnvConflicts } from "./claude-settings-overlay-rU83JjnR.js";
|
|
84
|
+
import { a as init_query_expansion, i as clearQueryExpansionCache, n as init_injection, o as buildMatchQuery, r as injectPromptTimeMemory, s as init_search } from "./injection-9zl1FzOq.js";
|
|
85
|
+
import { _ as init_transcript_adapter, a as generateFallbackSummary, c as handoffPreconditionFallbackReason, d as prependFallbackFocus, f as requestHandoffFromAgent, g as getTranscriptAdapter, i as copySessionFromCompactBoundary, l as init_summary_fork, o as generateSummaryForFork, p as reserveSummaryForkSession, r as authorHandoffExternal, s as handoffFailureReason, u as logHandoffFallback } from "./summary-fork-D8tUkORq.js";
|
|
86
|
+
import { a as init_conversation_compaction, c as shouldInterceptManualCompact, n as compactConversationNative, o as isCompacting } from "./conversation-compaction-NboSkcz4.js";
|
|
87
|
+
import { a as init_render, d as validateTemplate, i as hasManagedRegion, l as init_harness, o as userContentOutsideRegion, t as init_context_layers, u as renderForHarness } from "./context-layers-BpZBKYnw.js";
|
|
88
|
+
import { r as parseArtifactRef } from "./forge-Ca3Vecel.js";
|
|
89
|
+
import { t as getMergeBlockersPayload } from "./merge-blockers-B7ey3CkR.js";
|
|
90
|
+
import { a as spawnSequencerAgent, i as init_sequencer_agent, n as clearFinishedSequencerRun, r as getSequencerRunStatus, t as SEQUENCER_AGENT_ID } from "./sequencer-agent-C7AhB-75.js";
|
|
91
|
+
import "./supervisor-C0I_2Noz.js";
|
|
92
|
+
import { i as listTranscriptCheckpoints, r as getTranscriptCheckpoint$1 } from "./checkpoints-haUPQHnM.js";
|
|
95
93
|
import { createRequire } from "node:module";
|
|
96
94
|
import { Cause, Context, Data, Duration, Effect, Exit, Fiber, FileSystem, Layer, Option, Path, Queue, Result, Schedule, Schema, Stream } from "effect";
|
|
97
95
|
import { createReadStream, createWriteStream, existsSync, fstatSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
98
96
|
import { basename, dirname, join, resolve } from "path";
|
|
99
97
|
import { homedir } from "os";
|
|
100
|
-
import { homedir as homedir$1, type } from "node:os";
|
|
98
|
+
import { freemem as freemem$1, homedir as homedir$1, loadavg as loadavg$1, totalmem, type } from "node:os";
|
|
101
99
|
import { constants, createReadStream as createReadStream$1, existsSync as existsSync$1, mkdirSync as mkdirSync$1, promises as promises$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, readlinkSync, realpathSync as realpathSync$1, renameSync as renameSync$1, rmSync as rmSync$1, stat, statSync as statSync$1, symlinkSync as symlinkSync$1, unwatchFile, watch, watchFile, writeFileSync as writeFileSync$1 } from "node:fs";
|
|
102
100
|
import * as sp from "node:path";
|
|
103
101
|
import { basename as basename$1, delimiter, dirname as dirname$1, extname as extname$1, isAbsolute as isAbsolute$1, join as join$1, normalize, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "node:path";
|
|
@@ -105,10 +103,10 @@ import { FetchHttpClient, HttpRouter, HttpServer, HttpServerRequest, HttpServerR
|
|
|
105
103
|
import { EventEmitter } from "node:events";
|
|
106
104
|
import { access, appendFile, chmod, lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat as stat$1, unlink, writeFile } from "node:fs/promises";
|
|
107
105
|
import { createHash, randomUUID } from "crypto";
|
|
108
|
-
import { chmod as chmod$1, mkdir as mkdir$1, open as open$1, readFile as readFile$1, readdir as readdir$1, rename as rename$1, stat as stat$2, unlink as unlink$1, writeFile as writeFile$1 } from "fs/promises";
|
|
109
|
-
import { createHmac, randomBytes, randomUUID as randomUUID$1, timingSafeEqual } from "node:crypto";
|
|
110
106
|
import { exec, execFile, execFileSync, execSync, fork, spawn } from "node:child_process";
|
|
111
107
|
import { promisify } from "node:util";
|
|
108
|
+
import { chmod as chmod$1, mkdir as mkdir$1, open as open$1, readFile as readFile$1, readdir as readdir$1, rename as rename$1, stat as stat$2, unlink as unlink$1, writeFile as writeFile$1 } from "fs/promises";
|
|
109
|
+
import { createHmac, randomBytes, randomUUID as randomUUID$1, timingSafeEqual } from "node:crypto";
|
|
112
110
|
import * as pty from "@homebridge/node-pty-prebuilt-multiarch";
|
|
113
111
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
114
112
|
import { PassThrough, Readable } from "node:stream";
|
|
@@ -325,6 +323,7 @@ const ServerConfigLayer = Layer.effect(ServerConfig, Effect.sync(() => {
|
|
|
325
323
|
init_activity_logger();
|
|
326
324
|
init_paths$1();
|
|
327
325
|
init_driver();
|
|
326
|
+
init_auto_commit();
|
|
328
327
|
var import_dist = /* @__PURE__ */ __toESM(require_dist$1(), 1);
|
|
329
328
|
init_issue_id();
|
|
330
329
|
init_projects();
|
|
@@ -651,7 +650,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
|
|
|
651
650
|
const beadsDir = join$1(workspacePath, ".beads");
|
|
652
651
|
const projectBeadsDir = join$1(workspacePath, "..", "..", ".beads");
|
|
653
652
|
if (!(existsSync$1(beadsDir) || existsSync$1(projectBeadsDir))) throw new BeadsNotInitialized({ workspace: workspacePath });
|
|
654
|
-
const { getAgentState, spawnAgent, normalizeAgentId } = await import("./agents-
|
|
653
|
+
const { getAgentState, spawnAgent, normalizeAgentId } = await import("./agents-Ea9gfdKS.js");
|
|
655
654
|
const normalizedId = normalizeAgentId(issueId);
|
|
656
655
|
if ((await Effect.runPromise(getAgentState(normalizedId)))?.status === "running") throw new AgentAlreadyRunning({ id: issueId });
|
|
657
656
|
return {
|
|
@@ -682,7 +681,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
|
|
|
682
681
|
const { workspacePath, projectPath, issue } = opts;
|
|
683
682
|
if (!existsSync$1(workspacePath)) throw new WorkspaceNotFound({ id: issueId });
|
|
684
683
|
const sessionName = opts.sessionName ?? `planning-${issueId.toLowerCase()}`;
|
|
685
|
-
const { spawnPlanningSession } = await import("./spawn-planning-session-
|
|
684
|
+
const { spawnPlanningSession } = await import("./spawn-planning-session-Dr1yJhqp.js");
|
|
686
685
|
const result = await spawnPlanningSession({
|
|
687
686
|
issue: {
|
|
688
687
|
id: issue.id,
|
|
@@ -715,14 +714,14 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
|
|
|
715
714
|
}),
|
|
716
715
|
kill: (agentId) => Effect.tryPromise({
|
|
717
716
|
try: async () => {
|
|
718
|
-
const { stopAgent } = await import("./agents-
|
|
717
|
+
const { stopAgent } = await import("./agents-Ea9gfdKS.js");
|
|
719
718
|
await Effect.runPromise(stopAgent(agentId));
|
|
720
719
|
},
|
|
721
720
|
catch: () => void 0
|
|
722
721
|
}).pipe(Effect.ignore),
|
|
723
722
|
message: (agentId, msg) => Effect.tryPromise({
|
|
724
723
|
try: async () => {
|
|
725
|
-
const { messageAgent } = await import("./agents-
|
|
724
|
+
const { messageAgent } = await import("./agents-Ea9gfdKS.js");
|
|
726
725
|
await messageAgent(agentId, msg);
|
|
727
726
|
},
|
|
728
727
|
catch: (err) => new AgentStartError({
|
|
@@ -733,8 +732,8 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
|
|
|
733
732
|
}),
|
|
734
733
|
deepWipe: (issueId, opts) => Effect.tryPromise({
|
|
735
734
|
try: async () => {
|
|
736
|
-
const { deepWipe } = await import("./workflows-
|
|
737
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
735
|
+
const { deepWipe } = await import("./workflows-C94HmADx.js");
|
|
736
|
+
const { resolveProjectFromIssueSync } = await import("./projects-BbMEBykK.js");
|
|
738
737
|
const project = resolveProjectFromIssueSync(issueId);
|
|
739
738
|
const projectPath = project?.projectPath ?? process.cwd();
|
|
740
739
|
await Effect.runPromise(deepWipe({
|
|
@@ -763,7 +762,7 @@ const AgentSpawnerLive = Layer.effect(AgentSpawner, Effect.sync(() => ({
|
|
|
763
762
|
* Route handlers and AgentSpawner use this instead of calling workspace-manager directly.
|
|
764
763
|
*/
|
|
765
764
|
init_typed_errors();
|
|
766
|
-
const execAsync$
|
|
765
|
+
const execAsync$12 = promisify(exec);
|
|
767
766
|
var WorkspaceService = class extends Context.Service()("overdeck/dashboard/WorkspaceService") {};
|
|
768
767
|
const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
|
|
769
768
|
function getWorkspacePath(issueId) {
|
|
@@ -790,8 +789,8 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
|
|
|
790
789
|
const { workspacePath, branch } = getWorkspacePath(issueId);
|
|
791
790
|
const issueLower = issueId.toLowerCase();
|
|
792
791
|
if (existsSync$1(workspacePath)) return workspacePath;
|
|
793
|
-
const { createWorkspace } = await import("./workspace-manager-
|
|
794
|
-
const { loadProjectsConfigSync } = await import("./projects-
|
|
792
|
+
const { createWorkspace } = await import("./workspace-manager-BE2H8gD5.js");
|
|
793
|
+
const { loadProjectsConfigSync } = await import("./projects-BbMEBykK.js");
|
|
795
794
|
const { projects } = loadProjectsConfigSync();
|
|
796
795
|
const project = resolveProjectFromIssueSync(issueId);
|
|
797
796
|
if (!project) throw new WorkspaceCreateError({
|
|
@@ -826,7 +825,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
|
|
|
826
825
|
const { workspacePath } = getWorkspacePath(issueId);
|
|
827
826
|
const issueLower = issueId.toLowerCase();
|
|
828
827
|
if (!existsSync$1(workspacePath)) throw new WorkspaceNotFound({ id: issueId });
|
|
829
|
-
const { removeWorkspace } = await import("./workspace-manager-
|
|
828
|
+
const { removeWorkspace } = await import("./workspace-manager-BE2H8gD5.js");
|
|
830
829
|
const project = resolveProjectFromIssueSync(issueId);
|
|
831
830
|
if (!project) throw new WorkspaceCreateError({
|
|
832
831
|
id: issueId,
|
|
@@ -857,7 +856,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
|
|
|
857
856
|
try: async () => {
|
|
858
857
|
const { workspacePath } = getWorkspacePath(issueId);
|
|
859
858
|
const issueLower = issueId.toLowerCase();
|
|
860
|
-
const { stopWorkspaceDocker } = await import("./workspace-manager-
|
|
859
|
+
const { stopWorkspaceDocker } = await import("./workspace-manager-BE2H8gD5.js");
|
|
861
860
|
await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));
|
|
862
861
|
},
|
|
863
862
|
catch: () => void 0
|
|
@@ -926,7 +925,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
|
|
|
926
925
|
join$1(workspacePath, ".devcontainer", "docker-compose.devcontainer.yml")
|
|
927
926
|
];
|
|
928
927
|
if (!composePaths.find((p) => existsSync$1(p))) {
|
|
929
|
-
const { ensureDevcontainerSync } = await import("./ensure-devcontainer-
|
|
928
|
+
const { ensureDevcontainerSync } = await import("./ensure-devcontainer-Cy2QNx5O.js");
|
|
930
929
|
const ensure = ensureDevcontainerSync({
|
|
931
930
|
workspacePath,
|
|
932
931
|
issueId
|
|
@@ -937,7 +936,7 @@ const WorkspaceServiceLive = Layer.effect(WorkspaceService, Effect.sync(() => {
|
|
|
937
936
|
});
|
|
938
937
|
}
|
|
939
938
|
const finalComposePath = composePaths.find((p) => existsSync$1(p));
|
|
940
|
-
if (finalComposePath) await execAsync$
|
|
939
|
+
if (finalComposePath) await execAsync$12(`docker compose -f "${finalComposePath}" up -d --build`, {
|
|
941
940
|
cwd: dirname$1(finalComposePath),
|
|
942
941
|
timeout: 3e5
|
|
943
942
|
});
|
|
@@ -1100,7 +1099,7 @@ function getDefaultCwd() {
|
|
|
1100
1099
|
//#endregion
|
|
1101
1100
|
//#region services/open.ts
|
|
1102
1101
|
init_src();
|
|
1103
|
-
const execAsync$
|
|
1102
|
+
const execAsync$11 = promisify(exec);
|
|
1104
1103
|
function getFileManagerCommand() {
|
|
1105
1104
|
switch (process.platform) {
|
|
1106
1105
|
case "linux": return "xdg-open";
|
|
@@ -1133,7 +1132,7 @@ function launchDetached(launch) {
|
|
|
1133
1132
|
}
|
|
1134
1133
|
async function isCommandAvailable(command) {
|
|
1135
1134
|
try {
|
|
1136
|
-
await execAsync$
|
|
1135
|
+
await execAsync$11(`${process.platform === "win32" ? "where" : "which"} ${command}`);
|
|
1137
1136
|
return true;
|
|
1138
1137
|
} catch {
|
|
1139
1138
|
return false;
|
|
@@ -4811,7 +4810,7 @@ init_tmux();
|
|
|
4811
4810
|
init_config$1();
|
|
4812
4811
|
const reauthSessions = /* @__PURE__ */ new Map();
|
|
4813
4812
|
const SESSION_MAX_AGE_MS = 3600 * 1e3;
|
|
4814
|
-
const readJsonBody$
|
|
4813
|
+
const readJsonBody$17 = Effect.gen(function* () {
|
|
4815
4814
|
const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
|
|
4816
4815
|
return text ? JSON.parse(text) : {};
|
|
4817
4816
|
});
|
|
@@ -4916,7 +4915,7 @@ const postCodexReauthRoute = HttpRouter.add("POST", "/api/settings/codex-reauth"
|
|
|
4916
4915
|
const postCodexReauthStatusRoute = HttpRouter.add("POST", "/api/settings/codex-reauth/status", httpHandler(Effect.gen(function* () {
|
|
4917
4916
|
const originError = rejectInvalidOrigin(yield* HttpServerRequest.HttpServerRequest);
|
|
4918
4917
|
if (originError) return originError;
|
|
4919
|
-
const body = yield* readJsonBody$
|
|
4918
|
+
const body = yield* readJsonBody$17;
|
|
4920
4919
|
const sessionName = typeof body.session === "string" ? body.session : "";
|
|
4921
4920
|
const token = typeof body.token === "string" ? body.token : "";
|
|
4922
4921
|
const session = getLiveReauthSession(sessionName);
|
|
@@ -15944,7 +15943,7 @@ var require_get_vercel_oidc_token = /* @__PURE__ */ __commonJSMin(((exports, mod
|
|
|
15944
15943
|
err = error;
|
|
15945
15944
|
}
|
|
15946
15945
|
try {
|
|
15947
|
-
const [{ getTokenPayload, isExpired }, { refreshToken }] = await Promise.all([await import("./token-util-
|
|
15946
|
+
const [{ getTokenPayload, isExpired }, { refreshToken }] = await Promise.all([await import("./token-util-CEZZegEl.js").then((m) => /* @__PURE__ */ __toESM(m.default)), await import("./token-Cp6YfYRY.js").then((m) => /* @__PURE__ */ __toESM(m.default))]);
|
|
15948
15947
|
if (!token || isExpired(getTokenPayload(token), options?.expirationBufferMs)) {
|
|
15949
15948
|
await refreshToken(options);
|
|
15950
15949
|
token = getVercelOidcTokenSync();
|
|
@@ -28261,7 +28260,7 @@ var GoogleCloudTranscription = class {
|
|
|
28261
28260
|
}
|
|
28262
28261
|
async start(apiKey, model) {
|
|
28263
28262
|
try {
|
|
28264
|
-
const { SpeechClient } = await import("./src-
|
|
28263
|
+
const { SpeechClient } = await import("./src-DyS4x3Qs.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1)).then((module) => module.v1p1beta1 ?? module.v1);
|
|
28265
28264
|
if (this.closed) return;
|
|
28266
28265
|
const stream = new SpeechClient({ apiKey }).streamingRecognize({
|
|
28267
28266
|
config: {
|
|
@@ -28525,7 +28524,7 @@ const DEFAULT_VOICE_SETTINGS = {
|
|
|
28525
28524
|
model: "gpt-4.1-mini"
|
|
28526
28525
|
}
|
|
28527
28526
|
};
|
|
28528
|
-
const readJsonBody$
|
|
28527
|
+
const readJsonBody$16 = Effect.gen(function* () {
|
|
28529
28528
|
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
28530
28529
|
const contentLength = Number(request.headers["content-length"] ?? 0);
|
|
28531
28530
|
if (Number.isFinite(contentLength) && contentLength > MAX_VOICE_SETTINGS_BODY_BYTES) return {
|
|
@@ -28648,7 +28647,7 @@ const getVoiceSettingsRoute = HttpRouter.add("GET", "/api/voice/settings", httpH
|
|
|
28648
28647
|
const putVoiceSettingsRoute = HttpRouter.add("PUT", "/api/voice/settings", httpHandler(Effect.gen(function* () {
|
|
28649
28648
|
const originError = requireTrustedOrigin$2(yield* HttpServerRequest.HttpServerRequest);
|
|
28650
28649
|
if (originError) return originError;
|
|
28651
|
-
const parsed = yield* readJsonBody$
|
|
28650
|
+
const parsed = yield* readJsonBody$16;
|
|
28652
28651
|
if (!parsed.ok) return jsonResponse({ error: parsed.error }, { status: 413 });
|
|
28653
28652
|
const body = parsed.body;
|
|
28654
28653
|
if (!isVoiceSettings(body)) return jsonResponse({ error: "Invalid voice settings payload" }, { status: 400 });
|
|
@@ -30385,7 +30384,7 @@ function parseIssueIdFromTextSync(value) {
|
|
|
30385
30384
|
//#region services/resource-discovery.ts
|
|
30386
30385
|
init_projects();
|
|
30387
30386
|
init_tmux();
|
|
30388
|
-
const execFileAsync$
|
|
30387
|
+
const execFileAsync$14 = promisify(execFile);
|
|
30389
30388
|
const RESOURCE_DISCOVERY_TTL_MS = 3e4;
|
|
30390
30389
|
const RECENT_ACTIVITY_WINDOW_MS = 5e3;
|
|
30391
30390
|
let cachedResourceIssues = null;
|
|
@@ -30493,7 +30492,7 @@ function isDiscoverableAgentSession(sessionName) {
|
|
|
30493
30492
|
async function loadTrackerIssues() {
|
|
30494
30493
|
const map = /* @__PURE__ */ new Map();
|
|
30495
30494
|
try {
|
|
30496
|
-
const { getSharedIssueService } = await import("./issue-service-singleton-
|
|
30495
|
+
const { getSharedIssueService } = await import("./issue-service-singleton-DFei50PX.js");
|
|
30497
30496
|
const issues = (await getSharedIssueService()).getIssues();
|
|
30498
30497
|
for (const issue of issues) if (issue.identifier) map.set(issue.identifier.toUpperCase(), issue);
|
|
30499
30498
|
} catch {
|
|
@@ -30510,7 +30509,7 @@ async function loadTmuxSessions() {
|
|
|
30510
30509
|
}
|
|
30511
30510
|
async function loadDockerContainers() {
|
|
30512
30511
|
try {
|
|
30513
|
-
const { stdout } = await execFileAsync$
|
|
30512
|
+
const { stdout } = await execFileAsync$14("docker", [
|
|
30514
30513
|
"ps",
|
|
30515
30514
|
"--format",
|
|
30516
30515
|
"{{.Names}}"
|
|
@@ -30528,7 +30527,7 @@ async function loadOpenPullRequests() {
|
|
|
30528
30527
|
const repos = getGitHubConfig()?.repos ?? [];
|
|
30529
30528
|
await Promise.all(repos.map(async (repo) => {
|
|
30530
30529
|
try {
|
|
30531
|
-
const { stdout } = await execFileAsync$
|
|
30530
|
+
const { stdout } = await execFileAsync$14("gh", [
|
|
30532
30531
|
"pr",
|
|
30533
30532
|
"list",
|
|
30534
30533
|
"--repo",
|
|
@@ -30559,7 +30558,7 @@ async function loadOpenPullRequests() {
|
|
|
30559
30558
|
}
|
|
30560
30559
|
async function loadProjectBranches(projectPath) {
|
|
30561
30560
|
try {
|
|
30562
|
-
const [localResult, remoteResult] = await Promise.all([execFileAsync$
|
|
30561
|
+
const [localResult, remoteResult] = await Promise.all([execFileAsync$14("git", [
|
|
30563
30562
|
"for-each-ref",
|
|
30564
30563
|
"refs/heads/feature/*",
|
|
30565
30564
|
"--format=%(refname:short)"
|
|
@@ -30567,7 +30566,7 @@ async function loadProjectBranches(projectPath) {
|
|
|
30567
30566
|
cwd: projectPath,
|
|
30568
30567
|
encoding: "utf-8",
|
|
30569
30568
|
timeout: 1e4
|
|
30570
|
-
}).catch(() => ({ stdout: "" })), execFileAsync$
|
|
30569
|
+
}).catch(() => ({ stdout: "" })), execFileAsync$14("git", [
|
|
30571
30570
|
"for-each-ref",
|
|
30572
30571
|
"refs/remotes/origin/feature/*",
|
|
30573
30572
|
"--format=%(refname:short)"
|
|
@@ -30692,7 +30691,7 @@ async function computeResourceAllocatedIssues() {
|
|
|
30692
30691
|
if (!issue.resourceDetails.tmuxSessions.includes(sessionName)) issue.resourceDetails.tmuxSessions.push(sessionName);
|
|
30693
30692
|
}
|
|
30694
30693
|
try {
|
|
30695
|
-
const { listActiveRemoteAgentStates } = await import("./remote-agents-
|
|
30694
|
+
const { listActiveRemoteAgentStates } = await import("./remote-agents-BXKV5iBS.js");
|
|
30696
30695
|
for (const remoteState of listActiveRemoteAgentStates()) {
|
|
30697
30696
|
const issue = ensureIssue(remoteState.issueId);
|
|
30698
30697
|
if (!issue) continue;
|
|
@@ -30896,7 +30895,7 @@ function groupResourceAllocatedIssuesByProject(issues) {
|
|
|
30896
30895
|
//#region ../../lib/overdeck/issue-reads.ts
|
|
30897
30896
|
init_tracker_config();
|
|
30898
30897
|
init_projects();
|
|
30899
|
-
const execFileAsync$
|
|
30898
|
+
const execFileAsync$13 = promisify(execFile);
|
|
30900
30899
|
function isGitHubIssue$6(issueId) {
|
|
30901
30900
|
const resolved = resolveGitHubIssueSync(issueId);
|
|
30902
30901
|
if (resolved.isGitHub) return {
|
|
@@ -31077,7 +31076,7 @@ function getIssueBeads(id) {
|
|
|
31077
31076
|
const { beads, querySource } = yield* Effect.promise(async () => {
|
|
31078
31077
|
try {
|
|
31079
31078
|
const bdSearchDir = workspacePath && existsSync$1(workspacePath) ? workspacePath : projectPath || homedir$1();
|
|
31080
|
-
const { stdout } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$
|
|
31079
|
+
const { stdout } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$13("bd", [
|
|
31081
31080
|
"list",
|
|
31082
31081
|
"--json",
|
|
31083
31082
|
"-l",
|
|
@@ -31198,7 +31197,7 @@ init_projects();
|
|
|
31198
31197
|
init_remote_agents();
|
|
31199
31198
|
init_tracker_utils();
|
|
31200
31199
|
init_tmux();
|
|
31201
|
-
const execFileAsync$
|
|
31200
|
+
const execFileAsync$12 = promisify(execFile);
|
|
31202
31201
|
function getIssueDataService$9() {
|
|
31203
31202
|
return getSharedIssueService();
|
|
31204
31203
|
}
|
|
@@ -31255,7 +31254,7 @@ async function completePlanningArtifacts(options) {
|
|
|
31255
31254
|
if (workspaceIssueId && workspaceIssueId.toLowerCase() !== issueLower) throw new Error(`Workspace vBRIEF is for ${workspaceIssueId.toUpperCase()}, not ${upperIssueId}`);
|
|
31256
31255
|
assertPlanQuality(workspaceDoc);
|
|
31257
31256
|
const createBeads = options.createBeads ?? (async (path) => {
|
|
31258
|
-
const mod = await import("./beads-
|
|
31257
|
+
const mod = await import("./beads-B4ysfx-Y.js");
|
|
31259
31258
|
return await Effect.runPromise(mod.createBeadsFromVBrief(path));
|
|
31260
31259
|
});
|
|
31261
31260
|
emitCompletePlanningPhase(upperIssueId, "specWrite", "start", "writing proposed vBRIEF spec", { projectPath });
|
|
@@ -31322,8 +31321,72 @@ function completePlanningWorkspaceGitAddCommands(gitRoot) {
|
|
|
31322
31321
|
const commands = [];
|
|
31323
31322
|
if (existsSync$1(join$1(gitRoot, ".pan"))) commands.push(["add", ".pan/"]);
|
|
31324
31323
|
if (existsSync$1(join$1(gitRoot, ".beads"))) commands.push(["add", ".beads/"]);
|
|
31324
|
+
if (existsSync$1(join$1(gitRoot, ".gitignore"))) commands.push(["add", ".gitignore"]);
|
|
31325
31325
|
return commands;
|
|
31326
31326
|
}
|
|
31327
|
+
/**
|
|
31328
|
+
* PAN-2386: complete-planning can leave `.pan/records/<issue>.json` modified after
|
|
31329
|
+
* the main `chore(plan): complete planning` commit because record writes are
|
|
31330
|
+
* queued for debounced auto-commit. If auto-start is requested, the subsequent
|
|
31331
|
+
* start-agent dirty-workspace guard refuses to spawn the work agent. Flush any
|
|
31332
|
+
* pending auto-commits and explicitly stage/commit the per-issue record so the
|
|
31333
|
+
* tree handed to auto-start is clean.
|
|
31334
|
+
*/
|
|
31335
|
+
async function commitWorkspaceRecordBeforeAutoSpawn(gitRoot, issueId) {
|
|
31336
|
+
if (!existsSync$1(join$1(gitRoot, ".git"))) return;
|
|
31337
|
+
const issueLower = issueId.toLowerCase();
|
|
31338
|
+
try {
|
|
31339
|
+
await Effect.runPromise(flushAutoCommits(gitRoot));
|
|
31340
|
+
} catch {}
|
|
31341
|
+
const recordPath = join$1(".pan", "records", `${issueLower}.json`);
|
|
31342
|
+
try {
|
|
31343
|
+
const { stdout: statusOut } = await execFileAsync$12("git", [
|
|
31344
|
+
"status",
|
|
31345
|
+
"--porcelain",
|
|
31346
|
+
"--",
|
|
31347
|
+
recordPath
|
|
31348
|
+
], {
|
|
31349
|
+
cwd: gitRoot,
|
|
31350
|
+
encoding: "utf-8"
|
|
31351
|
+
});
|
|
31352
|
+
if (!statusOut.trim()) return;
|
|
31353
|
+
await execFileAsync$12("git", [
|
|
31354
|
+
"add",
|
|
31355
|
+
"--",
|
|
31356
|
+
recordPath
|
|
31357
|
+
], {
|
|
31358
|
+
cwd: gitRoot,
|
|
31359
|
+
encoding: "utf-8"
|
|
31360
|
+
});
|
|
31361
|
+
try {
|
|
31362
|
+
await execFileAsync$12("git", [
|
|
31363
|
+
"diff",
|
|
31364
|
+
"--cached",
|
|
31365
|
+
"--quiet",
|
|
31366
|
+
"--",
|
|
31367
|
+
recordPath
|
|
31368
|
+
], {
|
|
31369
|
+
cwd: gitRoot,
|
|
31370
|
+
encoding: "utf-8"
|
|
31371
|
+
});
|
|
31372
|
+
return;
|
|
31373
|
+
} catch {}
|
|
31374
|
+
await execFileAsync$12("git", [
|
|
31375
|
+
"commit",
|
|
31376
|
+
"-m",
|
|
31377
|
+
`chore(records): update ${issueId.toUpperCase()} per-issue record before auto-start`,
|
|
31378
|
+
"--",
|
|
31379
|
+
recordPath
|
|
31380
|
+
], {
|
|
31381
|
+
cwd: gitRoot,
|
|
31382
|
+
encoding: "utf-8"
|
|
31383
|
+
});
|
|
31384
|
+
console.log(`[complete-planning] Committed per-issue record for ${issueId.toUpperCase()} before auto-start`);
|
|
31385
|
+
} catch (err) {
|
|
31386
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
31387
|
+
console.warn(`[complete-planning] Could not commit per-issue record for ${issueId.toUpperCase()}: ${message}`);
|
|
31388
|
+
}
|
|
31389
|
+
}
|
|
31327
31390
|
function getInternalDashboardOrigin() {
|
|
31328
31391
|
const port = Number.parseInt(process.env["API_PORT"] ?? process.env["PORT"] ?? "3011", 10);
|
|
31329
31392
|
return process.env["OVERDECK_INTERNAL_DASHBOARD_URL"] ?? `http://127.0.0.1:${port}`;
|
|
@@ -31523,7 +31586,7 @@ async function completePlanningForIssue(options) {
|
|
|
31523
31586
|
const filesToStage = completePlanningFilesToStage(projectPath, proposed.filename);
|
|
31524
31587
|
if (!existsSync$1(join$1(projectPath, ".git"))) console.log(`[complete-planning] Project root ${projectPath} is not a git repository (polyrepo) — pan spec updated on disk but not committed`);
|
|
31525
31588
|
else {
|
|
31526
|
-
const { stdout: branchStdout } = await execFileAsync$
|
|
31589
|
+
const { stdout: branchStdout } = await execFileAsync$12("git", [
|
|
31527
31590
|
"rev-parse",
|
|
31528
31591
|
"--abbrev-ref",
|
|
31529
31592
|
"HEAD"
|
|
@@ -31533,7 +31596,7 @@ async function completePlanningForIssue(options) {
|
|
|
31533
31596
|
});
|
|
31534
31597
|
const currentBranch = branchStdout.trim();
|
|
31535
31598
|
if (currentBranch === "main") {
|
|
31536
|
-
await execFileAsync$
|
|
31599
|
+
await execFileAsync$12("git", [
|
|
31537
31600
|
"add",
|
|
31538
31601
|
"--",
|
|
31539
31602
|
...filesToStage
|
|
@@ -31542,7 +31605,7 @@ async function completePlanningForIssue(options) {
|
|
|
31542
31605
|
encoding: "utf-8"
|
|
31543
31606
|
});
|
|
31544
31607
|
try {
|
|
31545
|
-
await execFileAsync$
|
|
31608
|
+
await execFileAsync$12("git", [
|
|
31546
31609
|
"diff",
|
|
31547
31610
|
"--cached",
|
|
31548
31611
|
"--quiet",
|
|
@@ -31553,7 +31616,7 @@ async function completePlanningForIssue(options) {
|
|
|
31553
31616
|
encoding: "utf-8"
|
|
31554
31617
|
});
|
|
31555
31618
|
} catch {
|
|
31556
|
-
await execFileAsync$
|
|
31619
|
+
await execFileAsync$12("git", [
|
|
31557
31620
|
"commit",
|
|
31558
31621
|
"-m",
|
|
31559
31622
|
`chore(scope): propose ${upperIssueId} vBRIEF`,
|
|
@@ -31566,7 +31629,7 @@ async function completePlanningForIssue(options) {
|
|
|
31566
31629
|
});
|
|
31567
31630
|
console.log(`[complete-planning] Committed pan spec on main for ${upperIssueId}`);
|
|
31568
31631
|
try {
|
|
31569
|
-
const { stdout: remotes } = await execFileAsync$
|
|
31632
|
+
const { stdout: remotes } = await execFileAsync$12("git", ["remote"], {
|
|
31570
31633
|
cwd: projectPath,
|
|
31571
31634
|
encoding: "utf-8"
|
|
31572
31635
|
});
|
|
@@ -31579,16 +31642,16 @@ async function completePlanningForIssue(options) {
|
|
|
31579
31642
|
}
|
|
31580
31643
|
} else console.log(`[complete-planning] Project root not on main (${currentBranch}) — pan spec updated on disk but not committed on main`);
|
|
31581
31644
|
}
|
|
31582
|
-
if (!existsSync$1(join$1(gitRoot, ".git"))) await execFileAsync$
|
|
31645
|
+
if (!existsSync$1(join$1(gitRoot, ".git"))) await execFileAsync$12("git", ["init"], {
|
|
31583
31646
|
cwd: gitRoot,
|
|
31584
31647
|
encoding: "utf-8"
|
|
31585
31648
|
});
|
|
31586
|
-
for (const args of completePlanningWorkspaceGitAddCommands(gitRoot)) await execFileAsync$
|
|
31649
|
+
for (const args of completePlanningWorkspaceGitAddCommands(gitRoot)) await execFileAsync$12("git", args, {
|
|
31587
31650
|
cwd: gitRoot,
|
|
31588
31651
|
encoding: "utf-8"
|
|
31589
31652
|
});
|
|
31590
31653
|
try {
|
|
31591
|
-
await execFileAsync$
|
|
31654
|
+
await execFileAsync$12("git", [
|
|
31592
31655
|
"diff",
|
|
31593
31656
|
"--cached",
|
|
31594
31657
|
"--quiet"
|
|
@@ -31597,7 +31660,7 @@ async function completePlanningForIssue(options) {
|
|
|
31597
31660
|
encoding: "utf-8"
|
|
31598
31661
|
});
|
|
31599
31662
|
} catch {
|
|
31600
|
-
await execFileAsync$
|
|
31663
|
+
await execFileAsync$12("git", [
|
|
31601
31664
|
"commit",
|
|
31602
31665
|
"-m",
|
|
31603
31666
|
`chore(plan): complete planning for ${id}`,
|
|
@@ -31608,7 +31671,7 @@ async function completePlanningForIssue(options) {
|
|
|
31608
31671
|
});
|
|
31609
31672
|
}
|
|
31610
31673
|
try {
|
|
31611
|
-
const { stdout: remotes } = await execFileAsync$
|
|
31674
|
+
const { stdout: remotes } = await execFileAsync$12("git", ["remote"], {
|
|
31612
31675
|
cwd: gitRoot,
|
|
31613
31676
|
encoding: "utf-8"
|
|
31614
31677
|
});
|
|
@@ -31686,6 +31749,7 @@ async function completePlanningForIssue(options) {
|
|
|
31686
31749
|
source: "planning-agent",
|
|
31687
31750
|
eventType: "planning.complete"
|
|
31688
31751
|
});
|
|
31752
|
+
if (autoSpawn && workspacePath) await commitWorkspaceRecordBeforeAutoSpawn(workspacePath, id);
|
|
31689
31753
|
const autoSpawnResult = await completePlanningAutoSpawnAndKill({
|
|
31690
31754
|
issueId: id,
|
|
31691
31755
|
autoSpawn,
|
|
@@ -31785,8 +31849,8 @@ init_tracker_utils();
|
|
|
31785
31849
|
init_agents$1();
|
|
31786
31850
|
init_issue_id();
|
|
31787
31851
|
init_io();
|
|
31788
|
-
const execAsync$
|
|
31789
|
-
const execFileAsync$
|
|
31852
|
+
const execAsync$10 = promisify(exec);
|
|
31853
|
+
const execFileAsync$11 = promisify(execFile);
|
|
31790
31854
|
function isGitHubIssue$4(issueId) {
|
|
31791
31855
|
const resolved = resolveGitHubIssueSync(issueId);
|
|
31792
31856
|
if (resolved.isGitHub) return {
|
|
@@ -31825,7 +31889,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
|
|
|
31825
31889
|
if (!githubCheck.isGitHub || !githubCheck.owner || !githubCheck.repo) return ["No GitHub PR to close"];
|
|
31826
31890
|
const branchName = `feature/${issueId.toLowerCase()}`;
|
|
31827
31891
|
try {
|
|
31828
|
-
const { stdout: prListRaw } = await execFileAsync$
|
|
31892
|
+
const { stdout: prListRaw } = await execFileAsync$11("gh", [
|
|
31829
31893
|
"pr",
|
|
31830
31894
|
"list",
|
|
31831
31895
|
"--repo",
|
|
@@ -31844,7 +31908,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
|
|
|
31844
31908
|
});
|
|
31845
31909
|
const prNumber = prListRaw.trim();
|
|
31846
31910
|
if (!prNumber) return ["No open PR found for branch"];
|
|
31847
|
-
await execFileAsync$
|
|
31911
|
+
await execFileAsync$11("gh", [
|
|
31848
31912
|
"pr",
|
|
31849
31913
|
"close",
|
|
31850
31914
|
prNumber,
|
|
@@ -31857,7 +31921,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
|
|
|
31857
31921
|
timeout: 15e3
|
|
31858
31922
|
});
|
|
31859
31923
|
try {
|
|
31860
|
-
const { setReviewStatusSync } = await import("./review-status-
|
|
31924
|
+
const { setReviewStatusSync } = await import("./review-status-Rr4SB2Kj.js");
|
|
31861
31925
|
setReviewStatusSync(issueId.toUpperCase(), { prUrl: void 0 });
|
|
31862
31926
|
} catch {}
|
|
31863
31927
|
return [`Closed PR #${prNumber} on ${githubCheck.owner}/${githubCheck.repo}`];
|
|
@@ -31911,7 +31975,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
|
|
|
31911
31975
|
const { ctx, projectConfig } = buildLifecycleContext(id, getIssueDataService$8().getIssueSource(id) ?? void 0);
|
|
31912
31976
|
const deleteWorkspace = opts.deleteWorkspace ?? true;
|
|
31913
31977
|
cleanupLog.push(...await closeIssuePullRequest(id, mode === "cancel" ? "Canceled via Overdeck" : "Reset to Todo via Overdeck"));
|
|
31914
|
-
const { resetToTodo, cancelIssueWorkflow } = await import("./lifecycle-
|
|
31978
|
+
const { resetToTodo, cancelIssueWorkflow } = await import("./lifecycle-Va4AZGRx.js");
|
|
31915
31979
|
const workflow = mode === "cancel" ? cancelIssueWorkflow : resetToTodo;
|
|
31916
31980
|
const result = await Effect.runPromise(workflow(ctx, {
|
|
31917
31981
|
deleteWorkspace,
|
|
@@ -31926,7 +31990,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
|
|
|
31926
31990
|
}));
|
|
31927
31991
|
cleanupLog.push(...result.steps.flatMap((step) => step.details || [step.error].filter(Boolean)));
|
|
31928
31992
|
if (mode === "cancel") try {
|
|
31929
|
-
const { transitionVBriefOnMain } = await import("./lifecycle-io-
|
|
31993
|
+
const { transitionVBriefOnMain } = await import("./lifecycle-io-BHi2NPxK.js");
|
|
31930
31994
|
const tx = await Effect.runPromise(transitionVBriefOnMain(ctx.projectPath, id, "cancelled", "cancelled", `scope: cancel ${id.toUpperCase()} vBRIEF`));
|
|
31931
31995
|
if (tx.moved) cleanupLog.push(`vBRIEF moved ${tx.fromDir} → cancelled`);
|
|
31932
31996
|
if (tx.committed) cleanupLog.push(`Committed vBRIEF cancellation on main`);
|
|
@@ -31934,8 +31998,8 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
|
|
|
31934
31998
|
cleanupLog.push(`vBRIEF cancel transition failed (non-fatal): ${err?.message ?? err}`);
|
|
31935
31999
|
}
|
|
31936
32000
|
try {
|
|
31937
|
-
const { killAllReviewerSessions } = await import("./review-agent-
|
|
31938
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
32001
|
+
const { killAllReviewerSessions } = await import("./review-agent-DfN-7KW6.js");
|
|
32002
|
+
const { resolveProjectFromIssueSync } = await import("./projects-BbMEBykK.js");
|
|
31939
32003
|
const projectKey = resolveProjectFromIssueSync(id)?.projectKey;
|
|
31940
32004
|
if (projectKey) {
|
|
31941
32005
|
const { killed } = await Effect.runPromise(killAllReviewerSessions(projectKey, id.toUpperCase()));
|
|
@@ -31949,7 +32013,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
|
|
|
31949
32013
|
cleanupLog.push("Cleared review status");
|
|
31950
32014
|
} catch {}
|
|
31951
32015
|
try {
|
|
31952
|
-
const { resetPostMergeState } = await import("./merge-agent-
|
|
32016
|
+
const { resetPostMergeState } = await import("./merge-agent-DZaRBHQA.js");
|
|
31953
32017
|
resetPostMergeState(id);
|
|
31954
32018
|
resetPostMergeState(id.toUpperCase());
|
|
31955
32019
|
cleanupLog.push("Cleared merge state");
|
|
@@ -31969,7 +32033,7 @@ function closeIssueTransition(options) {
|
|
|
31969
32033
|
const { issueId, body, eventStore } = options;
|
|
31970
32034
|
const { reason } = body;
|
|
31971
32035
|
const projectPath = getProjectPath$3(void 0, extractPrefixSync(issueId) ?? issueId.split("-")[0]);
|
|
31972
|
-
const { close: closeWorkflow } = yield* Effect.promise(() => import("./lifecycle-
|
|
32036
|
+
const { close: closeWorkflow } = yield* Effect.promise(() => import("./lifecycle-Va4AZGRx.js"));
|
|
31973
32037
|
const githubCheck = isGitHubIssue$4(issueId);
|
|
31974
32038
|
const issueDataService = getIssueDataService$8();
|
|
31975
32039
|
const issueSource = issueDataService.getIssueSource(issueId);
|
|
@@ -31992,7 +32056,7 @@ function closeIssueTransition(options) {
|
|
|
31992
32056
|
};
|
|
31993
32057
|
}
|
|
31994
32058
|
const result = yield* closeWorkflow(ctx, { reason });
|
|
31995
|
-
if (githubCheck.isGitHub) execAsync$
|
|
32059
|
+
if (githubCheck.isGitHub) execAsync$10("pan sync", {
|
|
31996
32060
|
encoding: "utf-8",
|
|
31997
32061
|
timeout: 3e4
|
|
31998
32062
|
}).catch(() => {});
|
|
@@ -32203,13 +32267,13 @@ function reopenIssueTransition(options) {
|
|
|
32203
32267
|
}
|
|
32204
32268
|
yield* Effect.promise(async () => {
|
|
32205
32269
|
try {
|
|
32206
|
-
const { stdout } = await execAsync$
|
|
32270
|
+
const { stdout } = await execAsync$10(`gh pr list --head ${`feature/${id.toLowerCase()}`} --state closed --json number,mergedAt --limit 1`, {
|
|
32207
32271
|
encoding: "utf-8",
|
|
32208
32272
|
timeout: 15e3
|
|
32209
32273
|
});
|
|
32210
32274
|
const prs = JSON.parse(stdout.trim() || "[]");
|
|
32211
32275
|
if (prs.length > 0 && !prs[0].mergedAt) {
|
|
32212
|
-
await execAsync$
|
|
32276
|
+
await execAsync$10(`gh pr reopen ${prs[0].number}`, {
|
|
32213
32277
|
encoding: "utf-8",
|
|
32214
32278
|
timeout: 15e3
|
|
32215
32279
|
});
|
|
@@ -32236,7 +32300,7 @@ function reopenIssueTransition(options) {
|
|
|
32236
32300
|
else clearReviewStatus(id.toUpperCase());
|
|
32237
32301
|
} catch {}
|
|
32238
32302
|
try {
|
|
32239
|
-
const { resetPostMergeState } = await import("./merge-agent-
|
|
32303
|
+
const { resetPostMergeState } = await import("./merge-agent-DZaRBHQA.js");
|
|
32240
32304
|
resetPostMergeState(id);
|
|
32241
32305
|
resetPostMergeState(id.toUpperCase());
|
|
32242
32306
|
} catch {}
|
|
@@ -32256,9 +32320,9 @@ function reopenIssueTransition(options) {
|
|
|
32256
32320
|
const projectPath = (teamPrefix ? findProjectByTeamSync(teamPrefix) : null)?.path || "";
|
|
32257
32321
|
if (projectPath) {
|
|
32258
32322
|
const workspacePath = join$1(projectPath, "workspaces", `feature-${issueLower}`);
|
|
32259
|
-
const { createBeadsFromVBrief } = await import("./beads-
|
|
32323
|
+
const { createBeadsFromVBrief } = await import("./beads-B4ysfx-Y.js");
|
|
32260
32324
|
if (existsSync$1(workspacePath) && await Effect.runPromise(findPlan(workspacePath))) try {
|
|
32261
|
-
const { stdout: bdCheck } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$
|
|
32325
|
+
const { stdout: bdCheck } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$11("bd", [
|
|
32262
32326
|
"list",
|
|
32263
32327
|
"--json",
|
|
32264
32328
|
"-l",
|
|
@@ -32333,7 +32397,7 @@ function moveIssueStatus(options) {
|
|
|
32333
32397
|
"done"
|
|
32334
32398
|
];
|
|
32335
32399
|
if (!targetStatus || !validStatuses.includes(targetStatus)) return jsonResponse({ error: `Invalid targetStatus. Must be one of: ${validStatuses.join(", ")}` }, { status: 400 });
|
|
32336
|
-
const { updateShadowState } = yield* Effect.promise(() => import("./shadow-state-
|
|
32400
|
+
const { updateShadowState } = yield* Effect.promise(() => import("./shadow-state-nDkNqpYC.js"));
|
|
32337
32401
|
const issueState = {
|
|
32338
32402
|
backlog: "open",
|
|
32339
32403
|
todo: "open",
|
|
@@ -32396,7 +32460,7 @@ function moveIssueStatus(options) {
|
|
|
32396
32460
|
init_issue_service_singleton();
|
|
32397
32461
|
init_issue_id();
|
|
32398
32462
|
init_agents$1();
|
|
32399
|
-
const execAsync$
|
|
32463
|
+
const execAsync$9 = promisify(exec);
|
|
32400
32464
|
function getIssueDataService$7() {
|
|
32401
32465
|
return getSharedIssueService();
|
|
32402
32466
|
}
|
|
@@ -32421,28 +32485,28 @@ async function cleanupWorkspaceForIssue(rawId, eventStore) {
|
|
|
32421
32485
|
if (projectRoot) {
|
|
32422
32486
|
const workspacePath = join$1(projectRoot, "workspaces", `feature-${issueLower}`);
|
|
32423
32487
|
try {
|
|
32424
|
-
if ((await execAsync$
|
|
32488
|
+
if ((await execAsync$9("git worktree list --porcelain", {
|
|
32425
32489
|
cwd: projectRoot,
|
|
32426
32490
|
encoding: "utf-8"
|
|
32427
32491
|
})).stdout.includes(workspacePath)) {
|
|
32428
|
-
await execAsync$
|
|
32492
|
+
await execAsync$9(`git worktree remove "${workspacePath}" --force`, {
|
|
32429
32493
|
cwd: projectRoot,
|
|
32430
32494
|
encoding: "utf-8"
|
|
32431
32495
|
});
|
|
32432
32496
|
cleanupLog.push(`Removed git worktree: ${workspacePath}`);
|
|
32433
32497
|
} else if (existsSync$1(workspacePath)) {
|
|
32434
|
-
await execAsync$
|
|
32498
|
+
await execAsync$9(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
|
|
32435
32499
|
cleanupLog.push(`Removed directory: ${workspacePath}`);
|
|
32436
32500
|
}
|
|
32437
32501
|
} catch {
|
|
32438
32502
|
if (existsSync$1(workspacePath)) {
|
|
32439
|
-
await execAsync$
|
|
32503
|
+
await execAsync$9(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
|
|
32440
32504
|
cleanupLog.push(`Removed directory: ${workspacePath}`);
|
|
32441
32505
|
}
|
|
32442
32506
|
}
|
|
32443
32507
|
const branchName = `feature/${issueLower}`;
|
|
32444
32508
|
try {
|
|
32445
|
-
await execAsync$
|
|
32509
|
+
await execAsync$9(`git branch -D "${branchName}" 2>/dev/null || true`, {
|
|
32446
32510
|
cwd: projectRoot,
|
|
32447
32511
|
encoding: "utf-8"
|
|
32448
32512
|
});
|
|
@@ -32451,7 +32515,7 @@ async function cleanupWorkspaceForIssue(rawId, eventStore) {
|
|
|
32451
32515
|
}
|
|
32452
32516
|
const agentDir = join$1(homedir$1(), ".overdeck", "agents", `agent-${issueLower}`);
|
|
32453
32517
|
if (existsSync$1(agentDir)) {
|
|
32454
|
-
await execAsync$
|
|
32518
|
+
await execAsync$9(`rm -rf "${agentDir}"`, { encoding: "utf-8" });
|
|
32455
32519
|
cleanupLog.push(`Removed agent state: ${agentDir}`);
|
|
32456
32520
|
}
|
|
32457
32521
|
await Effect.runPromise(eventStore.append({
|
|
@@ -32560,7 +32624,7 @@ async function copySettingsToWorkspace(id) {
|
|
|
32560
32624
|
success: false,
|
|
32561
32625
|
error: "Workspace not found"
|
|
32562
32626
|
}, { status: 404 });
|
|
32563
|
-
const { copyOverdeckSettingsToWorkspaceSync } = await import("./workspace-manager-
|
|
32627
|
+
const { copyOverdeckSettingsToWorkspaceSync } = await import("./workspace-manager-BE2H8gD5.js");
|
|
32564
32628
|
const result = copyOverdeckSettingsToWorkspaceSync(workspacePath);
|
|
32565
32629
|
return jsonResponse({
|
|
32566
32630
|
success: result.errors.length === 0 || result.copied.length > 0,
|
|
@@ -32593,7 +32657,7 @@ function generateTasksForIssue(id) {
|
|
|
32593
32657
|
success: false,
|
|
32594
32658
|
error: `No vBRIEF spec found on main for ${id} — run planning first.`
|
|
32595
32659
|
}, { status: 409 });
|
|
32596
|
-
const { createBeadsFromVBrief } = yield* Effect.promise(() => import("./beads-
|
|
32660
|
+
const { createBeadsFromVBrief } = yield* Effect.promise(() => import("./beads-B4ysfx-Y.js"));
|
|
32597
32661
|
const result = yield* createBeadsFromVBrief(workspacePath);
|
|
32598
32662
|
if (!result.success || result.created.length === 0) {
|
|
32599
32663
|
const errors = result.errors.length > 0 ? result.errors : ["Beads creation produced no tasks"];
|
|
@@ -32623,8 +32687,8 @@ init_projects();
|
|
|
32623
32687
|
init_tracker_utils();
|
|
32624
32688
|
init_tmux();
|
|
32625
32689
|
init_typed_errors();
|
|
32626
|
-
const execAsync$
|
|
32627
|
-
const execFileAsync$
|
|
32690
|
+
const execAsync$8 = promisify(exec);
|
|
32691
|
+
const execFileAsync$10 = promisify(execFile);
|
|
32628
32692
|
function getIssueDataService$6() {
|
|
32629
32693
|
return getSharedIssueService();
|
|
32630
32694
|
}
|
|
@@ -32924,7 +32988,7 @@ function abortPlanningForIssue(options) {
|
|
|
32924
32988
|
const featureWorkspacePath = join$1(projectPath, "workspaces", `feature-${issueIdentifier.toLowerCase()}`);
|
|
32925
32989
|
const plainWorkspacePath = join$1(projectPath, "workspaces", issueIdentifier.toLowerCase());
|
|
32926
32990
|
if (existsSync$1(existsSync$1(featureWorkspacePath) ? featureWorkspacePath : plainWorkspacePath)) {
|
|
32927
|
-
await execFileAsync$
|
|
32991
|
+
await execFileAsync$10("pan", [
|
|
32928
32992
|
"workspace",
|
|
32929
32993
|
"destroy",
|
|
32930
32994
|
issueIdentifier.toLowerCase(),
|
|
@@ -33033,10 +33097,10 @@ function restartFromPlan(options) {
|
|
|
33033
33097
|
});
|
|
33034
33098
|
const resetResult = yield* Effect.promise(async () => {
|
|
33035
33099
|
try {
|
|
33036
|
-
const { runGitResetHard } = await import("./dangerous-git-ops-
|
|
33100
|
+
const { runGitResetHard } = await import("./dangerous-git-ops-C9iYTZ0M.js");
|
|
33037
33101
|
async function findPlanningCommit(grep, label) {
|
|
33038
33102
|
try {
|
|
33039
|
-
const { stdout } = await execAsync$
|
|
33103
|
+
const { stdout } = await execAsync$8(`git log --grep="${grep.replace(/"/g, "\\\"")}" --format=%H -1`, {
|
|
33040
33104
|
cwd: workspacePath,
|
|
33041
33105
|
encoding: "utf-8",
|
|
33042
33106
|
timeout: 1e4
|
|
@@ -33052,7 +33116,7 @@ function restartFromPlan(options) {
|
|
|
33052
33116
|
}
|
|
33053
33117
|
const found = await findPlanningCommit(`Complete planning for ${id}`, "complete-planning message") ?? await findPlanningCommit(`chore: planning artifacts for ${id}`, "agent-start message") ?? await (async () => {
|
|
33054
33118
|
try {
|
|
33055
|
-
const { stdout } = await execAsync$
|
|
33119
|
+
const { stdout } = await execAsync$8(`git log --diff-filter=A --format=%H -1 -- .pan/spec.vbrief.json`, {
|
|
33056
33120
|
cwd: workspacePath,
|
|
33057
33121
|
encoding: "utf-8",
|
|
33058
33122
|
timeout: 1e4
|
|
@@ -33179,6 +33243,7 @@ function getPlanningState(id) {
|
|
|
33179
33243
|
}
|
|
33180
33244
|
//#endregion
|
|
33181
33245
|
//#region ../../lib/overdeck/discussions.ts
|
|
33246
|
+
init_cost_sync();
|
|
33182
33247
|
init_github_app();
|
|
33183
33248
|
init_pull_requests();
|
|
33184
33249
|
init_issue_id();
|
|
@@ -33186,7 +33251,7 @@ init_github_pr_lookup();
|
|
|
33186
33251
|
init_tracker_utils();
|
|
33187
33252
|
init_tracker_config();
|
|
33188
33253
|
init_pr_tab_cache();
|
|
33189
|
-
const execAsync$
|
|
33254
|
+
const execAsync$7 = promisify(exec);
|
|
33190
33255
|
function isGitHubIssue$2(issueId) {
|
|
33191
33256
|
const resolved = resolveGitHubIssueSync(issueId);
|
|
33192
33257
|
if (resolved.isGitHub) return {
|
|
@@ -33234,7 +33299,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
|
|
|
33234
33299
|
if (githubCheck.isGitHub && githubCheck.owner && githubCheck.repo && githubCheck.number) {
|
|
33235
33300
|
const { owner, repo, number } = githubCheck;
|
|
33236
33301
|
try {
|
|
33237
|
-
const { stdout } = await execAsync$
|
|
33302
|
+
const { stdout } = await execAsync$7(`gh api "repos/${owner}/${repo}/issues/${number}/comments?per_page=100"`, {
|
|
33238
33303
|
encoding: "utf-8",
|
|
33239
33304
|
timeout: 15e3,
|
|
33240
33305
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -33297,7 +33362,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
|
|
|
33297
33362
|
const collectedItems = [];
|
|
33298
33363
|
const prConversation = (async () => {
|
|
33299
33364
|
try {
|
|
33300
|
-
const { stdout } = await execAsync$
|
|
33365
|
+
const { stdout } = await execAsync$7(`gh api "repos/${prOwner}/${prRepo}/issues/${prNumber}/comments?per_page=100"`, {
|
|
33301
33366
|
encoding: "utf-8",
|
|
33302
33367
|
timeout: 15e3,
|
|
33303
33368
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -33318,7 +33383,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
|
|
|
33318
33383
|
})();
|
|
33319
33384
|
const prReviews = (async () => {
|
|
33320
33385
|
try {
|
|
33321
|
-
const { stdout } = await execAsync$
|
|
33386
|
+
const { stdout } = await execAsync$7(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/reviews?per_page=100"`, {
|
|
33322
33387
|
encoding: "utf-8",
|
|
33323
33388
|
timeout: 15e3,
|
|
33324
33389
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -33343,7 +33408,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
|
|
|
33343
33408
|
})();
|
|
33344
33409
|
const prInlineComments = (async () => {
|
|
33345
33410
|
try {
|
|
33346
|
-
const { stdout } = await execAsync$
|
|
33411
|
+
const { stdout } = await execAsync$7(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/comments?per_page=100"`, {
|
|
33347
33412
|
encoding: "utf-8",
|
|
33348
33413
|
timeout: 15e3,
|
|
33349
33414
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -33485,7 +33550,7 @@ function closeOutIssue(id) {
|
|
|
33485
33550
|
}
|
|
33486
33551
|
const closeOutResult = yield* Effect.promise(async () => {
|
|
33487
33552
|
try {
|
|
33488
|
-
const { closeOut } = await import("./lifecycle-
|
|
33553
|
+
const { closeOut } = await import("./lifecycle-Va4AZGRx.js");
|
|
33489
33554
|
return {
|
|
33490
33555
|
ok: true,
|
|
33491
33556
|
result: await Effect.runPromise(closeOut(ctx))
|
|
@@ -33571,7 +33636,7 @@ function bulkCloseOut(body) {
|
|
|
33571
33636
|
const invalidIds = issueIds.filter((id) => !isValidIssueId(id));
|
|
33572
33637
|
if (invalidIds.length > 0) return jsonResponse({ error: `Invalid issue ID format: ${invalidIds.join(", ")}` }, { status: 400 });
|
|
33573
33638
|
const eventStore = yield* EventStoreService;
|
|
33574
|
-
const { closeOut } = yield* Effect.promise(() => import("./lifecycle-
|
|
33639
|
+
const { closeOut } = yield* Effect.promise(() => import("./lifecycle-Va4AZGRx.js"));
|
|
33575
33640
|
const issueDataService = getIssueDataService$5();
|
|
33576
33641
|
const tasks = [];
|
|
33577
33642
|
const agentChecks = yield* withConcurrencyLimit(issueIds.map((id) => Effect.promise(async () => {
|
|
@@ -33738,7 +33803,7 @@ promisify(execFile);
|
|
|
33738
33803
|
function getIssueDataService$4() {
|
|
33739
33804
|
return getSharedIssueService();
|
|
33740
33805
|
}
|
|
33741
|
-
const readJsonBody$
|
|
33806
|
+
const readJsonBody$15 = Effect.gen(function* () {
|
|
33742
33807
|
const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
|
|
33743
33808
|
try {
|
|
33744
33809
|
return text ? JSON.parse(text) : {};
|
|
@@ -33763,12 +33828,28 @@ const getIssueAnalyzeRoute = HttpRouter.add("GET", "/api/issues/:id/analyze", ht
|
|
|
33763
33828
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33764
33829
|
return yield* analyzeIssue(id);
|
|
33765
33830
|
})));
|
|
33831
|
+
const getIssueShipLogRoute = HttpRouter.add("GET", "/api/issues/:id/ship-log", httpHandler(Effect.gen(function* () {
|
|
33832
|
+
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
33833
|
+
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33834
|
+
return yield* Effect.promise(async () => {
|
|
33835
|
+
const { getShipLog } = await import("./ship-log-EV79hCrN.js");
|
|
33836
|
+
const { getReviewStatusSync } = await import("./review-status-Rr4SB2Kj.js");
|
|
33837
|
+
const log = getShipLog(id);
|
|
33838
|
+
const rs = getReviewStatusSync(id);
|
|
33839
|
+
return jsonResponse({
|
|
33840
|
+
issueId: id.toUpperCase(),
|
|
33841
|
+
mergeStatus: rs?.mergeStatus ?? null,
|
|
33842
|
+
mergeStep: rs?.mergeStep ?? null,
|
|
33843
|
+
log
|
|
33844
|
+
});
|
|
33845
|
+
});
|
|
33846
|
+
})));
|
|
33766
33847
|
const postIssueCloseRoute = HttpRouter.add("POST", "/api/issues/:issueId/close", httpHandler(Effect.gen(function* () {
|
|
33767
33848
|
const issueId = (yield* HttpRouter.params)["issueId"] ?? "";
|
|
33768
33849
|
if (!parseIssueIdSync(issueId)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33769
33850
|
return yield* closeIssueTransition({
|
|
33770
33851
|
issueId,
|
|
33771
|
-
body: yield* readJsonBody$
|
|
33852
|
+
body: yield* readJsonBody$15,
|
|
33772
33853
|
eventStore: yield* EventStoreService
|
|
33773
33854
|
});
|
|
33774
33855
|
})));
|
|
@@ -33777,7 +33858,7 @@ const postIssueStartPlanningRoute = HttpRouter.add("POST", "/api/issues/:id/star
|
|
|
33777
33858
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33778
33859
|
return yield* startPlanningForIssue({
|
|
33779
33860
|
id,
|
|
33780
|
-
body: yield* readJsonBody$
|
|
33861
|
+
body: yield* readJsonBody$15,
|
|
33781
33862
|
eventStore: yield* EventStoreService,
|
|
33782
33863
|
linear: yield* LinearClient,
|
|
33783
33864
|
github: yield* GitHubClient,
|
|
@@ -33790,7 +33871,7 @@ const postIssueAbortPlanningRoute = HttpRouter.add("POST", "/api/issues/:id/abor
|
|
|
33790
33871
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33791
33872
|
return yield* abortPlanningForIssue({
|
|
33792
33873
|
id,
|
|
33793
|
-
body: yield* readJsonBody$
|
|
33874
|
+
body: yield* readJsonBody$15,
|
|
33794
33875
|
lifecycle: yield* IssueLifecycle,
|
|
33795
33876
|
linear: yield* LinearClient,
|
|
33796
33877
|
eventStore: yield* EventStoreService
|
|
@@ -33800,7 +33881,7 @@ const postIssueCompletePlanningRoute = HttpRouter.add("POST", "/api/issues/:id/c
|
|
|
33800
33881
|
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
33801
33882
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
33802
33883
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33803
|
-
const body = yield* readJsonBody$
|
|
33884
|
+
const body = yield* readJsonBody$15;
|
|
33804
33885
|
const eventStore = yield* EventStoreService;
|
|
33805
33886
|
const linear = yield* LinearClient;
|
|
33806
33887
|
const lifecycle = yield* IssueLifecycle;
|
|
@@ -33826,7 +33907,7 @@ const postIssueResetRoute = HttpRouter.add("POST", "/api/issues/:id/reset", http
|
|
|
33826
33907
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33827
33908
|
return yield* resetIssueTransition({
|
|
33828
33909
|
id,
|
|
33829
|
-
body: yield* readJsonBody$
|
|
33910
|
+
body: yield* readJsonBody$15,
|
|
33830
33911
|
eventStore: yield* EventStoreService
|
|
33831
33912
|
});
|
|
33832
33913
|
})));
|
|
@@ -33835,7 +33916,7 @@ const postIssueCancelRoute = HttpRouter.add("POST", "/api/issues/:id/cancel", ht
|
|
|
33835
33916
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33836
33917
|
return yield* cancelIssueTransition({
|
|
33837
33918
|
id,
|
|
33838
|
-
body: yield* readJsonBody$
|
|
33919
|
+
body: yield* readJsonBody$15,
|
|
33839
33920
|
eventStore: yield* EventStoreService
|
|
33840
33921
|
});
|
|
33841
33922
|
})));
|
|
@@ -33846,7 +33927,7 @@ const postIssueReopenRoute = HttpRouter.add("POST", "/api/issues/:id/reopen", ht
|
|
|
33846
33927
|
if (authError) return authError;
|
|
33847
33928
|
return yield* reopenIssueTransition({
|
|
33848
33929
|
id,
|
|
33849
|
-
body: yield* readJsonBody$
|
|
33930
|
+
body: yield* readJsonBody$15,
|
|
33850
33931
|
lifecycle: yield* IssueLifecycle,
|
|
33851
33932
|
linear: yield* LinearClient,
|
|
33852
33933
|
eventStore: yield* EventStoreService
|
|
@@ -33866,7 +33947,7 @@ const postIssueMoveStatusRoute = HttpRouter.add("POST", "/api/issues/:id/move-st
|
|
|
33866
33947
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33867
33948
|
return yield* moveIssueStatus({
|
|
33868
33949
|
id,
|
|
33869
|
-
body: yield* readJsonBody$
|
|
33950
|
+
body: yield* readJsonBody$15,
|
|
33870
33951
|
eventStore: yield* EventStoreService,
|
|
33871
33952
|
lifecycle: yield* IssueLifecycle
|
|
33872
33953
|
});
|
|
@@ -33878,7 +33959,7 @@ const postIssueCleanupWorkspaceRoute = HttpRouter.add("POST", "/api/issues/:id/c
|
|
|
33878
33959
|
})));
|
|
33879
33960
|
const postIssueDeepWipeRoute = HttpRouter.add("POST", "/api/issues/:id/deep-wipe", httpHandler(Effect.gen(function* () {
|
|
33880
33961
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
33881
|
-
const body = yield* readJsonBody$
|
|
33962
|
+
const body = yield* readJsonBody$15;
|
|
33882
33963
|
const eventStore = yield* EventStoreService;
|
|
33883
33964
|
return yield* Effect.promise(() => deepWipeIssue(id, body, eventStore));
|
|
33884
33965
|
})));
|
|
@@ -33918,7 +33999,7 @@ const postIssueBeadInspectRoute = HttpRouter.add("POST", "/api/issues/:id/beads/
|
|
|
33918
33999
|
return yield* inspectIssueBead({
|
|
33919
34000
|
id: (params["id"] ?? "").toUpperCase(),
|
|
33920
34001
|
beadId: params["beadId"] ?? "",
|
|
33921
|
-
body: yield* readJsonBody$
|
|
34002
|
+
body: yield* readJsonBody$15
|
|
33922
34003
|
});
|
|
33923
34004
|
})));
|
|
33924
34005
|
const getIssuePlanningStateRoute = HttpRouter.add("GET", "/api/issues/:id/planning-state", httpHandler(Effect.gen(function* () {
|
|
@@ -33981,7 +34062,7 @@ const getIssueDiscussionsRoute = HttpRouter.add("GET", "/api/issues/:id/discussi
|
|
|
33981
34062
|
const getIssueCostsRoute = HttpRouter.add("GET", "/api/issues/:id/costs", httpHandler(Effect.gen(function* () {
|
|
33982
34063
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
33983
34064
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33984
|
-
const issueData =
|
|
34065
|
+
const issueData = getCostForIssueAggregateSync(id);
|
|
33985
34066
|
const agents = yield* Effect.promise(() => getCachedRunningAgents());
|
|
33986
34067
|
const resolvedCost = resolveIssueHeadlineCost({
|
|
33987
34068
|
issueId: id,
|
|
@@ -34029,7 +34110,7 @@ const getIssueCostsRoute = HttpRouter.add("GET", "/api/issues/:id/costs", httpHa
|
|
|
34029
34110
|
cost: stats.cost,
|
|
34030
34111
|
tokens: stats.tokens
|
|
34031
34112
|
}])),
|
|
34032
|
-
budget:
|
|
34113
|
+
budget: void 0,
|
|
34033
34114
|
budgetWarning: issueData.budgetWarning,
|
|
34034
34115
|
lastUpdated: issueData.lastUpdated
|
|
34035
34116
|
});
|
|
@@ -34040,7 +34121,7 @@ const getResourceAllocatedIssuesRoute = HttpRouter.add("GET", "/api/issues/resou
|
|
|
34040
34121
|
const getIssueResourceDetailsRoute = HttpRouter.add("GET", "/api/issues/:id/resource-details", httpHandler(Effect.gen(function* () {
|
|
34041
34122
|
return yield* getIssueResourceDetails((yield* HttpRouter.params)["id"] ?? "");
|
|
34042
34123
|
})));
|
|
34043
|
-
const issuesRouteLayer = Layer.mergeAll(getIssuesRoute, getIssueAnalyzeRoute, postIssueCloseRoute, postIssueStartPlanningRoute, postIssueAbortPlanningRoute, postIssueCompletePlanningRoute, postIssueAbortRoute, postIssueResetRoute, postIssueCancelRoute, postIssueReopenRoute, postIssueRestartFromPlanRoute, postIssueMoveStatusRoute, postIssueCleanupWorkspaceRoute, postIssueDeepWipeRoute, postIssueCopySettingsRoute, postIssueCloseOutRoute, postIssuesBulkCloseOutRoute, getIssueBeadsRoute, postIssueBeadInspectRoute, getIssuePlanningStateRoute, postIssueGenerateTasksRoute, getIssueCostsRoute, getResourceAllocatedIssuesRoute, getIssueResourceDetailsRoute, getIssuePrRoute, getIssuePrDiffRoute, getIssuePrDetailsRoute, getIssueCheckRunsRoute, getIssueDiscussionsRoute);
|
|
34124
|
+
const issuesRouteLayer = Layer.mergeAll(getIssuesRoute, getIssueAnalyzeRoute, getIssueShipLogRoute, postIssueCloseRoute, postIssueStartPlanningRoute, postIssueAbortPlanningRoute, postIssueCompletePlanningRoute, postIssueAbortRoute, postIssueResetRoute, postIssueCancelRoute, postIssueReopenRoute, postIssueRestartFromPlanRoute, postIssueMoveStatusRoute, postIssueCleanupWorkspaceRoute, postIssueDeepWipeRoute, postIssueCopySettingsRoute, postIssueCloseOutRoute, postIssuesBulkCloseOutRoute, getIssueBeadsRoute, postIssueBeadInspectRoute, getIssuePlanningStateRoute, postIssueGenerateTasksRoute, getIssueCostsRoute, getResourceAllocatedIssuesRoute, getIssueResourceDetailsRoute, getIssuePrRoute, getIssuePrDiffRoute, getIssuePrDetailsRoute, getIssueCheckRunsRoute, getIssueDiscussionsRoute);
|
|
34044
34125
|
//#endregion
|
|
34045
34126
|
//#region routes/costs.ts
|
|
34046
34127
|
/**
|
|
@@ -34075,7 +34156,7 @@ const getCostsSummaryRoute = HttpRouter.add("GET", "/api/costs/summary", httpHan
|
|
|
34075
34156
|
const monthEntries = scope(readEventsSync({ startDate: monthAgo }));
|
|
34076
34157
|
const summarize = (entries) => ({
|
|
34077
34158
|
totalCost: entries.reduce((sum, e) => sum + (e.cost || 0), 0),
|
|
34078
|
-
totalTokens: entries.reduce((sum, e) => sum + ((e.input || 0) + (e.output || 0)), 0),
|
|
34159
|
+
totalTokens: entries.reduce((sum, e) => sum + ((e.input || 0) + (e.output || 0) + (e.cacheRead || 0) + (e.cacheWrite || 0)), 0),
|
|
34079
34160
|
entryCount: entries.length,
|
|
34080
34161
|
byModel: entries.reduce((acc, e) => {
|
|
34081
34162
|
if (e.model) acc[e.model] = (acc[e.model] || 0) + (e.cost || 0);
|
|
@@ -34262,16 +34343,45 @@ const postCostsSyncWalRoute = HttpRouter.add("POST", "/api/costs/sync-wal", http
|
|
|
34262
34343
|
})
|
|
34263
34344
|
});
|
|
34264
34345
|
})));
|
|
34346
|
+
const runOverdeckCostReconcileSource = (source) => Effect.runPromise(CostWriter.use((writer) => writer.reconcile({ source })).pipe(Effect.provide(CostDoorLive)));
|
|
34347
|
+
const runClaudeTranscriptReconcile = () => Effect.runPromise(reconcile());
|
|
34348
|
+
async function runCostReconcileSources(runSource = runOverdeckCostReconcileSource, runClaude = runClaudeTranscriptReconcile) {
|
|
34349
|
+
const [claude, ohmypi, codex] = await Promise.all([
|
|
34350
|
+
runClaude(),
|
|
34351
|
+
runSource("ohmypi"),
|
|
34352
|
+
runSource("codex")
|
|
34353
|
+
]);
|
|
34354
|
+
return {
|
|
34355
|
+
claude,
|
|
34356
|
+
ohmypi,
|
|
34357
|
+
codex
|
|
34358
|
+
};
|
|
34359
|
+
}
|
|
34360
|
+
function buildCostReconcileResponse(overdeck) {
|
|
34361
|
+
return {
|
|
34362
|
+
success: true,
|
|
34363
|
+
...overdeck.claude,
|
|
34364
|
+
claude: overdeck.claude,
|
|
34365
|
+
ohmypi: overdeck.ohmypi,
|
|
34366
|
+
codex: overdeck.codex,
|
|
34367
|
+
overdeck,
|
|
34368
|
+
skipped: {
|
|
34369
|
+
ohmypi: overdeck.ohmypi.skipped,
|
|
34370
|
+
codex: overdeck.codex.skipped
|
|
34371
|
+
},
|
|
34372
|
+
warnings: {
|
|
34373
|
+
ohmypi: overdeck.ohmypi.warnings,
|
|
34374
|
+
codex: overdeck.codex.warnings
|
|
34375
|
+
}
|
|
34376
|
+
};
|
|
34377
|
+
}
|
|
34265
34378
|
const postCostsReconcileRoute = HttpRouter.add("POST", "/api/costs/reconcile", httpHandler(Effect.gen(function* () {
|
|
34266
|
-
const
|
|
34267
|
-
try: () =>
|
|
34379
|
+
const overdeck = yield* Effect.tryPromise({
|
|
34380
|
+
try: () => runCostReconcileSources(),
|
|
34268
34381
|
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34269
34382
|
});
|
|
34270
|
-
console.log(`[reconciler] Sweep complete:
|
|
34271
|
-
return jsonResponse(
|
|
34272
|
-
success: true,
|
|
34273
|
-
...result
|
|
34274
|
-
});
|
|
34383
|
+
console.log(`[reconciler] Sweep complete: claude=${overdeck.claude.eventsImported} imported, ohmypi=${overdeck.ohmypi.eventsImported} imported, codex=${overdeck.codex.eventsImported} imported`);
|
|
34384
|
+
return jsonResponse(buildCostReconcileResponse(overdeck));
|
|
34275
34385
|
})));
|
|
34276
34386
|
const getCostsExperimentsRoute = HttpRouter.add("GET", "/api/costs/experiments", httpHandler(Effect.try({
|
|
34277
34387
|
try: () => jsonResponse({ experiments: getCavemanExperimentDataSync() }),
|
|
@@ -34548,7 +34658,7 @@ function reloadDurableCloisterConfig(deps = {}) {
|
|
|
34548
34658
|
*/
|
|
34549
34659
|
init_config();
|
|
34550
34660
|
init_agents$1();
|
|
34551
|
-
const readJsonBody$
|
|
34661
|
+
const readJsonBody$14 = Effect.gen(function* () {
|
|
34552
34662
|
const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
|
|
34553
34663
|
try {
|
|
34554
34664
|
return text ? JSON.parse(text) : {};
|
|
@@ -34651,7 +34761,7 @@ const getCloisterConfigRoute = HttpRouter.add("GET", "/api/cloister/config", htt
|
|
|
34651
34761
|
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34652
34762
|
})));
|
|
34653
34763
|
const putCloisterConfigRoute = HttpRouter.add("PUT", "/api/cloister/config", httpHandler(Effect.gen(function* () {
|
|
34654
|
-
const updates = yield* readJsonBody$
|
|
34764
|
+
const updates = yield* readJsonBody$14;
|
|
34655
34765
|
yield* Effect.try({
|
|
34656
34766
|
try: () => {
|
|
34657
34767
|
saveCloisterConfigSync(updates);
|
|
@@ -34675,25 +34785,18 @@ const getCloisterAgentsHealthRoute = HttpRouter.add("GET", "/api/cloister/agents
|
|
|
34675
34785
|
})));
|
|
34676
34786
|
const cloisterRouteLayer = Layer.mergeAll(getCloisterStatusRoute, postCloisterStartRoute, postCloisterStopRoute, postCloisterEmergencyStopRoute, postCloisterBrakeRoute, postCloisterResumeSpawnsRoute, getCloisterSpawnStatusRoute, getCloisterConfigRoute, putCloisterConfigRoute, getCloisterAgentsHealthRoute);
|
|
34677
34787
|
//#endregion
|
|
34678
|
-
//#region routes/resources.ts
|
|
34679
|
-
/**
|
|
34680
|
-
* Resources route module — Effect HttpRouter.Layer (PAN-428 B12)
|
|
34681
|
-
*
|
|
34682
|
-
* Implements all /api/resources/* endpoints from the Express server:
|
|
34683
|
-
* GET /api/resources
|
|
34684
|
-
* GET /api/resources/:containerId/history
|
|
34685
|
-
* GET /api/resources/:containerId/details
|
|
34686
|
-
* DELETE /api/resources/docker/container/:id
|
|
34687
|
-
* POST /api/resources/docker/prune-containers
|
|
34688
|
-
* DELETE /api/resources/docker/network/:name
|
|
34689
|
-
* DELETE /api/resources/docker/volume/:name
|
|
34690
|
-
* POST /api/resources/docker/prune-volumes
|
|
34691
|
-
*/
|
|
34788
|
+
//#region routes/resources/shared.ts
|
|
34692
34789
|
init_docker_stats();
|
|
34693
|
-
|
|
34694
|
-
|
|
34695
|
-
|
|
34696
|
-
|
|
34790
|
+
function getCurrentDockerStats() {
|
|
34791
|
+
return [];
|
|
34792
|
+
}
|
|
34793
|
+
function getContainerHistory(containerId) {
|
|
34794
|
+
return {
|
|
34795
|
+
timestamps: [],
|
|
34796
|
+
cpuPercent: [],
|
|
34797
|
+
memoryPercent: []
|
|
34798
|
+
};
|
|
34799
|
+
}
|
|
34697
34800
|
function formatUptime(startedAt) {
|
|
34698
34801
|
const ms = Date.now() - new Date(startedAt).getTime();
|
|
34699
34802
|
const h = Math.floor(ms / 36e5);
|
|
@@ -34702,45 +34805,933 @@ function formatUptime(startedAt) {
|
|
|
34702
34805
|
if (h > 0) return `${h}h ${m}m`;
|
|
34703
34806
|
return `${m}m`;
|
|
34704
34807
|
}
|
|
34808
|
+
//#endregion
|
|
34809
|
+
//#region routes/resources/agents-stats.ts
|
|
34810
|
+
init_agents$1();
|
|
34811
|
+
init_cost_sync();
|
|
34812
|
+
init_tmux();
|
|
34813
|
+
const execFileAsync$9 = promisify(execFile);
|
|
34814
|
+
const BURN_WINDOW_MS = 1800 * 1e3;
|
|
34815
|
+
function buildAgentStatsSnapshot(options) {
|
|
34816
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
34817
|
+
const processTotalsByRoot = buildProcessTotalsByRoot(options.sessionRoots, options.processes);
|
|
34818
|
+
const rows = options.agents.filter((agent) => agent.status !== "stopped").map((agent) => {
|
|
34819
|
+
const root = options.sessionRoots.find((sessionRoot) => sessionRoot.agentId === agent.id);
|
|
34820
|
+
const processTotals = root ? processTotalsByRoot.get(root.rootPid) : void 0;
|
|
34821
|
+
const costStats = computeAgentCostStats(options.costEventsByAgent?.get(agent.id) ?? [], nowMs);
|
|
34822
|
+
const idleMinutes = getIdleMinutes(agent.lastActivity ?? agent.startedAt, nowMs);
|
|
34823
|
+
return {
|
|
34824
|
+
id: agent.id,
|
|
34825
|
+
issueId: agent.issueId,
|
|
34826
|
+
role: agent.role,
|
|
34827
|
+
model: agent.model,
|
|
34828
|
+
status: agent.status,
|
|
34829
|
+
statusChip: {
|
|
34830
|
+
state: idleMinutes > 5 ? "idle" : "working",
|
|
34831
|
+
idleMinutes,
|
|
34832
|
+
fanOut: (processTotals?.processCount ?? 0) >= 8
|
|
34833
|
+
},
|
|
34834
|
+
rootPid: root?.rootPid ?? null,
|
|
34835
|
+
processCount: processTotals?.processCount ?? 0,
|
|
34836
|
+
cpuPercent: processTotals?.cpuPercent ?? 0,
|
|
34837
|
+
memoryBytes: processTotals?.memoryBytes ?? 0,
|
|
34838
|
+
burnUsdPerHour: costStats.burnUsdPerHour,
|
|
34839
|
+
...costStats.hypotheticalUsdPerHour > 0 ? { hypotheticalUsdPerHour: costStats.hypotheticalUsdPerHour } : {},
|
|
34840
|
+
totalUsd: costStats.totalUsd
|
|
34841
|
+
};
|
|
34842
|
+
});
|
|
34843
|
+
return {
|
|
34844
|
+
agents: rows,
|
|
34845
|
+
hostVitals: { agents: {
|
|
34846
|
+
burnUsdPerHour: roundCurrency(rows.reduce((sum, row) => sum + row.burnUsdPerHour, 0)),
|
|
34847
|
+
hypotheticalUsdPerHour: roundCurrency(rows.reduce((sum, row) => sum + (row.hypotheticalUsdPerHour ?? 0), 0)),
|
|
34848
|
+
totalUsd: roundCurrency(rows.reduce((sum, row) => sum + row.totalUsd, 0))
|
|
34849
|
+
} }
|
|
34850
|
+
};
|
|
34851
|
+
}
|
|
34852
|
+
function getAgentStatsSnapshotEffect(deps = {}) {
|
|
34853
|
+
return Effect.gen(function* () {
|
|
34854
|
+
const nowMs = deps.nowMs ?? Date.now();
|
|
34855
|
+
const agents = (deps.listAgents ?? listAgentStates)().filter((agent) => agent.status !== "stopped");
|
|
34856
|
+
const sessionNames = yield* (deps.listSessionNames ?? defaultListSessionNames)().pipe(Effect.catch(() => Effect.succeed([])));
|
|
34857
|
+
const liveSessionNames = new Set(sessionNames);
|
|
34858
|
+
const sessionRoots = [];
|
|
34859
|
+
for (const agent of agents) {
|
|
34860
|
+
if (!liveSessionNames.has(agent.id)) continue;
|
|
34861
|
+
const rootPid = (yield* (deps.listPanePids ?? defaultListPanePids)(agent.id).pipe(Effect.catch(() => Effect.succeed([]))))[0];
|
|
34862
|
+
if (rootPid) sessionRoots.push({
|
|
34863
|
+
agentId: agent.id,
|
|
34864
|
+
rootPid
|
|
34865
|
+
});
|
|
34866
|
+
}
|
|
34867
|
+
const processes = sessionRoots.length > 0 ? yield* Effect.promise(() => (deps.readProcessTable ?? readBatchedProcessTable)()).pipe(Effect.catch(() => Effect.succeed([]))) : [];
|
|
34868
|
+
const costEventsByAgent = /* @__PURE__ */ new Map();
|
|
34869
|
+
const costQuery = deps.queryCostEvents ?? queryCostEventsSync;
|
|
34870
|
+
for (const agent of agents) {
|
|
34871
|
+
const recentStartTs = new Date(nowMs - BURN_WINDOW_MS).toISOString();
|
|
34872
|
+
costEventsByAgent.set(agent.id, [...costQuery({ agentId: agent.id }), ...costQuery({
|
|
34873
|
+
agentId: agent.id,
|
|
34874
|
+
startTs: recentStartTs
|
|
34875
|
+
})]);
|
|
34876
|
+
}
|
|
34877
|
+
return buildAgentStatsSnapshot({
|
|
34878
|
+
agents,
|
|
34879
|
+
sessionRoots,
|
|
34880
|
+
processes,
|
|
34881
|
+
costEventsByAgent,
|
|
34882
|
+
nowMs
|
|
34883
|
+
});
|
|
34884
|
+
});
|
|
34885
|
+
}
|
|
34886
|
+
function parseProcessTable$1(psTable) {
|
|
34887
|
+
return psTable.split("\n").map((line) => line.trim()).filter(Boolean).flatMap((line) => {
|
|
34888
|
+
const match = line.match(/^(\d+)\s+(\d+)\s+([0-9.]+)\s+(\d+)$/);
|
|
34889
|
+
if (!match) return [];
|
|
34890
|
+
return [{
|
|
34891
|
+
pid: Number(match[1]),
|
|
34892
|
+
ppid: Number(match[2]),
|
|
34893
|
+
cpuPercent: Number(match[3]),
|
|
34894
|
+
rssBytes: Number(match[4]) * 1024
|
|
34895
|
+
}];
|
|
34896
|
+
});
|
|
34897
|
+
}
|
|
34898
|
+
async function readBatchedProcessTable() {
|
|
34899
|
+
const { stdout } = await execFileAsync$9("ps", ["-eo", "pid=,ppid=,pcpu=,rss="], { encoding: "utf8" });
|
|
34900
|
+
return parseProcessTable$1(stdout);
|
|
34901
|
+
}
|
|
34902
|
+
function defaultListSessionNames() {
|
|
34903
|
+
return listSessions().pipe(Effect.map((sessions) => sessions.map((session) => session.name)));
|
|
34904
|
+
}
|
|
34905
|
+
function defaultListPanePids(sessionName) {
|
|
34906
|
+
return listPaneValues(sessionName, "#{pane_pid}").pipe(Effect.map((values) => values.map((value) => Number(value.trim())).filter((value) => Number.isFinite(value) && value > 0)));
|
|
34907
|
+
}
|
|
34908
|
+
function buildProcessTotalsByRoot(roots, processes) {
|
|
34909
|
+
const processByPid = new Map(processes.map((process) => [process.pid, process]));
|
|
34910
|
+
const childrenByPpid = /* @__PURE__ */ new Map();
|
|
34911
|
+
const totals = /* @__PURE__ */ new Map();
|
|
34912
|
+
for (const process of processes) {
|
|
34913
|
+
const siblings = childrenByPpid.get(process.ppid);
|
|
34914
|
+
if (siblings) siblings.push(process);
|
|
34915
|
+
else childrenByPpid.set(process.ppid, [process]);
|
|
34916
|
+
}
|
|
34917
|
+
for (const root of roots) {
|
|
34918
|
+
const queue = [root.rootPid];
|
|
34919
|
+
const seen = /* @__PURE__ */ new Set();
|
|
34920
|
+
let cpuPercent = 0;
|
|
34921
|
+
let memoryBytes = 0;
|
|
34922
|
+
while (queue.length > 0) {
|
|
34923
|
+
const pid = queue.pop();
|
|
34924
|
+
if (seen.has(pid)) continue;
|
|
34925
|
+
seen.add(pid);
|
|
34926
|
+
const process = processByPid.get(pid);
|
|
34927
|
+
if (process) {
|
|
34928
|
+
cpuPercent += process.cpuPercent;
|
|
34929
|
+
memoryBytes += process.rssBytes;
|
|
34930
|
+
}
|
|
34931
|
+
for (const child of childrenByPpid.get(pid) ?? []) queue.push(child.pid);
|
|
34932
|
+
}
|
|
34933
|
+
totals.set(root.rootPid, {
|
|
34934
|
+
processCount: seen.size,
|
|
34935
|
+
cpuPercent: roundOneDecimal$3(cpuPercent),
|
|
34936
|
+
memoryBytes
|
|
34937
|
+
});
|
|
34938
|
+
}
|
|
34939
|
+
return totals;
|
|
34940
|
+
}
|
|
34941
|
+
function computeAgentCostStats(events, nowMs) {
|
|
34942
|
+
const burnStartMs = nowMs - BURN_WINDOW_MS;
|
|
34943
|
+
let recentBillable = 0;
|
|
34944
|
+
let recentHypothetical = 0;
|
|
34945
|
+
let totalUsd = 0;
|
|
34946
|
+
for (const event of events) {
|
|
34947
|
+
const eventCost = event.hypotheticalCost ?? event.cost;
|
|
34948
|
+
if (isSubscriptionCovered(event)) recentHypothetical += Date.parse(event.ts) >= burnStartMs ? eventCost : 0;
|
|
34949
|
+
else {
|
|
34950
|
+
totalUsd += event.cost;
|
|
34951
|
+
recentBillable += Date.parse(event.ts) >= burnStartMs ? event.cost : 0;
|
|
34952
|
+
}
|
|
34953
|
+
}
|
|
34954
|
+
return {
|
|
34955
|
+
burnUsdPerHour: roundCurrency(recentBillable * 2),
|
|
34956
|
+
hypotheticalUsdPerHour: roundCurrency(recentHypothetical * 2),
|
|
34957
|
+
totalUsd: roundCurrency(totalUsd)
|
|
34958
|
+
};
|
|
34959
|
+
}
|
|
34960
|
+
function isSubscriptionCovered(event) {
|
|
34961
|
+
return event.subscriptionCovered === true || event.source === "subscription-covered";
|
|
34962
|
+
}
|
|
34963
|
+
function getIdleMinutes(lastActivity, nowMs) {
|
|
34964
|
+
if (!lastActivity) return 0;
|
|
34965
|
+
const lastActivityMs = Date.parse(lastActivity);
|
|
34966
|
+
if (!Number.isFinite(lastActivityMs)) return 0;
|
|
34967
|
+
return Math.max(0, Math.floor((nowMs - lastActivityMs) / 6e4));
|
|
34968
|
+
}
|
|
34969
|
+
function roundOneDecimal$3(value) {
|
|
34970
|
+
return Math.round(value * 10) / 10;
|
|
34971
|
+
}
|
|
34972
|
+
function roundCurrency(value) {
|
|
34973
|
+
return Math.round(value * 100) / 100;
|
|
34974
|
+
}
|
|
34975
|
+
//#endregion
|
|
34976
|
+
//#region services/event-loop-monitor.ts
|
|
34977
|
+
init_activity_logger();
|
|
34978
|
+
const EVENT_LOOP_MONITOR_WINDOW_MS = 6e4;
|
|
34979
|
+
let lastSample = {
|
|
34980
|
+
p50: 0,
|
|
34981
|
+
p99: 0,
|
|
34982
|
+
max: 0,
|
|
34983
|
+
unit: "ms",
|
|
34984
|
+
sampledAt: null,
|
|
34985
|
+
windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
|
|
34986
|
+
};
|
|
34987
|
+
let timer$3 = null;
|
|
34988
|
+
let activeHistogram = null;
|
|
34989
|
+
function nsToRoundedMs(ns) {
|
|
34990
|
+
if (!Number.isFinite(ns) || ns <= 0) return 0;
|
|
34991
|
+
return Math.round(ns / 1e6 * 100) / 100;
|
|
34992
|
+
}
|
|
34993
|
+
function sampleEventLoopDelay(histogram, deps = {}) {
|
|
34994
|
+
const warnThresholdMs = deps.warnThresholdMs ?? 100;
|
|
34995
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
34996
|
+
const warn = deps.warn ?? ((message) => console.warn(message));
|
|
34997
|
+
const emit = deps.emit ?? emitActivityEntrySync;
|
|
34998
|
+
const sample = {
|
|
34999
|
+
p50: nsToRoundedMs(histogram.percentile(50)),
|
|
35000
|
+
p99: nsToRoundedMs(histogram.percentile(99)),
|
|
35001
|
+
max: nsToRoundedMs(histogram.max),
|
|
35002
|
+
unit: "ms",
|
|
35003
|
+
sampledAt: now().toISOString(),
|
|
35004
|
+
windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
|
|
35005
|
+
};
|
|
35006
|
+
lastSample = sample;
|
|
35007
|
+
if (sample.p99 > warnThresholdMs) {
|
|
35008
|
+
const message = `Dashboard event loop p99 delay ${sample.p99}ms exceeded ${warnThresholdMs}ms`;
|
|
35009
|
+
warn(`[event-loop-monitor] ${message}`);
|
|
35010
|
+
emit({
|
|
35011
|
+
source: "dashboard",
|
|
35012
|
+
level: "warn",
|
|
35013
|
+
message,
|
|
35014
|
+
details: `p50=${sample.p50}ms p99=${sample.p99}ms max=${sample.max}ms over ${sample.windowMs}ms`
|
|
35015
|
+
});
|
|
35016
|
+
}
|
|
35017
|
+
histogram.reset();
|
|
35018
|
+
return sample;
|
|
35019
|
+
}
|
|
35020
|
+
function getEventLoopDelaySample() {
|
|
35021
|
+
return lastSample;
|
|
35022
|
+
}
|
|
35023
|
+
function startEventLoopMonitor(deps = {}) {
|
|
35024
|
+
if (timer$3) return;
|
|
35025
|
+
const intervalMs = deps.intervalMs ?? 6e4;
|
|
35026
|
+
activeHistogram = (deps.createHistogram ?? (() => monitorEventLoopDelay({ resolution: 20 })))();
|
|
35027
|
+
activeHistogram.enable();
|
|
35028
|
+
timer$3 = setInterval(() => {
|
|
35029
|
+
if (!activeHistogram) return;
|
|
35030
|
+
sampleEventLoopDelay(activeHistogram, deps);
|
|
35031
|
+
}, intervalMs);
|
|
35032
|
+
timer$3.unref?.();
|
|
35033
|
+
}
|
|
35034
|
+
function stopEventLoopMonitor() {
|
|
35035
|
+
if (timer$3) {
|
|
35036
|
+
clearInterval(timer$3);
|
|
35037
|
+
timer$3 = null;
|
|
35038
|
+
}
|
|
35039
|
+
activeHistogram?.disable();
|
|
35040
|
+
activeHistogram = null;
|
|
35041
|
+
}
|
|
35042
|
+
//#endregion
|
|
35043
|
+
//#region routes/resources/core-services.ts
|
|
35044
|
+
function buildCoreServices(options = {}) {
|
|
35045
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
35046
|
+
const processInfo = options.processInfo ?? getCurrentProcessInfo();
|
|
35047
|
+
const eventLoopSample = options.eventLoopSample ?? getEventLoopDelaySample();
|
|
35048
|
+
const deaconStatus = options.deaconStatus ?? readDurableDeaconStatus();
|
|
35049
|
+
const supportFleet = aggregateSupportFleet(options.supportProcesses ?? []);
|
|
35050
|
+
return [
|
|
35051
|
+
{
|
|
35052
|
+
id: "dashboard",
|
|
35053
|
+
label: "Dashboard server",
|
|
35054
|
+
status: "running",
|
|
35055
|
+
cpuPercent: processInfo.cpuPercent,
|
|
35056
|
+
memoryBytes: processInfo.memoryBytes,
|
|
35057
|
+
memberCount: 1,
|
|
35058
|
+
nodeVersion: processInfo.nodeVersion,
|
|
35059
|
+
distPath: processInfo.distPath,
|
|
35060
|
+
uptime: formatDuration(processInfo.uptimeSeconds),
|
|
35061
|
+
uptimeSeconds: Math.round(processInfo.uptimeSeconds),
|
|
35062
|
+
eventLoopP99Ms: eventLoopSample.p99,
|
|
35063
|
+
actions: [{
|
|
35064
|
+
label: "Metrics",
|
|
35065
|
+
href: "/api/metrics/summary",
|
|
35066
|
+
method: "GET"
|
|
35067
|
+
}]
|
|
35068
|
+
},
|
|
35069
|
+
{
|
|
35070
|
+
id: "deacon",
|
|
35071
|
+
label: "Deacon",
|
|
35072
|
+
status: deaconStatus.isRunning ? "running" : "stopped",
|
|
35073
|
+
cpuPercent: 0,
|
|
35074
|
+
memoryBytes: 0,
|
|
35075
|
+
memberCount: deaconStatus.pid ? 1 : 0,
|
|
35076
|
+
pid: deaconStatus.pid,
|
|
35077
|
+
lastTickAgeSeconds: getLastTickAgeSeconds(deaconStatus, nowMs),
|
|
35078
|
+
patrolCycle: deaconStatus.lastPatrol?.cycle ?? deaconStatus.state?.patrolCycle ?? 0,
|
|
35079
|
+
patrolSummaryCount: deaconStatus.lastPatrol?.actions.length ?? 0,
|
|
35080
|
+
stuckCount: deaconStatus.state?.stuckCount ?? 0,
|
|
35081
|
+
actions: [
|
|
35082
|
+
{
|
|
35083
|
+
label: "Status",
|
|
35084
|
+
href: "/api/deacon/status",
|
|
35085
|
+
method: "GET"
|
|
35086
|
+
},
|
|
35087
|
+
{
|
|
35088
|
+
label: "Logs",
|
|
35089
|
+
href: "/api/deacon/logs",
|
|
35090
|
+
method: "GET"
|
|
35091
|
+
},
|
|
35092
|
+
{
|
|
35093
|
+
label: "Patrol",
|
|
35094
|
+
href: "/api/deacon/patrol",
|
|
35095
|
+
method: "POST"
|
|
35096
|
+
}
|
|
35097
|
+
]
|
|
35098
|
+
},
|
|
35099
|
+
{
|
|
35100
|
+
id: "support-fleet",
|
|
35101
|
+
label: "Support fleet",
|
|
35102
|
+
status: supportFleet.memberCount > 0 ? "running" : "unknown",
|
|
35103
|
+
cpuPercent: supportFleet.cpuPercent,
|
|
35104
|
+
memoryBytes: supportFleet.memoryBytes,
|
|
35105
|
+
memberCount: supportFleet.memberCount,
|
|
35106
|
+
members: supportFleet.members,
|
|
35107
|
+
actions: []
|
|
35108
|
+
}
|
|
35109
|
+
];
|
|
35110
|
+
}
|
|
35111
|
+
function getCoreServicesSnapshot() {
|
|
35112
|
+
return buildCoreServices();
|
|
35113
|
+
}
|
|
35114
|
+
function getCurrentProcessInfo() {
|
|
35115
|
+
return {
|
|
35116
|
+
uptimeSeconds: process.uptime(),
|
|
35117
|
+
nodeVersion: process.version,
|
|
35118
|
+
distPath: process.argv.find((arg) => arg.includes("/dist/")) ?? process.argv[1] ?? "",
|
|
35119
|
+
cpuPercent: 0,
|
|
35120
|
+
memoryBytes: process.memoryUsage().rss
|
|
35121
|
+
};
|
|
35122
|
+
}
|
|
35123
|
+
function aggregateSupportFleet(processes) {
|
|
35124
|
+
const supportProcesses = processes.filter(isSupportFleetProcess);
|
|
35125
|
+
const members = supportProcesses.map((process) => process.command);
|
|
35126
|
+
return {
|
|
35127
|
+
cpuPercent: roundOneDecimal$2(supportProcesses.reduce((sum, process) => sum + process.cpuPercent, 0)),
|
|
35128
|
+
memoryBytes: supportProcesses.reduce((sum, process) => sum + process.memoryBytes, 0),
|
|
35129
|
+
memberCount: supportProcesses.length,
|
|
35130
|
+
members
|
|
35131
|
+
};
|
|
35132
|
+
}
|
|
35133
|
+
function isSupportFleetProcess(process) {
|
|
35134
|
+
return /\b(traefik|smee|pty-supervisor|pty-supervisors|tldr)\b/i.test(process.command);
|
|
35135
|
+
}
|
|
35136
|
+
function getLastTickAgeSeconds(deaconStatus, nowMs) {
|
|
35137
|
+
const timestamp = deaconStatus.lastPatrol?.timestamp ?? deaconStatus.state?.lastPatrol;
|
|
35138
|
+
if (!timestamp) return null;
|
|
35139
|
+
const tickMs = Date.parse(timestamp);
|
|
35140
|
+
if (!Number.isFinite(tickMs)) return null;
|
|
35141
|
+
return Math.max(0, Math.floor((nowMs - tickMs) / 1e3));
|
|
35142
|
+
}
|
|
35143
|
+
function formatDuration(totalSeconds) {
|
|
35144
|
+
const roundedSeconds = Math.max(0, Math.floor(totalSeconds));
|
|
35145
|
+
const days = Math.floor(roundedSeconds / 86400);
|
|
35146
|
+
const hours = Math.floor(roundedSeconds % 86400 / 3600);
|
|
35147
|
+
const minutes = Math.floor(roundedSeconds % 3600 / 60);
|
|
35148
|
+
const seconds = roundedSeconds % 60;
|
|
35149
|
+
if (days > 0) return `${days}d ${hours}h`;
|
|
35150
|
+
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
35151
|
+
if (minutes > 0) return `${minutes}m ${seconds}s`;
|
|
35152
|
+
return `${seconds}s`;
|
|
35153
|
+
}
|
|
35154
|
+
function roundOneDecimal$2(value) {
|
|
35155
|
+
return Math.round(value * 10) / 10;
|
|
35156
|
+
}
|
|
35157
|
+
//#endregion
|
|
35158
|
+
//#region routes/resources/forecast.ts
|
|
35159
|
+
init_paths();
|
|
35160
|
+
let forecastStateFileForTests = null;
|
|
35161
|
+
function buildCapacityForecast(stacks, options) {
|
|
35162
|
+
const records = loadForecastStore().peaks;
|
|
35163
|
+
const median = medianPeak(Object.values(records).map((record) => record.peakRamBytes));
|
|
35164
|
+
return {
|
|
35165
|
+
stacks: stacks.filter((stack) => stack.services.every((service) => service.status === "stopped")).map((stack) => {
|
|
35166
|
+
const record = records[stack.id];
|
|
35167
|
+
const predictedRamBytes = record?.peakRamBytes ?? median;
|
|
35168
|
+
return {
|
|
35169
|
+
stackId: stack.id,
|
|
35170
|
+
issueId: stack.issueId,
|
|
35171
|
+
composeProject: stack.composeProject,
|
|
35172
|
+
predictedRamBytes,
|
|
35173
|
+
predictedLoad: record?.peakCpuPercent ?? 0,
|
|
35174
|
+
approximate: true,
|
|
35175
|
+
source: record ? "last-run-peak" : "fleet-median"
|
|
35176
|
+
};
|
|
35177
|
+
}),
|
|
35178
|
+
headroom: {
|
|
35179
|
+
freeRamBytes: options.hostVitals.mem.availableBytes,
|
|
35180
|
+
loadHeadroom: Math.max(0, 100 - options.hostVitals.cpu.percent)
|
|
35181
|
+
}
|
|
35182
|
+
};
|
|
35183
|
+
}
|
|
35184
|
+
function forecastStateFile() {
|
|
35185
|
+
return forecastStateFileForTests ?? join$1(getOverdeckHome(), "resource-forecast-peaks.json");
|
|
35186
|
+
}
|
|
35187
|
+
function loadForecastStore() {
|
|
35188
|
+
const file = forecastStateFile();
|
|
35189
|
+
if (!existsSync$1(file)) return { peaks: {} };
|
|
35190
|
+
try {
|
|
35191
|
+
return { peaks: JSON.parse(readFileSync$1(file, "utf8")).peaks ?? {} };
|
|
35192
|
+
} catch {
|
|
35193
|
+
return { peaks: {} };
|
|
35194
|
+
}
|
|
35195
|
+
}
|
|
35196
|
+
function medianPeak(values) {
|
|
35197
|
+
const sorted = values.filter((value) => value > 0).sort((a, b) => a - b);
|
|
35198
|
+
if (sorted.length === 0) return 0;
|
|
35199
|
+
return sorted[Math.floor(sorted.length / 2)] ?? 0;
|
|
35200
|
+
}
|
|
35201
|
+
//#endregion
|
|
35202
|
+
//#region routes/resources/host-processes.ts
|
|
35203
|
+
const DEFAULT_LIMIT$1 = 10;
|
|
35204
|
+
const DEFAULT_BURST_CPU_THRESHOLD = 50;
|
|
35205
|
+
const DEFAULT_BURST_RETENTION_MS = 3600 * 1e3;
|
|
35206
|
+
const retainedBursts = /* @__PURE__ */ new Map();
|
|
35207
|
+
function buildHostProcesses(processes, options = {}) {
|
|
35208
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
35209
|
+
const limit = options.limit ?? DEFAULT_LIMIT$1;
|
|
35210
|
+
const burstCpuThreshold = options.burstCpuThreshold ?? DEFAULT_BURST_CPU_THRESHOLD;
|
|
35211
|
+
const burstRetentionMs = options.burstRetentionMs ?? DEFAULT_BURST_RETENTION_MS;
|
|
35212
|
+
const coreServicePids = new Set(options.coreServicePids ?? []);
|
|
35213
|
+
const processByPid = new Map(processes.map((process) => [process.pid, process]));
|
|
35214
|
+
const agentRootByPid = new Map((options.agentSessions ?? []).map((session) => [session.rootPid, session]));
|
|
35215
|
+
const groups = /* @__PURE__ */ new Map();
|
|
35216
|
+
for (const process of processes) {
|
|
35217
|
+
if (coreServicePids.has(process.pid) || agentRootByPid.has(process.pid) || isDockerProcess(process)) continue;
|
|
35218
|
+
const family = classifyCommandFamily(process.command);
|
|
35219
|
+
const owner = resolveOwner(process, processByPid, agentRootByPid, family);
|
|
35220
|
+
const id = [
|
|
35221
|
+
family,
|
|
35222
|
+
owner.agentId ?? owner.label,
|
|
35223
|
+
owner.issueId ?? "host"
|
|
35224
|
+
].join(":");
|
|
35225
|
+
const existing = groups.get(id);
|
|
35226
|
+
if (existing) {
|
|
35227
|
+
existing.pidCount += 1;
|
|
35228
|
+
existing.pids.push(process.pid);
|
|
35229
|
+
existing.cpuPercent += process.cpuPercent;
|
|
35230
|
+
existing.memoryBytes += process.memoryBytes;
|
|
35231
|
+
continue;
|
|
35232
|
+
}
|
|
35233
|
+
groups.set(id, {
|
|
35234
|
+
id,
|
|
35235
|
+
family,
|
|
35236
|
+
label: family,
|
|
35237
|
+
owner,
|
|
35238
|
+
pidCount: 1,
|
|
35239
|
+
pids: [process.pid],
|
|
35240
|
+
cpuPercent: process.cpuPercent,
|
|
35241
|
+
memoryBytes: process.memoryBytes
|
|
35242
|
+
});
|
|
35243
|
+
}
|
|
35244
|
+
const activeRows = [...groups.values()].map((group) => {
|
|
35245
|
+
const retained = retainedBursts.get(group.id);
|
|
35246
|
+
const peakCpuPercent = Math.max(group.cpuPercent, retained?.row.peakCpuPercent ?? 0);
|
|
35247
|
+
const peakMemoryBytes = Math.max(group.memoryBytes, retained?.row.peakMemoryBytes ?? 0);
|
|
35248
|
+
const row = {
|
|
35249
|
+
...group,
|
|
35250
|
+
pids: group.pids.sort((a, b) => a - b),
|
|
35251
|
+
cpuPercent: roundOneDecimal$1(group.cpuPercent),
|
|
35252
|
+
peakCpuPercent: roundOneDecimal$1(peakCpuPercent),
|
|
35253
|
+
peakMemoryBytes
|
|
35254
|
+
};
|
|
35255
|
+
if (peakCpuPercent >= burstCpuThreshold) retainedBursts.set(group.id, {
|
|
35256
|
+
row,
|
|
35257
|
+
lastSeenMs: nowMs
|
|
35258
|
+
});
|
|
35259
|
+
else retainedBursts.delete(group.id);
|
|
35260
|
+
return row;
|
|
35261
|
+
});
|
|
35262
|
+
const activeGroupIds = new Set(activeRows.map((row) => row.id));
|
|
35263
|
+
const retainedRows = [];
|
|
35264
|
+
for (const [groupId, retained] of retainedBursts) {
|
|
35265
|
+
if (activeGroupIds.has(groupId)) continue;
|
|
35266
|
+
const retainedUntilMs = retained.lastSeenMs + burstRetentionMs;
|
|
35267
|
+
if (retainedUntilMs <= nowMs) {
|
|
35268
|
+
retainedBursts.delete(groupId);
|
|
35269
|
+
continue;
|
|
35270
|
+
}
|
|
35271
|
+
retainedRows.push({
|
|
35272
|
+
...retained.row,
|
|
35273
|
+
cpuPercent: 0,
|
|
35274
|
+
memoryBytes: 0,
|
|
35275
|
+
retainedUntil: new Date(retainedUntilMs).toISOString(),
|
|
35276
|
+
note: buildRetainedBurstNote(retained.row, options.spikeAnnotations)
|
|
35277
|
+
});
|
|
35278
|
+
}
|
|
35279
|
+
return selectTopRows([...activeRows, ...retainedRows], limit);
|
|
35280
|
+
}
|
|
35281
|
+
function getHostProcessesSnapshot() {
|
|
35282
|
+
return buildHostProcesses([]);
|
|
35283
|
+
}
|
|
35284
|
+
function isDockerProcess(process) {
|
|
35285
|
+
return /\b(docker|containerd|kubepods)\b/i.test(process.cgroup ?? "");
|
|
35286
|
+
}
|
|
35287
|
+
function classifyCommandFamily(command) {
|
|
35288
|
+
const normalized = command.toLowerCase();
|
|
35289
|
+
if (normalized.includes("vitest")) return "vitest workers";
|
|
35290
|
+
if (normalized.includes("chrome") || normalized.includes("chromium")) return "chrome renderers";
|
|
35291
|
+
if (/\bmvn\b|maven/.test(normalized)) return "mvn";
|
|
35292
|
+
if (/\bnode\b/.test(normalized)) return "node";
|
|
35293
|
+
if (/\bbun\b/.test(normalized)) return "bun";
|
|
35294
|
+
return command.trim().split(/\s+/)[0] || "unknown";
|
|
35295
|
+
}
|
|
35296
|
+
function resolveOwner(process, processByPid, agentRootByPid, family) {
|
|
35297
|
+
if (family === "chrome renderers") return { label: "your browser" };
|
|
35298
|
+
let current = process;
|
|
35299
|
+
const seenPids = /* @__PURE__ */ new Set();
|
|
35300
|
+
while (current && !seenPids.has(current.pid)) {
|
|
35301
|
+
seenPids.add(current.pid);
|
|
35302
|
+
const agentSession = agentRootByPid.get(current.pid);
|
|
35303
|
+
if (agentSession) return {
|
|
35304
|
+
label: `spawned by ${agentSession.agentId}`,
|
|
35305
|
+
agentId: agentSession.agentId,
|
|
35306
|
+
issueId: agentSession.issueId
|
|
35307
|
+
};
|
|
35308
|
+
current = processByPid.get(current.ppid);
|
|
35309
|
+
}
|
|
35310
|
+
return { label: "host" };
|
|
35311
|
+
}
|
|
35312
|
+
function buildRetainedBurstNote(row, annotations) {
|
|
35313
|
+
const matchingAnnotation = annotations?.find((annotation) => {
|
|
35314
|
+
if (annotation.targetId && annotation.targetId === row.owner.agentId) return true;
|
|
35315
|
+
return annotation.label.includes(row.family) || annotation.label.includes(row.owner.label);
|
|
35316
|
+
});
|
|
35317
|
+
if (!matchingAnnotation) return "caused spike";
|
|
35318
|
+
return `caused spike: ${matchingAnnotation.label}`;
|
|
35319
|
+
}
|
|
35320
|
+
function selectTopRows(rows, limit) {
|
|
35321
|
+
const topByCpu = [...rows].sort((a, b) => b.cpuPercent - a.cpuPercent || b.peakCpuPercent - a.peakCpuPercent).slice(0, limit);
|
|
35322
|
+
const topByMemory = [...rows].sort((a, b) => b.memoryBytes - a.memoryBytes || b.peakMemoryBytes - a.peakMemoryBytes).slice(0, limit);
|
|
35323
|
+
const merged = /* @__PURE__ */ new Map();
|
|
35324
|
+
for (const row of [...topByCpu, ...topByMemory]) merged.set(row.id, row);
|
|
35325
|
+
return [...merged.values()].sort((a, b) => b.cpuPercent - a.cpuPercent || b.peakCpuPercent - a.peakCpuPercent).slice(0, limit);
|
|
35326
|
+
}
|
|
35327
|
+
function roundOneDecimal$1(value) {
|
|
35328
|
+
return Math.round(value * 10) / 10;
|
|
35329
|
+
}
|
|
35330
|
+
//#endregion
|
|
35331
|
+
//#region routes/resources/host-vitals.ts
|
|
35332
|
+
const CPU_SPARK_MAX = 30;
|
|
35333
|
+
let cpuSpark = [];
|
|
35334
|
+
let cachedDockerCounts = {
|
|
35335
|
+
containers: 0,
|
|
35336
|
+
running: 0,
|
|
35337
|
+
stacks: 0,
|
|
35338
|
+
networks: 0,
|
|
35339
|
+
networkPool: {
|
|
35340
|
+
used: 0,
|
|
35341
|
+
total: 31
|
|
35342
|
+
}
|
|
35343
|
+
};
|
|
35344
|
+
function buildHostVitalsSnapshot(options = {}) {
|
|
35345
|
+
const cpuPercent = roundOneDecimal(options.cpuPercent ?? 0);
|
|
35346
|
+
cpuSpark.push(cpuPercent);
|
|
35347
|
+
cpuSpark = cpuSpark.slice(-CPU_SPARK_MAX);
|
|
35348
|
+
const dockerCounts = options.dockerStale ? cachedDockerCounts : computeDockerCounts(options.containers ?? [], options.networkCount ?? 0, options.networkPoolTotal ?? 31);
|
|
35349
|
+
if (!options.dockerStale) cachedDockerCounts = dockerCounts;
|
|
35350
|
+
const memory = options.mem ?? getDefaultMemoryVitals();
|
|
35351
|
+
const disk = options.disk ?? {
|
|
35352
|
+
usedBytes: 0,
|
|
35353
|
+
freeBytes: 0,
|
|
35354
|
+
reclaimableBytes: 0
|
|
35355
|
+
};
|
|
35356
|
+
const agentCounts = computeAgentCounts(options.agents ?? [], options.agentFleet ?? {}, options.nowMs ?? Date.now());
|
|
35357
|
+
return {
|
|
35358
|
+
stale: options.dockerStale === true,
|
|
35359
|
+
cpu: {
|
|
35360
|
+
percent: cpuPercent,
|
|
35361
|
+
load: options.load ?? normalizeLoad(loadavg$1()),
|
|
35362
|
+
spark: [...cpuSpark]
|
|
35363
|
+
},
|
|
35364
|
+
mem: memory,
|
|
35365
|
+
disk: {
|
|
35366
|
+
usedBytes: disk.usedBytes,
|
|
35367
|
+
freeBytes: disk.freeBytes,
|
|
35368
|
+
reclaimableBytes: disk.reclaimableBytes ?? 0
|
|
35369
|
+
},
|
|
35370
|
+
docker: {
|
|
35371
|
+
...dockerCounts,
|
|
35372
|
+
stale: options.dockerStale === true
|
|
35373
|
+
},
|
|
35374
|
+
agents: agentCounts
|
|
35375
|
+
};
|
|
35376
|
+
}
|
|
35377
|
+
function computeDockerCounts(containers, networkCount, networkPoolTotal) {
|
|
35378
|
+
const stacks = /* @__PURE__ */ new Set();
|
|
35379
|
+
for (const container of containers) {
|
|
35380
|
+
const project = container.labels?.["com.docker.compose.project"];
|
|
35381
|
+
if (project) stacks.add(project);
|
|
35382
|
+
}
|
|
35383
|
+
return {
|
|
35384
|
+
containers: containers.length,
|
|
35385
|
+
running: containers.filter((container) => container.status === "running").length,
|
|
35386
|
+
stacks: stacks.size,
|
|
35387
|
+
networks: networkCount,
|
|
35388
|
+
networkPool: {
|
|
35389
|
+
used: networkCount,
|
|
35390
|
+
total: networkPoolTotal
|
|
35391
|
+
}
|
|
35392
|
+
};
|
|
35393
|
+
}
|
|
35394
|
+
function computeAgentCounts(agents, fleet, nowMs) {
|
|
35395
|
+
return {
|
|
35396
|
+
sessions: agents.length,
|
|
35397
|
+
active: agents.filter((agent) => agent.hasLiveTmuxSession).length,
|
|
35398
|
+
idleOver15m: agents.filter((agent) => {
|
|
35399
|
+
const lastActivityMs = Date.parse(agent.lastActivity ?? "");
|
|
35400
|
+
return Number.isFinite(lastActivityMs) && nowMs - lastActivityMs > 900 * 1e3;
|
|
35401
|
+
}).length,
|
|
35402
|
+
burnUsdPerHour: fleet.burnUsdPerHour ?? 0,
|
|
35403
|
+
hypotheticalUsdPerHour: fleet.hypotheticalUsdPerHour ?? 0,
|
|
35404
|
+
totalUsd: fleet.totalUsd ?? 0
|
|
35405
|
+
};
|
|
35406
|
+
}
|
|
35407
|
+
function getDefaultMemoryVitals() {
|
|
35408
|
+
const total = totalmem();
|
|
35409
|
+
const available = freemem$1();
|
|
35410
|
+
return {
|
|
35411
|
+
usedBytes: Math.max(0, total - available),
|
|
35412
|
+
availableBytes: available,
|
|
35413
|
+
swapUsedBytes: 0,
|
|
35414
|
+
swapTotalBytes: 0
|
|
35415
|
+
};
|
|
35416
|
+
}
|
|
35417
|
+
function normalizeLoad(load) {
|
|
35418
|
+
return [
|
|
35419
|
+
roundOneDecimal(load[0] ?? 0),
|
|
35420
|
+
roundOneDecimal(load[1] ?? 0),
|
|
35421
|
+
roundOneDecimal(load[2] ?? 0)
|
|
35422
|
+
];
|
|
35423
|
+
}
|
|
35424
|
+
function roundOneDecimal(value) {
|
|
35425
|
+
return Math.round(value * 10) / 10;
|
|
35426
|
+
}
|
|
35427
|
+
//#endregion
|
|
35428
|
+
//#region routes/resources/limits.ts
|
|
35429
|
+
const OOM_WINDOW_MS = 1440 * 60 * 1e3;
|
|
35430
|
+
let cachedOomEvents = [];
|
|
35431
|
+
function enrichContainersWithLimits(containers, options = {}) {
|
|
35432
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
35433
|
+
const oomEvents = options.oomEvents ?? cachedOomEvents;
|
|
35434
|
+
return containers.map((container) => {
|
|
35435
|
+
const limitBytes = getLimitBytes(container.memoryLimit);
|
|
35436
|
+
const percent = limitBytes === null ? void 0 : Math.round((container.memoryUsage ?? 0) / limitBytes * 100);
|
|
35437
|
+
return {
|
|
35438
|
+
...container,
|
|
35439
|
+
memLimitBytes: limitBytes,
|
|
35440
|
+
...percent === void 0 ? {} : { memPercentOfLimit: percent },
|
|
35441
|
+
oomKills24h: countRecentOomEvents(container, oomEvents, nowMs),
|
|
35442
|
+
...composeFileHint(container.labels)
|
|
35443
|
+
};
|
|
35444
|
+
});
|
|
35445
|
+
}
|
|
35446
|
+
function getLimitBytes(memoryLimit) {
|
|
35447
|
+
if (!memoryLimit || memoryLimit <= 0 || !Number.isFinite(memoryLimit)) return null;
|
|
35448
|
+
return memoryLimit;
|
|
35449
|
+
}
|
|
35450
|
+
function countRecentOomEvents(container, events, nowMs) {
|
|
35451
|
+
const cutoffMs = nowMs - OOM_WINDOW_MS;
|
|
35452
|
+
return events.filter((event) => {
|
|
35453
|
+
const eventMs = Date.parse(event.timestamp);
|
|
35454
|
+
if (!Number.isFinite(eventMs) || eventMs < cutoffMs || eventMs > nowMs) return false;
|
|
35455
|
+
if (event.containerId && container.id.startsWith(event.containerId)) return true;
|
|
35456
|
+
if (event.containerName && event.containerName === container.name) return true;
|
|
35457
|
+
return false;
|
|
35458
|
+
}).length;
|
|
35459
|
+
}
|
|
35460
|
+
function composeFileHint(labels) {
|
|
35461
|
+
const composeFile = labels?.["com.docker.compose.project.config_files"];
|
|
35462
|
+
if (!composeFile) return {};
|
|
35463
|
+
return { composeFile: composeFile.split(",")[0]?.trim() };
|
|
35464
|
+
}
|
|
35465
|
+
//#endregion
|
|
35466
|
+
//#region routes/resources/reclaim.ts
|
|
35467
|
+
init_review_status();
|
|
35468
|
+
const RECLAIM_THRESHOLD_BYTES = 500 * 1024 ** 2;
|
|
35469
|
+
const VENV_THRESHOLD_BYTES = 1024 ** 3;
|
|
35470
|
+
let cachedVenvCandidates = [];
|
|
35471
|
+
let venvDelete = async (path) => {
|
|
35472
|
+
await rm(path, {
|
|
35473
|
+
recursive: true,
|
|
35474
|
+
force: true
|
|
35475
|
+
});
|
|
35476
|
+
};
|
|
35477
|
+
let issueClosedReader = (issueId) => isClosedReviewStatus(issueId);
|
|
35478
|
+
let projectRootReader = () => resolve$1(process.cwd(), "..", "..");
|
|
35479
|
+
function buildReclaimPayload(stacks, agents, options = {}) {
|
|
35480
|
+
const liveIssueIds = new Set(agents.filter((agent) => agent.hasLiveTmuxSession === true).map((agent) => agent.issueId?.toUpperCase()).filter((issueId) => Boolean(issueId)));
|
|
35481
|
+
const candidates = [...stacks.filter((stack) => stack.issueId && isClosedStack(stack) && !liveIssueIds.has(stack.issueId.toUpperCase())).map((stack) => ({
|
|
35482
|
+
kind: "stack",
|
|
35483
|
+
label: stack.issueId ?? stack.composeProject,
|
|
35484
|
+
why: "Issue is merged or closed and no live agent references it.",
|
|
35485
|
+
ramBytes: stack.aggregates.memoryBytes,
|
|
35486
|
+
diskBytes: stack.aggregates.diskBytes,
|
|
35487
|
+
action: `GET /api/resources/stacks/${stack.issueId}/teardown-estimate`,
|
|
35488
|
+
issueId: stack.issueId ?? void 0
|
|
35489
|
+
})), ...(options.venvs ?? cachedVenvCandidates).filter((venv) => venv.diskBytes >= VENV_THRESHOLD_BYTES && issueClosedReader(venv.issueId)).map((venv) => ({
|
|
35490
|
+
kind: "venv",
|
|
35491
|
+
label: `${venv.issueId} virtualenv`,
|
|
35492
|
+
why: "Closed issue has an orphaned workspace .venv over 1 GB.",
|
|
35493
|
+
ramBytes: 0,
|
|
35494
|
+
diskBytes: venv.diskBytes,
|
|
35495
|
+
action: `DELETE /api/resources/venvs/${venv.issueId}`,
|
|
35496
|
+
issueId: venv.issueId
|
|
35497
|
+
}))];
|
|
35498
|
+
return {
|
|
35499
|
+
reclaimCandidates: candidates,
|
|
35500
|
+
reclaimTotals: {
|
|
35501
|
+
ramBytes: candidates.reduce((sum, candidate) => sum + candidate.ramBytes, 0),
|
|
35502
|
+
diskBytes: candidates.reduce((sum, candidate) => sum + candidate.diskBytes, 0)
|
|
35503
|
+
},
|
|
35504
|
+
reclaimThresholdBytes: RECLAIM_THRESHOLD_BYTES
|
|
35505
|
+
};
|
|
35506
|
+
}
|
|
35507
|
+
const deleteResourceVenvRoute = HttpRouter.add("DELETE", "/api/resources/venvs/:issue", httpHandler(Effect.gen(function* () {
|
|
35508
|
+
return yield* deleteResourceVenvEffect((yield* HttpRouter.schemaParams()).issue);
|
|
35509
|
+
})));
|
|
35510
|
+
function deleteResourceVenvEffect(issue) {
|
|
35511
|
+
return Effect.gen(function* () {
|
|
35512
|
+
const issueId = issue.toUpperCase();
|
|
35513
|
+
if (!issueClosedReader(issueId)) return jsonResponse({
|
|
35514
|
+
ok: false,
|
|
35515
|
+
error: `${issueId} is not closed; refusing to delete workspace venv.`
|
|
35516
|
+
}, { status: 409 });
|
|
35517
|
+
const venvPath = join$1(projectRootReader(), "workspaces", `feature-${issueId.toLowerCase()}`, ".venv");
|
|
35518
|
+
yield* Effect.tryPromise({
|
|
35519
|
+
try: () => venvDelete(venvPath),
|
|
35520
|
+
catch: (error) => error
|
|
35521
|
+
}).pipe(Effect.catch(() => Effect.succeed(void 0)));
|
|
35522
|
+
return jsonResponse({
|
|
35523
|
+
ok: true,
|
|
35524
|
+
issueId,
|
|
35525
|
+
path: venvPath
|
|
35526
|
+
});
|
|
35527
|
+
});
|
|
35528
|
+
}
|
|
35529
|
+
function isClosedStack(stack) {
|
|
35530
|
+
return stack.phase === "merged";
|
|
35531
|
+
}
|
|
35532
|
+
function isClosedReviewStatus(issueId) {
|
|
35533
|
+
return getReviewStatusSync(issueId)?.mergeStatus === "merged";
|
|
35534
|
+
}
|
|
35535
|
+
//#endregion
|
|
35536
|
+
//#region routes/resources/spawn-gate.ts
|
|
35537
|
+
let readSystemHealthSnapshot = () => getSystemHealthSnapshot();
|
|
35538
|
+
function mapSpawnGateDecision(decision) {
|
|
35539
|
+
const reason = decision.error ?? decision.warnings[0]?.message ?? "";
|
|
35540
|
+
return {
|
|
35541
|
+
state: decision.blocked ? "BLOCKED" : decision.requiresAcknowledgement ? "SOFT" : "OPEN",
|
|
35542
|
+
reason,
|
|
35543
|
+
warnings: decision.warnings,
|
|
35544
|
+
pressure: spawnGatePressure(decision)
|
|
35545
|
+
};
|
|
35546
|
+
}
|
|
35547
|
+
function getSpawnGatePayloadEffect() {
|
|
35548
|
+
return Effect.tryPromise({
|
|
35549
|
+
try: () => readSystemHealthSnapshot(),
|
|
35550
|
+
catch: (error) => error
|
|
35551
|
+
}).pipe(Effect.map((health) => mapSpawnGateDecision(evaluateSpawnGuardrails(health))), Effect.catch(() => Effect.succeed({
|
|
35552
|
+
state: "SOFT",
|
|
35553
|
+
reason: "Spawn gate health is unavailable.",
|
|
35554
|
+
warnings: [],
|
|
35555
|
+
pressure: 0,
|
|
35556
|
+
stale: true
|
|
35557
|
+
})));
|
|
35558
|
+
}
|
|
35559
|
+
function spawnGatePressure(decision) {
|
|
35560
|
+
if (decision.blocked) return 100;
|
|
35561
|
+
if (decision.requiresAcknowledgement) return 67;
|
|
35562
|
+
return 12;
|
|
35563
|
+
}
|
|
35564
|
+
//#endregion
|
|
35565
|
+
//#region routes/resources/stacks.ts
|
|
35566
|
+
init_review_status();
|
|
35567
|
+
let reviewStatusReader = (issueId) => getReviewStatusSync(issueId);
|
|
35568
|
+
function getResourceStacks(containers) {
|
|
35569
|
+
return buildResourceStacks(containers, reviewStatusesFor(containers));
|
|
35570
|
+
}
|
|
35571
|
+
function buildResourceStacks(containers, reviewStatuses = {}) {
|
|
35572
|
+
const groups = /* @__PURE__ */ new Map();
|
|
35573
|
+
for (const container of containers) {
|
|
35574
|
+
const key = composeProjectFor(container) ?? "unassigned";
|
|
35575
|
+
groups.set(key, [...groups.get(key) ?? [], container]);
|
|
35576
|
+
}
|
|
35577
|
+
return [...groups.entries()].map(([composeProject, services]) => {
|
|
35578
|
+
const issueId = issueIdFor$1(composeProject, services);
|
|
35579
|
+
const reviewStatus = issueId ? reviewStatuses[issueId] : void 0;
|
|
35580
|
+
return {
|
|
35581
|
+
id: issueId ?? composeProject,
|
|
35582
|
+
issueId,
|
|
35583
|
+
issueTitle: issueId ?? "Unassigned",
|
|
35584
|
+
composeProject,
|
|
35585
|
+
serviceCount: services.length,
|
|
35586
|
+
services,
|
|
35587
|
+
aggregates: {
|
|
35588
|
+
cpuPercent: roundOne(services.reduce((sum, service) => sum + (service.cpuPercent ?? 0), 0)),
|
|
35589
|
+
memoryBytes: services.reduce((sum, service) => sum + (service.memoryUsage ?? 0), 0),
|
|
35590
|
+
diskBytes: services.reduce((sum, service) => sum + (service.diskUsage ?? 0), 0)
|
|
35591
|
+
},
|
|
35592
|
+
phase: phaseFor(reviewStatus)
|
|
35593
|
+
};
|
|
35594
|
+
}).sort((a, b) => a.id.localeCompare(b.id));
|
|
35595
|
+
}
|
|
35596
|
+
function reviewStatusesFor(containers) {
|
|
35597
|
+
const issueIds = /* @__PURE__ */ new Set();
|
|
35598
|
+
const groups = /* @__PURE__ */ new Map();
|
|
35599
|
+
for (const container of containers) {
|
|
35600
|
+
const key = composeProjectFor(container) ?? "unassigned";
|
|
35601
|
+
groups.set(key, [...groups.get(key) ?? [], container]);
|
|
35602
|
+
}
|
|
35603
|
+
for (const [composeProject, services] of groups.entries()) {
|
|
35604
|
+
const issueId = issueIdFor$1(composeProject, services);
|
|
35605
|
+
if (issueId) issueIds.add(issueId);
|
|
35606
|
+
}
|
|
35607
|
+
const statuses = {};
|
|
35608
|
+
for (const issueId of issueIds) statuses[issueId] = reviewStatusReader(issueId) ?? void 0;
|
|
35609
|
+
return statuses;
|
|
35610
|
+
}
|
|
35611
|
+
function composeProjectFor(container) {
|
|
35612
|
+
const labelProject = container.labels?.["com.docker.compose.project"]?.trim();
|
|
35613
|
+
if (labelProject) return labelProject;
|
|
35614
|
+
const issueProject = container.name.match(/^(.+?feature[-_](?:pan|min|aur|krux)[-_]?\d+)/i);
|
|
35615
|
+
if (issueProject?.[1]) return issueProject[1];
|
|
35616
|
+
return container.name.match(/^(.+?)[_-][a-z0-9]+[._-][0-9]+$/i)?.[1] ?? null;
|
|
35617
|
+
}
|
|
35618
|
+
function issueIdFor$1(composeProject, services) {
|
|
35619
|
+
if (composeProject === "unassigned") return null;
|
|
35620
|
+
const fromProject = issueIdFromText(composeProject);
|
|
35621
|
+
if (fromProject) return fromProject;
|
|
35622
|
+
for (const service of services) {
|
|
35623
|
+
const fromLabels = issueIdFromText(Object.values(service.labels ?? {}).join(" "));
|
|
35624
|
+
if (fromLabels) return fromLabels;
|
|
35625
|
+
}
|
|
35626
|
+
return null;
|
|
35627
|
+
}
|
|
35628
|
+
function issueIdFromText(value) {
|
|
35629
|
+
const match = value.match(/(?:workspaces[\\/])?feature[-_](pan|min|aur|krux)[-_]?(\d+)/i) ?? value.match(/\b(pan|min|aur|krux)[-_]?(\d+)\b/i);
|
|
35630
|
+
if (!match) return null;
|
|
35631
|
+
return `${match[1].toUpperCase()}-${match[2]}`;
|
|
35632
|
+
}
|
|
35633
|
+
function phaseFor(status) {
|
|
35634
|
+
if (!status) return "todo";
|
|
35635
|
+
if (status.mergeStatus === "merged") return "merged";
|
|
35636
|
+
if (status.mergeStatus === "queued" || status.mergeStatus === "merging" || status.mergeStatus === "verifying" || status.mergeStatus === "failed" || status.readyForMerge === true) return "ship";
|
|
35637
|
+
if (status.verificationStatus === "running") return "verifying";
|
|
35638
|
+
if (status.reviewStatus === "reviewing" || status.reviewStatus === "passed" || status.reviewStatus === "failed" || status.reviewStatus === "blocked" || status.testStatus === "testing" || status.testStatus === "passed" || status.testStatus === "failed" || status.inspectStatus === "inspecting" || status.inspectStatus === "passed" || status.inspectStatus === "failed" || status.inspectStatus === "error") return "review";
|
|
35639
|
+
return "todo";
|
|
35640
|
+
}
|
|
35641
|
+
function roundOne(value) {
|
|
35642
|
+
return Math.round(value * 10) / 10;
|
|
35643
|
+
}
|
|
35644
|
+
//#endregion
|
|
35645
|
+
//#region routes/resources/snapshot.ts
|
|
35646
|
+
init_agents$1();
|
|
35647
|
+
init_tmux();
|
|
34705
35648
|
/** Build the GET /api/resources response from the SQLite agents table. */
|
|
34706
35649
|
function getResourcesEffect() {
|
|
34707
35650
|
return Effect.gen(function* () {
|
|
34708
|
-
const containers =
|
|
35651
|
+
const containers = enrichContainersWithLimits(getCurrentDockerStats());
|
|
35652
|
+
const agentStats = yield* getAgentStatsSnapshotEffect();
|
|
35653
|
+
const spawnGate = yield* getSpawnGatePayloadEffect();
|
|
34709
35654
|
const stoppedContainers = [];
|
|
34710
35655
|
const sessions = yield* listSessions().pipe(Effect.catch(() => Effect.succeed([])));
|
|
34711
35656
|
const tmuxSessionNames = new Set(sessions.map((s) => s.name));
|
|
35657
|
+
const agents = listAgentStates().filter((state) => state.status !== "stopped").map((state) => ({
|
|
35658
|
+
...state,
|
|
35659
|
+
hasLiveTmuxSession: tmuxSessionNames.has(state.id)
|
|
35660
|
+
}));
|
|
35661
|
+
const agentStatsById = new Map(agentStats.agents.map((agent) => [agent.id, agent]));
|
|
35662
|
+
const baseHostVitals = buildHostVitalsSnapshot({
|
|
35663
|
+
containers,
|
|
35664
|
+
agents: agents.map((agent) => ({
|
|
35665
|
+
id: String(agent.id),
|
|
35666
|
+
lastActivity: typeof agent.lastActivity === "string" ? agent.lastActivity : void 0,
|
|
35667
|
+
hasLiveTmuxSession: agent.hasLiveTmuxSession === true
|
|
35668
|
+
})),
|
|
35669
|
+
agentFleet: agentStats.hostVitals.agents
|
|
35670
|
+
});
|
|
35671
|
+
const stacks = getResourceStacks(containers);
|
|
35672
|
+
const reclaim = buildReclaimPayload(stacks, agents);
|
|
35673
|
+
const hostVitals = {
|
|
35674
|
+
...baseHostVitals,
|
|
35675
|
+
disk: {
|
|
35676
|
+
...baseHostVitals.disk,
|
|
35677
|
+
reclaimableBytes: reclaim.reclaimTotals.diskBytes
|
|
35678
|
+
}
|
|
35679
|
+
};
|
|
34712
35680
|
return jsonResponse({
|
|
35681
|
+
agents: agents.map((agent) => ({
|
|
35682
|
+
...agent,
|
|
35683
|
+
resourceStats: agentStatsById.get(String(agent.id)) ?? null
|
|
35684
|
+
})),
|
|
35685
|
+
coreServices: getCoreServicesSnapshot(),
|
|
34713
35686
|
containers,
|
|
35687
|
+
forecast: buildCapacityForecast(stacks, { hostVitals }),
|
|
35688
|
+
hostVitals,
|
|
35689
|
+
hostProcesses: getHostProcessesSnapshot(),
|
|
34714
35690
|
stoppedContainers,
|
|
34715
35691
|
networks: [],
|
|
35692
|
+
reclaimCandidates: reclaim.reclaimCandidates,
|
|
35693
|
+
reclaimThresholdBytes: reclaim.reclaimThresholdBytes,
|
|
35694
|
+
reclaimTotals: reclaim.reclaimTotals,
|
|
35695
|
+
spawnGate,
|
|
35696
|
+
stacks,
|
|
34716
35697
|
volumes: [],
|
|
34717
|
-
agents: listAgentStates().filter((state) => state.status !== "stopped").map((state) => ({
|
|
34718
|
-
...state,
|
|
34719
|
-
hasLiveTmuxSession: tmuxSessionNames.has(state.id)
|
|
34720
|
-
})),
|
|
34721
35698
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
34722
35699
|
});
|
|
34723
35700
|
});
|
|
34724
35701
|
}
|
|
34725
35702
|
const getResourcesRoute = HttpRouter.add("GET", "/api/resources", httpHandler(getResourcesEffect()));
|
|
35703
|
+
//#endregion
|
|
35704
|
+
//#region routes/resources/containers.ts
|
|
35705
|
+
init_domain_services();
|
|
35706
|
+
let dockerExec = promisify(execFile);
|
|
35707
|
+
function dockerActionErrorPayload(error) {
|
|
35708
|
+
const maybeError = error;
|
|
35709
|
+
return { error: (typeof maybeError.stderr === "string" ? maybeError.stderr.trim() : "") || (typeof maybeError.message === "string" ? maybeError.message : String(error)) };
|
|
35710
|
+
}
|
|
34726
35711
|
const getContainerHistoryRoute = HttpRouter.add("GET", "/api/resources/:containerId/history", Effect.gen(function* () {
|
|
34727
35712
|
const containerId = (yield* HttpRouter.params)["containerId"] ?? "";
|
|
34728
35713
|
if (!/^[a-f0-9]{12,64}$/.test(containerId)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34729
|
-
return jsonResponse(
|
|
34730
|
-
timestamps: [],
|
|
34731
|
-
cpuPercent: [],
|
|
34732
|
-
memoryPercent: []
|
|
34733
|
-
});
|
|
35714
|
+
return jsonResponse(getContainerHistory(containerId));
|
|
34734
35715
|
}));
|
|
34735
35716
|
const getContainerDetailsRoute = HttpRouter.add("GET", "/api/resources/:containerId/details", Effect.gen(function* () {
|
|
34736
35717
|
const containerId = (yield* HttpRouter.params)["containerId"] ?? "";
|
|
34737
35718
|
if (!/^[a-f0-9]{12,64}$/.test(containerId)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34738
35719
|
return yield* httpHandler(Effect.gen(function* () {
|
|
34739
35720
|
const [inspectResult, logsResult] = yield* Effect.tryPromise({
|
|
34740
|
-
try: () => Promise.all([
|
|
35721
|
+
try: () => Promise.all([dockerExec("docker", [
|
|
35722
|
+
"inspect",
|
|
35723
|
+
"--format",
|
|
35724
|
+
"{{json .}}",
|
|
35725
|
+
containerId
|
|
35726
|
+
], {
|
|
34741
35727
|
encoding: "utf-8",
|
|
34742
35728
|
timeout: 5e3
|
|
34743
|
-
}).catch(() => ({ stdout: "null" })),
|
|
35729
|
+
}).catch(() => ({ stdout: "null" })), dockerExec("docker", [
|
|
35730
|
+
"logs",
|
|
35731
|
+
"--tail",
|
|
35732
|
+
"100",
|
|
35733
|
+
containerId
|
|
35734
|
+
], {
|
|
34744
35735
|
encoding: "utf-8",
|
|
34745
35736
|
timeout: 5e3
|
|
34746
35737
|
}).catch(() => ({ stdout: "" }))]),
|
|
@@ -34779,7 +35770,7 @@ const deleteDockerContainerRoute = HttpRouter.add("DELETE", "/api/resources/dock
|
|
|
34779
35770
|
const eventStore = yield* EventStoreService;
|
|
34780
35771
|
if (!/^[a-f0-9]{12,64}$/.test(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34781
35772
|
yield* Effect.tryPromise({
|
|
34782
|
-
try: () =>
|
|
35773
|
+
try: () => dockerExec("docker", ["rm", id], {
|
|
34783
35774
|
encoding: "utf-8",
|
|
34784
35775
|
timeout: 1e4
|
|
34785
35776
|
}),
|
|
@@ -34788,90 +35779,16 @@ const deleteDockerContainerRoute = HttpRouter.add("DELETE", "/api/resources/dock
|
|
|
34788
35779
|
yield* eventStore.append({
|
|
34789
35780
|
type: "resources.updated",
|
|
34790
35781
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34791
|
-
payload: { resources: { containers:
|
|
35782
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
34792
35783
|
});
|
|
34793
35784
|
return jsonResponse({ ok: true });
|
|
34794
35785
|
})));
|
|
34795
|
-
const postPruneContainersRoute = HttpRouter.add("POST", "/api/resources/docker/prune-containers", httpHandler(Effect.gen(function* () {
|
|
34796
|
-
const eventStore = yield* EventStoreService;
|
|
34797
|
-
const { stdout } = yield* Effect.tryPromise({
|
|
34798
|
-
try: () => execAsync$7("docker container prune -f 2>&1", {
|
|
34799
|
-
encoding: "utf-8",
|
|
34800
|
-
timeout: 3e4
|
|
34801
|
-
}),
|
|
34802
|
-
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34803
|
-
});
|
|
34804
|
-
yield* eventStore.append({
|
|
34805
|
-
type: "resources.updated",
|
|
34806
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34807
|
-
payload: { resources: { containers: [] } }
|
|
34808
|
-
});
|
|
34809
|
-
return jsonResponse({
|
|
34810
|
-
ok: true,
|
|
34811
|
-
output: stdout.trim()
|
|
34812
|
-
});
|
|
34813
|
-
})));
|
|
34814
|
-
const deleteDockerNetworkRoute = HttpRouter.add("DELETE", "/api/resources/docker/network/:name", httpHandler(Effect.gen(function* () {
|
|
34815
|
-
const name = (yield* HttpRouter.params)["name"] ?? "";
|
|
34816
|
-
const eventStore = yield* EventStoreService;
|
|
34817
|
-
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid network name" }, { status: 400 });
|
|
34818
|
-
yield* Effect.tryPromise({
|
|
34819
|
-
try: () => execAsync$7(`docker network rm "${name}" 2>&1`, {
|
|
34820
|
-
encoding: "utf-8",
|
|
34821
|
-
timeout: 1e4
|
|
34822
|
-
}),
|
|
34823
|
-
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34824
|
-
});
|
|
34825
|
-
yield* eventStore.append({
|
|
34826
|
-
type: "resources.updated",
|
|
34827
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34828
|
-
payload: { resources: { containers: [] } }
|
|
34829
|
-
});
|
|
34830
|
-
return jsonResponse({ ok: true });
|
|
34831
|
-
})));
|
|
34832
|
-
const deleteDockerVolumeRoute = HttpRouter.add("DELETE", "/api/resources/docker/volume/:name", httpHandler(Effect.gen(function* () {
|
|
34833
|
-
const name = (yield* HttpRouter.params)["name"] ?? "";
|
|
34834
|
-
const eventStore = yield* EventStoreService;
|
|
34835
|
-
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid volume name" }, { status: 400 });
|
|
34836
|
-
yield* Effect.tryPromise({
|
|
34837
|
-
try: () => execAsync$7(`docker volume rm "${name}" 2>&1`, {
|
|
34838
|
-
encoding: "utf-8",
|
|
34839
|
-
timeout: 1e4
|
|
34840
|
-
}),
|
|
34841
|
-
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34842
|
-
});
|
|
34843
|
-
yield* eventStore.append({
|
|
34844
|
-
type: "resources.updated",
|
|
34845
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34846
|
-
payload: { resources: { containers: [] } }
|
|
34847
|
-
});
|
|
34848
|
-
return jsonResponse({ ok: true });
|
|
34849
|
-
})));
|
|
34850
|
-
const postPruneVolumesRoute = HttpRouter.add("POST", "/api/resources/docker/prune-volumes", httpHandler(Effect.gen(function* () {
|
|
34851
|
-
const eventStore = yield* EventStoreService;
|
|
34852
|
-
const { stdout } = yield* Effect.tryPromise({
|
|
34853
|
-
try: () => execAsync$7("docker volume prune -f 2>&1", {
|
|
34854
|
-
encoding: "utf-8",
|
|
34855
|
-
timeout: 3e4
|
|
34856
|
-
}),
|
|
34857
|
-
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34858
|
-
});
|
|
34859
|
-
yield* eventStore.append({
|
|
34860
|
-
type: "resources.updated",
|
|
34861
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34862
|
-
payload: { resources: { containers: [] } }
|
|
34863
|
-
});
|
|
34864
|
-
return jsonResponse({
|
|
34865
|
-
ok: true,
|
|
34866
|
-
output: stdout.trim()
|
|
34867
|
-
});
|
|
34868
|
-
})));
|
|
34869
35786
|
const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/restart", httpHandler(Effect.gen(function* () {
|
|
34870
35787
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
34871
35788
|
const eventStore = yield* EventStoreService;
|
|
34872
|
-
if (!id) return jsonResponse({ error: "
|
|
35789
|
+
if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34873
35790
|
const { stdout } = yield* Effect.tryPromise({
|
|
34874
|
-
try: () =>
|
|
35791
|
+
try: () => dockerExec("docker", ["restart", id], {
|
|
34875
35792
|
encoding: "utf-8",
|
|
34876
35793
|
timeout: 3e4
|
|
34877
35794
|
}),
|
|
@@ -34880,7 +35797,7 @@ const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/
|
|
|
34880
35797
|
yield* eventStore.append({
|
|
34881
35798
|
type: "resources.updated",
|
|
34882
35799
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34883
|
-
payload: { resources: { containers:
|
|
35800
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
34884
35801
|
});
|
|
34885
35802
|
return jsonResponse({
|
|
34886
35803
|
ok: true,
|
|
@@ -34891,9 +35808,9 @@ const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/
|
|
|
34891
35808
|
const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/start", httpHandler(Effect.gen(function* () {
|
|
34892
35809
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
34893
35810
|
const eventStore = yield* EventStoreService;
|
|
34894
|
-
if (!id) return jsonResponse({ error: "
|
|
35811
|
+
if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34895
35812
|
const { stdout } = yield* Effect.tryPromise({
|
|
34896
|
-
try: () =>
|
|
35813
|
+
try: () => dockerExec("docker", ["start", id], {
|
|
34897
35814
|
encoding: "utf-8",
|
|
34898
35815
|
timeout: 3e4
|
|
34899
35816
|
}),
|
|
@@ -34902,7 +35819,7 @@ const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/co
|
|
|
34902
35819
|
yield* eventStore.append({
|
|
34903
35820
|
type: "resources.updated",
|
|
34904
35821
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34905
|
-
payload: { resources: { containers:
|
|
35822
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
34906
35823
|
});
|
|
34907
35824
|
return jsonResponse({
|
|
34908
35825
|
ok: true,
|
|
@@ -34912,9 +35829,15 @@ const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/co
|
|
|
34912
35829
|
})));
|
|
34913
35830
|
const getContainerLogsRoute = HttpRouter.add("GET", "/api/resources/docker/container/:id/logs", httpHandler(Effect.gen(function* () {
|
|
34914
35831
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
34915
|
-
if (!id) return jsonResponse({ error: "
|
|
35832
|
+
if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34916
35833
|
const { stdout } = yield* Effect.tryPromise({
|
|
34917
|
-
try: () =>
|
|
35834
|
+
try: () => dockerExec("docker", [
|
|
35835
|
+
"logs",
|
|
35836
|
+
"--tail",
|
|
35837
|
+
"200",
|
|
35838
|
+
"--timestamps",
|
|
35839
|
+
id
|
|
35840
|
+
], {
|
|
34918
35841
|
encoding: "utf-8",
|
|
34919
35842
|
timeout: 1e4
|
|
34920
35843
|
}),
|
|
@@ -34922,7 +35845,516 @@ const getContainerLogsRoute = HttpRouter.add("GET", "/api/resources/docker/conta
|
|
|
34922
35845
|
});
|
|
34923
35846
|
return jsonResponse({ logs: stdout });
|
|
34924
35847
|
})));
|
|
34925
|
-
|
|
35848
|
+
function dockerContainerActionEffect(id, action) {
|
|
35849
|
+
return Effect.gen(function* () {
|
|
35850
|
+
if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
35851
|
+
const eventStore = yield* EventStoreService;
|
|
35852
|
+
const args = action === "stop" ? [
|
|
35853
|
+
"stop",
|
|
35854
|
+
"--time",
|
|
35855
|
+
"30",
|
|
35856
|
+
id
|
|
35857
|
+
] : [action, id];
|
|
35858
|
+
const timeout = action === "stop" ? 35e3 : 1e4;
|
|
35859
|
+
const result = yield* Effect.tryPromise({
|
|
35860
|
+
try: () => dockerExec("docker", args, {
|
|
35861
|
+
encoding: "utf-8",
|
|
35862
|
+
timeout
|
|
35863
|
+
}),
|
|
35864
|
+
catch: (error) => error
|
|
35865
|
+
}).pipe(Effect.matchEffect({
|
|
35866
|
+
onFailure: (error) => Effect.succeed({
|
|
35867
|
+
ok: false,
|
|
35868
|
+
error
|
|
35869
|
+
}),
|
|
35870
|
+
onSuccess: ({ stdout }) => Effect.succeed({
|
|
35871
|
+
ok: true,
|
|
35872
|
+
stdout
|
|
35873
|
+
})
|
|
35874
|
+
}));
|
|
35875
|
+
if (!result.ok) return jsonResponse(dockerActionErrorPayload(result.error), { status: 500 });
|
|
35876
|
+
const containers = getCurrentDockerStats();
|
|
35877
|
+
yield* eventStore.append({
|
|
35878
|
+
type: "resources.updated",
|
|
35879
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35880
|
+
payload: { resources: { containers } }
|
|
35881
|
+
});
|
|
35882
|
+
return jsonResponse({
|
|
35883
|
+
ok: true,
|
|
35884
|
+
container: id,
|
|
35885
|
+
action,
|
|
35886
|
+
output: result.stdout.trim(),
|
|
35887
|
+
containers
|
|
35888
|
+
});
|
|
35889
|
+
});
|
|
35890
|
+
}
|
|
35891
|
+
function isDockerIdentifier$2(value) {
|
|
35892
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}$/.test(value);
|
|
35893
|
+
}
|
|
35894
|
+
const postStopContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/stop", httpHandler(Effect.gen(function* () {
|
|
35895
|
+
return yield* dockerContainerActionEffect((yield* HttpRouter.params)["id"] ?? "", "stop");
|
|
35896
|
+
})));
|
|
35897
|
+
const postPauseContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/pause", httpHandler(Effect.gen(function* () {
|
|
35898
|
+
return yield* dockerContainerActionEffect((yield* HttpRouter.params)["id"] ?? "", "pause");
|
|
35899
|
+
})));
|
|
35900
|
+
const postUnpauseContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/unpause", httpHandler(Effect.gen(function* () {
|
|
35901
|
+
return yield* dockerContainerActionEffect((yield* HttpRouter.params)["id"] ?? "", "unpause");
|
|
35902
|
+
})));
|
|
35903
|
+
//#endregion
|
|
35904
|
+
//#region routes/resources/prune.ts
|
|
35905
|
+
init_domain_services();
|
|
35906
|
+
const execFileAsync$7 = promisify(execFile);
|
|
35907
|
+
const postPruneContainersRoute = HttpRouter.add("POST", "/api/resources/docker/prune-containers", httpHandler(Effect.gen(function* () {
|
|
35908
|
+
const eventStore = yield* EventStoreService;
|
|
35909
|
+
const { stdout } = yield* Effect.tryPromise({
|
|
35910
|
+
try: () => execFileAsync$7("docker", [
|
|
35911
|
+
"container",
|
|
35912
|
+
"prune",
|
|
35913
|
+
"-f"
|
|
35914
|
+
], {
|
|
35915
|
+
encoding: "utf-8",
|
|
35916
|
+
timeout: 3e4
|
|
35917
|
+
}),
|
|
35918
|
+
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
35919
|
+
});
|
|
35920
|
+
yield* eventStore.append({
|
|
35921
|
+
type: "resources.updated",
|
|
35922
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35923
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
35924
|
+
});
|
|
35925
|
+
return jsonResponse({
|
|
35926
|
+
ok: true,
|
|
35927
|
+
output: stdout.trim()
|
|
35928
|
+
});
|
|
35929
|
+
})));
|
|
35930
|
+
const deleteDockerNetworkRoute = HttpRouter.add("DELETE", "/api/resources/docker/network/:name", httpHandler(Effect.gen(function* () {
|
|
35931
|
+
const name = (yield* HttpRouter.params)["name"] ?? "";
|
|
35932
|
+
const eventStore = yield* EventStoreService;
|
|
35933
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid network name" }, { status: 400 });
|
|
35934
|
+
yield* Effect.tryPromise({
|
|
35935
|
+
try: () => execFileAsync$7("docker", [
|
|
35936
|
+
"network",
|
|
35937
|
+
"rm",
|
|
35938
|
+
name
|
|
35939
|
+
], {
|
|
35940
|
+
encoding: "utf-8",
|
|
35941
|
+
timeout: 1e4
|
|
35942
|
+
}),
|
|
35943
|
+
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
35944
|
+
});
|
|
35945
|
+
yield* eventStore.append({
|
|
35946
|
+
type: "resources.updated",
|
|
35947
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35948
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
35949
|
+
});
|
|
35950
|
+
return jsonResponse({ ok: true });
|
|
35951
|
+
})));
|
|
35952
|
+
const deleteDockerVolumeRoute = HttpRouter.add("DELETE", "/api/resources/docker/volume/:name", httpHandler(Effect.gen(function* () {
|
|
35953
|
+
const name = (yield* HttpRouter.params)["name"] ?? "";
|
|
35954
|
+
const eventStore = yield* EventStoreService;
|
|
35955
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid volume name" }, { status: 400 });
|
|
35956
|
+
yield* Effect.tryPromise({
|
|
35957
|
+
try: () => execFileAsync$7("docker", [
|
|
35958
|
+
"volume",
|
|
35959
|
+
"rm",
|
|
35960
|
+
name
|
|
35961
|
+
], {
|
|
35962
|
+
encoding: "utf-8",
|
|
35963
|
+
timeout: 1e4
|
|
35964
|
+
}),
|
|
35965
|
+
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
35966
|
+
});
|
|
35967
|
+
yield* eventStore.append({
|
|
35968
|
+
type: "resources.updated",
|
|
35969
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35970
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
35971
|
+
});
|
|
35972
|
+
return jsonResponse({ ok: true });
|
|
35973
|
+
})));
|
|
35974
|
+
const postPruneVolumesRoute = HttpRouter.add("POST", "/api/resources/docker/prune-volumes", httpHandler(Effect.gen(function* () {
|
|
35975
|
+
const eventStore = yield* EventStoreService;
|
|
35976
|
+
const { stdout } = yield* Effect.tryPromise({
|
|
35977
|
+
try: () => execFileAsync$7("docker", [
|
|
35978
|
+
"volume",
|
|
35979
|
+
"prune",
|
|
35980
|
+
"-f"
|
|
35981
|
+
], {
|
|
35982
|
+
encoding: "utf-8",
|
|
35983
|
+
timeout: 3e4
|
|
35984
|
+
}),
|
|
35985
|
+
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
35986
|
+
});
|
|
35987
|
+
yield* eventStore.append({
|
|
35988
|
+
type: "resources.updated",
|
|
35989
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35990
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
35991
|
+
});
|
|
35992
|
+
return jsonResponse({
|
|
35993
|
+
ok: true,
|
|
35994
|
+
output: stdout.trim()
|
|
35995
|
+
});
|
|
35996
|
+
})));
|
|
35997
|
+
//#endregion
|
|
35998
|
+
//#region routes/resources/history.ts
|
|
35999
|
+
init_domain_services();
|
|
36000
|
+
const WINDOW_MS = 1440 * 60 * 1e3;
|
|
36001
|
+
const BUCKET_MS = 300 * 1e3;
|
|
36002
|
+
const MAX_POINTS = 288;
|
|
36003
|
+
const MAX_ACTIVITY_EVENTS = 1e3;
|
|
36004
|
+
const samples = [];
|
|
36005
|
+
function nowMs() {
|
|
36006
|
+
return Date.now();
|
|
36007
|
+
}
|
|
36008
|
+
function pruneSamples(now = nowMs()) {
|
|
36009
|
+
const cutoff = now - WINDOW_MS;
|
|
36010
|
+
while (samples.length > 0 && samples[0].ts < cutoff) samples.shift();
|
|
36011
|
+
}
|
|
36012
|
+
function average(values) {
|
|
36013
|
+
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
36014
|
+
}
|
|
36015
|
+
function downsample(metric) {
|
|
36016
|
+
pruneSamples();
|
|
36017
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
36018
|
+
for (const sample of samples) {
|
|
36019
|
+
const bucket = Math.floor(sample.ts / BUCKET_MS) * BUCKET_MS;
|
|
36020
|
+
const values = buckets.get(bucket) ?? [];
|
|
36021
|
+
values.push(sample[metric]);
|
|
36022
|
+
buckets.set(bucket, values);
|
|
36023
|
+
}
|
|
36024
|
+
return [...buckets.entries()].sort(([a], [b]) => a - b).slice(-MAX_POINTS).map(([bucket, values]) => ({
|
|
36025
|
+
ts: new Date(bucket).toISOString(),
|
|
36026
|
+
value: Number(average(values).toFixed(2))
|
|
36027
|
+
}));
|
|
36028
|
+
}
|
|
36029
|
+
function asRecord(value) {
|
|
36030
|
+
if (!value) return null;
|
|
36031
|
+
if (typeof value === "object" && !Array.isArray(value)) return value;
|
|
36032
|
+
if (typeof value !== "string") return null;
|
|
36033
|
+
try {
|
|
36034
|
+
const parsed = JSON.parse(value);
|
|
36035
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null;
|
|
36036
|
+
} catch {
|
|
36037
|
+
return null;
|
|
36038
|
+
}
|
|
36039
|
+
}
|
|
36040
|
+
function resourceAnnotationFromEvent(event, now = nowMs()) {
|
|
36041
|
+
const eventTs = new Date(event.timestamp).getTime();
|
|
36042
|
+
if (!Number.isFinite(eventTs) || eventTs <= now - WINDOW_MS || eventTs > now) return null;
|
|
36043
|
+
const payload = asRecord(event.payload);
|
|
36044
|
+
const details = asRecord(payload?.["details"]);
|
|
36045
|
+
if (details?.["category"] !== "resources") return null;
|
|
36046
|
+
const targetKind = details["targetKind"];
|
|
36047
|
+
const targetId = details["targetId"];
|
|
36048
|
+
if (typeof targetKind !== "string" || typeof targetId !== "string") return null;
|
|
36049
|
+
const message = payload?.["message"];
|
|
36050
|
+
return {
|
|
36051
|
+
ts: event.timestamp,
|
|
36052
|
+
label: typeof message === "string" ? message : event.type,
|
|
36053
|
+
targetKind,
|
|
36054
|
+
targetId
|
|
36055
|
+
};
|
|
36056
|
+
}
|
|
36057
|
+
function buildResourceHistoryResponse(activityEvents, now = nowMs()) {
|
|
36058
|
+
pruneSamples(now);
|
|
36059
|
+
const startedAt = samples[0]?.ts ?? now;
|
|
36060
|
+
return {
|
|
36061
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
36062
|
+
cpu: downsample("cpuPercent"),
|
|
36063
|
+
mem: downsample("memoryPercent"),
|
|
36064
|
+
annotations: activityEvents.map((event) => resourceAnnotationFromEvent(event, now)).filter((annotation) => Boolean(annotation))
|
|
36065
|
+
};
|
|
36066
|
+
}
|
|
36067
|
+
function getResourceHistoryEffect() {
|
|
36068
|
+
return Effect.gen(function* () {
|
|
36069
|
+
return jsonResponse(buildResourceHistoryResponse(yield* (yield* EventStoreService).queryByType("activity.entry", MAX_ACTIVITY_EVENTS)));
|
|
36070
|
+
});
|
|
36071
|
+
}
|
|
36072
|
+
const getResourceHistoryRoute = HttpRouter.add("GET", "/api/resources/history/24h", httpHandler(getResourceHistoryEffect()));
|
|
36073
|
+
//#endregion
|
|
36074
|
+
//#region routes/resources/stack-verbs.ts
|
|
36075
|
+
init_domain_services();
|
|
36076
|
+
let dockerStackVerbExec = promisify(execFile);
|
|
36077
|
+
const postStartStackRoute = HttpRouter.add("POST", "/api/resources/stacks/:issueId/start", httpHandler(Effect.gen(function* () {
|
|
36078
|
+
return yield* dockerStackVerbEffect((yield* HttpRouter.params)["issueId"] ?? "", "start");
|
|
36079
|
+
})));
|
|
36080
|
+
const postStopStackRoute = HttpRouter.add("POST", "/api/resources/stacks/:issueId/stop", httpHandler(Effect.gen(function* () {
|
|
36081
|
+
return yield* dockerStackVerbEffect((yield* HttpRouter.params)["issueId"] ?? "", "stop");
|
|
36082
|
+
})));
|
|
36083
|
+
const postPauseStackRoute = HttpRouter.add("POST", "/api/resources/stacks/:issueId/pause", httpHandler(Effect.gen(function* () {
|
|
36084
|
+
return yield* dockerStackVerbEffect((yield* HttpRouter.params)["issueId"] ?? "", "pause");
|
|
36085
|
+
})));
|
|
36086
|
+
function dockerStackVerbEffect(issue, verb) {
|
|
36087
|
+
return Effect.gen(function* () {
|
|
36088
|
+
const issueId = issue.trim().toUpperCase();
|
|
36089
|
+
const stack = findStack$1(issueId);
|
|
36090
|
+
if (!stack) return jsonResponse({
|
|
36091
|
+
ok: false,
|
|
36092
|
+
error: `Stack not found for ${issueId || issue}`
|
|
36093
|
+
}, { status: 404 });
|
|
36094
|
+
const results = [];
|
|
36095
|
+
for (const service of stack.services) {
|
|
36096
|
+
if (!isDockerIdentifier$1(service.id)) {
|
|
36097
|
+
results.push({
|
|
36098
|
+
containerId: service.id,
|
|
36099
|
+
ok: false,
|
|
36100
|
+
error: "Invalid container ID"
|
|
36101
|
+
});
|
|
36102
|
+
continue;
|
|
36103
|
+
}
|
|
36104
|
+
const args = argsFor(service.id, service.status, verb);
|
|
36105
|
+
const timeout = verb === "stop" ? 35e3 : 3e4;
|
|
36106
|
+
const result = yield* Effect.tryPromise({
|
|
36107
|
+
try: () => dockerStackVerbExec("docker", args, {
|
|
36108
|
+
encoding: "utf-8",
|
|
36109
|
+
timeout
|
|
36110
|
+
}),
|
|
36111
|
+
catch: (error) => error
|
|
36112
|
+
}).pipe(Effect.matchEffect({
|
|
36113
|
+
onFailure: (error) => Effect.succeed({
|
|
36114
|
+
containerId: service.id,
|
|
36115
|
+
ok: false,
|
|
36116
|
+
error: dockerActionErrorPayload(error).error
|
|
36117
|
+
}),
|
|
36118
|
+
onSuccess: ({ stdout }) => Effect.succeed({
|
|
36119
|
+
containerId: service.id,
|
|
36120
|
+
ok: true,
|
|
36121
|
+
output: stdout.trim()
|
|
36122
|
+
})
|
|
36123
|
+
}));
|
|
36124
|
+
results.push(result);
|
|
36125
|
+
}
|
|
36126
|
+
const containers = getCurrentDockerStats();
|
|
36127
|
+
yield* (yield* EventStoreService).append({
|
|
36128
|
+
type: "resources.updated",
|
|
36129
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36130
|
+
payload: { resources: { containers } }
|
|
36131
|
+
});
|
|
36132
|
+
return jsonResponse({
|
|
36133
|
+
ok: results.every((result) => result.ok),
|
|
36134
|
+
issueId,
|
|
36135
|
+
action: verb,
|
|
36136
|
+
results,
|
|
36137
|
+
containers
|
|
36138
|
+
});
|
|
36139
|
+
});
|
|
36140
|
+
}
|
|
36141
|
+
function argsFor(containerId, status, verb) {
|
|
36142
|
+
if (verb === "stop") return [
|
|
36143
|
+
"stop",
|
|
36144
|
+
"--time",
|
|
36145
|
+
"30",
|
|
36146
|
+
containerId
|
|
36147
|
+
];
|
|
36148
|
+
if (verb === "pause") return ["pause", containerId];
|
|
36149
|
+
if (status === "paused") return ["unpause", containerId];
|
|
36150
|
+
return ["start", containerId];
|
|
36151
|
+
}
|
|
36152
|
+
function isDockerIdentifier$1(value) {
|
|
36153
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}$/.test(value);
|
|
36154
|
+
}
|
|
36155
|
+
function findStack$1(issueId) {
|
|
36156
|
+
return getResourceStacks(getCurrentDockerStats()).find((stack) => stack.issueId?.toUpperCase() === issueId);
|
|
36157
|
+
}
|
|
36158
|
+
//#endregion
|
|
36159
|
+
//#region routes/resources/teardown.ts
|
|
36160
|
+
init_activity_logger();
|
|
36161
|
+
init_domain_services();
|
|
36162
|
+
const execFileAsync$5 = promisify(execFile);
|
|
36163
|
+
const CONFIRM_TOKEN_TTL_MS = 300 * 1e3;
|
|
36164
|
+
let dockerTeardownExec = execFileAsync$5;
|
|
36165
|
+
let teardownTokenGenerator = () => `td-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
36166
|
+
let activityEmitter = emitActivityEntrySync;
|
|
36167
|
+
const teardownTokens = /* @__PURE__ */ new Map();
|
|
36168
|
+
const getStackTeardownEstimateRoute = HttpRouter.add("GET", "/api/resources/stacks/:issueId/teardown-estimate", httpHandler(Effect.gen(function* () {
|
|
36169
|
+
return yield* getStackTeardownEstimateEffect((yield* HttpRouter.params)["issueId"] ?? "");
|
|
36170
|
+
})));
|
|
36171
|
+
const postStackTeardownRoute = HttpRouter.add("POST", "/api/resources/stacks/:issueId/teardown", httpHandler(Effect.gen(function* () {
|
|
36172
|
+
const params = yield* HttpRouter.params;
|
|
36173
|
+
const body = yield* readJsonBody$13;
|
|
36174
|
+
return yield* postStackTeardownEffect(params["issueId"] ?? "", body);
|
|
36175
|
+
})));
|
|
36176
|
+
function getStackTeardownEstimateEffect(issue) {
|
|
36177
|
+
return Effect.gen(function* () {
|
|
36178
|
+
const issueId = normalizeIssueId$1(issue);
|
|
36179
|
+
const stack = findStack(issueId);
|
|
36180
|
+
if (!stack) return jsonResponse({ error: `Stack not found for ${issueId || issue}` }, { status: 404 });
|
|
36181
|
+
const token = teardownTokenGenerator();
|
|
36182
|
+
const expiresAt = Date.now() + CONFIRM_TOKEN_TTL_MS;
|
|
36183
|
+
teardownTokens.set(token, {
|
|
36184
|
+
issueId,
|
|
36185
|
+
composeProject: stack.composeProject,
|
|
36186
|
+
expiresAt
|
|
36187
|
+
});
|
|
36188
|
+
return jsonResponse(buildStackTeardownEstimate(stack, token, expiresAt));
|
|
36189
|
+
});
|
|
36190
|
+
}
|
|
36191
|
+
function postStackTeardownEffect(issue, input) {
|
|
36192
|
+
return Effect.gen(function* () {
|
|
36193
|
+
const issueId = normalizeIssueId$1(issue);
|
|
36194
|
+
const confirmToken = typeof input.confirmToken === "string" ? input.confirmToken : "";
|
|
36195
|
+
const typedText = typeof input.typedText === "string" ? input.typedText : "";
|
|
36196
|
+
const token = teardownTokens.get(confirmToken);
|
|
36197
|
+
if (!token || token.issueId !== issueId || token.expiresAt <= Date.now()) {
|
|
36198
|
+
teardownTokens.delete(confirmToken);
|
|
36199
|
+
return jsonResponse({
|
|
36200
|
+
ok: false,
|
|
36201
|
+
error: "Invalid or expired confirmation token."
|
|
36202
|
+
}, { status: 422 });
|
|
36203
|
+
}
|
|
36204
|
+
if (typedText !== token.composeProject) return jsonResponse({
|
|
36205
|
+
ok: false,
|
|
36206
|
+
error: `Type ${token.composeProject} to confirm stack teardown.`
|
|
36207
|
+
}, { status: 422 });
|
|
36208
|
+
const stack = findStack(issueId);
|
|
36209
|
+
if (!stack) return jsonResponse({
|
|
36210
|
+
ok: false,
|
|
36211
|
+
error: `Stack not found for ${issueId}`
|
|
36212
|
+
}, { status: 404 });
|
|
36213
|
+
const estimate = buildStackTeardownEstimate(stack, confirmToken, token.expiresAt);
|
|
36214
|
+
const result = yield* runDockerStackTeardown(stack);
|
|
36215
|
+
teardownTokens.delete(confirmToken);
|
|
36216
|
+
yield* (yield* EventStoreService).append({
|
|
36217
|
+
type: "resources.updated",
|
|
36218
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36219
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
36220
|
+
});
|
|
36221
|
+
activityEmitter({
|
|
36222
|
+
source: "dashboard",
|
|
36223
|
+
level: "info",
|
|
36224
|
+
issueId,
|
|
36225
|
+
message: `Tore down Docker stack for ${issueId}, freeing ${estimate.ramBytes} RAM bytes and ${estimate.diskBytes} disk bytes.`,
|
|
36226
|
+
details: JSON.stringify({
|
|
36227
|
+
issueId,
|
|
36228
|
+
composeProject: stack.composeProject,
|
|
36229
|
+
ramBytes: estimate.ramBytes,
|
|
36230
|
+
diskBytes: estimate.diskBytes,
|
|
36231
|
+
removedContainers: result.containers.length,
|
|
36232
|
+
removedNetworks: result.networks.length,
|
|
36233
|
+
removedVolumes: result.volumes.length
|
|
36234
|
+
})
|
|
36235
|
+
});
|
|
36236
|
+
return jsonResponse({
|
|
36237
|
+
ok: true,
|
|
36238
|
+
issueId,
|
|
36239
|
+
composeProject: stack.composeProject,
|
|
36240
|
+
ramBytes: estimate.ramBytes,
|
|
36241
|
+
diskBytes: estimate.diskBytes,
|
|
36242
|
+
removedContainers: result.containers,
|
|
36243
|
+
removedNetworks: result.networks,
|
|
36244
|
+
removedVolumes: result.volumes
|
|
36245
|
+
});
|
|
36246
|
+
});
|
|
36247
|
+
}
|
|
36248
|
+
function buildStackTeardownEstimate(stack, confirmToken, expiresAt) {
|
|
36249
|
+
return {
|
|
36250
|
+
issueId: stack.issueId ?? stack.id,
|
|
36251
|
+
composeProject: stack.composeProject,
|
|
36252
|
+
ramBytes: stack.aggregates.memoryBytes,
|
|
36253
|
+
diskBytes: stack.aggregates.diskBytes,
|
|
36254
|
+
confirmToken,
|
|
36255
|
+
expiresAt: new Date(expiresAt).toISOString()
|
|
36256
|
+
};
|
|
36257
|
+
}
|
|
36258
|
+
function runDockerStackTeardown(stack) {
|
|
36259
|
+
return Effect.tryPromise({
|
|
36260
|
+
try: async () => {
|
|
36261
|
+
const containers = stack.services.map((service) => service.id).filter(Boolean);
|
|
36262
|
+
for (const container of containers) {
|
|
36263
|
+
if (!isDockerIdentifier(container)) throw new Error(`Invalid container ID: ${container}`);
|
|
36264
|
+
await dockerTeardownExec("docker", [
|
|
36265
|
+
"stop",
|
|
36266
|
+
"--time",
|
|
36267
|
+
"30",
|
|
36268
|
+
container
|
|
36269
|
+
], {
|
|
36270
|
+
encoding: "utf-8",
|
|
36271
|
+
timeout: 35e3
|
|
36272
|
+
});
|
|
36273
|
+
}
|
|
36274
|
+
for (const container of containers) await dockerTeardownExec("docker", [
|
|
36275
|
+
"rm",
|
|
36276
|
+
"-f",
|
|
36277
|
+
container
|
|
36278
|
+
], {
|
|
36279
|
+
encoding: "utf-8",
|
|
36280
|
+
timeout: 3e4
|
|
36281
|
+
});
|
|
36282
|
+
const networks = lines((await dockerTeardownExec("docker", [
|
|
36283
|
+
"network",
|
|
36284
|
+
"ls",
|
|
36285
|
+
"--filter",
|
|
36286
|
+
`label=com.docker.compose.project=${stack.composeProject}`,
|
|
36287
|
+
"--format",
|
|
36288
|
+
"{{.Name}}"
|
|
36289
|
+
], {
|
|
36290
|
+
encoding: "utf-8",
|
|
36291
|
+
timeout: 1e4
|
|
36292
|
+
})).stdout);
|
|
36293
|
+
for (const network of networks) {
|
|
36294
|
+
if (!isDockerIdentifier(network)) throw new Error(`Invalid network name: ${network}`);
|
|
36295
|
+
await dockerTeardownExec("docker", [
|
|
36296
|
+
"network",
|
|
36297
|
+
"rm",
|
|
36298
|
+
network
|
|
36299
|
+
], {
|
|
36300
|
+
encoding: "utf-8",
|
|
36301
|
+
timeout: 1e4
|
|
36302
|
+
});
|
|
36303
|
+
}
|
|
36304
|
+
const volumes = lines((await dockerTeardownExec("docker", [
|
|
36305
|
+
"volume",
|
|
36306
|
+
"ls",
|
|
36307
|
+
"--filter",
|
|
36308
|
+
`label=com.docker.compose.project=${stack.composeProject}`,
|
|
36309
|
+
"--format",
|
|
36310
|
+
"{{.Name}}"
|
|
36311
|
+
], {
|
|
36312
|
+
encoding: "utf-8",
|
|
36313
|
+
timeout: 1e4
|
|
36314
|
+
})).stdout);
|
|
36315
|
+
for (const volume of volumes) {
|
|
36316
|
+
if (!isDockerIdentifier(volume)) throw new Error(`Invalid volume name: ${volume}`);
|
|
36317
|
+
await dockerTeardownExec("docker", [
|
|
36318
|
+
"volume",
|
|
36319
|
+
"rm",
|
|
36320
|
+
volume
|
|
36321
|
+
], {
|
|
36322
|
+
encoding: "utf-8",
|
|
36323
|
+
timeout: 1e4
|
|
36324
|
+
});
|
|
36325
|
+
}
|
|
36326
|
+
return {
|
|
36327
|
+
containers,
|
|
36328
|
+
networks,
|
|
36329
|
+
volumes
|
|
36330
|
+
};
|
|
36331
|
+
},
|
|
36332
|
+
catch: (error) => error
|
|
36333
|
+
});
|
|
36334
|
+
}
|
|
36335
|
+
function findStack(issueId) {
|
|
36336
|
+
return getResourceStacks(getCurrentDockerStats()).find((stack) => stack.issueId?.toUpperCase() === issueId);
|
|
36337
|
+
}
|
|
36338
|
+
function normalizeIssueId$1(issue) {
|
|
36339
|
+
return issue.trim().toUpperCase();
|
|
36340
|
+
}
|
|
36341
|
+
function lines(value) {
|
|
36342
|
+
return value.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
36343
|
+
}
|
|
36344
|
+
function isDockerIdentifier(value) {
|
|
36345
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}$/.test(value);
|
|
36346
|
+
}
|
|
36347
|
+
const readJsonBody$13 = Effect.gen(function* () {
|
|
36348
|
+
const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
|
|
36349
|
+
try {
|
|
36350
|
+
return text ? JSON.parse(text) : {};
|
|
36351
|
+
} catch {
|
|
36352
|
+
return {};
|
|
36353
|
+
}
|
|
36354
|
+
});
|
|
36355
|
+
//#endregion
|
|
36356
|
+
//#region routes/resources/index.ts
|
|
36357
|
+
const resourcesRouteLayer = Layer.mergeAll(getResourcesRoute, getResourceHistoryRoute, getContainerHistoryRoute, getContainerDetailsRoute, deleteDockerContainerRoute, postPruneContainersRoute, deleteDockerNetworkRoute, deleteDockerVolumeRoute, postPruneVolumesRoute, postRestartContainerRoute, postStartContainerRoute, postStopContainerRoute, postPauseContainerRoute, postUnpauseContainerRoute, getContainerLogsRoute, deleteResourceVenvRoute, postStartStackRoute, postStopStackRoute, postPauseStackRoute, getStackTeardownEstimateRoute, postStackTeardownRoute);
|
|
34926
36358
|
//#endregion
|
|
34927
36359
|
//#region services/model-origin-enrich.ts
|
|
34928
36360
|
init_agents$1();
|
|
@@ -36919,7 +38351,7 @@ Be specific: reference actual file names, function names, requirement text, disc
|
|
|
36919
38351
|
const { command: cliCmd, args: cliArgs } = getAgentCommandSync(statusModelId);
|
|
36920
38352
|
const modelFlag = cliArgs.length > 0 ? ` ${cliArgs.join(" ")}` : "";
|
|
36921
38353
|
const promptFile = join$1(planningDir, ".status-review-prompt.tmp");
|
|
36922
|
-
const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-
|
|
38354
|
+
const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-Djkuu1CD.js");
|
|
36923
38355
|
let providerEnvStr = "";
|
|
36924
38356
|
let providerEnv = {};
|
|
36925
38357
|
const statusProvider = getProviderForModelSync(statusModelId);
|
|
@@ -37002,7 +38434,7 @@ const postMissionControlUploadRoute = HttpRouter.add("POST", "/api/command-deck/
|
|
|
37002
38434
|
let safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
37003
38435
|
let processedContent = content;
|
|
37004
38436
|
if (safeName.endsWith(".vtt")) {
|
|
37005
|
-
const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-
|
|
38437
|
+
const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-d-QMKZn4.js"));
|
|
37006
38438
|
processedContent = vttToMarkdown(content);
|
|
37007
38439
|
safeName = safeName.replace(/\.vtt$/, ".md");
|
|
37008
38440
|
}
|
|
@@ -42340,73 +43772,6 @@ const resetRoute = HttpRouter.add("POST", "/api/autopreso/session/reset", httpHa
|
|
|
42340
43772
|
})));
|
|
42341
43773
|
const autopresoRouteLayer = Layer.mergeAll(startRoute, backToStagingRoute, resetRoute);
|
|
42342
43774
|
//#endregion
|
|
42343
|
-
//#region services/event-loop-monitor.ts
|
|
42344
|
-
init_activity_logger();
|
|
42345
|
-
const EVENT_LOOP_MONITOR_WINDOW_MS = 6e4;
|
|
42346
|
-
let lastSample = {
|
|
42347
|
-
p50: 0,
|
|
42348
|
-
p99: 0,
|
|
42349
|
-
max: 0,
|
|
42350
|
-
unit: "ms",
|
|
42351
|
-
sampledAt: null,
|
|
42352
|
-
windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
|
|
42353
|
-
};
|
|
42354
|
-
let timer$3 = null;
|
|
42355
|
-
let activeHistogram = null;
|
|
42356
|
-
function nsToRoundedMs(ns) {
|
|
42357
|
-
if (!Number.isFinite(ns) || ns <= 0) return 0;
|
|
42358
|
-
return Math.round(ns / 1e6 * 100) / 100;
|
|
42359
|
-
}
|
|
42360
|
-
function sampleEventLoopDelay(histogram, deps = {}) {
|
|
42361
|
-
const warnThresholdMs = deps.warnThresholdMs ?? 100;
|
|
42362
|
-
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
42363
|
-
const warn = deps.warn ?? ((message) => console.warn(message));
|
|
42364
|
-
const emit = deps.emit ?? emitActivityEntrySync;
|
|
42365
|
-
const sample = {
|
|
42366
|
-
p50: nsToRoundedMs(histogram.percentile(50)),
|
|
42367
|
-
p99: nsToRoundedMs(histogram.percentile(99)),
|
|
42368
|
-
max: nsToRoundedMs(histogram.max),
|
|
42369
|
-
unit: "ms",
|
|
42370
|
-
sampledAt: now().toISOString(),
|
|
42371
|
-
windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
|
|
42372
|
-
};
|
|
42373
|
-
lastSample = sample;
|
|
42374
|
-
if (sample.p99 > warnThresholdMs) {
|
|
42375
|
-
const message = `Dashboard event loop p99 delay ${sample.p99}ms exceeded ${warnThresholdMs}ms`;
|
|
42376
|
-
warn(`[event-loop-monitor] ${message}`);
|
|
42377
|
-
emit({
|
|
42378
|
-
source: "dashboard",
|
|
42379
|
-
level: "warn",
|
|
42380
|
-
message,
|
|
42381
|
-
details: `p50=${sample.p50}ms p99=${sample.p99}ms max=${sample.max}ms over ${sample.windowMs}ms`
|
|
42382
|
-
});
|
|
42383
|
-
}
|
|
42384
|
-
histogram.reset();
|
|
42385
|
-
return sample;
|
|
42386
|
-
}
|
|
42387
|
-
function getEventLoopDelaySample() {
|
|
42388
|
-
return lastSample;
|
|
42389
|
-
}
|
|
42390
|
-
function startEventLoopMonitor(deps = {}) {
|
|
42391
|
-
if (timer$3) return;
|
|
42392
|
-
const intervalMs = deps.intervalMs ?? 6e4;
|
|
42393
|
-
activeHistogram = (deps.createHistogram ?? (() => monitorEventLoopDelay({ resolution: 20 })))();
|
|
42394
|
-
activeHistogram.enable();
|
|
42395
|
-
timer$3 = setInterval(() => {
|
|
42396
|
-
if (!activeHistogram) return;
|
|
42397
|
-
sampleEventLoopDelay(activeHistogram, deps);
|
|
42398
|
-
}, intervalMs);
|
|
42399
|
-
timer$3.unref?.();
|
|
42400
|
-
}
|
|
42401
|
-
function stopEventLoopMonitor() {
|
|
42402
|
-
if (timer$3) {
|
|
42403
|
-
clearInterval(timer$3);
|
|
42404
|
-
timer$3 = null;
|
|
42405
|
-
}
|
|
42406
|
-
activeHistogram?.disable();
|
|
42407
|
-
activeHistogram = null;
|
|
42408
|
-
}
|
|
42409
|
-
//#endregion
|
|
42410
43775
|
//#region routes/metrics.ts
|
|
42411
43776
|
/**
|
|
42412
43777
|
* Metrics route module — Effect HttpRouter.Layer (PAN-428 B16)
|
|
@@ -42880,7 +44245,7 @@ const postRallyValidateRoute = HttpRouter.add("POST", "/api/rally/validate", Eff
|
|
|
42880
44245
|
}, { status: 400 });
|
|
42881
44246
|
return yield* Effect.promise(async () => {
|
|
42882
44247
|
try {
|
|
42883
|
-
const { RallyRestApi } = await import("./rally-api-
|
|
44248
|
+
const { RallyRestApi } = await import("./rally-api-DpFjC_KQ.js");
|
|
42884
44249
|
const api = new RallyRestApi({
|
|
42885
44250
|
apiKey,
|
|
42886
44251
|
server: server || "https://rally1.rallydev.com"
|
|
@@ -43208,7 +44573,7 @@ const getPlanningStatusRoute = HttpRouter.add("GET", "/api/planning/:issueId/sta
|
|
|
43208
44573
|
const remoteState = loadRemoteAgentState(sessionName);
|
|
43209
44574
|
const isRemote = !!remoteState;
|
|
43210
44575
|
const vmName = remoteState?.vmName ?? "";
|
|
43211
|
-
const { getAgentStateSync } = await import("./agents-
|
|
44576
|
+
const { getAgentStateSync } = await import("./agents-Ea9gfdKS.js");
|
|
43212
44577
|
const agentStarting = getAgentStateSync(sessionName)?.status === "starting";
|
|
43213
44578
|
let tmuxSessionAlive = false;
|
|
43214
44579
|
if (!isRemote) try {
|
|
@@ -43351,7 +44716,7 @@ Continue the PLANNING session. Do NOT implement anything.
|
|
|
43351
44716
|
`);
|
|
43352
44717
|
const agentCwd = workspacePath;
|
|
43353
44718
|
if (existsSync$1(outputFile)) await rename(outputFile, join$1(planningDir, `output-${Date.now()}.jsonl`));
|
|
43354
|
-
const { getAgentCommandSync } = await import("./settings-
|
|
44719
|
+
const { getAgentCommandSync } = await import("./settings-DXeuoifi.js");
|
|
43355
44720
|
let msgPlanningModel = "claude-sonnet-5";
|
|
43356
44721
|
try {
|
|
43357
44722
|
msgPlanningModel = resolveModel("plan", void 0, loadConfigSync().config);
|
|
@@ -43472,7 +44837,7 @@ async function getIndexStats(rootPath, isMain) {
|
|
|
43472
44837
|
}
|
|
43473
44838
|
const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Effect.promise(async () => {
|
|
43474
44839
|
try {
|
|
43475
|
-
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-
|
|
44840
|
+
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
|
|
43476
44841
|
const projectRoot = process.cwd();
|
|
43477
44842
|
const venvPath = join$1(projectRoot, ".venv");
|
|
43478
44843
|
const results = [];
|
|
@@ -43517,7 +44882,7 @@ const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Ef
|
|
|
43517
44882
|
}));
|
|
43518
44883
|
const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Effect.promise(async () => {
|
|
43519
44884
|
try {
|
|
43520
|
-
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-
|
|
44885
|
+
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
|
|
43521
44886
|
const projectRoot = process.cwd();
|
|
43522
44887
|
const venvPath = join$1(projectRoot, ".venv");
|
|
43523
44888
|
if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
|
|
@@ -43534,7 +44899,7 @@ const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Ef
|
|
|
43534
44899
|
}));
|
|
43535
44900
|
const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effect.promise(async () => {
|
|
43536
44901
|
try {
|
|
43537
|
-
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-
|
|
44902
|
+
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
|
|
43538
44903
|
const projectRoot = process.cwd();
|
|
43539
44904
|
const venvPath = join$1(projectRoot, ".venv");
|
|
43540
44905
|
if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
|
|
@@ -43551,7 +44916,7 @@ const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effe
|
|
|
43551
44916
|
}));
|
|
43552
44917
|
const postTldrReloadRoute = HttpRouter.add("POST", "/api/services/tldr/reload", Effect.promise(async () => {
|
|
43553
44918
|
try {
|
|
43554
|
-
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-
|
|
44919
|
+
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
|
|
43555
44920
|
const enabled = isTldrEnabledSync();
|
|
43556
44921
|
const projectRoot = process.cwd();
|
|
43557
44922
|
const targets = [];
|
|
@@ -43627,7 +44992,7 @@ const getVersionRoute = HttpRouter.add("GET", "/api/version", Effect.promise(asy
|
|
|
43627
44992
|
const version = await getOverdeckVersion();
|
|
43628
44993
|
let supervisorUrl = null;
|
|
43629
44994
|
try {
|
|
43630
|
-
const { getSupervisorUrlSync } = await import("./supervisor-
|
|
44995
|
+
const { getSupervisorUrlSync } = await import("./supervisor-BY_xA7eS.js");
|
|
43631
44996
|
supervisorUrl = getSupervisorUrlSync();
|
|
43632
44997
|
} catch {}
|
|
43633
44998
|
return jsonResponse({
|
|
@@ -43791,7 +45156,7 @@ const postShadowMonitorRoute = HttpRouter.add("POST", "/api/shadow/:issueId/moni
|
|
|
43791
45156
|
const projectPath = await getProjectPath(issuePrefix);
|
|
43792
45157
|
const workspacePath = join$1(projectPath, "workspaces", `feature-${issueLower}`);
|
|
43793
45158
|
if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
|
|
43794
|
-
const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-
|
|
45159
|
+
const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-DMC2pBPa.js");
|
|
43795
45160
|
const config = {
|
|
43796
45161
|
issueId,
|
|
43797
45162
|
workspacePath,
|
|
@@ -43821,7 +45186,7 @@ const postShadowObserveRoute = HttpRouter.add("POST", "/api/shadow/:issueId/obse
|
|
|
43821
45186
|
if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
|
|
43822
45187
|
const ghConfig = getGitHubConfig();
|
|
43823
45188
|
if (!ghConfig) return jsonResponse({ error: "GitHub not configured - Observer requires GitHub" }, { status: 400 });
|
|
43824
|
-
const { runObserverCycle } = await import("./shadow-engineering-
|
|
45189
|
+
const { runObserverCycle } = await import("./shadow-engineering-DMC2pBPa.js");
|
|
43825
45190
|
const firstRepo = ghConfig.repos[0];
|
|
43826
45191
|
const config = {
|
|
43827
45192
|
issueId,
|
|
@@ -44886,7 +46251,7 @@ async function cleanupConversationAttachments(name) {
|
|
|
44886
46251
|
* sessions that the lifecycle poller missed. */
|
|
44887
46252
|
async function cleanupOrphanedConversationAttachments() {
|
|
44888
46253
|
try {
|
|
44889
|
-
const { listConversations } = await import("./conversations-
|
|
46254
|
+
const { listConversations } = await import("./conversations-DSOZG_Sz.js");
|
|
44890
46255
|
const convNames = new Set(listConversations().map((c) => c.name));
|
|
44891
46256
|
const root = getConversationAttachmentsRoot();
|
|
44892
46257
|
await runInBatches((await readdir(root, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !convNames.has(entry.name)).map((entry) => join$1(root, entry.name)), 10, (path) => rm(path, {
|
|
@@ -45754,7 +47119,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
|
|
|
45754
47119
|
const codexApprovalPolicy = codexPermMode === "full-access" ? "never" : "on-request";
|
|
45755
47120
|
const codexSandboxMode = codexPermMode === "full-access" ? "danger-full-access" : codexPermMode === "read-only" ? "read-only" : "workspace-write";
|
|
45756
47121
|
const codexApprovalsReviewer = codexPermMode === "auto-review" ? "auto_review" : void 0;
|
|
45757
|
-
const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-
|
|
47122
|
+
const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-BObh4thD.js");
|
|
45758
47123
|
initCodexHome(codexHome, {
|
|
45759
47124
|
trustedDir: cwd,
|
|
45760
47125
|
approvalPolicy: codexApprovalPolicy,
|
|
@@ -45824,7 +47189,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
|
|
|
45824
47189
|
} catch {}
|
|
45825
47190
|
console.log(`[claude-invoke] purpose=conversation-session | model=${model || "default"} | source=conversations.ts:spawnConversationSession | session=${tmuxSession} | resume=${resume} | command="${runtimeCommand}"`);
|
|
45826
47191
|
try {
|
|
45827
|
-
const { preTrustDirectory } = await import("./workspace-manager-
|
|
47192
|
+
const { preTrustDirectory } = await import("./workspace-manager-BE2H8gD5.js");
|
|
45828
47193
|
preTrustDirectory(cwd);
|
|
45829
47194
|
} catch {}
|
|
45830
47195
|
try {
|
|
@@ -45841,7 +47206,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
|
|
|
45841
47206
|
const codexHomeDir = codexFields.codexHome;
|
|
45842
47207
|
(async () => {
|
|
45843
47208
|
try {
|
|
45844
|
-
const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-
|
|
47209
|
+
const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-BObh4thD.js");
|
|
45845
47210
|
const rollout = await waitForCodexRollout(codexHomeDir, 12e4);
|
|
45846
47211
|
if (rollout) {
|
|
45847
47212
|
const threadId = extractThreadIdFromRollout(rollout);
|
|
@@ -46767,6 +48132,19 @@ async function resolveSessionFile(conv) {
|
|
|
46767
48132
|
console.error(`[conversations] UNRESOLVED claude-code session for conversation '${conv.name}' (tmux=${conv.tmuxSession}, status=${conv.status}, cwd=${conv.cwd}): no --session-id pinned in launcher.sh and no recorded claudeSessionId. The transcript panel cannot be trusted.`);
|
|
46768
48133
|
return null;
|
|
46769
48134
|
}
|
|
48135
|
+
/**
|
|
48136
|
+
* True when a conversation that demonstrably had activity (tokens recorded, or
|
|
48137
|
+
* a title derived from its messages) no longer has a transcript file on disk.
|
|
48138
|
+
* This is the operator-facing "history was lost" signal — e.g. the 2026-07-05
|
|
48139
|
+
* incident where a boot-time cleanup deleted conv-* dirs holding ohmypi/codex
|
|
48140
|
+
* transcripts. Live sessions are excluded: a claude-code session writes its
|
|
48141
|
+
* JSONL only on the first message, so a missing file is normal while alive.
|
|
48142
|
+
*/
|
|
48143
|
+
function conversationTranscriptMissing(conv, sessionAlive, sessionFile) {
|
|
48144
|
+
if (sessionAlive) return false;
|
|
48145
|
+
if (!((conv.totalTokens ?? 0) > 0 || conv.titleSource === "ai" || conv.titleSource === "auto")) return false;
|
|
48146
|
+
return !sessionFile || !existsSync$1(sessionFile);
|
|
48147
|
+
}
|
|
46770
48148
|
const MESSAGES_CACHE_MAX = 100;
|
|
46771
48149
|
const messagesCache = /* @__PURE__ */ new Map();
|
|
46772
48150
|
function isCodexSessionFile(sessionFile) {
|
|
@@ -46935,7 +48313,8 @@ async function getConversationRead(rawId, deps) {
|
|
|
46935
48313
|
isWorktree: gitInfo.isWorktree,
|
|
46936
48314
|
pendingInputCount,
|
|
46937
48315
|
pendingInputKinds,
|
|
46938
|
-
pendingAskUserQuestion
|
|
48316
|
+
pendingAskUserQuestion,
|
|
48317
|
+
transcriptMissing: conversationTranscriptMissing(conv, sessionAlive, convSf)
|
|
46939
48318
|
});
|
|
46940
48319
|
} catch (error) {
|
|
46941
48320
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -47444,7 +48823,8 @@ async function enrichConversationList(limit, offset) {
|
|
|
47444
48823
|
isWorktree: gitInfo.isWorktree,
|
|
47445
48824
|
pendingInputCount,
|
|
47446
48825
|
pendingInputKinds,
|
|
47447
|
-
pendingAskUserQuestion
|
|
48826
|
+
pendingAskUserQuestion,
|
|
48827
|
+
transcriptMissing: conversationTranscriptMissing(row, sessionAlive, convSf)
|
|
47448
48828
|
};
|
|
47449
48829
|
})), CONVERSATION_LIST_ENRICHMENT_CONCURRENCY));
|
|
47450
48830
|
}
|
|
@@ -48910,7 +50290,7 @@ async function registerProjectFromPath(opts) {
|
|
|
48910
50290
|
registerProjectSync(key, projectConfig);
|
|
48911
50291
|
const seededContextLayer = ensureProjectLayer(fullPath);
|
|
48912
50292
|
try {
|
|
48913
|
-
const { preTrustDirectorySync } = await import("./workspace-manager-
|
|
50293
|
+
const { preTrustDirectorySync } = await import("./workspace-manager-BE2H8gD5.js");
|
|
48914
50294
|
preTrustDirectorySync(fullPath);
|
|
48915
50295
|
} catch {}
|
|
48916
50296
|
let hooksInstalled = 0;
|
|
@@ -48946,7 +50326,7 @@ init_pan_dir();
|
|
|
48946
50326
|
init_paths();
|
|
48947
50327
|
const execAsync$3 = promisify(exec);
|
|
48948
50328
|
async function getIssueDataService() {
|
|
48949
|
-
const { getSharedIssueService } = await import("./issue-service-singleton-
|
|
50329
|
+
const { getSharedIssueService } = await import("./issue-service-singleton-DFei50PX.js");
|
|
48950
50330
|
return getSharedIssueService();
|
|
48951
50331
|
}
|
|
48952
50332
|
/** Returns true if the path exists (any type). */
|
|
@@ -50873,7 +52253,7 @@ async function refreshMergeStateFromGitHub(issueId, repo, prNumber) {
|
|
|
50873
52253
|
let mergeState;
|
|
50874
52254
|
let isDraft;
|
|
50875
52255
|
let checksFailed;
|
|
50876
|
-
const { isGitHubAppConfigured, getPullRequestState } = await import("./github-app-
|
|
52256
|
+
const { isGitHubAppConfigured, getPullRequestState } = await import("./github-app-Dg25z05Z.js");
|
|
50877
52257
|
if (isGitHubAppConfigured()) {
|
|
50878
52258
|
const prState = await Effect.runPromise(getPullRequestState(owner, repoName, prNumber));
|
|
50879
52259
|
mergeable = prState.mergeable === null ? "UNKNOWN" : prState.mergeable ? "MERGEABLE" : "CONFLICTING";
|
|
@@ -51022,8 +52402,8 @@ async function handlePullRequestPromise(payload) {
|
|
|
51022
52402
|
bumpIssuePrTabCacheGeneration(issueId);
|
|
51023
52403
|
const repo = payload.repository.full_name;
|
|
51024
52404
|
if (payload.action === "closed" && pr.merged === true) try {
|
|
51025
|
-
const { postMergeLifecycle } = await import("./merge-agent-
|
|
51026
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
52405
|
+
const { postMergeLifecycle } = await import("./merge-agent-DZaRBHQA.js");
|
|
52406
|
+
const { resolveProjectFromIssueSync } = await import("./projects-BbMEBykK.js");
|
|
51027
52407
|
const project = resolveProjectFromIssueSync(issueId);
|
|
51028
52408
|
if (project) {
|
|
51029
52409
|
const branchName = pr.head.ref;
|
|
@@ -51522,7 +52902,7 @@ function getCheckpointWorker() {
|
|
|
51522
52902
|
return worker;
|
|
51523
52903
|
}
|
|
51524
52904
|
async function runInline(operation, payload) {
|
|
51525
|
-
const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-
|
|
52905
|
+
const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-auGbx5b0.js");
|
|
51526
52906
|
switch (operation) {
|
|
51527
52907
|
case "claimTranscriptRange": return claimTranscriptRange(payload);
|
|
51528
52908
|
case "commitTranscriptRange": return commitTranscriptRange(payload);
|
|
@@ -52676,7 +54056,7 @@ function buildPendingTurn(input, fromOffset, toOffset, compressed) {
|
|
|
52676
54056
|
};
|
|
52677
54057
|
}
|
|
52678
54058
|
async function emitMemoryObservationCreated(observation, timestamp) {
|
|
52679
|
-
const { initEventStore } = await import("./event-store-
|
|
54059
|
+
const { initEventStore } = await import("./event-store-D7_dSgP8.js");
|
|
52680
54060
|
await (await initEventStore()).appendAsync({
|
|
52681
54061
|
type: "memory.observation_created",
|
|
52682
54062
|
timestamp,
|
|
@@ -55715,7 +57095,7 @@ async function getFlywheelConversationPayload() {
|
|
|
55715
57095
|
};
|
|
55716
57096
|
}
|
|
55717
57097
|
async function postFlywheelReportPayload() {
|
|
55718
|
-
const { flywheelReportCommand } = await import("./flywheel-
|
|
57098
|
+
const { flywheelReportCommand } = await import("./flywheel-CFPOlOUd.js");
|
|
55719
57099
|
await flywheelReportCommand({ cwd: process.cwd() });
|
|
55720
57100
|
return {
|
|
55721
57101
|
status: 200,
|
|
@@ -55873,11 +57253,11 @@ const getMergeBlockersRoute = HttpRouter.add("GET", "/api/flywheel/merge-blocker
|
|
|
55873
57253
|
async function defaultGetOrderedIssueIds() {
|
|
55874
57254
|
const status = await readCurrentFlywheelStatusForDashboard();
|
|
55875
57255
|
if (!status) return [];
|
|
55876
|
-
const { computeMergeQueue } = await import("./flywheel-merge-order-
|
|
57256
|
+
const { computeMergeQueue } = await import("./flywheel-merge-order-FN6zqVXG.js");
|
|
55877
57257
|
return (await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd()).pipe(Effect.provide(layer)))).map((i) => i.issueId);
|
|
55878
57258
|
}
|
|
55879
57259
|
async function defaultMergeOne(issueId) {
|
|
55880
|
-
const { triggerMerge } = await import("./merge-ops-
|
|
57260
|
+
const { triggerMerge } = await import("./merge-ops-DRRd3YBA.js");
|
|
55881
57261
|
const r = await triggerMerge(issueId);
|
|
55882
57262
|
return r.success ? { ok: true } : {
|
|
55883
57263
|
ok: false,
|
|
@@ -55897,7 +57277,7 @@ async function postFlywheelMergeNextPayload(payload, deps = {}) {
|
|
|
55897
57277
|
body: { error: "n must be a positive integer" }
|
|
55898
57278
|
};
|
|
55899
57279
|
const issueIds = (await (deps.getOrderedIssueIds ?? defaultGetOrderedIssueIds)()).slice(0, n);
|
|
55900
|
-
const { shipMergeBatch } = await import("./merge-batch-
|
|
57280
|
+
const { shipMergeBatch } = await import("./merge-batch-BbG78hx-.js");
|
|
55901
57281
|
return {
|
|
55902
57282
|
status: 200,
|
|
55903
57283
|
body: { outcomes: await shipMergeBatch(issueIds, { merge: deps.merge ?? defaultMergeOne }) }
|
|
@@ -56054,7 +57434,7 @@ const getFlywheelMergeQueueRoute = HttpRouter.add("GET", "/api/flywheel/merge-qu
|
|
|
56054
57434
|
return yield* Effect.promise(async () => {
|
|
56055
57435
|
const status = await readCurrentFlywheelStatusForDashboard();
|
|
56056
57436
|
if (!status) return jsonResponse([]);
|
|
56057
|
-
const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-
|
|
57437
|
+
const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-FN6zqVXG.js");
|
|
56058
57438
|
return jsonResponse(await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd(), { getPrUrl: resolveMergeQueuePrUrl }).pipe(Effect.provide(layer))));
|
|
56059
57439
|
});
|
|
56060
57440
|
})));
|
|
@@ -56068,18 +57448,18 @@ function uatGenerationNameFromParam(param) {
|
|
|
56068
57448
|
return decoded.startsWith("uat/") ? decoded : `uat/${decoded}`;
|
|
56069
57449
|
}
|
|
56070
57450
|
const getUatGenerationsRoute = HttpRouter.add("GET", "/api/flywheel/uat-generations", httpHandler(Effect.gen(function* () {
|
|
56071
|
-
const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-
|
|
57451
|
+
const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
|
|
56072
57452
|
return jsonResponse(yield* Effect.promise(() => getUatGenerationsPayload()));
|
|
56073
57453
|
})));
|
|
56074
57454
|
const getUatCandidateRoute = HttpRouter.add("GET", "/api/flywheel/uat-candidate", httpHandler(Effect.gen(function* () {
|
|
56075
|
-
const { getUatCandidatePayload } = yield* Effect.promise(() => import("./uat-train-
|
|
57455
|
+
const { getUatCandidatePayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
|
|
56076
57456
|
return jsonResponse(yield* Effect.promise(() => getUatCandidatePayload()));
|
|
56077
57457
|
})));
|
|
56078
57458
|
const postUatGenerationStackRoute = HttpRouter.add("POST", "/api/flywheel/uat-generations/:name/stack", httpHandler(Effect.gen(function* () {
|
|
56079
57459
|
const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
|
|
56080
57460
|
if (authError) return authError;
|
|
56081
57461
|
const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
|
|
56082
|
-
const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-
|
|
57462
|
+
const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
|
|
56083
57463
|
const result = yield* Effect.promise(() => postUatGenerationStackPayload(name));
|
|
56084
57464
|
if (!result.ok) return jsonResponse({ error: result.error }, { status: result.status });
|
|
56085
57465
|
return jsonResponse({
|
|
@@ -56091,8 +57471,8 @@ const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-
|
|
|
56091
57471
|
const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
|
|
56092
57472
|
if (authError) return authError;
|
|
56093
57473
|
const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
|
|
56094
|
-
const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-
|
|
56095
|
-
const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-
|
|
57474
|
+
const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
|
|
57475
|
+
const { firePostMergeLifecycle } = yield* Effect.promise(() => import("./specialists-B6m2H7J6.js"));
|
|
56096
57476
|
const result = yield* Effect.promise(() => postUatGenerationPromotePayload(name, firePostMergeLifecycle));
|
|
56097
57477
|
if (!result.success) return jsonResponse(result, { status: result.reason === "not-found" ? 404 : result.reason === "merge-failed" ? 500 : 409 });
|
|
56098
57478
|
return jsonResponse(result);
|
|
@@ -56105,7 +57485,7 @@ const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-
|
|
|
56105
57485
|
const postFlywheelAssembleUatRoute = HttpRouter.add("POST", "/api/flywheel/assemble-uat", httpHandler(Effect.gen(function* () {
|
|
56106
57486
|
const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
|
|
56107
57487
|
if (authError) return authError;
|
|
56108
|
-
const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-
|
|
57488
|
+
const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
|
|
56109
57489
|
return jsonResponse(yield* Effect.promise(() => runUatTrainReconcile({ force: true })));
|
|
56110
57490
|
})));
|
|
56111
57491
|
const getFlywheelStateRoute = HttpRouter.add("GET", "/api/flywheel/state", httpHandler(Effect.gen(function* () {
|
|
@@ -59621,7 +61001,7 @@ async function pollOnce(state) {
|
|
|
59621
61001
|
try {
|
|
59622
61002
|
const remoteState = await readFile(join$1(homedir$1(), ".overdeck", "agents", agentId, "remote-state.json"), "utf-8").then((text) => JSON.parse(text)).catch(() => null);
|
|
59623
61003
|
if (remoteState?.location === "remote" && remoteState?.vmName) {
|
|
59624
|
-
const { getRemoteAgentOutput } = await import("./remote-agents-
|
|
61004
|
+
const { getRemoteAgentOutput } = await import("./remote-agents-BXKV5iBS.js");
|
|
59625
61005
|
stdout = await getRemoteAgentOutput(agentId, remoteState.vmName, 50);
|
|
59626
61006
|
} else stdout = await Effect.runPromise(capturePane(agentId, 50));
|
|
59627
61007
|
} catch {
|
|
@@ -60804,7 +62184,7 @@ const RESTART_MARKER = join(homedir(), ".overdeck", "dashboard-restarting.json")
|
|
|
60804
62184
|
* loads new content-hashed chunk filenames (no ERR_MODULE_NOT_FOUND after rebuild).
|
|
60805
62185
|
*/
|
|
60806
62186
|
async function defaultLifecycleRunner(pending) {
|
|
60807
|
-
const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-
|
|
62187
|
+
const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-DZaRBHQA.js");
|
|
60808
62188
|
await postMergeLifecycle(pending.issueId, pending.projectPath, pending.sourceBranch, { skipDeploy: true });
|
|
60809
62189
|
if (pending.sourceBranch) await notifyTldrDaemon(pending.projectPath, pending.sourceBranch);
|
|
60810
62190
|
}
|
|
@@ -61125,7 +62505,7 @@ function failureReason(result) {
|
|
|
61125
62505
|
return result.error ?? result.message ?? `merge returned status ${result.statusCode ?? "unknown"}`;
|
|
61126
62506
|
}
|
|
61127
62507
|
async function defaultMergeIssue(issueId) {
|
|
61128
|
-
const { triggerMerge } = await import("./merge-ops-
|
|
62508
|
+
const { triggerMerge } = await import("./merge-ops-DRRd3YBA.js");
|
|
61129
62509
|
return triggerMerge(issueId);
|
|
61130
62510
|
}
|
|
61131
62511
|
function defaultAnnounceFailure(issueId, reason) {
|
|
@@ -61229,6 +62609,34 @@ async function warnIfAutonomousMergeBackendUnavailable(deps = {}) {
|
|
|
61229
62609
|
}
|
|
61230
62610
|
}
|
|
61231
62611
|
//#endregion
|
|
62612
|
+
//#region services/merge-app-scopes-health.ts
|
|
62613
|
+
init_activity_logger();
|
|
62614
|
+
init_github_app();
|
|
62615
|
+
function shouldWarnAppCannotMerge(result) {
|
|
62616
|
+
return result.configured === true && result.canMerge === false;
|
|
62617
|
+
}
|
|
62618
|
+
async function warnIfAppCannotMerge(deps = {}) {
|
|
62619
|
+
try {
|
|
62620
|
+
const result = await (deps.getResult ?? (() => verifyAppCanMerge()))();
|
|
62621
|
+
if (!shouldWarnAppCannotMerge(result)) return;
|
|
62622
|
+
const missing = result.missing ?? [];
|
|
62623
|
+
const message = [
|
|
62624
|
+
"[overdeck] WARNING: overdeck-agent GitHub App installation cannot merge pull requests.",
|
|
62625
|
+
`Missing required permissions: ${missing.join(", ")}.`,
|
|
62626
|
+
"Grant these permissions to the App installation to enable autonomous merges."
|
|
62627
|
+
].join("\n");
|
|
62628
|
+
(deps.warn ?? console.warn)(message);
|
|
62629
|
+
(deps.emit ?? emitActivityEntrySync)({
|
|
62630
|
+
source: "dashboard",
|
|
62631
|
+
level: "warn",
|
|
62632
|
+
message: `GitHub App installation cannot merge: missing ${missing.join(", ")}`
|
|
62633
|
+
});
|
|
62634
|
+
} catch (error) {
|
|
62635
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
62636
|
+
(deps.warn ?? console.warn)(`[overdeck] WARNING: failed to verify GitHub App merge permissions: ${message}`);
|
|
62637
|
+
}
|
|
62638
|
+
}
|
|
62639
|
+
//#endregion
|
|
61232
62640
|
//#region services/cost-reconcile-service.ts
|
|
61233
62641
|
const RECONCILE_INTERVAL_MS = 5 * 6e4;
|
|
61234
62642
|
let timer = null;
|
|
@@ -61239,6 +62647,14 @@ async function runCostReconcileOnce(reason) {
|
|
|
61239
62647
|
const result = await reconcilePiTranscripts();
|
|
61240
62648
|
if (result.eventsImported > 0 || result.errors.length > 0) console.log(`[cost-reconciler] ${reason} sweep: ${result.eventsImported} imported, ${result.duplicatesSkipped} duplicate(s), ${result.errors.length} error(s)`);
|
|
61241
62649
|
if (result.errors.length > 0) for (const err of result.errors.slice(0, 5)) console.warn(`[cost-reconciler] ${err.path}: ${err.error}`);
|
|
62650
|
+
const backfillResult = reclassifyUnknownCostEventsSync();
|
|
62651
|
+
if (backfillResult.updated > 0) console.log(`[cost-reconciler] ${reason} UNKNOWN backfill: ${backfillResult.updated} updated`);
|
|
62652
|
+
try {
|
|
62653
|
+
const codexResult = await Effect.runPromise(CostWriter.use((writer) => writer.reconcile({ source: "codex" })).pipe(Effect.provide(CostDoorLive)));
|
|
62654
|
+
if (codexResult.imported > 0) console.log(`[cost-reconciler] ${reason} codex sweep: ${codexResult.imported} imported`);
|
|
62655
|
+
} catch (err) {
|
|
62656
|
+
console.warn("[cost-reconciler] codex sweep failed:", err instanceof Error ? err.message : err);
|
|
62657
|
+
}
|
|
61242
62658
|
})().finally(() => {
|
|
61243
62659
|
inFlight = null;
|
|
61244
62660
|
});
|
|
@@ -61264,6 +62680,7 @@ function stopCostReconcileService() {
|
|
|
61264
62680
|
}
|
|
61265
62681
|
//#endregion
|
|
61266
62682
|
//#region ../../../scripts/create-overdeck-db.ts
|
|
62683
|
+
init_boot_gates();
|
|
61267
62684
|
init_driver();
|
|
61268
62685
|
function migrationStatements(migrationPath) {
|
|
61269
62686
|
return readFileSync$1(migrationPath, "utf8").split("--> statement-breakpoint").map((statement) => statement.trim()).filter(Boolean);
|
|
@@ -61330,6 +62747,7 @@ init_paths();
|
|
|
61330
62747
|
init_tmux();
|
|
61331
62748
|
init_fts_db();
|
|
61332
62749
|
init_paths$1();
|
|
62750
|
+
init_auto_commit();
|
|
61333
62751
|
initDashboardLogFile();
|
|
61334
62752
|
console.log(`[boot-timing] module graph loaded at +${Math.round(performance.now())}ms (since process start)`);
|
|
61335
62753
|
console.log(`[overdeck] Boot gates: ${formatBootGateState(resolveBootGates())}`);
|
|
@@ -61338,6 +62756,7 @@ console.log("[overdeck] Event loop delay monitor started");
|
|
|
61338
62756
|
await mkdir(getOverdeckHome(), { recursive: true });
|
|
61339
62757
|
ensureInternalTokenSync();
|
|
61340
62758
|
warnIfAutonomousMergeBackendUnavailable();
|
|
62759
|
+
warnIfAppCannotMerge();
|
|
61341
62760
|
await ensureManagedTmuxContextOnce();
|
|
61342
62761
|
await initTrackerConfigCache().catch((err) => {
|
|
61343
62762
|
console.log("[tracker-config] Warning: failed to cache .overdeck.env:", err.message);
|
|
@@ -61674,6 +63093,7 @@ const handleShutdownSignal = async (signal) => {
|
|
|
61674
63093
|
stopCostReconcileService();
|
|
61675
63094
|
stopRestartAnnouncer();
|
|
61676
63095
|
await stopDeaconChild().catch((err) => console.warn("[deacon-supervisor] child shutdown failed:", err));
|
|
63096
|
+
await Effect.runPromise(flushAllPendingAutoCommits()).catch((err) => console.warn("[pan-dir/auto-commit] shutdown flush failed:", err));
|
|
61677
63097
|
await stopConversationSearchWatcher().catch((err) => console.warn("[conversation-search] watcher shutdown failed:", err));
|
|
61678
63098
|
closeConversationSearchService();
|
|
61679
63099
|
closeMemoryFtsDatabases();
|
|
@@ -61722,7 +63142,7 @@ reconcileStaleGitHubBlockers().then((n) => {
|
|
|
61722
63142
|
}
|
|
61723
63143
|
}).catch((err) => console.warn(`[overdeck] Startup blocker reconciliation failed: ${err.message}`));
|
|
61724
63144
|
try {
|
|
61725
|
-
const { resetProcessingToQueued } = await import("./merge-sync-
|
|
63145
|
+
const { resetProcessingToQueued } = await import("./merge-sync-D3wPN2-T.js");
|
|
61726
63146
|
const resetCount = resetProcessingToQueued();
|
|
61727
63147
|
if (resetCount > 0) {
|
|
61728
63148
|
console.log(`[overdeck] Reset ${resetCount} stuck merge queue entries to queued`);
|