@shepai/cli 1.125.0 → 1.126.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packages/core/src/application/ports/output/services/deployment-service.interface.d.ts +13 -2
- package/dist/packages/core/src/application/ports/output/services/deployment-service.interface.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.js +2 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations/040-create-dev-servers-table.d.ts +21 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations/040-create-dev-servers-table.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations/040-create-dev-servers-table.js +32 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-watch-fix-loop.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-watch-fix-loop.js +9 -0
- package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.d.ts +24 -14
- 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 +203 -31
- package/dist/packages/core/src/infrastructure/services/deployment/parse-port.d.ts +0 -6
- package/dist/packages/core/src/infrastructure/services/deployment/parse-port.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/deployment/parse-port.js +6 -1
- package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/git/git-pr.service.js +7 -2
- package/dist/src/presentation/web/app/actions/deploy-feature.js +1 -1
- package/dist/src/presentation/web/app/actions/deploy-repository.js +1 -1
- package/dist/src/presentation/web/components/common/repository-node/repository-node.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/repository-node/repository-node.js +7 -4
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +12 -3
- package/dist/src/presentation/web/hooks/use-deploy-action.d.ts.map +1 -1
- package/dist/src/presentation/web/hooks/use-deploy-action.js +69 -10
- package/dist/src/presentation/web/lib/layout-with-dagre.js +3 -3
- 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/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +2 -2
- package/web/.next/required-server-files.json +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +57 -57
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +69 -69
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +69 -69
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +53 -53
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +57 -57
- package/web/.next/server/app/(dashboard)/create/page.js +2 -2
- package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +69 -69
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js +2 -2
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +69 -69
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +2 -2
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +53 -53
- package/web/.next/server/app/(dashboard)/page.js +2 -2
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +53 -53
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +2 -2
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +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/settings/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/skills/page.js +1 -2
- package/web/.next/server/app/skills/page.js.nft.json +1 -1
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/tools/page.js +1 -2
- package/web/.next/server/app/tools/page.js.nft.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/403f9_next_dist_esm_build_templates_app-route_370c43b1.js +1 -1
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_370c43b1.js.map +1 -1
- package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_fdfbf5cc.js +3 -0
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_fdfbf5cc.js.map +1 -0
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_d8fa0b5f.js +3 -0
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_d8fa0b5f.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__06fef644._.js +1 -1
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__7cbda1ea._.js → [root-of-the-server]__248b9fc3._.js} +2 -2
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__9e8b25ad._.js.map → [root-of-the-server]__248b9fc3._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__357de47c._.js +4 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__357de47c._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__56ecbe40._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__56ecbe40._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__67fe07fe._.js +4 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__67fe07fe._.js.map +1 -0
- 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]__6bb00d90._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.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]__8c553503._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a50a7e39._.js +4 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a50a7e39._.js.map +1 -0
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__9e8b25ad._.js → [root-of-the-server]__dfc367f2._.js} +2 -2
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__ce6057f6._.js.map → [root-of-the-server]__dfc367f2._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__ce6057f6._.js → [root-of-the-server]__e7f61cc6._.js} +2 -2
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__7cbda1ea._.js.map → [root-of-the-server]__e7f61cc6._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__aed1d6f8._.js → [root-of-the-server]__fe48d2dc._.js} +2 -2
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__aed1d6f8._.js.map → [root-of-the-server]__fe48d2dc._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/{_d8b3f91f._.js → _243e3685._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_d8b3f91f._.js.map → _243e3685._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_2f6f48b8._.js +1 -1
- package/web/.next/server/chunks/ssr/_2f6f48b8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_7dca1882._.js +3 -0
- package/web/.next/server/chunks/ssr/_7dca1882._.js.map +1 -0
- package/web/.next/server/chunks/ssr/{_772be40b._.js → _821bbcc5._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_772be40b._.js.map → _821bbcc5._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_e9703421._.js +1 -1
- package/web/.next/server/chunks/ssr/_e9703421._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_f39a1adb._.js +3 -0
- package/web/.next/server/chunks/ssr/{_135edb9c._.js.map → _f39a1adb._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_9db47db7._.js +5 -0
- package/web/.next/server/chunks/ssr/{src_presentation_web_5f039db1._.js.map → src_presentation_web_9db47db7._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f66cd328.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f66cd328.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_aeddd007._.js +3 -0
- package/web/.next/server/chunks/ssr/{src_presentation_web_d2912c21._.js.map → src_presentation_web_aeddd007._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ede4a227._.js +5 -0
- package/web/.next/server/chunks/ssr/{src_presentation_web_c4337169._.js.map → src_presentation_web_ede4a227._.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 +325 -325
- package/web/.next/static/chunks/{88639a2a92364951.js → 044f4665499a9656.js} +1 -1
- package/web/.next/static/chunks/{ab42606b8d9a837b.js → 112cf5891d1ca558.js} +1 -1
- package/web/.next/static/chunks/1be7ac7a24009333.css +1 -0
- package/web/.next/static/chunks/{cab346230cafc536.js → 303a1217006489b9.js} +1 -1
- package/web/.next/static/chunks/4ac11847f73b50d6.js +1 -0
- package/web/.next/static/chunks/{ee1b7156e3985766.js → 4ac4a43b7eb441e1.js} +1 -1
- package/web/.next/static/chunks/{609a74a7c7084d18.js → 5fb66ee5a353a6c6.js} +1 -1
- package/web/.next/static/chunks/{225092aaa7291166.js → 6cfc75060a243290.js} +1 -1
- package/web/.next/static/chunks/{0eb9cffdddfd0bb6.js → d60f93a1a25ab09d.js} +1 -1
- package/web/.next/static/chunks/dcfed2c9b7cd1cb4.js +1 -0
- package/web/.next/static/chunks/{3b6956717a7c4e16.js → de66f8d6a67c2361.js} +1 -1
- package/web/.next/static/chunks/{03efd55a6de64460.js → de8b0265f780e212.js} +2 -2
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_f30d0343.js +0 -3
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_f30d0343.js.map +0 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_9f1775bb.js +0 -3
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_9f1775bb.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__14664e9b._.js +0 -4
- package/web/.next/server/chunks/ssr/[root-of-the-server]__14664e9b._.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a7de2711._.js +0 -4
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a7de2711._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_135edb9c._.js +0 -3
- package/web/.next/server/chunks/ssr/_cc77a143._.js +0 -3
- package/web/.next/server/chunks/ssr/_cc77a143._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_5f039db1._.js +0 -5
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_2a52b697.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_2a52b697.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_c4337169._.js +0 -5
- package/web/.next/server/chunks/ssr/src_presentation_web_d2912c21._.js +0 -3
- package/web/.next/static/chunks/0158bd913a9f8332.css +0 -1
- package/web/.next/static/chunks/93e492830319fe32.js +0 -1
- package/web/.next/static/chunks/9ff2ffa85c1a522d.js +0 -1
- /package/web/.next/static/{kV-WQWxVkAheI2w3nsEIN → 4hN7Gtw--ruMbDn0FRI2-}/_buildManifest.js +0 -0
- /package/web/.next/static/{kV-WQWxVkAheI2w3nsEIN → 4hN7Gtw--ruMbDn0FRI2-}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{kV-WQWxVkAheI2w3nsEIN → 4hN7Gtw--ruMbDn0FRI2-}/_ssgManifest.js +0 -0
package/dist/packages/core/src/application/ports/output/services/deployment-service.interface.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - Application layer depends on this interface
|
|
10
10
|
* - Infrastructure layer provides concrete implementations
|
|
11
11
|
*/
|
|
12
|
+
import type Database from 'better-sqlite3';
|
|
12
13
|
import type { DeploymentState } from '../../../../domain/generated/output.js';
|
|
13
14
|
/** A single log line captured from a deployment's stdout or stderr. */
|
|
14
15
|
export interface LogEntry {
|
|
@@ -39,16 +40,26 @@ export interface DeploymentStatus {
|
|
|
39
40
|
* - Support graceful shutdown (SIGTERM → SIGKILL) for individual and bulk stops
|
|
40
41
|
*/
|
|
41
42
|
export interface IDeploymentService {
|
|
43
|
+
/**
|
|
44
|
+
* Inject the database connection for persistence.
|
|
45
|
+
* Must be called before start/stop/getStatus to enable DB persistence.
|
|
46
|
+
*/
|
|
47
|
+
setDatabase(db: Database.Database): void;
|
|
48
|
+
/**
|
|
49
|
+
* Recover dev servers from the database on startup.
|
|
50
|
+
* Validates each PID is still alive; removes dead rows.
|
|
51
|
+
*/
|
|
52
|
+
recoverAll(): void;
|
|
42
53
|
/**
|
|
43
54
|
* Start a dev server deployment for the given target.
|
|
44
55
|
* If a deployment already exists for this targetId, it is stopped first.
|
|
45
56
|
*
|
|
46
57
|
* @param targetId - Unique identifier for the deployment target (featureId or repositoryId)
|
|
47
58
|
* @param targetPath - Absolute filesystem path to the directory to run the dev server in
|
|
48
|
-
* @
|
|
59
|
+
* @param targetType - Type of target ('feature' or 'repository')
|
|
49
60
|
* @throws Error if no dev script is found in package.json or the process fails to spawn
|
|
50
61
|
*/
|
|
51
|
-
start(targetId: string, targetPath: string): void;
|
|
62
|
+
start(targetId: string, targetPath: string, targetType?: string): void;
|
|
52
63
|
/**
|
|
53
64
|
* Stop a running deployment gracefully.
|
|
54
65
|
* Sends SIGTERM to the process group, then SIGKILL after a timeout.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment-service.interface.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/application/ports/output/services/deployment-service.interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,uEAAuE;AACvE,MAAM,WAAW,QAAQ;IACvB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,KAAK,EAAE,eAAe,CAAC;IACvB,qEAAqE;IACrE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;OAQG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"deployment-service.interface.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/application/ports/output/services/deployment-service.interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,uEAAuE;AACvE,MAAM,WAAW,QAAQ;IACvB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,KAAK,EAAE,eAAe,CAAC;IACvB,qEAAqE;IACrE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,WAAW,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC;IAEzC;;;OAGG;IACH,UAAU,IAAI,IAAI,CAAC;IAEnB;;;;;;;;OAQG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvE;;;;;;OAMG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtC;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAC;IAErD;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAC;IAIhB;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC;IAE7C;;;;;OAKG;IACH,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI,CAAC;IAE3D;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI,CAAC;CAC7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAoHrC;;;;;;GAMG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAoHrC;;;;;;GAMG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,SAAS,CAAC,CAmUrE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -169,6 +169,8 @@ export async function initializeContainer() {
|
|
|
169
169
|
container.registerSingleton(AttachmentStorageService);
|
|
170
170
|
container.register('AttachmentStorageService', { useToken: AttachmentStorageService });
|
|
171
171
|
const deploymentService = new DeploymentService();
|
|
172
|
+
deploymentService.setDatabase(db);
|
|
173
|
+
deploymentService.recoverAll();
|
|
172
174
|
container.registerInstance('IDeploymentService', deploymentService);
|
|
173
175
|
// Register agent infrastructure
|
|
174
176
|
container.register('IAgentRunRepository', {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration 040: Create dev_servers table.
|
|
3
|
+
*
|
|
4
|
+
* Persists running dev server deployments so they survive page reloads
|
|
5
|
+
* and server restarts. The DeploymentService reconciles on startup by
|
|
6
|
+
* checking PID liveness and cleaning up stale rows.
|
|
7
|
+
*
|
|
8
|
+
* Columns:
|
|
9
|
+
* - target_id TEXT PRIMARY KEY — featureId or repositoryPath
|
|
10
|
+
* - target_type TEXT NOT NULL — 'feature' | 'repository'
|
|
11
|
+
* - pid INTEGER NOT NULL — OS process ID
|
|
12
|
+
* - state TEXT NOT NULL — DeploymentState enum value
|
|
13
|
+
* - url TEXT — detected dev server URL (null while Booting)
|
|
14
|
+
* - target_path TEXT NOT NULL — filesystem path where the dev server runs
|
|
15
|
+
* - started_at INTEGER NOT NULL — unix milliseconds
|
|
16
|
+
*/
|
|
17
|
+
import type { MigrationParams } from 'umzug';
|
|
18
|
+
import type Database from 'better-sqlite3';
|
|
19
|
+
export declare function up({ context: db }: MigrationParams<Database.Database>): Promise<void>;
|
|
20
|
+
export declare function down({ context: db }: MigrationParams<Database.Database>): Promise<void>;
|
|
21
|
+
//# sourceMappingURL=040-create-dev-servers-table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"040-create-dev-servers-table.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/persistence/sqlite/migrations/040-create-dev-servers-table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,wBAAsB,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAY3F;AAED,wBAAsB,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7F"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration 040: Create dev_servers table.
|
|
3
|
+
*
|
|
4
|
+
* Persists running dev server deployments so they survive page reloads
|
|
5
|
+
* and server restarts. The DeploymentService reconciles on startup by
|
|
6
|
+
* checking PID liveness and cleaning up stale rows.
|
|
7
|
+
*
|
|
8
|
+
* Columns:
|
|
9
|
+
* - target_id TEXT PRIMARY KEY — featureId or repositoryPath
|
|
10
|
+
* - target_type TEXT NOT NULL — 'feature' | 'repository'
|
|
11
|
+
* - pid INTEGER NOT NULL — OS process ID
|
|
12
|
+
* - state TEXT NOT NULL — DeploymentState enum value
|
|
13
|
+
* - url TEXT — detected dev server URL (null while Booting)
|
|
14
|
+
* - target_path TEXT NOT NULL — filesystem path where the dev server runs
|
|
15
|
+
* - started_at INTEGER NOT NULL — unix milliseconds
|
|
16
|
+
*/
|
|
17
|
+
export async function up({ context: db }) {
|
|
18
|
+
db.exec(`
|
|
19
|
+
CREATE TABLE IF NOT EXISTS dev_servers (
|
|
20
|
+
target_id TEXT PRIMARY KEY,
|
|
21
|
+
target_type TEXT NOT NULL,
|
|
22
|
+
pid INTEGER NOT NULL,
|
|
23
|
+
state TEXT NOT NULL DEFAULT 'Booting',
|
|
24
|
+
url TEXT,
|
|
25
|
+
target_path TEXT NOT NULL,
|
|
26
|
+
started_at INTEGER NOT NULL
|
|
27
|
+
)
|
|
28
|
+
`);
|
|
29
|
+
}
|
|
30
|
+
export async function down({ context: db }) {
|
|
31
|
+
db.exec('DROP TABLE IF EXISTS dev_servers');
|
|
32
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci-watch-fix-loop.d.ts","sourceRoot":"","sources":["../../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-watch-fix-loop.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yEAAyE,CAAC;AAClH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iEAAiE,CAAC;AAKrG,OAAO,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+DAA+D,CAAC;AAK3G,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,CAAC;IACzB,YAAY,EAAE,aAAa,CAAC;IAC5B,iBAAiB,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC;CACpE;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,qBAAqB,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC/E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE,UAAU,CAAC;CACjB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;AAEpG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,WAAW,EAAE,gBAAgB,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"ci-watch-fix-loop.d.ts","sourceRoot":"","sources":["../../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-watch-fix-loop.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yEAAyE,CAAC;AAClH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iEAAiE,CAAC;AAKrG,OAAO,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+DAA+D,CAAC;AAK3G,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,cAAc,CAAC;IACzB,YAAY,EAAE,aAAa,CAAC;IAC5B,iBAAiB,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC;CACpE;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,qBAAqB,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC/E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE,UAAU,CAAC;CACjB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;AAEpG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,WAAW,EAAE,gBAAgB,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAuJ3B"}
|
|
@@ -84,6 +84,11 @@ export async function runCiWatchFixLoop(deps, params) {
|
|
|
84
84
|
}
|
|
85
85
|
throw err;
|
|
86
86
|
}
|
|
87
|
+
// Use the run URL from watchCi — it reflects the actual run watched,
|
|
88
|
+
// which may differ from the initial getCiStatus() result when multiple
|
|
89
|
+
// workflow runs exist for the same branch.
|
|
90
|
+
if (watchResult.runUrl)
|
|
91
|
+
runUrl = watchResult.runUrl;
|
|
87
92
|
if (watchResult.status === 'success') {
|
|
88
93
|
log.info('CI passed on first watch');
|
|
89
94
|
return { ciStatus: CiStatus.Success, ciFixAttempts, ciFixHistory, ciFixStatus: 'success' };
|
|
@@ -128,6 +133,10 @@ export async function runCiWatchFixLoop(deps, params) {
|
|
|
128
133
|
}
|
|
129
134
|
throw err;
|
|
130
135
|
}
|
|
136
|
+
// Update runUrl to the run that was actually watched (avoids mismatch
|
|
137
|
+
// when multiple workflow runs exist for the same branch).
|
|
138
|
+
if (fixWatchResult.runUrl)
|
|
139
|
+
runUrl = fixWatchResult.runUrl;
|
|
131
140
|
const outcome = fixWatchResult.status === 'success' ? 'fixed' : 'failed';
|
|
132
141
|
ciFixHistory.push({
|
|
133
142
|
attempt: ciFixAttempts,
|
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
* Deployment Service
|
|
3
3
|
*
|
|
4
4
|
* Infrastructure service that manages local dev server deployments.
|
|
5
|
-
* Holds an in-memory Map of active deployments keyed by targetId
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Holds an in-memory Map of active deployments keyed by targetId AND
|
|
6
|
+
* persists deployment records to the `dev_servers` SQLite table so that
|
|
7
|
+
* running dev servers survive page reloads and server restarts.
|
|
8
|
+
*
|
|
9
|
+
* On startup, call `recoverAll()` to reconcile the DB with actual
|
|
10
|
+
* process state (clean up dead PIDs, re-adopt live ones).
|
|
8
11
|
*/
|
|
9
12
|
import { spawn } from 'node:child_process';
|
|
13
|
+
import type Database from 'better-sqlite3';
|
|
10
14
|
import type { IDeploymentService, DeploymentStatus, LogEntry } from '../../../application/ports/output/services/deployment-service.interface.js';
|
|
11
15
|
import { detectDevScript } from './detect-dev-script.js';
|
|
12
16
|
export interface DeploymentServiceDeps {
|
|
@@ -19,15 +23,26 @@ export declare class DeploymentService implements IDeploymentService {
|
|
|
19
23
|
private readonly deployments;
|
|
20
24
|
private readonly deps;
|
|
21
25
|
private readonly emitter;
|
|
26
|
+
private db;
|
|
22
27
|
constructor(deps?: Partial<DeploymentServiceDeps>);
|
|
28
|
+
/**
|
|
29
|
+
* Inject the database connection. Called from DI container after DB is ready.
|
|
30
|
+
*/
|
|
31
|
+
setDatabase(db: Database.Database): void;
|
|
32
|
+
/**
|
|
33
|
+
* Recover dev servers from the database on startup.
|
|
34
|
+
* Validates each PID is still alive; removes dead rows.
|
|
35
|
+
*/
|
|
36
|
+
recoverAll(): void;
|
|
23
37
|
/**
|
|
24
38
|
* Start a deployment for the given target.
|
|
25
39
|
* If a deployment already exists for this target, it is stopped first.
|
|
26
40
|
*/
|
|
27
|
-
start(targetId: string, targetPath: string): void;
|
|
41
|
+
start(targetId: string, targetPath: string, targetType?: string): void;
|
|
28
42
|
/**
|
|
29
43
|
* Get the current deployment status for a target.
|
|
30
|
-
*
|
|
44
|
+
* Checks in-memory Map first, then falls back to DB for recovered deployments.
|
|
45
|
+
* Validates PID liveness — cleans up if dead.
|
|
31
46
|
*/
|
|
32
47
|
getStatus(targetId: string): DeploymentStatus | null;
|
|
33
48
|
/**
|
|
@@ -50,22 +65,17 @@ export declare class DeploymentService implements IDeploymentService {
|
|
|
50
65
|
* Unsubscribe from real-time log events.
|
|
51
66
|
*/
|
|
52
67
|
off(event: 'log', handler: (entry: LogEntry) => void): void;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
private dbUpsert;
|
|
69
|
+
private dbUpdateState;
|
|
70
|
+
private dbDelete;
|
|
71
|
+
private dbFind;
|
|
56
72
|
private killProcess;
|
|
57
73
|
/**
|
|
58
74
|
* Attach a line-buffered listener on stdout or stderr that calls parsePort
|
|
59
75
|
* and accumulates log entries.
|
|
60
76
|
*/
|
|
61
77
|
private attachOutputListener;
|
|
62
|
-
/**
|
|
63
|
-
* Poll until a process is dead or timeout expires.
|
|
64
|
-
*/
|
|
65
78
|
private pollUntilDead;
|
|
66
|
-
/**
|
|
67
|
-
* Wait for a child process to emit 'exit', with a short timeout.
|
|
68
|
-
*/
|
|
69
79
|
private waitForExit;
|
|
70
80
|
}
|
|
71
81
|
//# sourceMappingURL=deployment.service.d.ts.map
|
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
|
|
1
|
+
{"version":3,"file":"deployment.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/deployment/deployment.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAO3C,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACT,MAAM,qEAAqE,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAgCzD,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;AA6BD,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,EAAE,CAAkC;gBAEhC,IAAI,GAAE,OAAO,CAAC,qBAAqB,CAAM;IAIrD;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI;IAIxC;;;OAGG;IACH,UAAU,IAAI,IAAI;IA4ClB;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,SAAe,GAAG,IAAI;IAqF5E;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IA4CpD;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0D3C;;OAEG;IACH,OAAO,IAAI,IAAI;IAef;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI;IAM5C;;OAEG;IACH,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAI1D;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAM3D,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,MAAM;IAed,OAAO,CAAC,WAAW;IAQnB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;YA4Dd,aAAa;IAW3B,OAAO,CAAC,WAAW;CASpB"}
|
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
* Deployment Service
|
|
3
3
|
*
|
|
4
4
|
* Infrastructure service that manages local dev server deployments.
|
|
5
|
-
* Holds an in-memory Map of active deployments keyed by targetId
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Holds an in-memory Map of active deployments keyed by targetId AND
|
|
6
|
+
* persists deployment records to the `dev_servers` SQLite table so that
|
|
7
|
+
* running dev servers survive page reloads and server restarts.
|
|
8
|
+
*
|
|
9
|
+
* On startup, call `recoverAll()` to reconcile the DB with actual
|
|
10
|
+
* process state (clean up dead PIDs, re-adopt live ones).
|
|
8
11
|
*/
|
|
9
12
|
import { spawn } from 'node:child_process';
|
|
10
13
|
import { EventEmitter } from 'node:events';
|
|
11
|
-
|
|
14
|
+
// NOTE: We intentionally do NOT use tree-kill here. tree-kill traverses
|
|
15
|
+
// /proc to find child processes which can accidentally kill parent processes
|
|
16
|
+
// (including the Shep web UI). Instead, we use process.kill(-pid) to send
|
|
17
|
+
// signals to the process GROUP (since we spawn with detached: true, the
|
|
18
|
+
// child gets its own process group via setsid()).
|
|
12
19
|
import { DeploymentState } from '../../../domain/generated/output.js';
|
|
13
20
|
import { detectDevScript } from './detect-dev-script.js';
|
|
14
21
|
import { createDeploymentLogger } from './deployment-logger.js';
|
|
@@ -20,7 +27,22 @@ const MAX_WAIT_MS = 5000;
|
|
|
20
27
|
const defaultDeps = {
|
|
21
28
|
spawn,
|
|
22
29
|
detectDevScript,
|
|
23
|
-
kill: (pid, signal) =>
|
|
30
|
+
kill: (pid, signal) => {
|
|
31
|
+
try {
|
|
32
|
+
// Kill the entire process group (negative PID) — safe because
|
|
33
|
+
// detached: true puts the child in its own group via setsid().
|
|
34
|
+
process.kill(-pid, signal);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Fallback: kill just the process itself
|
|
38
|
+
try {
|
|
39
|
+
process.kill(pid, signal);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Process already dead
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
24
46
|
isAlive: (pid) => {
|
|
25
47
|
try {
|
|
26
48
|
process.kill(pid, 0);
|
|
@@ -35,14 +57,65 @@ export class DeploymentService {
|
|
|
35
57
|
deployments = new Map();
|
|
36
58
|
deps;
|
|
37
59
|
emitter = new EventEmitter();
|
|
60
|
+
db = null;
|
|
38
61
|
constructor(deps = {}) {
|
|
39
62
|
this.deps = { ...defaultDeps, ...deps };
|
|
40
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Inject the database connection. Called from DI container after DB is ready.
|
|
66
|
+
*/
|
|
67
|
+
setDatabase(db) {
|
|
68
|
+
this.db = db;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Recover dev servers from the database on startup.
|
|
72
|
+
* Validates each PID is still alive; removes dead rows.
|
|
73
|
+
*/
|
|
74
|
+
recoverAll() {
|
|
75
|
+
if (!this.db)
|
|
76
|
+
return;
|
|
77
|
+
let rows;
|
|
78
|
+
try {
|
|
79
|
+
const stmt = this.db.prepare('SELECT * FROM dev_servers');
|
|
80
|
+
rows = stmt.all() ?? [];
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
log.info('dev_servers table not ready — skipping recovery');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!rows || rows.length === 0) {
|
|
87
|
+
log.info('No dev servers to recover from database');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
log.info(`Recovering ${rows.length} dev server(s) from database`);
|
|
91
|
+
for (const row of rows) {
|
|
92
|
+
if (this.deps.isAlive(row.pid)) {
|
|
93
|
+
log.info(`Recovered "${row.target_id}" (pid=${row.pid}, state=${row.state}, url=${row.url})`);
|
|
94
|
+
const entry = {
|
|
95
|
+
pid: row.pid,
|
|
96
|
+
child: null, // orphan — we don't have the ChildProcess handle
|
|
97
|
+
state: row.state,
|
|
98
|
+
url: row.url,
|
|
99
|
+
targetId: row.target_id,
|
|
100
|
+
targetPath: row.target_path,
|
|
101
|
+
targetType: row.target_type,
|
|
102
|
+
stdoutBuffer: '',
|
|
103
|
+
stderrBuffer: '',
|
|
104
|
+
logs: new LogRingBuffer(),
|
|
105
|
+
};
|
|
106
|
+
this.deployments.set(row.target_id, entry);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
log.info(`Stale dev server "${row.target_id}" (pid=${row.pid}) — removing from DB`);
|
|
110
|
+
this.dbDelete(row.target_id);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
41
114
|
/**
|
|
42
115
|
* Start a deployment for the given target.
|
|
43
116
|
* If a deployment already exists for this target, it is stopped first.
|
|
44
117
|
*/
|
|
45
|
-
start(targetId, targetPath) {
|
|
118
|
+
start(targetId, targetPath, targetType = 'repository') {
|
|
46
119
|
log.info(`start() called — targetId="${targetId}", targetPath="${targetPath}"`);
|
|
47
120
|
// Stop any existing deployment for this target
|
|
48
121
|
const existing = this.deployments.get(targetId);
|
|
@@ -50,6 +123,7 @@ export class DeploymentService {
|
|
|
50
123
|
log.info(`Stopping existing deployment for "${targetId}" (pid=${existing.pid})`);
|
|
51
124
|
this.killProcess(existing);
|
|
52
125
|
this.deployments.delete(targetId);
|
|
126
|
+
this.dbDelete(targetId);
|
|
53
127
|
}
|
|
54
128
|
// Detect the dev script
|
|
55
129
|
const detection = this.deps.detectDevScript(targetPath);
|
|
@@ -78,11 +152,14 @@ export class DeploymentService {
|
|
|
78
152
|
state: DeploymentState.Booting,
|
|
79
153
|
url: null,
|
|
80
154
|
targetId,
|
|
155
|
+
targetPath,
|
|
156
|
+
targetType,
|
|
81
157
|
stdoutBuffer: '',
|
|
82
158
|
stderrBuffer: '',
|
|
83
159
|
logs: new LogRingBuffer(),
|
|
84
160
|
};
|
|
85
161
|
this.deployments.set(targetId, entry);
|
|
162
|
+
this.dbUpsert(entry);
|
|
86
163
|
// Attach stdout/stderr listeners for port detection
|
|
87
164
|
this.attachOutputListener(entry, 'stdout');
|
|
88
165
|
this.attachOutputListener(entry, 'stderr');
|
|
@@ -91,6 +168,7 @@ export class DeploymentService {
|
|
|
91
168
|
log.error(`Child process error for "${targetId}" (pid=${entry.pid}): ${err.message}`);
|
|
92
169
|
entry.state = DeploymentState.Stopped;
|
|
93
170
|
this.deployments.delete(targetId);
|
|
171
|
+
this.dbDelete(targetId);
|
|
94
172
|
});
|
|
95
173
|
// Clean up on process exit
|
|
96
174
|
child.on('exit', (code, signal) => {
|
|
@@ -100,20 +178,52 @@ export class DeploymentService {
|
|
|
100
178
|
log.warn('Process exited while still in Booting state — dev server likely crashed on startup. Check stderr output above.');
|
|
101
179
|
}
|
|
102
180
|
this.deployments.delete(targetId);
|
|
181
|
+
this.dbDelete(targetId);
|
|
103
182
|
});
|
|
104
183
|
}
|
|
105
184
|
/**
|
|
106
185
|
* Get the current deployment status for a target.
|
|
107
|
-
*
|
|
186
|
+
* Checks in-memory Map first, then falls back to DB for recovered deployments.
|
|
187
|
+
* Validates PID liveness — cleans up if dead.
|
|
108
188
|
*/
|
|
109
189
|
getStatus(targetId) {
|
|
110
190
|
const entry = this.deployments.get(targetId);
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
191
|
+
if (entry) {
|
|
192
|
+
// Validate the process is still alive (handles orphan crashes)
|
|
193
|
+
if (!this.deps.isAlive(entry.pid)) {
|
|
194
|
+
log.info(`getStatus("${targetId}") — pid=${entry.pid} is dead, cleaning up`);
|
|
195
|
+
this.deployments.delete(targetId);
|
|
196
|
+
this.dbDelete(targetId);
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
log.debug(`getStatus("${targetId}") — state=${entry.state}, url=${entry.url}, pid=${entry.pid}`);
|
|
200
|
+
return { state: entry.state, url: entry.url };
|
|
114
201
|
}
|
|
115
|
-
|
|
116
|
-
|
|
202
|
+
// Check DB for entries not yet in memory (e.g., after hook re-mount)
|
|
203
|
+
const row = this.dbFind(targetId);
|
|
204
|
+
if (row) {
|
|
205
|
+
if (this.deps.isAlive(row.pid)) {
|
|
206
|
+
// Re-adopt into memory
|
|
207
|
+
const recovered = {
|
|
208
|
+
pid: row.pid,
|
|
209
|
+
child: null,
|
|
210
|
+
state: row.state,
|
|
211
|
+
url: row.url,
|
|
212
|
+
targetId: row.target_id,
|
|
213
|
+
targetPath: row.target_path,
|
|
214
|
+
targetType: row.target_type,
|
|
215
|
+
stdoutBuffer: '',
|
|
216
|
+
stderrBuffer: '',
|
|
217
|
+
logs: new LogRingBuffer(),
|
|
218
|
+
};
|
|
219
|
+
this.deployments.set(targetId, recovered);
|
|
220
|
+
return { state: recovered.state, url: recovered.url };
|
|
221
|
+
}
|
|
222
|
+
// Dead — clean up
|
|
223
|
+
this.dbDelete(targetId);
|
|
224
|
+
}
|
|
225
|
+
log.debug(`getStatus("${targetId}") — no deployment found`);
|
|
226
|
+
return null;
|
|
117
227
|
}
|
|
118
228
|
/**
|
|
119
229
|
* Stop a deployment gracefully: SIGTERM → poll → SIGKILL.
|
|
@@ -121,7 +231,19 @@ export class DeploymentService {
|
|
|
121
231
|
async stop(targetId) {
|
|
122
232
|
const entry = this.deployments.get(targetId);
|
|
123
233
|
if (!entry) {
|
|
124
|
-
|
|
234
|
+
// Check DB in case it's a recovered orphan
|
|
235
|
+
const row = this.dbFind(targetId);
|
|
236
|
+
if (row && this.deps.isAlive(row.pid)) {
|
|
237
|
+
log.info(`stop("${targetId}") — killing orphan process (pid=${row.pid})`);
|
|
238
|
+
try {
|
|
239
|
+
this.deps.kill(row.pid, 'SIGKILL');
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// already dead
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
this.dbDelete(targetId);
|
|
246
|
+
log.info(`stop("${targetId}") — no in-memory deployment found, cleaned DB`);
|
|
125
247
|
return;
|
|
126
248
|
}
|
|
127
249
|
log.info(`stop("${targetId}") — sending SIGTERM to process tree (pid=${entry.pid})`);
|
|
@@ -133,13 +255,13 @@ export class DeploymentService {
|
|
|
133
255
|
catch {
|
|
134
256
|
log.info(`stop("${targetId}") — process already dead on SIGTERM`);
|
|
135
257
|
this.deployments.delete(targetId);
|
|
258
|
+
this.dbDelete(targetId);
|
|
136
259
|
return;
|
|
137
260
|
}
|
|
138
261
|
// Wait for the process to exit
|
|
139
262
|
const died = await this.pollUntilDead(entry.pid, MAX_WAIT_MS, POLL_INTERVAL_MS);
|
|
140
263
|
if (!died) {
|
|
141
264
|
log.warn(`stop("${targetId}") — process did not exit after ${MAX_WAIT_MS}ms, escalating to SIGKILL`);
|
|
142
|
-
// Escalate to SIGKILL
|
|
143
265
|
try {
|
|
144
266
|
this.deps.kill(entry.pid, 'SIGKILL');
|
|
145
267
|
}
|
|
@@ -150,8 +272,13 @@ export class DeploymentService {
|
|
|
150
272
|
else {
|
|
151
273
|
log.info(`stop("${targetId}") — process exited gracefully`);
|
|
152
274
|
}
|
|
153
|
-
// Wait for the exit event to clean up the map
|
|
154
|
-
|
|
275
|
+
// Wait for the exit event to clean up the map (only if we have a ChildProcess handle)
|
|
276
|
+
if (entry.child) {
|
|
277
|
+
await this.waitForExit(entry.child);
|
|
278
|
+
}
|
|
279
|
+
// Ensure cleanup
|
|
280
|
+
this.deployments.delete(targetId);
|
|
281
|
+
this.dbDelete(targetId);
|
|
155
282
|
}
|
|
156
283
|
/**
|
|
157
284
|
* Force-stop all tracked deployments immediately (for daemon shutdown).
|
|
@@ -161,6 +288,15 @@ export class DeploymentService {
|
|
|
161
288
|
entry.logs.clear();
|
|
162
289
|
this.killProcess(entry);
|
|
163
290
|
}
|
|
291
|
+
// Also clean DB
|
|
292
|
+
if (this.db) {
|
|
293
|
+
try {
|
|
294
|
+
this.db.prepare('DELETE FROM dev_servers').run();
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
// table might not exist yet
|
|
298
|
+
}
|
|
299
|
+
}
|
|
164
300
|
}
|
|
165
301
|
/**
|
|
166
302
|
* Get the accumulated log buffer for a deployment.
|
|
@@ -183,9 +319,54 @@ export class DeploymentService {
|
|
|
183
319
|
off(event, handler) {
|
|
184
320
|
this.emitter.off(event, handler);
|
|
185
321
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
322
|
+
// ─── Database helpers ───────────────────────────────────────────────
|
|
323
|
+
dbUpsert(entry) {
|
|
324
|
+
if (!this.db)
|
|
325
|
+
return;
|
|
326
|
+
try {
|
|
327
|
+
this.db
|
|
328
|
+
.prepare(`INSERT OR REPLACE INTO dev_servers
|
|
329
|
+
(target_id, target_type, pid, state, url, target_path, started_at)
|
|
330
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
|
331
|
+
.run(entry.targetId, entry.targetType, entry.pid, entry.state, entry.url, entry.targetPath, Date.now());
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
log.warn(`dbUpsert failed for "${entry.targetId}": ${err}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
dbUpdateState(targetId, state, url) {
|
|
338
|
+
if (!this.db)
|
|
339
|
+
return;
|
|
340
|
+
try {
|
|
341
|
+
this.db
|
|
342
|
+
.prepare('UPDATE dev_servers SET state = ?, url = ? WHERE target_id = ?')
|
|
343
|
+
.run(state, url, targetId);
|
|
344
|
+
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
log.warn(`dbUpdateState failed for "${targetId}": ${err}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
dbDelete(targetId) {
|
|
350
|
+
if (!this.db)
|
|
351
|
+
return;
|
|
352
|
+
try {
|
|
353
|
+
this.db.prepare('DELETE FROM dev_servers WHERE target_id = ?').run(targetId);
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
log.warn(`dbDelete failed for "${targetId}": ${err}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
dbFind(targetId) {
|
|
360
|
+
if (!this.db)
|
|
361
|
+
return null;
|
|
362
|
+
try {
|
|
363
|
+
return (this.db.prepare('SELECT * FROM dev_servers WHERE target_id = ?').get(targetId) ?? null);
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// ─── Process helpers ────────────────────────────────────────────────
|
|
189
370
|
killProcess(entry) {
|
|
190
371
|
try {
|
|
191
372
|
this.deps.kill(entry.pid, 'SIGKILL');
|
|
@@ -200,24 +381,20 @@ export class DeploymentService {
|
|
|
200
381
|
*/
|
|
201
382
|
attachOutputListener(entry, stream) {
|
|
202
383
|
const bufferKey = stream === 'stdout' ? 'stdoutBuffer' : 'stderrBuffer';
|
|
203
|
-
const childStream = entry.child[stream];
|
|
384
|
+
const childStream = entry.child?.[stream];
|
|
204
385
|
if (!childStream) {
|
|
205
386
|
log.warn(`[${entry.targetId}] No ${stream} stream available — cannot attach listener`);
|
|
206
387
|
return;
|
|
207
388
|
}
|
|
208
389
|
childStream.on('data', (chunk) => {
|
|
209
390
|
const text = chunk.toString();
|
|
210
|
-
// Append chunk to buffer
|
|
211
391
|
entry[bufferKey] += text;
|
|
212
|
-
// Process complete lines
|
|
213
392
|
const lines = entry[bufferKey].split('\n');
|
|
214
|
-
// Keep the last element (incomplete line) in the buffer
|
|
215
393
|
entry[bufferKey] = lines.pop() ?? '';
|
|
216
394
|
for (const line of lines) {
|
|
217
395
|
if (!line.trim())
|
|
218
396
|
continue;
|
|
219
397
|
log.debug(`[${entry.targetId}] ${stream}: ${line}`);
|
|
220
|
-
// Accumulate in log buffer and emit event
|
|
221
398
|
const logEntry = {
|
|
222
399
|
targetId: entry.targetId,
|
|
223
400
|
stream,
|
|
@@ -233,12 +410,12 @@ export class DeploymentService {
|
|
|
233
410
|
log.info(`[${entry.targetId}] Port detected — url="${url}" (from ${stream})`);
|
|
234
411
|
entry.state = DeploymentState.Ready;
|
|
235
412
|
entry.url = url;
|
|
413
|
+
this.dbUpdateState(entry.targetId, entry.state, entry.url);
|
|
236
414
|
}
|
|
237
415
|
}
|
|
238
416
|
}
|
|
239
417
|
});
|
|
240
418
|
childStream.on('end', () => {
|
|
241
|
-
// Flush remaining buffer content
|
|
242
419
|
const remaining = entry[bufferKey].trim();
|
|
243
420
|
if (remaining) {
|
|
244
421
|
log.debug(`[${entry.targetId}] ${stream} (flush): ${remaining}`);
|
|
@@ -248,6 +425,7 @@ export class DeploymentService {
|
|
|
248
425
|
log.info(`[${entry.targetId}] Port detected in flushed buffer — url="${url}"`);
|
|
249
426
|
entry.state = DeploymentState.Ready;
|
|
250
427
|
entry.url = url;
|
|
428
|
+
this.dbUpdateState(entry.targetId, entry.state, entry.url);
|
|
251
429
|
}
|
|
252
430
|
}
|
|
253
431
|
entry[bufferKey] = '';
|
|
@@ -255,9 +433,6 @@ export class DeploymentService {
|
|
|
255
433
|
log.debug(`[${entry.targetId}] ${stream} stream ended`);
|
|
256
434
|
});
|
|
257
435
|
}
|
|
258
|
-
/**
|
|
259
|
-
* Poll until a process is dead or timeout expires.
|
|
260
|
-
*/
|
|
261
436
|
async pollUntilDead(pid, maxMs, intervalMs) {
|
|
262
437
|
const deadline = Date.now() + maxMs;
|
|
263
438
|
while (Date.now() < deadline) {
|
|
@@ -268,9 +443,6 @@ export class DeploymentService {
|
|
|
268
443
|
}
|
|
269
444
|
return false;
|
|
270
445
|
}
|
|
271
|
-
/**
|
|
272
|
-
* Wait for a child process to emit 'exit', with a short timeout.
|
|
273
|
-
*/
|
|
274
446
|
waitForExit(child) {
|
|
275
447
|
return new Promise((resolve) => {
|
|
276
448
|
const timeout = setTimeout(() => resolve(), 1000);
|
|
@@ -5,11 +5,5 @@
|
|
|
5
5
|
* dev server stdout/stderr output. Handles common patterns from Next.js,
|
|
6
6
|
* Vite, Express, and generic servers.
|
|
7
7
|
*/
|
|
8
|
-
/**
|
|
9
|
-
* Parse a line of stdout/stderr output for a localhost URL or port.
|
|
10
|
-
*
|
|
11
|
-
* @param line - A single line of process output
|
|
12
|
-
* @returns The detected URL string, or null if no URL/port was found
|
|
13
|
-
*/
|
|
14
8
|
export declare function parsePort(line: string): string | null;
|
|
15
9
|
//# sourceMappingURL=parse-port.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-port.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/deployment/parse-port.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"parse-port.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/deployment/parse-port.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA4CH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBrD"}
|