11import { Command } from 'commander' ;
22import chalk from 'chalk' ;
3- import fs from 'fs' ;
4- import path from 'path' ;
5- import os from 'os' ;
6-
7- // PID file location
8- const PID_FILE = path . join ( os . tmpdir ( ) , 'deploystack-gateway.pid' ) ;
3+ import { ServerStopService } from '../services/server-stop-service' ;
94
105export function registerStopCommand ( program : Command ) {
116 program
@@ -14,134 +9,47 @@ export function registerStopCommand(program: Command) {
149 . option ( '-f, --force' , 'Force stop the server (SIGKILL)' )
1510 . option ( '--timeout <seconds>' , 'Timeout for graceful shutdown in seconds' , '30' )
1611 . action ( async ( options ) => {
12+ const stopService = new ServerStopService ( ) ;
13+
1714 try {
1815 const timeoutSeconds = parseInt ( options . timeout , 10 ) || 30 ;
1916 console . log ( chalk . blue ( '🛑 Stopping DeployStack Gateway...' ) ) ;
2017
21- // Check if PID file exists
22- if ( ! fs . existsSync ( PID_FILE ) ) {
23- console . log ( chalk . yellow ( '⚠️ Gateway server is not running (no PID file found)' ) ) ;
24- return ;
25- }
26-
27- // Read PID from file
28- const pidStr = fs . readFileSync ( PID_FILE , 'utf8' ) . trim ( ) ;
29- const pid = parseInt ( pidStr , 10 ) ;
30-
31- if ( isNaN ( pid ) ) {
32- console . log ( chalk . red ( '❌ Invalid PID in PID file' ) ) ;
33- removePidFile ( ) ;
34- return ;
35- }
36-
37- // Check if process is running
38- if ( ! isProcessRunning ( pid ) ) {
39- console . log ( chalk . yellow ( '⚠️ Gateway server is not running (process not found)' ) ) ;
40- removePidFile ( ) ;
41- return ;
42- }
43-
44- // NEW: The gateway now handles MCP server shutdown internally
45- // When we send SIGTERM to the gateway, it will:
46- // 1. Stop all MCP servers gracefully (following MCP spec)
47- // 2. Stop the HTTP server
48- // 3. Clean up and exit
49-
50- if ( options . force ) {
51- // Force stop - send SIGKILL immediately
18+ // Get current PID for progress messages
19+ const pid = stopService . getRunningPid ( ) ;
20+
21+ if ( options . force && pid ) {
5222 console . log ( chalk . yellow ( '⚠️ Force stopping gateway (MCP servers may not shutdown gracefully)' ) ) ;
5323 console . log ( chalk . gray ( ` Sending SIGKILL to process ${ pid } ...` ) ) ;
54-
55- try {
56- process . kill ( pid , 'SIGKILL' ) ;
57- console . log ( chalk . green ( '✅ Gateway server force stopped' ) ) ;
58- removePidFile ( ) ;
59- } catch {
60- console . log ( chalk . yellow ( '⚠️ Process was already stopped' ) ) ;
61- removePidFile ( ) ;
62- }
63- } else {
64- // Graceful stop - send SIGTERM and wait
24+ } else if ( pid ) {
6525 console . log ( chalk . gray ( ` Sending SIGTERM to process ${ pid } ...` ) ) ;
6626 console . log ( chalk . gray ( ' Gateway will stop MCP servers first, then HTTP server...' ) ) ;
27+ console . log ( chalk . gray ( ` Waiting for graceful shutdown (timeout: ${ timeoutSeconds } s)...` ) ) ;
28+ }
6729
68- try {
69- process . kill ( pid , 'SIGTERM' ) ;
70-
71- // Wait for graceful shutdown with longer timeout for MCP server cleanup
72- console . log ( chalk . gray ( ` Waiting for graceful shutdown (timeout: ${ timeoutSeconds } s)...` ) ) ;
73-
74- let attempts = 0 ;
75- const maxAttempts = timeoutSeconds * 2 ; // Check every 500ms
76-
77- while ( attempts < maxAttempts && isProcessRunning ( pid ) ) {
78- await new Promise ( resolve => setTimeout ( resolve , 500 ) ) ;
79- attempts ++ ;
80-
81- // Show progress every 5 seconds
82- if ( attempts % 10 === 0 ) {
83- const elapsed = Math . floor ( attempts / 2 ) ;
84- console . log ( chalk . gray ( ` Still shutting down... (${ elapsed } s elapsed)` ) ) ;
85- }
86- }
87-
88- if ( isProcessRunning ( pid ) ) {
89- console . log ( chalk . yellow ( `⚠️ Process did not stop within ${ timeoutSeconds } s, forcing shutdown...` ) ) ;
90- process . kill ( pid , 'SIGKILL' ) ;
91- await new Promise ( resolve => setTimeout ( resolve , 2000 ) ) ;
92-
93- if ( isProcessRunning ( pid ) ) {
94- console . log ( chalk . red ( '❌ Failed to stop process even with SIGKILL' ) ) ;
95- console . log ( chalk . gray ( ` Process ${ pid } may be stuck - manual intervention required` ) ) ;
96- process . exit ( 1 ) ;
97- } else {
98- console . log ( chalk . yellow ( '⚠️ Gateway force stopped after timeout' ) ) ;
99- }
100- } else {
101- console . log ( chalk . green ( '✅ Gateway server stopped gracefully' ) ) ;
102- }
103-
104- removePidFile ( ) ;
105- } catch ( error ) {
106- if ( ( error as NodeJS . ErrnoException ) . code === 'ESRCH' ) {
107- console . log ( chalk . yellow ( '⚠️ Process was already stopped' ) ) ;
108- removePidFile ( ) ;
109- } else {
110- throw error ;
111- }
30+ // Use the service to stop the server
31+ const result = await stopService . stopGatewayServer ( {
32+ force : options . force ,
33+ timeout : timeoutSeconds
34+ } ) ;
35+
36+ if ( result . success ) {
37+ if ( result . wasRunning ) {
38+ console . log ( chalk . green ( `✅ ${ result . message } ` ) ) ;
39+ console . log ( chalk . gray ( '💡 All MCP servers have been stopped along with the gateway' ) ) ;
40+ } else {
41+ console . log ( chalk . yellow ( `⚠️ ${ result . message } ` ) ) ;
11242 }
43+ } else {
44+ console . log ( chalk . red ( `❌ ${ result . message } ` ) ) ;
45+ process . exit ( 1 ) ;
11346 }
11447
115- console . log ( chalk . gray ( '💡 All MCP servers have been stopped along with the gateway' ) ) ;
116-
11748 } catch ( error ) {
118- console . error ( chalk . red ( '❌ Failed to stop gateway:' ) , error ) ;
49+ console . error ( chalk . red ( '❌ Failed to stop gateway:' ) , error instanceof Error ? error . message : String ( error ) ) ;
11950 process . exit ( 1 ) ;
12051 }
12152 } ) ;
12253}
12354
124- /**
125- * Check if process is running
126- */
127- function isProcessRunning ( pid : number ) : boolean {
128- try {
129- process . kill ( pid , 0 ) ; // Signal 0 checks if process exists
130- return true ;
131- } catch {
132- return false ;
133- }
134- }
13555
136- /**
137- * Remove PID file
138- */
139- function removePidFile ( ) : void {
140- try {
141- if ( fs . existsSync ( PID_FILE ) ) {
142- fs . unlinkSync ( PID_FILE ) ;
143- }
144- } catch {
145- console . warn ( chalk . yellow ( '⚠️ Could not remove PID file:' ) ) ;
146- }
147- }
0 commit comments