@shepai/cli 1.130.0 → 1.130.1-pr419.86475da
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/apis/json-schema/WorkflowConfig.yaml +5 -0
- package/dist/packages/core/src/domain/generated/output.d.ts +4 -0
- package/dist/packages/core/src/domain/generated/output.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/common/agent-validator.service.js +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.d.ts +5 -5
- package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.js +8 -8
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/fast-feature-agent-graph.d.ts +5 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/fast-feature-agent-graph.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts +17 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/evidence-output-parser.d.ts +52 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/evidence-output-parser.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/evidence-output-parser.js +121 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/evidence.node.d.ts +6 -2
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/evidence.node.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/evidence.node.js +139 -75
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/implement.node.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/implement.node.js +17 -63
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/evidence-prompts.d.ts +12 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/evidence-prompts.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/evidence-prompts.js +50 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/state.d.ts +1 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/state.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/state.js +4 -0
- package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.d.ts +21 -1
- 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 +152 -12
- package/dist/packages/core/src/infrastructure/services/deployment/detect-dev-script.d.ts +1 -0
- 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 +3 -2
- package/dist/src/presentation/web/app/actions/check-agent-auth.js +1 -1
- package/dist/src/presentation/web/dev-server.js +7 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +3 -3
- package/web/.next/required-server-files.json +3 -3
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +24 -24
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +24 -24
- 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 +30 -30
- 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 +30 -30
- 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 +22 -22
- 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 +24 -24
- 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 +30 -30
- 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 +30 -30
- 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 +22 -22
- 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 +22 -22
- 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 +3 -3
- 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 +8 -8
- 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 +8 -8
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +8 -8
- package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/server-reference-manifest.json +3 -3
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
- package/web/.next/server/chunks/[root-of-the-server]__cd67a84c._.js.map +1 -1
- package/web/.next/server/chunks/[root-of-the-server]__d1dc8fcd._.js.map +1 -1
- package/web/.next/server/chunks/ssr/744ca_web__next-internal_server_app_(dashboard)_@drawer_adopt_page_actions_4e741df2.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web__next-internal_server_app_(dashboard)_@drawer_adopt_page_actions_4e741df2.js.map +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_026fdffa.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_026fdffa.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_8b91e197.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_8b91e197.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__5142820f._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__5142820f._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__57018345._.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]__755cfe02._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__755cfe02._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__7894f27f._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__7894f27f._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__84f72684._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__99ab2519._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__99ab2519._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a628fb91._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a628fb91._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__bfba58e6._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__bfba58e6._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c3ab8422._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c3ab8422._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_02ec1aea._.js +1 -1
- package/web/.next/server/chunks/ssr/_02ec1aea._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_c6edd1ef._.js → _59831c3c._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_c6edd1ef._.js.map → _59831c3c._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_7dca1882._.js +1 -1
- package/web/.next/server/chunks/ssr/_7dca1882._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_a9f57758._.js +1 -1
- package/web/.next/server/chunks/ssr/{_2081d51d._.js → _cf09a53c._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_2081d51d._.js.map → _cf09a53c._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_d4b20e29._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_d8575088._.js +1 -1
- package/web/.next/server/chunks/ssr/_d8575088._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_f39a1adb._.js +1 -1
- package/web/.next/server/chunks/ssr/_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_8e7fe0d1._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_8e7fe0d1._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_ca051134.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_ca051134.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_b9023017._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_b9023017._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +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_de41598a._.js → src_presentation_web_e164bc5b._.js} +2 -2
- package/web/.next/server/chunks/ssr/{src_presentation_web_de41598a._.js.map → src_presentation_web_e164bc5b._.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 +38 -38
- package/web/.next/static/chunks/{3b5d54efc5f21506.js → 031c5ad2b3cc0fcf.js} +1 -1
- package/web/.next/static/chunks/{f8ce00057329618b.js → 1be2438b5f127e76.js} +1 -1
- package/web/.next/static/chunks/{22d08ce7fbc67732.js → 255bf7de35259808.js} +1 -1
- package/web/.next/static/chunks/{090b4b644f4526bf.js → 512315907ba20e64.js} +1 -1
- package/web/.next/static/chunks/{339e72cfa282c94b.js → 67dce937a03f61e0.js} +1 -1
- package/web/.next/static/chunks/{724dfca1907b26d7.js → 8e5d3752c2c82dbb.js} +1 -1
- package/web/.next/static/chunks/8ebc4fcc77449017.js +1 -0
- package/web/.next/static/chunks/{bdf477379453ead2.js → a1d525f20fc5274a.js} +2 -2
- package/web/.next/static/chunks/{8d936038e7d013d2.js → ab2c3c9ca5dd40a8.js} +1 -1
- package/web/.next/static/chunks/{9387d6dde6140968.js → cf791b69763664cf.js} +2 -2
- package/web/.next/static/chunks/{747b3443a866e39f.js → ec9886a3509ecef2.js} +1 -1
- package/web/.next/static/chunks/{90a32a641dd33f9a.js → ffb939826e7c743f.js} +1 -1
- package/web/.next/static/chunks/bcdcdce9139bfd25.js +0 -1
- /package/web/.next/static/{FkranKu-K5LY21_CdI_Jp → 0np7GuitLHRGDWR1YFoLi}/_buildManifest.js +0 -0
- /package/web/.next/static/{FkranKu-K5LY21_CdI_Jp → 0np7GuitLHRGDWR1YFoLi}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{FkranKu-K5LY21_CdI_Jp → 0np7GuitLHRGDWR1YFoLi}/_ssgManifest.js +0 -0
|
@@ -31,7 +31,16 @@ export declare class DeploymentService implements IDeploymentService {
|
|
|
31
31
|
setDatabase(db: Database.Database): void;
|
|
32
32
|
/**
|
|
33
33
|
* Recover dev servers from the database on startup.
|
|
34
|
-
*
|
|
34
|
+
*
|
|
35
|
+
* Three recovery strategies based on process state:
|
|
36
|
+
* 1. Alive + Ready (has URL) → re-adopt, then validate URL asynchronously
|
|
37
|
+
* 2. Alive + Booting (no URL) → kill orphan and re-spawn (can't re-attach stdout)
|
|
38
|
+
* 3. Dead → re-spawn if target directory still exists
|
|
39
|
+
*
|
|
40
|
+
* After sync recovery, fires off async URL health checks for re-adopted
|
|
41
|
+
* processes. Zombies (alive but not serving) are killed and re-spawned.
|
|
42
|
+
*
|
|
43
|
+
* All re-spawns are wrapped in try/catch so one failure doesn't block others.
|
|
35
44
|
*/
|
|
36
45
|
recoverAll(): void;
|
|
37
46
|
/**
|
|
@@ -77,5 +86,16 @@ export declare class DeploymentService implements IDeploymentService {
|
|
|
77
86
|
private attachOutputListener;
|
|
78
87
|
private pollUntilDead;
|
|
79
88
|
private waitForExit;
|
|
89
|
+
/**
|
|
90
|
+
* Async health check for a recovered entry. Probes the URL port via TCP.
|
|
91
|
+
* If the port is not responding (zombie process), kills and re-spawns.
|
|
92
|
+
* Fire-and-forget — errors are logged, never thrown.
|
|
93
|
+
*/
|
|
94
|
+
private validateAndRespawn;
|
|
95
|
+
/**
|
|
96
|
+
* TCP connect probe to check if a URL's port is accepting connections.
|
|
97
|
+
* Returns true if the port responds within 2 seconds, false otherwise.
|
|
98
|
+
*/
|
|
99
|
+
private probePort;
|
|
80
100
|
}
|
|
81
101
|
//# 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;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,
|
|
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,EAAmC,MAAM,oBAAoB,CAAC;AAK5E,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAK3C,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACT,MAAM,qEAAqE,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAiCzD,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;AA0CD,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;;;;;;;;;;;;OAYG;IACH,UAAU,IAAI,IAAI;IAgFlB;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,SAAe,GAAG,IAAI;IA0G5E;;;;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;IAUnB;;;;OAIG;YACW,kBAAkB;IAsChC;;;OAGG;IACH,OAAO,CAAC,SAAS;CAwBlB"}
|
|
@@ -9,18 +9,20 @@
|
|
|
9
9
|
* On startup, call `recoverAll()` to reconcile the DB with actual
|
|
10
10
|
* process state (clean up dead PIDs, re-adopt live ones).
|
|
11
11
|
*/
|
|
12
|
-
import { spawn } from 'node:child_process';
|
|
12
|
+
import { spawn, execFileSync } from 'node:child_process';
|
|
13
|
+
import { existsSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import net from 'node:net';
|
|
13
16
|
import { EventEmitter } from 'node:events';
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// signals to the process GROUP (since we spawn with detached: true, the
|
|
18
|
-
// child gets its own process group via setsid()).
|
|
17
|
+
// On Unix we use process.kill(-pid) to send signals to the process GROUP
|
|
18
|
+
// (detached: true puts the child in its own group via setsid()).
|
|
19
|
+
// On Windows, negative PIDs are not supported — use taskkill /T for tree kill.
|
|
19
20
|
import { DeploymentState } from '../../../domain/generated/output.js';
|
|
20
21
|
import { detectDevScript } from './detect-dev-script.js';
|
|
21
22
|
import { createDeploymentLogger } from './deployment-logger.js';
|
|
22
23
|
import { parsePort } from './parse-port.js';
|
|
23
24
|
import { LogRingBuffer } from './log-ring-buffer.js';
|
|
25
|
+
import { IS_WINDOWS } from '../../platform.js';
|
|
24
26
|
const log = createDeploymentLogger('[DeploymentService]');
|
|
25
27
|
const POLL_INTERVAL_MS = 200;
|
|
26
28
|
const MAX_WAIT_MS = 5000;
|
|
@@ -28,6 +30,20 @@ const defaultDeps = {
|
|
|
28
30
|
spawn,
|
|
29
31
|
detectDevScript,
|
|
30
32
|
kill: (pid, signal) => {
|
|
33
|
+
if (IS_WINDOWS) {
|
|
34
|
+
// On Windows, negative PIDs are not supported. Use taskkill /T to
|
|
35
|
+
// kill the entire process tree (shell + child dev server).
|
|
36
|
+
try {
|
|
37
|
+
execFileSync('taskkill', ['/F', '/T', '/PID', String(pid)], {
|
|
38
|
+
stdio: 'ignore',
|
|
39
|
+
windowsHide: true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Process already dead
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
31
47
|
try {
|
|
32
48
|
// Kill the entire process group (negative PID) — safe because
|
|
33
49
|
// detached: true puts the child in its own group via setsid().
|
|
@@ -69,7 +85,16 @@ export class DeploymentService {
|
|
|
69
85
|
}
|
|
70
86
|
/**
|
|
71
87
|
* Recover dev servers from the database on startup.
|
|
72
|
-
*
|
|
88
|
+
*
|
|
89
|
+
* Three recovery strategies based on process state:
|
|
90
|
+
* 1. Alive + Ready (has URL) → re-adopt, then validate URL asynchronously
|
|
91
|
+
* 2. Alive + Booting (no URL) → kill orphan and re-spawn (can't re-attach stdout)
|
|
92
|
+
* 3. Dead → re-spawn if target directory still exists
|
|
93
|
+
*
|
|
94
|
+
* After sync recovery, fires off async URL health checks for re-adopted
|
|
95
|
+
* processes. Zombies (alive but not serving) are killed and re-spawned.
|
|
96
|
+
*
|
|
97
|
+
* All re-spawns are wrapped in try/catch so one failure doesn't block others.
|
|
73
98
|
*/
|
|
74
99
|
recoverAll() {
|
|
75
100
|
if (!this.db)
|
|
@@ -89,7 +114,10 @@ export class DeploymentService {
|
|
|
89
114
|
}
|
|
90
115
|
log.info(`Recovering ${rows.length} dev server(s) from database`);
|
|
91
116
|
for (const row of rows) {
|
|
92
|
-
|
|
117
|
+
const alive = this.deps.isAlive(row.pid);
|
|
118
|
+
const hasUrl = row.state === DeploymentState.Ready && row.url;
|
|
119
|
+
if (alive && hasUrl) {
|
|
120
|
+
// Strategy 1: Process alive and Ready — re-adopt, then validate URL async
|
|
93
121
|
log.info(`Recovered "${row.target_id}" (pid=${row.pid}, state=${row.state}, url=${row.url})`);
|
|
94
122
|
const entry = {
|
|
95
123
|
pid: row.pid,
|
|
@@ -104,10 +132,36 @@ export class DeploymentService {
|
|
|
104
132
|
logs: new LogRingBuffer(),
|
|
105
133
|
};
|
|
106
134
|
this.deployments.set(row.target_id, entry);
|
|
135
|
+
// Fire-and-forget: validate URL is actually responding, kill zombie if not
|
|
136
|
+
this.validateAndRespawn(entry);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (alive) {
|
|
140
|
+
// Strategy 2: Process alive but stuck in Booting — can't re-attach stdout
|
|
141
|
+
// Kill the orphan so we can re-spawn with proper listeners
|
|
142
|
+
log.info(`Orphan "${row.target_id}" (pid=${row.pid}) stuck in ${row.state} — killing to re-spawn`);
|
|
143
|
+
try {
|
|
144
|
+
this.deps.kill(row.pid, 'SIGKILL');
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// already dead
|
|
148
|
+
}
|
|
107
149
|
}
|
|
108
150
|
else {
|
|
109
|
-
log.info(`
|
|
110
|
-
|
|
151
|
+
log.info(`Dev server "${row.target_id}" (pid=${row.pid}) is dead — will re-spawn`);
|
|
152
|
+
}
|
|
153
|
+
// Strategy 2 & 3: Re-spawn if target directory still has a package.json
|
|
154
|
+
this.dbDelete(row.target_id);
|
|
155
|
+
if (!existsSync(join(row.target_path, 'package.json'))) {
|
|
156
|
+
log.warn(`Skipping re-spawn for "${row.target_id}" — no package.json at "${row.target_path}"`);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
log.info(`Re-spawning dev server for "${row.target_id}" at "${row.target_path}"`);
|
|
161
|
+
this.start(row.target_id, row.target_path, row.target_type);
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
log.error(`Failed to re-spawn "${row.target_id}": ${err}`);
|
|
111
165
|
}
|
|
112
166
|
}
|
|
113
167
|
}
|
|
@@ -132,13 +186,34 @@ export class DeploymentService {
|
|
|
132
186
|
throw new Error(detection.error);
|
|
133
187
|
}
|
|
134
188
|
// Build spawn args based on package manager
|
|
135
|
-
const { packageManager, scriptName, command } = detection;
|
|
189
|
+
const { packageManager, scriptName, command, needsInstall } = detection;
|
|
136
190
|
const args = packageManager === 'npm' ? ['run', scriptName] : [scriptName];
|
|
191
|
+
// Install dependencies if node_modules is missing (e.g. fresh worktree)
|
|
192
|
+
if (needsInstall) {
|
|
193
|
+
log.info(`node_modules missing in "${targetPath}" — running ${packageManager} install`);
|
|
194
|
+
try {
|
|
195
|
+
execFileSync(packageManager, ['install'], {
|
|
196
|
+
cwd: targetPath,
|
|
197
|
+
shell: true,
|
|
198
|
+
stdio: 'ignore',
|
|
199
|
+
...(IS_WINDOWS ? { windowsHide: true } : {}),
|
|
200
|
+
});
|
|
201
|
+
log.info('Dependencies installed successfully');
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
log.error(`Dependency install failed: ${err}`);
|
|
205
|
+
throw new Error(`Failed to install dependencies: ${err}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
137
208
|
log.info(`Spawning dev server: command="${command}", packageManager="${packageManager}", scriptName="${scriptName}", cwd="${targetPath}"`);
|
|
138
209
|
const child = this.deps.spawn(packageManager, args, {
|
|
139
210
|
shell: true,
|
|
140
211
|
cwd: targetPath,
|
|
141
|
-
detached: true
|
|
212
|
+
// On Unix, detached: true creates a process group via setsid() so we can
|
|
213
|
+
// kill the entire tree with process.kill(-pid). On Windows this flag causes
|
|
214
|
+
// CREATE_NEW_CONSOLE which opens a visible terminal window and disconnects
|
|
215
|
+
// stdout/stderr — we don't need it because taskkill /F /T handles tree kill.
|
|
216
|
+
...(IS_WINDOWS ? { windowsHide: true } : { detached: true }),
|
|
142
217
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
143
218
|
});
|
|
144
219
|
if (!child.pid) {
|
|
@@ -452,4 +527,69 @@ export class DeploymentService {
|
|
|
452
527
|
});
|
|
453
528
|
});
|
|
454
529
|
}
|
|
530
|
+
/**
|
|
531
|
+
* Async health check for a recovered entry. Probes the URL port via TCP.
|
|
532
|
+
* If the port is not responding (zombie process), kills and re-spawns.
|
|
533
|
+
* Fire-and-forget — errors are logged, never thrown.
|
|
534
|
+
*/
|
|
535
|
+
async validateAndRespawn(entry) {
|
|
536
|
+
if (!entry.url)
|
|
537
|
+
return;
|
|
538
|
+
const responding = await this.probePort(entry.url);
|
|
539
|
+
if (responding) {
|
|
540
|
+
log.info(`[${entry.targetId}] URL health check passed — "${entry.url}" is responding`);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
log.warn(`[${entry.targetId}] URL health check FAILED — "${entry.url}" not responding, killing zombie (pid=${entry.pid})`);
|
|
544
|
+
// Kill the zombie
|
|
545
|
+
try {
|
|
546
|
+
this.deps.kill(entry.pid, 'SIGKILL');
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
// already dead
|
|
550
|
+
}
|
|
551
|
+
this.deployments.delete(entry.targetId);
|
|
552
|
+
this.dbDelete(entry.targetId);
|
|
553
|
+
// Re-spawn if target still exists
|
|
554
|
+
if (!existsSync(join(entry.targetPath, 'package.json'))) {
|
|
555
|
+
log.warn(`Skipping re-spawn for "${entry.targetId}" — no package.json at "${entry.targetPath}"`);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
try {
|
|
559
|
+
log.info(`Re-spawning dev server for "${entry.targetId}" at "${entry.targetPath}"`);
|
|
560
|
+
this.start(entry.targetId, entry.targetPath, entry.targetType);
|
|
561
|
+
}
|
|
562
|
+
catch (err) {
|
|
563
|
+
log.error(`Failed to re-spawn "${entry.targetId}": ${err}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* TCP connect probe to check if a URL's port is accepting connections.
|
|
568
|
+
* Returns true if the port responds within 2 seconds, false otherwise.
|
|
569
|
+
*/
|
|
570
|
+
probePort(url) {
|
|
571
|
+
return new Promise((resolve) => {
|
|
572
|
+
try {
|
|
573
|
+
const parsed = new URL(url);
|
|
574
|
+
const port = parseInt(parsed.port || (parsed.protocol === 'https:' ? '443' : '80'), 10);
|
|
575
|
+
const socket = net.createConnection({ host: parsed.hostname, port });
|
|
576
|
+
const timeout = setTimeout(() => {
|
|
577
|
+
socket.destroy();
|
|
578
|
+
resolve(false);
|
|
579
|
+
}, 2000);
|
|
580
|
+
socket.on('connect', () => {
|
|
581
|
+
clearTimeout(timeout);
|
|
582
|
+
socket.destroy();
|
|
583
|
+
resolve(true);
|
|
584
|
+
});
|
|
585
|
+
socket.on('error', () => {
|
|
586
|
+
clearTimeout(timeout);
|
|
587
|
+
resolve(false);
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
catch {
|
|
591
|
+
resolve(false);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
}
|
|
455
595
|
}
|
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;AAgBH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,IAAI,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;
|
|
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;IAChB,YAAY,EAAE,OAAO,CAAC;CACvB;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,CAwCtE"}
|
|
@@ -50,8 +50,9 @@ export function detectDevScript(dirPath) {
|
|
|
50
50
|
const packageManager = detectPackageManager(dirPath);
|
|
51
51
|
// Build the command — pnpm/yarn use `<pm> <script>`, npm uses `npm run <script>`
|
|
52
52
|
const command = packageManager === 'npm' ? `npm run ${scriptName}` : `${packageManager} ${scriptName}`;
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
const needsInstall = !existsSync(join(dirPath, 'node_modules'));
|
|
54
|
+
log.info(`detected — packageManager="${packageManager}", scriptName="${scriptName}", command="${command}", needsInstall=${needsInstall}`);
|
|
55
|
+
return { success: true, packageManager, scriptName, command, needsInstall };
|
|
55
56
|
}
|
|
56
57
|
/**
|
|
57
58
|
* Detect the package manager by checking for lockfile presence.
|
|
@@ -107,6 +107,13 @@ async function main() {
|
|
|
107
107
|
console.log('\n[dev-server] Shutting down...');
|
|
108
108
|
const forceExit = setTimeout(() => process.exit(0), 2000);
|
|
109
109
|
try {
|
|
110
|
+
try {
|
|
111
|
+
const deploymentService = container.resolve('IDeploymentService');
|
|
112
|
+
deploymentService.stopAll();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
/* not initialized */
|
|
116
|
+
}
|
|
110
117
|
try {
|
|
111
118
|
getNotificationWatcher().stop();
|
|
112
119
|
}
|