@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,756 @@
1
+ /**
2
+ * `mesh dev --externals` (alias `--mock`) — external-service realizations
3
+ * for a local dev session (MESH-1885, design C4).
4
+ *
5
+ * Apps DECLARE how each external dependency is realized locally in the root
6
+ * package.json under `mesh.externals` (legacy key `mesh.mocks` still read).
7
+ * Every declaration runs in exactly one of three MODES:
8
+ *
9
+ * mock — the service is MIMICKED: no real instance exists locally or
10
+ * remotely. Realized from an OpenAPI spec (`openapi` → Prism
11
+ * mock server) or a hand-written mock process (`src`).
12
+ * local — a LOCAL VERSION of the real service runs on this machine,
13
+ * e.g. a vendor-shaped database replica. Realized from a docker
14
+ * compose file (`compose` + published `port`).
15
+ * remote — the session connects to the ACTUAL external service: either
16
+ * the vendor's real endpoint with inline `credentials` (e.g. a
17
+ * sandbox), or the external service as configured in the app's
18
+ * tenant environment (`remote.env`, e.g. dev — credentials are
19
+ * pulled from that environment's secret).
20
+ *
21
+ * "mesh": {
22
+ * "externals": {
23
+ * "plaid-db": { // ── local (default) ──
24
+ * "mode": "local",
25
+ * "compose": "libs/plaid-db/docker/docker-compose.yml",
26
+ * "port": 1433, // REQUIRED: the published port
27
+ * "probe": "tcp", // non-HTTP uptime probe
28
+ * "external": "plaid-db",
29
+ * "credentials": { // {{host}}/{{port}} → localhost:1433
30
+ * "host": "{{host}}", "port": "{{port}}",
31
+ * "name": "Plaid_Clean", "username": "sa",
32
+ * "password": "{{env:PLAID_DB_PASSWORD}}"
33
+ * },
34
+ * "remote": { "env": "dev" } // `--externals plaid-db=remote`
35
+ * }, // pulls the dev-configured secret
36
+ * "plaid": { // ── remote ──
37
+ * "mode": "remote",
38
+ * "external": "plaid", // the vendor's REAL sandbox
39
+ * "displayName": "Plaid", // the Hub shows the real name…
40
+ * "type": "api", // …and the integration type
41
+ * "docs": "https://plaid.com/docs/api/", // vendor reference docs
42
+ * "credentials": {
43
+ * "endpoint": "https://sandbox.plaid.com",
44
+ * "client_id": "{{env:PLAID_CLIENT_ID}}",
45
+ * "secret": "{{env:PLAID_SECRET}}"
46
+ * }
47
+ * },
48
+ * "vendorpay": { // ── mock ──
49
+ * "mode": "mock",
50
+ * "openapi": "api/docs/spec/openapi.json", // spec → Prism emulation
51
+ * "external": "vendorpay",
52
+ * "credentials": { "baseUrl": "{{url}}", "apiKey": "mock" }
53
+ * }
54
+ * }
55
+ * }
56
+ *
57
+ * `mode` picks the DEFAULT; a declaration may carry more than one
58
+ * realization (e.g. compose + remote.env) and the CLI overrides per
59
+ * external at run time: `mesh dev --externals plaid-db=remote,plaid`.
60
+ * Declarations without `mode` infer it from their fields (compose → local,
61
+ * openapi/src → mock, sandbox/credentials-only → remote), so pre-mode
62
+ * declarations keep working (`sandbox: true` is a deprecated alias for
63
+ * mode remote).
64
+ *
65
+ * All credential values support `{{env:VAR}}` interpolation from the
66
+ * developer's environment, so real secrets (sandbox keys, DB passwords)
67
+ * never land in package.json.
68
+ *
69
+ * Realization: mock externals join the SAME orchestration as services named
70
+ * mock-{name} (ports, env files, tmux windows, Loki log pipeline); local
71
+ * externals come up via `docker compose up -d --wait` before the app's
72
+ * services and are torn down on `mesh dev --kill`; remote externals spawn
73
+ * nothing. In every mode the ExternalService credential secret is seeded at
74
+ * mesh/{tenant}/{env}/external/{name} so the app's resolveCredentials()
75
+ * path runs UNCHANGED, the vendor is registered in the local Hub's
76
+ * external-services view, and a blackbox uptime probe watches the
77
+ * realization — remote endpoints included.
78
+ */
79
+ import { execFileSync } from "child_process";
80
+ import * as fs from "fs";
81
+ import * as path from "path";
82
+ import { logInfo, logSuccess, logWarn } from "../../utils/log.js";
83
+ import { MeshCliError } from "../../utils/errors.js";
84
+ import { LOCAL_ENV, LOCAL_AWS_CONFIG } from "./seed.js";
85
+ import { probeTcp, upsertLocalSecret } from "./helpers.js";
86
+ import { localProbesDir } from "./stack.js";
87
+ const EXTERNAL_MODES = ["mock", "local", "remote"];
88
+ const DECL_DOCS = { docs: 'package.json → "mesh": { "externals": { … } }' };
89
+ /**
90
+ * Resolve + validate an external's mode: explicit `mode` (or the one
91
+ * inferred from the realization fields), checked against the fields that
92
+ * mode needs. Pre-mode declarations infer: compose → local, openapi/src →
93
+ * mock, sandbox/credentials-only → remote.
94
+ */
95
+ export function externalMode(name, decl) {
96
+ const mode = decl.mode ??
97
+ (decl.compose
98
+ ? "local"
99
+ : decl.openapi || decl.src
100
+ ? "mock"
101
+ : decl.sandbox || decl.credentials || decl.remote
102
+ ? "remote"
103
+ : undefined);
104
+ if (!mode || !EXTERNAL_MODES.includes(mode)) {
105
+ throw new MeshCliError(`External '${name}': cannot determine mode — set "mode" to mock | local | remote (or declare openapi/src, compose, or credentials).`, { remediation: DECL_DOCS });
106
+ }
107
+ if (mode === "local") {
108
+ if (!decl.compose) {
109
+ throw new MeshCliError(`External '${name}': local mode requires "compose" — the docker compose file running the local version of the service.`, { remediation: DECL_DOCS });
110
+ }
111
+ if (typeof decl.port !== "number") {
112
+ throw new MeshCliError(`External '${name}': local (compose) mode requires "port" — the port the compose file publishes on localhost.`, { remediation: DECL_DOCS });
113
+ }
114
+ }
115
+ if (mode === "mock" && !decl.openapi && !decl.src) {
116
+ throw new MeshCliError(`External '${name}': mock mode emulates the service — declare "openapi" (spec → Prism mock) or "src" (mock process).`, { remediation: DECL_DOCS });
117
+ }
118
+ if (mode === "remote" && !remoteInlineCredentials(decl) && !decl.external) {
119
+ throw new MeshCliError(`External '${name}': remote mode needs a credential source — inline "credentials", "remote": { "env": … }, or "external" (pulls that environment's configured secret).`, { remediation: DECL_DOCS });
120
+ }
121
+ return mode;
122
+ }
123
+ /**
124
+ * Parse a `--externals` value — a comma-separated list of `name` or
125
+ * `name=mode` entries. An explicit mode overrides that declaration's
126
+ * default FOR THIS RUN; a bare name keeps the declared default.
127
+ *
128
+ * Names are returned in the order typed, de-duplicated (the last mode
129
+ * given for a name wins, so `--externals plaid,plaid=remote` is remote).
130
+ */
131
+ export function parseExternalsSelection(input) {
132
+ const names = [];
133
+ const overrides = new Map();
134
+ for (const entry of input.split(",").map((s) => s.trim()).filter(Boolean)) {
135
+ const [rawName, mode, ...rest] = entry.split("=").map((s) => s.trim());
136
+ const name = rawName ?? "";
137
+ if (!name) {
138
+ throw new MeshCliError(`Bad --externals entry '${entry}' — expected name or name=mode with mode ∈ ${EXTERNAL_MODES.join(" | ")}.`, { remediation: { command: "mesh dev --externals plaid-db=remote,plaid" } });
139
+ }
140
+ if (mode !== undefined) {
141
+ if (rest.length > 0 || !EXTERNAL_MODES.includes(mode)) {
142
+ throw new MeshCliError(`Bad --externals entry '${entry}' — expected name or name=mode with mode ∈ ${EXTERNAL_MODES.join(" | ")}.`, { remediation: { command: "mesh dev --externals plaid-db=remote,plaid" } });
143
+ }
144
+ overrides.set(name, mode);
145
+ }
146
+ if (!names.includes(name))
147
+ names.push(name);
148
+ }
149
+ return { names, overrides };
150
+ }
151
+ /**
152
+ * The inline credential template a remote-mode run should use, if any:
153
+ * `remote.credentials` always wins; top-level `credentials` count only when
154
+ * the declaration has no local/mock realization (otherwise they are that
155
+ * realization's template, full of {{host}}/{{port}} tokens that only mean
156
+ * something against a local endpoint).
157
+ */
158
+ export function remoteInlineCredentials(decl) {
159
+ if (decl.remote?.credentials)
160
+ return decl.remote.credentials;
161
+ if (decl.credentials && !decl.compose && !decl.openapi && !decl.src)
162
+ return decl.credentials;
163
+ return undefined;
164
+ }
165
+ /** Whether this declaration runs as a mock-{name} service in the dev
166
+ * orchestration (mock mode) vs outside it (local compose / remote). */
167
+ export function isServiceMode(name, decl) {
168
+ return externalMode(name, decl) === "mock";
169
+ }
170
+ /**
171
+ * Interpolate one credential value: {{url}}/{{host}}/{{port}} from the
172
+ * stand-in's endpoint, {{env:VAR}} from the developer's environment
173
+ * (missing env vars are a hard error — a half-seeded secret is worse).
174
+ */
175
+ export function interpolateCredentialValue(raw, endpoint, env = process.env) {
176
+ let value = raw.replace(/\{\{env:([A-Za-z_][A-Za-z0-9_]*)\}\}/g, (_, name) => {
177
+ const resolved = env[name];
178
+ if (resolved === undefined) {
179
+ throw new MeshCliError(`Credential value references {{env:${name}}} but ${name} is not set in your environment.`, { remediation: { command: `export ${name}=… # then re-run mesh dev` } });
180
+ }
181
+ return resolved;
182
+ });
183
+ if (endpoint) {
184
+ value = value
185
+ .replaceAll("{{url}}", endpoint.url)
186
+ .replaceAll("{{host}}", endpoint.host)
187
+ .replaceAll("{{port}}", String(endpoint.port));
188
+ }
189
+ return value;
190
+ }
191
+ /** The Prism mock command for an openapi-mode declaration. `$PORT` expands
192
+ * in the service's launch shell (tmux and docker runners both source the
193
+ * env file), so port reallocation keeps working. */
194
+ export function openapiMockCommand(decl) {
195
+ return (decl.command ?? [
196
+ "npx",
197
+ "-y",
198
+ "@stoplight/prism-cli@5",
199
+ "mock",
200
+ "-p",
201
+ "$PORT",
202
+ "-h",
203
+ "0.0.0.0",
204
+ decl.openapi,
205
+ ]);
206
+ }
207
+ /** Compose-mode project name — pinned so `--kill` can tear down exactly
208
+ * what `up` created, regardless of the compose file's own `name:`. */
209
+ export function composeProjectName(sessionName, mockName) {
210
+ return `mesh-ext-${sessionName}-${mockName}`.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
211
+ }
212
+ /** Wait until the external's published port accepts TCP (bounded). */
213
+ async function waitForPort(port, timeoutMs) {
214
+ const deadline = Date.now() + timeoutMs;
215
+ while (Date.now() < deadline) {
216
+ if (await probeTcp(port, { timeoutMs: 2000 }))
217
+ return true;
218
+ await new Promise((r) => setTimeout(r, 2000));
219
+ }
220
+ return false;
221
+ }
222
+ /**
223
+ * Decide how to realize a compose external, given what already exists.
224
+ *
225
+ * OWNERSHIP FIRST: a port probe can't tell WHO serves the port, and the
226
+ * ordinary flow re-runs `mesh dev` against a session whose compose externals
227
+ * are deliberately left running. Adopting there would drop the ref from
228
+ * session state and permanently orphan the checkout's own container, so our
229
+ * own realization takes the normal `up` path (idempotent, ref re-recorded)
230
+ * and only a foreign one is adopted.
231
+ *
232
+ * IDENTITY SECOND: a foreign publisher is only adopted when it is a service
233
+ * THIS compose file defines. Well-known ports (1433/5432/22) are exactly
234
+ * where a native install or an unrelated container turns up, and adopting one
235
+ * silently seeds the app's credentials against the wrong database.
236
+ */
237
+ export async function planComposeExternal(composeFile, project, port, probes) {
238
+ if (probes.ownsRunning(composeFile, project))
239
+ return { action: "up" };
240
+ if (await probes.portServed(port)) {
241
+ const publisher = probes.portPublisher(composeFile, port);
242
+ if (!publisher || !publisher.definedByFile) {
243
+ return { action: "conflict", ...(publisher ? { container: publisher.container } : {}) };
244
+ }
245
+ return { action: "adopt-served", container: publisher.container };
246
+ }
247
+ // Pinned container_name held by a STOPPED container from another checkout/
248
+ // project would make `up` fail with a name conflict — start that container
249
+ // instead. (A RUNNING one is caught by the port probe above.)
250
+ const foreign = probes.foreignPinned(composeFile, project);
251
+ if (foreign)
252
+ return { action: "adopt-stopped", container: foreign };
253
+ return { action: "up" };
254
+ }
255
+ /** Real docker/network probes — replaced in tests. */
256
+ const dockerProbes = {
257
+ ownsRunning: ownsRunningRealization,
258
+ portServed: (port) => probeTcp(port, { timeoutMs: 2000 }),
259
+ foreignPinned: foreignPinnedContainer,
260
+ portPublisher: composeFilePortPublisher,
261
+ };
262
+ /**
263
+ * Bring a compose-mode external up (idempotent; waits for healthchecks —
264
+ * vendor DBs restoring seed data can take a while, hence the timeout).
265
+ *
266
+ * ADOPTION: the same external is often realized by more than one checkout —
267
+ * e.g. a vendor-DB compose ported from a legacy repo pins `container_name`
268
+ * and a host port, so whichever checkout created it first blocks every other
269
+ * `docker compose up` (name + port conflicts). An external service is by
270
+ * definition outside the app, so if ANOTHER checkout already serves its
271
+ * published port — or left a stopped container holding the pinned name —
272
+ * this ADOPTS that realization instead of failing: skip/`docker start`, wait
273
+ * for the port, and return undefined so `--kill` never tears down what
274
+ * another checkout owns. Our OWN realization is never adopted (see
275
+ * `planComposeExternal`) — it stays recorded so `--kill` still cleans it up,
276
+ * and a publisher this compose file doesn't define is never adopted either
277
+ * (it would seed the app's credentials against the wrong service).
278
+ */
279
+ export async function composeExternalUp(appRoot, sessionName, name, decl, probes = dockerProbes) {
280
+ const composeFile = path.resolve(appRoot, decl.compose);
281
+ if (!fs.existsSync(composeFile)) {
282
+ throw new MeshCliError(`External '${name}': compose file not found at ${composeFile}.`, {
283
+ remediation: { docs: 'package.json → "mesh": { "externals": { … } }' },
284
+ });
285
+ }
286
+ const project = composeProjectName(sessionName, name);
287
+ const plan = await planComposeExternal(composeFile, project, decl.port, probes);
288
+ if (plan.action === "conflict") {
289
+ throw new MeshCliError(`External '${name}': localhost:${decl.port} is already served by ${plan.container ? `container '${plan.container}'` : "a process outside docker"}, which ${decl.compose} does not define — refusing to seed '${name}' credentials against it.`, {
290
+ remediation: {
291
+ command: `docker ps --filter publish=${decl.port} # stop it, or change the declared port`,
292
+ },
293
+ });
294
+ }
295
+ if (plan.action === "adopt-served") {
296
+ logInfo(`External '${name}' already served on localhost:${decl.port} by container '${plan.container}' — adopting it (started outside this checkout; \`mesh dev --kill\` won't touch it).`);
297
+ return undefined;
298
+ }
299
+ if (plan.action === "adopt-stopped") {
300
+ const { container } = plan;
301
+ logInfo(`External '${name}': container '${container}' exists from another checkout — starting and adopting it.`);
302
+ execFileSync("docker", ["start", container], { stdio: ["ignore", "ignore", "inherit"] });
303
+ if (!(await waitForPort(decl.port, 300_000))) {
304
+ throw new MeshCliError(`External '${name}': adopted container '${container}' never served localhost:${decl.port}.`, { remediation: { command: `docker logs ${container} # then: docker rm -f ${container} and re-run` } });
305
+ }
306
+ return undefined;
307
+ }
308
+ execFileSync("docker", ["compose", "-p", project, "-f", composeFile, "up", "-d", "--wait", "--wait-timeout", "300"], { stdio: ["ignore", "inherit", "inherit"] });
309
+ return { name, composeFile, project };
310
+ }
311
+ /** Whether OUR compose project already has a container running for this file.
312
+ * `docker compose ps` (running only) covers services WITHOUT a pinned
313
+ * `container_name` too, which the label-inspect path below structurally
314
+ * cannot — an unpinned service of ours is just `<project>-<svc>-1`. */
315
+ function ownsRunningRealization(composeFile, project) {
316
+ try {
317
+ const out = execFileSync("docker", ["compose", "-p", project, "-f", composeFile, "ps", "--format", "json"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
318
+ // Compose emits either a JSON array or NDJSON depending on version;
319
+ // "has any row" is all we need from it.
320
+ return out.length > 0 && out !== "[]";
321
+ }
322
+ catch {
323
+ // ps hiccup — treat as "not ours" and let the normal paths decide.
324
+ return false;
325
+ }
326
+ }
327
+ /** The compose file's rendered service map, or undefined when it can't be
328
+ * rendered (docker missing / bad file — callers decide what that means). */
329
+ function composeServices(composeFile) {
330
+ try {
331
+ const config = JSON.parse(execFileSync("docker", ["compose", "-f", composeFile, "config", "--format", "json"], {
332
+ encoding: "utf-8",
333
+ stdio: ["ignore", "pipe", "ignore"],
334
+ }));
335
+ const services = config?.services;
336
+ return services && typeof services === "object" ? services : undefined;
337
+ }
338
+ catch {
339
+ return undefined;
340
+ }
341
+ }
342
+ function portPublisher(port) {
343
+ try {
344
+ const out = execFileSync("docker", [
345
+ "ps",
346
+ "--filter",
347
+ `publish=${port}`,
348
+ "--format",
349
+ '{{.Names}}\t{{.Image}}\t{{.Label "com.docker.compose.project"}}\t{{.Label "com.docker.compose.service"}}',
350
+ ], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
351
+ const line = out
352
+ .split("\n")
353
+ .map((l) => l.trim())
354
+ .filter(Boolean)[0];
355
+ if (!line)
356
+ return undefined;
357
+ const [container, image, project, service] = line.split("\t");
358
+ if (!container)
359
+ return undefined;
360
+ return {
361
+ container,
362
+ image: image || undefined,
363
+ project: project || undefined,
364
+ service: service || undefined,
365
+ };
366
+ }
367
+ catch {
368
+ return undefined;
369
+ }
370
+ }
371
+ /** Whether the container publishing the declared port is a service THIS
372
+ * compose file defines — the evidence adoption requires. Matches on the
373
+ * pinned `container_name`, or on the compose service name (cross-checked
374
+ * against the image when both sides declare one, so an unrelated project's
375
+ * service that happens to be called `db` isn't mistaken for ours). */
376
+ function composeFileOwns(composeFile, publisher) {
377
+ if (!publisher)
378
+ return false;
379
+ const services = composeServices(composeFile);
380
+ if (!services)
381
+ return false; // no identity evidence — don't adopt
382
+ for (const [key, svc] of Object.entries(services)) {
383
+ if (svc?.container_name && svc.container_name === publisher.container)
384
+ return true;
385
+ if (publisher.service && publisher.service === key) {
386
+ if (!svc?.image || !publisher.image || svc.image === publisher.image)
387
+ return true;
388
+ }
389
+ }
390
+ return false;
391
+ }
392
+ /** `ComposeExternalProbes.portPublisher` — who serves the declared port, and
393
+ * whether this compose file defines it. Undefined when docker publishes
394
+ * nothing there (a native process on the port, or no docker at all), which
395
+ * `planComposeExternal` treats as a conflict rather than an adoption. */
396
+ function composeFilePortPublisher(composeFile, port) {
397
+ const publisher = portPublisher(port);
398
+ if (!publisher)
399
+ return undefined;
400
+ return { container: publisher.container, definedByFile: composeFileOwns(composeFile, publisher) };
401
+ }
402
+ /** Find a container holding a `container_name` this compose file pins, when
403
+ * it belongs to a different compose project (another checkout's realization
404
+ * of the same external). Returns the container name, or undefined. */
405
+ function foreignPinnedContainer(composeFile, project) {
406
+ // config render hiccup — fall through to the normal `up`, which reports
407
+ // real errors itself.
408
+ for (const svc of Object.values(composeServices(composeFile) ?? {})) {
409
+ const pinned = svc?.container_name;
410
+ if (!pinned)
411
+ continue;
412
+ try {
413
+ const owner = execFileSync("docker", ["inspect", pinned, "--format", '{{ index .Config.Labels "com.docker.compose.project" }}'], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
414
+ if (owner !== project)
415
+ return pinned;
416
+ }
417
+ catch {
418
+ // no container by that name — nothing to adopt for this service
419
+ }
420
+ }
421
+ return undefined;
422
+ }
423
+ /** Tear down compose-mode externals recorded in session state. Volumes are
424
+ * kept (vendor DB data survives sessions, like the cloud external it
425
+ * stands in for); best-effort per project. */
426
+ export function composeExternalsDown(refs) {
427
+ for (const ref of refs ?? []) {
428
+ try {
429
+ execFileSync("docker", ["compose", "-p", ref.project, "-f", ref.composeFile, "down", "--remove-orphans"], {
430
+ stdio: ["ignore", "inherit", "inherit"],
431
+ });
432
+ }
433
+ catch {
434
+ /* best effort — the file may be gone or docker down */
435
+ }
436
+ }
437
+ }
438
+ /** Same heuristic the schema seed uses: which credential keys are secret. */
439
+ const SECRET_KEY_RE = /key|secret|password|token/i;
440
+ /** Read the app's declared externals (root package.json → mesh.externals;
441
+ * legacy key mesh.mocks still read, externals wins on name collision). */
442
+ export function readLocalMocks(appRoot) {
443
+ try {
444
+ const pkg = JSON.parse(fs.readFileSync(path.join(appRoot, "package.json"), "utf-8"));
445
+ const mocks = pkg?.mesh?.mocks;
446
+ const externals = pkg?.mesh?.externals;
447
+ return {
448
+ ...(mocks && typeof mocks === "object" ? mocks : {}),
449
+ ...(externals && typeof externals === "object" ? externals : {}),
450
+ };
451
+ }
452
+ catch {
453
+ return {};
454
+ }
455
+ }
456
+ export function externalSecretPath(tenant, external) {
457
+ return `mesh/${tenant}/${LOCAL_ENV}/external/${external}`;
458
+ }
459
+ /**
460
+ * Pull an external service's credentials as configured in the app's tenant
461
+ * environment (the CLOUD platform, not the local fabric) — secret
462
+ * mesh/{tenant}/{env}/external/{name}, read with the developer's ambient
463
+ * AWS credentials (AWS_PROFILE / SSO session for that tenant's account).
464
+ * When the shell carries no AWS identity at all, `profile` (from the
465
+ * declaration; MESH_AWS_PROFILE overrides) names the profile to use — the
466
+ * same convention repo pull scripts follow.
467
+ */
468
+ export async function fetchRemoteExternalCredentials(tenant, env, external, profile) {
469
+ const secretId = `mesh/${tenant}/${env}/external/${external}`;
470
+ const region = process.env.MESH_PLATFORM_REGION ?? process.env.AWS_REGION ?? "us-east-2";
471
+ const fallbackProfile = !process.env.AWS_ACCESS_KEY_ID && !process.env.AWS_PROFILE
472
+ ? (process.env.MESH_AWS_PROFILE ?? profile)
473
+ : undefined;
474
+ try {
475
+ const { SecretsManagerClient, GetSecretValueCommand } = await import("@aws-sdk/client-secrets-manager");
476
+ // The SDK's default chain reads AWS_PROFILE at first resolution — scope
477
+ // the fallback to this client's call (no ambient identity to clobber).
478
+ if (fallbackProfile)
479
+ process.env.AWS_PROFILE = fallbackProfile;
480
+ const sm = new SecretsManagerClient({ region });
481
+ let res;
482
+ try {
483
+ res = await sm.send(new GetSecretValueCommand({ SecretId: secretId }));
484
+ }
485
+ finally {
486
+ if (fallbackProfile)
487
+ delete process.env.AWS_PROFILE;
488
+ }
489
+ const parsed = JSON.parse(res.SecretString ?? "{}");
490
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
491
+ throw new Error("secret value is not a JSON object");
492
+ }
493
+ return Object.fromEntries(Object.entries(parsed).map(([key, v]) => [key, String(v)]));
494
+ }
495
+ catch (err) {
496
+ throw new MeshCliError(`External '${external}': could not pull remote credentials from the ${env} environment (${secretId}, region ${region}): ${err instanceof Error ? err.message : err}`, {
497
+ remediation: {
498
+ command: `AWS_PROFILE=<${tenant}-${env} profile> mesh dev … # or set "remote": { "profile": … } on the declaration / MESH_AWS_PROFILE`,
499
+ },
500
+ });
501
+ }
502
+ }
503
+ /** The prober runs inside the stack: anything addressed at the developer
504
+ * host must go through host.docker.internal. */
505
+ function hostForProber(target) {
506
+ return target.replace(/\b(?:localhost|127\.0\.0\.1)\b/, "host.docker.internal");
507
+ }
508
+ /**
509
+ * A credential's endpoint value as an HTTP probe target, or undefined when
510
+ * it isn't one. Credentials pulled from a tenant secret carry whatever the
511
+ * vendor configured — a JDBC/ODBC connection string, an `sftp://` URL, or
512
+ * an http URL with embedded userinfo. Anything non-http(s) falls through to
513
+ * the tcp branch, and userinfo is stripped: the target is written to a
514
+ * plaintext probe file and relabeled into Prometheus' `instance` label, so
515
+ * a password in it would leak into every `probe_*` series.
516
+ */
517
+ function httpProbeUrl(raw) {
518
+ if (!raw)
519
+ return undefined;
520
+ let parsed;
521
+ try {
522
+ parsed = new URL(raw);
523
+ }
524
+ catch {
525
+ return undefined;
526
+ }
527
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
528
+ return undefined;
529
+ parsed.username = "";
530
+ parsed.password = "";
531
+ return hostForProber(parsed.toString());
532
+ }
533
+ /** Reachability target for a non-HTTP integration, derived from its
534
+ * credentials: a host/port pair (databases, SFTP) or an S3 bucket endpoint
535
+ * (probed via TCP — no SDK or credentials needed, the technique CSP's
536
+ * network lib used). */
537
+ function derivedTcpTarget(creds) {
538
+ if (creds.host && creds.port)
539
+ return `${hostForProber(creds.host)}:${creds.port}`;
540
+ if (creds.bucket)
541
+ return `${creds.bucket}.s3.${creds.region || "us-east-1"}.amazonaws.com:443`;
542
+ return undefined;
543
+ }
544
+ /** The blackbox probe target for a realization, by mode: local/mock
545
+ * stand-ins are probed on the host (the prober runs inside the stack →
546
+ * host.docker.internal); remote externals are probed at their REAL
547
+ * endpoint, so the Hub shows actual vendor reachability. Every integration
548
+ * gets AT LEAST a reachability check when its credentials expose a target:
549
+ * an HTTP endpoint, a host(:port), or an S3 bucket (probed via TCP to the
550
+ * bucket endpoint — no SDK/credentials needed, the same technique CSP's
551
+ * network lib used). Undefined = nothing probeable in the credentials.
552
+ *
553
+ * Target and module come from ONE decision here so they can't drift: a
554
+ * misrouted probe just reports probe_success 0, indistinguishable from a
555
+ * genuinely down service. `tcp` yields host:port with no scheme/path and
556
+ * routes the scrape to blackbox's tcp_connect (the scrape config's default
557
+ * stays http_2xx); `http` GETs the target and expects 2xx. */
558
+ export function resolveProbeTarget(mode, decl, endpoint, creds) {
559
+ if (mode === "remote") {
560
+ // The endpoint comes from a TENANT SECRET, so it may be anything a
561
+ // vendor credential carries — a JDBC/ODBC connection string, or a URL
562
+ // with userinfo. Only a real http(s) URL is probeable, and its
563
+ // credentials must never reach the probe file (world-readable) or the
564
+ // Prometheus `instance` label.
565
+ const url = httpProbeUrl(creds.endpoint ?? creds.url ?? creds.baseUrl);
566
+ // Reachability fallbacks for non-HTTP integrations: host:port
567
+ // (databases, SFTP) or the S3 bucket endpoint (object storage).
568
+ const tcpTarget = derivedTcpTarget(creds);
569
+ if (decl.probe === "tcp" || (!url && tcpTarget)) {
570
+ if (!tcpTarget && decl.probe === "tcp") {
571
+ logWarn(`External '${decl.external ?? ""}': probe: "tcp" declared but the remote credentials carry no host/port (or bucket) to probe — no uptime probe registered.`);
572
+ }
573
+ return tcpTarget ? { target: tcpTarget, module: "tcp_connect" } : undefined;
574
+ }
575
+ // Remote endpoints rarely expose /health — default to the endpoint
576
+ // root (Plaid's sandbox, for one, answers 200 there).
577
+ return url ? { target: url.replace(/\/+$/, "") + (decl.healthPath ?? "") } : undefined;
578
+ }
579
+ if (!endpoint)
580
+ return undefined;
581
+ if (decl.probe === "tcp") {
582
+ return { target: `host.docker.internal:${endpoint.port}`, module: "tcp_connect" };
583
+ }
584
+ return {
585
+ target: hostForProber(endpoint.url).replace(/\/+$/, "") + (decl.healthPath ?? "/health"),
586
+ };
587
+ }
588
+ /** Hub display name: explicit `displayName`, else the external name
589
+ * title-cased on word boundaries ("plaid-db" → "Plaid Db"). */
590
+ export function externalDisplayName(name, decl) {
591
+ return (decl.displayName ??
592
+ name
593
+ .split(/[-_\s]+/)
594
+ .filter(Boolean)
595
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
596
+ .join(" "));
597
+ }
598
+ /** Normalize the decl's `docs` (string URL or {url, provider}) for the meta. */
599
+ export function externalDocs(decl) {
600
+ if (!decl.docs)
601
+ return undefined;
602
+ return typeof decl.docs === "string" ? { url: decl.docs } : decl.docs;
603
+ }
604
+ /** Prometheus file_sd target file for one external's uptime probe. One
605
+ * file per (tenant, app, external) so a session's probes can be removed on
606
+ * `--kill` without touching another app's. */
607
+ export function localProbeFile(tenant, app, external) {
608
+ return path.join(localProbesDir(), `${tenant}-${app}-${external}.json`);
609
+ }
610
+ /** Drop probe target files (best effort). Called on `mesh dev --kill` so
611
+ * Prometheus stops scraping stand-ins that no longer exist — otherwise the
612
+ * Hub shows an integration down for a session that isn't running. */
613
+ export function localProbesRemove(files) {
614
+ for (const file of files ?? []) {
615
+ try {
616
+ fs.rmSync(file, { force: true });
617
+ }
618
+ catch {
619
+ /* best effort — the cache dir may be gone */
620
+ }
621
+ }
622
+ }
623
+ /**
624
+ * Wire a realization into the platform: seed the external-service
625
+ * credential secret and register the vendor in the local Hub's
626
+ * external-services view — the same shapes ExternalService exports in the
627
+ * cloud. `endpoint` is the local stand-in's address (absent in remote
628
+ * mode — the credentials carry the real endpoint).
629
+ *
630
+ * Returns the probe target file this realization registered, if any, so the
631
+ * caller can record it in session state and remove it on `--kill`.
632
+ */
633
+ export async function seedLocalMock(args) {
634
+ const { tenant, app, name, decl, endpoint } = args;
635
+ if (!decl.external)
636
+ return undefined;
637
+ const mode = externalMode(name, decl);
638
+ // Credential values by mode: local/mock interpolate the declared template
639
+ // against the stand-in's endpoint; remote uses inline credentials or
640
+ // pulls the tenant environment's configured secret.
641
+ let value;
642
+ if (mode === "remote") {
643
+ const inline = remoteInlineCredentials(decl);
644
+ value = inline
645
+ ? Object.fromEntries(Object.entries(inline).map(([key, raw]) => [
646
+ key,
647
+ interpolateCredentialValue(raw, undefined),
648
+ ]))
649
+ : await fetchRemoteExternalCredentials(tenant, decl.remote?.env ?? "dev", decl.external, decl.remote?.profile);
650
+ }
651
+ else {
652
+ if (!decl.credentials)
653
+ return undefined;
654
+ value = Object.fromEntries(Object.entries(decl.credentials).map(([key, raw]) => [
655
+ key,
656
+ interpolateCredentialValue(raw, endpoint),
657
+ ]));
658
+ }
659
+ const secretName = externalSecretPath(tenant, decl.external);
660
+ await upsertLocalSecret(secretName, value);
661
+ // The .config mirror — non-secret fields only. This is what the Hub's
662
+ // credential views READ (the main secret is runtime-only by design), so
663
+ // baseUrl etc. display in the Integrations detail page.
664
+ await upsertLocalSecret(`${secretName}/.config`, Object.fromEntries(Object.entries(value).filter(([key]) => !SECRET_KEY_RE.test(key))));
665
+ // Hub discovery — the same per-stack shapes ExternalService exports in the
666
+ // cloud (/apps/{app}/stacks/{stack}/external-services/{name}/…).
667
+ const { SSMClient, PutParameterCommand } = await import("@aws-sdk/client-ssm");
668
+ const ssm = new SSMClient(LOCAL_AWS_CONFIG);
669
+ const base = `/mesh-platform/${tenant}/${LOCAL_ENV}/apps/${app}/stacks/local/external-services/${decl.external}`;
670
+ await ssm.send(new PutParameterCommand({
671
+ Name: `${base}/meta`,
672
+ Type: "String",
673
+ Overwrite: true,
674
+ Value: JSON.stringify({
675
+ name: decl.external,
676
+ // The integration keeps its REAL name in the Hub ("Plaid", not a
677
+ // generated phrase); the description leads with it too, so display
678
+ // heuristics that promote description heads still land on the name.
679
+ displayName: externalDisplayName(name, decl),
680
+ type: decl.type ?? "other",
681
+ description: mode === "local"
682
+ ? `${externalDisplayName(name, decl)} — local replica via mesh dev (docker compose)`
683
+ : mode === "remote"
684
+ ? remoteInlineCredentials(decl)
685
+ ? `${externalDisplayName(name, decl)} — remote service (vendor credentials)`
686
+ : `${externalDisplayName(name, decl)} — remote service (${decl.remote?.env ?? "dev"} environment credentials)`
687
+ : `${externalDisplayName(name, decl)} — emulated by mesh dev (mock)`,
688
+ secretPrefix: secretName,
689
+ ...(externalDocs(decl) ? { docs: externalDocs(decl) } : {}),
690
+ }),
691
+ Description: `External service registration (local mock ${name})`,
692
+ }));
693
+ await ssm.send(new PutParameterCommand({
694
+ Name: `${base}/credentials`,
695
+ Type: "String",
696
+ Overwrite: true,
697
+ Value: JSON.stringify({
698
+ fields: Object.fromEntries(Object.keys(value).map((key) => [
699
+ key,
700
+ { type: "field", name: key, description: "", secret: SECRET_KEY_RE.test(key), optional: false },
701
+ ])),
702
+ keyedBy: null,
703
+ }),
704
+ Description: `External service credential schema (local mock ${name})`,
705
+ }));
706
+ // Blackbox probe target (Prometheus file_sd) — the local analog of the
707
+ // Probe CR ExternalService creates in the cloud, with the SAME label set,
708
+ // so probe_success drives the Hub's health/uptime views unchanged.
709
+ // Local/mock stand-ins are probed on the host; remote externals at their
710
+ // real endpoint (host:port via the tcp_connect module for non-HTTP
711
+ // externals — the __probe_module label routes the module through the
712
+ // scrape config's relabeling, and "__"-prefixed labels drop after
713
+ // relabeling so it never pollutes the stored series).
714
+ const probe = resolveProbeTarget(mode, decl, endpoint, value);
715
+ await ssm.send(new PutParameterCommand({
716
+ Name: `${base}/healthCheck`,
717
+ Type: "String",
718
+ Overwrite: true,
719
+ Value: JSON.stringify({ intervalSeconds: 30, timeoutSeconds: 10, hasCustomCheck: false, hasProbe: !!probe }),
720
+ Description: `External service health check config (local mock ${name})`,
721
+ }));
722
+ // The file and `hasProbe` must never disagree: a realization that no
723
+ // longer resolves a probe target drops the file it wrote on a previous
724
+ // run, or Prometheus keeps scraping the old target forever.
725
+ const probeFile = localProbeFile(tenant, app, decl.external);
726
+ if (probe) {
727
+ fs.writeFileSync(probeFile, JSON.stringify([
728
+ {
729
+ targets: [probe.target],
730
+ labels: {
731
+ type: "external-service",
732
+ tenant,
733
+ env: LOCAL_ENV,
734
+ app,
735
+ external_service: decl.external,
736
+ target: decl.external,
737
+ // "__"-prefixed labels drop after relabeling, so the module
738
+ // selector never pollutes the stored series.
739
+ ...(probe.module ? { __probe_module: probe.module } : {}),
740
+ },
741
+ },
742
+ ], null, 2));
743
+ }
744
+ else {
745
+ localProbesRemove([probeFile]);
746
+ }
747
+ const endpointShown = endpoint?.url ??
748
+ value.endpoint ??
749
+ value.url ??
750
+ value.baseUrl ??
751
+ (value.host ? `${value.host}${value.port ? `:${value.port}` : ""}` : "credentials");
752
+ logSuccess(`External '${name}' wired (${mode}): ${secretName} → ${endpointShown}` +
753
+ ` (Hub registration${probe ? " + uptime probe" : ""})`);
754
+ return probe ? probeFile : undefined;
755
+ }
756
+ //# sourceMappingURL=mocks.js.map