@mesh-tech/mesh-cli 0.1.0 → 0.12.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.
Files changed (504) hide show
  1. package/assets/intent/agents-fence.md +10 -0
  2. package/assets/intent/intent-claude-gate.mjs +255 -0
  3. package/assets/log-shipper.mjs +94 -0
  4. package/bin/mesh.mjs +80 -0
  5. package/dist/bin/mesh.d.ts +7 -9
  6. package/dist/bin/mesh.d.ts.map +1 -1
  7. package/dist/bin/mesh.js +20826 -37
  8. package/dist/bin/mesh.js.map +7 -1
  9. package/dist/build-info.json +4 -0
  10. package/dist/src/commands/agent-api-client.d.ts +79 -0
  11. package/dist/src/commands/agent-api-client.d.ts.map +1 -0
  12. package/dist/src/commands/agent-api-client.js +170 -0
  13. package/dist/src/commands/agent-api-client.js.map +1 -0
  14. package/dist/src/commands/artifacts.d.ts +13 -0
  15. package/dist/src/commands/artifacts.d.ts.map +1 -0
  16. package/dist/src/commands/artifacts.js +140 -0
  17. package/dist/src/commands/artifacts.js.map +1 -0
  18. package/dist/src/commands/cluster.d.ts +45 -0
  19. package/dist/src/commands/cluster.d.ts.map +1 -0
  20. package/dist/src/commands/cluster.js +259 -0
  21. package/dist/src/commands/cluster.js.map +1 -0
  22. package/dist/src/commands/conversations.d.ts +61 -0
  23. package/dist/src/commands/conversations.d.ts.map +1 -0
  24. package/dist/src/commands/conversations.js +239 -0
  25. package/dist/src/commands/conversations.js.map +1 -0
  26. package/dist/src/commands/create-app.d.ts +26 -3
  27. package/dist/src/commands/create-app.d.ts.map +1 -1
  28. package/dist/src/commands/create-app.js +465 -107
  29. package/dist/src/commands/create-app.js.map +1 -1
  30. package/dist/src/commands/deploy.d.ts +19 -0
  31. package/dist/src/commands/deploy.d.ts.map +1 -1
  32. package/dist/src/commands/deploy.js +83 -142
  33. package/dist/src/commands/deploy.js.map +1 -1
  34. package/dist/src/commands/dev-doctor.d.ts +137 -0
  35. package/dist/src/commands/dev-doctor.d.ts.map +1 -0
  36. package/dist/src/commands/dev-doctor.js +671 -0
  37. package/dist/src/commands/dev-doctor.js.map +1 -0
  38. package/dist/src/commands/dev-launch.d.ts +59 -0
  39. package/dist/src/commands/dev-launch.d.ts.map +1 -0
  40. package/dist/src/commands/dev-launch.js +118 -0
  41. package/dist/src/commands/dev-launch.js.map +1 -0
  42. package/dist/src/commands/dev-token-server.d.ts +32 -0
  43. package/dist/src/commands/dev-token-server.d.ts.map +1 -0
  44. package/dist/src/commands/dev-token-server.js +68 -0
  45. package/dist/src/commands/dev-token-server.js.map +1 -0
  46. package/dist/src/commands/dev.d.ts +289 -2
  47. package/dist/src/commands/dev.d.ts.map +1 -1
  48. package/dist/src/commands/dev.js +2501 -574
  49. package/dist/src/commands/dev.js.map +1 -1
  50. package/dist/src/commands/docs.d.ts +3 -0
  51. package/dist/src/commands/docs.d.ts.map +1 -0
  52. package/dist/src/commands/docs.js +262 -0
  53. package/dist/src/commands/docs.js.map +1 -0
  54. package/dist/src/commands/hub/index.d.ts +99 -0
  55. package/dist/src/commands/hub/index.d.ts.map +1 -0
  56. package/dist/src/commands/hub/index.js +452 -0
  57. package/dist/src/commands/hub/index.js.map +1 -0
  58. package/dist/src/commands/init.d.ts +65 -0
  59. package/dist/src/commands/init.d.ts.map +1 -0
  60. package/dist/src/commands/init.js +370 -0
  61. package/dist/src/commands/init.js.map +1 -0
  62. package/dist/src/commands/install-shim.d.ts +21 -0
  63. package/dist/src/commands/install-shim.d.ts.map +1 -0
  64. package/dist/src/commands/install-shim.js +132 -0
  65. package/dist/src/commands/install-shim.js.map +1 -0
  66. package/dist/src/commands/local/auth-provision.d.ts +71 -0
  67. package/dist/src/commands/local/auth-provision.d.ts.map +1 -0
  68. package/dist/src/commands/local/auth-provision.js +286 -0
  69. package/dist/src/commands/local/auth-provision.js.map +1 -0
  70. package/dist/src/commands/local/dev-local.d.ts +97 -0
  71. package/dist/src/commands/local/dev-local.d.ts.map +1 -0
  72. package/dist/src/commands/local/dev-local.js +306 -0
  73. package/dist/src/commands/local/dev-local.js.map +1 -0
  74. package/dist/src/commands/local/docker-runner.d.ts +37 -0
  75. package/dist/src/commands/local/docker-runner.d.ts.map +1 -0
  76. package/dist/src/commands/local/docker-runner.js +104 -0
  77. package/dist/src/commands/local/docker-runner.js.map +1 -0
  78. package/dist/src/commands/local/helpers.d.ts +22 -0
  79. package/dist/src/commands/local/helpers.d.ts.map +1 -0
  80. package/dist/src/commands/local/helpers.js +46 -0
  81. package/dist/src/commands/local/helpers.js.map +1 -0
  82. package/dist/src/commands/local/hub-local.d.ts +51 -0
  83. package/dist/src/commands/local/hub-local.d.ts.map +1 -0
  84. package/dist/src/commands/local/hub-local.js +197 -0
  85. package/dist/src/commands/local/hub-local.js.map +1 -0
  86. package/dist/src/commands/local/index.d.ts +23 -0
  87. package/dist/src/commands/local/index.d.ts.map +1 -0
  88. package/dist/src/commands/local/index.js +386 -0
  89. package/dist/src/commands/local/index.js.map +1 -0
  90. package/dist/src/commands/local/mocks.d.ts +361 -0
  91. package/dist/src/commands/local/mocks.d.ts.map +1 -0
  92. package/dist/src/commands/local/mocks.js +756 -0
  93. package/dist/src/commands/local/mocks.js.map +1 -0
  94. package/dist/src/commands/local/seed-zitadel.d.ts +151 -0
  95. package/dist/src/commands/local/seed-zitadel.d.ts.map +1 -0
  96. package/dist/src/commands/local/seed-zitadel.js +408 -0
  97. package/dist/src/commands/local/seed-zitadel.js.map +1 -0
  98. package/dist/src/commands/local/seed.d.ts +106 -0
  99. package/dist/src/commands/local/seed.d.ts.map +1 -0
  100. package/dist/src/commands/local/seed.js +252 -0
  101. package/dist/src/commands/local/seed.js.map +1 -0
  102. package/dist/src/commands/local/stack.d.ts +173 -0
  103. package/dist/src/commands/local/stack.d.ts.map +1 -0
  104. package/dist/src/commands/local/stack.js +499 -0
  105. package/dist/src/commands/local/stack.js.map +1 -0
  106. package/dist/src/commands/login.d.ts +256 -12
  107. package/dist/src/commands/login.d.ts.map +1 -1
  108. package/dist/src/commands/login.js +978 -50
  109. package/dist/src/commands/login.js.map +1 -1
  110. package/dist/src/commands/peer-addressing.d.ts +51 -0
  111. package/dist/src/commands/peer-addressing.d.ts.map +1 -0
  112. package/dist/src/commands/peer-addressing.js +64 -0
  113. package/dist/src/commands/peer-addressing.js.map +1 -0
  114. package/dist/src/commands/port-ownership.d.ts +69 -0
  115. package/dist/src/commands/port-ownership.d.ts.map +1 -0
  116. package/dist/src/commands/port-ownership.js +161 -0
  117. package/dist/src/commands/port-ownership.js.map +1 -0
  118. package/dist/src/commands/registry-publish.d.ts +3 -0
  119. package/dist/src/commands/registry-publish.d.ts.map +1 -0
  120. package/dist/src/commands/registry-publish.js +161 -0
  121. package/dist/src/commands/registry-publish.js.map +1 -0
  122. package/dist/src/commands/registry.d.ts +146 -2
  123. package/dist/src/commands/registry.d.ts.map +1 -1
  124. package/dist/src/commands/registry.js +541 -85
  125. package/dist/src/commands/registry.js.map +1 -1
  126. package/dist/src/commands/secrets/index.d.ts.map +1 -1
  127. package/dist/src/commands/secrets/index.js +15 -1
  128. package/dist/src/commands/secrets/index.js.map +1 -1
  129. package/dist/src/commands/secrets/migrate-config.d.ts +24 -0
  130. package/dist/src/commands/secrets/migrate-config.d.ts.map +1 -0
  131. package/dist/src/commands/secrets/migrate-config.js +277 -0
  132. package/dist/src/commands/secrets/migrate-config.js.map +1 -0
  133. package/dist/src/commands/secrets/set.d.ts +4 -0
  134. package/dist/src/commands/secrets/set.d.ts.map +1 -1
  135. package/dist/src/commands/secrets/set.js +243 -64
  136. package/dist/src/commands/secrets/set.js.map +1 -1
  137. package/dist/src/commands/skills.d.ts +34 -0
  138. package/dist/src/commands/skills.d.ts.map +1 -0
  139. package/dist/src/commands/skills.js +207 -0
  140. package/dist/src/commands/skills.js.map +1 -0
  141. package/dist/src/commands/stack.d.ts.map +1 -1
  142. package/dist/src/commands/stack.js +361 -23
  143. package/dist/src/commands/stack.js.map +1 -1
  144. package/dist/src/commands/temporal.d.ts +15 -0
  145. package/dist/src/commands/temporal.d.ts.map +1 -0
  146. package/dist/src/commands/temporal.js +781 -0
  147. package/dist/src/commands/temporal.js.map +1 -0
  148. package/dist/src/commands/tenant.d.ts +56 -0
  149. package/dist/src/commands/tenant.d.ts.map +1 -0
  150. package/dist/src/commands/tenant.js +233 -0
  151. package/dist/src/commands/tenant.js.map +1 -0
  152. package/dist/src/commands/tunnel/index.d.ts +49 -3
  153. package/dist/src/commands/tunnel/index.d.ts.map +1 -1
  154. package/dist/src/commands/tunnel/index.js +331 -8
  155. package/dist/src/commands/tunnel/index.js.map +1 -1
  156. package/dist/src/commands/vcs/clone.d.ts +3 -0
  157. package/dist/src/commands/vcs/clone.d.ts.map +1 -0
  158. package/dist/src/commands/vcs/clone.js +20 -0
  159. package/dist/src/commands/vcs/clone.js.map +1 -0
  160. package/dist/src/commands/vcs/common.d.ts +21 -0
  161. package/dist/src/commands/vcs/common.d.ts.map +1 -0
  162. package/dist/src/commands/vcs/common.js +62 -0
  163. package/dist/src/commands/vcs/common.js.map +1 -0
  164. package/dist/src/commands/vcs/drafts.d.ts +13 -0
  165. package/dist/src/commands/vcs/drafts.d.ts.map +1 -0
  166. package/dist/src/commands/vcs/drafts.js +45 -0
  167. package/dist/src/commands/vcs/drafts.js.map +1 -0
  168. package/dist/src/commands/vcs/get.d.ts +8 -0
  169. package/dist/src/commands/vcs/get.d.ts.map +1 -0
  170. package/dist/src/commands/vcs/get.js +61 -0
  171. package/dist/src/commands/vcs/get.js.map +1 -0
  172. package/dist/src/commands/vcs/index.d.ts +3 -0
  173. package/dist/src/commands/vcs/index.d.ts.map +1 -0
  174. package/dist/src/commands/vcs/index.js +58 -0
  175. package/dist/src/commands/vcs/index.js.map +1 -0
  176. package/dist/src/commands/vcs/propose.d.ts +23 -0
  177. package/dist/src/commands/vcs/propose.d.ts.map +1 -0
  178. package/dist/src/commands/vcs/propose.js +94 -0
  179. package/dist/src/commands/vcs/propose.js.map +1 -0
  180. package/dist/src/commands/vcs/review.d.ts +15 -0
  181. package/dist/src/commands/vcs/review.d.ts.map +1 -0
  182. package/dist/src/commands/vcs/review.js +35 -0
  183. package/dist/src/commands/vcs/review.js.map +1 -0
  184. package/dist/src/commands/vcs-enrich.d.ts +15 -0
  185. package/dist/src/commands/vcs-enrich.d.ts.map +1 -0
  186. package/dist/src/commands/vcs-enrich.js +28 -0
  187. package/dist/src/commands/vcs-enrich.js.map +1 -0
  188. package/dist/src/commands/vpn/index.d.ts +24 -0
  189. package/dist/src/commands/vpn/index.d.ts.map +1 -0
  190. package/dist/src/commands/vpn/index.js +543 -0
  191. package/dist/src/commands/vpn/index.js.map +1 -0
  192. package/dist/src/commands/vpn/tunnel.d.ts +11 -0
  193. package/dist/src/commands/vpn/tunnel.d.ts.map +1 -0
  194. package/dist/src/commands/vpn/tunnel.js +188 -0
  195. package/dist/src/commands/vpn/tunnel.js.map +1 -0
  196. package/dist/src/commands/workflow.d.ts +24 -0
  197. package/dist/src/commands/workflow.d.ts.map +1 -0
  198. package/dist/src/commands/workflow.js +577 -0
  199. package/dist/src/commands/workflow.js.map +1 -0
  200. package/dist/src/docs/assemble.d.ts +217 -0
  201. package/dist/src/docs/assemble.d.ts.map +1 -0
  202. package/dist/src/docs/assemble.js +739 -0
  203. package/dist/src/docs/assemble.js.map +1 -0
  204. package/dist/src/docs/cli-reference.d.ts +93 -0
  205. package/dist/src/docs/cli-reference.d.ts.map +1 -0
  206. package/dist/src/docs/cli-reference.js +256 -0
  207. package/dist/src/docs/cli-reference.js.map +1 -0
  208. package/dist/src/docs/discover.d.ts +60 -0
  209. package/dist/src/docs/discover.d.ts.map +1 -0
  210. package/dist/src/docs/discover.js +233 -0
  211. package/dist/src/docs/discover.js.map +1 -0
  212. package/dist/src/docs/portal.d.ts +79 -0
  213. package/dist/src/docs/portal.d.ts.map +1 -0
  214. package/dist/src/docs/portal.js +242 -0
  215. package/dist/src/docs/portal.js.map +1 -0
  216. package/dist/src/docs/registry-docs.d.ts +43 -0
  217. package/dist/src/docs/registry-docs.d.ts.map +1 -0
  218. package/dist/src/docs/registry-docs.js +138 -0
  219. package/dist/src/docs/registry-docs.js.map +1 -0
  220. package/dist/src/docs/schema.d.ts +48 -0
  221. package/dist/src/docs/schema.d.ts.map +1 -0
  222. package/dist/src/docs/schema.js +96 -0
  223. package/dist/src/docs/schema.js.map +1 -0
  224. package/dist/src/docs/serve.d.ts +27 -0
  225. package/dist/src/docs/serve.d.ts.map +1 -0
  226. package/dist/src/docs/serve.js +102 -0
  227. package/dist/src/docs/serve.js.map +1 -0
  228. package/dist/src/docs/start.d.ts +80 -0
  229. package/dist/src/docs/start.d.ts.map +1 -0
  230. package/dist/src/docs/start.js +313 -0
  231. package/dist/src/docs/start.js.map +1 -0
  232. package/dist/src/eval/agent-eval.d.mts +14 -0
  233. package/dist/src/eval/agent-eval.d.mts.map +1 -0
  234. package/dist/src/eval/agent-eval.mjs +273 -0
  235. package/dist/src/eval/agent-eval.mjs.map +1 -0
  236. package/dist/src/index.js +624 -7
  237. package/dist/src/index.js.map +7 -1
  238. package/dist/src/program.d.ts +26 -0
  239. package/dist/src/program.d.ts.map +1 -0
  240. package/dist/src/program.js +76 -0
  241. package/dist/src/program.js.map +1 -0
  242. package/dist/src/utils/auth-preflight.d.ts +159 -0
  243. package/dist/src/utils/auth-preflight.d.ts.map +1 -0
  244. package/dist/src/utils/auth-preflight.js +298 -0
  245. package/dist/src/utils/auth-preflight.js.map +1 -0
  246. package/dist/src/utils/aws-auth.d.ts +181 -0
  247. package/dist/src/utils/aws-auth.d.ts.map +1 -0
  248. package/dist/src/utils/aws-auth.js +527 -0
  249. package/dist/src/utils/aws-auth.js.map +1 -0
  250. package/dist/src/utils/bastion.d.ts +1 -1
  251. package/dist/src/utils/bastion.d.ts.map +1 -1
  252. package/dist/src/utils/bastion.js +29 -8
  253. package/dist/src/utils/bastion.js.map +1 -1
  254. package/dist/src/utils/build-info.d.ts +33 -0
  255. package/dist/src/utils/build-info.d.ts.map +1 -0
  256. package/dist/src/utils/build-info.js +102 -0
  257. package/dist/src/utils/build-info.js.map +1 -0
  258. package/dist/src/utils/capture-history.d.ts +62 -0
  259. package/dist/src/utils/capture-history.d.ts.map +1 -0
  260. package/dist/src/utils/capture-history.js +94 -0
  261. package/dist/src/utils/capture-history.js.map +1 -0
  262. package/dist/src/utils/deploy-preflight.d.ts +52 -0
  263. package/dist/src/utils/deploy-preflight.d.ts.map +1 -0
  264. package/dist/src/utils/deploy-preflight.js +183 -0
  265. package/dist/src/utils/deploy-preflight.js.map +1 -0
  266. package/dist/src/utils/errors.d.ts +75 -0
  267. package/dist/src/utils/errors.d.ts.map +1 -0
  268. package/dist/src/utils/errors.js +106 -0
  269. package/dist/src/utils/errors.js.map +1 -0
  270. package/dist/src/utils/first-party-contexts.d.ts +40 -0
  271. package/dist/src/utils/first-party-contexts.d.ts.map +1 -0
  272. package/dist/src/utils/first-party-contexts.js +53 -0
  273. package/dist/src/utils/first-party-contexts.js.map +1 -0
  274. package/dist/src/utils/index.d.ts +2 -0
  275. package/dist/src/utils/index.d.ts.map +1 -1
  276. package/dist/src/utils/index.js +2 -0
  277. package/dist/src/utils/index.js.map +1 -1
  278. package/dist/src/utils/kubeconfig.d.ts +62 -0
  279. package/dist/src/utils/kubeconfig.d.ts.map +1 -0
  280. package/dist/src/utils/kubeconfig.js +118 -0
  281. package/dist/src/utils/kubeconfig.js.map +1 -0
  282. package/dist/src/utils/log.d.ts +27 -1
  283. package/dist/src/utils/log.d.ts.map +1 -1
  284. package/dist/src/utils/log.js +59 -5
  285. package/dist/src/utils/log.js.map +1 -1
  286. package/dist/src/utils/pid.d.ts +47 -0
  287. package/dist/src/utils/pid.d.ts.map +1 -0
  288. package/dist/src/utils/pid.js +83 -0
  289. package/dist/src/utils/pid.js.map +1 -0
  290. package/dist/src/utils/pulumi-run.d.ts +23 -0
  291. package/dist/src/utils/pulumi-run.d.ts.map +1 -0
  292. package/dist/src/utils/pulumi-run.js +112 -0
  293. package/dist/src/utils/pulumi-run.js.map +1 -0
  294. package/dist/src/utils/pulumi.d.ts +15 -0
  295. package/dist/src/utils/pulumi.d.ts.map +1 -1
  296. package/dist/src/utils/pulumi.js +35 -0
  297. package/dist/src/utils/pulumi.js.map +1 -1
  298. package/dist/src/utils/reachability.d.ts +29 -0
  299. package/dist/src/utils/reachability.d.ts.map +1 -0
  300. package/dist/src/utils/reachability.js +81 -0
  301. package/dist/src/utils/reachability.js.map +1 -0
  302. package/dist/src/utils/recover-conversation.d.ts +154 -0
  303. package/dist/src/utils/recover-conversation.d.ts.map +1 -0
  304. package/dist/src/utils/recover-conversation.js +314 -0
  305. package/dist/src/utils/recover-conversation.js.map +1 -0
  306. package/dist/src/utils/registry-broker.d.ts +148 -0
  307. package/dist/src/utils/registry-broker.d.ts.map +1 -0
  308. package/dist/src/utils/registry-broker.js +180 -0
  309. package/dist/src/utils/registry-broker.js.map +1 -0
  310. package/dist/src/utils/socks-forward.d.ts +40 -0
  311. package/dist/src/utils/socks-forward.d.ts.map +1 -0
  312. package/dist/src/utils/socks-forward.js +97 -0
  313. package/dist/src/utils/socks-forward.js.map +1 -0
  314. package/dist/src/utils/stack-flag.d.ts +23 -0
  315. package/dist/src/utils/stack-flag.d.ts.map +1 -0
  316. package/dist/src/utils/stack-flag.js +41 -0
  317. package/dist/src/utils/stack-flag.js.map +1 -0
  318. package/dist/src/utils/tailscale-targets.d.ts +52 -0
  319. package/dist/src/utils/tailscale-targets.d.ts.map +1 -0
  320. package/dist/src/utils/tailscale-targets.js +93 -0
  321. package/dist/src/utils/tailscale-targets.js.map +1 -0
  322. package/dist/src/utils/tailscale.d.ts +277 -0
  323. package/dist/src/utils/tailscale.d.ts.map +1 -0
  324. package/dist/src/utils/tailscale.js +1007 -0
  325. package/dist/src/utils/tailscale.js.map +1 -0
  326. package/dist/src/utils/temporal-auth.d.ts +43 -0
  327. package/dist/src/utils/temporal-auth.d.ts.map +1 -0
  328. package/dist/src/utils/temporal-auth.js +176 -0
  329. package/dist/src/utils/temporal-auth.js.map +1 -0
  330. package/dist/src/utils/temporal-codec.d.ts +101 -0
  331. package/dist/src/utils/temporal-codec.d.ts.map +1 -0
  332. package/dist/src/utils/temporal-codec.js +319 -0
  333. package/dist/src/utils/temporal-codec.js.map +1 -0
  334. package/dist/src/utils/tunnel-ownership.d.ts +130 -0
  335. package/dist/src/utils/tunnel-ownership.d.ts.map +1 -0
  336. package/dist/src/utils/tunnel-ownership.js +274 -0
  337. package/dist/src/utils/tunnel-ownership.js.map +1 -0
  338. package/dist/src/utils/vpn-join.d.ts +32 -0
  339. package/dist/src/utils/vpn-join.d.ts.map +1 -0
  340. package/dist/src/utils/vpn-join.js +62 -0
  341. package/dist/src/utils/vpn-join.js.map +1 -0
  342. package/dist/src/utils/vpn.d.ts +62 -0
  343. package/dist/src/utils/vpn.d.ts.map +1 -0
  344. package/dist/src/utils/vpn.js +183 -0
  345. package/dist/src/utils/vpn.js.map +1 -0
  346. package/dist/src/utils/workflow-fingerprint.d.ts +49 -0
  347. package/dist/src/utils/workflow-fingerprint.d.ts.map +1 -0
  348. package/dist/src/utils/workflow-fingerprint.js +135 -0
  349. package/dist/src/utils/workflow-fingerprint.js.map +1 -0
  350. package/dist/src/utils/worktree-identity.d.ts +86 -0
  351. package/dist/src/utils/worktree-identity.d.ts.map +1 -0
  352. package/dist/src/utils/worktree-identity.js +164 -0
  353. package/dist/src/utils/worktree-identity.js.map +1 -0
  354. package/fragments/agents/AGENTS.md.hbs +92 -0
  355. package/fragments/agents/CLAUDE.md.hbs +8 -0
  356. package/fragments/base/Pulumi.dev.yaml.hbs +10 -0
  357. package/fragments/base/Pulumi.yaml.hbs +9 -0
  358. package/fragments/base/index.ts.hbs +107 -0
  359. package/fragments/base/package.json.hbs +22 -0
  360. package/{templates/api-web-db/api → fragments/base}/tsconfig.json +3 -4
  361. package/fragments/bucket/api/src/storage.ts.hbs +81 -0
  362. package/fragments/database/api/src/db.ts.hbs +23 -0
  363. package/fragments/database/prisma/schema.prisma.hbs +18 -0
  364. package/{templates/api-web-db → fragments/service}/api/.dockerignore +2 -0
  365. package/fragments/service/api/package.json.hbs +40 -0
  366. package/fragments/service/api/src/index.ts.hbs +58 -0
  367. package/{templates/api-web-db → fragments/service/api}/tsconfig.json +5 -3
  368. package/fragments/temporal/worker/.dockerignore +4 -0
  369. package/fragments/temporal/worker/Dockerfile +15 -0
  370. package/{templates/temporal-api-worker → fragments/temporal}/worker/package.json.hbs +3 -2
  371. package/fragments/temporal/worker/src/activities.ts.hbs +14 -0
  372. package/fragments/temporal/worker/src/worker.ts.hbs +27 -0
  373. package/fragments/temporal/worker/src/workflows.ts.hbs +24 -0
  374. package/fragments/temporal/worker/tsconfig.json +13 -0
  375. package/package.json +35 -21
  376. package/skills/core/SKILL.md +365 -0
  377. package/skills/pull-remote-agent-context/SKILL.md +194 -0
  378. package/stack/docker-compose.hub.yml +134 -0
  379. package/stack/docker-compose.yml +447 -0
  380. package/stack/hub/Dockerfile.api +18 -0
  381. package/stack/hub/Dockerfile.auth +10 -0
  382. package/stack/hub/Dockerfile.ui +10 -0
  383. package/stack/init/01-databases.sql +12 -0
  384. package/stack/observability/blackbox.yml +20 -0
  385. package/stack/observability/otel-collector.yaml +73 -0
  386. package/stack/observability/prometheus.yml +36 -0
  387. package/stack/observability/tempo.yaml +25 -0
  388. package/stack/temporal/local.yaml +44 -0
  389. package/templates/api-auth/AGENTS.md.hbs +16 -0
  390. package/templates/api-auth/Pulumi.dev.yaml.hbs +8 -0
  391. package/templates/api-auth/Pulumi.yaml.hbs +8 -0
  392. package/templates/api-auth/README.md.hbs +67 -0
  393. package/templates/api-auth/api/package.json.hbs +28 -0
  394. package/templates/api-auth/api/src/index.ts.hbs +96 -0
  395. package/templates/api-auth/api/src/schema.ts.hbs +35 -0
  396. package/templates/api-auth/api/src/seed-authz.ts.hbs +48 -0
  397. package/templates/api-auth/api/src/spicedb-http-provider.ts.hbs +89 -0
  398. package/templates/api-auth/index.ts.hbs +65 -0
  399. package/templates/api-auth/package.json.hbs +20 -0
  400. package/templates/api-auth/skills/develop/SKILL.md.hbs +32 -0
  401. package/templates/api-role-gating/AGENTS.md.hbs +20 -0
  402. package/templates/api-role-gating/Pulumi.dev.yaml.hbs +8 -0
  403. package/templates/api-role-gating/Pulumi.yaml.hbs +8 -0
  404. package/templates/api-role-gating/README.md.hbs +64 -0
  405. package/templates/api-role-gating/api/Dockerfile +17 -0
  406. package/templates/api-role-gating/api/package.json.hbs +28 -0
  407. package/templates/api-role-gating/api/src/authn.ts.hbs +29 -0
  408. package/templates/api-role-gating/api/src/authz.ts.hbs +12 -0
  409. package/templates/api-role-gating/api/src/main.ts.hbs +58 -0
  410. package/templates/api-role-gating/api/src/schema.ts.hbs +22 -0
  411. package/templates/api-role-gating/api/tsconfig.json +13 -0
  412. package/templates/api-role-gating/index.ts.hbs +107 -0
  413. package/templates/api-role-gating/package.json.hbs +21 -0
  414. package/templates/api-role-gating/skills/develop/SKILL.md.hbs +60 -0
  415. package/templates/api-role-gating/tsconfig.json +10 -0
  416. package/templates/apps-repo/README.md.hbs +24 -0
  417. package/templates/apps-repo/gitignore +41 -0
  418. package/templates/apps-repo/npmrc.hbs +7 -0
  419. package/templates/apps-repo/package.json.hbs +15 -0
  420. package/templates/apps-repo/pnpm-workspace.yaml +3 -0
  421. package/templates/apps-repo/tsconfig.json +18 -0
  422. package/templates/external-service/AGENTS.md.hbs +20 -0
  423. package/templates/external-service/Pulumi.dev.yaml.hbs +8 -0
  424. package/templates/external-service/Pulumi.yaml.hbs +8 -0
  425. package/templates/external-service/README.md.hbs +56 -0
  426. package/templates/external-service/api/Dockerfile +15 -0
  427. package/templates/external-service/api/package.json.hbs +25 -0
  428. package/templates/external-service/api/src/index.ts.hbs +81 -0
  429. package/templates/external-service/index.ts.hbs +82 -0
  430. package/templates/external-service/mocks/vendorpay/package.json.hbs +9 -0
  431. package/templates/external-service/mocks/vendorpay/server.mjs.hbs +61 -0
  432. package/templates/external-service/package.json.hbs +34 -0
  433. package/templates/external-service/skills/develop/SKILL.md.hbs +55 -0
  434. package/templates/external-service/tsconfig.json +10 -0
  435. package/templates/platform-repo/AGENTS.md.hbs +18 -0
  436. package/templates/platform-repo/README.md.hbs +38 -0
  437. package/templates/platform-repo/core/Pulumi.yaml.hbs +12 -0
  438. package/templates/platform-repo/core/Pulumi.{{tenant}}-{{env}}.yaml.hbs +57 -0
  439. package/templates/platform-repo/core/index.ts +62 -0
  440. package/templates/platform-repo/core/package.json.hbs +22 -0
  441. package/templates/platform-repo/core/tsconfig.json +8 -0
  442. package/templates/platform-repo/package.json.hbs +15 -0
  443. package/templates/platform-repo/platform/Pulumi.yaml.hbs +12 -0
  444. package/templates/platform-repo/platform/Pulumi.{{tenant}}-{{env}}.yaml.hbs +59 -0
  445. package/templates/platform-repo/platform/index.ts +95 -0
  446. package/templates/platform-repo/platform/package.json.hbs +26 -0
  447. package/templates/platform-repo/platform/tsconfig.json +8 -0
  448. package/templates/platform-repo/pnpm-workspace.yaml +3 -0
  449. package/templates/workflow/AGENTS.md.hbs +15 -0
  450. package/templates/workflow/Pulumi.dev.yaml.hbs +8 -0
  451. package/templates/workflow/Pulumi.yaml.hbs +8 -0
  452. package/templates/workflow/README.md.hbs +45 -0
  453. package/templates/workflow/api/.dockerignore +4 -0
  454. package/templates/workflow/api/Dockerfile +15 -0
  455. package/templates/{temporal-api-worker → workflow}/api/package.json.hbs +8 -6
  456. package/templates/workflow/api/src/index.ts.hbs +130 -0
  457. package/templates/workflow/api/tsconfig.json +14 -0
  458. package/templates/workflow/index.ts.hbs +67 -0
  459. package/templates/workflow/package.json.hbs +20 -0
  460. package/templates/workflow/skills/develop/SKILL.md.hbs +32 -0
  461. package/templates/workflow/tsconfig.json +10 -0
  462. package/templates/workflow/worker/.dockerignore +4 -0
  463. package/templates/workflow/worker/Dockerfile +13 -0
  464. package/templates/workflow/worker/package.json.hbs +25 -0
  465. package/templates/workflow/worker/src/worker.ts.hbs +24 -0
  466. package/templates/workflow/worker/tsconfig.json +14 -0
  467. package/dist/src/commands/devbox.d.ts +0 -22
  468. package/dist/src/commands/devbox.d.ts.map +0 -1
  469. package/dist/src/commands/devbox.js +0 -463
  470. package/dist/src/commands/devbox.js.map +0 -1
  471. package/templates/api-web-db/api/Dockerfile +0 -16
  472. package/templates/api-web-db/api/package.json.hbs +0 -21
  473. package/templates/api-web-db/api/src/index.ts.hbs +0 -123
  474. package/templates/api-web-db/package.json.hbs +0 -27
  475. package/templates/api-web-db/sst.config.ts.hbs +0 -151
  476. package/templates/api-web-db/web/.dockerignore +0 -3
  477. package/templates/api-web-db/web/Dockerfile +0 -17
  478. package/templates/api-web-db/web/app/lib/db.server.ts +0 -36
  479. package/templates/api-web-db/web/app/root.tsx +0 -29
  480. package/templates/api-web-db/web/app/routes/home.tsx.hbs +0 -114
  481. package/templates/api-web-db/web/app/routes.ts +0 -3
  482. package/templates/api-web-db/web/package.json.hbs +0 -30
  483. package/templates/api-web-db/web/prisma/schema.prisma +0 -25
  484. package/templates/api-web-db/web/prisma.config.ts +0 -18
  485. package/templates/api-web-db/web/react-router.config.ts +0 -5
  486. package/templates/api-web-db/web/tsconfig.json +0 -19
  487. package/templates/api-web-db/web/vite.config.ts +0 -9
  488. package/templates/temporal-api-worker/README.md.hbs +0 -133
  489. package/templates/temporal-api-worker/api/src/index.ts.hbs +0 -145
  490. package/templates/temporal-api-worker/package.json.hbs +0 -29
  491. package/templates/temporal-api-worker/sst.config.ts.hbs +0 -161
  492. package/templates/temporal-api-worker/worker/Dockerfile +0 -11
  493. package/templates/temporal-api-worker/worker/src/bundle-workflows.ts +0 -38
  494. package/templates/temporal-api-worker/worker/src/worker.ts.hbs +0 -93
  495. /package/templates/{temporal-api-worker → api-auth}/api/.dockerignore +0 -0
  496. /package/templates/{temporal-api-worker → api-auth}/api/Dockerfile +0 -0
  497. /package/templates/{temporal-api-worker → api-auth}/api/tsconfig.json +0 -0
  498. /package/templates/{temporal-api-worker → api-auth}/tsconfig.json +0 -0
  499. /package/templates/{temporal-api-worker/worker → api-role-gating/api}/.dockerignore +0 -0
  500. /package/templates/{temporal-api-worker/worker → external-service/api}/tsconfig.json +0 -0
  501. /package/templates/{temporal-api-worker → workflow}/api/src/encryption-codec.ts +0 -0
  502. /package/templates/{temporal-api-worker → workflow}/worker/src/activities.ts.hbs +0 -0
  503. /package/templates/{temporal-api-worker → workflow}/worker/src/encryption-codec.ts +0 -0
  504. /package/templates/{temporal-api-worker → workflow}/worker/src/workflows.ts.hbs +0 -0
@@ -0,0 +1,671 @@
1
+ /**
2
+ * `mesh dev doctor` — preflight + on-demand diagnosis for `mesh dev`.
3
+ *
4
+ * Three layers kept deliberately separate so the bug-prone logic stays pure and
5
+ * unit-testable without network/AWS:
6
+ * 1. Engine + renderers (this section): `aggregateStatus`, `runChecks`,
7
+ * `renderHuman`, `renderJson`.
8
+ * 2. `DoctorContext` builder (resolves role/context/session once).
9
+ * 3. The individual `Check`s (creds, tunnels, ports, …), each returning data.
10
+ *
11
+ * Both `mesh dev doctor` and the `mesh dev` launch preflight call `runChecks`;
12
+ * see MESH-2015 (docs/superpowers/specs/2026-07-08-mesh-dev-doctor-design.md).
13
+ */
14
+ import { execFileSync } from "node:child_process";
15
+ import * as fs from "node:fs";
16
+ import * as net from "node:net";
17
+ import * as path from "node:path";
18
+ import { isPortFree } from "./dev.js";
19
+ import { appUsesMeshPackages, probeRegistryToken, registryLoginFix, } from "../utils/auth-preflight.js";
20
+ import { probeCredentials, isRemoteEnvironment } from "./login.js";
21
+ // ============================================================================
22
+ // Engine + renderers (pure)
23
+ // ============================================================================
24
+ /** `error` if any error, else `warn` if any warn, else `ok` (empty → `ok`). */
25
+ export function aggregateStatus(results) {
26
+ if (results.some((r) => r.status === "error"))
27
+ return "error";
28
+ if (results.some((r) => r.status === "warn"))
29
+ return "warn";
30
+ return "ok";
31
+ }
32
+ /**
33
+ * Run every check whose `phases` includes `phase`, concurrently. A check that
34
+ * throws is captured as an `error` result rather than aborting the run — the
35
+ * whole point of doctor is to report, never to crash.
36
+ */
37
+ export async function runChecks(ctx, phase, checks) {
38
+ const applicable = checks.filter((c) => c.phases.includes(phase));
39
+ return Promise.all(applicable.map(async (check) => {
40
+ try {
41
+ return { check, result: await check.run(ctx, phase) };
42
+ }
43
+ catch (err) {
44
+ const msg = err instanceof Error ? err.message : String(err);
45
+ return {
46
+ check,
47
+ result: {
48
+ status: "error",
49
+ summary: `check '${check.id}' threw: ${msg}`,
50
+ },
51
+ };
52
+ }
53
+ }));
54
+ }
55
+ const ICONS = { ok: "✓", warn: "⚠", error: "✗" };
56
+ /** Human-readable: one line per check + indented remediation when present. */
57
+ export function renderHuman(results) {
58
+ const lines = [];
59
+ for (const { check, result } of results) {
60
+ lines.push(`${ICONS[result.status]} ${check.title} — ${result.summary}`);
61
+ if (result.detail)
62
+ lines.push(` ${result.detail}`);
63
+ if (result.remediation)
64
+ lines.push(` Fix: ${result.remediation}`);
65
+ }
66
+ return lines.join("\n");
67
+ }
68
+ /** Machine-readable: stable key shape for tooling / future CI. */
69
+ export function renderJson(results) {
70
+ return JSON.stringify({
71
+ status: aggregateStatus(results.map((r) => r.result)),
72
+ checks: results.map(({ check, result }) => ({
73
+ id: check.id,
74
+ status: result.status,
75
+ summary: result.summary,
76
+ // Stable keys for tooling: always present, `null` when absent
77
+ // (JSON.stringify would otherwise drop `undefined`).
78
+ remediation: result.remediation ?? null,
79
+ detail: result.detail ?? null,
80
+ })),
81
+ }, null, 2);
82
+ }
83
+ // ============================================================================
84
+ // Check: credentials + TTL (creds)
85
+ // ============================================================================
86
+ /** Render a TTL in seconds as a compact human string ("47m", "1h 10m"). */
87
+ function fmtTtl(seconds) {
88
+ if (seconds <= 0)
89
+ return "expired";
90
+ // Round to whole minutes first, then split — so 11h59.5m carries to "12h"
91
+ // rather than rendering "11h 60m".
92
+ const totalMin = Math.round(seconds / 60);
93
+ const h = Math.floor(totalMin / 60);
94
+ const m = totalMin % 60;
95
+ if (h > 0)
96
+ return m > 0 ? `${h}h ${m}m` : `${h}h`;
97
+ return `${m}m`;
98
+ }
99
+ /**
100
+ * Pure mapper: a `CredProbe` → a `CheckResult` with the exact remediation.
101
+ * `headless` selects the device-flow `mesh login` variant (browser flow times
102
+ * out over SSH/tailscale — see the credential runbook).
103
+ */
104
+ export function credProbeToResult(probe, context, headless) {
105
+ const loginCmd = `mesh login ${context}${headless ? " --device" : ""}`;
106
+ switch (probe.state) {
107
+ case "ok":
108
+ return {
109
+ status: probe.ttlSeconds < 300 ? "warn" : "ok",
110
+ summary: `deployer creds valid ${fmtTtl(probe.ttlSeconds)}${probe.email ? ` (${probe.email})` : ""}`,
111
+ detail: `expires ${probe.expiresAt}`,
112
+ };
113
+ case "no-session":
114
+ return {
115
+ status: "error",
116
+ summary: `no Zitadel session for ${context}`,
117
+ remediation: loginCmd,
118
+ };
119
+ case "expired-session":
120
+ return {
121
+ status: "error",
122
+ summary: `Zitadel session for ${context} expired and could not refresh`,
123
+ remediation: loginCmd,
124
+ };
125
+ case "assume-denied":
126
+ return {
127
+ status: "error",
128
+ summary: "session valid but AssumeRole denied — wrong IAM role/policy",
129
+ detail: probe.detail,
130
+ remediation: `check mesh:deployerRole in the stack config and the role's trust/permissions; re-login if role changed: ${loginCmd}`,
131
+ };
132
+ case "stale-env-override":
133
+ return {
134
+ status: "error",
135
+ summary: "stale AWS_* env vars would override the self-refreshing login profile",
136
+ remediation: "unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN",
137
+ };
138
+ }
139
+ }
140
+ export const credsCheck = {
141
+ id: "creds",
142
+ title: "Credentials",
143
+ phases: ["preflight", "ondemand"],
144
+ async run(ctx) {
145
+ // When creds were already resolved (launch preflight passes the method),
146
+ // trust that result — avoids a second AssumeRole call, and correctly
147
+ // reports the SSO/environment paths that `probeCredentials` (Zitadel-only)
148
+ // would otherwise misdiagnose.
149
+ switch (ctx.credMethod) {
150
+ case "sso":
151
+ return {
152
+ status: "ok",
153
+ summary: "using AWS SSO credentials (auto-refresh from SSO cache)",
154
+ };
155
+ case "environment":
156
+ return { status: "ok", summary: "using AWS credentials from environment" };
157
+ case "zitadel":
158
+ return {
159
+ status: "ok",
160
+ summary: "using Zitadel deployer credentials (credential_process auto-refresh)",
161
+ };
162
+ }
163
+ // credMethod null (on-demand doctor, or a failed launch resolution) or
164
+ // 'ambient': determine the real state by probing.
165
+ if (!ctx.platformContext || !ctx.deployerRole) {
166
+ return {
167
+ status: "warn",
168
+ summary: "no deployer role/context resolved — using ambient AWS credentials",
169
+ detail: "add mesh:deployerRole to the Pulumi stack config to use mesh login credentials",
170
+ };
171
+ }
172
+ const probe = await probeCredentials(ctx.platformContext, ctx.deployerRole);
173
+ return credProbeToResult(probe, ctx.platformContext, isRemoteEnvironment());
174
+ },
175
+ };
176
+ // ============================================================================
177
+ // Check: CodeArtifact registry token (registry) — MESH-2479
178
+ // ============================================================================
179
+ /**
180
+ * Verify the @mesh-tech CodeArtifact token actually works BEFORE long work —
181
+ * an expired token otherwise surfaces minutes later as npm's generic E401
182
+ * ("try npm login", which cannot fix a CodeArtifact registry). Warn-level:
183
+ * `mesh dev` itself runs installed code, so a stale token shouldn't block
184
+ * launch — it should name the fix.
185
+ */
186
+ export const registryCheck = {
187
+ id: "registry",
188
+ title: "Registry auth",
189
+ phases: ["preflight", "ondemand"],
190
+ async run(ctx, phase) {
191
+ // The other preflight checks are fast — don't let an unreachable
192
+ // registry (offline / VPN off) hold the whole launch for the full
193
+ // on-demand bound. The zero-access local loop must stay snappy.
194
+ const probe = await probeRegistryToken(phase === "preflight" ? { timeoutMs: 3_000 } : undefined);
195
+ switch (probe.state) {
196
+ case "fresh":
197
+ return { status: "ok", summary: "CodeArtifact token accepted by the @mesh-tech registry" };
198
+ case "expired":
199
+ return {
200
+ status: "warn",
201
+ summary: "CodeArtifact token expired/rejected — installing @mesh-tech packages will fail with E401",
202
+ detail: probe.detail,
203
+ remediation: registryLoginFix(ctx.platformContext ?? undefined),
204
+ };
205
+ case "unreachable":
206
+ return {
207
+ status: "ok",
208
+ summary: "registry unreachable — CodeArtifact token not verified (offline?)",
209
+ detail: probe.detail,
210
+ };
211
+ case "missing":
212
+ return appUsesMeshPackages(ctx.appRoot)
213
+ ? {
214
+ status: "warn",
215
+ summary: "no CodeArtifact auth in ~/.npmrc, but this app depends on @mesh-tech packages — pnpm install will fail with E401",
216
+ remediation: registryLoginFix(ctx.platformContext ?? undefined),
217
+ }
218
+ : {
219
+ status: "ok",
220
+ summary: "no CodeArtifact auth configured (needed only to install @mesh-tech packages)",
221
+ };
222
+ }
223
+ },
224
+ };
225
+ // ============================================================================
226
+ // Checks: tmux, ports, worktree
227
+ // ============================================================================
228
+ /**
229
+ * Parse `lsof -ti :PORT` + `ps -o comm= -p PID` output into the squatting
230
+ * process. `lsof` may list several PIDs (one per line); we name the first.
231
+ */
232
+ export function parsePortSquatter(lsofOut, psOut) {
233
+ const firstLine = lsofOut.trim().split("\n")[0]?.trim();
234
+ const pid = Number(firstLine);
235
+ if (!firstLine || !Number.isInteger(pid))
236
+ return null;
237
+ const command = psOut.trim().split("\n")[0]?.trim() || "unknown";
238
+ return { pid, command };
239
+ }
240
+ /** True when `serviceSrc` lives under (or equals) `repoRoot`. */
241
+ export function sharesWorktreeRoot(serviceSrc, repoRoot) {
242
+ const src = path.resolve(serviceSrc);
243
+ const root = path.resolve(repoRoot);
244
+ return src === root || src.startsWith(root + path.sep);
245
+ }
246
+ /**
247
+ * Pure classification for the on-demand ports check. Given the PID currently
248
+ * listening on a service port (`null` when nothing is) and the set of PIDs in
249
+ * the live session's tmux process tree:
250
+ * - `null` listener → `dead` (the service isn't running)
251
+ * - listener in the session → `owned` (healthy — the session's own svc)
252
+ * - listener outside it → `foreign` (a real squatter)
253
+ * Mid-session the session's services are *supposed* to hold their ports, so a
254
+ * naive "occupied ⇒ squatted" test misreports a healthy session (MESH-2015).
255
+ */
256
+ export function classifyPortListener(listenerPid, sessionPids) {
257
+ if (listenerPid === null)
258
+ return "dead";
259
+ return sessionPids.has(listenerPid) ? "owned" : "foreign";
260
+ }
261
+ /**
262
+ * Pure: expand tmux pane PIDs to the full set of PIDs in their process
263
+ * subtrees, using the output of `ps -eo pid=,ppid=` (each line "PID PPID").
264
+ * The roots themselves are included; unparseable lines are skipped.
265
+ */
266
+ export function collectSessionPids(panePids, psOut) {
267
+ const children = new Map();
268
+ for (const line of psOut.trim().split("\n")) {
269
+ const [pidStr, ppidStr] = line.trim().split(/\s+/);
270
+ const pid = Number(pidStr);
271
+ const ppid = Number(ppidStr);
272
+ if (!Number.isInteger(pid) || !Number.isInteger(ppid))
273
+ continue;
274
+ const kids = children.get(ppid) ?? [];
275
+ kids.push(pid);
276
+ children.set(ppid, kids);
277
+ }
278
+ const result = new Set();
279
+ const queue = panePids.filter((p) => Number.isInteger(p));
280
+ while (queue.length > 0) {
281
+ const pid = queue.shift();
282
+ if (result.has(pid))
283
+ continue;
284
+ result.add(pid);
285
+ for (const child of children.get(pid) ?? [])
286
+ queue.push(child);
287
+ }
288
+ return result;
289
+ }
290
+ /** Identify the process squatting a port; null if it can't be determined. */
291
+ function whoHasPort(port) {
292
+ try {
293
+ const lsof = execFileSync("lsof", ["-ti", `:${port}`], {
294
+ encoding: "utf8",
295
+ stdio: ["ignore", "pipe", "ignore"],
296
+ });
297
+ const pid = lsof.trim().split("\n")[0]?.trim();
298
+ if (!pid)
299
+ return null;
300
+ let ps = "";
301
+ try {
302
+ ps = execFileSync("ps", ["-o", "comm=", "-p", pid], {
303
+ encoding: "utf8",
304
+ stdio: ["ignore", "pipe", "ignore"],
305
+ });
306
+ }
307
+ catch {
308
+ /* ps failed — parsePortSquatter falls back to "unknown" */
309
+ }
310
+ return parsePortSquatter(lsof, ps);
311
+ }
312
+ catch {
313
+ return null;
314
+ }
315
+ }
316
+ /**
317
+ * Gather every PID in the live session's tmux process trees (pane PIDs plus
318
+ * their descendants). Used to tell the session's own service processes apart
319
+ * from foreign squatters. Returns an empty set when tmux/ps are unavailable or
320
+ * the session has no panes — the caller then treats occupied ports as
321
+ * unidentified rather than owned.
322
+ */
323
+ function sessionProcessTree(sessionName) {
324
+ let panePids;
325
+ try {
326
+ const out = execFileSync("tmux", ["list-panes", "-s", "-t", sessionName, "-F", "#{pane_pid}"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
327
+ panePids = out
328
+ .trim()
329
+ .split("\n")
330
+ .map((l) => Number(l.trim()))
331
+ .filter((n) => Number.isInteger(n));
332
+ }
333
+ catch {
334
+ return new Set();
335
+ }
336
+ if (panePids.length === 0)
337
+ return new Set();
338
+ let psOut = "";
339
+ try {
340
+ psOut = execFileSync("ps", ["-eo", "pid=,ppid="], {
341
+ encoding: "utf8",
342
+ stdio: ["ignore", "pipe", "ignore"],
343
+ });
344
+ }
345
+ catch {
346
+ // No process table — the pane PIDs themselves are still session-owned.
347
+ return new Set(panePids);
348
+ }
349
+ return collectSessionPids(panePids, psOut);
350
+ }
351
+ export const tmuxCheck = {
352
+ id: "tmux",
353
+ title: "tmux",
354
+ phases: ["preflight", "ondemand"],
355
+ async run() {
356
+ try {
357
+ execFileSync("which", ["tmux"], { stdio: "ignore" });
358
+ return { status: "ok", summary: "installed" };
359
+ }
360
+ catch {
361
+ return {
362
+ status: "error",
363
+ summary: "tmux is not installed",
364
+ remediation: "brew install tmux",
365
+ };
366
+ }
367
+ },
368
+ };
369
+ export const portsCheck = {
370
+ id: "ports",
371
+ title: "Ports",
372
+ phases: ["preflight", "ondemand"],
373
+ async run(ctx) {
374
+ const services = ctx.sessionState?.devOutput.services;
375
+ if (!services || Object.keys(services).length === 0) {
376
+ // Preflight (no session): ports are validated at allocation/launch time.
377
+ return {
378
+ status: "ok",
379
+ summary: "no session — ports are checked at allocation time",
380
+ };
381
+ }
382
+ // Mid-session the session's OWN services should be listening on their
383
+ // allocated ports. Classify each port against the session's process tree so
384
+ // healthy self-owned ports don't read as squatters (MESH-2015).
385
+ const sessionPids = sessionProcessTree(ctx.sessionName);
386
+ const foreign = []; // held by a process outside the session
387
+ const dead = []; // nothing listening — service died/isn't up
388
+ const unknown = []; // occupied but the owner couldn't be resolved
389
+ for (const [name, svc] of Object.entries(services)) {
390
+ const free = await isPortFree(svc.port);
391
+ const who = free ? null : whoHasPort(svc.port);
392
+ const ownership = classifyPortListener(who?.pid ?? null, sessionPids);
393
+ if (ownership === "owned")
394
+ continue;
395
+ if (ownership === "foreign") {
396
+ foreign.push(`${name} :${svc.port} held by ${who.command} (pid ${who.pid})`);
397
+ continue;
398
+ }
399
+ // `dead` from the helper (null listener pid): either the port is
400
+ // genuinely free (service down) or it's occupied but the owner couldn't
401
+ // be resolved via lsof.
402
+ if (free) {
403
+ dead.push(`${name} :${svc.port} not listening`);
404
+ }
405
+ else {
406
+ unknown.push(`${name} :${svc.port} in use (owner unknown)`);
407
+ }
408
+ }
409
+ const total = Object.keys(services).length;
410
+ if (foreign.length === 0 && dead.length === 0 && unknown.length === 0) {
411
+ return {
412
+ status: "ok",
413
+ summary: `all ${total} service port(s) owned by this session`,
414
+ };
415
+ }
416
+ const parts = [];
417
+ if (foreign.length)
418
+ parts.push(`${foreign.length} squatted by another process`);
419
+ if (dead.length)
420
+ parts.push(`${dead.length} not listening (service down)`);
421
+ if (unknown.length)
422
+ parts.push(`${unknown.length} in use by an unidentified process`);
423
+ const remediation = foreign.length > 0
424
+ ? "stop the squatting process (or its SSH tunnel), then `mesh dev restart <svc>`"
425
+ : dead.length > 0
426
+ ? "restart the stopped service: `mesh dev restart <svc>`"
427
+ : "identify the port owner (`lsof -i :<port>`), then `mesh dev restart <svc>`";
428
+ return {
429
+ // A real squatter or a down service is actionable → error; an
430
+ // unidentifiable occupant alone is only a warning.
431
+ status: foreign.length > 0 || dead.length > 0 ? "error" : "warn",
432
+ summary: `service ports: ${parts.join(", ")}`,
433
+ detail: [...foreign, ...dead, ...unknown].join("; "),
434
+ remediation,
435
+ };
436
+ },
437
+ };
438
+ // ============================================================================
439
+ // Check: config staleness (config-staleness)
440
+ // ============================================================================
441
+ /** True when the stack config file changed after the session was launched. */
442
+ export function isConfigStale(configMtimeMs, startedAtIso) {
443
+ return configMtimeMs > Date.parse(startedAtIso);
444
+ }
445
+ export const configStalenessCheck = {
446
+ // Preflight runs *at* launch, so nothing can be stale-relative-to-launch yet
447
+ // — this only makes sense mid-session.
448
+ id: "config-staleness",
449
+ title: "Config freshness",
450
+ phases: ["ondemand"],
451
+ async run(ctx) {
452
+ const state = ctx.sessionState;
453
+ if (!state) {
454
+ return { status: "ok", summary: "no session — config read fresh at launch" };
455
+ }
456
+ const configPath = path.join(ctx.appRoot, `Pulumi.${ctx.stack}.yaml`);
457
+ if (!fs.existsSync(configPath)) {
458
+ return {
459
+ status: "ok",
460
+ summary: `no Pulumi.${ctx.stack}.yaml to compare`,
461
+ };
462
+ }
463
+ const mtimeMs = fs.statSync(configPath).mtimeMs;
464
+ if (isConfigStale(mtimeMs, state.startedAt)) {
465
+ return {
466
+ status: "warn",
467
+ summary: `Pulumi.${ctx.stack}.yaml changed since launch — a restart won't pick this up`,
468
+ detail: `config mtime ${new Date(mtimeMs).toISOString()} > session start ${state.startedAt}`,
469
+ remediation: `mesh deploy up --stack ${ctx.stack} && mesh dev`,
470
+ };
471
+ }
472
+ return { status: "ok", summary: "stack config unchanged since launch" };
473
+ },
474
+ };
475
+ export const worktreeCheck = {
476
+ id: "worktree",
477
+ title: "Worktree",
478
+ phases: ["preflight", "ondemand"],
479
+ async run(ctx) {
480
+ const services = ctx.sessionState?.devOutput.services;
481
+ if (!services) {
482
+ return { status: "ok", summary: "no session — worktree checked at launch" };
483
+ }
484
+ let repoRoot;
485
+ try {
486
+ repoRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
487
+ cwd: ctx.appRoot,
488
+ encoding: "utf8",
489
+ stdio: ["ignore", "pipe", "ignore"],
490
+ }).trim();
491
+ }
492
+ catch {
493
+ return {
494
+ status: "warn",
495
+ summary: "could not resolve git worktree root for the app",
496
+ };
497
+ }
498
+ const foreign = Object.entries(services)
499
+ .filter(([, svc]) => svc.src && !sharesWorktreeRoot(svc.src, repoRoot))
500
+ .map(([name, svc]) => `${name} → ${svc.src}`);
501
+ if (foreign.length === 0) {
502
+ return {
503
+ status: "ok",
504
+ summary: `all services resolve under ${repoRoot}`,
505
+ };
506
+ }
507
+ return {
508
+ status: "warn",
509
+ summary: `${foreign.length} service(s) resolve outside this worktree — you may be running another worktree's code`,
510
+ detail: foreign.join("; "),
511
+ remediation: "relaunch from this worktree: `mesh dev` (re-reads stack output paths)",
512
+ };
513
+ },
514
+ };
515
+ // ============================================================================
516
+ // Checks: tunnels + temporal (connectivity)
517
+ // ============================================================================
518
+ /** Resolve `true` iff a TCP connection to host:port succeeds within `timeoutMs`. */
519
+ export function canConnect(host, port, timeoutMs = 800) {
520
+ return new Promise((resolve) => {
521
+ const socket = new net.Socket();
522
+ let settled = false;
523
+ const done = (ok) => {
524
+ if (settled)
525
+ return;
526
+ settled = true;
527
+ socket.destroy();
528
+ resolve(ok);
529
+ };
530
+ socket.setTimeout(timeoutMs);
531
+ socket.once("connect", () => done(true));
532
+ socket.once("timeout", () => done(false));
533
+ socket.once("error", () => done(false));
534
+ socket.connect(port, host);
535
+ });
536
+ }
537
+ function isLocalHost(host) {
538
+ return host === "localhost" || host === "127.0.0.1" || host === "::1";
539
+ }
540
+ export const tunnelsCheck = {
541
+ // On-demand only: pre-launch the tunnels don't exist yet, and dialing the
542
+ // VPC endpoints they'll use would false-positive on an SSM-only machine.
543
+ id: "tunnels",
544
+ title: "Tunnels",
545
+ phases: ["ondemand"],
546
+ async run(ctx) {
547
+ const tunnels = ctx.sessionState?.devOutput.tunnels;
548
+ if (!tunnels || Object.keys(tunnels).length === 0) {
549
+ return { status: "ok", summary: "no tunnels in this session" };
550
+ }
551
+ // Only dial the SSM-forwarded (localhost) tunnels — those are the ones that
552
+ // die with "broken pipe → ECONNREFUSED". VPC-direct tunnels are reachable
553
+ // only over VPN and would false-positive here.
554
+ const local = Object.entries(tunnels).filter(([, t]) => isLocalHost(t.host));
555
+ if (local.length === 0) {
556
+ return {
557
+ status: "ok",
558
+ summary: `${Object.keys(tunnels).length} tunnel(s), all VPC-direct (not dialed)`,
559
+ };
560
+ }
561
+ const dead = [];
562
+ for (const [name, t] of local) {
563
+ if (!(await canConnect(t.host, t.port)))
564
+ dead.push(`${name} (${t.host}:${t.port})`);
565
+ }
566
+ if (dead.length === 0) {
567
+ return { status: "ok", summary: `${local.length} SSM tunnel(s) live` };
568
+ }
569
+ return {
570
+ status: "error",
571
+ summary: `${dead.length} SSM tunnel(s) down`,
572
+ detail: dead.join("; "),
573
+ remediation: "relaunch to re-establish tunnels: `mesh dev`",
574
+ };
575
+ },
576
+ };
577
+ export const temporalCheck = {
578
+ // On-demand only: reachability depends on the session's tunnel being up.
579
+ id: "temporal",
580
+ title: "Temporal",
581
+ phases: ["ondemand"],
582
+ async run(ctx) {
583
+ const state = ctx.sessionState;
584
+ const t = state?.devOutput.tunnels["temporal"];
585
+ if (!state || !t) {
586
+ return { status: "ok", summary: "no temporal tunnel in this session" };
587
+ }
588
+ const addr = `${t.host}:${t.port}`;
589
+ if (!(await canConnect(t.host, t.port))) {
590
+ return {
591
+ status: "error",
592
+ summary: `Temporal frontend unreachable at ${addr}`,
593
+ remediation: "tunnel likely down — relaunch: `mesh dev`",
594
+ };
595
+ }
596
+ const p = state.devOutput.platform;
597
+ const app = state.devOutput.app;
598
+ if (!p || !app) {
599
+ return { status: "ok", summary: `frontend reachable at ${addr} (namespace unknown)` };
600
+ }
601
+ const namespace = `${p.tenant}-${p.env}-${app}`;
602
+ try {
603
+ const { Connection } = await import("@temporalio/client");
604
+ const connection = await Connection.connect({
605
+ address: addr,
606
+ connectTimeout: "3s",
607
+ });
608
+ try {
609
+ await connection.workflowService.describeNamespace({ namespace });
610
+ return { status: "ok", summary: `frontend reachable; namespace ${namespace} present` };
611
+ }
612
+ finally {
613
+ await connection.close().catch(() => { });
614
+ }
615
+ }
616
+ catch (err) {
617
+ const code = err.code;
618
+ // gRPC status: 5 NOT_FOUND, 7 PERMISSION_DENIED, 16 UNAUTHENTICATED.
619
+ if (code === 5) {
620
+ return {
621
+ status: "error",
622
+ summary: `namespace ${namespace} not found on the server`,
623
+ remediation: `verify the app is deployed to this env (mesh deploy up --stack ${ctx.stack})`,
624
+ };
625
+ }
626
+ if (code === 7 || code === 16) {
627
+ return {
628
+ status: "ok",
629
+ summary: `frontend reachable; namespace ${namespace} auth-gated (not verified)`,
630
+ };
631
+ }
632
+ return {
633
+ status: "warn",
634
+ summary: "frontend reachable but namespace check errored",
635
+ detail: err instanceof Error ? err.message : String(err),
636
+ };
637
+ }
638
+ },
639
+ };
640
+ // ============================================================================
641
+ // Registry + runner
642
+ // ============================================================================
643
+ /** All checks, in display order. */
644
+ export const ALL_CHECKS = [
645
+ tmuxCheck,
646
+ credsCheck,
647
+ registryCheck,
648
+ configStalenessCheck,
649
+ portsCheck,
650
+ worktreeCheck,
651
+ tunnelsCheck,
652
+ temporalCheck,
653
+ ];
654
+ /**
655
+ * Run the full on-demand doctor against a prebuilt context, print the report,
656
+ * and return the aggregate status. The caller (the `mesh dev doctor`
657
+ * subcommand) owns process exit.
658
+ */
659
+ export async function runDoctor(ctx, opts) {
660
+ const results = await runChecks(ctx, "ondemand", ALL_CHECKS);
661
+ const status = aggregateStatus(results.map((r) => r.result));
662
+ if (opts.json) {
663
+ console.log(renderJson(results));
664
+ }
665
+ else {
666
+ console.log(renderHuman(results));
667
+ console.log(`\n${ICONS[status]} overall: ${status}`);
668
+ }
669
+ return status;
670
+ }
671
+ //# sourceMappingURL=dev-doctor.js.map