@runeditrun/rer 0.0.0-stage → 0.1.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 (526) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -2
  3. package/bin/rer.mjs +3 -0
  4. package/cli/README.md +243 -0
  5. package/cli/adapter.mjs +10 -0
  6. package/cli/ai-local.mjs +65 -0
  7. package/cli/archive-path.mjs +52 -0
  8. package/cli/archive-vite.mjs +55 -0
  9. package/cli/archive.mjs +8 -0
  10. package/cli/auth-copy.mjs +80 -0
  11. package/cli/bounded.mjs +70 -0
  12. package/cli/cache-doctor.mjs +63 -0
  13. package/cli/container-transfer.mjs +35 -0
  14. package/cli/container.mjs +62 -0
  15. package/cli/contract.mjs +25 -0
  16. package/cli/dependencies.mjs +45 -0
  17. package/cli/dependency-approval.mjs +33 -0
  18. package/cli/dependency-doctor.mjs +34 -0
  19. package/cli/dependency-input.mjs +43 -0
  20. package/cli/deploy-config.mjs +118 -0
  21. package/cli/deploy.mjs +37 -0
  22. package/cli/dev-identity.mjs +80 -0
  23. package/cli/errors.mjs +14 -0
  24. package/cli/export.mjs +42 -0
  25. package/cli/font-cache.mjs +29 -0
  26. package/cli/hosted-output.mjs +110 -0
  27. package/cli/independent.mjs +29 -0
  28. package/cli/inspect.mjs +174 -0
  29. package/cli/jsonc.mjs +111 -0
  30. package/cli/lifecycle.mjs +148 -0
  31. package/cli/local.mjs +100 -0
  32. package/cli/main.mjs +57 -0
  33. package/cli/managed-server.mjs +143 -0
  34. package/cli/node-runtime-cache.mjs +68 -0
  35. package/cli/node-runtime-discovery.mjs +56 -0
  36. package/cli/node-runtime-download.mjs +59 -0
  37. package/cli/node-runtime.mjs +50 -0
  38. package/cli/output.mjs +22 -0
  39. package/cli/parse.mjs +77 -0
  40. package/cli/port.mjs +52 -0
  41. package/cli/portability-worker.mjs +56 -0
  42. package/cli/portability.mjs +66 -0
  43. package/cli/preview-cache.mjs +99 -0
  44. package/cli/project.mjs +48 -0
  45. package/cli/release.mjs +24 -0
  46. package/cli/runtime-front-door.mjs +46 -0
  47. package/cli/scheduled.mjs +81 -0
  48. package/cli/server-activity.mjs +12 -0
  49. package/cli/server-proxy.mjs +93 -0
  50. package/cli/server-registry.mjs +177 -0
  51. package/cli/server-resources.mjs +22 -0
  52. package/cli/settings.mjs +15 -0
  53. package/cli/setup-answers.mjs +29 -0
  54. package/cli/setup-input.mjs +113 -0
  55. package/cli/shared.mjs +7 -0
  56. package/cli/transfers.mjs +33 -0
  57. package/cli/version.mjs +36 -0
  58. package/composition/git-source.mjs +111 -0
  59. package/edition-authoring/files.mjs +81 -0
  60. package/edition-authoring/manifest.mjs +65 -0
  61. package/global/README.md +155 -0
  62. package/global/checkout.mjs +72 -0
  63. package/global/cli/command.mjs +13 -0
  64. package/global/cli/install.mjs +75 -0
  65. package/global/cli/layout.mjs +45 -0
  66. package/global/cli/status.mjs +54 -0
  67. package/global/cli/uninstall.mjs +36 -0
  68. package/global/delegate.mjs +13 -0
  69. package/global/launch/capacity.mjs +72 -0
  70. package/global/launch/dev.mjs +129 -0
  71. package/global/launch/facts.mjs +93 -0
  72. package/global/launch/failure.mjs +5 -0
  73. package/global/launch/local-defaults.mjs +4 -0
  74. package/global/launch/prepare.mjs +61 -0
  75. package/global/launch/render.mjs +44 -0
  76. package/global/launch/state.mjs +97 -0
  77. package/global/launch/steps.mjs +81 -0
  78. package/global/launch/worker.mjs +43 -0
  79. package/global/list.mjs +59 -0
  80. package/global/main.mjs +53 -0
  81. package/global/package-info.mjs +38 -0
  82. package/global/registry.mjs +24 -0
  83. package/global/rer.mjs +29 -0
  84. package/global/update/archive.mjs +89 -0
  85. package/global/update/command.mjs +90 -0
  86. package/global/update/latest.mjs +6 -0
  87. package/global/update/notice.mjs +58 -0
  88. package/global/update/registry.mjs +47 -0
  89. package/global/version.mjs +29 -0
  90. package/install.sh +144 -0
  91. package/modules/ai/LICENSE +21 -0
  92. package/modules/ai/README.md +90 -0
  93. package/modules/ai/client.mjs +71 -0
  94. package/modules/ai/config.mjs +39 -0
  95. package/modules/ai/dev/index.d.mts +6 -0
  96. package/modules/ai/dev/index.mjs +64 -0
  97. package/modules/ai/dev/outbox.mjs +33 -0
  98. package/modules/ai/errors.mjs +28 -0
  99. package/modules/ai/fake/index.d.mts +3 -0
  100. package/modules/ai/fake/index.mjs +82 -0
  101. package/modules/ai/index.d.mts +10 -0
  102. package/modules/ai/index.mjs +3 -0
  103. package/modules/ai/package.json +6 -0
  104. package/modules/container/README.md +156 -0
  105. package/modules/container/adapters/d1.mjs +34 -0
  106. package/modules/container/adapters/egress.mjs +40 -0
  107. package/modules/container/adapters/jobs.mjs +47 -0
  108. package/modules/container/adapters/objects.mjs +84 -0
  109. package/modules/container/adapters/retained.mjs +14 -0
  110. package/modules/container/adapters/sqlite-error.mjs +7 -0
  111. package/modules/container/adapters/sqlite-worker.mjs +26 -0
  112. package/modules/container/assets.mjs +14 -0
  113. package/modules/container/bindings.mjs +1 -0
  114. package/modules/container/boundary.mjs +38 -0
  115. package/modules/container/build.mjs +65 -0
  116. package/modules/container/deploy-input.mjs +17 -0
  117. package/modules/container/entrypoint.sh +5 -0
  118. package/modules/container/host.mjs +43 -0
  119. package/modules/container/ingress.mjs +23 -0
  120. package/modules/container/lock.mjs +11 -0
  121. package/modules/container/migrations.mjs +16 -0
  122. package/modules/container/node-engine.mjs +39 -0
  123. package/modules/container/portability-schema.mjs +14 -0
  124. package/modules/container/remote/deploy.py +76 -0
  125. package/modules/container/remote/files.py +275 -0
  126. package/modules/container/remote/resources.py +157 -0
  127. package/modules/container/runner.mjs +10 -0
  128. package/modules/container/ssh-plan.mjs +44 -0
  129. package/modules/container/ssh.mjs +63 -0
  130. package/modules/container/transfer.mjs +27 -0
  131. package/modules/container/worker-engine.mjs +32 -0
  132. package/modules/dependencies/README.md +211 -0
  133. package/modules/dependencies/approval.mjs +51 -0
  134. package/modules/dependencies/auth.mjs +40 -0
  135. package/modules/dependencies/authority.mjs +14 -0
  136. package/modules/dependencies/crypto.mjs +13 -0
  137. package/modules/dependencies/index.d.mts +23 -0
  138. package/modules/dependencies/index.mjs +113 -0
  139. package/modules/dependencies/logout.mjs +42 -0
  140. package/modules/dependencies/native.mjs +9 -0
  141. package/modules/dependencies/protocol.mjs +23 -0
  142. package/modules/dependencies/schema/approval-attempt.sql +3 -0
  143. package/modules/dependencies/schema/dependencies.sql +15 -0
  144. package/modules/dependencies/store.mjs +19 -0
  145. package/modules/deploy-kit/README.md +308 -0
  146. package/modules/deploy-kit/artifact.mjs +47 -0
  147. package/modules/deploy-kit/build-modules.mjs +21 -0
  148. package/modules/deploy-kit/check.mjs +13 -0
  149. package/modules/deploy-kit/cli.mjs +50 -0
  150. package/modules/deploy-kit/container-config.mjs +8 -0
  151. package/modules/deploy-kit/default-stage.mjs +14 -0
  152. package/modules/deploy-kit/deploy.mjs +80 -0
  153. package/modules/deploy-kit/health-request.mjs +86 -0
  154. package/modules/deploy-kit/health.mjs +99 -0
  155. package/modules/deploy-kit/index.mjs +12 -0
  156. package/modules/deploy-kit/local-only.mjs +10 -0
  157. package/modules/deploy-kit/migrations.mjs +48 -0
  158. package/modules/deploy-kit/owner-cli.mjs +63 -0
  159. package/modules/deploy-kit/owner.mjs +297 -0
  160. package/modules/deploy-kit/prepare.mjs +62 -0
  161. package/modules/deploy-kit/provider.mjs +99 -0
  162. package/modules/deploy-kit/queue-consumer.mjs +10 -0
  163. package/modules/deploy-kit/receipt.mjs +16 -0
  164. package/modules/deploy-kit/render.mjs +47 -0
  165. package/modules/deploy-kit/resources.mjs +106 -0
  166. package/modules/deploy-kit/service-grants.mjs +25 -0
  167. package/modules/deploy-kit/sql-safety.mjs +94 -0
  168. package/modules/deploy-kit/sql-tokens.mjs +49 -0
  169. package/modules/deploy-kit/teardown.mjs +100 -0
  170. package/modules/deploy-kit/templates.mjs +31 -0
  171. package/modules/deploy-kit/validation.mjs +58 -0
  172. package/modules/deploy-kit/worker.mjs +105 -0
  173. package/modules/erasure/CONTRACT.md +90 -0
  174. package/modules/erasure/README.md +221 -0
  175. package/modules/erasure/audit.mjs +23 -0
  176. package/modules/erasure/blobs.mjs +45 -0
  177. package/modules/erasure/cascade.mjs +91 -0
  178. package/modules/erasure/completion.mjs +62 -0
  179. package/modules/erasure/declaration.mjs +51 -0
  180. package/modules/erasure/fence.mjs +19 -0
  181. package/modules/erasure/index.d.mts +62 -0
  182. package/modules/erasure/index.mjs +85 -0
  183. package/modules/erasure/order.mjs +56 -0
  184. package/modules/erasure/plan.mjs +121 -0
  185. package/modules/erasure/rate.mjs +57 -0
  186. package/modules/erasure/receipt.mjs +32 -0
  187. package/modules/erasure/root.mjs +21 -0
  188. package/modules/erasure/schema/erasure-rates.sql +5 -0
  189. package/modules/erasure/schema/erasure.sql +27 -0
  190. package/modules/erasure/scope.mjs +29 -0
  191. package/modules/erasure/sql.mjs +23 -0
  192. package/modules/erasure/verification.mjs +69 -0
  193. package/modules/integrations/core/CONTRACT.md +50 -0
  194. package/modules/integrations/core/README.md +27 -0
  195. package/modules/integrations/core/crypto.ts +19 -0
  196. package/modules/integrations/core/database.ts +21 -0
  197. package/modules/integrations/core/delivery.ts +97 -0
  198. package/modules/integrations/core/destination.ts +13 -0
  199. package/modules/integrations/core/index.ts +7 -0
  200. package/modules/integrations/core/node-transport.ts +72 -0
  201. package/modules/integrations/core/public-address.ts +57 -0
  202. package/modules/integrations/core/repository.ts +74 -0
  203. package/modules/integrations/core/schema.sql +36 -0
  204. package/modules/integrations/core/sqlite.ts +20 -0
  205. package/modules/integrations/core/timestamped-body-signature.ts +38 -0
  206. package/modules/integrations/core/tokens.ts +71 -0
  207. package/modules/integrations/core/types.ts +52 -0
  208. package/modules/integrations/core/upgrade-token-usage-context.sql +5 -0
  209. package/modules/integrations/core/webhook.ts +73 -0
  210. package/modules/job-queue/README.md +173 -0
  211. package/modules/job-queue/cli.mjs +64 -0
  212. package/modules/job-queue/command.mjs +64 -0
  213. package/modules/job-queue/config.mjs +48 -0
  214. package/modules/job-queue/identity.mjs +48 -0
  215. package/modules/job-queue/index.mjs +135 -0
  216. package/modules/job-queue/membership.mjs +30 -0
  217. package/modules/job-queue/module.json +10 -0
  218. package/modules/job-queue/policy.mjs +29 -0
  219. package/modules/job-queue/processes.mjs +80 -0
  220. package/modules/job-queue/resources.mjs +37 -0
  221. package/modules/job-queue/storage.mjs +64 -0
  222. package/modules/job-queue/supervisor.mjs +30 -0
  223. package/modules/job-queue/waiting.mjs +16 -0
  224. package/modules/mail/LICENSE +21 -0
  225. package/modules/mail/README.md +336 -0
  226. package/modules/mail/doctor.mjs +81 -0
  227. package/modules/mail/index.mjs +8 -0
  228. package/modules/mail/live-proof/README.md +113 -0
  229. package/modules/mail/live-proof/run.mjs +55 -0
  230. package/modules/mail/package.json +19 -0
  231. package/modules/mail/pnpm-lock.yaml +537 -0
  232. package/modules/mail/src/adapters/cloudflare/README.md +84 -0
  233. package/modules/mail/src/adapters/cloudflare/headers.mjs +63 -0
  234. package/modules/mail/src/adapters/cloudflare/index.mjs +47 -0
  235. package/modules/mail/src/adapters/cloudflare/response.mjs +99 -0
  236. package/modules/mail/src/adapters/messaging/README.md +92 -0
  237. package/modules/mail/src/adapters/messaging/contract.mjs +66 -0
  238. package/modules/mail/src/adapters/messaging/event-schema.mjs +28 -0
  239. package/modules/mail/src/adapters/messaging/index.mjs +61 -0
  240. package/modules/mail/src/adapters/messaging/jws.mjs +68 -0
  241. package/modules/mail/src/adapters/messaging/request.mjs +52 -0
  242. package/modules/mail/src/adapters/messaging/serialize.mjs +21 -0
  243. package/modules/mail/src/adapters/resend/README.md +120 -0
  244. package/modules/mail/src/adapters/resend/index.mjs +22 -0
  245. package/modules/mail/src/adapters/resend/response.mjs +54 -0
  246. package/modules/mail/src/adapters/resend/serialize.mjs +46 -0
  247. package/modules/mail/src/adapters/resend/size.mjs +99 -0
  248. package/modules/mail/src/adapters/sendgrid/README.md +75 -0
  249. package/modules/mail/src/adapters/sendgrid/index.mjs +35 -0
  250. package/modules/mail/src/adapters/sendgrid/serialize.mjs +104 -0
  251. package/modules/mail/src/core/errors.mjs +9 -0
  252. package/modules/mail/src/core/transport.mjs +33 -0
  253. package/modules/mail/src/core/validation.mjs +172 -0
  254. package/modules/mail/src/development/index.d.mts +37 -0
  255. package/modules/mail/src/development/index.mjs +35 -0
  256. package/modules/mail/src/development/outbox.mjs +78 -0
  257. package/modules/mail/src/development/schema.sql +9 -0
  258. package/modules/mail/src/development/viewer-html.mjs +42 -0
  259. package/modules/mail/src/development/viewer.mjs +65 -0
  260. package/modules/mail/src/doctor/README.md +107 -0
  261. package/modules/mail/src/doctor/common.mjs +64 -0
  262. package/modules/mail/src/doctor/dns.mjs +102 -0
  263. package/modules/mail/src/doctor/index.mjs +28 -0
  264. package/modules/mail/src/doctor/probe.mjs +66 -0
  265. package/modules/mail/src/doctor/receive.mjs +81 -0
  266. package/modules/mail/src/doctor/records.mjs +45 -0
  267. package/modules/mail/src/events/README.md +268 -0
  268. package/modules/mail/src/events/d1-schema.sql +25 -0
  269. package/modules/mail/src/events/d1-storage.mjs +76 -0
  270. package/modules/mail/src/events/index.mjs +3 -0
  271. package/modules/mail/src/events/intake.mjs +201 -0
  272. package/modules/mail/src/events/storage.mjs +52 -0
  273. package/modules/mail/src/events/verify.mjs +92 -0
  274. package/modules/mail/src/inbound/README.md +137 -0
  275. package/modules/mail/src/inbound/bytes.mjs +91 -0
  276. package/modules/mail/src/inbound/headers.mjs +143 -0
  277. package/modules/mail/src/inbound/index.mjs +29 -0
  278. package/modules/mail/src/inbound/mime.mjs +36 -0
  279. package/modules/mail/src/inbound/multipart.mjs +27 -0
  280. package/modules/mail/src/inbound/resend-attachment.mjs +43 -0
  281. package/modules/mail/src/inbound/resend.mjs +54 -0
  282. package/modules/mail/src/inbound/sendgrid.mjs +60 -0
  283. package/modules/mail/src/sender.mjs +33 -0
  284. package/modules/mail/src/sendgrid-email/index.d.mts +33 -0
  285. package/modules/mail/src/sendgrid-email/index.mjs +86 -0
  286. package/modules/mail/src/unsubscribe/index.mjs +142 -0
  287. package/modules/operator-auth/README.md +599 -0
  288. package/modules/operator-auth/accounts/adapter.mjs +41 -0
  289. package/modules/operator-auth/accounts/admin.mjs +159 -0
  290. package/modules/operator-auth/accounts/credentials.mjs +27 -0
  291. package/modules/operator-auth/accounts/editor-owner.mjs +13 -0
  292. package/modules/operator-auth/accounts/index.d.mts +9 -0
  293. package/modules/operator-auth/accounts/index.mjs +9 -0
  294. package/modules/operator-auth/accounts/native.d.mts +38 -0
  295. package/modules/operator-auth/accounts/native.mjs +171 -0
  296. package/modules/operator-auth/accounts/oa.mjs +90 -0
  297. package/modules/operator-auth/accounts/provider-control.mjs +50 -0
  298. package/modules/operator-auth/accounts/provider-pin.mjs +14 -0
  299. package/modules/operator-auth/accounts/roles.mjs +26 -0
  300. package/modules/operator-auth/accounts/schema/dependency-setup.sql +17 -0
  301. package/modules/operator-auth/accounts/schema/dev-sessions.sql +22 -0
  302. package/modules/operator-auth/accounts/schema/domain-rules-step-up.sql +20 -0
  303. package/modules/operator-auth/accounts/schema/invitation-cancellation.sql +17 -0
  304. package/modules/operator-auth/accounts/schema/operator-auth.sql +142 -0
  305. package/modules/operator-auth/accounts/schema/provider-control.sql +30 -0
  306. package/modules/operator-auth/accounts/schema/provider-pins.sql +6 -0
  307. package/modules/operator-auth/accounts/settings-page.mjs +4 -0
  308. package/modules/operator-auth/accounts/store.mjs +246 -0
  309. package/modules/operator-auth/branding.mjs +66 -0
  310. package/modules/operator-auth/config.mjs +35 -0
  311. package/modules/operator-auth/conformance/README.md +81 -0
  312. package/modules/operator-auth/conformance/index.d.mts +43 -0
  313. package/modules/operator-auth/conformance/index.mjs +300 -0
  314. package/modules/operator-auth/conformance/presentation.mjs +57 -0
  315. package/modules/operator-auth/crypto.mjs +17 -0
  316. package/modules/operator-auth/dev/badge.mjs +62 -0
  317. package/modules/operator-auth/dev/index.d.mts +14 -0
  318. package/modules/operator-auth/dev/index.mjs +137 -0
  319. package/modules/operator-auth/dev/personas.mjs +17 -0
  320. package/modules/operator-auth/dev/sign-in-as.d.mts +3 -0
  321. package/modules/operator-auth/dev/sign-in-as.mjs +25 -0
  322. package/modules/operator-auth/dev-admission.d.mts +4 -0
  323. package/modules/operator-auth/dev-admission.mjs +10 -0
  324. package/modules/operator-auth/dev-gate.mjs +86 -0
  325. package/modules/operator-auth/docs/README.md +78 -0
  326. package/modules/operator-auth/docs/native-accounts.md +313 -0
  327. package/modules/operator-auth/docs/tanstack-start.md +218 -0
  328. package/modules/operator-auth/docs/vite-spa.md +144 -0
  329. package/modules/operator-auth/errors.mjs +9 -0
  330. package/modules/operator-auth/http.mjs +42 -0
  331. package/modules/operator-auth/identity/admission.mjs +105 -0
  332. package/modules/operator-auth/identity/after-confirm.mjs +18 -0
  333. package/modules/operator-auth/identity/email-link/confirmation.mjs +14 -0
  334. package/modules/operator-auth/identity/email-link/index.d.mts +48 -0
  335. package/modules/operator-auth/identity/email-link/index.mjs +128 -0
  336. package/modules/operator-auth/identity/email-link/message.mjs +16 -0
  337. package/modules/operator-auth/identity/index.d.mts +7 -0
  338. package/modules/operator-auth/identity/index.mjs +5 -0
  339. package/modules/operator-auth/identity/oidc/README.md +171 -0
  340. package/modules/operator-auth/identity/oidc/client-auth.mjs +16 -0
  341. package/modules/operator-auth/identity/oidc/config.mjs +102 -0
  342. package/modules/operator-auth/identity/oidc/crypto.mjs +29 -0
  343. package/modules/operator-auth/identity/oidc/errors.mjs +5 -0
  344. package/modules/operator-auth/identity/oidc/index.d.mts +33 -0
  345. package/modules/operator-auth/identity/oidc/index.mjs +287 -0
  346. package/modules/operator-auth/identity/oidc/jwt.mjs +102 -0
  347. package/modules/operator-auth/identity/oidc/network.mjs +93 -0
  348. package/modules/operator-auth/identity/oidc/presets.mjs +26 -0
  349. package/modules/operator-auth/identity/oidc/schema/oidc.sql +50 -0
  350. package/modules/operator-auth/identity/oidc/store.mjs +68 -0
  351. package/modules/operator-auth/identity/principal.mjs +28 -0
  352. package/modules/operator-auth/index.d.mts +223 -0
  353. package/modules/operator-auth/index.mjs +11 -0
  354. package/modules/operator-auth/input.mjs +55 -0
  355. package/modules/operator-auth/local-cli.mjs +16 -0
  356. package/modules/operator-auth/page.mjs +34 -0
  357. package/modules/operator-auth/policy.mjs +20 -0
  358. package/modules/operator-auth/routes.mjs +91 -0
  359. package/modules/operator-auth/service.mjs +152 -0
  360. package/modules/operator-auth/signin/index.mjs +67 -0
  361. package/modules/owner-claim/README.md +137 -0
  362. package/modules/owner-claim/idp.mjs +40 -0
  363. package/modules/owner-claim/index.d.mts +27 -0
  364. package/modules/owner-claim/index.mjs +39 -0
  365. package/modules/owner-claim/page.mjs +11 -0
  366. package/modules/owner-claim/schema/idp.sql +4 -0
  367. package/modules/owner-claim/schema/owner-claim.sql +18 -0
  368. package/modules/owner-claim/transaction.mjs +87 -0
  369. package/modules/owner-claim/verify.mjs +80 -0
  370. package/modules/portability/README.md +444 -0
  371. package/modules/portability/bridge.mjs +102 -0
  372. package/modules/portability/catalogue.mjs +118 -0
  373. package/modules/portability/cli-options.mjs +38 -0
  374. package/modules/portability/cli.mjs +56 -0
  375. package/modules/portability/copies.mjs +68 -0
  376. package/modules/portability/dev-data.mjs +32 -0
  377. package/modules/portability/diff.mjs +33 -0
  378. package/modules/portability/export.mjs +129 -0
  379. package/modules/portability/files.mjs +65 -0
  380. package/modules/portability/format.mjs +122 -0
  381. package/modules/portability/generated.mjs +60 -0
  382. package/modules/portability/hash.mjs +79 -0
  383. package/modules/portability/http.mjs +42 -0
  384. package/modules/portability/import.mjs +185 -0
  385. package/modules/portability/index.d.mts +58 -0
  386. package/modules/portability/index.mjs +22 -0
  387. package/modules/portability/journal.mjs +49 -0
  388. package/modules/portability/legacy-files.mjs +43 -0
  389. package/modules/portability/legacy-safety.mjs +27 -0
  390. package/modules/portability/lines.mjs +36 -0
  391. package/modules/portability/migration-history.mjs +18 -0
  392. package/modules/portability/migration-statements.mjs +23 -0
  393. package/modules/portability/payloads.mjs +52 -0
  394. package/modules/portability/provider-data.mjs +25 -0
  395. package/modules/portability/restore-safety.mjs +30 -0
  396. package/modules/portability/schema/portability.sql +25 -0
  397. package/modules/portability/storage.mjs +49 -0
  398. package/modules/release/README.md +212 -0
  399. package/modules/release/catalogue.mjs +79 -0
  400. package/modules/release/export.mjs +51 -0
  401. package/modules/release/produce.mjs +136 -0
  402. package/modules/release/public-child-transport-state.cjs +29 -0
  403. package/modules/release/public-child.mjs +71 -0
  404. package/modules/release/public-git.mjs +21 -0
  405. package/modules/release/publication-scan.mjs +22 -0
  406. package/modules/release/publish-handoff.mjs +37 -0
  407. package/modules/release/source.mjs +71 -0
  408. package/modules/release/test-summary.mjs +50 -0
  409. package/onboarding/README.md +163 -0
  410. package/onboarding/activity.mjs +31 -0
  411. package/onboarding/adapter-auth.mjs +88 -0
  412. package/onboarding/adapter-hosting.mjs +35 -0
  413. package/onboarding/adapter-labels.mjs +13 -0
  414. package/onboarding/adapter-settings.mjs +129 -0
  415. package/onboarding/adapter.mjs +238 -0
  416. package/onboarding/agent-handoff.mjs +9 -0
  417. package/onboarding/app-processes.mjs +21 -0
  418. package/onboarding/archive-output.d.mts +3 -0
  419. package/onboarding/archive-output.mjs +54 -0
  420. package/onboarding/command-custody.mjs +24 -0
  421. package/onboarding/command-recovery.mjs +8 -0
  422. package/onboarding/commands.mjs +166 -0
  423. package/onboarding/dependency-approval.mjs +36 -0
  424. package/onboarding/dev-identity.mjs +23 -0
  425. package/onboarding/email.mjs +24 -0
  426. package/onboarding/failures.mjs +42 -0
  427. package/onboarding/input-limits.mjs +7 -0
  428. package/onboarding/installer-inputs.mjs +24 -0
  429. package/onboarding/launcher.mjs +34 -0
  430. package/onboarding/local/build-files.mjs +64 -0
  431. package/onboarding/local/build.mjs +49 -0
  432. package/onboarding/local/environment.mjs +30 -0
  433. package/onboarding/local/files.mjs +35 -0
  434. package/onboarding/local/operation.mjs +166 -0
  435. package/onboarding/local/owner.mjs +34 -0
  436. package/onboarding/local/process-table.mjs +114 -0
  437. package/onboarding/local/recovery.mjs +79 -0
  438. package/onboarding/local/state.mjs +31 -0
  439. package/onboarding/metadata.mjs +7 -0
  440. package/onboarding/operator-install.mjs +45 -0
  441. package/onboarding/operator-installer.mjs +39 -0
  442. package/onboarding/operator-prerequisites.mjs +27 -0
  443. package/onboarding/package.json +6 -0
  444. package/onboarding/prefill.mjs +16 -0
  445. package/onboarding/private-files.mjs +97 -0
  446. package/onboarding/private.mjs +35 -0
  447. package/onboarding/process-cleanup.mjs +134 -0
  448. package/onboarding/process-diagnostics.mjs +91 -0
  449. package/onboarding/process-scope.mjs +118 -0
  450. package/onboarding/process-table.mjs +118 -0
  451. package/onboarding/process-tree.mjs +36 -0
  452. package/onboarding/production-modules.mjs +22 -0
  453. package/onboarding/production-options.mjs +40 -0
  454. package/onboarding/production-plan.mjs +8 -0
  455. package/onboarding/production-runtime.mjs +30 -0
  456. package/onboarding/project-vite.mjs +14 -0
  457. package/onboarding/providers.mjs +289 -0
  458. package/onboarding/queue.mjs +53 -0
  459. package/onboarding/queued-entry.mjs +34 -0
  460. package/onboarding/readiness.mjs +59 -0
  461. package/onboarding/redaction-jwt.mjs +32 -0
  462. package/onboarding/redaction.mjs +298 -0
  463. package/onboarding/schema/SETUP.schema.json +514 -0
  464. package/onboarding/schema/dependencies.mjs +35 -0
  465. package/onboarding/schema/json-schema.mjs +202 -0
  466. package/onboarding/schema/setup-validation.mjs +325 -0
  467. package/onboarding/secrets.mjs +21 -0
  468. package/onboarding/server.mjs +188 -0
  469. package/onboarding/setup-manifest.mjs +116 -0
  470. package/onboarding/state.mjs +46 -0
  471. package/onboarding/theme-files.mjs +37 -0
  472. package/onboarding/theme.mjs +118 -0
  473. package/onboarding/ui/activity.js +98 -0
  474. package/onboarding/ui/address-client.mjs +33 -0
  475. package/onboarding/ui/app.js +377 -0
  476. package/onboarding/ui/index.html +1 -0
  477. package/onboarding/ui/model-core.mjs +127 -0
  478. package/onboarding/ui/model.mjs +5 -0
  479. package/onboarding/ui/style.css +63 -0
  480. package/onboarding/ui/theme.css +37 -0
  481. package/onboarding/ui/workspace-art.js +30 -0
  482. package/onboarding/wrangler-auth.mjs +43 -0
  483. package/operations/README.md +246 -0
  484. package/operations/operations.mjs +168 -0
  485. package/operations/project-operations.mjs +157 -0
  486. package/package.json +11 -4
  487. package/scripts/publish-check/README.md +72 -0
  488. package/scripts/publish-check/checksum_maps.py +52 -0
  489. package/scripts/publish-check/cli.py +34 -0
  490. package/scripts/publish-check/command_arguments.py +51 -0
  491. package/scripts/publish-check/command_shell_quotes.py +41 -0
  492. package/scripts/publish-check/command_source_quotes.py +120 -0
  493. package/scripts/publish-check/content_identities.py +250 -0
  494. package/scripts/publish-check/copy_identities.py +41 -0
  495. package/scripts/publish-check/der.py +61 -0
  496. package/scripts/publish-check/destination.py +117 -0
  497. package/scripts/publish-check/env_values.py +74 -0
  498. package/scripts/publish-check/font_licence.py +23 -0
  499. package/scripts/publish-check/git_history.py +115 -0
  500. package/scripts/publish-check/github_refs.py +217 -0
  501. package/scripts/publish-check/gitleaks.py +72 -0
  502. package/scripts/publish-check/gitleaks.toml +40 -0
  503. package/scripts/publish-check/history_scan.py +176 -0
  504. package/scripts/publish-check/hit_location.py +18 -0
  505. package/scripts/publish-check/hygiene_config.py +42 -0
  506. package/scripts/publish-check/hygiene_text.py +11 -0
  507. package/scripts/publish-check/identity-recipe.mjs +54 -0
  508. package/scripts/publish-check/json_identity.py +79 -0
  509. package/scripts/publish-check/license-exceptions.json +11 -0
  510. package/scripts/publish-check/licenses.py +49 -0
  511. package/scripts/publish-check/policy.py +18 -0
  512. package/scripts/publish-check/project_identity.py +89 -0
  513. package/scripts/publish-check/provenance.py +91 -0
  514. package/scripts/publish-check/provenance_pins.py +33 -0
  515. package/scripts/publish-check/public_base.py +63 -0
  516. package/scripts/publish-check/public_hygiene.py +80 -0
  517. package/scripts/publish-check/publication-config.json +12 -0
  518. package/scripts/publish-check/publication_config.py +34 -0
  519. package/scripts/publish-check/report.py +52 -0
  520. package/scripts/publish-check/repository_commands.py +75 -0
  521. package/scripts/publish-check/repository_intent.py +52 -0
  522. package/scripts/publish-check/reviewed.py +41 -0
  523. package/scripts/publish-check/source_hashes.mjs +6 -0
  524. package/scripts/publish-check/tree_scan.py +88 -0
  525. package/scripts/publish-check/trust_bundle.py +87 -0
  526. package/scripts/publish-check.mjs +58 -0
package/cli/deploy.mjs ADDED
@@ -0,0 +1,37 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import { contained } from './project.mjs';
4
+ import { missing, invalid, refuse } from './errors.mjs';
5
+ import { ownerPlan, ownerWorkerName, localOnlyBinding } from '../modules/deploy-kit/owner.mjs';
6
+ import { packageBuild } from '../modules/deploy-kit/prepare.mjs';
7
+ import { refuseLocalModels } from '../modules/deploy-kit/local-only.mjs';
8
+
9
+ export function ownerDeploy(declarationPath, lifecycle) {
10
+ // rer verify serves the build with this declaration's vars and secrets.
11
+ return Object.assign(async ({ root, options, store, signal, operationToken, localOnlyKeys = [], checkBuild = async () => {} }) => {
12
+ try { await refuseLocalModels(root); } catch { throw refuse('Deploy refuses the local-only key file .rer/local/models.json. Remove live-dev credentials before deployment.'); }
13
+ const path = await contained(root, declarationPath);
14
+ let declaration; try { declaration = JSON.parse(await readFile(path, 'utf8')); } catch { throw invalid('Malformed owner-account deploy declaration.'); }
15
+ const local = localOnlyBinding(declaration, localOnlyKeys);
16
+ if (local) throw refuse(`${declarationPath} names the local-only key ${local} as a deploy binding or secret. Remove it; local dev keys never reach a deployment.`);
17
+ const environment = options.environment ?? 'production';
18
+ await lifecycle.run(root, {}, store, () => {}, signal, { verb: 'build', operationToken });
19
+ await checkBuild();
20
+ if (options.dryRun) {
21
+ // Locally validate the real declaration/artifact without ownerTarget's
22
+ // account lookup. This synthetic target is validation data, not a provider.
23
+ const worker = ownerWorkerName(declaration.project, environment);
24
+ const artifact = await packageBuild({ root, source: { sha: '0'.repeat(40), tree: '0'.repeat(40) }, runIdentity: { id: '0', attempt: '1' }, productDirectory: '' });
25
+ ownerPlan({ project: declaration.project, environment, worker, origin: `https://${worker}.example.invalid` }, declaration, artifact, { localOnlyKeys });
26
+ store.report('deploy: dry run complete; no deployment occurred and no provider request was made.');
27
+ return { dryRun: true, deployed: false };
28
+ }
29
+ const names = ['CLOUDFLARE_ACCOUNT_ID', 'CLOUDFLARE_API_TOKEN'];
30
+ if (names.some(name => !process.env[name]?.trim())) throw missing('CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN are required. See the deploy-kit owner-account instructions.');
31
+ const { main } = await import('../modules/deploy-kit/owner-cli.mjs');
32
+ const argv = ['deploy', '--declaration', declarationPath, '--environment', environment];
33
+ const result = await main(argv, process.env, { cwd: root, localOnlyKeys, notice: line => store.report(line) });
34
+ await main(['health', ...argv.slice(1)], process.env, { cwd: root, localOnlyKeys });
35
+ return { deployed: true, origin: result.origin };
36
+ }, { declaration: declarationPath });
37
+ }
@@ -0,0 +1,80 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { lstat, open, readdir } from 'node:fs/promises';
3
+ import { join, relative } from 'node:path';
4
+ import { shared } from './shared.mjs';
5
+ import { contained } from './project.mjs';
6
+ import { invalid, missing, refuse } from './errors.mjs';
7
+ import { loopback } from './parse.mjs';
8
+
9
+ // The per-checkout key gate. Setup writes RER_DEV_KEY (32 random bytes) to
10
+ // owner-only .rer/local/ (directory 0700, file 0600); a repeat setup keeps it,
11
+ // reset removes it with the rest of .rer/local. Only local runtimes receive it.
12
+ export const DEV_KEY_FILE = '.rer/local/dev-key.env';
13
+ export const DEV_KEY_HEADER = 'x-rer-dev-key';
14
+ // modules/operator-auth/dev/index.mjs DEV_LAYER_MARKER: present in any output
15
+ // that bundles the local-only dev layer.
16
+ export const DEV_LAYER_MARKER = 'rer-dev-layer:local-only-never-deploy';
17
+ export const DEV_AI_MARKER = 'rer-ai-dev:local-only-never-deploy';
18
+ const KEY = /^RER_DEV_KEY=([A-Za-z0-9_-]{43,256})\n?$/;
19
+
20
+ export async function ensureDevKey(root) {
21
+ const existing = await readDevKey(root);
22
+ if (existing) return existing;
23
+ const { privateDirectory, writePrivateFile } = await shared('private-files.mjs');
24
+ const directory = await privateDirectory(root, ['.rer', 'local']), key = randomBytes(32).toString('base64url');
25
+ await writePrivateFile(join(directory, 'dev-key.env'), `RER_DEV_KEY=${key}\n`);
26
+ return key;
27
+ }
28
+
29
+ export async function readDevKey(root) {
30
+ let path; try { path = await contained(root, DEV_KEY_FILE, { privateFile: true }); } catch (error) { if (error.code === 'missing_input') return null; throw error; }
31
+ const { privateDirectory, readPrivateFile } = await shared('private-files.mjs');
32
+ await privateDirectory(root, ['.rer', 'local']);
33
+ const key = KEY.exec(await readPrivateFile(path))?.[1];
34
+ if (!key) throw refuse(`${DEV_KEY_FILE} is malformed. Run rer reset --yes, then rer setup, for a new key.`);
35
+ return key;
36
+ }
37
+
38
+ export async function requireDevKey(root) {
39
+ const key = await readDevKey(root);
40
+ if (!key) throw missing(`The local dev key ${DEV_KEY_FILE} is missing. Run rer setup to create it.`);
41
+ return key;
42
+ }
43
+
44
+ // RER_OPEN carries the key in the fragment only: browsers never send a
45
+ // fragment to a server, and the page removes it from the address bar.
46
+ export function openUrl(url, key, { path = '/', as, next } = {}) {
47
+ const fragment = new URLSearchParams({ 'rer-dev': key, ...(as ? { as } : {}), ...(next ? { next } : {}) });
48
+ return `${url}${path}#${fragment}`;
49
+ }
50
+
51
+ export function refuseManagedInstallation(...sources) {
52
+ for (const vars of sources) {
53
+ if (!vars?.RER_INSTALLATION_ID || !vars.RER_MANAGEMENT_ORIGIN) continue;
54
+ let host; try { host = new URL(vars.RER_MANAGEMENT_ORIGIN).hostname.replace(/^\[|\]$/g, ''); } catch {}
55
+ if (!loopback(host)) throw refuse('Dev identity refuses to start beside RER_INSTALLATION_ID with a non-loopback RER_MANAGEMENT_ORIGIN. Remove them from local settings or run rer dev --login.');
56
+ }
57
+ }
58
+
59
+ // The persona list the running app declares, read with the key header.
60
+ export async function resolvePersona(url, key, as, signal) {
61
+ const response = await fetch(url + '/__dev/personas', { headers: { [DEV_KEY_HEADER]: key }, signal: AbortSignal.any([signal, AbortSignal.timeout(10000)].filter(Boolean)) });
62
+ if (!response.ok) { await response.body?.cancel(); throw refuse(`The application did not serve dev identity (HTTP ${response.status}). Check its local-only entry.`); }
63
+ const personas = await response.json(), persona = personas.find(entry => entry.as === as);
64
+ if (!persona) throw invalid(`Unknown dev persona ${as}. This app declares: ${personas.map(entry => entry.as).join(', ')}.`);
65
+ return persona;
66
+ }
67
+
68
+ // rer build refuses output that bundles the dev layer, naming the file.
69
+ export async function refuseDevLayerOutput(root, devIdentity) {
70
+ const markers = [DEV_LAYER_MARKER, DEV_AI_MARKER].map(value => Buffer.from(value));
71
+ async function walk(path) {
72
+ let status; try { status = await lstat(path); } catch (error) { if (error.code === 'ENOENT') return; throw error; }
73
+ if (status.isDirectory()) { for (const entry of (await readdir(path)).sort()) await walk(join(path, entry)); return; }
74
+ if (!status.isFile()) return;
75
+ const file = await open(path, 'r');
76
+ try { const bytes = await file.readFile(); if (markers.some(marker => bytes.includes(marker))) throw refuse(`The build output contains the local-only dev layer: ${relative(root, path)}. Import dev modules only from the local entry ${devIdentity?.entry ?? 'server.dev'}.`); }
77
+ finally { await file.close(); }
78
+ }
79
+ for (const output of devIdentity?.output ?? ['dist']) await walk(await contained(root, output, { optional: true }));
80
+ }
package/cli/errors.mjs ADDED
@@ -0,0 +1,14 @@
1
+ export class RerError extends Error {
2
+ constructor(code, message, exitCode, details = {}) { super(message); Object.assign(this, { code, exitCode, ...details }); }
3
+ }
4
+ export const invalid = message => new RerError('invalid_invocation', message, 2);
5
+ export const missing = message => new RerError('missing_input', message, 3);
6
+ export const refuse = message => new RerError('safety_refusal', message, 4);
7
+
8
+ export async function lifecycleError(error, store) {
9
+ if(error instanceof RerError)return error;
10
+ const {shared}=await import('./shared.mjs');
11
+ const {approvedFailure}=await shared('failures.mjs');
12
+ const approved=approvedFailure(error);
13
+ return new RerError(approved?.code==='owner_conflict'?'safety_refusal':'command_failed', approved?.message ?? `${store?.state?.step ?? 'Local lifecycle'} failed. Inspect the private redacted capture log.`, approved?.exitCode ?? 1, {childExitCode:approved?.childExitCode,signal:approved?.signal});
14
+ }
package/cli/export.mjs ADDED
@@ -0,0 +1,42 @@
1
+ import { cp, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';
2
+ import { createHash } from 'node:crypto';
3
+ import { resolve, dirname, relative } from 'node:path';
4
+ import { testSource } from '../composition/git-source.mjs';
5
+ import { cliVersion } from './version.mjs';
6
+
7
+ // Copy whole maintained source/tooling directories. Python publication scans
8
+ // load sibling modules and data dynamically; they belong in the inventory too.
9
+ export const sourceRoots = ['bin/rer.mjs', 'cli', 'modules/job-queue', 'modules/release', 'modules/deploy-kit', 'modules/container', 'modules/integrations/core', 'modules/mail', 'modules/ai', 'modules/portability', 'modules/erasure', 'modules/dependencies', 'modules/operator-auth', 'modules/owner-claim', 'operations', 'scripts/publish-check', 'scripts/publish-check.mjs', 'composition/git-source.mjs', 'edition-authoring/manifest.mjs', 'edition-authoring/files.mjs'];
10
+ export async function inventory(base, roots = sourceRoots) {
11
+ const files = [];
12
+ async function walk(path) {
13
+ for (const entry of (await readdir(resolve(base, path), { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
14
+ if (['node_modules', '__pycache__', '.state', '.wrangler', '.rer', '.seed', '.cache'].includes(entry.name)) continue;
15
+ const name = `${path}/${entry.name}`;
16
+ if (testSource(name)) continue;
17
+ if (entry.isDirectory()) await walk(name);
18
+ else if (entry.isFile()) files.push(name);
19
+ else throw Error('Non-file CLI export source');
20
+ }
21
+ }
22
+ for (const source of roots) { if (source.endsWith('.mjs')) files.push(source); else await walk(source); }
23
+ return files.sort();
24
+ }
25
+
26
+ export async function exportCLI(base, destination) {
27
+ const target = resolve(destination, 'scripts/base-cli');
28
+ await rm(target, { recursive: true, force: true });
29
+ await mkdir(target, { recursive: true });
30
+ const files = await inventory(base), record = {};
31
+ for (const path of files) {
32
+ const bytes = await readFile(resolve(base, path));
33
+ await mkdir(dirname(resolve(target, path)), { recursive: true });
34
+ await cp(resolve(base, path), resolve(target, path));
35
+ record[path] = { source: path, sha256: createHash('sha256').update(bytes).digest('hex') };
36
+ }
37
+ const onboardingFiles=Object.fromEntries(await Promise.all((await inventory(base,['onboarding'])).map(async path=>[path.slice('onboarding/'.length),{source:path,sha256:createHash('sha256').update(await readFile(resolve(base,path))).digest('hex')}])));
38
+ // cliVersion: the rer version of this Base source, which `rer describe` reports.
39
+ const version = cliVersion(base);
40
+ await writeFile(resolve(target, 'inventory.json'), JSON.stringify({ version: 1, ...(version ? { cliVersion: version } : {}), onboarding: '../base-onboarding', onboardingFiles, files: record }, null, 2) + '\n');
41
+ await writeFile(resolve(destination, 'scripts/rer.mjs'), "#!/usr/bin/env node\nimport { main } from './base-cli/cli/main.mjs';\nprocess.exitCode = await main();\n");
42
+ }
@@ -0,0 +1,29 @@
1
+ // Fonts must survive stylesheet replacement during dev hydration without a
2
+ // network revalidation. Keep all other response headers (including repeated
3
+ // Set-Cookie fields) in their original order.
4
+ export function fontResponseHeaders(url, status, headers, requestHeaders = {}) {
5
+ if (![200, 206, 304].includes(status)) return headers;
6
+ let path;
7
+ try { path = new URL(url, 'http://localhost'); } catch { return headers; }
8
+ const index = headers.findIndex((value, index) => index % 2 === 0 && value.toLowerCase() === 'content-type');
9
+ const mime = index < 0 ? '' : headers[index + 1].split(';', 1)[0].trim().toLowerCase();
10
+ const fontType = /^(?:font\/[^;]+|application\/(?:font-(?:woff2?|sfnt)|x-font-(?:woff2?|ttf|otf|truetype|opentype)|vnd\.ms-opentype))$/.test(mime);
11
+ // Vite's ?import font modules are JavaScript, not font bytes. Likewise, an
12
+ // HTML route/error with a font suffix must retain its original policy.
13
+ // Conditional Vite module responses may omit Content-Type. Their transform
14
+ // queries or script destination still distinguish them from font bytes.
15
+ const transformed = requestHeaders['sec-fetch-dest'] === 'script' || ['import', 'raw', 'url', 'inline'].some(name => path.searchParams.has(name));
16
+ const fontPath = /\.(?:woff2?|ttf|otf)$/i.test(path.pathname) && (!mime || mime === 'application/octet-stream') &&
17
+ (requestHeaders['sec-fetch-dest'] === 'font' || !transformed);
18
+ if (!fontType && !fontPath) return headers;
19
+ const packageFont = path.pathname.split('/').includes('node_modules');
20
+ const hashed = /[.-][a-f0-9]{8,}\.(?:woff2?|ttf|otf)$/i.test(path.pathname) ||
21
+ /\/assets\/[^/]+-[A-Za-z0-9_-]{8}\.(?:woff2?|ttf|otf)$/.test(path.pathname) ||
22
+ /^[a-f0-9]{8,}$/i.test(path.searchParams.get('v') ?? '');
23
+ const result = [];
24
+ for (let i = 0; i < headers.length; i += 2) {
25
+ if (headers[i].toLowerCase() !== 'cache-control') result.push(headers[i], headers[i + 1]);
26
+ }
27
+ result.push('Cache-Control', packageFont || hashed ? 'public, max-age=31536000, immutable' : 'public, max-age=3600');
28
+ return result;
29
+ }
@@ -0,0 +1,110 @@
1
+ import { readFile, readdir } from 'node:fs/promises';
2
+ import { createRequire } from 'node:module';
3
+ import { createHash, randomBytes } from 'node:crypto';
4
+ import { join, resolve, extname } from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import { RUN_WORKER_FIRST } from '../modules/deploy-kit/render.mjs';
7
+ import { publicAssetPaths } from '../modules/deploy-kit/artifact.mjs';
8
+ import { migrate } from '../modules/container/migrations.mjs';
9
+ import { RerError } from './errors.mjs';
10
+
11
+ // Serve the production build the way rer deploy hosts it (assets behind the
12
+ // Worker, deploy identity bound, a public https origin) on loopback, with no
13
+ // network or account. The ingress hands the app the hosted URL and Host.
14
+ const ORIGIN = 'https://rer-hosted-check.invalid';
15
+ const ingress = `export default {fetch(request,env){const url=new URL(request.headers.get('x-rer-original-url')),headers=new Headers(request.headers);headers.delete('x-rer-original-url');headers.set('host',url.host);return env.APP.fetch(new Request(url,{method:request.method,headers,redirect:'manual',...(!['GET','HEAD'].includes(request.method)?{body:request.body}:{})}))}}`;
16
+ const read = async path => JSON.parse(await readFile(path, 'utf8'));
17
+ const optional = path => read(path).catch(error => { if (error.code === 'ENOENT') return {}; throw error; });
18
+
19
+ async function files(root, directory = '') {
20
+ const result = [];
21
+ for (const entry of await readdir(join(root, directory), { withFileTypes: true })) {
22
+ const path = directory ? `${directory}/${entry.name}` : entry.name;
23
+ if (entry.isDirectory()) result.push(...await files(root, path)); else if (entry.isFile()) result.push(path);
24
+ }
25
+ return result.sort();
26
+ }
27
+ const moduleTypes = { '.js':'ESModule', '.mjs':'ESModule', '.cjs':'CommonJS', '.wasm':'CompiledWasm', '.bin':'Data', '.txt':'Text', '.html':'Text', '.sql':'Text' };
28
+
29
+ // The seed's own wrangler carries the Miniflare its preview already runs.
30
+ async function miniflareFor(root) {
31
+ const wrangler = createRequire(join(root, 'package.json')).resolve('wrangler/package.json');
32
+ return import(pathToFileURL(createRequire(wrangler).resolve('miniflare')));
33
+ }
34
+
35
+ // Deploy binds its declared secrets; stand-ins follow the setup manifest's kind or default.
36
+ const standIn = field => field?.kind === 'email' ? 'owner@example.com' : typeof field?.default === 'string' && field.default ? field.default : randomBytes(24).toString('hex');
37
+ const links = html => [...html.matchAll(/(?:src|href)=["'](\/[^"'#?\s]+)/g)].map(match => match[1].slice(1)).filter(path => !path.startsWith('/') && /\.[A-Za-z0-9]+$/.test(path));
38
+
39
+ /** The build served as rer deploy hosts it, with the vars, origin and (stand-in) secrets of its
40
+ * deploy declaration. `fetch(path, init)` answers for the deployed origin. */
41
+ export async function serveHosted(root, { declaration: declarationPath = 'deploy.json' } = {}) {
42
+ const build = join(root, 'dist/server'), client = join(root, 'dist/client');
43
+ let config;
44
+ try { config = await read(join(build, 'wrangler.json')); }
45
+ catch { throw new RerError('hosted_output_missing', 'No production build at dist/server/wrangler.json. Run rer build, then rer verify.', 1); }
46
+ let declaration;
47
+ try { declaration = await read(join(root, declarationPath)); }
48
+ catch { throw new RerError('hosted_output_missing', `No readable deploy declaration at ${declarationPath}. A seed whose deploy reads another file names it with the commandAdapter option deployDeclaration.`, 1); }
49
+ const manifest = await optional(join(root, 'rer-setup.json'));
50
+ const { Miniflare, convertV4MiniflareOptions = options => options } = await miniflareFor(root);
51
+ const entry = join(build, config.main);
52
+ const modules = [entry, ...(await files(build)).map(path => join(build, path)).filter(path => path !== entry && moduleTypes[extname(path)])].map(path => ({ type: moduleTypes[extname(path)], path }));
53
+ const identity = { sourceSha: createHash('sha1').update(randomBytes(16)).digest('hex'), fingerprint: createHash('sha256').update(randomBytes(16)).digest('hex'), versionId: crypto.randomUUID() };
54
+ const secrets = Object.fromEntries((declaration.secrets ?? []).map(item => [item.binding, standIn(manifest.fields?.find(field => field.name === item.binding))]));
55
+ const mf = new Miniflare(convertV4MiniflareOptions({ host: '127.0.0.1', port: 0, cf: false, workers: [{ name: 'ingress', modules: true, script: ingress, serviceBindings: { APP: 'app' } }, {
56
+ name: 'app', modules, modulesRoot: build, compatibilityDate: config.compatibility_date, compatibilityFlags: config.compatibility_flags,
57
+ bindings: { ...config.vars, ...declaration.vars, ...secrets, ...(declaration.originVar ? { [declaration.originVar]: ORIGIN } : {}),
58
+ RER_DEPLOY_SOURCE: identity.sourceSha, RER_DEPLOY_FINGERPRINT: identity.fingerprint, RER_VERSION: { id: identity.versionId, tag: '', timestamp: new Date().toISOString() } },
59
+ d1Databases: Object.fromEntries((config.d1_databases ?? []).map(item => [item.binding, item.database_id ?? item.database_name])),
60
+ r2Buckets: Object.fromEntries((config.r2_buckets ?? []).map(item => [item.binding, item.bucket_name])),
61
+ kvNamespaces: Object.fromEntries((config.kv_namespaces ?? []).map(item => [item.binding, item.id ?? item.binding])),
62
+ queueProducers: Object.fromEntries((config.queues?.producers ?? []).map(item => [item.binding, item.queue])),
63
+ durableObjects: Object.fromEntries((config.durable_objects?.bindings ?? []).filter(item => !item.script_name).map(item => [item.name, { className: item.class_name, useSQLite: true }])),
64
+ ...(config.assets ? { assets: { directory: client, binding: config.assets.binding ?? 'ASSETS', routerConfig: { has_user_worker: true, invoke_user_worker_ahead_of_assets: RUN_WORKER_FIRST } } } : {}),
65
+ }] }));
66
+ let loopback;
67
+ try {
68
+ loopback = await mf.ready;
69
+ const { unstable_splitSqlQuery: split } = createRequire(join(root, 'package.json'))('wrangler');
70
+ for (const item of config.d1_databases ?? []) {
71
+ const directory = resolve(build, item.migrations_dir ?? 'migrations'), plan = [];
72
+ for (const name of (await readdir(directory).catch(() => [])).filter(name => name.endsWith('.sql')).sort()) {
73
+ const sql = await readFile(join(directory, name), 'utf8');
74
+ plan.push({ name, sql, sha256: createHash('sha256').update(sql).digest('hex'), statements: split(sql) });
75
+ }
76
+ await migrate(await mf.getD1Database(item.binding, 'app'), plan);
77
+ }
78
+ } catch (error) { await mf.dispose(); throw error; }
79
+ return { client, identity, origin: ORIGIN, close: () => mf.dispose(),
80
+ fetch: (path, { headers, ...init } = {}) => fetch(loopback, { ...init, redirect: 'manual', headers: { ...headers, 'x-rer-original-url': `${ORIGIN}/${path}` } }) };
81
+ }
82
+
83
+ export async function checkHostedOutput(root, options) {
84
+ const hosted = await serveHosted(root, options), { client, identity } = hosted, failures = [];
85
+ try {
86
+ const request = async path => { const response = await hosted.fetch(path); return { status: response.status, type: response.headers.get('content-type') ?? '', body: await response.text() }; };
87
+ const built = await files(client).catch(() => []);
88
+ const documents = await Promise.all(built.filter(path => extname(path) === '.html').map(path => readFile(join(client, path), 'utf8')));
89
+ const home = await request('');
90
+ if (home.status === 200 && home.type.includes('text/html')) documents.push(home.body);
91
+ const paths = [...new Set([...publicAssetPaths(built).map(path => path.split('/').map(encodeURIComponent).join('/')), ...documents.flatMap(links)])];
92
+ for (let i = 0; i < paths.length; i += 8) await Promise.all(paths.slice(i, i + 8).map(async path => {
93
+ const answer = await request(path);
94
+ if (answer.status !== 200) failures.push(`${answer.status} /${path}`);
95
+ }));
96
+ failures.sort();
97
+ const health = await request('health');
98
+ let body; try { body = JSON.parse(health.body); } catch {}
99
+ const deployment = body?.deployment ?? {};
100
+ const wrong = health.status !== 200 ? [`HTTP ${health.status}`] : [...(body?.ok === true ? [] : ['ok']), ...['sourceSha', 'fingerprint', 'versionId'].filter(key => deployment[key] !== identity[key]).map(key => `deployment.${key}`)];
101
+ if (wrong.length) failures.push(`/health lacks the deploy identity (${wrong.join(', ')}); it must answer {ok:true, deployment:{sourceSha, fingerprint, versionId}} from RER_DEPLOY_SOURCE, RER_DEPLOY_FINGERPRINT and RER_VERSION.id`);
102
+ if (failures.length) throw new RerError('hosted_output_failed', `The production build would break when hosted: rer deploy runs the Worker before static assets (run_worker_first), and served this way:\n ${failures.join('\n ')}\nServe built files with staticAssetResponse and answer /health with deploymentHealth (SEED-COMMAND-CONTRACT.md, Hosted assets and deploy identity).`, 1, { failures });
103
+ return { assets: paths.length, health: 'identity' };
104
+ } finally { await hosted.close(); }
105
+ }
106
+
107
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
108
+ try { console.log(JSON.stringify(await checkHostedOutput(resolve(process.argv[2] ?? '.'), { declaration: process.argv[3] }))); }
109
+ catch (error) { console.error(error.message); process.exitCode = 1; }
110
+ }
@@ -0,0 +1,29 @@
1
+ import { isAbsolute } from 'node:path';
2
+ import { inspectRequiredLoops } from '../operations/project-operations.mjs';
3
+ import { contained } from './project.mjs';
4
+ import { missing, refuse, RerError } from './errors.mjs';
5
+
6
+ export async function executeDeclared(project, options, store, signal) {
7
+ if (project.descriptor.version !== 2) throw refuse('Execution requires explicit OPERATIONS v2 argv; version 1 remains inspection-only.');
8
+ const command = project.descriptor.commands[options.verb];
9
+ if (!command) throw refuse('No command declared for this verb. Add a real OPERATIONS v2 argv or use the project commands.');
10
+ for (const input of command.inputs) await contained(project.root, input);
11
+ const absent = project.descriptor.requiredEnvironment.filter(name => typeof process.env[name] !== 'string' || !process.env[name].trim());
12
+ const loops = inspectRequiredLoops(project.descriptor, process.env);
13
+ if (absent.length || loops.some(loop => !['ready', 'optional-disabled'].includes(loop.status))) throw missing(`Required declared inputs or loops are missing. See ${project.descriptor.documentation?.document ?? 'the project operating documentation'}.`);
14
+ const [program, ...argv] = command.argv;
15
+ if (!isAbsolute(program) && (program.includes('/') || program.includes('\\'))) await contained(project.root, program);
16
+ if(['install','setup','build','verify','deploy','release','export','import'].includes(options.verb)) {
17
+ try{await store.run('Running declared project command',program,[...argv,...options.forwarded],{cwd:project.root,signal});return {childExitCode:0,signal:null};}
18
+ catch(error){const code=error.signal?({SIGINT:130,SIGTERM:143}[error.signal]??1):error.childExitCode??1;throw new RerError('child_failed','The declared command failed.',code,{childExitCode:error.childExitCode,signal:error.signal});}
19
+ }
20
+ const child = store.spawn('Running declared project command', program, [...argv, ...options.forwarded], { cwd: project.root, signal });
21
+ const completion = new Promise(resolve => { child.once('error', () => resolve({ error: true })); child.once('close', (code, terminationSignal) => resolve({ code, signal: terminationSignal })); });
22
+ await new Promise(resolve => { child.once('error', resolve); child.once('exit', resolve); });
23
+ await store.stop(child);
24
+ const result = await completion;
25
+ if (result.error) throw missing('The declared command executable is unavailable. See the project runtime requirements.');
26
+ const exitCode = result.signal ? ({ SIGINT: 130, SIGTERM: 143 }[result.signal] ?? 1) : result.code;
27
+ if (exitCode !== 0) throw new RerError('child_failed', 'The declared command failed.', exitCode, { childExitCode: result.code, signal: result.signal });
28
+ return { childExitCode: result.code, signal: result.signal };
29
+ }
@@ -0,0 +1,174 @@
1
+ import { dependencyDoctor } from './dependency-doctor.mjs';
2
+ import { inventory } from './export.mjs';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { createHash } from 'node:crypto';
5
+ import { resolve } from 'node:path';
6
+ import { aliases, pins, requiredHooks, serverDefaults, verbs, releaseFlags } from './contract.mjs';
7
+ import { runningVersion } from './version.mjs';
8
+ import { contained, readPackage } from './project.mjs';
9
+ import { shared } from './shared.mjs';
10
+ import { missing } from './errors.mjs';
11
+ import { projectNodePin } from './node-runtime-discovery.mjs';
12
+ import { localSettingKeys, requiredLocalSettings } from './settings.mjs';
13
+ import { localAiDiagnostics } from './ai-local.mjs';
14
+ import { authCopyClauses } from './auth-copy.mjs';
15
+ import { cacheWarnings } from './cache-doctor.mjs';
16
+ import { deployedMain } from './deploy-config.mjs';
17
+
18
+ // cliVersion: the rer version of the code answering (absent for a vendored copy
19
+ // too old to record one). defaults: what a server start applies when the person
20
+ // sets nothing.
21
+ export async function describe(project) {
22
+ const pkg = await readPackage(project.root), cliVersion = runningVersion(), versioned = cliVersion ? { cliVersion } : {};
23
+ const defaults = { serverLimit: serverDefaults.serverLimit, idleSeconds: serverDefaults.idleSeconds };
24
+ if (project.profile === 'independent') return {
25
+ contractVersion: 1, ...versioned, profile: 'independent', defaults, pins: {}, aliases: {},
26
+ verbs: Object.fromEntries(Object.entries(project.descriptor.commands).map(([verb, declaration]) => [verb, { flags: ['--help', '--cwd', '--non-interactive', '--json', ...(['install','setup','build','verify','dev','preview','deploy','export','import'].includes(verb)?['--no-queue']:[]), '--'], effects: declaration.effects }])),
27
+ };
28
+ const capabilities = project.adapter?.verbs ?? {};
29
+ return { contractVersion: 1, ...versioned, profile: 'first-party', defaults, pins: { node: pkg.engines?.node, pnpm: pkg.packageManager?.replace(/^pnpm@/, '') },
30
+ verbs: Object.fromEntries(Object.entries(capabilities).map(([verb, capability]) => [verb, { flags: (verbs[verb]?.flags ?? releaseFlags).filter(flag => capability.flags?.includes(flag)), effects: verbs[verb]?.effects ?? 'Existing Base release producer.' }])),
31
+ aliases: Object.fromEntries(Object.entries(aliases).filter(([alias, verb]) => pkg.scripts?.[alias] === `node scripts/rer.mjs${verb ? ' ' + verb : ''}`)),
32
+ local: { state: project.adapter?.state, settings: project.adapter?.settings ?? [], installerState: '.seed/installer', outbox: '/__dev/outbox', ...await localState(project), ...(project.adapter?.devIdentity ? { devIdentity: true, devKey: '.rer/local/dev-key.env' } : {}) },
33
+ };
34
+ }
35
+
36
+ // prepared: local setup has completed (the private settings file holds the
37
+ // owner, the origin and every required generated or supplied secret).
38
+ // ownerSaved: that file already holds the owner email, which a later setup keeps.
39
+ async function localState(project) {
40
+ const settings = project.adapter?.hooks?.secrets ?? {}, unprepared = { prepared: false, ownerSaved: false };
41
+ let values;
42
+ try {
43
+ const text = await readFile(await contained(project.root, settings.file ?? '.dev.vars'), 'utf8');
44
+ values = settings.format === 'json' ? JSON.parse(text) : (await shared('local/environment.mjs')).localVariables(text);
45
+ } catch { return unprepared; }
46
+ if (!values || typeof values !== 'object' || Array.isArray(values)) return unprepared;
47
+ if (project.adapter?.setup && !project.setupManifest) {
48
+ const { loadInstallerSeed } = await shared('setup-manifest.mjs');
49
+ try { project.setupManifest = (await loadInstallerSeed(project.root, project.adapter)).manifest; } catch { return unprepared; }
50
+ }
51
+ return { prepared: requiredLocalSettings(project, values).length === 0, ownerSaved: Boolean(values[localSettingKeys(project).email]) };
52
+ }
53
+
54
+ export async function check(project, explicitProfile) {
55
+ const clauses = [], add = (id, ok, message) => clauses.push({ id, ok: Boolean(ok), ...(!ok ? { message } : {}) });
56
+ for (const command of project.commands) add('SC-10.inputs.' + command.name, command.available, `Restore the declared inputs for ${command.name}.`);
57
+ for (const loop of project.descriptor.loops ?? []) add('SC-10.loop.' + loop.id, !loop.required || loop.enabled, `Enable the required ${loop.id} loop or correct its declaration.`);
58
+ if (project.profile === 'first-party' || explicitProfile === 'first-party') {
59
+ const adapter = project.adapter, pkg = await readPackage(project.root), marker = project.descriptor.seedContract;
60
+ add('SC-09.profile', marker?.version === 1 && marker?.profile === 'first-party', 'Restore OPERATIONS v2 seedContract version 1, profile first-party and the adapter pointer.');
61
+ add('SC-01.adapter', adapter?.version === 1, 'Export commandAdapter version 1 from scripts/installer/local.mjs.');
62
+ for (const hook of requiredHooks) add('SC-01.hook.' + hook, adapter?.[hook] ?? adapter?.hooks?.[hook], `Missing ${hook} hook/inventory; adopt the shared command adapter with a real ${hook} target.`);
63
+ for (const [verb, definition] of Object.entries({ ...verbs, release: { flags: releaseFlags } })) {
64
+ const declaration = project.descriptor.commands[verb];
65
+ add('SC-01.verb.' + verb, declaration && adapter?.verbs?.[verb], `Missing ${verb}; declare it in OPERATIONS and expose its shared implementation.`);
66
+ for (const flag of definition.flags) add('SC-01.flag.' + verb + '.' + flag.slice(2), adapter?.verbs?.[verb]?.flags?.includes(flag), `Missing ${verb} ${flag}; restore the shared implementation supporting this flag.`);
67
+ add('SC-02.target.' + verb, declaration?.argv?.join('\0') === ['node', 'scripts/rer.mjs', verb].join('\0'), `Declare ${verb} argv as node scripts/rer.mjs ${verb}.`);
68
+ }
69
+ for (const [alias, verb] of Object.entries(aliases)) add('SC-02.alias.' + alias, pkg.scripts?.[alias] === `node scripts/rer.mjs${verb ? ' ' + verb : ''}`, `Set package script ${alias} to node scripts/rer.mjs${verb ? ' ' + verb : ''}.`);
70
+ add('SC-02.install-lifecycle', !pkg.scripts?.install, 'Remove the install lifecycle alias; use deps or frozen pnpm install.');
71
+ let nodeFile; try { nodeFile = (await readFile(resolve(project.root, '.node-version'), 'utf8')).trim(); } catch {}
72
+ add('SC-02.node-file', nodeFile === pins.node, `Set .node-version to ${pins.node}.`);
73
+ add('SC-02.node-engine', pkg.engines?.node === pins.node, `Pin package.json engines.node to ${pins.node}.`);
74
+ add('SC-02.pnpm', pkg.packageManager === `pnpm@${pins.pnpm}`, `Pin packageManager to pnpm@${pins.pnpm}.`);
75
+ for (const path of ['scripts/rer.mjs', 'scripts/base-cli/inventory.json', 'scripts/base-onboarding/adapter.mjs']) {
76
+ let exists = false; try { await contained(project.root, path); exists = true; } catch {}
77
+ add('SC-01.portable.' + path, exists, `Restore portable source ${path} through the documented export tool.`);
78
+ }
79
+ try {
80
+ const record = JSON.parse(await readFile(resolve(project.root, 'scripts/base-cli/inventory.json'), 'utf8'));
81
+ for(const name of ['production-runtime.mjs','production-options.mjs','production-plan.mjs','production-modules.mjs','project-vite.mjs','operator-install.mjs','operator-prerequisites.mjs','command-recovery.mjs','queue.mjs','queued-entry.mjs','commands.mjs','process-scope.mjs'])
82
+ add('SC-12.onboarding.'+name,record.onboardingFiles?.[name],`Restore the required onboarding/queue source ${name}.`);
83
+ for(const [path,source]of Object.entries(record.onboardingFiles??{})){
84
+ let exact=false;try{const local=await contained(project.root,'scripts/base-onboarding/'+path);exact=createHash('sha256').update(await readFile(local)).digest('hex')===source.sha256;}catch{}
85
+ add('SC-12.onboarding-inventory.'+path,exact,`Restore stale or missing shared execution source ${path}.`);
86
+ }
87
+ const actual=await inventory(resolve(project.root,'scripts/base-cli'));
88
+ add('SC-09.inventory-complete',JSON.stringify(actual)===JSON.stringify(Object.keys(record.files).sort()),'Regenerate the complete portable source inventory.');
89
+ for(const path of ['cli/settings.mjs','cli/bounded.mjs','cli/server-resources.mjs','cli/managed-server.mjs','cli/server-registry.mjs','cli/server-activity.mjs','cli/server-proxy.mjs','cli/preview-cache.mjs','modules/job-queue/cli.mjs','modules/job-queue/config.mjs','modules/job-queue/resources.mjs','modules/job-queue/storage.mjs','modules/job-queue/identity.mjs','modules/job-queue/processes.mjs','modules/job-queue/index.mjs','modules/job-queue/command.mjs','modules/job-queue/supervisor.mjs','modules/job-queue/membership.mjs','modules/job-queue/policy.mjs'])
90
+ add(path.startsWith('cli/')?'SC-11.source.'+path:'SC-12.source.'+path,record.files[path],`Restore required managed lifecycle/queue source ${path}.`);
91
+ for (const [path, source] of Object.entries(record.files)) {
92
+ let exact = false;
93
+ try { const local = await contained(project.root, 'scripts/base-cli/' + path); exact = createHash('sha256').update(await readFile(local)).digest('hex') === source.sha256; } catch {}
94
+ add('SC-01.inventory.' + path, exact, `Restore stale or missing portable CLI source ${path} with the source export tool.`);
95
+ }
96
+ } catch { add('SC-01.inventory', false, 'Restore the deterministic portable CLI source inventory.'); }
97
+ add('SC-08.portability', typeof adapter?.portability?.export === 'function' && typeof adapter?.portability?.import === 'function', 'Adopt the reviewed mainline complete local portability API; archive support remains unavailable until then.');
98
+ if (adapter?.devIdentity) for (const [id, ok, message] of await devIdentityClauses(project.root, adapter)) add(id, ok, message);
99
+ for (const [id, ok, message] of await authCopyClauses(project.root)) add(id, ok, message);
100
+ }
101
+ for(const issue of await localAiDiagnostics(project,process.env))add('AI.'+issue.feature,false,issue.code+': '+issue.message);
102
+ return { clauses, ok: clauses.every(clause => clause.ok) };
103
+ }
104
+
105
+ export async function tools(root, store, signal) {
106
+ const node = await projectNodePin(root);
107
+ if (node && process.versions.node !== node) throw missing(`Node ${node} is required. Run it with rer, with automatic project runtime selection enabled.`);
108
+ const version = await probePnpm(root, store, signal);
109
+ if (version !== pins.pnpm) throw missing(`pnpm ${pins.pnpm} is required. Install/select the documented pin; rer never changes global tools.`);
110
+ }
111
+
112
+ export async function doctor(project, store, signal) {
113
+ const { inspectOperations } = await import('../operations/operations.mjs');
114
+ const diagnostic = await inspectOperations(project.root);
115
+ const { localVariables } = await shared('local/environment.mjs');
116
+ let values = {}, pnpm, stateValid = true;
117
+ if (project.profile === 'first-party') {
118
+ const { selectLocalState } = await shared('local/state.mjs');
119
+ try { await selectLocalState(project.root, project.adapter.state); } catch { stateValid = false; }
120
+ }
121
+ if (project.profile === 'first-party') {
122
+ try {
123
+ const settings = project.adapter?.hooks?.secrets ?? {};
124
+ const file = await contained(project.root, settings.file ?? '.dev.vars');
125
+ const text = await readFile(file, 'utf8');
126
+ const parsed = settings.format === 'json' ? JSON.parse(text) : localVariables(text);
127
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) values = parsed;
128
+ } catch {}
129
+ try { pnpm = await probePnpm(project.root, store, signal); } catch (error) { if (signal?.aborted) throw error; }
130
+ }
131
+ const aiIssues = await localAiDiagnostics(project,process.env);
132
+ const dependencies=project.profile==='first-party'?await dependencyDoctor(project.root):[];
133
+ const expectedNode = await projectNodePin(project.root);
134
+ const keys = localSettingKeys(project);
135
+ const inputs = project.profile === 'first-party' ? requiredLocalSettings(project, values) : [];
136
+ const warnings = project.profile === 'first-party' ? await cacheWarnings(project.root) : [];
137
+ return { dependencies, warnings, ok: !aiIssues.length && dependencies.every(d=>d.ok) && diagnostic.ok && stateValid && !inputs.length && (project.profile !== 'first-party' || (process.versions.node === expectedNode && pnpm === pins.pnpm)),
138
+ node: { actual: process.versions.node, expected: expectedNode },
139
+ pnpm: project.profile === 'first-party' ? { actual: pnpm, expected: pins.pnpm } : undefined,
140
+ ai: { issues: aiIssues },
141
+ missingInputs: [...new Set([...diagnostic.missingEnvironment, ...inputs])],
142
+ sources: project.descriptor.environmentSources ?? {}, state: { path: project.adapter?.state, safe: stateValid }, scope: 'local-inputs-only' };
143
+ }
144
+
145
+ // Diagnostics and execution use the same identity-checked process custody.
146
+ // Exit starts cleanup even if a descendant still holds the output pipes.
147
+ async function probePnpm(root, store, signal) {
148
+ const probeSignal = AbortSignal.any([signal, AbortSignal.timeout(10000)].filter(Boolean));
149
+ const child = store.spawn('Checking pinned pnpm', 'pnpm', ['--version'], { cwd: root, signal: probeSignal });
150
+ let version = '';
151
+ child.stdout.on('data', chunk => { version = (version + chunk).slice(-1024); });
152
+ const code = await new Promise(resolve => { child.once('error', () => resolve(null)); child.once('exit', code => resolve(code)); });
153
+ await store.stop(child);
154
+ if (probeSignal.aborted || code !== 0) throw missing('The pinned pnpm version probe failed. Select the documented tool.');
155
+ return version.trim();
156
+ }
157
+
158
+ // SC-05: a seed declaring devIdentity loads the dev layer only from a local-only
159
+ // entry that its dev configuration names, never from the deployed main, lets
160
+ // rer build scan its output, and keeps one end-to-end email-link test.
161
+ async function devIdentityClauses(root, adapter) {
162
+ const declaration = adapter.devIdentity;
163
+ const text = async path => { try { return await readFile(await contained(root, path), 'utf8'); } catch { return null; } };
164
+ const entry = typeof declaration.entry === 'string' ? declaration.entry.replace(/^\.\//, '') : '';
165
+ const main = await deployedMain(root, adapter);
166
+ const production = main ? await text(main) : null;
167
+ const configs = (await Promise.all(['vite.config.ts', 'vite.config.mts', 'vite.config.js', 'vite.config.mjs', 'wrangler.local.jsonc', 'wrangler.local.json', 'wrangler.local.toml'].map(text))).filter(Boolean);
168
+ const test = typeof declaration.emailLinkTest === 'string' ? await text(declaration.emailLinkTest) : null;
169
+ return [
170
+ ['SC-05.dev-identity.entry', entry && await text(entry) !== null && entry !== main && configs.some(config => config.includes(entry)), 'Declare devIdentity.entry as an existing local-only Worker entry that the local Vite or wrangler configuration loads and that is not the deployed main.'],
171
+ ['SC-05.dev-identity.exclusion', production !== null && !/operator-auth\/dev\//.test(production) && !production.includes(entry.replace(/\.[a-z]+$/, '')) && Array.isArray(declaration.output) && declaration.output.length > 0, 'Keep the deployed main free of the dev layer and its local entry, and declare devIdentity.output so rer build refuses output containing the dev layer.'],
172
+ ['SC-05.dev-identity.email-link-test', test !== null && /outbox/i.test(test), 'Keep one end-to-end email-link test through the outbox and declare it as devIdentity.emailLinkTest.'],
173
+ ];
174
+ }