@shepai/cli 1.64.2 → 1.65.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/dist/src/presentation/cli/commands/daemon/stop-daemon.d.ts +27 -0
- package/dist/src/presentation/cli/commands/daemon/stop-daemon.d.ts.map +1 -0
- package/dist/src/presentation/cli/commands/daemon/stop-daemon.js +77 -0
- package/dist/src/presentation/cli/commands/restart.command.d.ts +14 -0
- package/dist/src/presentation/cli/commands/restart.command.d.ts.map +1 -0
- package/dist/src/presentation/cli/commands/restart.command.js +47 -0
- package/dist/src/presentation/cli/commands/stop.command.d.ts +1 -9
- package/dist/src/presentation/cli/commands/stop.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/stop.command.js +6 -58
- package/dist/src/presentation/cli/commands/upgrade.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/upgrade.command.js +30 -4
- package/dist/src/presentation/cli/index.d.ts +1 -0
- package/dist/src/presentation/cli/index.d.ts.map +1 -1
- package/dist/src/presentation/cli/index.js +3 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/cache/.previewinfo +1 -1
- package/web/.next/cache/.rscinfo +1 -1
- package/web/.next/cache/config.json +3 -3
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +1 -1
- package/web/.next/required-server-files.json +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/page/server-reference-manifest.json +14 -14
- package/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fbc89707._.js +1 -1
- package/web/.next/server/chunks/ssr/_d3711354._.js +1 -1
- package/web/.next/server/chunks/ssr/_d3711354._.js.map +1 -1
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +15 -15
- package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +14 -14
- package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__fbc89707._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_d3711354._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +15 -15
- package/web/.next/standalone/src/presentation/web/server.js +1 -1
- package/web/.next/static/chunks/{6ed00d2d29823855.js → 233fbb89beb137e8.js} +1 -1
- package/web/.next/static/chunks/{60dc6ac13a7c312d.js → fa8058049a43c698.js} +2 -2
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- /package/web/.next/static/{LmnCUTqidV1JIrYPVF8ji → zuqVLdEhCDdtLqCuWgUm5}/_buildManifest.js +0 -0
- /package/web/.next/static/{LmnCUTqidV1JIrYPVF8ji → zuqVLdEhCDdtLqCuWgUm5}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{LmnCUTqidV1JIrYPVF8ji → zuqVLdEhCDdtLqCuWgUm5}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stopDaemon() — Shared daemon-stop helper
|
|
3
|
+
*
|
|
4
|
+
* Contains the stop logic for gracefully terminating a running Shep daemon.
|
|
5
|
+
* Used by:
|
|
6
|
+
* - stop.command.ts (shep stop)
|
|
7
|
+
* - restart.command.ts (shep restart)
|
|
8
|
+
* - upgrade.command.ts (shep upgrade — stops before install)
|
|
9
|
+
*
|
|
10
|
+
* Stop sequence:
|
|
11
|
+
* 1. Read daemon.json via IDaemonService
|
|
12
|
+
* 2. If no daemon / PID not alive: silently clean up daemon.json and return
|
|
13
|
+
* 3. Validate PID is a positive finite integer
|
|
14
|
+
* 4. Send SIGTERM
|
|
15
|
+
* 5. Poll process liveness every 200ms for up to 5000ms
|
|
16
|
+
* 6. If still alive after 5s, send SIGKILL (errors suppressed — process may have exited)
|
|
17
|
+
* 7. Always delete daemon.json (in finally block)
|
|
18
|
+
*
|
|
19
|
+
* @param daemonService - IDaemonService instance (injected by caller for testability, NFR-4)
|
|
20
|
+
*/
|
|
21
|
+
import type { IDaemonService } from '../../../../../packages/core/src/application/ports/output/services/daemon-service.interface.js';
|
|
22
|
+
/**
|
|
23
|
+
* Stop the Shep daemon gracefully.
|
|
24
|
+
* Safe to call when no daemon is running — silently cleans up stale daemon.json (NFR-2).
|
|
25
|
+
*/
|
|
26
|
+
export declare function stopDaemon(daemonService: IDaemonService): Promise<void>;
|
|
27
|
+
//# sourceMappingURL=stop-daemon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-daemon.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/daemon/stop-daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iEAAiE,CAAC;AA0BtG;;;GAGG;AACH,wBAAsB,UAAU,CAAC,aAAa,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAyC7E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stopDaemon() — Shared daemon-stop helper
|
|
3
|
+
*
|
|
4
|
+
* Contains the stop logic for gracefully terminating a running Shep daemon.
|
|
5
|
+
* Used by:
|
|
6
|
+
* - stop.command.ts (shep stop)
|
|
7
|
+
* - restart.command.ts (shep restart)
|
|
8
|
+
* - upgrade.command.ts (shep upgrade — stops before install)
|
|
9
|
+
*
|
|
10
|
+
* Stop sequence:
|
|
11
|
+
* 1. Read daemon.json via IDaemonService
|
|
12
|
+
* 2. If no daemon / PID not alive: silently clean up daemon.json and return
|
|
13
|
+
* 3. Validate PID is a positive finite integer
|
|
14
|
+
* 4. Send SIGTERM
|
|
15
|
+
* 5. Poll process liveness every 200ms for up to 5000ms
|
|
16
|
+
* 6. If still alive after 5s, send SIGKILL (errors suppressed — process may have exited)
|
|
17
|
+
* 7. Always delete daemon.json (in finally block)
|
|
18
|
+
*
|
|
19
|
+
* @param daemonService - IDaemonService instance (injected by caller for testability, NFR-4)
|
|
20
|
+
*/
|
|
21
|
+
import { messages } from '../../ui/index.js';
|
|
22
|
+
const POLL_INTERVAL_MS = 200;
|
|
23
|
+
const MAX_WAIT_MS = 5000;
|
|
24
|
+
/**
|
|
25
|
+
* Poll until the given PID is dead or the timeout expires.
|
|
26
|
+
* Returns true if the process is dead, false if it survived the timeout.
|
|
27
|
+
*/
|
|
28
|
+
async function pollUntilDead(daemonService, pid, maxMs, intervalMs) {
|
|
29
|
+
const deadline = Date.now() + maxMs;
|
|
30
|
+
while (Date.now() < deadline) {
|
|
31
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
32
|
+
if (!daemonService.isAlive(pid)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Stop the Shep daemon gracefully.
|
|
40
|
+
* Safe to call when no daemon is running — silently cleans up stale daemon.json (NFR-2).
|
|
41
|
+
*/
|
|
42
|
+
export async function stopDaemon(daemonService) {
|
|
43
|
+
const state = await daemonService.read();
|
|
44
|
+
// No daemon running or PID not alive — silently clean up and return
|
|
45
|
+
if (!state || !daemonService.isAlive(state.pid)) {
|
|
46
|
+
await daemonService.delete();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const { pid } = state;
|
|
50
|
+
// Validate PID is a positive finite integer before kill
|
|
51
|
+
if (!Number.isFinite(pid) || !Number.isInteger(pid) || pid <= 0) {
|
|
52
|
+
await daemonService.delete();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
messages.info(`Stopping Shep daemon (PID ${pid})...`);
|
|
57
|
+
// Send SIGTERM — request graceful shutdown
|
|
58
|
+
process.kill(pid, 'SIGTERM');
|
|
59
|
+
// Poll for up to 5s waiting for the process to exit
|
|
60
|
+
const died = await pollUntilDead(daemonService, pid, MAX_WAIT_MS, POLL_INTERVAL_MS);
|
|
61
|
+
if (!died) {
|
|
62
|
+
// Graceful shutdown timed out — force kill
|
|
63
|
+
messages.info('Daemon did not stop gracefully, sending SIGKILL...');
|
|
64
|
+
try {
|
|
65
|
+
process.kill(pid, 'SIGKILL');
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Process may have exited between the check and the kill — ignore
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
messages.success('Shep daemon stopped.');
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
// Always clean up daemon.json regardless of termination path
|
|
75
|
+
await daemonService.delete();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* restart Command
|
|
3
|
+
*
|
|
4
|
+
* Gracefully restarts the Shep web UI daemon. If the daemon is not running,
|
|
5
|
+
* starts it instead. Accepts an optional --port flag (parity with shep start).
|
|
6
|
+
*
|
|
7
|
+
* Usage: shep restart [--port <number>]
|
|
8
|
+
*/
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
/**
|
|
11
|
+
* Create the restart command.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createRestartCommand(): Command;
|
|
14
|
+
//# sourceMappingURL=restart.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/restart.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAe1D;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CA2B9C"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* restart Command
|
|
3
|
+
*
|
|
4
|
+
* Gracefully restarts the Shep web UI daemon. If the daemon is not running,
|
|
5
|
+
* starts it instead. Accepts an optional --port flag (parity with shep start).
|
|
6
|
+
*
|
|
7
|
+
* Usage: shep restart [--port <number>]
|
|
8
|
+
*/
|
|
9
|
+
import { Command, InvalidArgumentError } from 'commander';
|
|
10
|
+
import { container } from '../../../../packages/core/src/infrastructure/di/container.js';
|
|
11
|
+
import { messages } from '../ui/index.js';
|
|
12
|
+
import { stopDaemon } from './daemon/stop-daemon.js';
|
|
13
|
+
import { startDaemon } from './daemon/start-daemon.js';
|
|
14
|
+
function parsePort(value) {
|
|
15
|
+
const port = parseInt(value, 10);
|
|
16
|
+
if (isNaN(port) || port < 1024 || port > 65535) {
|
|
17
|
+
throw new InvalidArgumentError('Port must be an integer between 1024 and 65535');
|
|
18
|
+
}
|
|
19
|
+
return port;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create the restart command.
|
|
23
|
+
*/
|
|
24
|
+
export function createRestartCommand() {
|
|
25
|
+
return new Command('restart')
|
|
26
|
+
.description('Gracefully restart the Shep web UI daemon (starts it if not running)')
|
|
27
|
+
.option('-p, --port <number>', 'Port number (1024-65535)', parsePort)
|
|
28
|
+
.addHelpText('after', `
|
|
29
|
+
Examples:
|
|
30
|
+
$ shep restart Restart (or start) on default port
|
|
31
|
+
$ shep restart --port 8080 Restart on custom port`)
|
|
32
|
+
.action(async (options) => {
|
|
33
|
+
const daemonService = container.resolve('IDaemonService');
|
|
34
|
+
const state = await daemonService.read();
|
|
35
|
+
const isRunning = state !== null && daemonService.isAlive(state.pid);
|
|
36
|
+
if (isRunning) {
|
|
37
|
+
// Daemon is running — stop it then start it, preserving the port unless overridden
|
|
38
|
+
await stopDaemon(daemonService);
|
|
39
|
+
await startDaemon({ port: options.port ?? state.port });
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Daemon is not running — start it directly
|
|
43
|
+
messages.info('Daemon was not running — starting...');
|
|
44
|
+
await startDaemon({ port: options.port });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -2,15 +2,7 @@
|
|
|
2
2
|
* stop Command
|
|
3
3
|
*
|
|
4
4
|
* Stops the running Shep web UI daemon.
|
|
5
|
-
*
|
|
6
|
-
* Stop sequence:
|
|
7
|
-
* 1. Read daemon.json via IDaemonService
|
|
8
|
-
* 2. If no daemon / PID not alive: print clear message and exit 0
|
|
9
|
-
* 3. Validate PID is a positive finite integer
|
|
10
|
-
* 4. Send SIGTERM
|
|
11
|
-
* 5. Poll process liveness every 200ms for up to 5000ms
|
|
12
|
-
* 6. If still alive after 5s, send SIGKILL
|
|
13
|
-
* 7. Always delete daemon.json (in finally block)
|
|
5
|
+
* Stop logic is implemented in the shared stopDaemon() helper.
|
|
14
6
|
*
|
|
15
7
|
* Usage: shep stop
|
|
16
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stop.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/stop.command.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"stop.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/stop.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAc3C"}
|
|
@@ -2,37 +2,14 @@
|
|
|
2
2
|
* stop Command
|
|
3
3
|
*
|
|
4
4
|
* Stops the running Shep web UI daemon.
|
|
5
|
-
*
|
|
6
|
-
* Stop sequence:
|
|
7
|
-
* 1. Read daemon.json via IDaemonService
|
|
8
|
-
* 2. If no daemon / PID not alive: print clear message and exit 0
|
|
9
|
-
* 3. Validate PID is a positive finite integer
|
|
10
|
-
* 4. Send SIGTERM
|
|
11
|
-
* 5. Poll process liveness every 200ms for up to 5000ms
|
|
12
|
-
* 6. If still alive after 5s, send SIGKILL
|
|
13
|
-
* 7. Always delete daemon.json (in finally block)
|
|
5
|
+
* Stop logic is implemented in the shared stopDaemon() helper.
|
|
14
6
|
*
|
|
15
7
|
* Usage: shep stop
|
|
16
8
|
*/
|
|
17
9
|
import { Command } from 'commander';
|
|
18
10
|
import { container } from '../../../../packages/core/src/infrastructure/di/container.js';
|
|
19
11
|
import { messages } from '../ui/index.js';
|
|
20
|
-
|
|
21
|
-
const MAX_WAIT_MS = 5000;
|
|
22
|
-
/**
|
|
23
|
-
* Poll until the given PID is dead or the timeout expires.
|
|
24
|
-
* Returns true if the process is dead, false if it survived the timeout.
|
|
25
|
-
*/
|
|
26
|
-
async function pollUntilDead(daemonService, pid, maxMs, intervalMs) {
|
|
27
|
-
const deadline = Date.now() + maxMs;
|
|
28
|
-
while (Date.now() < deadline) {
|
|
29
|
-
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
30
|
-
if (!daemonService.isAlive(pid)) {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
12
|
+
import { stopDaemon } from './daemon/stop-daemon.js';
|
|
36
13
|
/**
|
|
37
14
|
* Create the stop command.
|
|
38
15
|
*/
|
|
@@ -40,40 +17,11 @@ export function createStopCommand() {
|
|
|
40
17
|
return new Command('stop').description('Stop the running Shep web UI daemon').action(async () => {
|
|
41
18
|
const daemonService = container.resolve('IDaemonService');
|
|
42
19
|
const state = await daemonService.read();
|
|
43
|
-
//
|
|
44
|
-
|
|
20
|
+
// Print a user-facing message when no daemon state is recorded at all.
|
|
21
|
+
// The alive-but-stale case (state exists, PID dead) is handled silently by stopDaemon.
|
|
22
|
+
if (!state) {
|
|
45
23
|
messages.info('No Shep daemon is running.');
|
|
46
|
-
await daemonService.delete();
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
const { pid } = state;
|
|
50
|
-
// Validate PID is a positive finite integer before kill
|
|
51
|
-
if (!Number.isFinite(pid) || !Number.isInteger(pid) || pid <= 0) {
|
|
52
|
-
messages.info('No Shep daemon is running (invalid PID in state file).');
|
|
53
|
-
await daemonService.delete();
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
try {
|
|
57
|
-
messages.info(`Stopping Shep daemon (PID ${pid})...`);
|
|
58
|
-
// Send SIGTERM — request graceful shutdown
|
|
59
|
-
process.kill(pid, 'SIGTERM');
|
|
60
|
-
// Poll for up to 5s waiting for the process to exit
|
|
61
|
-
const died = await pollUntilDead(daemonService, pid, MAX_WAIT_MS, POLL_INTERVAL_MS);
|
|
62
|
-
if (!died) {
|
|
63
|
-
// Graceful shutdown timed out — force kill
|
|
64
|
-
messages.info('Daemon did not stop gracefully, sending SIGKILL...');
|
|
65
|
-
try {
|
|
66
|
-
process.kill(pid, 'SIGKILL');
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
// Process may have exited between the check and the kill — ignore
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
messages.success('Shep daemon stopped.');
|
|
73
|
-
}
|
|
74
|
-
finally {
|
|
75
|
-
// Always clean up daemon.json regardless of termination path
|
|
76
|
-
await daemonService.delete();
|
|
77
24
|
}
|
|
25
|
+
await stopDaemon(daemonService);
|
|
78
26
|
});
|
|
79
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upgrade.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/upgrade.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,IAAI,YAAY,EAAqB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"upgrade.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/upgrade.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,IAAI,YAAY,EAAqB,MAAM,oBAAoB,CAAC;AAQ9E,KAAK,OAAO,GAAG,OAAO,YAAY,CAAC;AAyEnC;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,OAAsB,GAAG,OAAO,CA+D7E"}
|
|
@@ -10,6 +10,8 @@ import { Command } from 'commander';
|
|
|
10
10
|
import { spawn as defaultSpawn } from 'node:child_process';
|
|
11
11
|
import { container } from '../../../../packages/core/src/infrastructure/di/container.js';
|
|
12
12
|
import { messages } from '../ui/index.js';
|
|
13
|
+
import { stopDaemon } from './daemon/stop-daemon.js';
|
|
14
|
+
import { startDaemon } from './daemon/start-daemon.js';
|
|
13
15
|
const VERSION_CHECK_TIMEOUT_MS = 10_000;
|
|
14
16
|
/**
|
|
15
17
|
* Get the latest published version of @shepai/cli from npm registry.
|
|
@@ -96,13 +98,37 @@ export function createUpgradeCommand(spawnFn = defaultSpawn) {
|
|
|
96
98
|
else {
|
|
97
99
|
messages.info(`Upgrading from v${currentVersion} to latest`);
|
|
98
100
|
}
|
|
99
|
-
// 4.
|
|
100
|
-
const
|
|
101
|
-
|
|
101
|
+
// 4. Check daemon state before install (FR-1)
|
|
102
|
+
const daemonService = container.resolve('IDaemonService');
|
|
103
|
+
const daemonState = await daemonService.read();
|
|
104
|
+
const daemonWasRunning = daemonState !== null && daemonService.isAlive(daemonState.pid);
|
|
105
|
+
const previousPort = daemonWasRunning ? daemonState.port : undefined;
|
|
106
|
+
if (daemonWasRunning) {
|
|
107
|
+
messages.info('Stopping daemon before upgrade...');
|
|
108
|
+
await stopDaemon(daemonService);
|
|
109
|
+
}
|
|
110
|
+
// 5. Run npm i -g @shepai/cli@latest; always restore daemon in finally (FR-2, FR-3)
|
|
111
|
+
let installExitCode = 1;
|
|
112
|
+
try {
|
|
113
|
+
installExitCode = await runNpmInstall(spawnFn);
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
if (daemonWasRunning) {
|
|
117
|
+
messages.info('Restarting daemon...');
|
|
118
|
+
await startDaemon({ port: previousPort });
|
|
119
|
+
messages.success('Daemon restarted successfully.');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (installExitCode === 0) {
|
|
102
123
|
messages.success('Shep CLI upgraded successfully');
|
|
103
124
|
}
|
|
104
125
|
else {
|
|
105
|
-
|
|
126
|
+
if (daemonWasRunning) {
|
|
127
|
+
messages.error('Upgrade failed — daemon restored on previous version.');
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
messages.error('Upgrade failed');
|
|
131
|
+
}
|
|
106
132
|
process.exitCode = 1;
|
|
107
133
|
}
|
|
108
134
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* shep Start the web UI daemon (or run onboarding on first run)
|
|
12
12
|
* shep start Start the web UI as a background daemon
|
|
13
13
|
* shep stop Stop the running web UI daemon
|
|
14
|
+
* shep restart Restart (or start) the web UI daemon
|
|
14
15
|
* shep status Show status and metrics of the running daemon
|
|
15
16
|
* shep version Display version information
|
|
16
17
|
* shep ui Start the web UI (foreground, interactive)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/cli/index.ts"],"names":[],"mappings":";AAEA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,kBAAkB,CAAC"}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* shep Start the web UI daemon (or run onboarding on first run)
|
|
12
12
|
* shep start Start the web UI as a background daemon
|
|
13
13
|
* shep stop Stop the running web UI daemon
|
|
14
|
+
* shep restart Restart (or start) the web UI daemon
|
|
14
15
|
* shep status Show status and metrics of the running daemon
|
|
15
16
|
* shep version Display version information
|
|
16
17
|
* shep ui Start the web UI (foreground, interactive)
|
|
@@ -46,6 +47,7 @@ import { messages } from './ui/index.js';
|
|
|
46
47
|
import { createStartCommand } from './commands/start.command.js';
|
|
47
48
|
import { createStopCommand } from './commands/stop.command.js';
|
|
48
49
|
import { createStatusCommand } from './commands/status.command.js';
|
|
50
|
+
import { createRestartCommand } from './commands/restart.command.js';
|
|
49
51
|
import { createServeCommand } from './commands/_serve.command.js';
|
|
50
52
|
import { startDaemon } from './commands/daemon/start-daemon.js';
|
|
51
53
|
// DI container and settings
|
|
@@ -122,6 +124,7 @@ async function bootstrap() {
|
|
|
122
124
|
// Daemon lifecycle commands (task-9)
|
|
123
125
|
program.addCommand(createStartCommand());
|
|
124
126
|
program.addCommand(createStopCommand());
|
|
127
|
+
program.addCommand(createRestartCommand());
|
|
125
128
|
program.addCommand(createStatusCommand());
|
|
126
129
|
program.addCommand(createServeCommand()); // hidden from --help
|
|
127
130
|
// Parse arguments (parseAsync needed for async command actions like init)
|