@shepai/cli 1.70.0 → 1.70.1

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 (112) hide show
  1. package/dist/packages/core/src/infrastructure/services/deployment/deployment-logger.d.ts +7 -0
  2. package/dist/packages/core/src/infrastructure/services/deployment/deployment-logger.d.ts.map +1 -0
  3. package/dist/packages/core/src/infrastructure/services/deployment/deployment-logger.js +17 -0
  4. package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.d.ts.map +1 -1
  5. package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.js +60 -7
  6. package/dist/packages/core/src/infrastructure/services/deployment/detect-dev-script.d.ts.map +1 -1
  7. package/dist/packages/core/src/infrastructure/services/deployment/detect-dev-script.js +13 -6
  8. package/dist/src/presentation/web/app/actions/deploy-feature.d.ts.map +1 -1
  9. package/dist/src/presentation/web/app/actions/deploy-feature.js +11 -0
  10. package/dist/src/presentation/web/app/actions/deploy-repository.d.ts.map +1 -1
  11. package/dist/src/presentation/web/app/actions/deploy-repository.js +8 -0
  12. package/dist/src/presentation/web/hooks/use-deploy-action.d.ts.map +1 -1
  13. package/dist/src/presentation/web/hooks/use-deploy-action.js +36 -5
  14. package/dist/tsconfig.build.tsbuildinfo +1 -1
  15. package/package.json +1 -1
  16. package/web/.next/BUILD_ID +1 -1
  17. package/web/.next/build-manifest.json +2 -2
  18. package/web/.next/cache/.previewinfo +1 -1
  19. package/web/.next/cache/.rscinfo +1 -1
  20. package/web/.next/cache/.tsbuildinfo +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 +18 -18
  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 +5 -5
  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]__249c74f6._.js +1 -1
  46. package/web/.next/server/chunks/ssr/[root-of-the-server]__551fb7e1._.js +1 -1
  47. package/web/.next/server/chunks/ssr/[root-of-the-server]__551fb7e1._.js.map +1 -1
  48. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  49. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  50. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  51. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  52. package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +2 -2
  53. package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js.map +1 -1
  54. package/web/.next/server/chunks/ssr/[root-of-the-server]__e41b5eec._.js +2 -2
  55. package/web/.next/server/chunks/ssr/[root-of-the-server]__e41b5eec._.js.map +1 -1
  56. package/web/.next/server/chunks/ssr/_49bf495c._.js +1 -1
  57. package/web/.next/server/chunks/ssr/_49bf495c._.js.map +1 -1
  58. package/web/.next/server/chunks/ssr/_68b5e0de._.js +1 -1
  59. package/web/.next/server/chunks/ssr/_68b5e0de._.js.map +1 -1
  60. package/web/.next/server/chunks/ssr/_725584e5._.js +1 -1
  61. package/web/.next/server/chunks/ssr/_725584e5._.js.map +1 -1
  62. package/web/.next/server/pages/500.html +2 -2
  63. package/web/.next/server/server-reference-manifest.js +1 -1
  64. package/web/.next/server/server-reference-manifest.json +19 -19
  65. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  66. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  67. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  68. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  69. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  70. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  71. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  72. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  73. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  74. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  75. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  76. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  77. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  78. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +18 -18
  79. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  80. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +5 -5
  81. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  82. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  83. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  84. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  85. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  86. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  87. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  88. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__551fb7e1._.js +1 -1
  89. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  90. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  91. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +2 -2
  92. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__e41b5eec._.js +2 -2
  93. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_49bf495c._.js +1 -1
  94. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_68b5e0de._.js +1 -1
  95. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_725584e5._.js +1 -1
  96. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  97. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  98. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +19 -19
  99. package/web/.next/standalone/src/presentation/web/app/actions/deploy-feature.ts +16 -0
  100. package/web/.next/standalone/src/presentation/web/app/actions/deploy-repository.ts +10 -0
  101. package/web/.next/standalone/src/presentation/web/hooks/use-deploy-action.ts +45 -5
  102. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  103. package/web/.next/static/chunks/16e380b8dd9d11bc.js +1 -0
  104. package/web/.next/static/chunks/{6d7d233d07d9a026.js → 1ed0df845a1625f6.js} +1 -1
  105. package/web/.next/static/chunks/{86b38e36afcd19f3.js → 24e1d97cab8bad6d.js} +1 -1
  106. package/web/.next/static/chunks/{fdfd68aacc4727cf.js → 9db5c06001f3e664.js} +2 -2
  107. package/web/.next/trace +1 -1
  108. package/web/.next/trace-build +1 -1
  109. package/web/.next/static/chunks/94562d62bf8e4bf7.js +0 -1
  110. /package/web/.next/static/{6pJLWKt-ohZJ_2kGrHG_m → 8xZoRkWykApYNlvamCGUe}/_buildManifest.js +0 -0
  111. /package/web/.next/static/{6pJLWKt-ohZJ_2kGrHG_m → 8xZoRkWykApYNlvamCGUe}/_clientMiddlewareManifest.json +0 -0
  112. /package/web/.next/static/{6pJLWKt-ohZJ_2kGrHG_m → 8xZoRkWykApYNlvamCGUe}/_ssgManifest.js +0 -0
@@ -0,0 +1,7 @@
1
+ export declare function createDeploymentLogger(prefix: string): {
2
+ info: (...args: unknown[]) => void;
3
+ debug: (...args: unknown[]) => void;
4
+ warn: (...args: unknown[]) => void;
5
+ error: (...args: unknown[]) => void;
6
+ };
7
+ //# sourceMappingURL=deployment-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment-logger.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/deployment/deployment-logger.ts"],"names":[],"mappings":"AAWA,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM;oBAIvB,OAAO,EAAE;qBACR,OAAO,EAAE;oBACpB,OAAO,EAAE;qBACR,OAAO,EAAE;EAE7B"}
@@ -0,0 +1,17 @@
1
+ /* eslint-disable no-console */
2
+ /**
3
+ * Debug-gated logger for deployment services.
4
+ *
5
+ * - `info` and `debug` only emit when `process.env.DEBUG` is set.
6
+ * - `warn` and `error` always emit (they indicate real problems).
7
+ */
8
+ const noop = () => undefined;
9
+ export function createDeploymentLogger(prefix) {
10
+ const isDebug = !!process.env.DEBUG;
11
+ return {
12
+ info: isDebug ? (...args) => console.info(prefix, ...args) : noop,
13
+ debug: isDebug ? (...args) => console.debug(prefix, ...args) : noop,
14
+ warn: (...args) => console.warn(prefix, ...args),
15
+ error: (...args) => console.error(prefix, ...args),
16
+ };
17
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"deployment.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/deployment/deployment.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,qEAAqE,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAgBzD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAC7D,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC;AAgBD,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;gBAEjC,IAAI,GAAE,OAAO,CAAC,qBAAqB,CAAM;IAIrD;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAmDjD;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAMpD;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B3C;;OAEG;IACH,OAAO,IAAI,IAAI;IAMf;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA0B5B;;OAEG;YACW,aAAa;IAW3B;;OAEG;IACH,OAAO,CAAC,WAAW;CASpB"}
1
+ {"version":3,"file":"deployment.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/deployment/deployment.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,qEAAqE,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAkBzD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAC7D,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC;AAgBD,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;gBAEjC,IAAI,GAAE,OAAO,CAAC,qBAAqB,CAAM;IAIrD;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IA8EjD;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAYpD;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuC3C;;OAEG;IACH,OAAO,IAAI,IAAI;IAMf;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAoD5B;;OAEG;YACW,aAAa;IAW3B;;OAEG;IACH,OAAO,CAAC,WAAW;CASpB"}
@@ -9,7 +9,9 @@
9
9
  import { spawn } from 'node:child_process';
10
10
  import { DeploymentState } from '../../../domain/generated/output.js';
11
11
  import { detectDevScript } from './detect-dev-script.js';
12
+ import { createDeploymentLogger } from './deployment-logger.js';
12
13
  import { parsePort } from './parse-port.js';
14
+ const log = createDeploymentLogger('[DeploymentService]');
13
15
  const POLL_INTERVAL_MS = 200;
14
16
  const MAX_WAIT_MS = 5000;
15
17
  const defaultDeps = {
@@ -37,20 +39,24 @@ export class DeploymentService {
37
39
  * If a deployment already exists for this target, it is stopped first.
38
40
  */
39
41
  start(targetId, targetPath) {
42
+ log.info(`start() called — targetId="${targetId}", targetPath="${targetPath}"`);
40
43
  // Stop any existing deployment for this target
41
44
  const existing = this.deployments.get(targetId);
42
45
  if (existing) {
46
+ log.info(`Stopping existing deployment for "${targetId}" (pid=${existing.pid})`);
43
47
  this.killProcess(existing);
44
48
  this.deployments.delete(targetId);
45
49
  }
46
50
  // Detect the dev script
47
51
  const detection = this.deps.detectDevScript(targetPath);
48
52
  if (!detection.success) {
53
+ log.error(`Dev script detection failed: ${detection.error}`);
49
54
  throw new Error(detection.error);
50
55
  }
51
56
  // Build spawn args based on package manager
52
- const { packageManager, scriptName } = detection;
57
+ const { packageManager, scriptName, command } = detection;
53
58
  const args = packageManager === 'npm' ? ['run', scriptName] : [scriptName];
59
+ log.info(`Spawning dev server: command="${command}", packageManager="${packageManager}", scriptName="${scriptName}", cwd="${targetPath}"`);
54
60
  const child = this.deps.spawn(packageManager, args, {
55
61
  shell: true,
56
62
  cwd: targetPath,
@@ -58,8 +64,10 @@ export class DeploymentService {
58
64
  stdio: ['ignore', 'pipe', 'pipe'],
59
65
  });
60
66
  if (!child.pid) {
67
+ log.error('spawn() returned no PID — process failed to start');
61
68
  throw new Error('Failed to spawn dev server: no PID returned');
62
69
  }
70
+ log.info(`Process spawned — pid=${child.pid}`);
63
71
  const entry = {
64
72
  pid: child.pid,
65
73
  child: child,
@@ -73,8 +81,19 @@ export class DeploymentService {
73
81
  // Attach stdout/stderr listeners for port detection
74
82
  this.attachOutputListener(entry, 'stdout');
75
83
  this.attachOutputListener(entry, 'stderr');
84
+ // Handle spawn errors (command not found, permission denied, etc.)
85
+ child.on('error', (err) => {
86
+ log.error(`Child process error for "${targetId}" (pid=${entry.pid}): ${err.message}`);
87
+ entry.state = DeploymentState.Stopped;
88
+ this.deployments.delete(targetId);
89
+ });
76
90
  // Clean up on process exit
77
- child.on('exit', () => {
91
+ child.on('exit', (code, signal) => {
92
+ const wasBooting = entry.state === DeploymentState.Booting;
93
+ log.info(`Process exited for "${targetId}" (pid=${entry.pid}) — code=${code}, signal=${signal}, wasBooting=${wasBooting}`);
94
+ if (wasBooting) {
95
+ log.warn('Process exited while still in Booting state — dev server likely crashed on startup. Check stderr output above.');
96
+ }
78
97
  this.deployments.delete(targetId);
79
98
  });
80
99
  }
@@ -84,8 +103,11 @@ export class DeploymentService {
84
103
  */
85
104
  getStatus(targetId) {
86
105
  const entry = this.deployments.get(targetId);
87
- if (!entry)
106
+ if (!entry) {
107
+ log.debug(`getStatus("${targetId}") — no deployment found`);
88
108
  return null;
109
+ }
110
+ log.debug(`getStatus("${targetId}") — state=${entry.state}, url=${entry.url}, pid=${entry.pid}`);
89
111
  return { state: entry.state, url: entry.url };
90
112
  }
91
113
  /**
@@ -93,20 +115,24 @@ export class DeploymentService {
93
115
  */
94
116
  async stop(targetId) {
95
117
  const entry = this.deployments.get(targetId);
96
- if (!entry)
118
+ if (!entry) {
119
+ log.info(`stop("${targetId}") — no deployment found, nothing to stop`);
97
120
  return;
121
+ }
122
+ log.info(`stop("${targetId}") — sending SIGTERM to process group (pid=${entry.pid})`);
98
123
  // Send SIGTERM to process group
99
124
  try {
100
125
  this.deps.kill(-entry.pid, 'SIGTERM');
101
126
  }
102
127
  catch {
103
- // Process may already be dead
128
+ log.info(`stop("${targetId}") process already dead on SIGTERM`);
104
129
  this.deployments.delete(targetId);
105
130
  return;
106
131
  }
107
132
  // Wait for the process to exit
108
133
  const died = await this.pollUntilDead(entry.pid, MAX_WAIT_MS, POLL_INTERVAL_MS);
109
134
  if (!died) {
135
+ log.warn(`stop("${targetId}") — process did not exit after ${MAX_WAIT_MS}ms, escalating to SIGKILL`);
110
136
  // Escalate to SIGKILL
111
137
  try {
112
138
  this.deps.kill(-entry.pid, 'SIGKILL');
@@ -115,6 +141,9 @@ export class DeploymentService {
115
141
  // Process may have exited between check and kill
116
142
  }
117
143
  }
144
+ else {
145
+ log.info(`stop("${targetId}") — process exited gracefully`);
146
+ }
118
147
  // Wait for the exit event to clean up the map
119
148
  await this.waitForExit(entry.child);
120
149
  }
@@ -143,26 +172,50 @@ export class DeploymentService {
143
172
  attachOutputListener(entry, stream) {
144
173
  const bufferKey = stream === 'stdout' ? 'stdoutBuffer' : 'stderrBuffer';
145
174
  const childStream = entry.child[stream];
146
- if (!childStream)
175
+ if (!childStream) {
176
+ log.warn(`[${entry.targetId}] No ${stream} stream available — cannot attach listener`);
147
177
  return;
178
+ }
148
179
  childStream.on('data', (chunk) => {
180
+ const text = chunk.toString();
149
181
  // Append chunk to buffer
150
- entry[bufferKey] += chunk.toString();
182
+ entry[bufferKey] += text;
151
183
  // Process complete lines
152
184
  const lines = entry[bufferKey].split('\n');
153
185
  // Keep the last element (incomplete line) in the buffer
154
186
  entry[bufferKey] = lines.pop() ?? '';
155
187
  for (const line of lines) {
188
+ if (!line.trim())
189
+ continue;
190
+ log.debug(`[${entry.targetId}] ${stream}: ${line}`);
156
191
  if (entry.state !== DeploymentState.Booting)
157
192
  break;
158
193
  const url = parsePort(line);
159
194
  if (url) {
195
+ log.info(`[${entry.targetId}] Port detected — url="${url}" (from ${stream})`);
160
196
  entry.state = DeploymentState.Ready;
161
197
  entry.url = url;
162
198
  break;
163
199
  }
164
200
  }
165
201
  });
202
+ childStream.on('end', () => {
203
+ // Flush remaining buffer content
204
+ const remaining = entry[bufferKey].trim();
205
+ if (remaining) {
206
+ log.debug(`[${entry.targetId}] ${stream} (flush): ${remaining}`);
207
+ if (entry.state === DeploymentState.Booting) {
208
+ const url = parsePort(remaining);
209
+ if (url) {
210
+ log.info(`[${entry.targetId}] Port detected in flushed buffer — url="${url}"`);
211
+ entry.state = DeploymentState.Ready;
212
+ entry.url = url;
213
+ }
214
+ }
215
+ entry[bufferKey] = '';
216
+ }
217
+ log.debug(`[${entry.targetId}] ${stream} stream ended`);
218
+ });
166
219
  }
167
220
  /**
168
221
  * Poll until a process is dead or timeout expires.
@@ -1 +1 @@
1
- {"version":3,"file":"detect-dev-script.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/deployment/detect-dev-script.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,IAAI,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,qBAAqB,GAAG,sBAAsB,GAAG,oBAAoB,CAAC;AAElF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CA4BtE"}
1
+ {"version":3,"file":"detect-dev-script.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/deployment/detect-dev-script.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgBH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,IAAI,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,qBAAqB,GAAG,sBAAsB,GAAG,oBAAoB,CAAC;AAIlF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAuCtE"}
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { readFileSync, existsSync } from 'node:fs';
9
9
  import { join } from 'node:path';
10
+ import { createDeploymentLogger } from './deployment-logger.js';
10
11
  /** Script names to search for, in priority order */
11
12
  const SCRIPT_PRIORITY = ['dev', 'start', 'serve'];
12
13
  /** Lockfile-to-package-manager mapping, checked in order */
@@ -15,6 +16,7 @@ const LOCKFILE_MANAGERS = [
15
16
  { lockfile: 'yarn.lock', manager: 'yarn' },
16
17
  { lockfile: 'package-lock.json', manager: 'npm' },
17
18
  ];
19
+ const log = createDeploymentLogger('[detectDevScript]');
18
20
  /**
19
21
  * Detect the dev script and package manager for a project directory.
20
22
  *
@@ -22,28 +24,33 @@ const LOCKFILE_MANAGERS = [
22
24
  * @returns Detection result with command info, or an error
23
25
  */
24
26
  export function detectDevScript(dirPath) {
27
+ log.info(`scanning dirPath="${dirPath}"`);
25
28
  // Read and parse package.json
26
29
  let packageJson;
27
30
  try {
28
31
  const raw = readFileSync(join(dirPath, 'package.json'), 'utf-8');
29
32
  packageJson = JSON.parse(raw);
30
33
  }
31
- catch {
32
- return { success: false, error: `No package.json found in ${dirPath}` };
34
+ catch (err) {
35
+ const msg = `No package.json found in ${dirPath}`;
36
+ log.error(msg, err);
37
+ return { success: false, error: msg };
33
38
  }
34
39
  // Find the first matching script in priority order
35
40
  const scripts = packageJson.scripts ?? {};
41
+ const availableScripts = Object.keys(scripts);
42
+ log.info(`available scripts: [${availableScripts.join(', ')}], looking for: [${SCRIPT_PRIORITY.join(', ')}]`);
36
43
  const scriptName = SCRIPT_PRIORITY.find((name) => name in scripts);
37
44
  if (!scriptName) {
38
- return {
39
- success: false,
40
- error: `No dev script found in package.json. Expected one of: ${SCRIPT_PRIORITY.join(', ')}`,
41
- };
45
+ const msg = `No dev script found in package.json. Expected one of: ${SCRIPT_PRIORITY.join(', ')}`;
46
+ log.warn(msg);
47
+ return { success: false, error: msg };
42
48
  }
43
49
  // Detect package manager from lockfile
44
50
  const packageManager = detectPackageManager(dirPath);
45
51
  // Build the command — pnpm/yarn use `<pm> <script>`, npm uses `npm run <script>`
46
52
  const command = packageManager === 'npm' ? `npm run ${scriptName}` : `${packageManager} ${scriptName}`;
53
+ log.info(`detected — packageManager="${packageManager}", scriptName="${scriptName}", command="${command}"`);
47
54
  return { success: true, packageManager, scriptName, command };
48
55
  }
49
56
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"deploy-feature.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/deploy-feature.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAEvE,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,eAAe,CAAA;CAAE,CAAC,CA2BxE"}
1
+ {"version":3,"file":"deploy-feature.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/deploy-feature.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAIvE,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,eAAe,CAAA;CAAE,CAAC,CAwCxE"}
@@ -1,28 +1,39 @@
1
1
  'use server';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { resolve } from '../../lib/server-container.js';
4
+ import { createDeploymentLogger } from '../../../../../packages/core/src/infrastructure/services/deployment/deployment-logger.js';
4
5
  import { computeWorktreePath } from '../../../../../packages/core/src/infrastructure/services/ide-launchers/compute-worktree-path.js';
5
6
  import { DeploymentState } from '../../../../../packages/core/src/domain/generated/output.js';
7
+ const log = createDeploymentLogger('[deployFeature]');
6
8
  export async function deployFeature(featureId) {
9
+ log.info(`called — featureId="${featureId}"`);
7
10
  if (!featureId?.trim()) {
11
+ log.warn('rejected — featureId is empty');
8
12
  return { success: false, error: 'featureId is required' };
9
13
  }
10
14
  try {
11
15
  const featureRepo = resolve('IFeatureRepository');
12
16
  const feature = await featureRepo.findById(featureId);
13
17
  if (!feature) {
18
+ log.warn(`feature not found in repository: "${featureId}"`);
14
19
  return { success: false, error: `Feature not found: ${featureId}` };
15
20
  }
21
+ log.info(`feature found — repositoryPath="${feature.repositoryPath}", branch="${feature.branch}"`);
16
22
  const worktreePath = computeWorktreePath(feature.repositoryPath, feature.branch);
23
+ log.info(`computed worktreePath="${worktreePath}"`);
17
24
  if (!existsSync(worktreePath)) {
25
+ log.warn(`worktree path does not exist on disk: "${worktreePath}"`);
18
26
  return { success: false, error: `Worktree path does not exist: ${worktreePath}` };
19
27
  }
28
+ log.info('worktree path exists, calling deploymentService.start()');
20
29
  const deploymentService = resolve('IDeploymentService');
21
30
  deploymentService.start(featureId, worktreePath);
31
+ log.info('start() returned successfully — state=Booting');
22
32
  return { success: true, state: DeploymentState.Booting };
23
33
  }
24
34
  catch (error) {
25
35
  const message = error instanceof Error ? error.message : 'Failed to deploy feature';
36
+ log.error(`error: ${message}`, error);
26
37
  return { success: false, error: message };
27
38
  }
28
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"deploy-repository.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/deploy-repository.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAEvE,wBAAsB,gBAAgB,CACpC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,eAAe,CAAA;CAAE,CAAC,CAkBxE"}
1
+ {"version":3,"file":"deploy-repository.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/deploy-repository.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAIvE,wBAAsB,gBAAgB,CACpC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,eAAe,CAAA;CAAE,CAAC,CAyBxE"}
@@ -1,21 +1,29 @@
1
1
  'use server';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { resolve } from '../../lib/server-container.js';
4
+ import { createDeploymentLogger } from '../../../../../packages/core/src/infrastructure/services/deployment/deployment-logger.js';
4
5
  import { DeploymentState } from '../../../../../packages/core/src/domain/generated/output.js';
6
+ const log = createDeploymentLogger('[deployRepository]');
5
7
  export async function deployRepository(repositoryPath) {
8
+ log.info(`called — repositoryPath="${repositoryPath}"`);
6
9
  if (!repositoryPath?.startsWith('/')) {
10
+ log.warn('rejected — not an absolute path');
7
11
  return { success: false, error: 'repositoryPath must be an absolute path' };
8
12
  }
9
13
  try {
10
14
  if (!existsSync(repositoryPath)) {
15
+ log.warn(`directory does not exist: "${repositoryPath}"`);
11
16
  return { success: false, error: `Directory does not exist: ${repositoryPath}` };
12
17
  }
18
+ log.info('directory exists, calling deploymentService.start()');
13
19
  const deploymentService = resolve('IDeploymentService');
14
20
  deploymentService.start(repositoryPath, repositoryPath);
21
+ log.info('start() returned successfully — state=Booting');
15
22
  return { success: true, state: DeploymentState.Booting };
16
23
  }
17
24
  catch (error) {
18
25
  const message = error instanceof Error ? error.message : 'Failed to deploy repository';
26
+ log.error(`error: ${message}`, error);
19
27
  return { success: false, error: message };
20
28
  }
21
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAKD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAmIlF"}
1
+ {"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAeD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAgKlF"}
@@ -1,9 +1,19 @@
1
1
  'use client';
2
+ /* eslint-disable no-console */
2
3
  import { useState, useCallback, useRef, useEffect } from 'react';
3
4
  import { deployFeature } from '../app/actions/deploy-feature.js';
4
5
  import { deployRepository } from '../app/actions/deploy-repository.js';
5
6
  import { stopDeployment } from '../app/actions/stop-deployment.js';
6
7
  import { getDeploymentStatus } from '../app/actions/get-deployment-status.js';
8
+ const PREFIX = '[useDeployAction]';
9
+ const isDebug = !!process.env.NEXT_PUBLIC_DEBUG;
10
+ const noop = () => undefined;
11
+ const log = {
12
+ info: isDebug ? (...args) => console.info(PREFIX, ...args) : noop,
13
+ debug: isDebug ? (...args) => console.debug(PREFIX, ...args) : noop,
14
+ warn: (...args) => console.warn(PREFIX, ...args),
15
+ error: (...args) => console.error(PREFIX, ...args),
16
+ };
7
17
  const ERROR_CLEAR_DELAY = 5000;
8
18
  const POLL_INTERVAL = 3000;
9
19
  export function useDeployAction(input) {
@@ -33,12 +43,14 @@ export function useDeployAction(input) {
33
43
  }, []);
34
44
  const stopPolling = useCallback(() => {
35
45
  if (pollIntervalRef.current) {
46
+ log.debug('stopping polling');
36
47
  clearInterval(pollIntervalRef.current);
37
48
  pollIntervalRef.current = null;
38
49
  }
39
50
  }, []);
40
51
  const startPolling = useCallback((targetId) => {
41
52
  stopPolling();
53
+ log.debug(`starting polling for "${targetId}" every ${POLL_INTERVAL}ms`);
42
54
  pollIntervalRef.current = setInterval(async () => {
43
55
  if (!mountedRef.current) {
44
56
  stopPolling();
@@ -48,19 +60,30 @@ export function useDeployAction(input) {
48
60
  if (!mountedRef.current)
49
61
  return;
50
62
  if (!result || result.state === 'Stopped') {
63
+ log.info(`poll result: ${result ? `state=${result.state}` : 'null (deployment gone)'} — stopping poll`);
51
64
  setStatus(null);
52
65
  setUrl(null);
53
66
  stopPolling();
54
67
  }
55
68
  else {
69
+ if (result.state !== status) {
70
+ log.info(`poll state changed: ${status} → ${result.state}, url=${result.url}`);
71
+ }
56
72
  setStatus(result.state);
57
73
  setUrl(result.url);
58
74
  }
59
75
  }, POLL_INTERVAL);
60
- }, [stopPolling]);
76
+ }, [stopPolling, status]);
61
77
  const handleDeploy = useCallback(async () => {
62
- if (!input || deployLoading)
78
+ if (!input) {
79
+ log.warn('deploy() called but input is null — no-op');
63
80
  return;
81
+ }
82
+ if (deployLoading) {
83
+ log.warn('deploy() called but already loading — no-op');
84
+ return;
85
+ }
86
+ log.info(`deploy() — targetType="${input.targetType}", targetId="${input.targetId}", repositoryPath="${input.repositoryPath}"`);
64
87
  if (errorTimerRef.current)
65
88
  clearTimeout(errorTimerRef.current);
66
89
  setDeployLoading(true);
@@ -69,14 +92,19 @@ export function useDeployAction(input) {
69
92
  const result = input.targetType === 'feature'
70
93
  ? await deployFeature(input.targetId)
71
94
  : await deployRepository(input.repositoryPath);
72
- if (!mountedRef.current)
95
+ log.info('server action result:', result);
96
+ if (!mountedRef.current) {
97
+ log.warn('component unmounted after deploy — discarding result');
73
98
  return;
99
+ }
74
100
  if (!result.success) {
75
101
  const errorMessage = result.error ?? 'An unexpected error occurred';
102
+ log.error(`deploy failed: ${errorMessage}`);
76
103
  setDeployError(errorMessage);
77
104
  errorTimerRef.current = setTimeout(() => setDeployError(null), ERROR_CLEAR_DELAY);
78
105
  }
79
106
  else {
107
+ log.info(`deploy succeeded — initial state=${result.state}, starting polling`);
80
108
  setStatus(result.state ?? null);
81
109
  setUrl(null);
82
110
  startPolling(input.targetId);
@@ -86,6 +114,7 @@ export function useDeployAction(input) {
86
114
  if (!mountedRef.current)
87
115
  return;
88
116
  const errorMessage = err instanceof Error ? err.message : 'An unexpected error occurred';
117
+ log.error(`deploy threw exception: ${errorMessage}`, err);
89
118
  setDeployError(errorMessage);
90
119
  errorTimerRef.current = setTimeout(() => setDeployError(null), ERROR_CLEAR_DELAY);
91
120
  }
@@ -98,9 +127,11 @@ export function useDeployAction(input) {
98
127
  const handleStop = useCallback(async () => {
99
128
  if (!input || stopLoading)
100
129
  return;
130
+ log.info(`stop() — targetId="${input.targetId}"`);
101
131
  setStopLoading(true);
102
132
  try {
103
133
  const result = await stopDeployment(input.targetId);
134
+ log.info('stop result:', result);
104
135
  if (!mountedRef.current)
105
136
  return;
106
137
  if (result.success) {
@@ -109,8 +140,8 @@ export function useDeployAction(input) {
109
140
  setUrl(null);
110
141
  }
111
142
  }
112
- catch {
113
- // Stop errors are non-critical; process may have already exited
143
+ catch (err) {
144
+ log.warn('stop error (non-critical):', err);
114
145
  }
115
146
  finally {
116
147
  if (mountedRef.current) {