@overdeck/core 0.49.0 → 0.49.1
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/dist/{build-info-Ck2TzOSQ.js → build-info-Bts-0E0B.js} +3 -3
- package/dist/{build-info-Ck2TzOSQ.js.map → build-info-Bts-0E0B.js.map} +1 -1
- package/dist/build-info-CD0O1MvT.js +2 -0
- package/dist/cli/index.js +4 -4
- package/dist/dashboard/{build-info-DXfOjbg3.js → build-info-DTX8JwVP.js} +3 -3
- package/dist/dashboard/{build-info-DXfOjbg3.js.map → build-info-DTX8JwVP.js.map} +1 -1
- package/dist/dashboard/build-info-PQlh_Rhb.js +2 -0
- package/dist/dashboard/{deacon-CeqVPw0o.js → deacon-BEH-IY0v.js} +2 -2
- package/dist/dashboard/{deacon-CQxv6Qwv.js → deacon-TPYssFYt.js} +8 -8
- package/dist/dashboard/{deacon-CQxv6Qwv.js.map → deacon-TPYssFYt.js.map} +1 -1
- package/dist/dashboard/{deacon-merge-ugZ7jekt.js → deacon-merge-BOGFEV8_.js} +5 -5
- package/dist/dashboard/{deacon-merge-ugZ7jekt.js.map → deacon-merge-BOGFEV8_.js.map} +1 -1
- package/dist/dashboard/{deacon-pending-lifecycle-Cxu6Rezs.js → deacon-pending-lifecycle-DVHjOLje.js} +2 -2
- package/dist/dashboard/{deacon-pending-lifecycle-Cxu6Rezs.js.map → deacon-pending-lifecycle-DVHjOLje.js.map} +1 -1
- package/dist/dashboard/deacon.js +2 -2
- package/dist/dashboard/{deploy-patrol-DxnpZcnl.js → deploy-patrol-BXjlvoV3.js} +2 -2
- package/dist/dashboard/{deploy-patrol-DxnpZcnl.js.map → deploy-patrol-BXjlvoV3.js.map} +1 -1
- package/dist/dashboard/{identity-BnxKo9-z.js → identity-Bwh6VGeL.js} +2 -2
- package/dist/dashboard/{identity-BnxKo9-z.js.map → identity-Bwh6VGeL.js.map} +1 -1
- package/dist/dashboard/{lifecycle-ozA4GZNs.js → lifecycle-B0rL38D3.js} +2 -2
- package/dist/dashboard/{lifecycle-ozA4GZNs.js.map → lifecycle-B0rL38D3.js.map} +1 -1
- package/dist/dashboard/{memory-governor-W8994ASc.js → memory-governor-A4O6vI9d.js} +3 -3
- package/dist/dashboard/{memory-governor-W8994ASc.js.map → memory-governor-A4O6vI9d.js.map} +1 -1
- package/dist/dashboard/{memory-pressure-patrol-D_T_QLFC.js → memory-pressure-patrol-BWlWywmA.js} +2 -2
- package/dist/dashboard/{memory-pressure-patrol-D_T_QLFC.js.map → memory-pressure-patrol-BWlWywmA.js.map} +1 -1
- package/dist/dashboard/{merge-agent-B8YsWRDu.js → merge-agent-BRSz26Oh.js} +1 -1
- package/dist/dashboard/{merge-agent-DxecsTre.js → merge-agent-GWhmnxFk.js} +2 -2
- package/dist/dashboard/{merge-agent-DxecsTre.js.map → merge-agent-GWhmnxFk.js.map} +1 -1
- package/dist/dashboard/merge-ops-DtHx17Vd.js +2 -0
- package/dist/dashboard/{post-merge-lifecycle-worker-DC-i1kqz.js → post-merge-lifecycle-worker-KK1RTKRE.js} +2 -2
- package/dist/dashboard/{post-merge-lifecycle-worker-DC-i1kqz.js.map → post-merge-lifecycle-worker-KK1RTKRE.js.map} +1 -1
- package/dist/dashboard/{resolver-BdHzKhNL.js → resolver-C8rooxoz.js} +2 -2
- package/dist/dashboard/{resolver-BdHzKhNL.js.map → resolver-C8rooxoz.js.map} +1 -1
- package/dist/dashboard/server.js +28 -28
- package/dist/dashboard/{service-0hbSVeHz.js → service-BLb_CyPL.js} +6 -6
- package/dist/dashboard/{service-0hbSVeHz.js.map → service-BLb_CyPL.js.map} +1 -1
- package/dist/dashboard/{specialists-C2ZVszIW.js → specialists-DocAK-7A.js} +5 -5
- package/dist/dashboard/{specialists-C2ZVszIW.js.map → specialists-DocAK-7A.js.map} +1 -1
- package/dist/dashboard/{specialists-DhLEuxVX.js → specialists-xXcJHAyg.js} +1 -1
- package/dist/dashboard/{stall-sweeper-o4-q0vqY.js → stall-sweeper-DOyIfeLR.js} +2 -2
- package/dist/dashboard/{stall-sweeper-o4-q0vqY.js.map → stall-sweeper-DOyIfeLR.js.map} +1 -1
- package/dist/dashboard/{uat-train-fK0FbCx7.js → uat-train-COSy-vEX.js} +1 -1
- package/dist/dashboard/{uat-train-CSOval9z.js → uat-train-DVUNW03P.js} +0 -0
- package/dist/dashboard/{uat-train-CSOval9z.js.map → uat-train-DVUNW03P.js.map} +1 -1
- package/dist/dashboard/{workflows-BnNc1QlT.js → workflows-BYwR2aIN.js} +2 -2
- package/dist/dashboard/{workflows-BnNc1QlT.js.map → workflows-BYwR2aIN.js.map} +1 -1
- package/dist/dashboard/workflows-Cf4LAnhg.js +2 -0
- package/dist/dashboard/{workspaces-CqeaoIK6.js → workspaces-BlpbOPZ7.js} +11 -11
- package/dist/dashboard/{workspaces-CqeaoIK6.js.map → workspaces-BlpbOPZ7.js.map} +1 -1
- package/dist/{lifecycle-Bhu9mEsV.js → lifecycle-BhAciIXh.js} +1 -1
- package/dist/{lifecycle-CMRdm3mR.js → lifecycle-D8m3w850.js} +2 -2
- package/dist/{lifecycle-CMRdm3mR.js.map → lifecycle-D8m3w850.js.map} +1 -1
- package/dist/{merge-agent-DQ7DOXE2.js → merge-agent-CUnIrr8R.js} +2 -2
- package/dist/{merge-agent-DQ7DOXE2.js.map → merge-agent-CUnIrr8R.js.map} +1 -1
- package/dist/{merge-agent-D33x_y_T.js → merge-agent-DwfVId_E.js} +1 -1
- package/package.json +1 -1
- package/dist/build-info-B9bbKbXz.js +0 -2
- package/dist/dashboard/build-info-CrBYH-L2.js +0 -2
- package/dist/dashboard/merge-ops-C9wpFz1G.js +0 -2
- package/dist/dashboard/workflows-C6MkcBR_.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-patrol-DxnpZcnl.js","names":[],"sources":["../../src/lib/cloister/deploy-patrol.ts"],"sourcesContent":["import { open, mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { execFile, spawn } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\n\nimport { emitActivityEntrySync, emitActivityTtsSync } from '../activity-logger.js';\nimport { getBuildInfo } from '../deploy/build-info.js';\nimport {\n clearPendingDeploy,\n markPendingDeployEscalated,\n readPendingDeploy,\n recordDeployIntent,\n type PendingDeploy,\n} from '../deploy/deploy-queue.js';\nimport {\n getDeployWindowAssessment,\n type DeployWindowAssessment,\n} from '../deploy/deploy-window.js';\nimport { computeBuildStaleness, type BuildStaleness } from '../deploy/staleness.js';\nimport { OVERDECK_HOME } from '../paths.js';\nimport { loadCloisterConfigSync, type DeployConfig } from './config.js';\n\nconst OBSERVATION_INTERVAL_MS = 30 * 60 * 1000;\nconst DEPLOY_SPAWN_COOLDOWN_MS = 10 * 60 * 1000;\nconst DEPLOY_PATROL_INITIATOR = 'deploy-patrol';\nconst CI_WORKFLOW_FILE = 'ci.yml';\nconst CI_RUN_FIELDS = 'databaseId,status,conclusion,headSha,attempt,createdAt';\nconst SYSTEMD_ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\ninterface DeployPatrolState {\n lastObservedBehind: number | null;\n lastObservationAt: number;\n lastDeferralReason: string | null;\n lastDeferralAt: number;\n lastDeploySpawnAt: number;\n lastEscalationReason: string | null;\n lastEscalationAt: number;\n unknownObserved: boolean;\n}\n\nconst state: DeployPatrolState = {\n lastObservedBehind: null,\n lastObservationAt: 0,\n lastDeferralReason: null,\n lastDeferralAt: 0,\n lastDeploySpawnAt: 0,\n lastEscalationReason: null,\n lastEscalationAt: 0,\n unknownObserved: false,\n};\n\ntype EmitEntry = typeof emitActivityEntrySync;\ntype EmitTts = typeof emitActivityTtsSync;\n\nexport interface DeployCiState {\n readonly status: 'green' | 'pending' | 'red' | 'unknown';\n readonly reason?: string;\n}\n\ninterface DeployCiRun {\n readonly databaseId: number;\n readonly status: string;\n readonly conclusion: string | null;\n readonly headSha: string;\n readonly attempt: number;\n readonly createdAt: string;\n}\n\nexport interface DeployPatrolContext {\n readonly repoRoot: string;\n readonly config: DeployConfig;\n readonly computeStaleness?: () => Promise<BuildStaleness>;\n readonly getCiState?: (sha: string) => Promise<DeployCiState>;\n readonly getWindowAssessment?: () => Promise<DeployWindowAssessment>;\n readonly readQueue?: typeof readPendingDeploy;\n readonly clearQueue?: typeof clearPendingDeploy;\n readonly recordIntent?: typeof recordDeployIntent;\n readonly markQueueEscalated?: typeof markPendingDeployEscalated;\n readonly spawnReload?: (request: ReloadRequest) => Promise<void>;\n readonly emitEntry?: EmitEntry;\n readonly emitTts?: EmitTts;\n readonly now?: () => number;\n}\n\nexport interface ReloadRequest {\n readonly command: string;\n readonly args: readonly string[];\n readonly cwd: string;\n readonly detached: true;\n readonly initiator: string;\n}\n\nfunction reloadEnvironment(request: ReloadRequest, env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n return { ...env, OVERDECK_AGENT_ID: request.initiator };\n}\n\nfunction execGh(args: readonly string[], cwd: string): Promise<string> {\n return new Promise((resolve, reject) => {\n execFile('gh', [...args], { cwd, encoding: 'utf8', timeout: 30_000 }, (error, stdout) => {\n if (error) reject(error);\n else resolve(stdout);\n });\n });\n}\n\nexport async function getDeployCiState(\n repoRoot: string,\n sha: string,\n runGh: (args: readonly string[], cwd: string) => Promise<string> = execGh,\n): Promise<DeployCiState> {\n try {\n const runs = JSON.parse(await runGh([\n 'run', 'list',\n '--branch', 'main',\n '--commit', sha,\n '--workflow', CI_WORKFLOW_FILE,\n '--limit', '10',\n '--json', CI_RUN_FIELDS,\n ], repoRoot)) as DeployCiRun[];\n const run = runs.find((candidate) => candidate.headSha === sha);\n const tip = shortSha(sha);\n\n if (!run) {\n return {\n status: 'pending',\n reason: `Automatic deployment deferred because CI has not started for origin/main ${tip}.`,\n };\n }\n if (run.status !== 'completed') {\n return {\n status: 'pending',\n reason: `Automatic deployment deferred because CI is ${run.status} for origin/main ${tip}.`,\n };\n }\n if (run.conclusion !== 'success') {\n return {\n status: 'red',\n reason: `Automatic deployment blocked because CI concluded ${run.conclusion || 'without a result'} for origin/main ${tip}.`,\n };\n }\n return { status: 'green' };\n } catch (error) {\n return {\n status: 'unknown',\n reason: `Automatic deployment deferred because CI status for origin/main ${shortSha(sha)} could not be read: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n\nexport function buildSystemdReloadArgs(\n request: ReloadRequest,\n now: number,\n logPath: string,\n env: NodeJS.ProcessEnv = process.env,\n): string[] {\n const setenvArgs = Object.entries(reloadEnvironment(request, env))\n .filter(([name, value]) => value !== undefined && SYSTEMD_ENV_NAME_RE.test(name))\n .flatMap(([name, value]) => ['--setenv', `${name}=${value}`]);\n\n return [\n '--user', '--unit', `overdeck-auto-deploy-${now}`,\n '--collect', '--quiet',\n '--property=Restart=on-failure',\n '--property=RestartSec=10s',\n '--property=StartLimitIntervalSec=0',\n `--property=StandardOutput=append:${logPath}`,\n `--property=StandardError=append:${logPath}`,\n `--property=WorkingDirectory=${request.cwd}`,\n ...setenvArgs,\n request.command,\n ...request.args,\n ];\n}\n\nfunction shortSha(sha: string | null): string {\n return sha?.slice(0, 8) || 'unknown';\n}\n\nexport async function waitForChildSpawn(\n child: Pick<ChildProcess, 'once' | 'removeListener' | 'unref'>,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const onSpawn = () => {\n cleanup();\n child.unref();\n resolve();\n };\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n const cleanup = () => {\n child.removeListener('spawn', onSpawn);\n child.removeListener('error', onError);\n };\n\n child.once('spawn', onSpawn);\n child.once('error', onError);\n });\n}\n\nasync function waitForChildExit(child: ChildProcess): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n child.once('error', reject);\n child.once('close', (code) => resolve(code ?? 1));\n });\n}\n\nasync function spawnDetachedReload(request: ReloadRequest, now: number): Promise<void> {\n const logsDir = join(OVERDECK_HOME, 'logs');\n const logPath = join(logsDir, 'auto-deploy.log');\n await mkdir(logsDir, { recursive: true });\n await writeFile(\n join(OVERDECK_HOME, 'dashboard-restarting.json'),\n JSON.stringify({ reason: 'auto-deploy', trigger: 'deacon', timestamp: now }, null, 2),\n 'utf8',\n );\n\n const log = await open(logPath, 'a');\n try {\n if (process.platform === 'linux') {\n let systemdFailure: string | null = null;\n try {\n const launcher = spawn(\n 'systemd-run',\n buildSystemdReloadArgs(request, now, logPath),\n {\n cwd: request.cwd,\n stdio: ['ignore', log.fd, log.fd],\n },\n );\n const exitCode = await waitForChildExit(launcher);\n if (exitCode === 0) return;\n systemdFailure = `systemd-run exited ${exitCode}`;\n } catch (error) {\n systemdFailure = error instanceof Error ? error.message : String(error);\n }\n throw new Error(\n `Automatic deploy requires an independent systemd unit before it can stop the dashboard: ${systemdFailure}`,\n );\n }\n\n const child = spawn(\n request.command,\n [...request.args],\n {\n cwd: request.cwd,\n detached: request.detached,\n env: reloadEnvironment(request, process.env),\n stdio: ['ignore', log.fd, log.fd],\n },\n );\n await waitForChildSpawn(child);\n } finally {\n await log.close();\n }\n}\n\nfunction clearState(): void {\n state.lastObservedBehind = null;\n state.lastObservationAt = 0;\n state.lastDeferralReason = null;\n state.lastDeferralAt = 0;\n state.lastDeploySpawnAt = 0;\n state.lastEscalationReason = null;\n state.lastEscalationAt = 0;\n state.unknownObserved = false;\n}\n\nfunction emitDeferral(reason: string, now: number, emitEntry: EmitEntry): void {\n if (\n state.lastDeferralReason === reason &&\n now - state.lastDeferralAt < OBSERVATION_INTERVAL_MS\n ) return;\n\n emitEntry({\n source: 'deploy-script',\n level: 'warn',\n message: reason,\n });\n state.lastDeferralReason = reason;\n state.lastDeferralAt = now;\n}\n\nasync function refreshQueuedDeployReason(\n queuedDeploy: PendingDeploy | null,\n reason: string,\n context: DeployPatrolContext,\n): Promise<void> {\n if (!queuedDeploy) return;\n await (context.recordIntent ?? recordDeployIntent)({\n requestedBy: DEPLOY_PATROL_INITIATOR,\n reason,\n blockedBy: [],\n });\n}\n\nasync function escalateOverdueQueue(\n queuedDeploy: PendingDeploy | null,\n blockReason: string,\n context: DeployPatrolContext,\n now: number,\n emitEntry: EmitEntry,\n emitTts: EmitTts,\n): Promise<void> {\n if (\n !queuedDeploy\n || queuedDeploy.escalated\n || now - Date.parse(queuedDeploy.requestedAt) <= context.config.queue_deadline_minutes * 60_000\n ) return;\n\n const ageMinutes = Math.floor((now - Date.parse(queuedDeploy.requestedAt)) / 60_000);\n const message = `Queued dashboard deploy has waited ${ageMinutes} minutes; current block: ${blockReason}`;\n if (\n state.lastEscalationReason !== blockReason\n || now - state.lastEscalationAt >= OBSERVATION_INTERVAL_MS\n ) {\n emitEntry({ source: 'deploy-script', level: 'error', message });\n emitTts({\n utterance: `Dashboard deploy has been queued for ${ageMinutes} minutes and needs attention.`,\n priority: 1,\n source: 'deploy-script',\n eventType: 'deploy_queue_stuck',\n });\n state.lastEscalationReason = blockReason;\n state.lastEscalationAt = now;\n }\n\n await (context.markQueueEscalated ?? markPendingDeployEscalated)();\n}\n\nexport async function runDeployPatrol(context: DeployPatrolContext): Promise<void> {\n const now = (context.now ?? Date.now)();\n const emitEntry = context.emitEntry ?? emitActivityEntrySync;\n const emitTts = context.emitTts ?? emitActivityTtsSync;\n const staleness = await (context.computeStaleness ?? (() => computeBuildStaleness({\n repoRoot: context.repoRoot,\n buildCommit: getBuildInfo().buildCommit,\n })))();\n const queuedDeploy = await (context.readQueue ?? readPendingDeploy)();\n\n if (staleness.status === 'unknown') {\n const reason = `Automatic deployment cannot compare the running build with origin/main: ${staleness.reason ?? 'build staleness is unknown'}`;\n if (!state.unknownObserved) {\n emitEntry({\n source: 'deploy-script',\n level: 'warn',\n message: reason,\n });\n state.unknownObserved = true;\n }\n await refreshQueuedDeployReason(queuedDeploy, reason, context);\n await escalateOverdueQueue(queuedDeploy, reason, context, now, emitEntry, emitTts);\n return;\n }\n\n if (staleness.status === 'fresh') {\n clearState();\n await (context.clearQueue ?? clearPendingDeploy)();\n return;\n }\n\n state.unknownObserved = false;\n const behind = staleness.behindBuildInputs ?? 0;\n if (\n state.lastObservedBehind !== behind ||\n now - state.lastObservationAt >= OBSERVATION_INTERVAL_MS\n ) {\n emitEntry({\n source: 'deploy-script',\n level: 'warn',\n message: `The running build ${shortSha(staleness.buildCommit)} is ${behind} build-input commit(s) behind origin/main ${shortSha(staleness.originMainSha)}. ${context.config.auto_deploy || queuedDeploy ? 'Automatic deployment will start after the merge debounce and safety checks clear.' : 'Automatic deployment is disabled, so operator action is required.'}`,\n });\n state.lastObservedBehind = behind;\n state.lastObservationAt = now;\n }\n\n if (!context.config.auto_deploy && !queuedDeploy) return;\n if (\n staleness.originMainLastBuildInputCommitAt !== null\n && now - staleness.originMainLastBuildInputCommitAt < context.config.debounce_minutes * 60 * 1000\n ) {\n const reason = 'Automatic deployment deferred because the merge debounce has not elapsed.';\n await refreshQueuedDeployReason(queuedDeploy, reason, context);\n await escalateOverdueQueue(queuedDeploy, reason, context, now, emitEntry, emitTts);\n return;\n }\n if (state.lastDeploySpawnAt > 0 && now - state.lastDeploySpawnAt < DEPLOY_SPAWN_COOLDOWN_MS) {\n const reason = 'Automatic deployment deferred because a recent deploy is still inside the spawn cooldown.';\n await refreshQueuedDeployReason(queuedDeploy, reason, context);\n await escalateOverdueQueue(queuedDeploy, reason, context, now, emitEntry, emitTts);\n return;\n }\n\n if (!staleness.originMainSha) {\n const reason = 'Automatic deployment deferred because the origin/main commit is unknown.';\n emitDeferral(reason, now, emitEntry);\n await refreshQueuedDeployReason(queuedDeploy, reason, context);\n await escalateOverdueQueue(queuedDeploy, reason, context, now, emitEntry, emitTts);\n return;\n }\n const ciState = await (context.getCiState ?? ((sha) => getDeployCiState(context.repoRoot, sha)))(staleness.originMainSha);\n if (ciState.status !== 'green') {\n const reason = ciState.reason\n ?? `Automatic deployment deferred because CI is ${ciState.status} for origin/main ${shortSha(staleness.originMainSha)}.`;\n emitDeferral(reason, now, emitEntry);\n await refreshQueuedDeployReason(queuedDeploy, reason, context);\n await escalateOverdueQueue(queuedDeploy, reason, context, now, emitEntry, emitTts);\n return;\n }\n\n const assessment = await (context.getWindowAssessment ?? getDeployWindowAssessment)();\n if (assessment.reason) {\n emitDeferral(assessment.reason, now, emitEntry);\n if (context.config.auto_deploy || queuedDeploy) {\n await (context.recordIntent ?? recordDeployIntent)({\n requestedBy: DEPLOY_PATROL_INITIATOR,\n reason: assessment.reason,\n blockedBy: [],\n });\n }\n await escalateOverdueQueue(\n queuedDeploy,\n assessment.reason,\n context,\n now,\n emitEntry,\n emitTts,\n );\n return;\n }\n\n const reloadRequest: ReloadRequest = {\n command: process.execPath,\n args: [join(context.repoRoot, 'dist/cli/index.js'), 'reload', '--health-timeout', '120000'],\n cwd: context.repoRoot,\n detached: true,\n initiator: DEPLOY_PATROL_INITIATOR,\n };\n await (context.spawnReload ?? ((request) => spawnDetachedReload(request, now)))(reloadRequest);\n state.lastDeploySpawnAt = now;\n state.lastDeferralReason = null;\n state.lastDeferralAt = 0;\n emitEntry({\n source: 'deploy-script',\n level: 'info',\n message: `Automatic deployment started for origin/main ${shortSha(staleness.originMainSha)}; the dashboard will rebuild and restart with a 120-second health timeout.${queuedDeploy ? ` This was a queued deploy requested by ${queuedDeploy.requestedBy.join(', ')}.` : ''}`,\n });\n emitTts({\n utterance: 'Automatic dashboard deployment started.',\n priority: 2,\n source: 'deploy-script',\n eventType: 'auto_deploy_started',\n });\n}\n\nexport function _resetDeployPatrolForTests(): void {\n clearState();\n}\n\nexport async function runScheduledDeployPatrol(): Promise<void> {\n try {\n await runDeployPatrol({\n repoRoot: process.cwd(),\n config: loadCloisterConfigSync().deploy,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.warn(`[deacon] Automatic deploy patrol failed: ${message}`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"deploy-patrol-BXjlvoV3.js","names":[],"sources":["../../src/lib/cloister/deploy-patrol.ts"],"sourcesContent":["import { open, mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { execFile, spawn } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\n\nimport { emitActivityEntrySync, emitActivityTtsSync } from '../activity-logger.js';\nimport { getBuildInfo } from '../deploy/build-info.js';\nimport {\n clearPendingDeploy,\n markPendingDeployEscalated,\n readPendingDeploy,\n recordDeployIntent,\n type PendingDeploy,\n} from '../deploy/deploy-queue.js';\nimport {\n getDeployWindowAssessment,\n type DeployWindowAssessment,\n} from '../deploy/deploy-window.js';\nimport { computeBuildStaleness, type BuildStaleness } from '../deploy/staleness.js';\nimport { OVERDECK_HOME } from '../paths.js';\nimport { loadCloisterConfigSync, type DeployConfig } from './config.js';\n\nconst OBSERVATION_INTERVAL_MS = 30 * 60 * 1000;\nconst DEPLOY_SPAWN_COOLDOWN_MS = 10 * 60 * 1000;\nconst DEPLOY_PATROL_INITIATOR = 'deploy-patrol';\nconst CI_WORKFLOW_FILE = 'ci.yml';\nconst CI_RUN_FIELDS = 'databaseId,status,conclusion,headSha,attempt,createdAt';\nconst SYSTEMD_ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\ninterface DeployPatrolState {\n lastObservedBehind: number | null;\n lastObservationAt: number;\n lastDeferralReason: string | null;\n lastDeferralAt: number;\n lastDeploySpawnAt: number;\n lastEscalationReason: string | null;\n lastEscalationAt: number;\n unknownObserved: boolean;\n}\n\nconst state: DeployPatrolState = {\n lastObservedBehind: null,\n lastObservationAt: 0,\n lastDeferralReason: null,\n lastDeferralAt: 0,\n lastDeploySpawnAt: 0,\n lastEscalationReason: null,\n lastEscalationAt: 0,\n unknownObserved: false,\n};\n\ntype EmitEntry = typeof emitActivityEntrySync;\ntype EmitTts = typeof emitActivityTtsSync;\n\nexport interface DeployCiState {\n readonly status: 'green' | 'pending' | 'red' | 'unknown';\n readonly reason?: string;\n}\n\ninterface DeployCiRun {\n readonly databaseId: number;\n readonly status: string;\n readonly conclusion: string | null;\n readonly headSha: string;\n readonly attempt: number;\n readonly createdAt: string;\n}\n\nexport interface DeployPatrolContext {\n readonly repoRoot: string;\n readonly config: DeployConfig;\n readonly computeStaleness?: () => Promise<BuildStaleness>;\n readonly getCiState?: (sha: string) => Promise<DeployCiState>;\n readonly getWindowAssessment?: () => Promise<DeployWindowAssessment>;\n readonly readQueue?: typeof readPendingDeploy;\n readonly clearQueue?: typeof clearPendingDeploy;\n readonly recordIntent?: typeof recordDeployIntent;\n readonly markQueueEscalated?: typeof markPendingDeployEscalated;\n readonly spawnReload?: (request: ReloadRequest) => Promise<void>;\n readonly emitEntry?: EmitEntry;\n readonly emitTts?: EmitTts;\n readonly now?: () => number;\n}\n\nexport interface ReloadRequest {\n readonly command: string;\n readonly args: readonly string[];\n readonly cwd: string;\n readonly detached: true;\n readonly initiator: string;\n}\n\nfunction reloadEnvironment(request: ReloadRequest, env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n return { ...env, OVERDECK_AGENT_ID: request.initiator };\n}\n\nfunction execGh(args: readonly string[], cwd: string): Promise<string> {\n return new Promise((resolve, reject) => {\n execFile('gh', [...args], { cwd, encoding: 'utf8', timeout: 30_000 }, (error, stdout) => {\n if (error) reject(error);\n else resolve(stdout);\n });\n });\n}\n\nexport async function getDeployCiState(\n repoRoot: string,\n sha: string,\n runGh: (args: readonly string[], cwd: string) => Promise<string> = execGh,\n): Promise<DeployCiState> {\n try {\n const runs = JSON.parse(await runGh([\n 'run', 'list',\n '--branch', 'main',\n '--commit', sha,\n '--workflow', CI_WORKFLOW_FILE,\n '--limit', '10',\n '--json', CI_RUN_FIELDS,\n ], repoRoot)) as DeployCiRun[];\n const run = runs.find((candidate) => candidate.headSha === sha);\n const tip = shortSha(sha);\n\n if (!run) {\n return {\n status: 'pending',\n reason: `Automatic deployment deferred because CI has not started for origin/main ${tip}.`,\n };\n }\n if (run.status !== 'completed') {\n return {\n status: 'pending',\n reason: `Automatic deployment deferred because CI is ${run.status} for origin/main ${tip}.`,\n };\n }\n if (run.conclusion !== 'success') {\n return {\n status: 'red',\n reason: `Automatic deployment blocked because CI concluded ${run.conclusion || 'without a result'} for origin/main ${tip}.`,\n };\n }\n return { status: 'green' };\n } catch (error) {\n return {\n status: 'unknown',\n reason: `Automatic deployment deferred because CI status for origin/main ${shortSha(sha)} could not be read: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n\nexport function buildSystemdReloadArgs(\n request: ReloadRequest,\n now: number,\n logPath: string,\n env: NodeJS.ProcessEnv = process.env,\n): string[] {\n const setenvArgs = Object.entries(reloadEnvironment(request, env))\n .filter(([name, value]) => value !== undefined && SYSTEMD_ENV_NAME_RE.test(name))\n .flatMap(([name, value]) => ['--setenv', `${name}=${value}`]);\n\n return [\n '--user', '--unit', `overdeck-auto-deploy-${now}`,\n '--collect', '--quiet',\n '--property=Restart=on-failure',\n '--property=RestartSec=10s',\n '--property=StartLimitIntervalSec=0',\n `--property=StandardOutput=append:${logPath}`,\n `--property=StandardError=append:${logPath}`,\n `--property=WorkingDirectory=${request.cwd}`,\n ...setenvArgs,\n request.command,\n ...request.args,\n ];\n}\n\nfunction shortSha(sha: string | null): string {\n return sha?.slice(0, 8) || 'unknown';\n}\n\nexport async function waitForChildSpawn(\n child: Pick<ChildProcess, 'once' | 'removeListener' | 'unref'>,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const onSpawn = () => {\n cleanup();\n child.unref();\n resolve();\n };\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n const cleanup = () => {\n child.removeListener('spawn', onSpawn);\n child.removeListener('error', onError);\n };\n\n child.once('spawn', onSpawn);\n child.once('error', onError);\n });\n}\n\nasync function waitForChildExit(child: ChildProcess): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n child.once('error', reject);\n child.once('close', (code) => resolve(code ?? 1));\n });\n}\n\nasync function spawnDetachedReload(request: ReloadRequest, now: number): Promise<void> {\n const logsDir = join(OVERDECK_HOME, 'logs');\n const logPath = join(logsDir, 'auto-deploy.log');\n await mkdir(logsDir, { recursive: true });\n await writeFile(\n join(OVERDECK_HOME, 'dashboard-restarting.json'),\n JSON.stringify({ reason: 'auto-deploy', trigger: 'deacon', timestamp: now }, null, 2),\n 'utf8',\n );\n\n const log = await open(logPath, 'a');\n try {\n if (process.platform === 'linux') {\n let systemdFailure: string | null = null;\n try {\n const launcher = spawn(\n 'systemd-run',\n buildSystemdReloadArgs(request, now, logPath),\n {\n cwd: request.cwd,\n stdio: ['ignore', log.fd, log.fd],\n },\n );\n const exitCode = await waitForChildExit(launcher);\n if (exitCode === 0) return;\n systemdFailure = `systemd-run exited ${exitCode}`;\n } catch (error) {\n systemdFailure = error instanceof Error ? error.message : String(error);\n }\n throw new Error(\n `Automatic deploy requires an independent systemd unit before it can stop the dashboard: ${systemdFailure}`,\n );\n }\n\n const child = spawn(\n request.command,\n [...request.args],\n {\n cwd: request.cwd,\n detached: request.detached,\n env: reloadEnvironment(request, process.env),\n stdio: ['ignore', log.fd, log.fd],\n },\n );\n await waitForChildSpawn(child);\n } finally {\n await log.close();\n }\n}\n\nfunction clearState(): void {\n state.lastObservedBehind = null;\n state.lastObservationAt = 0;\n state.lastDeferralReason = null;\n state.lastDeferralAt = 0;\n state.lastDeploySpawnAt = 0;\n state.lastEscalationReason = null;\n state.lastEscalationAt = 0;\n state.unknownObserved = false;\n}\n\nfunction emitDeferral(reason: string, now: number, emitEntry: EmitEntry): void {\n if (\n state.lastDeferralReason === reason &&\n now - state.lastDeferralAt < OBSERVATION_INTERVAL_MS\n ) return;\n\n emitEntry({\n source: 'deploy-script',\n level: 'warn',\n message: reason,\n });\n state.lastDeferralReason = reason;\n state.lastDeferralAt = now;\n}\n\nasync function refreshQueuedDeployReason(\n queuedDeploy: PendingDeploy | null,\n reason: string,\n context: DeployPatrolContext,\n): Promise<void> {\n if (!queuedDeploy) return;\n await (context.recordIntent ?? recordDeployIntent)({\n requestedBy: DEPLOY_PATROL_INITIATOR,\n reason,\n blockedBy: [],\n });\n}\n\nasync function escalateOverdueQueue(\n queuedDeploy: PendingDeploy | null,\n blockReason: string,\n context: DeployPatrolContext,\n now: number,\n emitEntry: EmitEntry,\n emitTts: EmitTts,\n): Promise<void> {\n if (\n !queuedDeploy\n || queuedDeploy.escalated\n || now - Date.parse(queuedDeploy.requestedAt) <= context.config.queue_deadline_minutes * 60_000\n ) return;\n\n const ageMinutes = Math.floor((now - Date.parse(queuedDeploy.requestedAt)) / 60_000);\n const message = `Queued dashboard deploy has waited ${ageMinutes} minutes; current block: ${blockReason}`;\n if (\n state.lastEscalationReason !== blockReason\n || now - state.lastEscalationAt >= OBSERVATION_INTERVAL_MS\n ) {\n emitEntry({ source: 'deploy-script', level: 'error', message });\n emitTts({\n utterance: `Dashboard deploy has been queued for ${ageMinutes} minutes and needs attention.`,\n priority: 1,\n source: 'deploy-script',\n eventType: 'deploy_queue_stuck',\n });\n state.lastEscalationReason = blockReason;\n state.lastEscalationAt = now;\n }\n\n await (context.markQueueEscalated ?? markPendingDeployEscalated)();\n}\n\nexport async function runDeployPatrol(context: DeployPatrolContext): Promise<void> {\n const now = (context.now ?? Date.now)();\n const emitEntry = context.emitEntry ?? emitActivityEntrySync;\n const emitTts = context.emitTts ?? emitActivityTtsSync;\n const staleness = await (context.computeStaleness ?? (() => computeBuildStaleness({\n repoRoot: context.repoRoot,\n buildCommit: getBuildInfo().buildCommit,\n })))();\n const queuedDeploy = await (context.readQueue ?? readPendingDeploy)();\n\n if (staleness.status === 'unknown') {\n const reason = `Automatic deployment cannot compare the running build with origin/main: ${staleness.reason ?? 'build staleness is unknown'}`;\n if (!state.unknownObserved) {\n emitEntry({\n source: 'deploy-script',\n level: 'warn',\n message: reason,\n });\n state.unknownObserved = true;\n }\n await refreshQueuedDeployReason(queuedDeploy, reason, context);\n await escalateOverdueQueue(queuedDeploy, reason, context, now, emitEntry, emitTts);\n return;\n }\n\n if (staleness.status === 'fresh') {\n clearState();\n await (context.clearQueue ?? clearPendingDeploy)();\n return;\n }\n\n state.unknownObserved = false;\n const behind = staleness.behindBuildInputs ?? 0;\n if (\n state.lastObservedBehind !== behind ||\n now - state.lastObservationAt >= OBSERVATION_INTERVAL_MS\n ) {\n emitEntry({\n source: 'deploy-script',\n level: 'warn',\n message: `The running build ${shortSha(staleness.buildCommit)} is ${behind} build-input commit(s) behind origin/main ${shortSha(staleness.originMainSha)}. ${context.config.auto_deploy || queuedDeploy ? 'Automatic deployment will start after the merge debounce and safety checks clear.' : 'Automatic deployment is disabled, so operator action is required.'}`,\n });\n state.lastObservedBehind = behind;\n state.lastObservationAt = now;\n }\n\n if (!context.config.auto_deploy && !queuedDeploy) return;\n if (\n staleness.originMainLastBuildInputCommitAt !== null\n && now - staleness.originMainLastBuildInputCommitAt < context.config.debounce_minutes * 60 * 1000\n ) {\n const reason = 'Automatic deployment deferred because the merge debounce has not elapsed.';\n await refreshQueuedDeployReason(queuedDeploy, reason, context);\n await escalateOverdueQueue(queuedDeploy, reason, context, now, emitEntry, emitTts);\n return;\n }\n if (state.lastDeploySpawnAt > 0 && now - state.lastDeploySpawnAt < DEPLOY_SPAWN_COOLDOWN_MS) {\n const reason = 'Automatic deployment deferred because a recent deploy is still inside the spawn cooldown.';\n await refreshQueuedDeployReason(queuedDeploy, reason, context);\n await escalateOverdueQueue(queuedDeploy, reason, context, now, emitEntry, emitTts);\n return;\n }\n\n if (!staleness.originMainSha) {\n const reason = 'Automatic deployment deferred because the origin/main commit is unknown.';\n emitDeferral(reason, now, emitEntry);\n await refreshQueuedDeployReason(queuedDeploy, reason, context);\n await escalateOverdueQueue(queuedDeploy, reason, context, now, emitEntry, emitTts);\n return;\n }\n const ciState = await (context.getCiState ?? ((sha) => getDeployCiState(context.repoRoot, sha)))(staleness.originMainSha);\n if (ciState.status !== 'green') {\n const reason = ciState.reason\n ?? `Automatic deployment deferred because CI is ${ciState.status} for origin/main ${shortSha(staleness.originMainSha)}.`;\n emitDeferral(reason, now, emitEntry);\n await refreshQueuedDeployReason(queuedDeploy, reason, context);\n await escalateOverdueQueue(queuedDeploy, reason, context, now, emitEntry, emitTts);\n return;\n }\n\n const assessment = await (context.getWindowAssessment ?? getDeployWindowAssessment)();\n if (assessment.reason) {\n emitDeferral(assessment.reason, now, emitEntry);\n if (context.config.auto_deploy || queuedDeploy) {\n await (context.recordIntent ?? recordDeployIntent)({\n requestedBy: DEPLOY_PATROL_INITIATOR,\n reason: assessment.reason,\n blockedBy: [],\n });\n }\n await escalateOverdueQueue(\n queuedDeploy,\n assessment.reason,\n context,\n now,\n emitEntry,\n emitTts,\n );\n return;\n }\n\n const reloadRequest: ReloadRequest = {\n command: process.execPath,\n args: [join(context.repoRoot, 'dist/cli/index.js'), 'reload', '--health-timeout', '120000'],\n cwd: context.repoRoot,\n detached: true,\n initiator: DEPLOY_PATROL_INITIATOR,\n };\n await (context.spawnReload ?? ((request) => spawnDetachedReload(request, now)))(reloadRequest);\n state.lastDeploySpawnAt = now;\n state.lastDeferralReason = null;\n state.lastDeferralAt = 0;\n emitEntry({\n source: 'deploy-script',\n level: 'info',\n message: `Automatic deployment started for origin/main ${shortSha(staleness.originMainSha)}; the dashboard will rebuild and restart with a 120-second health timeout.${queuedDeploy ? ` This was a queued deploy requested by ${queuedDeploy.requestedBy.join(', ')}.` : ''}`,\n });\n emitTts({\n utterance: 'Automatic dashboard deployment started.',\n priority: 2,\n source: 'deploy-script',\n eventType: 'auto_deploy_started',\n });\n}\n\nexport function _resetDeployPatrolForTests(): void {\n clearState();\n}\n\nexport async function runScheduledDeployPatrol(): Promise<void> {\n try {\n await runDeployPatrol({\n repoRoot: process.cwd(),\n config: loadCloisterConfigSync().deploy,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.warn(`[deacon] Automatic deploy patrol failed: ${message}`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fi as init_paths, pi as CONFIG_FILE } from "./config-yaml-BwOEANMr.js";
|
|
2
2
|
import { t as require_toml } from "./toml-CeLDATih.js";
|
|
3
|
-
import { t as getBuildInfo } from "./build-info-
|
|
3
|
+
import { t as getBuildInfo } from "./build-info-DTX8JwVP.js";
|
|
4
4
|
import { relative, resolve, sep } from "node:path";
|
|
5
5
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
6
6
|
import { cwd } from "node:process";
|
|
@@ -55,4 +55,4 @@ function shouldRefuseHostDashboardPort(input) {
|
|
|
55
55
|
//#endregion
|
|
56
56
|
export { shouldRefuseHostDashboardPort as i, isNonPrimaryCheckoutRoot as n, readHostDashboardApiPort as r, getDashboardIdentity as t };
|
|
57
57
|
|
|
58
|
-
//# sourceMappingURL=identity-
|
|
58
|
+
//# sourceMappingURL=identity-Bwh6VGeL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-
|
|
1
|
+
{"version":3,"file":"identity-Bwh6VGeL.js","names":[],"sources":["../../src/dashboard/server/identity.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync } from 'node:fs';\nimport { cwd } from 'node:process';\nimport { relative, resolve, sep } from 'node:path';\n\nimport { parse as parseToml } from '@iarna/toml';\n\nimport { CONFIG_FILE } from '../../lib/paths.js';\nimport { getBuildInfo, type BuildInfo } from '../../lib/deploy/build-info.js';\n\nexport type DashboardMode = 'primary' | 'peer';\n\nexport interface DashboardIdentity extends BuildInfo {\n readonly repoRoot: string;\n readonly mode: DashboardMode;\n readonly pid: number;\n}\n\nexport function getDashboardIdentity(): DashboardIdentity {\n return {\n repoRoot: resolve(cwd()),\n mode: process.env.OVERDECK_DISABLE_DEACON === '1' ? 'peer' : 'primary',\n pid: process.pid,\n ...getBuildInfo(),\n };\n}\n\nexport function isWorkspaceRepoRoot(repoRoot: string): boolean {\n return /(^|\\/)workspaces\\/feature-[^/]+$/i.test(repoRoot.replaceAll('\\\\', '/'));\n}\n\nexport function isLinkedWorktreeRoot(repoRoot: string): boolean {\n try {\n return statSync(resolve(repoRoot, '.git')).isFile();\n } catch {\n return false;\n }\n}\n\nexport function primaryRootFromLinkedWorktree(repoRoot: string): string | null {\n try {\n const match = readFileSync(resolve(repoRoot, '.git'), 'utf-8').match(/^gitdir:\\s*(.+)$/m);\n const gitDir = match?.[1]?.trim().replaceAll('\\\\', '/');\n if (!gitDir) return null;\n\n const worktreeMarker = '/.git/worktrees/';\n const markerIndex = gitDir.indexOf(worktreeMarker);\n return markerIndex === -1 ? null : gitDir.slice(0, markerIndex);\n } catch {\n return null;\n }\n}\n\nfunction isSameOrInside(parent: string, candidate: string): boolean {\n const rel = relative(resolve(parent), resolve(candidate));\n return rel === '' || (!rel.startsWith('..') && !rel.startsWith(sep));\n}\n\nexport function isNonPrimaryCheckoutRoot(repoRoot: string): boolean {\n const resolvedRoot = resolve(repoRoot);\n return isLinkedWorktreeRoot(resolvedRoot) || isWorkspaceRepoRoot(resolvedRoot);\n}\n\nexport function readHostDashboardApiPort(defaultPort = 3011): number {\n if (!existsSync(CONFIG_FILE)) return defaultPort;\n try {\n const config = parseToml(readFileSync(CONFIG_FILE, 'utf-8')) as {\n dashboard?: { api_port?: unknown };\n };\n const value = config.dashboard?.api_port;\n return typeof value === 'number' && Number.isFinite(value) ? value : defaultPort;\n } catch {\n return defaultPort;\n }\n}\n\nexport function shouldRefuseHostDashboardPort(input: {\n readonly repoRoot: string;\n readonly mode: DashboardMode;\n readonly port: number;\n readonly hostDashboardApiPort?: number;\n readonly runningInContainer?: boolean;\n}): boolean {\n const hostDashboardApiPort = input.hostDashboardApiPort ?? readHostDashboardApiPort();\n if (input.port !== hostDashboardApiPort) return false;\n\n const repoRoot = resolve(input.repoRoot);\n const runningInContainer =\n input.runningInContainer ?? (existsSync('/.dockerenv') || repoRoot === '/workspaces/overdeck');\n if (runningInContainer) {\n return false;\n }\n if (input.mode === 'peer') return true;\n if (isNonPrimaryCheckoutRoot(repoRoot)) return true;\n\n const workspacesDir = resolve(repoRoot, '..');\n return workspacesDir.endsWith(`${sep}workspaces`) && isSameOrInside(workspacesDir, repoRoot);\n}\n"],"mappings":";;;;;;;;YAMiD;AAWjD,SAAgB,uBAA0C;AACxD,QAAO;EACL,UAAU,QAAQ,KAAK,CAAC;EACxB,MAAM,QAAQ,IAAI,4BAA4B,MAAM,SAAS;EAC7D,KAAK,QAAQ;EACb,GAAG,cAAc;EAClB;;AAGH,SAAgB,oBAAoB,UAA2B;AAC7D,QAAO,oCAAoC,KAAK,SAAS,WAAW,MAAM,IAAI,CAAC;;AAGjF,SAAgB,qBAAqB,UAA2B;AAC9D,KAAI;AACF,SAAO,SAAS,QAAQ,UAAU,OAAO,CAAC,CAAC,QAAQ;SAC7C;AACN,SAAO;;;AAkBX,SAAS,eAAe,QAAgB,WAA4B;CAClE,MAAM,MAAM,SAAS,QAAQ,OAAO,EAAE,QAAQ,UAAU,CAAC;AACzD,QAAO,QAAQ,MAAO,CAAC,IAAI,WAAW,KAAK,IAAI,CAAC,IAAI,WAAW,IAAI;;AAGrE,SAAgB,yBAAyB,UAA2B;CAClE,MAAM,eAAe,QAAQ,SAAS;AACtC,QAAO,qBAAqB,aAAa,IAAI,oBAAoB,aAAa;;AAGhF,SAAgB,yBAAyB,cAAc,MAAc;AACnE,KAAI,CAAC,WAAW,YAAY,CAAE,QAAO;AACrC,KAAI;EAIF,MAAM,SAAA,GAAA,YAAA,OAHmB,aAAa,aAAa,QAAQ,CAGvC,CAAC,WAAW;AAChC,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,MAAM,GAAG,QAAQ;SAC/D;AACN,SAAO;;;AAIX,SAAgB,8BAA8B,OAMlC;CACV,MAAM,uBAAuB,MAAM,wBAAwB,0BAA0B;AACrF,KAAI,MAAM,SAAS,qBAAsB,QAAO;CAEhD,MAAM,WAAW,QAAQ,MAAM,SAAS;AAGxC,KADE,MAAM,uBAAuB,WAAW,cAAc,IAAI,aAAa,wBAEvE,QAAO;AAET,KAAI,MAAM,SAAS,OAAQ,QAAO;AAClC,KAAI,yBAAyB,SAAS,CAAE,QAAO;CAE/C,MAAM,gBAAgB,QAAQ,UAAU,KAAK;AAC7C,QAAO,cAAc,SAAS,GAAG,IAAI,YAAY,IAAI,eAAe,eAAe,SAAS"}
|
|
@@ -2,10 +2,10 @@ import { t as archivePlanning } from "./archive-planning-BwEhu9Oq.js";
|
|
|
2
2
|
import { a as stepSkipped, i as stepOk, n as init_types, r as stepFailed } from "./types-DjpLVpsj.js";
|
|
3
3
|
import { t as closeIssue } from "./close-issue-J0_eWYT1.js";
|
|
4
4
|
import { t as teardownWorkspace } from "./teardown-workspace-CYend-KY.js";
|
|
5
|
-
import { a as deepWipe, i as closeOut, n as cancelIssueWorkflow, o as resetToTodo, r as close, t as approve } from "./workflows-
|
|
5
|
+
import { a as deepWipe, i as closeOut, n as cancelIssueWorkflow, o as resetToTodo, r as close, t as approve } from "./workflows-BYwR2aIN.js";
|
|
6
6
|
//#region ../../lib/lifecycle/index.ts
|
|
7
7
|
init_types();
|
|
8
8
|
//#endregion
|
|
9
9
|
export { approve, archivePlanning, cancelIssueWorkflow, close, closeIssue, closeOut, deepWipe, resetToTodo, stepFailed, stepOk, stepSkipped, teardownWorkspace };
|
|
10
10
|
|
|
11
|
-
//# sourceMappingURL=lifecycle-
|
|
11
|
+
//# sourceMappingURL=lifecycle-B0rL38D3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-
|
|
1
|
+
{"version":3,"file":"lifecycle-B0rL38D3.js","names":[],"sources":["../../src/lib/lifecycle/index.ts"],"sourcesContent":["/**\n * Lifecycle — Composable, idempotent post-merge operations.\n *\n * Atomic operations:\n * archive-planning — PRD active→completed + workspace .pan/ preservation\n * close-issue — IssueTracker transition + label management\n * teardown-workspace — Full workspace cleanup (agent, worktree, Docker, tmux)\n *\n * Workflows (compose the above):\n * approve() — merge + close + archive + teardown + compact\n * close() — close + teardown\n * closeOut() — verify-merged + archive + teardown + close + label + clear-status\n * deepWipe() — teardown(deleteBranches) + delete agent state + reset issue\n */\n\n// Types\nexport type {\n StepResult,\n WorkflowResult,\n LifecycleContext,\n TeardownOptions,\n ArchiveOptions,\n ApproveOptions,\n DeepWipeOptions,\n CloseOutOptions,\n} from './types.js';\n\nexport { stepOk, stepSkipped, stepFailed } from './types.js';\n\n// Atomic operations\nexport { archivePlanning } from './archive-planning.js';\nexport { closeIssue } from './close-issue.js';\nexport { teardownWorkspace } from './teardown-workspace.js';\n\n// Workflows\nexport { approve, close, closeOut, deepWipe, resetToTodo, cancelIssueWorkflow } from './workflows.js';\n"],"mappings":";;;;;;YA2B6D"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { i as __require, t as __commonJSMin } from "./rolldown-runtime-DNCzzKnI.js";
|
|
2
2
|
import { On as resolveProjectFromIssueSync, S as init_defaults, c as init_load, fn as init_projects, oi as init_errors, p as loadConfigSync, x as DEFAULT_CONFIG } from "./config-yaml-BwOEANMr.js";
|
|
3
|
-
import { t as getBuildInfo } from "./build-info-
|
|
4
|
-
import { t as getDashboardIdentity } from "./identity-
|
|
3
|
+
import { t as getBuildInfo } from "./build-info-DTX8JwVP.js";
|
|
4
|
+
import { t as getDashboardIdentity } from "./identity-Bwh6VGeL.js";
|
|
5
5
|
import { h as loadConfigSync$1, p as init_config } from "./config-tucKBCCc.js";
|
|
6
6
|
import { a as init_event_store, i as initEventStore } from "./event-store-D7qcJXuZ.js";
|
|
7
7
|
import { D as projectLegacySystemHealthSummary, t as init_src } from "./src-DudQNYVw.js";
|
|
@@ -23107,4 +23107,4 @@ async function assessMemoryPressure() {
|
|
|
23107
23107
|
//#endregion
|
|
23108
23108
|
export { getResourceStacks as a, getCurrentDockerStats as c, getResourceConfig as d, getSystemHealthEvidenceSnapshot as f, classifyAgentHealth as g, startSmeeProcessSync as h, readGovernorWatchReserveBytes as i, getAcceptedSystemHealthSnapshot as l, isSmeeConfiguredSync as m, classifyMemoryPressure as n, formatUptime as o, getSystemHealthSnapshot as p, readGovernorReserves as r, getContainerHistory as s, assessMemoryPressure as t, getDeployStaleness as u };
|
|
23109
23109
|
|
|
23110
|
-
//# sourceMappingURL=memory-governor-
|
|
23110
|
+
//# sourceMappingURL=memory-governor-A4O6vI9d.js.map
|