thevoidforge 21.0.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 (328) hide show
  1. package/dist/scripts/vault-read.d.ts +11 -0
  2. package/dist/scripts/vault-read.js +89 -0
  3. package/dist/scripts/voidforge.d.ts +20 -0
  4. package/dist/scripts/voidforge.js +404 -0
  5. package/dist/tsconfig.tsbuildinfo +1 -0
  6. package/dist/wizard/api/auth.d.ts +5 -0
  7. package/dist/wizard/api/auth.js +133 -0
  8. package/dist/wizard/api/blueprint.d.ts +45 -0
  9. package/dist/wizard/api/blueprint.js +184 -0
  10. package/dist/wizard/api/cloud-providers.d.ts +16 -0
  11. package/dist/wizard/api/cloud-providers.js +363 -0
  12. package/dist/wizard/api/credentials.d.ts +1 -0
  13. package/dist/wizard/api/credentials.js +258 -0
  14. package/dist/wizard/api/danger-room.d.ts +18 -0
  15. package/dist/wizard/api/danger-room.js +401 -0
  16. package/dist/wizard/api/deploy.d.ts +4 -0
  17. package/dist/wizard/api/deploy.js +164 -0
  18. package/dist/wizard/api/prd.d.ts +1 -0
  19. package/dist/wizard/api/prd.js +363 -0
  20. package/dist/wizard/api/project.d.ts +1 -0
  21. package/dist/wizard/api/project.js +239 -0
  22. package/dist/wizard/api/projects.d.ts +6 -0
  23. package/dist/wizard/api/projects.js +648 -0
  24. package/dist/wizard/api/provision.d.ts +4 -0
  25. package/dist/wizard/api/provision.js +535 -0
  26. package/dist/wizard/api/terminal.d.ts +25 -0
  27. package/dist/wizard/api/terminal.js +241 -0
  28. package/dist/wizard/api/users.d.ts +6 -0
  29. package/dist/wizard/api/users.js +244 -0
  30. package/dist/wizard/api/war-room.d.ts +14 -0
  31. package/dist/wizard/api/war-room.js +45 -0
  32. package/dist/wizard/lib/ad-platform-core.d.ts +6 -0
  33. package/dist/wizard/lib/ad-platform-core.js +1 -0
  34. package/dist/wizard/lib/adapters/index.d.ts +52 -0
  35. package/dist/wizard/lib/adapters/index.js +38 -0
  36. package/dist/wizard/lib/adapters/sandbox-bank.d.ts +17 -0
  37. package/dist/wizard/lib/adapters/sandbox-bank.js +77 -0
  38. package/dist/wizard/lib/adapters/sandbox.d.ts +39 -0
  39. package/dist/wizard/lib/adapters/sandbox.js +174 -0
  40. package/dist/wizard/lib/adapters/stripe.d.ts +19 -0
  41. package/dist/wizard/lib/adapters/stripe.js +143 -0
  42. package/dist/wizard/lib/adapters/types.d.ts +9 -0
  43. package/dist/wizard/lib/adapters/types.js +10 -0
  44. package/dist/wizard/lib/agent-memory.d.ts +36 -0
  45. package/dist/wizard/lib/agent-memory.js +114 -0
  46. package/dist/wizard/lib/anomaly-detection.d.ts +59 -0
  47. package/dist/wizard/lib/anomaly-detection.js +122 -0
  48. package/dist/wizard/lib/anthropic.d.ts +21 -0
  49. package/dist/wizard/lib/anthropic.js +105 -0
  50. package/dist/wizard/lib/asset-scanner.d.ts +23 -0
  51. package/dist/wizard/lib/asset-scanner.js +107 -0
  52. package/dist/wizard/lib/audit-log.d.ts +23 -0
  53. package/dist/wizard/lib/audit-log.js +70 -0
  54. package/dist/wizard/lib/autonomy-controller.d.ts +76 -0
  55. package/dist/wizard/lib/autonomy-controller.js +183 -0
  56. package/dist/wizard/lib/body-parser.d.ts +2 -0
  57. package/dist/wizard/lib/body-parser.js +36 -0
  58. package/dist/wizard/lib/build-analytics.d.ts +39 -0
  59. package/dist/wizard/lib/build-analytics.js +91 -0
  60. package/dist/wizard/lib/build-step.d.ts +21 -0
  61. package/dist/wizard/lib/build-step.js +104 -0
  62. package/dist/wizard/lib/campaign-proposer.d.ts +39 -0
  63. package/dist/wizard/lib/campaign-proposer.js +180 -0
  64. package/dist/wizard/lib/campaign-state-machine.d.ts +63 -0
  65. package/dist/wizard/lib/campaign-state-machine.js +114 -0
  66. package/dist/wizard/lib/ci-generator.d.ts +14 -0
  67. package/dist/wizard/lib/ci-generator.js +187 -0
  68. package/dist/wizard/lib/claude-merge.d.ts +38 -0
  69. package/dist/wizard/lib/claude-merge.js +115 -0
  70. package/dist/wizard/lib/codegen/erd-gen.d.ts +16 -0
  71. package/dist/wizard/lib/codegen/erd-gen.js +98 -0
  72. package/dist/wizard/lib/codegen/integrations.d.ts +18 -0
  73. package/dist/wizard/lib/codegen/integrations.js +189 -0
  74. package/dist/wizard/lib/codegen/openapi-gen.d.ts +15 -0
  75. package/dist/wizard/lib/codegen/openapi-gen.js +79 -0
  76. package/dist/wizard/lib/codegen/prisma-types.d.ts +15 -0
  77. package/dist/wizard/lib/codegen/prisma-types.js +44 -0
  78. package/dist/wizard/lib/codegen/seed-gen.d.ts +16 -0
  79. package/dist/wizard/lib/codegen/seed-gen.js +128 -0
  80. package/dist/wizard/lib/compliance.d.ts +51 -0
  81. package/dist/wizard/lib/compliance.js +112 -0
  82. package/dist/wizard/lib/correlation-engine.d.ts +59 -0
  83. package/dist/wizard/lib/correlation-engine.js +151 -0
  84. package/dist/wizard/lib/cost-estimator.d.ts +22 -0
  85. package/dist/wizard/lib/cost-estimator.js +72 -0
  86. package/dist/wizard/lib/cost-tracker.d.ts +27 -0
  87. package/dist/wizard/lib/cost-tracker.js +37 -0
  88. package/dist/wizard/lib/daemon-aggregator.d.ts +71 -0
  89. package/dist/wizard/lib/daemon-aggregator.js +204 -0
  90. package/dist/wizard/lib/daemon-core.d.ts +6 -0
  91. package/dist/wizard/lib/daemon-core.js +5 -0
  92. package/dist/wizard/lib/dashboard-data.d.ts +132 -0
  93. package/dist/wizard/lib/dashboard-data.js +336 -0
  94. package/dist/wizard/lib/dashboard-ws.d.ts +25 -0
  95. package/dist/wizard/lib/dashboard-ws.js +91 -0
  96. package/dist/wizard/lib/deep-current.d.ts +77 -0
  97. package/dist/wizard/lib/deep-current.js +234 -0
  98. package/dist/wizard/lib/deploy-coordinator.d.ts +40 -0
  99. package/dist/wizard/lib/deploy-coordinator.js +86 -0
  100. package/dist/wizard/lib/deploy-log.d.ts +28 -0
  101. package/dist/wizard/lib/deploy-log.js +52 -0
  102. package/dist/wizard/lib/desktop-notify.d.ts +27 -0
  103. package/dist/wizard/lib/desktop-notify.js +98 -0
  104. package/dist/wizard/lib/dns/cloudflare-dns.d.ts +35 -0
  105. package/dist/wizard/lib/dns/cloudflare-dns.js +216 -0
  106. package/dist/wizard/lib/dns/cloudflare-registrar.d.ts +31 -0
  107. package/dist/wizard/lib/dns/cloudflare-registrar.js +148 -0
  108. package/dist/wizard/lib/dns/types.d.ts +22 -0
  109. package/dist/wizard/lib/dns/types.js +4 -0
  110. package/dist/wizard/lib/document-discovery.d.ts +33 -0
  111. package/dist/wizard/lib/document-discovery.js +145 -0
  112. package/dist/wizard/lib/env-validator.d.ts +14 -0
  113. package/dist/wizard/lib/env-validator.js +205 -0
  114. package/dist/wizard/lib/env-writer.d.ts +13 -0
  115. package/dist/wizard/lib/env-writer.js +26 -0
  116. package/dist/wizard/lib/exec.d.ts +30 -0
  117. package/dist/wizard/lib/exec.js +52 -0
  118. package/dist/wizard/lib/experiment.d.ts +70 -0
  119. package/dist/wizard/lib/experiment.js +169 -0
  120. package/dist/wizard/lib/extensions.d.ts +20 -0
  121. package/dist/wizard/lib/extensions.js +183 -0
  122. package/dist/wizard/lib/financial/adapter-factory.d.ts +47 -0
  123. package/dist/wizard/lib/financial/adapter-factory.js +225 -0
  124. package/dist/wizard/lib/financial/billing/base.d.ts +6 -0
  125. package/dist/wizard/lib/financial/billing/base.js +1 -0
  126. package/dist/wizard/lib/financial/billing/google-billing.d.ts +56 -0
  127. package/dist/wizard/lib/financial/billing/google-billing.js +298 -0
  128. package/dist/wizard/lib/financial/billing/meta-billing.d.ts +54 -0
  129. package/dist/wizard/lib/financial/billing/meta-billing.js +243 -0
  130. package/dist/wizard/lib/financial/billing/tiktok-billing.d.ts +54 -0
  131. package/dist/wizard/lib/financial/billing/tiktok-billing.js +260 -0
  132. package/dist/wizard/lib/financial/campaign/base.d.ts +13 -0
  133. package/dist/wizard/lib/financial/campaign/base.js +1 -0
  134. package/dist/wizard/lib/financial/campaign/google-campaign.d.ts +42 -0
  135. package/dist/wizard/lib/financial/campaign/google-campaign.js +388 -0
  136. package/dist/wizard/lib/financial/campaign/meta-campaign.d.ts +41 -0
  137. package/dist/wizard/lib/financial/campaign/meta-campaign.js +311 -0
  138. package/dist/wizard/lib/financial/campaign/sandbox-campaign.d.ts +45 -0
  139. package/dist/wizard/lib/financial/campaign/sandbox-campaign.js +261 -0
  140. package/dist/wizard/lib/financial/campaign/tiktok-campaign.d.ts +40 -0
  141. package/dist/wizard/lib/financial/campaign/tiktok-campaign.js +350 -0
  142. package/dist/wizard/lib/financial/funding-auto.d.ts +44 -0
  143. package/dist/wizard/lib/financial/funding-auto.js +52 -0
  144. package/dist/wizard/lib/financial/funding-policy.d.ts +60 -0
  145. package/dist/wizard/lib/financial/funding-policy.js +179 -0
  146. package/dist/wizard/lib/financial/platform-planner.d.ts +47 -0
  147. package/dist/wizard/lib/financial/platform-planner.js +134 -0
  148. package/dist/wizard/lib/financial/reconciliation-engine.d.ts +78 -0
  149. package/dist/wizard/lib/financial/reconciliation-engine.js +193 -0
  150. package/dist/wizard/lib/financial/registry.d.ts +22 -0
  151. package/dist/wizard/lib/financial/registry.js +26 -0
  152. package/dist/wizard/lib/financial/reporting.d.ts +96 -0
  153. package/dist/wizard/lib/financial/reporting.js +198 -0
  154. package/dist/wizard/lib/financial/stablecoin/base.d.ts +6 -0
  155. package/dist/wizard/lib/financial/stablecoin/base.js +1 -0
  156. package/dist/wizard/lib/financial/stablecoin/circle.d.ts +54 -0
  157. package/dist/wizard/lib/financial/stablecoin/circle.js +367 -0
  158. package/dist/wizard/lib/financial/stablecoin/mercury.d.ts +24 -0
  159. package/dist/wizard/lib/financial/stablecoin/mercury.js +171 -0
  160. package/dist/wizard/lib/financial/stablecoin/sandbox-stablecoin.d.ts +47 -0
  161. package/dist/wizard/lib/financial/stablecoin/sandbox-stablecoin.js +202 -0
  162. package/dist/wizard/lib/financial/treasury-planner.d.ts +52 -0
  163. package/dist/wizard/lib/financial/treasury-planner.js +128 -0
  164. package/dist/wizard/lib/financial-core.d.ts +6 -0
  165. package/dist/wizard/lib/financial-core.js +5 -0
  166. package/dist/wizard/lib/financial-vault.d.ts +34 -0
  167. package/dist/wizard/lib/financial-vault.js +199 -0
  168. package/dist/wizard/lib/frontmatter.d.ts +30 -0
  169. package/dist/wizard/lib/frontmatter.js +96 -0
  170. package/dist/wizard/lib/gap-analysis.d.ts +37 -0
  171. package/dist/wizard/lib/gap-analysis.js +218 -0
  172. package/dist/wizard/lib/github.d.ts +22 -0
  173. package/dist/wizard/lib/github.js +261 -0
  174. package/dist/wizard/lib/headless-deploy.d.ts +14 -0
  175. package/dist/wizard/lib/headless-deploy.js +452 -0
  176. package/dist/wizard/lib/health-monitor.d.ts +15 -0
  177. package/dist/wizard/lib/health-monitor.js +91 -0
  178. package/dist/wizard/lib/health-poller.d.ts +9 -0
  179. package/dist/wizard/lib/health-poller.js +123 -0
  180. package/dist/wizard/lib/heartbeat.d.ts +15 -0
  181. package/dist/wizard/lib/heartbeat.js +827 -0
  182. package/dist/wizard/lib/http-helpers.d.ts +9 -0
  183. package/dist/wizard/lib/http-helpers.js +24 -0
  184. package/dist/wizard/lib/image-gen.d.ts +56 -0
  185. package/dist/wizard/lib/image-gen.js +159 -0
  186. package/dist/wizard/lib/instance-sizing.d.ts +26 -0
  187. package/dist/wizard/lib/instance-sizing.js +51 -0
  188. package/dist/wizard/lib/kongo/analytics.d.ts +29 -0
  189. package/dist/wizard/lib/kongo/analytics.js +179 -0
  190. package/dist/wizard/lib/kongo/campaigns.d.ts +52 -0
  191. package/dist/wizard/lib/kongo/campaigns.js +91 -0
  192. package/dist/wizard/lib/kongo/client.d.ts +58 -0
  193. package/dist/wizard/lib/kongo/client.js +221 -0
  194. package/dist/wizard/lib/kongo/jobs.d.ts +57 -0
  195. package/dist/wizard/lib/kongo/jobs.js +122 -0
  196. package/dist/wizard/lib/kongo/pages.d.ts +60 -0
  197. package/dist/wizard/lib/kongo/pages.js +150 -0
  198. package/dist/wizard/lib/kongo/provisioner.d.ts +64 -0
  199. package/dist/wizard/lib/kongo/provisioner.js +116 -0
  200. package/dist/wizard/lib/kongo/seed.d.ts +49 -0
  201. package/dist/wizard/lib/kongo/seed.js +237 -0
  202. package/dist/wizard/lib/kongo/types.d.ts +323 -0
  203. package/dist/wizard/lib/kongo/types.js +11 -0
  204. package/dist/wizard/lib/kongo/variants.d.ts +57 -0
  205. package/dist/wizard/lib/kongo/variants.js +88 -0
  206. package/dist/wizard/lib/kongo/webhooks.d.ts +41 -0
  207. package/dist/wizard/lib/kongo/webhooks.js +112 -0
  208. package/dist/wizard/lib/marker.d.ts +28 -0
  209. package/dist/wizard/lib/marker.js +79 -0
  210. package/dist/wizard/lib/migrator.d.ts +35 -0
  211. package/dist/wizard/lib/migrator.js +190 -0
  212. package/dist/wizard/lib/natural-language-deploy.d.ts +30 -0
  213. package/dist/wizard/lib/natural-language-deploy.js +186 -0
  214. package/dist/wizard/lib/network.d.ts +22 -0
  215. package/dist/wizard/lib/network.js +72 -0
  216. package/dist/wizard/lib/oauth-core.d.ts +6 -0
  217. package/dist/wizard/lib/oauth-core.js +5 -0
  218. package/dist/wizard/lib/open-browser.d.ts +1 -0
  219. package/dist/wizard/lib/open-browser.js +26 -0
  220. package/dist/wizard/lib/patterns/ad-billing-adapter.d.ts +209 -0
  221. package/dist/wizard/lib/patterns/ad-billing-adapter.js +269 -0
  222. package/dist/wizard/lib/patterns/ad-platform-adapter.d.ts +200 -0
  223. package/dist/wizard/lib/patterns/ad-platform-adapter.js +212 -0
  224. package/dist/wizard/lib/patterns/daemon-process.d.ts +88 -0
  225. package/dist/wizard/lib/patterns/daemon-process.js +271 -0
  226. package/dist/wizard/lib/patterns/financial-transaction.d.ts +161 -0
  227. package/dist/wizard/lib/patterns/financial-transaction.js +132 -0
  228. package/dist/wizard/lib/patterns/funding-plan.d.ts +136 -0
  229. package/dist/wizard/lib/patterns/funding-plan.js +200 -0
  230. package/dist/wizard/lib/patterns/oauth-token-lifecycle.d.ts +94 -0
  231. package/dist/wizard/lib/patterns/oauth-token-lifecycle.js +139 -0
  232. package/dist/wizard/lib/patterns/outbound-rate-limiter.d.ts +67 -0
  233. package/dist/wizard/lib/patterns/outbound-rate-limiter.js +216 -0
  234. package/dist/wizard/lib/patterns/revenue-source-adapter.d.ts +96 -0
  235. package/dist/wizard/lib/patterns/revenue-source-adapter.js +182 -0
  236. package/dist/wizard/lib/patterns/stablecoin-adapter.d.ts +218 -0
  237. package/dist/wizard/lib/patterns/stablecoin-adapter.js +264 -0
  238. package/dist/wizard/lib/prd-validator.d.ts +39 -0
  239. package/dist/wizard/lib/prd-validator.js +137 -0
  240. package/dist/wizard/lib/project-init.d.ts +24 -0
  241. package/dist/wizard/lib/project-init.js +193 -0
  242. package/dist/wizard/lib/project-registry.d.ts +86 -0
  243. package/dist/wizard/lib/project-registry.js +359 -0
  244. package/dist/wizard/lib/provision-manifest.d.ts +44 -0
  245. package/dist/wizard/lib/provision-manifest.js +164 -0
  246. package/dist/wizard/lib/provisioner-registry.d.ts +15 -0
  247. package/dist/wizard/lib/provisioner-registry.js +34 -0
  248. package/dist/wizard/lib/provisioners/aws-vps.d.ts +6 -0
  249. package/dist/wizard/lib/provisioners/aws-vps.js +643 -0
  250. package/dist/wizard/lib/provisioners/cloudflare.d.ts +6 -0
  251. package/dist/wizard/lib/provisioners/cloudflare.js +300 -0
  252. package/dist/wizard/lib/provisioners/docker.d.ts +6 -0
  253. package/dist/wizard/lib/provisioners/docker.js +75 -0
  254. package/dist/wizard/lib/provisioners/http-client.d.ts +20 -0
  255. package/dist/wizard/lib/provisioners/http-client.js +79 -0
  256. package/dist/wizard/lib/provisioners/railway.d.ts +6 -0
  257. package/dist/wizard/lib/provisioners/railway.js +413 -0
  258. package/dist/wizard/lib/provisioners/scripts/caddyfile.d.ts +10 -0
  259. package/dist/wizard/lib/provisioners/scripts/caddyfile.js +54 -0
  260. package/dist/wizard/lib/provisioners/scripts/deploy-vps.d.ts +10 -0
  261. package/dist/wizard/lib/provisioners/scripts/deploy-vps.js +112 -0
  262. package/dist/wizard/lib/provisioners/scripts/docker-compose.d.ts +11 -0
  263. package/dist/wizard/lib/provisioners/scripts/docker-compose.js +91 -0
  264. package/dist/wizard/lib/provisioners/scripts/dockerfile.d.ts +5 -0
  265. package/dist/wizard/lib/provisioners/scripts/dockerfile.js +185 -0
  266. package/dist/wizard/lib/provisioners/scripts/ecosystem-config.d.ts +10 -0
  267. package/dist/wizard/lib/provisioners/scripts/ecosystem-config.js +36 -0
  268. package/dist/wizard/lib/provisioners/scripts/provision-vps.d.ts +14 -0
  269. package/dist/wizard/lib/provisioners/scripts/provision-vps.js +202 -0
  270. package/dist/wizard/lib/provisioners/scripts/rollback-vps.d.ts +10 -0
  271. package/dist/wizard/lib/provisioners/scripts/rollback-vps.js +67 -0
  272. package/dist/wizard/lib/provisioners/self-deploy.d.ts +41 -0
  273. package/dist/wizard/lib/provisioners/self-deploy.js +185 -0
  274. package/dist/wizard/lib/provisioners/static-s3.d.ts +6 -0
  275. package/dist/wizard/lib/provisioners/static-s3.js +235 -0
  276. package/dist/wizard/lib/provisioners/types.d.ts +40 -0
  277. package/dist/wizard/lib/provisioners/types.js +4 -0
  278. package/dist/wizard/lib/provisioners/vercel.d.ts +6 -0
  279. package/dist/wizard/lib/provisioners/vercel.js +287 -0
  280. package/dist/wizard/lib/pty-manager.d.ts +42 -0
  281. package/dist/wizard/lib/pty-manager.js +231 -0
  282. package/dist/wizard/lib/rate-limiter-core.d.ts +5 -0
  283. package/dist/wizard/lib/rate-limiter-core.js +5 -0
  284. package/dist/wizard/lib/reconciliation.d.ts +43 -0
  285. package/dist/wizard/lib/reconciliation.js +173 -0
  286. package/dist/wizard/lib/revenue-types.d.ts +5 -0
  287. package/dist/wizard/lib/revenue-types.js +1 -0
  288. package/dist/wizard/lib/route-optimizer.d.ts +28 -0
  289. package/dist/wizard/lib/route-optimizer.js +93 -0
  290. package/dist/wizard/lib/s3-deploy.d.ts +19 -0
  291. package/dist/wizard/lib/s3-deploy.js +156 -0
  292. package/dist/wizard/lib/safety-tiers.d.ts +76 -0
  293. package/dist/wizard/lib/safety-tiers.js +134 -0
  294. package/dist/wizard/lib/sentry-generator.d.ts +15 -0
  295. package/dist/wizard/lib/sentry-generator.js +116 -0
  296. package/dist/wizard/lib/server-config.d.ts +13 -0
  297. package/dist/wizard/lib/server-config.js +23 -0
  298. package/dist/wizard/lib/service-install.d.ts +18 -0
  299. package/dist/wizard/lib/service-install.js +182 -0
  300. package/dist/wizard/lib/site-scanner.d.ts +80 -0
  301. package/dist/wizard/lib/site-scanner.js +262 -0
  302. package/dist/wizard/lib/ssh-deploy.d.ts +25 -0
  303. package/dist/wizard/lib/ssh-deploy.js +225 -0
  304. package/dist/wizard/lib/templates.d.ts +24 -0
  305. package/dist/wizard/lib/templates.js +219 -0
  306. package/dist/wizard/lib/totp.d.ts +35 -0
  307. package/dist/wizard/lib/totp.js +276 -0
  308. package/dist/wizard/lib/tower-auth.d.ts +43 -0
  309. package/dist/wizard/lib/tower-auth.js +352 -0
  310. package/dist/wizard/lib/tower-rate-limit.d.ts +14 -0
  311. package/dist/wizard/lib/tower-rate-limit.js +61 -0
  312. package/dist/wizard/lib/tower-session.d.ts +28 -0
  313. package/dist/wizard/lib/tower-session.js +119 -0
  314. package/dist/wizard/lib/treasury-backup.d.ts +23 -0
  315. package/dist/wizard/lib/treasury-backup.js +126 -0
  316. package/dist/wizard/lib/treasury-heartbeat.d.ts +82 -0
  317. package/dist/wizard/lib/treasury-heartbeat.js +1104 -0
  318. package/dist/wizard/lib/updater.d.ts +29 -0
  319. package/dist/wizard/lib/updater.js +190 -0
  320. package/dist/wizard/lib/user-manager.d.ts +39 -0
  321. package/dist/wizard/lib/user-manager.js +182 -0
  322. package/dist/wizard/lib/vault.d.ts +26 -0
  323. package/dist/wizard/lib/vault.js +161 -0
  324. package/dist/wizard/router.d.ts +5 -0
  325. package/dist/wizard/router.js +15 -0
  326. package/dist/wizard/server.d.ts +18 -0
  327. package/dist/wizard/server.js +436 -0
  328. package/package.json +59 -0
@@ -0,0 +1,401 @@
1
+ /**
2
+ * Danger Room API — Real-time data feeds for the mission control dashboard.
3
+ *
4
+ * Shared parsers and WebSocket infra live in wizard/lib/dashboard-*.ts.
5
+ * This file registers Danger Room routes and any Danger Room-specific endpoints
6
+ * (heartbeat, freeze, Deep Current).
7
+ */
8
+ import { readFile, readdir, stat, open } from 'node:fs/promises';
9
+ import { join } from 'node:path';
10
+ import { homedir } from 'node:os';
11
+ import { watch, existsSync } from 'node:fs';
12
+ import { addRoute } from '../router.js';
13
+ import { sendJson, readFileOrNull } from '../lib/http-helpers.js';
14
+ import { parseCampaignState, parseBuildState, parseFindings, readDeployLog, readVersion, readContextStats, readTestResults, readGitStatus, detectDeployDrift, PROJECT_ROOT, } from '../lib/dashboard-data.js';
15
+ import { createDashboardWs } from '../lib/dashboard-ws.js';
16
+ // ── WebSocket ───────────────────────────────────
17
+ const ws = createDashboardWs('Danger Room');
18
+ /** Broadcast a message to all connected Danger Room clients. */
19
+ export const broadcastDangerRoom = ws.broadcast;
20
+ /** Close all Danger Room WebSocket connections, activity watcher, and shut down. */
21
+ export function closeDangerRoom() {
22
+ ws.close();
23
+ // Clean up activity watcher resources (Infinity Gauntlet ARCH-002)
24
+ if (activityPollInterval)
25
+ clearInterval(activityPollInterval);
26
+ if (activityWatcher) {
27
+ try {
28
+ activityWatcher.close();
29
+ }
30
+ catch { /* ignore */ }
31
+ activityWatcher = null;
32
+ }
33
+ if (activityDebounce)
34
+ clearTimeout(activityDebounce);
35
+ }
36
+ /** Handle WebSocket upgrade for /ws/danger-room. */
37
+ export const handleDangerRoomUpgrade = (req, socket, head) => ws.handleUpgrade(req, socket, head);
38
+ // ── Agent Activity Watcher (methodology-driven JSONL) ──
39
+ const ACTIVITY_FILE = join(PROJECT_ROOT, 'logs', 'agent-activity.jsonl');
40
+ let lastActivitySize = 0;
41
+ let activityDebounce = null;
42
+ let activityCheckInProgress = false;
43
+ /** Read new lines from agent-activity.jsonl and broadcast via WebSocket. */
44
+ async function checkAgentActivity() {
45
+ if (activityCheckInProgress)
46
+ return; // prevent concurrent reads (Gauntlet DR-08)
47
+ activityCheckInProgress = true;
48
+ try {
49
+ await _checkAgentActivity();
50
+ }
51
+ finally {
52
+ activityCheckInProgress = false;
53
+ }
54
+ }
55
+ async function _checkAgentActivity() {
56
+ try {
57
+ const st = await stat(ACTIVITY_FILE);
58
+ if (st.size <= lastActivitySize)
59
+ return;
60
+ // Read only the new bytes appended since last check (Gauntlet DR-05: no line estimation)
61
+ const fd = await open(ACTIVITY_FILE, 'r');
62
+ const buf = Buffer.alloc(st.size - lastActivitySize);
63
+ await fd.read(buf, 0, buf.length, lastActivitySize);
64
+ await fd.close();
65
+ const newContent = buf.toString('utf-8');
66
+ const newLines = newContent.trim().split('\n').filter(Boolean);
67
+ lastActivitySize = st.size;
68
+ for (const line of newLines) {
69
+ try {
70
+ const entry = JSON.parse(line);
71
+ if (entry.agent) {
72
+ // Server-side sanitization: cap field lengths (Gauntlet Kenobi DR-05)
73
+ const agent = String(entry.agent).slice(0, 50);
74
+ const action = String(entry.task || entry.status || 'dispatched').slice(0, 200);
75
+ ws.broadcast({ type: 'agent-activity', agent, action });
76
+ }
77
+ }
78
+ catch { /* skip malformed lines */ }
79
+ }
80
+ }
81
+ catch { /* file doesn't exist yet — normal before first agent dispatch */ }
82
+ }
83
+ // Hybrid approach: fs.watch for immediate + poll fallback (fs.watch is unreliable on some OSes)
84
+ let activityWatcher = null;
85
+ function setupActivityWatch() {
86
+ if (activityWatcher)
87
+ return;
88
+ try {
89
+ activityWatcher = watch(ACTIVITY_FILE, { persistent: false }, () => {
90
+ if (activityDebounce)
91
+ clearTimeout(activityDebounce);
92
+ activityDebounce = setTimeout(checkAgentActivity, 200);
93
+ });
94
+ activityWatcher.on('error', () => { activityWatcher = null; }); // re-establish on next poll
95
+ }
96
+ catch { /* file doesn't exist yet — poll will re-try */ }
97
+ }
98
+ setupActivityWatch();
99
+ // Poll fallback — catches events fs.watch misses AND re-establishes watch if file appeared (DR-06)
100
+ const activityPollInterval = setInterval(() => {
101
+ if (!activityWatcher)
102
+ setupActivityWatch();
103
+ checkAgentActivity();
104
+ }, 3000);
105
+ // ── Shared REST endpoints ────────────────────────
106
+ addRoute('GET', '/api/danger-room/campaign', async (_req, res) => {
107
+ sendJson(res, 200, await parseCampaignState());
108
+ });
109
+ addRoute('GET', '/api/danger-room/build', async (_req, res) => {
110
+ sendJson(res, 200, await parseBuildState());
111
+ });
112
+ addRoute('GET', '/api/danger-room/findings', async (_req, res) => {
113
+ sendJson(res, 200, await parseFindings());
114
+ });
115
+ addRoute('GET', '/api/danger-room/version', async (_req, res) => {
116
+ sendJson(res, 200, await readVersion());
117
+ });
118
+ addRoute('GET', '/api/danger-room/deploy', async (_req, res) => {
119
+ sendJson(res, 200, await readDeployLog());
120
+ });
121
+ addRoute('GET', '/api/danger-room/context', async (_req, res) => {
122
+ const stats = await readContextStats();
123
+ sendJson(res, 200, stats);
124
+ });
125
+ addRoute('GET', '/api/danger-room/experiments', async (_req, res) => {
126
+ try {
127
+ const { listExperiments } = await import('../lib/experiment.js');
128
+ const experiments = await listExperiments();
129
+ sendJson(res, 200, { experiments, total: experiments.length });
130
+ }
131
+ catch {
132
+ sendJson(res, 200, { experiments: [], total: 0 });
133
+ }
134
+ });
135
+ addRoute('GET', '/api/danger-room/tests', async (_req, res) => {
136
+ sendJson(res, 200, await readTestResults());
137
+ });
138
+ addRoute('GET', '/api/danger-room/git-status', async (_req, res) => {
139
+ sendJson(res, 200, await readGitStatus());
140
+ });
141
+ addRoute('GET', '/api/danger-room/drift', async (_req, res) => {
142
+ sendJson(res, 200, await detectDeployDrift());
143
+ });
144
+ // ── Danger Room-specific endpoints ───────────────
145
+ addRoute('GET', '/api/danger-room/heartbeat', async (_req, res) => {
146
+ const voidforgeDir = join(homedir(), '.voidforge');
147
+ const treasuryDir = join(voidforgeDir, 'treasury');
148
+ const treasuryVaultPath = join(treasuryDir, 'vault.enc');
149
+ const heartbeatJsonPath = join(voidforgeDir, 'heartbeat.json');
150
+ let cultivationInstalled = false;
151
+ let heartbeatData = null;
152
+ let campaigns = [];
153
+ let treasury = {
154
+ revenue: 0, spend: 0, net: 0, roas: 0, budgetRemaining: 0,
155
+ stablecoinBalance: null, pendingOfframps: 0,
156
+ bankAvailable: null, bankReserved: null,
157
+ runwayDays: null, fundingState: null,
158
+ nextTreasuryEvent: null, unsettledInvoices: 0,
159
+ reconciliationStatus: null,
160
+ };
161
+ try {
162
+ // existsSync imported statically at top (Infinity Gauntlet ARCH-009)
163
+ cultivationInstalled = existsSync(treasuryVaultPath);
164
+ const raw = await readFileOrNull(heartbeatJsonPath);
165
+ if (raw)
166
+ heartbeatData = JSON.parse(raw);
167
+ }
168
+ catch { /* no heartbeat data */ }
169
+ // Read campaigns from treasury/campaigns directory (mirrors heartbeat.ts readCampaigns)
170
+ try {
171
+ const campaignsDir = join(treasuryDir, 'campaigns');
172
+ if (existsSync(campaignsDir)) {
173
+ const files = await readdir(campaignsDir);
174
+ for (const file of files) {
175
+ if (!file.endsWith('.json'))
176
+ continue;
177
+ try {
178
+ const content = await readFile(join(campaignsDir, file), 'utf-8');
179
+ campaigns.push(JSON.parse(content));
180
+ }
181
+ catch { /* skip malformed campaign files */ }
182
+ }
183
+ }
184
+ }
185
+ catch { /* no campaigns directory */ }
186
+ // Read treasury summary from spend/revenue logs (mirrors heartbeat.ts readTreasurySummary)
187
+ try {
188
+ const spendLog = join(treasuryDir, 'spend-log.jsonl');
189
+ const revenueLog = join(treasuryDir, 'revenue-log.jsonl');
190
+ let totalSpendCents = 0;
191
+ let totalRevenueCents = 0;
192
+ if (existsSync(spendLog)) {
193
+ const lines = (await readFile(spendLog, 'utf-8')).trim().split('\n').filter(Boolean);
194
+ for (const line of lines) {
195
+ try {
196
+ const entry = JSON.parse(line);
197
+ // Clamp negative values — spend should never be negative
198
+ totalSpendCents += Math.max(0, entry.amountCents ?? 0);
199
+ }
200
+ catch { /* skip malformed lines */ }
201
+ }
202
+ }
203
+ if (existsSync(revenueLog)) {
204
+ const lines = (await readFile(revenueLog, 'utf-8')).trim().split('\n').filter(Boolean);
205
+ for (const line of lines) {
206
+ try {
207
+ const entry = JSON.parse(line);
208
+ totalRevenueCents += entry.amountCents ?? 0;
209
+ }
210
+ catch { /* skip malformed lines */ }
211
+ }
212
+ }
213
+ const net = totalRevenueCents - totalSpendCents;
214
+ const roas = totalSpendCents > 0 ? totalRevenueCents / totalSpendCents : 0;
215
+ // Read budget if available
216
+ let budgetRemaining = 0;
217
+ const budgetsFile = join(treasuryDir, 'budgets.json');
218
+ if (existsSync(budgetsFile)) {
219
+ try {
220
+ const budgetData = JSON.parse(await readFile(budgetsFile, 'utf-8'));
221
+ budgetRemaining = (budgetData.totalBudgetCents ?? 0) - totalSpendCents;
222
+ }
223
+ catch { /* skip malformed budgets */ }
224
+ }
225
+ // ── Stablecoin funding data (v19.0 — read from treasury JSONL logs) ──
226
+ let stablecoinBalance = null;
227
+ let pendingOfframps = 0;
228
+ let bankAvailable = null;
229
+ let bankReserved = null;
230
+ let runwayDays = null;
231
+ let fundingState = null;
232
+ let nextTreasuryEvent = null;
233
+ let unsettledInvoices = 0;
234
+ let reconciliationStatus = null;
235
+ // Read funding config for stablecoin balance and bank state
236
+ const fundingConfigPath = join(treasuryDir, 'funding-config.json.enc');
237
+ if (existsSync(fundingConfigPath)) {
238
+ // If funding config exists, try to read bank and stablecoin state from heartbeat data
239
+ // (heartbeat.json is the live state written by the daemon; funding-config is encrypted)
240
+ if (heartbeatData) {
241
+ const hb = heartbeatData;
242
+ if (typeof hb.stablecoinBalanceCents === 'number')
243
+ stablecoinBalance = hb.stablecoinBalanceCents;
244
+ if (typeof hb.bankAvailableCents === 'number')
245
+ bankAvailable = hb.bankAvailableCents;
246
+ if (typeof hb.bankReservedCents === 'number')
247
+ bankReserved = hb.bankReservedCents;
248
+ if (typeof hb.runwayDays === 'number')
249
+ runwayDays = hb.runwayDays;
250
+ if (typeof hb.fundingState === 'string')
251
+ fundingState = hb.fundingState;
252
+ if (typeof hb.nextTreasuryEvent === 'string')
253
+ nextTreasuryEvent = hb.nextTreasuryEvent;
254
+ }
255
+ }
256
+ // Read funding plans for pending off-ramps and unsettled invoices
257
+ const fundingPlansLog = join(treasuryDir, 'funding-plans.jsonl');
258
+ if (existsSync(fundingPlansLog)) {
259
+ try {
260
+ const lines = (await readFile(fundingPlansLog, 'utf-8')).trim().split('\n').filter(Boolean);
261
+ for (const line of lines) {
262
+ try {
263
+ const plan = JSON.parse(line);
264
+ if (plan.status === 'PENDING_SETTLEMENT' || plan.status === 'APPROVED') {
265
+ unsettledInvoices++;
266
+ }
267
+ }
268
+ catch { /* skip malformed lines */ }
269
+ }
270
+ }
271
+ catch { /* skip read errors */ }
272
+ }
273
+ // Read transfers for pending off-ramp count
274
+ const transfersLog = join(treasuryDir, 'transfers.jsonl');
275
+ if (existsSync(transfersLog)) {
276
+ try {
277
+ const lines = (await readFile(transfersLog, 'utf-8')).trim().split('\n').filter(Boolean);
278
+ for (const line of lines) {
279
+ try {
280
+ const transfer = JSON.parse(line);
281
+ if ((transfer.status === 'pending' || transfer.status === 'processing')
282
+ && transfer.direction === 'crypto_to_fiat') {
283
+ pendingOfframps++;
284
+ }
285
+ }
286
+ catch { /* skip malformed lines */ }
287
+ }
288
+ }
289
+ catch { /* skip read errors */ }
290
+ }
291
+ // Read latest reconciliation status
292
+ const reconciliationLog = join(treasuryDir, 'reconciliation.jsonl');
293
+ if (existsSync(reconciliationLog)) {
294
+ try {
295
+ const lines = (await readFile(reconciliationLog, 'utf-8')).trim().split('\n').filter(Boolean);
296
+ if (lines.length > 0) {
297
+ // Use last reconciliation entry as current status
298
+ const last = JSON.parse(lines[lines.length - 1]);
299
+ if (last.result === 'MATCHED' || last.result === 'WITHIN_THRESHOLD') {
300
+ reconciliationStatus = 'matched';
301
+ }
302
+ else if (last.result === 'MISMATCH') {
303
+ reconciliationStatus = 'mismatch';
304
+ }
305
+ }
306
+ }
307
+ catch { /* skip read errors */ }
308
+ }
309
+ // Calculate funding state from data if not provided by heartbeat daemon
310
+ if (fundingState === null && (stablecoinBalance !== null || bankAvailable !== null)) {
311
+ if (runwayDays !== null && runwayDays < 3)
312
+ fundingState = 'frozen';
313
+ else if (runwayDays !== null && runwayDays < 7)
314
+ fundingState = 'degraded';
315
+ else
316
+ fundingState = 'healthy';
317
+ }
318
+ treasury = {
319
+ revenue: totalRevenueCents, spend: totalSpendCents, net, roas, budgetRemaining,
320
+ stablecoinBalance, pendingOfframps,
321
+ bankAvailable, bankReserved,
322
+ runwayDays, fundingState,
323
+ nextTreasuryEvent, unsettledInvoices,
324
+ reconciliationStatus,
325
+ };
326
+ }
327
+ catch { /* no treasury data */ }
328
+ sendJson(res, 200, { cultivationInstalled, heartbeat: heartbeatData, campaigns, treasury });
329
+ });
330
+ addRoute('POST', '/api/danger-room/freeze', async (_req, res) => {
331
+ // RBAC enforced by ROUTE_ROLES in server.ts (deployer+ required).
332
+ // Previous implementation checked client-supplied X-VoidForge-Role header (SEC-R1-001 — privilege escalation).
333
+ // v18.0: Removed client-header check. Session-based role verification happens in server middleware.
334
+ // v17.0: Wire to daemon Unix socket with auth token.
335
+ try {
336
+ const net = await import('node:net');
337
+ const { readFile: fsReadFile } = await import('node:fs/promises');
338
+ const { SOCKET_PATH, TOKEN_FILE } = await import('../lib/daemon-core.js');
339
+ const { existsSync } = await import('node:fs');
340
+ if (!existsSync(SOCKET_PATH)) {
341
+ sendJson(res, 503, { ok: false, error: 'Heartbeat daemon not running. Start with: voidforge heartbeat start' });
342
+ return;
343
+ }
344
+ // Read auth token from TOKEN_FILE
345
+ let authToken = '';
346
+ try {
347
+ authToken = (await fsReadFile(TOKEN_FILE, 'utf-8')).trim();
348
+ }
349
+ catch {
350
+ sendJson(res, 503, { ok: false, error: 'Cannot read daemon auth token — heartbeat may not be running' });
351
+ return;
352
+ }
353
+ const response = await new Promise((resolve, reject) => {
354
+ const socket = net.connect(SOCKET_PATH);
355
+ let data = '';
356
+ socket.on('data', (chunk) => { data += chunk.toString(); });
357
+ socket.on('end', () => resolve(data));
358
+ socket.on('error', (err) => reject(err));
359
+ socket.setTimeout(5000, () => { socket.destroy(); reject(new Error('Daemon socket timeout')); });
360
+ socket.write(`POST /freeze HTTP/1.0\r\nAuthorization: Bearer ${authToken}\r\nContent-Length: 0\r\n\r\n`);
361
+ });
362
+ // Parse daemon response — expects JSON body after HTTP headers
363
+ const bodyStart = response.indexOf('\r\n\r\n');
364
+ const body = bodyStart >= 0 ? response.slice(bodyStart + 4) : response;
365
+ const parsed = JSON.parse(body);
366
+ sendJson(res, parsed.ok ? 200 : 500, parsed);
367
+ }
368
+ catch (err) {
369
+ const message = err instanceof Error ? err.message : 'Failed to contact daemon';
370
+ sendJson(res, 503, { ok: false, error: `Daemon communication failed: ${message}` });
371
+ }
372
+ });
373
+ // ── Deep Current endpoints (v12.x) ─────────────────
374
+ addRoute('GET', '/api/danger-room/current', async (_req, res) => {
375
+ const situationPath = join(PROJECT_ROOT, 'logs', 'deep-current', 'situation.json');
376
+ const content = await readFileOrNull(situationPath);
377
+ if (!content) {
378
+ sendJson(res, 200, { initialized: false });
379
+ return;
380
+ }
381
+ try {
382
+ const situation = JSON.parse(content);
383
+ const proposalsDir = join(PROJECT_ROOT, 'logs', 'deep-current', 'proposals');
384
+ let latestProposal = null;
385
+ try {
386
+ // existsSync imported statically at top (Infinity Gauntlet ARCH-009)
387
+ if (existsSync(proposalsDir)) {
388
+ const files = await readdir(proposalsDir);
389
+ const mdFiles = files.filter(f => f.endsWith('.md')).sort().reverse();
390
+ if (mdFiles.length > 0) {
391
+ latestProposal = await readFileOrNull(join(proposalsDir, mdFiles[0]));
392
+ }
393
+ }
394
+ }
395
+ catch { /* no proposals dir */ }
396
+ sendJson(res, 200, { initialized: true, situation, latestProposal });
397
+ }
398
+ catch {
399
+ sendJson(res, 200, { initialized: false, error: 'Failed to parse situation model' });
400
+ }
401
+ });
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Deploy wizard API routes — project scanning for Haku.
3
+ */
4
+ export {};
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Deploy wizard API routes — project scanning for Haku.
3
+ */
4
+ import { readFile, access, realpath } from 'node:fs/promises';
5
+ import { join } from 'node:path';
6
+ import { addRoute } from '../router.js';
7
+ import { parseJsonBody } from '../lib/body-parser.js';
8
+ import { parseFrontmatter } from '../lib/frontmatter.js';
9
+ import { recommendInstanceType } from '../lib/instance-sizing.js';
10
+ import { sendJson } from '../lib/http-helpers.js';
11
+ // POST /api/deploy/scan — scan a project directory for deploy info
12
+ addRoute('POST', '/api/deploy/scan', async (req, res) => {
13
+ const body = await parseJsonBody(req);
14
+ if (!body.directory) {
15
+ sendJson(res, 400, { error: 'directory is required' });
16
+ return;
17
+ }
18
+ // SEC-010: Validate path — absolute, no traversal
19
+ if (!body.directory.startsWith('/') || body.directory.includes('..')) {
20
+ sendJson(res, 400, { error: 'directory must be an absolute path with no ".." segments' });
21
+ return;
22
+ }
23
+ let dir = body.directory;
24
+ // Check directory exists and resolve symlinks (IG-R4: use real path for all operations)
25
+ try {
26
+ await access(dir);
27
+ dir = await realpath(dir);
28
+ }
29
+ catch {
30
+ sendJson(res, 400, { error: `Directory not found: ${dir}` });
31
+ return;
32
+ }
33
+ // Check it's a VoidForge project (has CLAUDE.md)
34
+ try {
35
+ await access(join(dir, 'CLAUDE.md'));
36
+ }
37
+ catch {
38
+ sendJson(res, 400, { error: 'Not a VoidForge project — no CLAUDE.md found' });
39
+ return;
40
+ }
41
+ // Read project name from CLAUDE.md
42
+ let name = 'Unknown';
43
+ try {
44
+ const claudeMd = await readFile(join(dir, 'CLAUDE.md'), 'utf-8');
45
+ const nameMatch = claudeMd.match(/\*\*Name:\*\*\s*(.+)/);
46
+ if (nameMatch) {
47
+ const extracted = nameMatch[1].trim();
48
+ if (!extracted.startsWith('['))
49
+ name = extracted;
50
+ }
51
+ }
52
+ catch { /* use default */ }
53
+ // Read deploy target from .env
54
+ let deploy = '';
55
+ try {
56
+ const envContent = await readFile(join(dir, '.env'), 'utf-8');
57
+ const deployMatch = envContent.match(/DEPLOY_TARGET=(.+)/);
58
+ if (deployMatch) {
59
+ deploy = deployMatch[1].trim().replace(/^["']|["']$/g, '').split('#')[0].trim();
60
+ }
61
+ }
62
+ catch { /* no .env yet */ }
63
+ // Read framework/database/cache from PRD frontmatter
64
+ let framework = '';
65
+ let database = 'none';
66
+ let cache = 'none';
67
+ let instanceType = '';
68
+ let hostname = '';
69
+ let prdFrontmatter = {};
70
+ try {
71
+ const prd = await readFile(join(dir, 'docs', 'PRD.md'), 'utf-8');
72
+ const { frontmatter } = parseFrontmatter(prd);
73
+ prdFrontmatter = frontmatter;
74
+ if (frontmatter.framework)
75
+ framework = frontmatter.framework;
76
+ if (frontmatter.database)
77
+ database = frontmatter.database;
78
+ if (frontmatter.cache)
79
+ cache = frontmatter.cache;
80
+ if (frontmatter.deploy && !deploy)
81
+ deploy = frontmatter.deploy;
82
+ if (frontmatter.instance_type)
83
+ instanceType = frontmatter.instance_type;
84
+ if (frontmatter.hostname)
85
+ hostname = frontmatter.hostname;
86
+ }
87
+ catch { /* no PRD or no frontmatter */ }
88
+ // Also check .env for hostname if not in PRD
89
+ if (!hostname) {
90
+ try {
91
+ const envContent = await readFile(join(dir, '.env'), 'utf-8');
92
+ const hostnameMatch = envContent.match(/HOSTNAME=(.+)/);
93
+ if (hostnameMatch) {
94
+ hostname = hostnameMatch[1].trim().replace(/^["']|["']$/g, '').split('#')[0].trim();
95
+ }
96
+ }
97
+ catch { /* no .env */ }
98
+ }
99
+ // Auto-detect framework from files if not in PRD
100
+ if (!framework) {
101
+ try {
102
+ const pkg = await readFile(join(dir, 'package.json'), 'utf-8');
103
+ const pkgData = JSON.parse(pkg);
104
+ const deps = pkgData.dependencies || {};
105
+ if (deps['next'])
106
+ framework = 'next.js';
107
+ else if (deps['express'])
108
+ framework = 'express';
109
+ }
110
+ catch { /* not a Node project */ }
111
+ if (!framework) {
112
+ try {
113
+ const reqs = await readFile(join(dir, 'requirements.txt'), 'utf-8');
114
+ const reqsLower = reqs.toLowerCase();
115
+ if (reqsLower.includes('django'))
116
+ framework = 'django';
117
+ else
118
+ framework = 'python';
119
+ }
120
+ catch { /* not Python */ }
121
+ }
122
+ if (!framework) {
123
+ try {
124
+ await access(join(dir, 'Gemfile'));
125
+ framework = 'rails';
126
+ }
127
+ catch { /* not Ruby */ }
128
+ }
129
+ }
130
+ // Detect PostgreSQL extensions from Prisma schema
131
+ let extensions = [];
132
+ if (database === 'postgres') {
133
+ try {
134
+ const prismaSchema = await readFile(join(dir, 'prisma', 'schema.prisma'), 'utf-8');
135
+ const extMatch = prismaSchema.match(/extensions\s*=\s*\[([^\]]+)\]/);
136
+ if (extMatch) {
137
+ extensions = extMatch[1].split(',').map((e) => e.trim().replace(/["']/g, '')).filter(Boolean);
138
+ }
139
+ }
140
+ catch { /* no Prisma schema or no extensions */ }
141
+ }
142
+ // Auto-recommend instance type from PRD scope if not explicitly set
143
+ if (!instanceType && (deploy === 'vps' || !deploy)) {
144
+ instanceType = recommendInstanceType({
145
+ type: prdFrontmatter.type,
146
+ framework,
147
+ database,
148
+ cache,
149
+ workers: prdFrontmatter.workers,
150
+ payments: prdFrontmatter.payments,
151
+ });
152
+ }
153
+ sendJson(res, 200, {
154
+ valid: true,
155
+ name,
156
+ deploy: deploy || 'docker',
157
+ framework,
158
+ database,
159
+ cache,
160
+ instanceType: instanceType || 't3.micro',
161
+ hostname: hostname || '',
162
+ extensions,
163
+ });
164
+ });
@@ -0,0 +1 @@
1
+ export {};