@overdeck/desktop 0.42.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-DGdm9pVn.js → diagram-OG6HWLK6-Dk7-KYG7.js} +1 -1
- package/server/public/assets/{dist-YEo-2Oy8.js → dist-CaEw8nYm.js} +5 -5
- package/server/public/assets/{dist-AFfBwqj3.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-Bq_qtCsW.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-B0yqF_w8.js → pieDiagram-4H26LBE5-_QEBgJ8d.js} +1 -1
- package/server/public/assets/{prod-DhREhukv.js → prod-oCB7Fr_u.js} +5 -5
- package/server/public/assets/{quadrantDiagram-W4KKPZXB-CdByrHkW.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-DApIsH9w.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-BrVcjfKW.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 +1889 -430
- 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-C4IC6tXf.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-BsV09cPX.js +0 -292
- package/server/public/assets/image-GAAHSSAO-CCe2yzQe.js +0 -1
- package/server/public/assets/index-C2PMO6nB.js +0 -818
- package/server/public/assets/index-Dzio_3kK.css +0 -1
- package/server/public/assets/linear-DxIhHUPD.js +0 -1
- package/server/public/assets/mermaid-GHXKKRXX-BZNohdCo.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-Bykrzblr.js";
|
|
94
|
-
import { i as listTranscriptCheckpoints, r as getTranscriptCheckpoint$1 } from "./checkpoints-BcXu6L_6.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 });
|
|
@@ -29286,6 +29285,12 @@ function storedToDomainEvent(stored) {
|
|
|
29286
29285
|
payload: stored.payload
|
|
29287
29286
|
};
|
|
29288
29287
|
}
|
|
29288
|
+
const isKnownDomainEvent = Schema.is(DomainEvent);
|
|
29289
|
+
function passesDomainEventSchema(event) {
|
|
29290
|
+
if (isKnownDomainEvent(event)) return true;
|
|
29291
|
+
console.error(`[ws-rpc] dropping domain event that fails DomainEvent schema validation — add it to the contracts union: type=${event.type} seq=${event.sequence}`);
|
|
29292
|
+
return false;
|
|
29293
|
+
}
|
|
29289
29294
|
function createSystemHeartbeatEvent() {
|
|
29290
29295
|
const ts = Date.now();
|
|
29291
29296
|
return {
|
|
@@ -29719,7 +29724,7 @@ const PanRpcLayer = PanRpcGroup.toLayer(Effect.gen(function* () {
|
|
|
29719
29724
|
[WS_METHODS.subscribeDomainEvents]: (_input) => {
|
|
29720
29725
|
console.log("[ws-rpc] subscribeDomainEvents invoked");
|
|
29721
29726
|
const heartbeats = Stream.tick("15 seconds").pipe(Stream.map(createSystemHeartbeatEvent));
|
|
29722
|
-
return eventStore.streamEvents.pipe(Stream.map(storedToDomainEvent), Stream.merge(heartbeats));
|
|
29727
|
+
return eventStore.streamEvents.pipe(Stream.map(storedToDomainEvent), Stream.filter(passesDomainEventSchema), Stream.merge(heartbeats));
|
|
29723
29728
|
},
|
|
29724
29729
|
[WS_METHODS.subscribeFlywheelStatus]: (_input) => Stream.callback((queue) => Effect.acquireRelease(Effect.promise(async () => {
|
|
29725
29730
|
const latest = await readCurrentLatestFlywheelStatus({ activeRunId: await runDashboardDbJob("getSetting", "flywheel.active_run_id") });
|
|
@@ -29730,7 +29735,7 @@ const PanRpcLayer = PanRpcGroup.toLayer(Effect.gen(function* () {
|
|
|
29730
29735
|
}), (unsubscribe) => Effect.sync(() => unsubscribe()))),
|
|
29731
29736
|
[WS_METHODS.subscribeIssueEvents]: (input) => {
|
|
29732
29737
|
console.log(`[ws-rpc] subscribeIssueEvents invoked issueId=${input.issueId}`);
|
|
29733
|
-
return eventStore.streamEvents.pipe(Stream.map(storedToDomainEvent), Stream.mapEffect((event) => getCachedAgentIssueLookup(readModel).pipe(Effect.map((lookup) => filterDomainEventForIssue(event, input.issueId, lookup)))), Stream.filter((event) => event !== null));
|
|
29738
|
+
return eventStore.streamEvents.pipe(Stream.map(storedToDomainEvent), Stream.filter(passesDomainEventSchema), Stream.mapEffect((event) => getCachedAgentIssueLookup(readModel).pipe(Effect.map((lookup) => filterDomainEventForIssue(event, input.issueId, lookup)))), Stream.filter((event) => event !== null));
|
|
29734
29739
|
},
|
|
29735
29740
|
[WS_METHODS.subscribeTerminal]: (input) => terminalService.streamSession(input.sessionName, input.cols, input.rows),
|
|
29736
29741
|
[WS_METHODS.subscribeAgentOutput]: (input) => eventStore.streamEvents.pipe(Stream.filter((e) => e.type === "agent.output_received" && e.payload["agentId"] === input.agentId), Stream.flatMap((e) => {
|
|
@@ -29752,7 +29757,7 @@ const PanRpcLayer = PanRpcGroup.toLayer(Effect.gen(function* () {
|
|
|
29752
29757
|
message: `Failed to build dashboard snapshot: ${String(cause)}`,
|
|
29753
29758
|
code: "SNAPSHOT_FAILED"
|
|
29754
29759
|
}))),
|
|
29755
|
-
[WS_METHODS.replayEvents]: (input) => eventStore.readFrom(input.fromSequence).pipe(Effect.map((stored) => stored.map(storedToDomainEvent)), Effect.mapError((cause) => new PanRpcError({
|
|
29760
|
+
[WS_METHODS.replayEvents]: (input) => eventStore.readFrom(input.fromSequence).pipe(Effect.map((stored) => stored.map(storedToDomainEvent).filter(passesDomainEventSchema)), Effect.mapError((cause) => new PanRpcError({
|
|
29756
29761
|
message: `Failed to replay events: ${String(cause)}`,
|
|
29757
29762
|
code: "REPLAY_FAILED"
|
|
29758
29763
|
}))),
|
|
@@ -30379,7 +30384,7 @@ function parseIssueIdFromTextSync(value) {
|
|
|
30379
30384
|
//#region services/resource-discovery.ts
|
|
30380
30385
|
init_projects();
|
|
30381
30386
|
init_tmux();
|
|
30382
|
-
const execFileAsync$
|
|
30387
|
+
const execFileAsync$14 = promisify(execFile);
|
|
30383
30388
|
const RESOURCE_DISCOVERY_TTL_MS = 3e4;
|
|
30384
30389
|
const RECENT_ACTIVITY_WINDOW_MS = 5e3;
|
|
30385
30390
|
let cachedResourceIssues = null;
|
|
@@ -30487,7 +30492,7 @@ function isDiscoverableAgentSession(sessionName) {
|
|
|
30487
30492
|
async function loadTrackerIssues() {
|
|
30488
30493
|
const map = /* @__PURE__ */ new Map();
|
|
30489
30494
|
try {
|
|
30490
|
-
const { getSharedIssueService } = await import("./issue-service-singleton-
|
|
30495
|
+
const { getSharedIssueService } = await import("./issue-service-singleton-DFei50PX.js");
|
|
30491
30496
|
const issues = (await getSharedIssueService()).getIssues();
|
|
30492
30497
|
for (const issue of issues) if (issue.identifier) map.set(issue.identifier.toUpperCase(), issue);
|
|
30493
30498
|
} catch {
|
|
@@ -30504,7 +30509,7 @@ async function loadTmuxSessions() {
|
|
|
30504
30509
|
}
|
|
30505
30510
|
async function loadDockerContainers() {
|
|
30506
30511
|
try {
|
|
30507
|
-
const { stdout } = await execFileAsync$
|
|
30512
|
+
const { stdout } = await execFileAsync$14("docker", [
|
|
30508
30513
|
"ps",
|
|
30509
30514
|
"--format",
|
|
30510
30515
|
"{{.Names}}"
|
|
@@ -30522,7 +30527,7 @@ async function loadOpenPullRequests() {
|
|
|
30522
30527
|
const repos = getGitHubConfig()?.repos ?? [];
|
|
30523
30528
|
await Promise.all(repos.map(async (repo) => {
|
|
30524
30529
|
try {
|
|
30525
|
-
const { stdout } = await execFileAsync$
|
|
30530
|
+
const { stdout } = await execFileAsync$14("gh", [
|
|
30526
30531
|
"pr",
|
|
30527
30532
|
"list",
|
|
30528
30533
|
"--repo",
|
|
@@ -30553,7 +30558,7 @@ async function loadOpenPullRequests() {
|
|
|
30553
30558
|
}
|
|
30554
30559
|
async function loadProjectBranches(projectPath) {
|
|
30555
30560
|
try {
|
|
30556
|
-
const [localResult, remoteResult] = await Promise.all([execFileAsync$
|
|
30561
|
+
const [localResult, remoteResult] = await Promise.all([execFileAsync$14("git", [
|
|
30557
30562
|
"for-each-ref",
|
|
30558
30563
|
"refs/heads/feature/*",
|
|
30559
30564
|
"--format=%(refname:short)"
|
|
@@ -30561,7 +30566,7 @@ async function loadProjectBranches(projectPath) {
|
|
|
30561
30566
|
cwd: projectPath,
|
|
30562
30567
|
encoding: "utf-8",
|
|
30563
30568
|
timeout: 1e4
|
|
30564
|
-
}).catch(() => ({ stdout: "" })), execFileAsync$
|
|
30569
|
+
}).catch(() => ({ stdout: "" })), execFileAsync$14("git", [
|
|
30565
30570
|
"for-each-ref",
|
|
30566
30571
|
"refs/remotes/origin/feature/*",
|
|
30567
30572
|
"--format=%(refname:short)"
|
|
@@ -30686,7 +30691,7 @@ async function computeResourceAllocatedIssues() {
|
|
|
30686
30691
|
if (!issue.resourceDetails.tmuxSessions.includes(sessionName)) issue.resourceDetails.tmuxSessions.push(sessionName);
|
|
30687
30692
|
}
|
|
30688
30693
|
try {
|
|
30689
|
-
const { listActiveRemoteAgentStates } = await import("./remote-agents-
|
|
30694
|
+
const { listActiveRemoteAgentStates } = await import("./remote-agents-BXKV5iBS.js");
|
|
30690
30695
|
for (const remoteState of listActiveRemoteAgentStates()) {
|
|
30691
30696
|
const issue = ensureIssue(remoteState.issueId);
|
|
30692
30697
|
if (!issue) continue;
|
|
@@ -30890,7 +30895,7 @@ function groupResourceAllocatedIssuesByProject(issues) {
|
|
|
30890
30895
|
//#region ../../lib/overdeck/issue-reads.ts
|
|
30891
30896
|
init_tracker_config();
|
|
30892
30897
|
init_projects();
|
|
30893
|
-
const execFileAsync$
|
|
30898
|
+
const execFileAsync$13 = promisify(execFile);
|
|
30894
30899
|
function isGitHubIssue$6(issueId) {
|
|
30895
30900
|
const resolved = resolveGitHubIssueSync(issueId);
|
|
30896
30901
|
if (resolved.isGitHub) return {
|
|
@@ -31071,7 +31076,7 @@ function getIssueBeads(id) {
|
|
|
31071
31076
|
const { beads, querySource } = yield* Effect.promise(async () => {
|
|
31072
31077
|
try {
|
|
31073
31078
|
const bdSearchDir = workspacePath && existsSync$1(workspacePath) ? workspacePath : projectPath || homedir$1();
|
|
31074
|
-
const { stdout } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$
|
|
31079
|
+
const { stdout } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$13("bd", [
|
|
31075
31080
|
"list",
|
|
31076
31081
|
"--json",
|
|
31077
31082
|
"-l",
|
|
@@ -31192,7 +31197,7 @@ init_projects();
|
|
|
31192
31197
|
init_remote_agents();
|
|
31193
31198
|
init_tracker_utils();
|
|
31194
31199
|
init_tmux();
|
|
31195
|
-
const execFileAsync$
|
|
31200
|
+
const execFileAsync$12 = promisify(execFile);
|
|
31196
31201
|
function getIssueDataService$9() {
|
|
31197
31202
|
return getSharedIssueService();
|
|
31198
31203
|
}
|
|
@@ -31249,7 +31254,7 @@ async function completePlanningArtifacts(options) {
|
|
|
31249
31254
|
if (workspaceIssueId && workspaceIssueId.toLowerCase() !== issueLower) throw new Error(`Workspace vBRIEF is for ${workspaceIssueId.toUpperCase()}, not ${upperIssueId}`);
|
|
31250
31255
|
assertPlanQuality(workspaceDoc);
|
|
31251
31256
|
const createBeads = options.createBeads ?? (async (path) => {
|
|
31252
|
-
const mod = await import("./beads-
|
|
31257
|
+
const mod = await import("./beads-B4ysfx-Y.js");
|
|
31253
31258
|
return await Effect.runPromise(mod.createBeadsFromVBrief(path));
|
|
31254
31259
|
});
|
|
31255
31260
|
emitCompletePlanningPhase(upperIssueId, "specWrite", "start", "writing proposed vBRIEF spec", { projectPath });
|
|
@@ -31316,8 +31321,72 @@ function completePlanningWorkspaceGitAddCommands(gitRoot) {
|
|
|
31316
31321
|
const commands = [];
|
|
31317
31322
|
if (existsSync$1(join$1(gitRoot, ".pan"))) commands.push(["add", ".pan/"]);
|
|
31318
31323
|
if (existsSync$1(join$1(gitRoot, ".beads"))) commands.push(["add", ".beads/"]);
|
|
31324
|
+
if (existsSync$1(join$1(gitRoot, ".gitignore"))) commands.push(["add", ".gitignore"]);
|
|
31319
31325
|
return commands;
|
|
31320
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
|
+
}
|
|
31321
31390
|
function getInternalDashboardOrigin() {
|
|
31322
31391
|
const port = Number.parseInt(process.env["API_PORT"] ?? process.env["PORT"] ?? "3011", 10);
|
|
31323
31392
|
return process.env["OVERDECK_INTERNAL_DASHBOARD_URL"] ?? `http://127.0.0.1:${port}`;
|
|
@@ -31517,7 +31586,7 @@ async function completePlanningForIssue(options) {
|
|
|
31517
31586
|
const filesToStage = completePlanningFilesToStage(projectPath, proposed.filename);
|
|
31518
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`);
|
|
31519
31588
|
else {
|
|
31520
|
-
const { stdout: branchStdout } = await execFileAsync$
|
|
31589
|
+
const { stdout: branchStdout } = await execFileAsync$12("git", [
|
|
31521
31590
|
"rev-parse",
|
|
31522
31591
|
"--abbrev-ref",
|
|
31523
31592
|
"HEAD"
|
|
@@ -31527,7 +31596,7 @@ async function completePlanningForIssue(options) {
|
|
|
31527
31596
|
});
|
|
31528
31597
|
const currentBranch = branchStdout.trim();
|
|
31529
31598
|
if (currentBranch === "main") {
|
|
31530
|
-
await execFileAsync$
|
|
31599
|
+
await execFileAsync$12("git", [
|
|
31531
31600
|
"add",
|
|
31532
31601
|
"--",
|
|
31533
31602
|
...filesToStage
|
|
@@ -31536,7 +31605,7 @@ async function completePlanningForIssue(options) {
|
|
|
31536
31605
|
encoding: "utf-8"
|
|
31537
31606
|
});
|
|
31538
31607
|
try {
|
|
31539
|
-
await execFileAsync$
|
|
31608
|
+
await execFileAsync$12("git", [
|
|
31540
31609
|
"diff",
|
|
31541
31610
|
"--cached",
|
|
31542
31611
|
"--quiet",
|
|
@@ -31547,7 +31616,7 @@ async function completePlanningForIssue(options) {
|
|
|
31547
31616
|
encoding: "utf-8"
|
|
31548
31617
|
});
|
|
31549
31618
|
} catch {
|
|
31550
|
-
await execFileAsync$
|
|
31619
|
+
await execFileAsync$12("git", [
|
|
31551
31620
|
"commit",
|
|
31552
31621
|
"-m",
|
|
31553
31622
|
`chore(scope): propose ${upperIssueId} vBRIEF`,
|
|
@@ -31560,7 +31629,7 @@ async function completePlanningForIssue(options) {
|
|
|
31560
31629
|
});
|
|
31561
31630
|
console.log(`[complete-planning] Committed pan spec on main for ${upperIssueId}`);
|
|
31562
31631
|
try {
|
|
31563
|
-
const { stdout: remotes } = await execFileAsync$
|
|
31632
|
+
const { stdout: remotes } = await execFileAsync$12("git", ["remote"], {
|
|
31564
31633
|
cwd: projectPath,
|
|
31565
31634
|
encoding: "utf-8"
|
|
31566
31635
|
});
|
|
@@ -31573,16 +31642,16 @@ async function completePlanningForIssue(options) {
|
|
|
31573
31642
|
}
|
|
31574
31643
|
} else console.log(`[complete-planning] Project root not on main (${currentBranch}) — pan spec updated on disk but not committed on main`);
|
|
31575
31644
|
}
|
|
31576
|
-
if (!existsSync$1(join$1(gitRoot, ".git"))) await execFileAsync$
|
|
31645
|
+
if (!existsSync$1(join$1(gitRoot, ".git"))) await execFileAsync$12("git", ["init"], {
|
|
31577
31646
|
cwd: gitRoot,
|
|
31578
31647
|
encoding: "utf-8"
|
|
31579
31648
|
});
|
|
31580
|
-
for (const args of completePlanningWorkspaceGitAddCommands(gitRoot)) await execFileAsync$
|
|
31649
|
+
for (const args of completePlanningWorkspaceGitAddCommands(gitRoot)) await execFileAsync$12("git", args, {
|
|
31581
31650
|
cwd: gitRoot,
|
|
31582
31651
|
encoding: "utf-8"
|
|
31583
31652
|
});
|
|
31584
31653
|
try {
|
|
31585
|
-
await execFileAsync$
|
|
31654
|
+
await execFileAsync$12("git", [
|
|
31586
31655
|
"diff",
|
|
31587
31656
|
"--cached",
|
|
31588
31657
|
"--quiet"
|
|
@@ -31591,7 +31660,7 @@ async function completePlanningForIssue(options) {
|
|
|
31591
31660
|
encoding: "utf-8"
|
|
31592
31661
|
});
|
|
31593
31662
|
} catch {
|
|
31594
|
-
await execFileAsync$
|
|
31663
|
+
await execFileAsync$12("git", [
|
|
31595
31664
|
"commit",
|
|
31596
31665
|
"-m",
|
|
31597
31666
|
`chore(plan): complete planning for ${id}`,
|
|
@@ -31602,7 +31671,7 @@ async function completePlanningForIssue(options) {
|
|
|
31602
31671
|
});
|
|
31603
31672
|
}
|
|
31604
31673
|
try {
|
|
31605
|
-
const { stdout: remotes } = await execFileAsync$
|
|
31674
|
+
const { stdout: remotes } = await execFileAsync$12("git", ["remote"], {
|
|
31606
31675
|
cwd: gitRoot,
|
|
31607
31676
|
encoding: "utf-8"
|
|
31608
31677
|
});
|
|
@@ -31680,6 +31749,7 @@ async function completePlanningForIssue(options) {
|
|
|
31680
31749
|
source: "planning-agent",
|
|
31681
31750
|
eventType: "planning.complete"
|
|
31682
31751
|
});
|
|
31752
|
+
if (autoSpawn && workspacePath) await commitWorkspaceRecordBeforeAutoSpawn(workspacePath, id);
|
|
31683
31753
|
const autoSpawnResult = await completePlanningAutoSpawnAndKill({
|
|
31684
31754
|
issueId: id,
|
|
31685
31755
|
autoSpawn,
|
|
@@ -31779,8 +31849,8 @@ init_tracker_utils();
|
|
|
31779
31849
|
init_agents$1();
|
|
31780
31850
|
init_issue_id();
|
|
31781
31851
|
init_io();
|
|
31782
|
-
const execAsync$
|
|
31783
|
-
const execFileAsync$
|
|
31852
|
+
const execAsync$10 = promisify(exec);
|
|
31853
|
+
const execFileAsync$11 = promisify(execFile);
|
|
31784
31854
|
function isGitHubIssue$4(issueId) {
|
|
31785
31855
|
const resolved = resolveGitHubIssueSync(issueId);
|
|
31786
31856
|
if (resolved.isGitHub) return {
|
|
@@ -31819,7 +31889,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
|
|
|
31819
31889
|
if (!githubCheck.isGitHub || !githubCheck.owner || !githubCheck.repo) return ["No GitHub PR to close"];
|
|
31820
31890
|
const branchName = `feature/${issueId.toLowerCase()}`;
|
|
31821
31891
|
try {
|
|
31822
|
-
const { stdout: prListRaw } = await execFileAsync$
|
|
31892
|
+
const { stdout: prListRaw } = await execFileAsync$11("gh", [
|
|
31823
31893
|
"pr",
|
|
31824
31894
|
"list",
|
|
31825
31895
|
"--repo",
|
|
@@ -31838,7 +31908,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
|
|
|
31838
31908
|
});
|
|
31839
31909
|
const prNumber = prListRaw.trim();
|
|
31840
31910
|
if (!prNumber) return ["No open PR found for branch"];
|
|
31841
|
-
await execFileAsync$
|
|
31911
|
+
await execFileAsync$11("gh", [
|
|
31842
31912
|
"pr",
|
|
31843
31913
|
"close",
|
|
31844
31914
|
prNumber,
|
|
@@ -31851,7 +31921,7 @@ async function closeIssuePullRequest(issueId, reason = "Canceled via Overdeck")
|
|
|
31851
31921
|
timeout: 15e3
|
|
31852
31922
|
});
|
|
31853
31923
|
try {
|
|
31854
|
-
const { setReviewStatusSync } = await import("./review-status-
|
|
31924
|
+
const { setReviewStatusSync } = await import("./review-status-Rr4SB2Kj.js");
|
|
31855
31925
|
setReviewStatusSync(issueId.toUpperCase(), { prUrl: void 0 });
|
|
31856
31926
|
} catch {}
|
|
31857
31927
|
return [`Closed PR #${prNumber} on ${githubCheck.owner}/${githubCheck.repo}`];
|
|
@@ -31905,7 +31975,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
|
|
|
31905
31975
|
const { ctx, projectConfig } = buildLifecycleContext(id, getIssueDataService$8().getIssueSource(id) ?? void 0);
|
|
31906
31976
|
const deleteWorkspace = opts.deleteWorkspace ?? true;
|
|
31907
31977
|
cleanupLog.push(...await closeIssuePullRequest(id, mode === "cancel" ? "Canceled via Overdeck" : "Reset to Todo via Overdeck"));
|
|
31908
|
-
const { resetToTodo, cancelIssueWorkflow } = await import("./lifecycle-
|
|
31978
|
+
const { resetToTodo, cancelIssueWorkflow } = await import("./lifecycle-Va4AZGRx.js");
|
|
31909
31979
|
const workflow = mode === "cancel" ? cancelIssueWorkflow : resetToTodo;
|
|
31910
31980
|
const result = await Effect.runPromise(workflow(ctx, {
|
|
31911
31981
|
deleteWorkspace,
|
|
@@ -31920,7 +31990,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
|
|
|
31920
31990
|
}));
|
|
31921
31991
|
cleanupLog.push(...result.steps.flatMap((step) => step.details || [step.error].filter(Boolean)));
|
|
31922
31992
|
if (mode === "cancel") try {
|
|
31923
|
-
const { transitionVBriefOnMain } = await import("./lifecycle-io-
|
|
31993
|
+
const { transitionVBriefOnMain } = await import("./lifecycle-io-BHi2NPxK.js");
|
|
31924
31994
|
const tx = await Effect.runPromise(transitionVBriefOnMain(ctx.projectPath, id, "cancelled", "cancelled", `scope: cancel ${id.toUpperCase()} vBRIEF`));
|
|
31925
31995
|
if (tx.moved) cleanupLog.push(`vBRIEF moved ${tx.fromDir} → cancelled`);
|
|
31926
31996
|
if (tx.committed) cleanupLog.push(`Committed vBRIEF cancellation on main`);
|
|
@@ -31928,8 +31998,8 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
|
|
|
31928
31998
|
cleanupLog.push(`vBRIEF cancel transition failed (non-fatal): ${err?.message ?? err}`);
|
|
31929
31999
|
}
|
|
31930
32000
|
try {
|
|
31931
|
-
const { killAllReviewerSessions } = await import("./review-agent-
|
|
31932
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
32001
|
+
const { killAllReviewerSessions } = await import("./review-agent-DfN-7KW6.js");
|
|
32002
|
+
const { resolveProjectFromIssueSync } = await import("./projects-BbMEBykK.js");
|
|
31933
32003
|
const projectKey = resolveProjectFromIssueSync(id)?.projectKey;
|
|
31934
32004
|
if (projectKey) {
|
|
31935
32005
|
const { killed } = await Effect.runPromise(killAllReviewerSessions(projectKey, id.toUpperCase()));
|
|
@@ -31943,7 +32013,7 @@ async function runDestructiveIssueLifecycle(id, mode, opts = {}) {
|
|
|
31943
32013
|
cleanupLog.push("Cleared review status");
|
|
31944
32014
|
} catch {}
|
|
31945
32015
|
try {
|
|
31946
|
-
const { resetPostMergeState } = await import("./merge-agent-
|
|
32016
|
+
const { resetPostMergeState } = await import("./merge-agent-DZaRBHQA.js");
|
|
31947
32017
|
resetPostMergeState(id);
|
|
31948
32018
|
resetPostMergeState(id.toUpperCase());
|
|
31949
32019
|
cleanupLog.push("Cleared merge state");
|
|
@@ -31963,7 +32033,7 @@ function closeIssueTransition(options) {
|
|
|
31963
32033
|
const { issueId, body, eventStore } = options;
|
|
31964
32034
|
const { reason } = body;
|
|
31965
32035
|
const projectPath = getProjectPath$3(void 0, extractPrefixSync(issueId) ?? issueId.split("-")[0]);
|
|
31966
|
-
const { close: closeWorkflow } = yield* Effect.promise(() => import("./lifecycle-
|
|
32036
|
+
const { close: closeWorkflow } = yield* Effect.promise(() => import("./lifecycle-Va4AZGRx.js"));
|
|
31967
32037
|
const githubCheck = isGitHubIssue$4(issueId);
|
|
31968
32038
|
const issueDataService = getIssueDataService$8();
|
|
31969
32039
|
const issueSource = issueDataService.getIssueSource(issueId);
|
|
@@ -31986,7 +32056,7 @@ function closeIssueTransition(options) {
|
|
|
31986
32056
|
};
|
|
31987
32057
|
}
|
|
31988
32058
|
const result = yield* closeWorkflow(ctx, { reason });
|
|
31989
|
-
if (githubCheck.isGitHub) execAsync$
|
|
32059
|
+
if (githubCheck.isGitHub) execAsync$10("pan sync", {
|
|
31990
32060
|
encoding: "utf-8",
|
|
31991
32061
|
timeout: 3e4
|
|
31992
32062
|
}).catch(() => {});
|
|
@@ -32197,13 +32267,13 @@ function reopenIssueTransition(options) {
|
|
|
32197
32267
|
}
|
|
32198
32268
|
yield* Effect.promise(async () => {
|
|
32199
32269
|
try {
|
|
32200
|
-
const { stdout } = await execAsync$
|
|
32270
|
+
const { stdout } = await execAsync$10(`gh pr list --head ${`feature/${id.toLowerCase()}`} --state closed --json number,mergedAt --limit 1`, {
|
|
32201
32271
|
encoding: "utf-8",
|
|
32202
32272
|
timeout: 15e3
|
|
32203
32273
|
});
|
|
32204
32274
|
const prs = JSON.parse(stdout.trim() || "[]");
|
|
32205
32275
|
if (prs.length > 0 && !prs[0].mergedAt) {
|
|
32206
|
-
await execAsync$
|
|
32276
|
+
await execAsync$10(`gh pr reopen ${prs[0].number}`, {
|
|
32207
32277
|
encoding: "utf-8",
|
|
32208
32278
|
timeout: 15e3
|
|
32209
32279
|
});
|
|
@@ -32230,7 +32300,7 @@ function reopenIssueTransition(options) {
|
|
|
32230
32300
|
else clearReviewStatus(id.toUpperCase());
|
|
32231
32301
|
} catch {}
|
|
32232
32302
|
try {
|
|
32233
|
-
const { resetPostMergeState } = await import("./merge-agent-
|
|
32303
|
+
const { resetPostMergeState } = await import("./merge-agent-DZaRBHQA.js");
|
|
32234
32304
|
resetPostMergeState(id);
|
|
32235
32305
|
resetPostMergeState(id.toUpperCase());
|
|
32236
32306
|
} catch {}
|
|
@@ -32250,9 +32320,9 @@ function reopenIssueTransition(options) {
|
|
|
32250
32320
|
const projectPath = (teamPrefix ? findProjectByTeamSync(teamPrefix) : null)?.path || "";
|
|
32251
32321
|
if (projectPath) {
|
|
32252
32322
|
const workspacePath = join$1(projectPath, "workspaces", `feature-${issueLower}`);
|
|
32253
|
-
const { createBeadsFromVBrief } = await import("./beads-
|
|
32323
|
+
const { createBeadsFromVBrief } = await import("./beads-B4ysfx-Y.js");
|
|
32254
32324
|
if (existsSync$1(workspacePath) && await Effect.runPromise(findPlan(workspacePath))) try {
|
|
32255
|
-
const { stdout: bdCheck } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$
|
|
32325
|
+
const { stdout: bdCheck } = await Effect.runPromise(withBdMutex(() => Effect.promise(() => execFileAsync$11("bd", [
|
|
32256
32326
|
"list",
|
|
32257
32327
|
"--json",
|
|
32258
32328
|
"-l",
|
|
@@ -32327,7 +32397,7 @@ function moveIssueStatus(options) {
|
|
|
32327
32397
|
"done"
|
|
32328
32398
|
];
|
|
32329
32399
|
if (!targetStatus || !validStatuses.includes(targetStatus)) return jsonResponse({ error: `Invalid targetStatus. Must be one of: ${validStatuses.join(", ")}` }, { status: 400 });
|
|
32330
|
-
const { updateShadowState } = yield* Effect.promise(() => import("./shadow-state-
|
|
32400
|
+
const { updateShadowState } = yield* Effect.promise(() => import("./shadow-state-nDkNqpYC.js"));
|
|
32331
32401
|
const issueState = {
|
|
32332
32402
|
backlog: "open",
|
|
32333
32403
|
todo: "open",
|
|
@@ -32390,7 +32460,7 @@ function moveIssueStatus(options) {
|
|
|
32390
32460
|
init_issue_service_singleton();
|
|
32391
32461
|
init_issue_id();
|
|
32392
32462
|
init_agents$1();
|
|
32393
|
-
const execAsync$
|
|
32463
|
+
const execAsync$9 = promisify(exec);
|
|
32394
32464
|
function getIssueDataService$7() {
|
|
32395
32465
|
return getSharedIssueService();
|
|
32396
32466
|
}
|
|
@@ -32415,28 +32485,28 @@ async function cleanupWorkspaceForIssue(rawId, eventStore) {
|
|
|
32415
32485
|
if (projectRoot) {
|
|
32416
32486
|
const workspacePath = join$1(projectRoot, "workspaces", `feature-${issueLower}`);
|
|
32417
32487
|
try {
|
|
32418
|
-
if ((await execAsync$
|
|
32488
|
+
if ((await execAsync$9("git worktree list --porcelain", {
|
|
32419
32489
|
cwd: projectRoot,
|
|
32420
32490
|
encoding: "utf-8"
|
|
32421
32491
|
})).stdout.includes(workspacePath)) {
|
|
32422
|
-
await execAsync$
|
|
32492
|
+
await execAsync$9(`git worktree remove "${workspacePath}" --force`, {
|
|
32423
32493
|
cwd: projectRoot,
|
|
32424
32494
|
encoding: "utf-8"
|
|
32425
32495
|
});
|
|
32426
32496
|
cleanupLog.push(`Removed git worktree: ${workspacePath}`);
|
|
32427
32497
|
} else if (existsSync$1(workspacePath)) {
|
|
32428
|
-
await execAsync$
|
|
32498
|
+
await execAsync$9(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
|
|
32429
32499
|
cleanupLog.push(`Removed directory: ${workspacePath}`);
|
|
32430
32500
|
}
|
|
32431
32501
|
} catch {
|
|
32432
32502
|
if (existsSync$1(workspacePath)) {
|
|
32433
|
-
await execAsync$
|
|
32503
|
+
await execAsync$9(`rm -rf "${workspacePath}"`, { encoding: "utf-8" });
|
|
32434
32504
|
cleanupLog.push(`Removed directory: ${workspacePath}`);
|
|
32435
32505
|
}
|
|
32436
32506
|
}
|
|
32437
32507
|
const branchName = `feature/${issueLower}`;
|
|
32438
32508
|
try {
|
|
32439
|
-
await execAsync$
|
|
32509
|
+
await execAsync$9(`git branch -D "${branchName}" 2>/dev/null || true`, {
|
|
32440
32510
|
cwd: projectRoot,
|
|
32441
32511
|
encoding: "utf-8"
|
|
32442
32512
|
});
|
|
@@ -32445,7 +32515,7 @@ async function cleanupWorkspaceForIssue(rawId, eventStore) {
|
|
|
32445
32515
|
}
|
|
32446
32516
|
const agentDir = join$1(homedir$1(), ".overdeck", "agents", `agent-${issueLower}`);
|
|
32447
32517
|
if (existsSync$1(agentDir)) {
|
|
32448
|
-
await execAsync$
|
|
32518
|
+
await execAsync$9(`rm -rf "${agentDir}"`, { encoding: "utf-8" });
|
|
32449
32519
|
cleanupLog.push(`Removed agent state: ${agentDir}`);
|
|
32450
32520
|
}
|
|
32451
32521
|
await Effect.runPromise(eventStore.append({
|
|
@@ -32554,7 +32624,7 @@ async function copySettingsToWorkspace(id) {
|
|
|
32554
32624
|
success: false,
|
|
32555
32625
|
error: "Workspace not found"
|
|
32556
32626
|
}, { status: 404 });
|
|
32557
|
-
const { copyOverdeckSettingsToWorkspaceSync } = await import("./workspace-manager-
|
|
32627
|
+
const { copyOverdeckSettingsToWorkspaceSync } = await import("./workspace-manager-BE2H8gD5.js");
|
|
32558
32628
|
const result = copyOverdeckSettingsToWorkspaceSync(workspacePath);
|
|
32559
32629
|
return jsonResponse({
|
|
32560
32630
|
success: result.errors.length === 0 || result.copied.length > 0,
|
|
@@ -32587,7 +32657,7 @@ function generateTasksForIssue(id) {
|
|
|
32587
32657
|
success: false,
|
|
32588
32658
|
error: `No vBRIEF spec found on main for ${id} — run planning first.`
|
|
32589
32659
|
}, { status: 409 });
|
|
32590
|
-
const { createBeadsFromVBrief } = yield* Effect.promise(() => import("./beads-
|
|
32660
|
+
const { createBeadsFromVBrief } = yield* Effect.promise(() => import("./beads-B4ysfx-Y.js"));
|
|
32591
32661
|
const result = yield* createBeadsFromVBrief(workspacePath);
|
|
32592
32662
|
if (!result.success || result.created.length === 0) {
|
|
32593
32663
|
const errors = result.errors.length > 0 ? result.errors : ["Beads creation produced no tasks"];
|
|
@@ -32617,8 +32687,8 @@ init_projects();
|
|
|
32617
32687
|
init_tracker_utils();
|
|
32618
32688
|
init_tmux();
|
|
32619
32689
|
init_typed_errors();
|
|
32620
|
-
const execAsync$
|
|
32621
|
-
const execFileAsync$
|
|
32690
|
+
const execAsync$8 = promisify(exec);
|
|
32691
|
+
const execFileAsync$10 = promisify(execFile);
|
|
32622
32692
|
function getIssueDataService$6() {
|
|
32623
32693
|
return getSharedIssueService();
|
|
32624
32694
|
}
|
|
@@ -32918,7 +32988,7 @@ function abortPlanningForIssue(options) {
|
|
|
32918
32988
|
const featureWorkspacePath = join$1(projectPath, "workspaces", `feature-${issueIdentifier.toLowerCase()}`);
|
|
32919
32989
|
const plainWorkspacePath = join$1(projectPath, "workspaces", issueIdentifier.toLowerCase());
|
|
32920
32990
|
if (existsSync$1(existsSync$1(featureWorkspacePath) ? featureWorkspacePath : plainWorkspacePath)) {
|
|
32921
|
-
await execFileAsync$
|
|
32991
|
+
await execFileAsync$10("pan", [
|
|
32922
32992
|
"workspace",
|
|
32923
32993
|
"destroy",
|
|
32924
32994
|
issueIdentifier.toLowerCase(),
|
|
@@ -33027,10 +33097,10 @@ function restartFromPlan(options) {
|
|
|
33027
33097
|
});
|
|
33028
33098
|
const resetResult = yield* Effect.promise(async () => {
|
|
33029
33099
|
try {
|
|
33030
|
-
const { runGitResetHard } = await import("./dangerous-git-ops-
|
|
33100
|
+
const { runGitResetHard } = await import("./dangerous-git-ops-C9iYTZ0M.js");
|
|
33031
33101
|
async function findPlanningCommit(grep, label) {
|
|
33032
33102
|
try {
|
|
33033
|
-
const { stdout } = await execAsync$
|
|
33103
|
+
const { stdout } = await execAsync$8(`git log --grep="${grep.replace(/"/g, "\\\"")}" --format=%H -1`, {
|
|
33034
33104
|
cwd: workspacePath,
|
|
33035
33105
|
encoding: "utf-8",
|
|
33036
33106
|
timeout: 1e4
|
|
@@ -33046,7 +33116,7 @@ function restartFromPlan(options) {
|
|
|
33046
33116
|
}
|
|
33047
33117
|
const found = await findPlanningCommit(`Complete planning for ${id}`, "complete-planning message") ?? await findPlanningCommit(`chore: planning artifacts for ${id}`, "agent-start message") ?? await (async () => {
|
|
33048
33118
|
try {
|
|
33049
|
-
const { stdout } = await execAsync$
|
|
33119
|
+
const { stdout } = await execAsync$8(`git log --diff-filter=A --format=%H -1 -- .pan/spec.vbrief.json`, {
|
|
33050
33120
|
cwd: workspacePath,
|
|
33051
33121
|
encoding: "utf-8",
|
|
33052
33122
|
timeout: 1e4
|
|
@@ -33173,6 +33243,7 @@ function getPlanningState(id) {
|
|
|
33173
33243
|
}
|
|
33174
33244
|
//#endregion
|
|
33175
33245
|
//#region ../../lib/overdeck/discussions.ts
|
|
33246
|
+
init_cost_sync();
|
|
33176
33247
|
init_github_app();
|
|
33177
33248
|
init_pull_requests();
|
|
33178
33249
|
init_issue_id();
|
|
@@ -33180,7 +33251,7 @@ init_github_pr_lookup();
|
|
|
33180
33251
|
init_tracker_utils();
|
|
33181
33252
|
init_tracker_config();
|
|
33182
33253
|
init_pr_tab_cache();
|
|
33183
|
-
const execAsync$
|
|
33254
|
+
const execAsync$7 = promisify(exec);
|
|
33184
33255
|
function isGitHubIssue$2(issueId) {
|
|
33185
33256
|
const resolved = resolveGitHubIssueSync(issueId);
|
|
33186
33257
|
if (resolved.isGitHub) return {
|
|
@@ -33228,7 +33299,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
|
|
|
33228
33299
|
if (githubCheck.isGitHub && githubCheck.owner && githubCheck.repo && githubCheck.number) {
|
|
33229
33300
|
const { owner, repo, number } = githubCheck;
|
|
33230
33301
|
try {
|
|
33231
|
-
const { stdout } = await execAsync$
|
|
33302
|
+
const { stdout } = await execAsync$7(`gh api "repos/${owner}/${repo}/issues/${number}/comments?per_page=100"`, {
|
|
33232
33303
|
encoding: "utf-8",
|
|
33233
33304
|
timeout: 15e3,
|
|
33234
33305
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -33291,7 +33362,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
|
|
|
33291
33362
|
const collectedItems = [];
|
|
33292
33363
|
const prConversation = (async () => {
|
|
33293
33364
|
try {
|
|
33294
|
-
const { stdout } = await execAsync$
|
|
33365
|
+
const { stdout } = await execAsync$7(`gh api "repos/${prOwner}/${prRepo}/issues/${prNumber}/comments?per_page=100"`, {
|
|
33295
33366
|
encoding: "utf-8",
|
|
33296
33367
|
timeout: 15e3,
|
|
33297
33368
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -33312,7 +33383,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
|
|
|
33312
33383
|
})();
|
|
33313
33384
|
const prReviews = (async () => {
|
|
33314
33385
|
try {
|
|
33315
|
-
const { stdout } = await execAsync$
|
|
33386
|
+
const { stdout } = await execAsync$7(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/reviews?per_page=100"`, {
|
|
33316
33387
|
encoding: "utf-8",
|
|
33317
33388
|
timeout: 15e3,
|
|
33318
33389
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -33337,7 +33408,7 @@ async function fetchIssueDiscussions(issueId, deps = {}) {
|
|
|
33337
33408
|
})();
|
|
33338
33409
|
const prInlineComments = (async () => {
|
|
33339
33410
|
try {
|
|
33340
|
-
const { stdout } = await execAsync$
|
|
33411
|
+
const { stdout } = await execAsync$7(`gh api "repos/${prOwner}/${prRepo}/pulls/${prNumber}/comments?per_page=100"`, {
|
|
33341
33412
|
encoding: "utf-8",
|
|
33342
33413
|
timeout: 15e3,
|
|
33343
33414
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -33479,7 +33550,7 @@ function closeOutIssue(id) {
|
|
|
33479
33550
|
}
|
|
33480
33551
|
const closeOutResult = yield* Effect.promise(async () => {
|
|
33481
33552
|
try {
|
|
33482
|
-
const { closeOut } = await import("./lifecycle-
|
|
33553
|
+
const { closeOut } = await import("./lifecycle-Va4AZGRx.js");
|
|
33483
33554
|
return {
|
|
33484
33555
|
ok: true,
|
|
33485
33556
|
result: await Effect.runPromise(closeOut(ctx))
|
|
@@ -33565,7 +33636,7 @@ function bulkCloseOut(body) {
|
|
|
33565
33636
|
const invalidIds = issueIds.filter((id) => !isValidIssueId(id));
|
|
33566
33637
|
if (invalidIds.length > 0) return jsonResponse({ error: `Invalid issue ID format: ${invalidIds.join(", ")}` }, { status: 400 });
|
|
33567
33638
|
const eventStore = yield* EventStoreService;
|
|
33568
|
-
const { closeOut } = yield* Effect.promise(() => import("./lifecycle-
|
|
33639
|
+
const { closeOut } = yield* Effect.promise(() => import("./lifecycle-Va4AZGRx.js"));
|
|
33569
33640
|
const issueDataService = getIssueDataService$5();
|
|
33570
33641
|
const tasks = [];
|
|
33571
33642
|
const agentChecks = yield* withConcurrencyLimit(issueIds.map((id) => Effect.promise(async () => {
|
|
@@ -33732,7 +33803,7 @@ promisify(execFile);
|
|
|
33732
33803
|
function getIssueDataService$4() {
|
|
33733
33804
|
return getSharedIssueService();
|
|
33734
33805
|
}
|
|
33735
|
-
const readJsonBody$
|
|
33806
|
+
const readJsonBody$15 = Effect.gen(function* () {
|
|
33736
33807
|
const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
|
|
33737
33808
|
try {
|
|
33738
33809
|
return text ? JSON.parse(text) : {};
|
|
@@ -33757,12 +33828,28 @@ const getIssueAnalyzeRoute = HttpRouter.add("GET", "/api/issues/:id/analyze", ht
|
|
|
33757
33828
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33758
33829
|
return yield* analyzeIssue(id);
|
|
33759
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
|
+
})));
|
|
33760
33847
|
const postIssueCloseRoute = HttpRouter.add("POST", "/api/issues/:issueId/close", httpHandler(Effect.gen(function* () {
|
|
33761
33848
|
const issueId = (yield* HttpRouter.params)["issueId"] ?? "";
|
|
33762
33849
|
if (!parseIssueIdSync(issueId)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33763
33850
|
return yield* closeIssueTransition({
|
|
33764
33851
|
issueId,
|
|
33765
|
-
body: yield* readJsonBody$
|
|
33852
|
+
body: yield* readJsonBody$15,
|
|
33766
33853
|
eventStore: yield* EventStoreService
|
|
33767
33854
|
});
|
|
33768
33855
|
})));
|
|
@@ -33771,7 +33858,7 @@ const postIssueStartPlanningRoute = HttpRouter.add("POST", "/api/issues/:id/star
|
|
|
33771
33858
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33772
33859
|
return yield* startPlanningForIssue({
|
|
33773
33860
|
id,
|
|
33774
|
-
body: yield* readJsonBody$
|
|
33861
|
+
body: yield* readJsonBody$15,
|
|
33775
33862
|
eventStore: yield* EventStoreService,
|
|
33776
33863
|
linear: yield* LinearClient,
|
|
33777
33864
|
github: yield* GitHubClient,
|
|
@@ -33784,7 +33871,7 @@ const postIssueAbortPlanningRoute = HttpRouter.add("POST", "/api/issues/:id/abor
|
|
|
33784
33871
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33785
33872
|
return yield* abortPlanningForIssue({
|
|
33786
33873
|
id,
|
|
33787
|
-
body: yield* readJsonBody$
|
|
33874
|
+
body: yield* readJsonBody$15,
|
|
33788
33875
|
lifecycle: yield* IssueLifecycle,
|
|
33789
33876
|
linear: yield* LinearClient,
|
|
33790
33877
|
eventStore: yield* EventStoreService
|
|
@@ -33794,7 +33881,7 @@ const postIssueCompletePlanningRoute = HttpRouter.add("POST", "/api/issues/:id/c
|
|
|
33794
33881
|
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
33795
33882
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
33796
33883
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33797
|
-
const body = yield* readJsonBody$
|
|
33884
|
+
const body = yield* readJsonBody$15;
|
|
33798
33885
|
const eventStore = yield* EventStoreService;
|
|
33799
33886
|
const linear = yield* LinearClient;
|
|
33800
33887
|
const lifecycle = yield* IssueLifecycle;
|
|
@@ -33820,7 +33907,7 @@ const postIssueResetRoute = HttpRouter.add("POST", "/api/issues/:id/reset", http
|
|
|
33820
33907
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33821
33908
|
return yield* resetIssueTransition({
|
|
33822
33909
|
id,
|
|
33823
|
-
body: yield* readJsonBody$
|
|
33910
|
+
body: yield* readJsonBody$15,
|
|
33824
33911
|
eventStore: yield* EventStoreService
|
|
33825
33912
|
});
|
|
33826
33913
|
})));
|
|
@@ -33829,7 +33916,7 @@ const postIssueCancelRoute = HttpRouter.add("POST", "/api/issues/:id/cancel", ht
|
|
|
33829
33916
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33830
33917
|
return yield* cancelIssueTransition({
|
|
33831
33918
|
id,
|
|
33832
|
-
body: yield* readJsonBody$
|
|
33919
|
+
body: yield* readJsonBody$15,
|
|
33833
33920
|
eventStore: yield* EventStoreService
|
|
33834
33921
|
});
|
|
33835
33922
|
})));
|
|
@@ -33840,7 +33927,7 @@ const postIssueReopenRoute = HttpRouter.add("POST", "/api/issues/:id/reopen", ht
|
|
|
33840
33927
|
if (authError) return authError;
|
|
33841
33928
|
return yield* reopenIssueTransition({
|
|
33842
33929
|
id,
|
|
33843
|
-
body: yield* readJsonBody$
|
|
33930
|
+
body: yield* readJsonBody$15,
|
|
33844
33931
|
lifecycle: yield* IssueLifecycle,
|
|
33845
33932
|
linear: yield* LinearClient,
|
|
33846
33933
|
eventStore: yield* EventStoreService
|
|
@@ -33860,7 +33947,7 @@ const postIssueMoveStatusRoute = HttpRouter.add("POST", "/api/issues/:id/move-st
|
|
|
33860
33947
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33861
33948
|
return yield* moveIssueStatus({
|
|
33862
33949
|
id,
|
|
33863
|
-
body: yield* readJsonBody$
|
|
33950
|
+
body: yield* readJsonBody$15,
|
|
33864
33951
|
eventStore: yield* EventStoreService,
|
|
33865
33952
|
lifecycle: yield* IssueLifecycle
|
|
33866
33953
|
});
|
|
@@ -33872,7 +33959,7 @@ const postIssueCleanupWorkspaceRoute = HttpRouter.add("POST", "/api/issues/:id/c
|
|
|
33872
33959
|
})));
|
|
33873
33960
|
const postIssueDeepWipeRoute = HttpRouter.add("POST", "/api/issues/:id/deep-wipe", httpHandler(Effect.gen(function* () {
|
|
33874
33961
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
33875
|
-
const body = yield* readJsonBody$
|
|
33962
|
+
const body = yield* readJsonBody$15;
|
|
33876
33963
|
const eventStore = yield* EventStoreService;
|
|
33877
33964
|
return yield* Effect.promise(() => deepWipeIssue(id, body, eventStore));
|
|
33878
33965
|
})));
|
|
@@ -33912,7 +33999,7 @@ const postIssueBeadInspectRoute = HttpRouter.add("POST", "/api/issues/:id/beads/
|
|
|
33912
33999
|
return yield* inspectIssueBead({
|
|
33913
34000
|
id: (params["id"] ?? "").toUpperCase(),
|
|
33914
34001
|
beadId: params["beadId"] ?? "",
|
|
33915
|
-
body: yield* readJsonBody$
|
|
34002
|
+
body: yield* readJsonBody$15
|
|
33916
34003
|
});
|
|
33917
34004
|
})));
|
|
33918
34005
|
const getIssuePlanningStateRoute = HttpRouter.add("GET", "/api/issues/:id/planning-state", httpHandler(Effect.gen(function* () {
|
|
@@ -33975,7 +34062,7 @@ const getIssueDiscussionsRoute = HttpRouter.add("GET", "/api/issues/:id/discussi
|
|
|
33975
34062
|
const getIssueCostsRoute = HttpRouter.add("GET", "/api/issues/:id/costs", httpHandler(Effect.gen(function* () {
|
|
33976
34063
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
33977
34064
|
if (!parseIssueIdSync(id)) return jsonResponse({ error: "Invalid issue ID" }, { status: 400 });
|
|
33978
|
-
const issueData =
|
|
34065
|
+
const issueData = getCostForIssueAggregateSync(id);
|
|
33979
34066
|
const agents = yield* Effect.promise(() => getCachedRunningAgents());
|
|
33980
34067
|
const resolvedCost = resolveIssueHeadlineCost({
|
|
33981
34068
|
issueId: id,
|
|
@@ -34023,7 +34110,7 @@ const getIssueCostsRoute = HttpRouter.add("GET", "/api/issues/:id/costs", httpHa
|
|
|
34023
34110
|
cost: stats.cost,
|
|
34024
34111
|
tokens: stats.tokens
|
|
34025
34112
|
}])),
|
|
34026
|
-
budget:
|
|
34113
|
+
budget: void 0,
|
|
34027
34114
|
budgetWarning: issueData.budgetWarning,
|
|
34028
34115
|
lastUpdated: issueData.lastUpdated
|
|
34029
34116
|
});
|
|
@@ -34034,7 +34121,7 @@ const getResourceAllocatedIssuesRoute = HttpRouter.add("GET", "/api/issues/resou
|
|
|
34034
34121
|
const getIssueResourceDetailsRoute = HttpRouter.add("GET", "/api/issues/:id/resource-details", httpHandler(Effect.gen(function* () {
|
|
34035
34122
|
return yield* getIssueResourceDetails((yield* HttpRouter.params)["id"] ?? "");
|
|
34036
34123
|
})));
|
|
34037
|
-
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);
|
|
34038
34125
|
//#endregion
|
|
34039
34126
|
//#region routes/costs.ts
|
|
34040
34127
|
/**
|
|
@@ -34069,7 +34156,7 @@ const getCostsSummaryRoute = HttpRouter.add("GET", "/api/costs/summary", httpHan
|
|
|
34069
34156
|
const monthEntries = scope(readEventsSync({ startDate: monthAgo }));
|
|
34070
34157
|
const summarize = (entries) => ({
|
|
34071
34158
|
totalCost: entries.reduce((sum, e) => sum + (e.cost || 0), 0),
|
|
34072
|
-
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),
|
|
34073
34160
|
entryCount: entries.length,
|
|
34074
34161
|
byModel: entries.reduce((acc, e) => {
|
|
34075
34162
|
if (e.model) acc[e.model] = (acc[e.model] || 0) + (e.cost || 0);
|
|
@@ -34256,16 +34343,45 @@ const postCostsSyncWalRoute = HttpRouter.add("POST", "/api/costs/sync-wal", http
|
|
|
34256
34343
|
})
|
|
34257
34344
|
});
|
|
34258
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
|
+
}
|
|
34259
34378
|
const postCostsReconcileRoute = HttpRouter.add("POST", "/api/costs/reconcile", httpHandler(Effect.gen(function* () {
|
|
34260
|
-
const
|
|
34261
|
-
try: () =>
|
|
34379
|
+
const overdeck = yield* Effect.tryPromise({
|
|
34380
|
+
try: () => runCostReconcileSources(),
|
|
34262
34381
|
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34263
34382
|
});
|
|
34264
|
-
console.log(`[reconciler] Sweep complete:
|
|
34265
|
-
return jsonResponse(
|
|
34266
|
-
success: true,
|
|
34267
|
-
...result
|
|
34268
|
-
});
|
|
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));
|
|
34269
34385
|
})));
|
|
34270
34386
|
const getCostsExperimentsRoute = HttpRouter.add("GET", "/api/costs/experiments", httpHandler(Effect.try({
|
|
34271
34387
|
try: () => jsonResponse({ experiments: getCavemanExperimentDataSync() }),
|
|
@@ -34542,7 +34658,7 @@ function reloadDurableCloisterConfig(deps = {}) {
|
|
|
34542
34658
|
*/
|
|
34543
34659
|
init_config();
|
|
34544
34660
|
init_agents$1();
|
|
34545
|
-
const readJsonBody$
|
|
34661
|
+
const readJsonBody$14 = Effect.gen(function* () {
|
|
34546
34662
|
const text = yield* (yield* HttpServerRequest.HttpServerRequest).text;
|
|
34547
34663
|
try {
|
|
34548
34664
|
return text ? JSON.parse(text) : {};
|
|
@@ -34645,7 +34761,7 @@ const getCloisterConfigRoute = HttpRouter.add("GET", "/api/cloister/config", htt
|
|
|
34645
34761
|
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34646
34762
|
})));
|
|
34647
34763
|
const putCloisterConfigRoute = HttpRouter.add("PUT", "/api/cloister/config", httpHandler(Effect.gen(function* () {
|
|
34648
|
-
const updates = yield* readJsonBody$
|
|
34764
|
+
const updates = yield* readJsonBody$14;
|
|
34649
34765
|
yield* Effect.try({
|
|
34650
34766
|
try: () => {
|
|
34651
34767
|
saveCloisterConfigSync(updates);
|
|
@@ -34669,25 +34785,18 @@ const getCloisterAgentsHealthRoute = HttpRouter.add("GET", "/api/cloister/agents
|
|
|
34669
34785
|
})));
|
|
34670
34786
|
const cloisterRouteLayer = Layer.mergeAll(getCloisterStatusRoute, postCloisterStartRoute, postCloisterStopRoute, postCloisterEmergencyStopRoute, postCloisterBrakeRoute, postCloisterResumeSpawnsRoute, getCloisterSpawnStatusRoute, getCloisterConfigRoute, putCloisterConfigRoute, getCloisterAgentsHealthRoute);
|
|
34671
34787
|
//#endregion
|
|
34672
|
-
//#region routes/resources.ts
|
|
34673
|
-
/**
|
|
34674
|
-
* Resources route module — Effect HttpRouter.Layer (PAN-428 B12)
|
|
34675
|
-
*
|
|
34676
|
-
* Implements all /api/resources/* endpoints from the Express server:
|
|
34677
|
-
* GET /api/resources
|
|
34678
|
-
* GET /api/resources/:containerId/history
|
|
34679
|
-
* GET /api/resources/:containerId/details
|
|
34680
|
-
* DELETE /api/resources/docker/container/:id
|
|
34681
|
-
* POST /api/resources/docker/prune-containers
|
|
34682
|
-
* DELETE /api/resources/docker/network/:name
|
|
34683
|
-
* DELETE /api/resources/docker/volume/:name
|
|
34684
|
-
* POST /api/resources/docker/prune-volumes
|
|
34685
|
-
*/
|
|
34788
|
+
//#region routes/resources/shared.ts
|
|
34686
34789
|
init_docker_stats();
|
|
34687
|
-
|
|
34688
|
-
|
|
34689
|
-
|
|
34690
|
-
|
|
34790
|
+
function getCurrentDockerStats() {
|
|
34791
|
+
return [];
|
|
34792
|
+
}
|
|
34793
|
+
function getContainerHistory(containerId) {
|
|
34794
|
+
return {
|
|
34795
|
+
timestamps: [],
|
|
34796
|
+
cpuPercent: [],
|
|
34797
|
+
memoryPercent: []
|
|
34798
|
+
};
|
|
34799
|
+
}
|
|
34691
34800
|
function formatUptime(startedAt) {
|
|
34692
34801
|
const ms = Date.now() - new Date(startedAt).getTime();
|
|
34693
34802
|
const h = Math.floor(ms / 36e5);
|
|
@@ -34696,45 +34805,933 @@ function formatUptime(startedAt) {
|
|
|
34696
34805
|
if (h > 0) return `${h}h ${m}m`;
|
|
34697
34806
|
return `${m}m`;
|
|
34698
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();
|
|
34699
35648
|
/** Build the GET /api/resources response from the SQLite agents table. */
|
|
34700
35649
|
function getResourcesEffect() {
|
|
34701
35650
|
return Effect.gen(function* () {
|
|
34702
|
-
const containers =
|
|
35651
|
+
const containers = enrichContainersWithLimits(getCurrentDockerStats());
|
|
35652
|
+
const agentStats = yield* getAgentStatsSnapshotEffect();
|
|
35653
|
+
const spawnGate = yield* getSpawnGatePayloadEffect();
|
|
34703
35654
|
const stoppedContainers = [];
|
|
34704
35655
|
const sessions = yield* listSessions().pipe(Effect.catch(() => Effect.succeed([])));
|
|
34705
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
|
+
};
|
|
34706
35680
|
return jsonResponse({
|
|
35681
|
+
agents: agents.map((agent) => ({
|
|
35682
|
+
...agent,
|
|
35683
|
+
resourceStats: agentStatsById.get(String(agent.id)) ?? null
|
|
35684
|
+
})),
|
|
35685
|
+
coreServices: getCoreServicesSnapshot(),
|
|
34707
35686
|
containers,
|
|
35687
|
+
forecast: buildCapacityForecast(stacks, { hostVitals }),
|
|
35688
|
+
hostVitals,
|
|
35689
|
+
hostProcesses: getHostProcessesSnapshot(),
|
|
34708
35690
|
stoppedContainers,
|
|
34709
35691
|
networks: [],
|
|
35692
|
+
reclaimCandidates: reclaim.reclaimCandidates,
|
|
35693
|
+
reclaimThresholdBytes: reclaim.reclaimThresholdBytes,
|
|
35694
|
+
reclaimTotals: reclaim.reclaimTotals,
|
|
35695
|
+
spawnGate,
|
|
35696
|
+
stacks,
|
|
34710
35697
|
volumes: [],
|
|
34711
|
-
agents: listAgentStates().filter((state) => state.status !== "stopped").map((state) => ({
|
|
34712
|
-
...state,
|
|
34713
|
-
hasLiveTmuxSession: tmuxSessionNames.has(state.id)
|
|
34714
|
-
})),
|
|
34715
35698
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
34716
35699
|
});
|
|
34717
35700
|
});
|
|
34718
35701
|
}
|
|
34719
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
|
+
}
|
|
34720
35711
|
const getContainerHistoryRoute = HttpRouter.add("GET", "/api/resources/:containerId/history", Effect.gen(function* () {
|
|
34721
35712
|
const containerId = (yield* HttpRouter.params)["containerId"] ?? "";
|
|
34722
35713
|
if (!/^[a-f0-9]{12,64}$/.test(containerId)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34723
|
-
return jsonResponse(
|
|
34724
|
-
timestamps: [],
|
|
34725
|
-
cpuPercent: [],
|
|
34726
|
-
memoryPercent: []
|
|
34727
|
-
});
|
|
35714
|
+
return jsonResponse(getContainerHistory(containerId));
|
|
34728
35715
|
}));
|
|
34729
35716
|
const getContainerDetailsRoute = HttpRouter.add("GET", "/api/resources/:containerId/details", Effect.gen(function* () {
|
|
34730
35717
|
const containerId = (yield* HttpRouter.params)["containerId"] ?? "";
|
|
34731
35718
|
if (!/^[a-f0-9]{12,64}$/.test(containerId)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34732
35719
|
return yield* httpHandler(Effect.gen(function* () {
|
|
34733
35720
|
const [inspectResult, logsResult] = yield* Effect.tryPromise({
|
|
34734
|
-
try: () => Promise.all([
|
|
35721
|
+
try: () => Promise.all([dockerExec("docker", [
|
|
35722
|
+
"inspect",
|
|
35723
|
+
"--format",
|
|
35724
|
+
"{{json .}}",
|
|
35725
|
+
containerId
|
|
35726
|
+
], {
|
|
34735
35727
|
encoding: "utf-8",
|
|
34736
35728
|
timeout: 5e3
|
|
34737
|
-
}).catch(() => ({ stdout: "null" })),
|
|
35729
|
+
}).catch(() => ({ stdout: "null" })), dockerExec("docker", [
|
|
35730
|
+
"logs",
|
|
35731
|
+
"--tail",
|
|
35732
|
+
"100",
|
|
35733
|
+
containerId
|
|
35734
|
+
], {
|
|
34738
35735
|
encoding: "utf-8",
|
|
34739
35736
|
timeout: 5e3
|
|
34740
35737
|
}).catch(() => ({ stdout: "" }))]),
|
|
@@ -34773,7 +35770,7 @@ const deleteDockerContainerRoute = HttpRouter.add("DELETE", "/api/resources/dock
|
|
|
34773
35770
|
const eventStore = yield* EventStoreService;
|
|
34774
35771
|
if (!/^[a-f0-9]{12,64}$/.test(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34775
35772
|
yield* Effect.tryPromise({
|
|
34776
|
-
try: () =>
|
|
35773
|
+
try: () => dockerExec("docker", ["rm", id], {
|
|
34777
35774
|
encoding: "utf-8",
|
|
34778
35775
|
timeout: 1e4
|
|
34779
35776
|
}),
|
|
@@ -34782,90 +35779,16 @@ const deleteDockerContainerRoute = HttpRouter.add("DELETE", "/api/resources/dock
|
|
|
34782
35779
|
yield* eventStore.append({
|
|
34783
35780
|
type: "resources.updated",
|
|
34784
35781
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34785
|
-
payload: { resources: { containers:
|
|
35782
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
34786
35783
|
});
|
|
34787
35784
|
return jsonResponse({ ok: true });
|
|
34788
35785
|
})));
|
|
34789
|
-
const postPruneContainersRoute = HttpRouter.add("POST", "/api/resources/docker/prune-containers", httpHandler(Effect.gen(function* () {
|
|
34790
|
-
const eventStore = yield* EventStoreService;
|
|
34791
|
-
const { stdout } = yield* Effect.tryPromise({
|
|
34792
|
-
try: () => execAsync$7("docker container prune -f 2>&1", {
|
|
34793
|
-
encoding: "utf-8",
|
|
34794
|
-
timeout: 3e4
|
|
34795
|
-
}),
|
|
34796
|
-
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34797
|
-
});
|
|
34798
|
-
yield* eventStore.append({
|
|
34799
|
-
type: "resources.updated",
|
|
34800
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34801
|
-
payload: { resources: { containers: [] } }
|
|
34802
|
-
});
|
|
34803
|
-
return jsonResponse({
|
|
34804
|
-
ok: true,
|
|
34805
|
-
output: stdout.trim()
|
|
34806
|
-
});
|
|
34807
|
-
})));
|
|
34808
|
-
const deleteDockerNetworkRoute = HttpRouter.add("DELETE", "/api/resources/docker/network/:name", httpHandler(Effect.gen(function* () {
|
|
34809
|
-
const name = (yield* HttpRouter.params)["name"] ?? "";
|
|
34810
|
-
const eventStore = yield* EventStoreService;
|
|
34811
|
-
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid network name" }, { status: 400 });
|
|
34812
|
-
yield* Effect.tryPromise({
|
|
34813
|
-
try: () => execAsync$7(`docker network rm "${name}" 2>&1`, {
|
|
34814
|
-
encoding: "utf-8",
|
|
34815
|
-
timeout: 1e4
|
|
34816
|
-
}),
|
|
34817
|
-
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34818
|
-
});
|
|
34819
|
-
yield* eventStore.append({
|
|
34820
|
-
type: "resources.updated",
|
|
34821
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34822
|
-
payload: { resources: { containers: [] } }
|
|
34823
|
-
});
|
|
34824
|
-
return jsonResponse({ ok: true });
|
|
34825
|
-
})));
|
|
34826
|
-
const deleteDockerVolumeRoute = HttpRouter.add("DELETE", "/api/resources/docker/volume/:name", httpHandler(Effect.gen(function* () {
|
|
34827
|
-
const name = (yield* HttpRouter.params)["name"] ?? "";
|
|
34828
|
-
const eventStore = yield* EventStoreService;
|
|
34829
|
-
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name)) return jsonResponse({ error: "Invalid volume name" }, { status: 400 });
|
|
34830
|
-
yield* Effect.tryPromise({
|
|
34831
|
-
try: () => execAsync$7(`docker volume rm "${name}" 2>&1`, {
|
|
34832
|
-
encoding: "utf-8",
|
|
34833
|
-
timeout: 1e4
|
|
34834
|
-
}),
|
|
34835
|
-
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34836
|
-
});
|
|
34837
|
-
yield* eventStore.append({
|
|
34838
|
-
type: "resources.updated",
|
|
34839
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34840
|
-
payload: { resources: { containers: [] } }
|
|
34841
|
-
});
|
|
34842
|
-
return jsonResponse({ ok: true });
|
|
34843
|
-
})));
|
|
34844
|
-
const postPruneVolumesRoute = HttpRouter.add("POST", "/api/resources/docker/prune-volumes", httpHandler(Effect.gen(function* () {
|
|
34845
|
-
const eventStore = yield* EventStoreService;
|
|
34846
|
-
const { stdout } = yield* Effect.tryPromise({
|
|
34847
|
-
try: () => execAsync$7("docker volume prune -f 2>&1", {
|
|
34848
|
-
encoding: "utf-8",
|
|
34849
|
-
timeout: 3e4
|
|
34850
|
-
}),
|
|
34851
|
-
catch: (err) => new Error(err instanceof Error ? err.message : String(err))
|
|
34852
|
-
});
|
|
34853
|
-
yield* eventStore.append({
|
|
34854
|
-
type: "resources.updated",
|
|
34855
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34856
|
-
payload: { resources: { containers: [] } }
|
|
34857
|
-
});
|
|
34858
|
-
return jsonResponse({
|
|
34859
|
-
ok: true,
|
|
34860
|
-
output: stdout.trim()
|
|
34861
|
-
});
|
|
34862
|
-
})));
|
|
34863
35786
|
const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/restart", httpHandler(Effect.gen(function* () {
|
|
34864
35787
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
34865
35788
|
const eventStore = yield* EventStoreService;
|
|
34866
|
-
if (!id) return jsonResponse({ error: "
|
|
35789
|
+
if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34867
35790
|
const { stdout } = yield* Effect.tryPromise({
|
|
34868
|
-
try: () =>
|
|
35791
|
+
try: () => dockerExec("docker", ["restart", id], {
|
|
34869
35792
|
encoding: "utf-8",
|
|
34870
35793
|
timeout: 3e4
|
|
34871
35794
|
}),
|
|
@@ -34874,7 +35797,7 @@ const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/
|
|
|
34874
35797
|
yield* eventStore.append({
|
|
34875
35798
|
type: "resources.updated",
|
|
34876
35799
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34877
|
-
payload: { resources: { containers:
|
|
35800
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
34878
35801
|
});
|
|
34879
35802
|
return jsonResponse({
|
|
34880
35803
|
ok: true,
|
|
@@ -34885,9 +35808,9 @@ const postRestartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/
|
|
|
34885
35808
|
const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/container/:id/start", httpHandler(Effect.gen(function* () {
|
|
34886
35809
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
34887
35810
|
const eventStore = yield* EventStoreService;
|
|
34888
|
-
if (!id) return jsonResponse({ error: "
|
|
35811
|
+
if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34889
35812
|
const { stdout } = yield* Effect.tryPromise({
|
|
34890
|
-
try: () =>
|
|
35813
|
+
try: () => dockerExec("docker", ["start", id], {
|
|
34891
35814
|
encoding: "utf-8",
|
|
34892
35815
|
timeout: 3e4
|
|
34893
35816
|
}),
|
|
@@ -34896,7 +35819,7 @@ const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/co
|
|
|
34896
35819
|
yield* eventStore.append({
|
|
34897
35820
|
type: "resources.updated",
|
|
34898
35821
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34899
|
-
payload: { resources: { containers:
|
|
35822
|
+
payload: { resources: { containers: getCurrentDockerStats() } }
|
|
34900
35823
|
});
|
|
34901
35824
|
return jsonResponse({
|
|
34902
35825
|
ok: true,
|
|
@@ -34906,9 +35829,15 @@ const postStartContainerRoute = HttpRouter.add("POST", "/api/resources/docker/co
|
|
|
34906
35829
|
})));
|
|
34907
35830
|
const getContainerLogsRoute = HttpRouter.add("GET", "/api/resources/docker/container/:id/logs", httpHandler(Effect.gen(function* () {
|
|
34908
35831
|
const id = (yield* HttpRouter.params)["id"] ?? "";
|
|
34909
|
-
if (!id) return jsonResponse({ error: "
|
|
35832
|
+
if (!isDockerIdentifier$2(id)) return jsonResponse({ error: "Invalid container ID" }, { status: 400 });
|
|
34910
35833
|
const { stdout } = yield* Effect.tryPromise({
|
|
34911
|
-
try: () =>
|
|
35834
|
+
try: () => dockerExec("docker", [
|
|
35835
|
+
"logs",
|
|
35836
|
+
"--tail",
|
|
35837
|
+
"200",
|
|
35838
|
+
"--timestamps",
|
|
35839
|
+
id
|
|
35840
|
+
], {
|
|
34912
35841
|
encoding: "utf-8",
|
|
34913
35842
|
timeout: 1e4
|
|
34914
35843
|
}),
|
|
@@ -34916,7 +35845,516 @@ const getContainerLogsRoute = HttpRouter.add("GET", "/api/resources/docker/conta
|
|
|
34916
35845
|
});
|
|
34917
35846
|
return jsonResponse({ logs: stdout });
|
|
34918
35847
|
})));
|
|
34919
|
-
|
|
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);
|
|
34920
36358
|
//#endregion
|
|
34921
36359
|
//#region services/model-origin-enrich.ts
|
|
34922
36360
|
init_agents$1();
|
|
@@ -34979,8 +36417,34 @@ async function deriveSessionPresence(agentId, rtState, tmuxSessionNames) {
|
|
|
34979
36417
|
return "idle";
|
|
34980
36418
|
}
|
|
34981
36419
|
//#endregion
|
|
34982
|
-
//#region ../../lib/settings-api.ts
|
|
36420
|
+
//#region ../../lib/settings-api-tiered-execution.ts
|
|
34983
36421
|
init_prd_locations();
|
|
36422
|
+
function tieredExecutionConfigForSave(config, providerAuth) {
|
|
36423
|
+
if (config === void 0) return void 0;
|
|
36424
|
+
const validated = validateTieredExecutionConfig(config, { providerAuth });
|
|
36425
|
+
return {
|
|
36426
|
+
enabled: validated.enabled,
|
|
36427
|
+
tiers: validated.tiers,
|
|
36428
|
+
supervisor: validated.supervisor,
|
|
36429
|
+
by_kind: validated.by_kind,
|
|
36430
|
+
feed: validated.feed,
|
|
36431
|
+
escalation: validated.escalation,
|
|
36432
|
+
compaction_reroute: validated.compaction_reroute,
|
|
36433
|
+
replay_threshold: validated.replay_threshold
|
|
36434
|
+
};
|
|
36435
|
+
}
|
|
36436
|
+
function validateTieredExecutionSettings(config, providerAuth) {
|
|
36437
|
+
if (config === void 0) return null;
|
|
36438
|
+
try {
|
|
36439
|
+
validateTieredExecutionConfig(config, { providerAuth });
|
|
36440
|
+
return null;
|
|
36441
|
+
} catch (error) {
|
|
36442
|
+
if (error instanceof TieredExecutionConfigError) return error.message;
|
|
36443
|
+
throw error;
|
|
36444
|
+
}
|
|
36445
|
+
}
|
|
36446
|
+
//#endregion
|
|
36447
|
+
//#region ../../lib/settings-api.ts
|
|
34984
36448
|
/**
|
|
34985
36449
|
* Settings API Adapter
|
|
34986
36450
|
*
|
|
@@ -35440,7 +36904,8 @@ async function writeYamlConfigPreservingComments(yamlConfig) {
|
|
|
35440
36904
|
["tracker_keys", config.tracker_keys],
|
|
35441
36905
|
["experimental", config.experimental],
|
|
35442
36906
|
["claude", config.claude],
|
|
35443
|
-
["codex", config.codex]
|
|
36907
|
+
["codex", config.codex],
|
|
36908
|
+
["tiered_execution", config.tiered_execution]
|
|
35444
36909
|
];
|
|
35445
36910
|
for (const [key, value] of topLevelSections) if (value === void 0) doc.deleteIn([key]);
|
|
35446
36911
|
else doc.setIn([key], value);
|
|
@@ -35544,7 +37009,8 @@ async function saveSettingsApiPromise(settings) {
|
|
|
35544
37009
|
} : void 0,
|
|
35545
37010
|
claude: settings.claude?.permissionMode ? { permissionMode: settings.claude.permissionMode } : void 0,
|
|
35546
37011
|
codex: settings.codex?.permissionMode ? { permissionMode: settings.codex.permissionMode } : void 0,
|
|
35547
|
-
remote: settings.remote
|
|
37012
|
+
remote: settings.remote,
|
|
37013
|
+
tiered_execution: tieredExecutionConfigForSave(settings.tiered_execution, currentConfig.providerAuth)
|
|
35548
37014
|
});
|
|
35549
37015
|
clearConfigCache();
|
|
35550
37016
|
}
|
|
@@ -35711,6 +37177,10 @@ function validateSettingsApi(settings) {
|
|
|
35711
37177
|
if (settings.remote.resiliency_tier !== void 0 && settings.remote.resiliency_tier !== "ephemeral" && settings.remote.resiliency_tier !== "durable") errors.push("remote.resiliency_tier must be ephemeral or durable");
|
|
35712
37178
|
if (settings.remote.max_concurrent_agents !== void 0 && (!Number.isInteger(settings.remote.max_concurrent_agents) || settings.remote.max_concurrent_agents < 0)) errors.push("remote.max_concurrent_agents must be a non-negative integer");
|
|
35713
37179
|
}
|
|
37180
|
+
if (settings.tiered_execution !== void 0) {
|
|
37181
|
+
const tieredExecutionError = validateTieredExecutionSettings(settings.tiered_execution, loadConfigSync().config.providerAuth);
|
|
37182
|
+
if (tieredExecutionError) errors.push(tieredExecutionError);
|
|
37183
|
+
}
|
|
35714
37184
|
return {
|
|
35715
37185
|
valid: errors.length === 0,
|
|
35716
37186
|
errors,
|
|
@@ -36881,7 +38351,7 @@ Be specific: reference actual file names, function names, requirement text, disc
|
|
|
36881
38351
|
const { command: cliCmd, args: cliArgs } = getAgentCommandSync(statusModelId);
|
|
36882
38352
|
const modelFlag = cliArgs.length > 0 ? ` ${cliArgs.join(" ")}` : "";
|
|
36883
38353
|
const promptFile = join$1(planningDir, ".status-review-prompt.tmp");
|
|
36884
|
-
const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-
|
|
38354
|
+
const { getProviderForModelSync, getProviderEnvSync } = await import("./providers-Djkuu1CD.js");
|
|
36885
38355
|
let providerEnvStr = "";
|
|
36886
38356
|
let providerEnv = {};
|
|
36887
38357
|
const statusProvider = getProviderForModelSync(statusModelId);
|
|
@@ -36964,7 +38434,7 @@ const postMissionControlUploadRoute = HttpRouter.add("POST", "/api/command-deck/
|
|
|
36964
38434
|
let safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
36965
38435
|
let processedContent = content;
|
|
36966
38436
|
if (safeName.endsWith(".vtt")) {
|
|
36967
|
-
const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-
|
|
38437
|
+
const { vttToMarkdown } = yield* Effect.promise(() => import("./vtt-parser-d-QMKZn4.js"));
|
|
36968
38438
|
processedContent = vttToMarkdown(content);
|
|
36969
38439
|
safeName = safeName.replace(/\.vtt$/, ".md");
|
|
36970
38440
|
}
|
|
@@ -42302,73 +43772,6 @@ const resetRoute = HttpRouter.add("POST", "/api/autopreso/session/reset", httpHa
|
|
|
42302
43772
|
})));
|
|
42303
43773
|
const autopresoRouteLayer = Layer.mergeAll(startRoute, backToStagingRoute, resetRoute);
|
|
42304
43774
|
//#endregion
|
|
42305
|
-
//#region services/event-loop-monitor.ts
|
|
42306
|
-
init_activity_logger();
|
|
42307
|
-
const EVENT_LOOP_MONITOR_WINDOW_MS = 6e4;
|
|
42308
|
-
let lastSample = {
|
|
42309
|
-
p50: 0,
|
|
42310
|
-
p99: 0,
|
|
42311
|
-
max: 0,
|
|
42312
|
-
unit: "ms",
|
|
42313
|
-
sampledAt: null,
|
|
42314
|
-
windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
|
|
42315
|
-
};
|
|
42316
|
-
let timer$3 = null;
|
|
42317
|
-
let activeHistogram = null;
|
|
42318
|
-
function nsToRoundedMs(ns) {
|
|
42319
|
-
if (!Number.isFinite(ns) || ns <= 0) return 0;
|
|
42320
|
-
return Math.round(ns / 1e6 * 100) / 100;
|
|
42321
|
-
}
|
|
42322
|
-
function sampleEventLoopDelay(histogram, deps = {}) {
|
|
42323
|
-
const warnThresholdMs = deps.warnThresholdMs ?? 100;
|
|
42324
|
-
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
42325
|
-
const warn = deps.warn ?? ((message) => console.warn(message));
|
|
42326
|
-
const emit = deps.emit ?? emitActivityEntrySync;
|
|
42327
|
-
const sample = {
|
|
42328
|
-
p50: nsToRoundedMs(histogram.percentile(50)),
|
|
42329
|
-
p99: nsToRoundedMs(histogram.percentile(99)),
|
|
42330
|
-
max: nsToRoundedMs(histogram.max),
|
|
42331
|
-
unit: "ms",
|
|
42332
|
-
sampledAt: now().toISOString(),
|
|
42333
|
-
windowMs: EVENT_LOOP_MONITOR_WINDOW_MS
|
|
42334
|
-
};
|
|
42335
|
-
lastSample = sample;
|
|
42336
|
-
if (sample.p99 > warnThresholdMs) {
|
|
42337
|
-
const message = `Dashboard event loop p99 delay ${sample.p99}ms exceeded ${warnThresholdMs}ms`;
|
|
42338
|
-
warn(`[event-loop-monitor] ${message}`);
|
|
42339
|
-
emit({
|
|
42340
|
-
source: "dashboard",
|
|
42341
|
-
level: "warn",
|
|
42342
|
-
message,
|
|
42343
|
-
details: `p50=${sample.p50}ms p99=${sample.p99}ms max=${sample.max}ms over ${sample.windowMs}ms`
|
|
42344
|
-
});
|
|
42345
|
-
}
|
|
42346
|
-
histogram.reset();
|
|
42347
|
-
return sample;
|
|
42348
|
-
}
|
|
42349
|
-
function getEventLoopDelaySample() {
|
|
42350
|
-
return lastSample;
|
|
42351
|
-
}
|
|
42352
|
-
function startEventLoopMonitor(deps = {}) {
|
|
42353
|
-
if (timer$3) return;
|
|
42354
|
-
const intervalMs = deps.intervalMs ?? 6e4;
|
|
42355
|
-
activeHistogram = (deps.createHistogram ?? (() => monitorEventLoopDelay({ resolution: 20 })))();
|
|
42356
|
-
activeHistogram.enable();
|
|
42357
|
-
timer$3 = setInterval(() => {
|
|
42358
|
-
if (!activeHistogram) return;
|
|
42359
|
-
sampleEventLoopDelay(activeHistogram, deps);
|
|
42360
|
-
}, intervalMs);
|
|
42361
|
-
timer$3.unref?.();
|
|
42362
|
-
}
|
|
42363
|
-
function stopEventLoopMonitor() {
|
|
42364
|
-
if (timer$3) {
|
|
42365
|
-
clearInterval(timer$3);
|
|
42366
|
-
timer$3 = null;
|
|
42367
|
-
}
|
|
42368
|
-
activeHistogram?.disable();
|
|
42369
|
-
activeHistogram = null;
|
|
42370
|
-
}
|
|
42371
|
-
//#endregion
|
|
42372
43775
|
//#region routes/metrics.ts
|
|
42373
43776
|
/**
|
|
42374
43777
|
* Metrics route module — Effect HttpRouter.Layer (PAN-428 B16)
|
|
@@ -42842,7 +44245,7 @@ const postRallyValidateRoute = HttpRouter.add("POST", "/api/rally/validate", Eff
|
|
|
42842
44245
|
}, { status: 400 });
|
|
42843
44246
|
return yield* Effect.promise(async () => {
|
|
42844
44247
|
try {
|
|
42845
|
-
const { RallyRestApi } = await import("./rally-api-
|
|
44248
|
+
const { RallyRestApi } = await import("./rally-api-DpFjC_KQ.js");
|
|
42846
44249
|
const api = new RallyRestApi({
|
|
42847
44250
|
apiKey,
|
|
42848
44251
|
server: server || "https://rally1.rallydev.com"
|
|
@@ -43170,7 +44573,7 @@ const getPlanningStatusRoute = HttpRouter.add("GET", "/api/planning/:issueId/sta
|
|
|
43170
44573
|
const remoteState = loadRemoteAgentState(sessionName);
|
|
43171
44574
|
const isRemote = !!remoteState;
|
|
43172
44575
|
const vmName = remoteState?.vmName ?? "";
|
|
43173
|
-
const { getAgentStateSync } = await import("./agents-
|
|
44576
|
+
const { getAgentStateSync } = await import("./agents-Ea9gfdKS.js");
|
|
43174
44577
|
const agentStarting = getAgentStateSync(sessionName)?.status === "starting";
|
|
43175
44578
|
let tmuxSessionAlive = false;
|
|
43176
44579
|
if (!isRemote) try {
|
|
@@ -43313,7 +44716,7 @@ Continue the PLANNING session. Do NOT implement anything.
|
|
|
43313
44716
|
`);
|
|
43314
44717
|
const agentCwd = workspacePath;
|
|
43315
44718
|
if (existsSync$1(outputFile)) await rename(outputFile, join$1(planningDir, `output-${Date.now()}.jsonl`));
|
|
43316
|
-
const { getAgentCommandSync } = await import("./settings-
|
|
44719
|
+
const { getAgentCommandSync } = await import("./settings-DXeuoifi.js");
|
|
43317
44720
|
let msgPlanningModel = "claude-sonnet-5";
|
|
43318
44721
|
try {
|
|
43319
44722
|
msgPlanningModel = resolveModel("plan", void 0, loadConfigSync().config);
|
|
@@ -43434,7 +44837,7 @@ async function getIndexStats(rootPath, isMain) {
|
|
|
43434
44837
|
}
|
|
43435
44838
|
const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Effect.promise(async () => {
|
|
43436
44839
|
try {
|
|
43437
|
-
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-
|
|
44840
|
+
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
|
|
43438
44841
|
const projectRoot = process.cwd();
|
|
43439
44842
|
const venvPath = join$1(projectRoot, ".venv");
|
|
43440
44843
|
const results = [];
|
|
@@ -43479,7 +44882,7 @@ const getTldrStatusRoute = HttpRouter.add("GET", "/api/services/tldr/status", Ef
|
|
|
43479
44882
|
}));
|
|
43480
44883
|
const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Effect.promise(async () => {
|
|
43481
44884
|
try {
|
|
43482
|
-
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-
|
|
44885
|
+
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
|
|
43483
44886
|
const projectRoot = process.cwd();
|
|
43484
44887
|
const venvPath = join$1(projectRoot, ".venv");
|
|
43485
44888
|
if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
|
|
@@ -43496,7 +44899,7 @@ const postTldrStartRoute = HttpRouter.add("POST", "/api/services/tldr/start", Ef
|
|
|
43496
44899
|
}));
|
|
43497
44900
|
const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effect.promise(async () => {
|
|
43498
44901
|
try {
|
|
43499
|
-
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-
|
|
44902
|
+
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
|
|
43500
44903
|
const projectRoot = process.cwd();
|
|
43501
44904
|
const venvPath = join$1(projectRoot, ".venv");
|
|
43502
44905
|
if (!existsSync$1(venvPath)) return jsonResponse({ error: "No .venv found in project root" }, { status: 404 });
|
|
@@ -43513,7 +44916,7 @@ const postTldrStopRoute = HttpRouter.add("POST", "/api/services/tldr/stop", Effe
|
|
|
43513
44916
|
}));
|
|
43514
44917
|
const postTldrReloadRoute = HttpRouter.add("POST", "/api/services/tldr/reload", Effect.promise(async () => {
|
|
43515
44918
|
try {
|
|
43516
|
-
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-
|
|
44919
|
+
const { getTldrDaemonServiceSync } = await import("./tldr-daemon-CwUB_rve.js");
|
|
43517
44920
|
const enabled = isTldrEnabledSync();
|
|
43518
44921
|
const projectRoot = process.cwd();
|
|
43519
44922
|
const targets = [];
|
|
@@ -43589,7 +44992,7 @@ const getVersionRoute = HttpRouter.add("GET", "/api/version", Effect.promise(asy
|
|
|
43589
44992
|
const version = await getOverdeckVersion();
|
|
43590
44993
|
let supervisorUrl = null;
|
|
43591
44994
|
try {
|
|
43592
|
-
const { getSupervisorUrlSync } = await import("./supervisor-
|
|
44995
|
+
const { getSupervisorUrlSync } = await import("./supervisor-BY_xA7eS.js");
|
|
43593
44996
|
supervisorUrl = getSupervisorUrlSync();
|
|
43594
44997
|
} catch {}
|
|
43595
44998
|
return jsonResponse({
|
|
@@ -43753,7 +45156,7 @@ const postShadowMonitorRoute = HttpRouter.add("POST", "/api/shadow/:issueId/moni
|
|
|
43753
45156
|
const projectPath = await getProjectPath(issuePrefix);
|
|
43754
45157
|
const workspacePath = join$1(projectPath, "workspaces", `feature-${issueLower}`);
|
|
43755
45158
|
if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
|
|
43756
|
-
const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-
|
|
45159
|
+
const { gatherArtifacts, generateBasicInference, updateInferenceDocumentSync } = await import("./shadow-engineering-DMC2pBPa.js");
|
|
43757
45160
|
const config = {
|
|
43758
45161
|
issueId,
|
|
43759
45162
|
workspacePath,
|
|
@@ -43783,7 +45186,7 @@ const postShadowObserveRoute = HttpRouter.add("POST", "/api/shadow/:issueId/obse
|
|
|
43783
45186
|
if (!existsSync$1(workspacePath)) return jsonResponse({ error: "Workspace not found" }, { status: 404 });
|
|
43784
45187
|
const ghConfig = getGitHubConfig();
|
|
43785
45188
|
if (!ghConfig) return jsonResponse({ error: "GitHub not configured - Observer requires GitHub" }, { status: 400 });
|
|
43786
|
-
const { runObserverCycle } = await import("./shadow-engineering-
|
|
45189
|
+
const { runObserverCycle } = await import("./shadow-engineering-DMC2pBPa.js");
|
|
43787
45190
|
const firstRepo = ghConfig.repos[0];
|
|
43788
45191
|
const config = {
|
|
43789
45192
|
issueId,
|
|
@@ -44848,7 +46251,7 @@ async function cleanupConversationAttachments(name) {
|
|
|
44848
46251
|
* sessions that the lifecycle poller missed. */
|
|
44849
46252
|
async function cleanupOrphanedConversationAttachments() {
|
|
44850
46253
|
try {
|
|
44851
|
-
const { listConversations } = await import("./conversations-
|
|
46254
|
+
const { listConversations } = await import("./conversations-DSOZG_Sz.js");
|
|
44852
46255
|
const convNames = new Set(listConversations().map((c) => c.name));
|
|
44853
46256
|
const root = getConversationAttachmentsRoot();
|
|
44854
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, {
|
|
@@ -45716,7 +47119,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
|
|
|
45716
47119
|
const codexApprovalPolicy = codexPermMode === "full-access" ? "never" : "on-request";
|
|
45717
47120
|
const codexSandboxMode = codexPermMode === "full-access" ? "danger-full-access" : codexPermMode === "read-only" ? "read-only" : "workspace-write";
|
|
45718
47121
|
const codexApprovalsReviewer = codexPermMode === "auto-review" ? "auto_review" : void 0;
|
|
45719
|
-
const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-
|
|
47122
|
+
const { initCodexHome, extractThreadIdFromRollout } = await import("./codex-BObh4thD.js");
|
|
45720
47123
|
initCodexHome(codexHome, {
|
|
45721
47124
|
trustedDir: cwd,
|
|
45722
47125
|
approvalPolicy: codexApprovalPolicy,
|
|
@@ -45786,7 +47189,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
|
|
|
45786
47189
|
} catch {}
|
|
45787
47190
|
console.log(`[claude-invoke] purpose=conversation-session | model=${model || "default"} | source=conversations.ts:spawnConversationSession | session=${tmuxSession} | resume=${resume} | command="${runtimeCommand}"`);
|
|
45788
47191
|
try {
|
|
45789
|
-
const { preTrustDirectory } = await import("./workspace-manager-
|
|
47192
|
+
const { preTrustDirectory } = await import("./workspace-manager-BE2H8gD5.js");
|
|
45790
47193
|
preTrustDirectory(cwd);
|
|
45791
47194
|
} catch {}
|
|
45792
47195
|
try {
|
|
@@ -45803,7 +47206,7 @@ async function spawnConversationSession(tmuxSession, cwd, claudeSessionId, model
|
|
|
45803
47206
|
const codexHomeDir = codexFields.codexHome;
|
|
45804
47207
|
(async () => {
|
|
45805
47208
|
try {
|
|
45806
|
-
const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-
|
|
47209
|
+
const { waitForCodexRollout, extractThreadIdFromRollout, writeThreadId } = await import("./codex-BObh4thD.js");
|
|
45807
47210
|
const rollout = await waitForCodexRollout(codexHomeDir, 12e4);
|
|
45808
47211
|
if (rollout) {
|
|
45809
47212
|
const threadId = extractThreadIdFromRollout(rollout);
|
|
@@ -46729,6 +48132,19 @@ async function resolveSessionFile(conv) {
|
|
|
46729
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.`);
|
|
46730
48133
|
return null;
|
|
46731
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
|
+
}
|
|
46732
48148
|
const MESSAGES_CACHE_MAX = 100;
|
|
46733
48149
|
const messagesCache = /* @__PURE__ */ new Map();
|
|
46734
48150
|
function isCodexSessionFile(sessionFile) {
|
|
@@ -46897,7 +48313,8 @@ async function getConversationRead(rawId, deps) {
|
|
|
46897
48313
|
isWorktree: gitInfo.isWorktree,
|
|
46898
48314
|
pendingInputCount,
|
|
46899
48315
|
pendingInputKinds,
|
|
46900
|
-
pendingAskUserQuestion
|
|
48316
|
+
pendingAskUserQuestion,
|
|
48317
|
+
transcriptMissing: conversationTranscriptMissing(conv, sessionAlive, convSf)
|
|
46901
48318
|
});
|
|
46902
48319
|
} catch (error) {
|
|
46903
48320
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -47406,7 +48823,8 @@ async function enrichConversationList(limit, offset) {
|
|
|
47406
48823
|
isWorktree: gitInfo.isWorktree,
|
|
47407
48824
|
pendingInputCount,
|
|
47408
48825
|
pendingInputKinds,
|
|
47409
|
-
pendingAskUserQuestion
|
|
48826
|
+
pendingAskUserQuestion,
|
|
48827
|
+
transcriptMissing: conversationTranscriptMissing(row, sessionAlive, convSf)
|
|
47410
48828
|
};
|
|
47411
48829
|
})), CONVERSATION_LIST_ENRICHMENT_CONCURRENCY));
|
|
47412
48830
|
}
|
|
@@ -48872,7 +50290,7 @@ async function registerProjectFromPath(opts) {
|
|
|
48872
50290
|
registerProjectSync(key, projectConfig);
|
|
48873
50291
|
const seededContextLayer = ensureProjectLayer(fullPath);
|
|
48874
50292
|
try {
|
|
48875
|
-
const { preTrustDirectorySync } = await import("./workspace-manager-
|
|
50293
|
+
const { preTrustDirectorySync } = await import("./workspace-manager-BE2H8gD5.js");
|
|
48876
50294
|
preTrustDirectorySync(fullPath);
|
|
48877
50295
|
} catch {}
|
|
48878
50296
|
let hooksInstalled = 0;
|
|
@@ -48908,7 +50326,7 @@ init_pan_dir();
|
|
|
48908
50326
|
init_paths();
|
|
48909
50327
|
const execAsync$3 = promisify(exec);
|
|
48910
50328
|
async function getIssueDataService() {
|
|
48911
|
-
const { getSharedIssueService } = await import("./issue-service-singleton-
|
|
50329
|
+
const { getSharedIssueService } = await import("./issue-service-singleton-DFei50PX.js");
|
|
48912
50330
|
return getSharedIssueService();
|
|
48913
50331
|
}
|
|
48914
50332
|
/** Returns true if the path exists (any type). */
|
|
@@ -50835,7 +52253,7 @@ async function refreshMergeStateFromGitHub(issueId, repo, prNumber) {
|
|
|
50835
52253
|
let mergeState;
|
|
50836
52254
|
let isDraft;
|
|
50837
52255
|
let checksFailed;
|
|
50838
|
-
const { isGitHubAppConfigured, getPullRequestState } = await import("./github-app-
|
|
52256
|
+
const { isGitHubAppConfigured, getPullRequestState } = await import("./github-app-Dg25z05Z.js");
|
|
50839
52257
|
if (isGitHubAppConfigured()) {
|
|
50840
52258
|
const prState = await Effect.runPromise(getPullRequestState(owner, repoName, prNumber));
|
|
50841
52259
|
mergeable = prState.mergeable === null ? "UNKNOWN" : prState.mergeable ? "MERGEABLE" : "CONFLICTING";
|
|
@@ -50984,8 +52402,8 @@ async function handlePullRequestPromise(payload) {
|
|
|
50984
52402
|
bumpIssuePrTabCacheGeneration(issueId);
|
|
50985
52403
|
const repo = payload.repository.full_name;
|
|
50986
52404
|
if (payload.action === "closed" && pr.merged === true) try {
|
|
50987
|
-
const { postMergeLifecycle } = await import("./merge-agent-
|
|
50988
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
52405
|
+
const { postMergeLifecycle } = await import("./merge-agent-DZaRBHQA.js");
|
|
52406
|
+
const { resolveProjectFromIssueSync } = await import("./projects-BbMEBykK.js");
|
|
50989
52407
|
const project = resolveProjectFromIssueSync(issueId);
|
|
50990
52408
|
if (project) {
|
|
50991
52409
|
const branchName = pr.head.ref;
|
|
@@ -51484,7 +52902,7 @@ function getCheckpointWorker() {
|
|
|
51484
52902
|
return worker;
|
|
51485
52903
|
}
|
|
51486
52904
|
async function runInline(operation, payload) {
|
|
51487
|
-
const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-
|
|
52905
|
+
const { claimTranscriptRange, commitTranscriptRange, releaseTranscriptRange, getTranscriptCheckpoint, listTranscriptCheckpoints } = await import("./checkpoints-auGbx5b0.js");
|
|
51488
52906
|
switch (operation) {
|
|
51489
52907
|
case "claimTranscriptRange": return claimTranscriptRange(payload);
|
|
51490
52908
|
case "commitTranscriptRange": return commitTranscriptRange(payload);
|
|
@@ -52638,7 +54056,7 @@ function buildPendingTurn(input, fromOffset, toOffset, compressed) {
|
|
|
52638
54056
|
};
|
|
52639
54057
|
}
|
|
52640
54058
|
async function emitMemoryObservationCreated(observation, timestamp) {
|
|
52641
|
-
const { initEventStore } = await import("./event-store-
|
|
54059
|
+
const { initEventStore } = await import("./event-store-D7_dSgP8.js");
|
|
52642
54060
|
await (await initEventStore()).appendAsync({
|
|
52643
54061
|
type: "memory.observation_created",
|
|
52644
54062
|
timestamp,
|
|
@@ -55677,7 +57095,7 @@ async function getFlywheelConversationPayload() {
|
|
|
55677
57095
|
};
|
|
55678
57096
|
}
|
|
55679
57097
|
async function postFlywheelReportPayload() {
|
|
55680
|
-
const { flywheelReportCommand } = await import("./flywheel-
|
|
57098
|
+
const { flywheelReportCommand } = await import("./flywheel-CFPOlOUd.js");
|
|
55681
57099
|
await flywheelReportCommand({ cwd: process.cwd() });
|
|
55682
57100
|
return {
|
|
55683
57101
|
status: 200,
|
|
@@ -55835,11 +57253,11 @@ const getMergeBlockersRoute = HttpRouter.add("GET", "/api/flywheel/merge-blocker
|
|
|
55835
57253
|
async function defaultGetOrderedIssueIds() {
|
|
55836
57254
|
const status = await readCurrentFlywheelStatusForDashboard();
|
|
55837
57255
|
if (!status) return [];
|
|
55838
|
-
const { computeMergeQueue } = await import("./flywheel-merge-order-
|
|
57256
|
+
const { computeMergeQueue } = await import("./flywheel-merge-order-FN6zqVXG.js");
|
|
55839
57257
|
return (await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd()).pipe(Effect.provide(layer)))).map((i) => i.issueId);
|
|
55840
57258
|
}
|
|
55841
57259
|
async function defaultMergeOne(issueId) {
|
|
55842
|
-
const { triggerMerge } = await import("./merge-ops-
|
|
57260
|
+
const { triggerMerge } = await import("./merge-ops-DRRd3YBA.js");
|
|
55843
57261
|
const r = await triggerMerge(issueId);
|
|
55844
57262
|
return r.success ? { ok: true } : {
|
|
55845
57263
|
ok: false,
|
|
@@ -55859,7 +57277,7 @@ async function postFlywheelMergeNextPayload(payload, deps = {}) {
|
|
|
55859
57277
|
body: { error: "n must be a positive integer" }
|
|
55860
57278
|
};
|
|
55861
57279
|
const issueIds = (await (deps.getOrderedIssueIds ?? defaultGetOrderedIssueIds)()).slice(0, n);
|
|
55862
|
-
const { shipMergeBatch } = await import("./merge-batch-
|
|
57280
|
+
const { shipMergeBatch } = await import("./merge-batch-BbG78hx-.js");
|
|
55863
57281
|
return {
|
|
55864
57282
|
status: 200,
|
|
55865
57283
|
body: { outcomes: await shipMergeBatch(issueIds, { merge: deps.merge ?? defaultMergeOne }) }
|
|
@@ -56016,7 +57434,7 @@ const getFlywheelMergeQueueRoute = HttpRouter.add("GET", "/api/flywheel/merge-qu
|
|
|
56016
57434
|
return yield* Effect.promise(async () => {
|
|
56017
57435
|
const status = await readCurrentFlywheelStatusForDashboard();
|
|
56018
57436
|
if (!status) return jsonResponse([]);
|
|
56019
|
-
const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-
|
|
57437
|
+
const { computeMergeQueue, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-FN6zqVXG.js");
|
|
56020
57438
|
return jsonResponse(await Effect.runPromise(computeMergeQueue(status.activePipeline, process.cwd(), { getPrUrl: resolveMergeQueuePrUrl }).pipe(Effect.provide(layer))));
|
|
56021
57439
|
});
|
|
56022
57440
|
})));
|
|
@@ -56030,18 +57448,18 @@ function uatGenerationNameFromParam(param) {
|
|
|
56030
57448
|
return decoded.startsWith("uat/") ? decoded : `uat/${decoded}`;
|
|
56031
57449
|
}
|
|
56032
57450
|
const getUatGenerationsRoute = HttpRouter.add("GET", "/api/flywheel/uat-generations", httpHandler(Effect.gen(function* () {
|
|
56033
|
-
const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-
|
|
57451
|
+
const { getUatGenerationsPayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
|
|
56034
57452
|
return jsonResponse(yield* Effect.promise(() => getUatGenerationsPayload()));
|
|
56035
57453
|
})));
|
|
56036
57454
|
const getUatCandidateRoute = HttpRouter.add("GET", "/api/flywheel/uat-candidate", httpHandler(Effect.gen(function* () {
|
|
56037
|
-
const { getUatCandidatePayload } = yield* Effect.promise(() => import("./uat-train-
|
|
57455
|
+
const { getUatCandidatePayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
|
|
56038
57456
|
return jsonResponse(yield* Effect.promise(() => getUatCandidatePayload()));
|
|
56039
57457
|
})));
|
|
56040
57458
|
const postUatGenerationStackRoute = HttpRouter.add("POST", "/api/flywheel/uat-generations/:name/stack", httpHandler(Effect.gen(function* () {
|
|
56041
57459
|
const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
|
|
56042
57460
|
if (authError) return authError;
|
|
56043
57461
|
const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
|
|
56044
|
-
const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-
|
|
57462
|
+
const { postUatGenerationStackPayload } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
|
|
56045
57463
|
const result = yield* Effect.promise(() => postUatGenerationStackPayload(name));
|
|
56046
57464
|
if (!result.ok) return jsonResponse({ error: result.error }, { status: result.status });
|
|
56047
57465
|
return jsonResponse({
|
|
@@ -56053,8 +57471,8 @@ const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-
|
|
|
56053
57471
|
const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
|
|
56054
57472
|
if (authError) return authError;
|
|
56055
57473
|
const name = uatGenerationNameFromParam((yield* HttpRouter.params)["name"] ?? "");
|
|
56056
|
-
const { postUatGenerationPromotePayload } = yield* Effect.promise(() => import("./uat-train-
|
|
56057
|
-
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"));
|
|
56058
57476
|
const result = yield* Effect.promise(() => postUatGenerationPromotePayload(name, firePostMergeLifecycle));
|
|
56059
57477
|
if (!result.success) return jsonResponse(result, { status: result.reason === "not-found" ? 404 : result.reason === "merge-failed" ? 500 : 409 });
|
|
56060
57478
|
return jsonResponse(result);
|
|
@@ -56067,7 +57485,7 @@ const postUatGenerationPromoteRoute = HttpRouter.add("POST", "/api/flywheel/uat-
|
|
|
56067
57485
|
const postFlywheelAssembleUatRoute = HttpRouter.add("POST", "/api/flywheel/assemble-uat", httpHandler(Effect.gen(function* () {
|
|
56068
57486
|
const authError = rejectUnsafeDashboardMutationRequest(yield* HttpServerRequest.HttpServerRequest);
|
|
56069
57487
|
if (authError) return authError;
|
|
56070
|
-
const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-
|
|
57488
|
+
const { runUatTrainReconcile } = yield* Effect.promise(() => import("./uat-train-ByN1YyZx.js"));
|
|
56071
57489
|
return jsonResponse(yield* Effect.promise(() => runUatTrainReconcile({ force: true })));
|
|
56072
57490
|
})));
|
|
56073
57491
|
const getFlywheelStateRoute = HttpRouter.add("GET", "/api/flywheel/state", httpHandler(Effect.gen(function* () {
|
|
@@ -58805,6 +60223,7 @@ init_config_yaml();
|
|
|
58805
60223
|
init_review_status();
|
|
58806
60224
|
init_activity_logger();
|
|
58807
60225
|
init_tier_supervisor();
|
|
60226
|
+
init_tier_table();
|
|
58808
60227
|
function parseCalloutBody(body) {
|
|
58809
60228
|
if (!body || typeof body !== "object") return void 0;
|
|
58810
60229
|
const record = body;
|
|
@@ -59582,7 +61001,7 @@ async function pollOnce(state) {
|
|
|
59582
61001
|
try {
|
|
59583
61002
|
const remoteState = await readFile(join$1(homedir$1(), ".overdeck", "agents", agentId, "remote-state.json"), "utf-8").then((text) => JSON.parse(text)).catch(() => null);
|
|
59584
61003
|
if (remoteState?.location === "remote" && remoteState?.vmName) {
|
|
59585
|
-
const { getRemoteAgentOutput } = await import("./remote-agents-
|
|
61004
|
+
const { getRemoteAgentOutput } = await import("./remote-agents-BXKV5iBS.js");
|
|
59586
61005
|
stdout = await getRemoteAgentOutput(agentId, remoteState.vmName, 50);
|
|
59587
61006
|
} else stdout = await Effect.runPromise(capturePane(agentId, 50));
|
|
59588
61007
|
} catch {
|
|
@@ -60765,7 +62184,7 @@ const RESTART_MARKER = join(homedir(), ".overdeck", "dashboard-restarting.json")
|
|
|
60765
62184
|
* loads new content-hashed chunk filenames (no ERR_MODULE_NOT_FOUND after rebuild).
|
|
60766
62185
|
*/
|
|
60767
62186
|
async function defaultLifecycleRunner(pending) {
|
|
60768
|
-
const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-
|
|
62187
|
+
const { postMergeLifecycle, notifyTldrDaemon } = await import("./merge-agent-DZaRBHQA.js");
|
|
60769
62188
|
await postMergeLifecycle(pending.issueId, pending.projectPath, pending.sourceBranch, { skipDeploy: true });
|
|
60770
62189
|
if (pending.sourceBranch) await notifyTldrDaemon(pending.projectPath, pending.sourceBranch);
|
|
60771
62190
|
}
|
|
@@ -61086,7 +62505,7 @@ function failureReason(result) {
|
|
|
61086
62505
|
return result.error ?? result.message ?? `merge returned status ${result.statusCode ?? "unknown"}`;
|
|
61087
62506
|
}
|
|
61088
62507
|
async function defaultMergeIssue(issueId) {
|
|
61089
|
-
const { triggerMerge } = await import("./merge-ops-
|
|
62508
|
+
const { triggerMerge } = await import("./merge-ops-DRRd3YBA.js");
|
|
61090
62509
|
return triggerMerge(issueId);
|
|
61091
62510
|
}
|
|
61092
62511
|
function defaultAnnounceFailure(issueId, reason) {
|
|
@@ -61190,6 +62609,34 @@ async function warnIfAutonomousMergeBackendUnavailable(deps = {}) {
|
|
|
61190
62609
|
}
|
|
61191
62610
|
}
|
|
61192
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
|
|
61193
62640
|
//#region services/cost-reconcile-service.ts
|
|
61194
62641
|
const RECONCILE_INTERVAL_MS = 5 * 6e4;
|
|
61195
62642
|
let timer = null;
|
|
@@ -61200,6 +62647,14 @@ async function runCostReconcileOnce(reason) {
|
|
|
61200
62647
|
const result = await reconcilePiTranscripts();
|
|
61201
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)`);
|
|
61202
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
|
+
}
|
|
61203
62658
|
})().finally(() => {
|
|
61204
62659
|
inFlight = null;
|
|
61205
62660
|
});
|
|
@@ -61225,6 +62680,7 @@ function stopCostReconcileService() {
|
|
|
61225
62680
|
}
|
|
61226
62681
|
//#endregion
|
|
61227
62682
|
//#region ../../../scripts/create-overdeck-db.ts
|
|
62683
|
+
init_boot_gates();
|
|
61228
62684
|
init_driver();
|
|
61229
62685
|
function migrationStatements(migrationPath) {
|
|
61230
62686
|
return readFileSync$1(migrationPath, "utf8").split("--> statement-breakpoint").map((statement) => statement.trim()).filter(Boolean);
|
|
@@ -61291,6 +62747,7 @@ init_paths();
|
|
|
61291
62747
|
init_tmux();
|
|
61292
62748
|
init_fts_db();
|
|
61293
62749
|
init_paths$1();
|
|
62750
|
+
init_auto_commit();
|
|
61294
62751
|
initDashboardLogFile();
|
|
61295
62752
|
console.log(`[boot-timing] module graph loaded at +${Math.round(performance.now())}ms (since process start)`);
|
|
61296
62753
|
console.log(`[overdeck] Boot gates: ${formatBootGateState(resolveBootGates())}`);
|
|
@@ -61299,6 +62756,7 @@ console.log("[overdeck] Event loop delay monitor started");
|
|
|
61299
62756
|
await mkdir(getOverdeckHome(), { recursive: true });
|
|
61300
62757
|
ensureInternalTokenSync();
|
|
61301
62758
|
warnIfAutonomousMergeBackendUnavailable();
|
|
62759
|
+
warnIfAppCannotMerge();
|
|
61302
62760
|
await ensureManagedTmuxContextOnce();
|
|
61303
62761
|
await initTrackerConfigCache().catch((err) => {
|
|
61304
62762
|
console.log("[tracker-config] Warning: failed to cache .overdeck.env:", err.message);
|
|
@@ -61635,6 +63093,7 @@ const handleShutdownSignal = async (signal) => {
|
|
|
61635
63093
|
stopCostReconcileService();
|
|
61636
63094
|
stopRestartAnnouncer();
|
|
61637
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));
|
|
61638
63097
|
await stopConversationSearchWatcher().catch((err) => console.warn("[conversation-search] watcher shutdown failed:", err));
|
|
61639
63098
|
closeConversationSearchService();
|
|
61640
63099
|
closeMemoryFtsDatabases();
|
|
@@ -61683,7 +63142,7 @@ reconcileStaleGitHubBlockers().then((n) => {
|
|
|
61683
63142
|
}
|
|
61684
63143
|
}).catch((err) => console.warn(`[overdeck] Startup blocker reconciliation failed: ${err.message}`));
|
|
61685
63144
|
try {
|
|
61686
|
-
const { resetProcessingToQueued } = await import("./merge-sync-
|
|
63145
|
+
const { resetProcessingToQueued } = await import("./merge-sync-D3wPN2-T.js");
|
|
61687
63146
|
const resetCount = resetProcessingToQueued();
|
|
61688
63147
|
if (resetCount > 0) {
|
|
61689
63148
|
console.log(`[overdeck] Reset ${resetCount} stuck merge queue entries to queued`);
|