@@ -3,15 +3,46 @@ import path from "node:path";
33import type { OperationDispatcher , OperationRequestContext } from "../operation-dispatcher.js" ;
44import { DirectoryNotAllowedError , assertPathAllowed } from "../security.js" ;
55import { expandHome , resolveWorktreeDir } from "./symphony-utils.js" ;
6+ import type { JobStore , LocalJob } from "../../main/job-store.js" ;
67
78type ResolveResult =
89 | { pid : number ; pidFilePath : string | null ; worktreeDir : string | null }
910 | { noPidFile : true ; worktreeDir : string }
1011 | { error : string ; status : number } ;
1112
13+ function findJobForKill (
14+ jobStore : JobStore ,
15+ pid : number | null ,
16+ worktreeDir : string | null
17+ ) : LocalJob | undefined {
18+ const running = jobStore . listRunning ( ) ;
19+ if ( pid != null ) {
20+ const byPid = running . find ( ( j ) => j . pid === pid ) ;
21+ if ( byPid ) return byPid ;
22+ }
23+ if ( worktreeDir != null ) {
24+ return running . find ( ( j ) => j . worktreeDir === worktreeDir ) ;
25+ }
26+ return undefined ;
27+ }
28+
29+ function markJobStopped (
30+ jobStore : JobStore | undefined ,
31+ pid : number | null ,
32+ worktreeDir : string | null
33+ ) : void {
34+ if ( ! jobStore ) return ;
35+ const job = findJobForKill ( jobStore , pid , worktreeDir ) ;
36+ if ( job ) {
37+ const now = new Date ( ) . toISOString ( ) ;
38+ jobStore . upsert ( { ...job , status : "STOPPED" , updatedAt : now , completedAt : now } ) ;
39+ }
40+ }
41+
1242export function registerSymphonyKillRoutes (
1343 dispatcher : OperationDispatcher ,
14- getAllowedDirectories : ( ) => string [ ]
44+ getAllowedDirectories : ( ) => string [ ] ,
45+ jobStore ?: JobStore
1546) : void {
1647 dispatcher . register ( "POST" , "/api/engineer/symphony/kill" , async ( context ) => {
1748 try {
@@ -30,6 +61,7 @@ export function registerSymphonyKillRoutes(
3061 if ( "noPidFile" in resolved ) {
3162 cancelLoop ( resolved . worktreeDir ) ;
3263 markStateAsStopped ( resolved . worktreeDir ) ;
64+ markJobStopped ( jobStore , null , resolved . worktreeDir ) ;
3365 json ( context , 200 , {
3466 success : true ,
3567 message : "No process to kill (no PID file), state marked as stopped"
@@ -50,6 +82,7 @@ export function registerSymphonyKillRoutes(
5082 if ( worktreeDir ) {
5183 markStateAsStopped ( worktreeDir ) ;
5284 }
85+ markJobStopped ( jobStore , pid , worktreeDir ) ;
5386 json ( context , 200 , { success : true , message : "Process already terminated" , pid } ) ;
5487 return ;
5588 }
@@ -69,6 +102,7 @@ export function registerSymphonyKillRoutes(
69102 if ( worktreeDir ) {
70103 markStateAsStopped ( worktreeDir ) ;
71104 }
105+ markJobStopped ( jobStore , pid , worktreeDir ) ;
72106
73107 json ( context , 200 , { success : true , message : "Process terminated" , pid } ) ;
74108 } catch ( error ) {
@@ -78,6 +112,13 @@ export function registerSymphonyKillRoutes(
78112 if ( worktreeDir ) {
79113 markStateAsStopped ( worktreeDir ) ;
80114 }
115+ if ( jobStore ) {
116+ const job = findJobForKill ( jobStore , pid , worktreeDir ) ;
117+ if ( job ) {
118+ const now = new Date ( ) . toISOString ( ) ;
119+ jobStore . upsert ( { ...job , status : "STOPPED" , updatedAt : now , completedAt : now } ) ;
120+ }
121+ }
81122 json ( context , 200 , { success : true , message : "Process already terminated" , pid } ) ;
82123 return ;
83124 }
0 commit comments