@shepai/cli 1.70.0 → 1.70.2
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/packages/core/src/infrastructure/services/deployment/deployment-logger.d.ts +7 -0
- package/dist/packages/core/src/infrastructure/services/deployment/deployment-logger.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/deployment/deployment-logger.js +17 -0
- package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.js +60 -7
- package/dist/packages/core/src/infrastructure/services/deployment/detect-dev-script.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/deployment/detect-dev-script.js +13 -6
- package/dist/src/presentation/web/app/actions/deploy-feature.d.ts.map +1 -1
- package/dist/src/presentation/web/app/actions/deploy-feature.js +11 -0
- package/dist/src/presentation/web/app/actions/deploy-repository.d.ts.map +1 -1
- package/dist/src/presentation/web/app/actions/deploy-repository.js +8 -0
- package/dist/src/presentation/web/components/common/base-drawer/base-drawer.js +1 -1
- package/dist/src/presentation/web/components/common/base-drawer/base-drawer.stories.js +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.js +1 -1
- package/dist/src/presentation/web/components/common/merge-review/merge-review.js +1 -1
- package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.js +1 -1
- package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.js +1 -1
- package/dist/src/presentation/web/hooks/use-deploy-action.d.ts.map +1 -1
- package/dist/src/presentation/web/hooks/use-deploy-action.js +36 -5
- 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/.tsbuildinfo +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 +18 -18
- package/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +5 -5
- 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]__249c74f6._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__551fb7e1._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__551fb7e1._.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]__e41b5eec._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__e41b5eec._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_49bf495c._.js +1 -1
- package/web/.next/server/chunks/ssr/_49bf495c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_68b5e0de._.js +1 -1
- package/web/.next/server/chunks/ssr/_68b5e0de._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_725584e5._.js +1 -1
- package/web/.next/server/chunks/ssr/_725584e5._.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 +19 -19
- 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 +18 -18
- 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 +5 -5
- 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]__249c74f6._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__551fb7e1._.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]__e41b5eec._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_49bf495c._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_68b5e0de._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_725584e5._.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 +19 -19
- package/web/.next/standalone/src/presentation/web/app/actions/deploy-feature.ts +16 -0
- package/web/.next/standalone/src/presentation/web/app/actions/deploy-repository.ts +10 -0
- package/web/.next/standalone/src/presentation/web/components/common/base-drawer/base-drawer.stories.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/components/common/base-drawer/base-drawer.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/control-center-drawer.tsx +2 -2
- package/web/.next/standalone/src/presentation/web/components/common/merge-review/merge-review.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/hooks/use-deploy-action.ts +45 -5
- package/web/.next/standalone/src/presentation/web/server.js +1 -1
- package/web/.next/static/chunks/{fdfd68aacc4727cf.js → 09e70c24eceecf48.js} +2 -2
- package/web/.next/static/chunks/{6d7d233d07d9a026.js → 2eae41f87676d999.js} +1 -1
- package/web/.next/static/chunks/4b357783626e0916.js +1 -0
- package/web/.next/static/chunks/{86b38e36afcd19f3.js → b581320ecaa97e4a.js} +1 -1
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/web/.next/static/chunks/94562d62bf8e4bf7.js +0 -1
- /package/web/.next/static/{6pJLWKt-ohZJ_2kGrHG_m → MoPU_ZZ5QcyEys7Gfl_6r}/_buildManifest.js +0 -0
- /package/web/.next/static/{6pJLWKt-ohZJ_2kGrHG_m → MoPU_ZZ5QcyEys7Gfl_6r}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{6pJLWKt-ohZJ_2kGrHG_m → MoPU_ZZ5QcyEys7Gfl_6r}/_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
|
+
}
|
package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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] +=
|
|
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.
|
package/dist/packages/core/src/infrastructure/services/deployment/detect-dev-script.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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":"
|
|
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":"
|
|
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
|
}
|
|
@@ -44,7 +44,7 @@ export function BaseDrawer({ open, onClose, modal = false, size, header, childre
|
|
|
44
44
|
return (_jsxs(Drawer, { direction: "right", modal: modal, handleOnly: true, open: open, onOpenChange: (isOpen) => {
|
|
45
45
|
if (!isOpen)
|
|
46
46
|
onClose();
|
|
47
|
-
}, children: [modal ? _jsx(DrawerOverlay, {}) : null, _jsxs(DrawerContent, { ref: contentRef, direction: "right", showCloseButton: false, className: cn(drawerVariants({ size }), className), "data-testid": testId, children: [_jsxs("button", { type: "button", "aria-label": "Close", onClick: onClose, className: "ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden", "data-testid": testId ? `${testId}-close-button` : undefined, children: [_jsx(XIcon, { className: "size-4" }), _jsx("span", { className: "sr-only", children: "Close" })] }), header ? _jsx(DrawerHeader, { className: "shrink-0", children: header }) : null, header ? _jsx(Separator, {}) : null, featureFlags.envDeploy && deployTarget ? _jsx(DeployBar, { deployTarget: deployTarget }) : null, _jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: children }), footer ? _jsx(DrawerFooter, { className: "shrink-0", children: footer }) : null] })] }));
|
|
47
|
+
}, children: [modal ? _jsx(DrawerOverlay, {}) : null, _jsxs(DrawerContent, { ref: contentRef, direction: "right", showCloseButton: false, className: cn(drawerVariants({ size }), className), "data-testid": testId, children: [_jsxs("button", { type: "button", "aria-label": "Close", onClick: onClose, className: "ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden", "data-testid": testId ? `${testId}-close-button` : undefined, children: [_jsx(XIcon, { className: "size-4" }), _jsx("span", { className: "sr-only", children: "Close" })] }), header ? _jsx(DrawerHeader, { className: "shrink-0", children: header }) : null, header ? _jsx(Separator, {}) : null, featureFlags.envDeploy && deployTarget ? _jsx(DeployBar, { deployTarget: deployTarget }) : null, _jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: children }), footer ? _jsx(DrawerFooter, { className: "shrink-0", children: footer }) : null] })] }));
|
|
48
48
|
}
|
|
49
49
|
function DeployBar({ deployTarget }) {
|
|
50
50
|
const deployAction = useDeployAction(deployTarget);
|
|
@@ -75,5 +75,5 @@ export const WithDevServerBar = {
|
|
|
75
75
|
* Try scrolling the content below to see the fixed header behavior.
|
|
76
76
|
*/
|
|
77
77
|
export const ScrollableContent = {
|
|
78
|
-
render: () => (_jsx(DrawerTrigger, { header: _jsxs(_Fragment, { children: [_jsx(DrawerTitle, { children: "Scrollable Content" }), _jsx(DrawerDescription, { children: "Content below overflows and scrolls" })] }), children: _jsx("div", { className: "flex flex-col gap-4 p-4", children: Array.from({ length: 30 }, (_, i) => (_jsxs("div", { className: "border-border rounded-md border p-3", children: [_jsxs("h4", { className: "text-sm font-medium", children: ["Item ", i + 1] }), _jsx("p", { className: "text-muted-foreground text-xs", children: "This is a scrollable content item to demonstrate overflow handling." })] }, i))) }) })),
|
|
78
|
+
render: () => (_jsx(DrawerTrigger, { header: _jsxs(_Fragment, { children: [_jsx(DrawerTitle, { children: "Scrollable Content" }), _jsx(DrawerDescription, { children: "Content below overflows and scrolls" })] }), children: _jsx("div", { className: "flex flex-col gap-4 overflow-y-auto p-4", children: Array.from({ length: 30 }, (_, i) => (_jsxs("div", { className: "border-border rounded-md border p-3", children: [_jsxs("h4", { className: "text-sm font-medium", children: ["Item ", i + 1] }), _jsx("p", { className: "text-muted-foreground text-xs", children: "This is a scrollable content item to demonstrate overflow handling." })] }, i))) }) })),
|
|
79
79
|
};
|
package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.js
CHANGED
|
@@ -273,7 +273,7 @@ export function ControlCenterDrawer({ view, onClose, onDelete, isDeleting, onCre
|
|
|
273
273
|
body = mergeData ? (_jsx(MergeReview, { data: mergeData, onApprove: handleMergeApprove, onReject: handleMergeReject, isProcessing: isLoadingMerge, isRejecting: isRejecting })) : (_jsx("div", { className: "flex items-center justify-center p-8", children: _jsx(Loader2, { className: "text-muted-foreground h-6 w-6 animate-spin" }) }));
|
|
274
274
|
}
|
|
275
275
|
else if (view?.type === 'repository' && repoData?.repositoryPath) {
|
|
276
|
-
body = (_jsxs(
|
|
276
|
+
body = (_jsxs("div", { className: "flex-1 overflow-y-auto", children: [_jsx(Separator, {}), _jsxs("div", { className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "OPEN WITH" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(ActionButton, { label: "Open in IDE", onClick: repoActions.openInIde, loading: repoActions.ideLoading, error: !!repoActions.ideError, icon: Code2, variant: "outline", size: "sm" }), _jsx(ActionButton, { label: "Open in Shell", onClick: repoActions.openInShell, loading: repoActions.shellLoading, error: !!repoActions.shellError, icon: Terminal, variant: "outline", size: "sm" }), _jsx(ActionButton, { label: "Open Folder", onClick: repoActions.openFolder, loading: repoActions.folderLoading, error: !!repoActions.folderError, icon: FolderOpen, variant: "outline", size: "sm" })] })] })] }));
|
|
277
277
|
}
|
|
278
278
|
// ── Render ──────────────────────────────────────────────────────────────
|
|
279
279
|
return (_jsxs(_Fragment, { children: [_jsx(BaseDrawer, { open: view !== null && !isCreateView, onClose: onClose, size: "md", modal: false, header: header, deployTarget: repoDeployTarget, "data-testid": view?.type === 'feature'
|
|
@@ -9,7 +9,7 @@ function PhaseList({ phases }) {
|
|
|
9
9
|
}
|
|
10
10
|
export function MergeReview({ data, onApprove, onReject, isProcessing = false, isRejecting = false, }) {
|
|
11
11
|
const { pr, diffSummary, phases, branch, warning } = data;
|
|
12
|
-
return (_jsxs("div", { className: "flex flex-1 flex-col", children: [_jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto p-4", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "mt-1.5 h-2.5 w-2.5 shrink-0 rounded-full bg-emerald-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("h2", { className: "text-foreground text-sm font-bold", children: "Merge Review" }), _jsx("p", { className: "text-muted-foreground mt-1 text-xs leading-relaxed", children: pr
|
|
12
|
+
return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [_jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto p-4", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "mt-1.5 h-2.5 w-2.5 shrink-0 rounded-full bg-emerald-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("h2", { className: "text-foreground text-sm font-bold", children: "Merge Review" }), _jsx("p", { className: "text-muted-foreground mt-1 text-xs leading-relaxed", children: pr
|
|
13
13
|
? 'Review the pull request details and approve to merge.'
|
|
14
14
|
: 'Review the changes and approve to merge.' })] })] }), branch ? (_jsx("div", { className: "border-border rounded-lg border", children: _jsxs("div", { className: "flex items-center gap-2 px-4 py-3", children: [_jsx(GitBranch, { className: "text-muted-foreground h-4 w-4 shrink-0" }), _jsx(Badge, { variant: "secondary", className: "font-mono text-[11px]", children: branch.source }), _jsx(ArrowRight, { className: "text-muted-foreground h-3.5 w-3.5 shrink-0" }), _jsx(Badge, { variant: "outline", className: "font-mono text-[11px]", children: branch.target })] }) })) : null, pr ? (_jsx("div", { className: "border-border rounded-lg border", children: _jsxs("div", { className: "space-y-3 px-4 py-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("a", { href: pr.url, target: "_blank", rel: "noopener noreferrer", className: "text-primary flex items-center gap-1.5 text-sm font-semibold underline underline-offset-2", children: ["PR #", pr.number, _jsx(ExternalLink, { className: "h-3.5 w-3.5" })] }), _jsx(Badge, { variant: "outline", className: "text-xs", children: pr.status })] }), pr.ciStatus ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "CI Status" }), _jsx(CiStatusBadge, { status: pr.ciStatus })] })) : null, pr.commitHash ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Commit" }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(GitCommitHorizontal, { className: "text-muted-foreground h-3.5 w-3.5" }), _jsx("code", { className: "bg-muted text-foreground rounded-md px-1.5 py-0.5 font-mono text-[11px]", children: pr.commitHash.slice(0, 7) })] })] })) : null] }) })) : null, diffSummary ? (_jsx("div", { className: "border-border rounded-lg border", children: _jsxs("div", { className: "px-4 py-3", children: [_jsxs("div", { className: "mb-3 flex items-center gap-2", children: [_jsx(FileDiff, { className: "text-muted-foreground h-4 w-4" }), _jsx("span", { className: "text-foreground text-xs font-semibold", children: "Changes" })] }), _jsxs("div", { className: "grid grid-cols-4 gap-3", children: [_jsxs("div", { className: "text-center", children: [_jsx("div", { className: "text-foreground text-sm font-bold", children: diffSummary.filesChanged }), _jsx("div", { className: "text-muted-foreground text-[10px]", children: "files" })] }), _jsxs("div", { className: "text-center", children: [_jsxs("div", { className: "text-sm font-bold text-green-600", children: ["+", diffSummary.additions] }), _jsx("div", { className: "text-muted-foreground text-[10px]", children: "additions" })] }), _jsxs("div", { className: "text-center", children: [_jsxs("div", { className: "text-sm font-bold text-red-600", children: ["-", diffSummary.deletions] }), _jsx("div", { className: "text-muted-foreground text-[10px]", children: "deletions" })] }), _jsxs("div", { className: "text-center", children: [_jsx("div", { className: "text-foreground text-sm font-bold", children: diffSummary.commitCount }), _jsx("div", { className: "text-muted-foreground text-[10px]", children: "commits" })] })] })] }) })) : warning ? (_jsx("div", { className: "border-border rounded-lg border", children: _jsxs("div", { className: "flex items-center gap-2 px-4 py-3", children: [_jsx(AlertTriangle, { className: "text-muted-foreground h-4 w-4 shrink-0" }), _jsx("span", { className: "text-muted-foreground text-xs", children: warning })] }) })) : null, phases && phases.length > 0 ? _jsx(PhaseList, { phases: phases }) : null] }), _jsx(DrawerActionBar, { onReject: onReject, onApprove: onApprove, approveLabel: "Approve Merge", approveIcon: isProcessing ? (_jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" })) : (_jsx(GitMerge, { className: "mr-1.5 h-4 w-4" })), revisionPlaceholder: "Ask AI to revise before merging...", isProcessing: isProcessing, isRejecting: isRejecting })] }));
|
|
15
15
|
}
|
|
@@ -27,7 +27,7 @@ export function PrdQuestionnaire({ data, selections, onSelect, onApprove, onReje
|
|
|
27
27
|
}, [onSelect, isLastStep, selectSound]);
|
|
28
28
|
if (total === 0)
|
|
29
29
|
return null;
|
|
30
|
-
return (_jsxs("div", { className: "flex flex-1 flex-col", children: [_jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto p-4", children: [showHeader ? (_jsxs("div", { className: "border-border flex items-start gap-3 border-b pb-3", children: [_jsx("div", { className: "mt-1.5 h-2 w-2 shrink-0 rounded-full bg-amber-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "text-foreground mb-1.5 text-sm font-bold", children: question }), _jsx("p", { className: "text-muted-foreground text-xs leading-relaxed", children: context })] })] })) : null, _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("label", { className: "text-foreground min-w-0 flex-1 text-sm font-semibold", children: currentQuestion.question }), _jsx("div", { className: "mt-1.5 flex shrink-0 gap-1", children: questions.map((q, idx) => (_jsx("button", { type: "button", "aria-label": `Go to question ${idx + 1}`, className: cn('h-1.5 rounded-full transition-all duration-200', idx === currentStep ? 'bg-primary w-4' : 'w-1.5', idx !== currentStep && selections[q.id] ? 'bg-primary/50' : '', idx !== currentStep && !selections[q.id] ? 'bg-muted-foreground/25' : ''), onClick: () => {
|
|
30
|
+
return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [_jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto p-4", children: [showHeader ? (_jsxs("div", { className: "border-border flex items-start gap-3 border-b pb-3", children: [_jsx("div", { className: "mt-1.5 h-2 w-2 shrink-0 rounded-full bg-amber-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "text-foreground mb-1.5 text-sm font-bold", children: question }), _jsx("p", { className: "text-muted-foreground text-xs leading-relaxed", children: context })] })] })) : null, _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("label", { className: "text-foreground min-w-0 flex-1 text-sm font-semibold", children: currentQuestion.question }), _jsx("div", { className: "mt-1.5 flex shrink-0 gap-1", children: questions.map((q, idx) => (_jsx("button", { type: "button", "aria-label": `Go to question ${idx + 1}`, className: cn('h-1.5 rounded-full transition-all duration-200', idx === currentStep ? 'bg-primary w-4' : 'w-1.5', idx !== currentStep && selections[q.id] ? 'bg-primary/50' : '', idx !== currentStep && !selections[q.id] ? 'bg-muted-foreground/25' : ''), onClick: () => {
|
|
31
31
|
navigateSound.play();
|
|
32
32
|
setCurrentStep(idx);
|
|
33
33
|
} }, q.id))) })] }), _jsx("div", { className: "space-y-2", children: currentQuestion.options.map((opt, optIdx) => {
|
package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.js
CHANGED
|
@@ -38,5 +38,5 @@ export function TechDecisionsReview({ data, onApprove, onReject, isProcessing =
|
|
|
38
38
|
const { summary, decisions } = data;
|
|
39
39
|
if (decisions.length === 0)
|
|
40
40
|
return null;
|
|
41
|
-
return (_jsxs("div", { className: "flex flex-1 flex-col", children: [_jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto p-4", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "mt-1.5 h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("h2", { className: "text-foreground text-sm font-bold", children: "Technical Implementation Plan Review" }), summary ? (_jsx("p", { className: "text-muted-foreground mt-1 text-xs leading-relaxed", children: summary })) : null] })] }), _jsxs("div", { className: "flex items-center gap-2 pt-1", children: [_jsx(GitCompareArrows, { className: "text-primary h-4 w-4" }), _jsx("h3", { className: "text-foreground text-sm font-bold", children: "Technical Decisions" })] }), decisions.map((decision, i) => (_jsx(DecisionCard, { decision: decision, index: i }, decision.title)))] }), _jsx(DrawerActionBar, { onReject: onReject, onApprove: onApprove, approveLabel: "Approve Plan", approveIcon: _jsx(Check, { className: "mr-1.5 h-4 w-4" }), revisionPlaceholder: "Ask AI to revise the plan...", isProcessing: isProcessing, isRejecting: isRejecting })] }));
|
|
41
|
+
return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [_jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto p-4", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "mt-1.5 h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("h2", { className: "text-foreground text-sm font-bold", children: "Technical Implementation Plan Review" }), summary ? (_jsx("p", { className: "text-muted-foreground mt-1 text-xs leading-relaxed", children: summary })) : null] })] }), _jsxs("div", { className: "flex items-center gap-2 pt-1", children: [_jsx(GitCompareArrows, { className: "text-primary h-4 w-4" }), _jsx("h3", { className: "text-foreground text-sm font-bold", children: "Technical Decisions" })] }), decisions.map((decision, i) => (_jsx(DecisionCard, { decision: decision, index: i }, decision.title)))] }), _jsx(DrawerActionBar, { onReject: onReject, onApprove: onApprove, approveLabel: "Approve Plan", approveIcon: _jsx(Check, { className: "mr-1.5 h-4 w-4" }), revisionPlaceholder: "Ask AI to revise the plan...", isProcessing: isProcessing, isRejecting: isRejecting })] }));
|
|
42
42
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
143
|
+
catch (err) {
|
|
144
|
+
log.warn('stop error (non-critical):', err);
|
|
114
145
|
}
|
|
115
146
|
finally {
|
|
116
147
|
if (mountedRef.current) {
|