@shepai/cli 1.130.0-pr413.0773bb9 → 1.130.0-pr413.cb198fb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.d.ts +21 -1
  2. package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.d.ts.map +1 -1
  3. package/dist/packages/core/src/infrastructure/services/deployment/deployment.service.js +110 -4
  4. package/dist/src/presentation/web/dev-server.js +7 -0
  5. package/dist/tsconfig.build.tsbuildinfo +1 -1
  6. package/package.json +1 -1
  7. package/web/.next/BUILD_ID +1 -1
  8. package/web/.next/build-manifest.json +2 -2
  9. package/web/.next/fallback-build-manifest.json +2 -2
  10. package/web/.next/prerender-manifest.json +3 -3
  11. package/web/.next/required-server-files.js +1 -1
  12. package/web/.next/required-server-files.json +1 -1
  13. package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +24 -24
  14. package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
  15. package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
  16. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +24 -24
  17. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  18. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  19. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +30 -30
  20. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  21. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  22. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +30 -30
  23. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  24. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  25. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +22 -22
  26. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  27. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  28. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +24 -24
  29. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  30. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  31. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +30 -30
  32. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  33. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  34. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +30 -30
  35. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  36. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  37. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +22 -22
  38. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  39. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  40. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +22 -22
  41. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  42. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/_global-error.html +2 -2
  44. package/web/.next/server/app/_global-error.rsc +1 -1
  45. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  46. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  47. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  48. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  49. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  50. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +3 -3
  51. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  52. package/web/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  53. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  54. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  55. package/web/.next/server/app/skills/page/server-reference-manifest.json +8 -8
  56. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  57. package/web/.next/server/app/tools/page/server-reference-manifest.json +8 -8
  58. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  59. package/web/.next/server/app/version/page/server-reference-manifest.json +3 -3
  60. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  61. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  62. package/web/.next/server/chunks/ssr/744ca_web__next-internal_server_app_(dashboard)_@drawer_adopt_page_actions_4e741df2.js +1 -1
  63. package/web/.next/server/chunks/ssr/744ca_web__next-internal_server_app_(dashboard)_@drawer_adopt_page_actions_4e741df2.js.map +1 -1
  64. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  65. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  66. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_026fdffa.js +1 -1
  67. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_026fdffa.js.map +1 -1
  68. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_8b91e197.js +1 -1
  69. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_8b91e197.js.map +1 -1
  70. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  71. package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js +1 -1
  72. package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js.map +1 -1
  73. package/web/.next/server/chunks/ssr/[root-of-the-server]__5142820f._.js +2 -2
  74. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +2 -2
  75. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
  76. package/web/.next/server/chunks/ssr/[root-of-the-server]__755cfe02._.js +1 -1
  77. package/web/.next/server/chunks/ssr/[root-of-the-server]__755cfe02._.js.map +1 -1
  78. package/web/.next/server/chunks/ssr/[root-of-the-server]__7894f27f._.js +1 -1
  79. package/web/.next/server/chunks/ssr/[root-of-the-server]__7894f27f._.js.map +1 -1
  80. package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js +1 -1
  81. package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js.map +1 -1
  82. package/web/.next/server/chunks/ssr/[root-of-the-server]__84f72684._.js +1 -1
  83. package/web/.next/server/chunks/ssr/[root-of-the-server]__99ab2519._.js +1 -1
  84. package/web/.next/server/chunks/ssr/[root-of-the-server]__a628fb91._.js +1 -1
  85. package/web/.next/server/chunks/ssr/[root-of-the-server]__a628fb91._.js.map +1 -1
  86. package/web/.next/server/chunks/ssr/[root-of-the-server]__bfba58e6._.js +1 -1
  87. package/web/.next/server/chunks/ssr/[root-of-the-server]__bfba58e6._.js.map +1 -1
  88. package/web/.next/server/chunks/ssr/[root-of-the-server]__c3ab8422._.js +1 -1
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__c3ab8422._.js.map +1 -1
  90. package/web/.next/server/chunks/ssr/_02ec1aea._.js +1 -1
  91. package/web/.next/server/chunks/ssr/_02ec1aea._.js.map +1 -1
  92. package/web/.next/server/chunks/ssr/{_18e5397a._.js → _611072ae._.js} +2 -2
  93. package/web/.next/server/chunks/ssr/{_18e5397a._.js.map → _611072ae._.js.map} +1 -1
  94. package/web/.next/server/chunks/ssr/{_523f2f6d._.js → _6366c042._.js} +2 -2
  95. package/web/.next/server/chunks/ssr/{_523f2f6d._.js.map → _6366c042._.js.map} +1 -1
  96. package/web/.next/server/chunks/ssr/_7dca1882._.js +1 -1
  97. package/web/.next/server/chunks/ssr/_7dca1882._.js.map +1 -1
  98. package/web/.next/server/chunks/ssr/_a9f57758._.js +1 -1
  99. package/web/.next/server/chunks/ssr/_d8575088._.js +1 -1
  100. package/web/.next/server/chunks/ssr/_d8575088._.js.map +1 -1
  101. package/web/.next/server/chunks/ssr/_f39a1adb._.js +1 -1
  102. package/web/.next/server/chunks/ssr/_f39a1adb._.js.map +1 -1
  103. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  104. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  105. package/web/.next/server/chunks/ssr/{src_presentation_web_8eab1fad._.js → src_presentation_web_67c8803c._.js} +2 -2
  106. package/web/.next/server/chunks/ssr/{src_presentation_web_8eab1fad._.js.map → src_presentation_web_67c8803c._.js.map} +1 -1
  107. package/web/.next/server/chunks/ssr/src_presentation_web_8e7fe0d1._.js +2 -2
  108. package/web/.next/server/chunks/ssr/src_presentation_web_8e7fe0d1._.js.map +1 -1
  109. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_ca051134.js +1 -1
  110. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_ca051134.js.map +1 -1
  111. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js +1 -1
  112. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js.map +1 -1
  113. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js +1 -1
  114. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js.map +1 -1
  115. package/web/.next/server/chunks/ssr/src_presentation_web_b9023017._.js +2 -2
  116. package/web/.next/server/chunks/ssr/src_presentation_web_b9023017._.js.map +1 -1
  117. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  118. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  119. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  120. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  121. package/web/.next/server/pages/500.html +2 -2
  122. package/web/.next/server/server-reference-manifest.js +1 -1
  123. package/web/.next/server/server-reference-manifest.json +38 -38
  124. package/web/.next/static/chunks/{afad5b7ffdb30745.js → 0b7fe2ac4f078f54.js} +1 -1
  125. package/web/.next/static/chunks/{dca5b0caef486192.js → 26c8a1b2767a0f44.js} +1 -1
  126. package/web/.next/static/chunks/{2670b73949e10d8c.js → 57b92ebf9c593039.js} +2 -2
  127. package/web/.next/static/chunks/{5b06c2b203512867.js → 594b768ab52a93c3.js} +2 -2
  128. package/web/.next/static/chunks/{d519eb952b36ec01.js → 5fa24b8a585d15f2.js} +1 -1
  129. package/web/.next/static/chunks/{317bd484909d1d9e.js → 680a6e616c6e0ebe.js} +1 -1
  130. package/web/.next/static/chunks/6e9d87e44b2bbd0a.js +1 -0
  131. package/web/.next/static/chunks/{3f3e1e2df1cfe629.js → a167a7612070f8e3.js} +1 -1
  132. package/web/.next/static/chunks/{bbe33a3a4993e0c4.js → b0c6315acb2c261c.js} +1 -1
  133. package/web/.next/static/chunks/{d6904209f1c50c18.js → be27326a00f57951.js} +1 -1
  134. package/web/.next/static/chunks/{08ef7dc6818ebd7a.js → d59f35d64a1e6635.js} +1 -1
  135. package/web/.next/static/chunks/{a70fd762b3ed4788.js → e534694ccd1fd0f7.js} +1 -1
  136. package/web/.next/static/chunks/{01653d5435a458ee.js → e9ba45c10084a7f5.js} +1 -1
  137. package/web/.next/static/chunks/28ef00467f5956bc.js +0 -1
  138. /package/web/.next/static/{wS9YDApckYoE-8HrhN7NN → qkuc0YEQFahFRBKBhIgGw}/_buildManifest.js +0 -0
  139. /package/web/.next/static/{wS9YDApckYoE-8HrhN7NN → qkuc0YEQFahFRBKBhIgGw}/_clientMiddlewareManifest.json +0 -0
  140. /package/web/.next/static/{wS9YDApckYoE-8HrhN7NN → qkuc0YEQFahFRBKBhIgGw}/_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
- * Validates each PID is still alive; removes dead rows.
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
@@ -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,EAAmC,MAAM,oBAAoB,CAAC;AAE5E,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;;;OAGG;IACH,UAAU,IAAI,IAAI;IA4ClB;;;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;CASpB"}
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"}
@@ -10,6 +10,9 @@
10
10
  * process state (clean up dead PIDs, re-adopt live ones).
11
11
  */
12
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
17
  // On Unix we use process.kill(-pid) to send signals to the process GROUP
15
18
  // (detached: true puts the child in its own group via setsid()).
@@ -82,7 +85,16 @@ export class DeploymentService {
82
85
  }
83
86
  /**
84
87
  * Recover dev servers from the database on startup.
85
- * Validates each PID is still alive; removes dead rows.
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.
86
98
  */
87
99
  recoverAll() {
88
100
  if (!this.db)
@@ -102,7 +114,10 @@ export class DeploymentService {
102
114
  }
103
115
  log.info(`Recovering ${rows.length} dev server(s) from database`);
104
116
  for (const row of rows) {
105
- if (this.deps.isAlive(row.pid)) {
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
106
121
  log.info(`Recovered "${row.target_id}" (pid=${row.pid}, state=${row.state}, url=${row.url})`);
107
122
  const entry = {
108
123
  pid: row.pid,
@@ -117,10 +132,36 @@ export class DeploymentService {
117
132
  logs: new LogRingBuffer(),
118
133
  };
119
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
+ }
120
149
  }
121
150
  else {
122
- log.info(`Stale dev server "${row.target_id}" (pid=${row.pid}) — removing from DB`);
123
- this.dbDelete(row.target_id);
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}`);
124
165
  }
125
166
  }
126
167
  }
@@ -486,4 +527,69 @@ export class DeploymentService {
486
527
  });
487
528
  });
488
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
+ }
489
595
  }
@@ -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
  }