(live: LiveShellRun, abandoned: boolean)
| 1216 | } |
| 1217 | |
| 1218 | private async finalizeLive(live: LiveShellRun, abandoned: boolean): Promise<ShellRunRecord> { |
| 1219 | if (live.termination) await live.termination.finished.join(); |
| 1220 | if (abandoned && !live.integrityFailure) { |
| 1221 | live.integrityFailure = new Error( |
| 1222 | 'Shell process did not acknowledge exit after forced termination', |
| 1223 | ); |
| 1224 | } |
| 1225 | this.clearLiveTimers(live); |
| 1226 | if (live.mode === 'pty') live.collector.closeDataAdmission(); |
| 1227 | |
| 1228 | let finalSnapshot: SnapshotAtCut; |
| 1229 | try { |
| 1230 | finalSnapshot = await this.snapshotAtCut(live, true); |
| 1231 | } catch (error) { |
| 1232 | live.integrityFailure ??= asError(error, 'ShellRun final snapshot failed'); |
| 1233 | finalSnapshot = { |
| 1234 | output: live.record.output, |
| 1235 | generation: live.lastPersistedGeneration, |
| 1236 | }; |
| 1237 | } |
| 1238 | let cleanupError: Error | undefined; |
| 1239 | try { |
| 1240 | if (live.mode === 'pty') live.collector.dispose(); |
| 1241 | } catch (error) { |
| 1242 | cleanupError ??= asError(error, 'PTY collector cleanup failed'); |
| 1243 | } |
| 1244 | try { |
| 1245 | live.driver.dispose(); |
| 1246 | } catch (error) { |
| 1247 | cleanupError ??= asError(error, 'Shell process driver cleanup failed'); |
| 1248 | } |
| 1249 | |
| 1250 | live.integrityFailure ??= cleanupError; |
| 1251 | |
| 1252 | let finalRecord: ShellRunRecord | undefined; |
| 1253 | let completionError: Error | undefined; |
| 1254 | try { |
| 1255 | finalRecord = await this.queuePersist(live, this.finalState(live), { |
| 1256 | bestEffort: true, |
| 1257 | snapshotBarrier: Promise.resolve(finalSnapshot), |
| 1258 | }); |
| 1259 | } catch (error) { |
| 1260 | completionError = asError(error, 'ShellRun final persistence failed'); |
| 1261 | } |
| 1262 | try { |
| 1263 | if (completionError || !finalRecord) { |
| 1264 | const error = |
| 1265 | completionError ?? |
| 1266 | new Error(`ShellRun ${live.shellRunId} finalized without a durable record`); |
| 1267 | live.finished.reject(error); |
| 1268 | throw error; |
| 1269 | } |
| 1270 | this.notifyCompletionOwner( |
| 1271 | live, |
| 1272 | finalRecord.status === 'completed' && finalRecord.exitCode === 0, |
| 1273 | ); |
| 1274 | live.finished.resolve(finalRecord); |
| 1275 | return finalRecord; |
no test coverage detected