@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.
Files changed (100) hide show
  1. package/dist/src/presentation/cli/commands/daemon/stop-daemon.d.ts +27 -0
  2. package/dist/src/presentation/cli/commands/daemon/stop-daemon.d.ts.map +1 -0
  3. package/dist/src/presentation/cli/commands/daemon/stop-daemon.js +77 -0
  4. package/dist/src/presentation/cli/commands/restart.command.d.ts +14 -0
  5. package/dist/src/presentation/cli/commands/restart.command.d.ts.map +1 -0
  6. package/dist/src/presentation/cli/commands/restart.command.js +47 -0
  7. package/dist/src/presentation/cli/commands/stop.command.d.ts +1 -9
  8. package/dist/src/presentation/cli/commands/stop.command.d.ts.map +1 -1
  9. package/dist/src/presentation/cli/commands/stop.command.js +6 -58
  10. package/dist/src/presentation/cli/commands/upgrade.command.d.ts.map +1 -1
  11. package/dist/src/presentation/cli/commands/upgrade.command.js +30 -4
  12. package/dist/src/presentation/cli/index.d.ts +1 -0
  13. package/dist/src/presentation/cli/index.d.ts.map +1 -1
  14. package/dist/src/presentation/cli/index.js +3 -0
  15. package/dist/tsconfig.build.tsbuildinfo +1 -1
  16. package/package.json +1 -1
  17. package/web/.next/BUILD_ID +1 -1
  18. package/web/.next/build-manifest.json +2 -2
  19. package/web/.next/cache/.previewinfo +1 -1
  20. package/web/.next/cache/.rscinfo +1 -1
  21. package/web/.next/cache/config.json +3 -3
  22. package/web/.next/fallback-build-manifest.json +2 -2
  23. package/web/.next/prerender-manifest.json +3 -3
  24. package/web/.next/required-server-files.js +1 -1
  25. package/web/.next/required-server-files.json +1 -1
  26. package/web/.next/server/app/_global-error.html +2 -2
  27. package/web/.next/server/app/_global-error.rsc +1 -1
  28. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  29. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  30. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  31. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  32. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  33. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  34. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  35. package/web/.next/server/app/page/server-reference-manifest.json +14 -14
  36. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  37. package/web/.next/server/app/skills/page/server-reference-manifest.json +1 -1
  38. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  39. package/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  40. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  41. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  42. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  44. package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js.map +1 -1
  45. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  46. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  47. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  48. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  49. package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +2 -2
  50. package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js.map +1 -1
  51. package/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js +2 -2
  52. package/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js.map +1 -1
  53. package/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js +1 -1
  54. package/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js.map +1 -1
  55. package/web/.next/server/chunks/ssr/[root-of-the-server]__fbc89707._.js +1 -1
  56. package/web/.next/server/chunks/ssr/_d3711354._.js +1 -1
  57. package/web/.next/server/chunks/ssr/_d3711354._.js.map +1 -1
  58. package/web/.next/server/pages/500.html +2 -2
  59. package/web/.next/server/server-reference-manifest.js +1 -1
  60. package/web/.next/server/server-reference-manifest.json +15 -15
  61. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  62. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  63. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  64. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  65. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  66. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  67. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  68. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  69. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  70. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  71. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  72. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  73. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  74. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +14 -14
  75. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  76. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +1 -1
  77. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  78. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  79. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  80. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  81. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  82. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  83. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  84. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  85. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +2 -2
  86. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js +2 -2
  87. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js +1 -1
  88. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__fbc89707._.js +1 -1
  89. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_d3711354._.js +1 -1
  90. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  91. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  92. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +15 -15
  93. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  94. package/web/.next/static/chunks/{6ed00d2d29823855.js → 233fbb89beb137e8.js} +1 -1
  95. package/web/.next/static/chunks/{60dc6ac13a7c312d.js → fa8058049a43c698.js} +2 -2
  96. package/web/.next/trace +1 -1
  97. package/web/.next/trace-build +1 -1
  98. /package/web/.next/static/{LmnCUTqidV1JIrYPVF8ji → zuqVLdEhCDdtLqCuWgUm5}/_buildManifest.js +0 -0
  99. /package/web/.next/static/{LmnCUTqidV1JIrYPVF8ji → zuqVLdEhCDdtLqCuWgUm5}/_clientMiddlewareManifest.json +0 -0
  100. /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;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4BpC;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CA+C3C"}
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
- const POLL_INTERVAL_MS = 200;
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
- // No daemon running or PID not alive
44
- if (!state || !daemonService.isAlive(state.pid)) {
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;AAK9E,KAAK,OAAO,GAAG,OAAO,YAAY,CAAC;AAyEnC;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,OAAsB,GAAG,OAAO,CAuC7E"}
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. Run npm i -g @shepai/cli@latest with inherited stdio
100
- const exitCode = await runNpmInstall(spawnFn);
101
- if (exitCode === 0) {
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
- messages.error('Upgrade failed');
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;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,kBAAkB,CAAC"}
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)