primitive-admin 1.0.62 → 1.0.64

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 (344) hide show
  1. package/README.md +132 -42
  2. package/assets/skill/skills/primitive-platform/SKILL.md +142 -661
  3. package/dist/bin/primitive.js +121 -41
  4. package/dist/bin/primitive.js.map +1 -1
  5. package/dist/src/commands/admins.js +37 -50
  6. package/dist/src/commands/admins.js.map +1 -1
  7. package/dist/src/commands/analytics.js +178 -9
  8. package/dist/src/commands/analytics.js.map +1 -1
  9. package/dist/src/commands/apps.js +17 -7
  10. package/dist/src/commands/apps.js.map +1 -1
  11. package/dist/src/commands/auth-sessions.d.ts +7 -0
  12. package/dist/src/commands/auth-sessions.js +144 -0
  13. package/dist/src/commands/auth-sessions.js.map +1 -0
  14. package/dist/src/commands/auth.js +76 -116
  15. package/dist/src/commands/auth.js.map +1 -1
  16. package/dist/src/commands/blob-buckets.js +2 -1
  17. package/dist/src/commands/blob-buckets.js.map +1 -1
  18. package/dist/src/commands/catalog.js +26 -10
  19. package/dist/src/commands/catalog.js.map +1 -1
  20. package/dist/src/commands/collection-type-configs.js +3 -10
  21. package/dist/src/commands/collection-type-configs.js.map +1 -1
  22. package/dist/src/commands/collections.js +736 -19
  23. package/dist/src/commands/collections.js.map +1 -1
  24. package/dist/src/commands/config.js +13 -27
  25. package/dist/src/commands/config.js.map +1 -1
  26. package/dist/src/commands/connections.js +8 -9
  27. package/dist/src/commands/connections.js.map +1 -1
  28. package/dist/src/commands/cron-triggers.js +2 -1
  29. package/dist/src/commands/cron-triggers.js.map +1 -1
  30. package/dist/src/commands/database-type-configs.js +5 -12
  31. package/dist/src/commands/database-type-configs.js.map +1 -1
  32. package/dist/src/commands/databases.js +59 -72
  33. package/dist/src/commands/databases.js.map +1 -1
  34. package/dist/src/commands/documents.d.ts +58 -0
  35. package/dist/src/commands/documents.js +1623 -95
  36. package/dist/src/commands/documents.js.map +1 -1
  37. package/dist/src/commands/email-templates.js +5 -4
  38. package/dist/src/commands/email-templates.js.map +1 -1
  39. package/dist/src/commands/env.d.ts +1 -1
  40. package/dist/src/commands/env.js +106 -44
  41. package/dist/src/commands/env.js.map +1 -1
  42. package/dist/src/commands/feature-flags.js +2 -1
  43. package/dist/src/commands/feature-flags.js.map +1 -1
  44. package/dist/src/commands/functions.d.ts +20 -0
  45. package/dist/src/commands/functions.js +1630 -0
  46. package/dist/src/commands/functions.js.map +1 -0
  47. package/dist/src/commands/group-type-configs.js +2 -1
  48. package/dist/src/commands/group-type-configs.js.map +1 -1
  49. package/dist/src/commands/groups.js +30 -17
  50. package/dist/src/commands/groups.js.map +1 -1
  51. package/dist/src/commands/guides.js +17 -11
  52. package/dist/src/commands/guides.js.map +1 -1
  53. package/dist/src/commands/init.d.ts +24 -12
  54. package/dist/src/commands/init.js +198 -110
  55. package/dist/src/commands/init.js.map +1 -1
  56. package/dist/src/commands/integrations.js +65 -26
  57. package/dist/src/commands/integrations.js.map +1 -1
  58. package/dist/src/commands/locks.js +17 -2
  59. package/dist/src/commands/locks.js.map +1 -1
  60. package/dist/src/commands/metadata-category-configs.js +2 -1
  61. package/dist/src/commands/metadata-category-configs.js.map +1 -1
  62. package/dist/src/commands/metadata.js +8 -1
  63. package/dist/src/commands/metadata.js.map +1 -1
  64. package/dist/src/commands/prompts.js +106 -59
  65. package/dist/src/commands/prompts.js.map +1 -1
  66. package/dist/src/commands/rule-sets.js +3 -10
  67. package/dist/src/commands/rule-sets.js.map +1 -1
  68. package/dist/src/commands/scripts.d.ts +10 -0
  69. package/dist/src/commands/scripts.js +159 -25
  70. package/dist/src/commands/scripts.js.map +1 -1
  71. package/dist/src/commands/secrets.js +2 -1
  72. package/dist/src/commands/secrets.js.map +1 -1
  73. package/dist/src/commands/sessions.js +10 -9
  74. package/dist/src/commands/sessions.js.map +1 -1
  75. package/dist/src/commands/sync-app-settings.js +3 -5
  76. package/dist/src/commands/sync-app-settings.js.map +1 -1
  77. package/dist/src/commands/sync.d.ts +391 -44
  78. package/dist/src/commands/sync.js +2622 -534
  79. package/dist/src/commands/sync.js.map +1 -1
  80. package/dist/src/commands/tokens.js +2 -1
  81. package/dist/src/commands/tokens.js.map +1 -1
  82. package/dist/src/commands/users.js +19 -10
  83. package/dist/src/commands/users.js.map +1 -1
  84. package/dist/src/commands/vars.js +2 -1
  85. package/dist/src/commands/vars.js.map +1 -1
  86. package/dist/src/commands/waitlist.js +2 -1
  87. package/dist/src/commands/waitlist.js.map +1 -1
  88. package/dist/src/commands/webhooks.d.ts +29 -0
  89. package/dist/src/commands/webhooks.js +135 -64
  90. package/dist/src/commands/webhooks.js.map +1 -1
  91. package/dist/src/commands/workflows.d.ts +1 -29
  92. package/dist/src/commands/workflows.js +113 -179
  93. package/dist/src/commands/workflows.js.map +1 -1
  94. package/dist/src/lib/api-client.d.ts +628 -14
  95. package/dist/src/lib/api-client.js +776 -70
  96. package/dist/src/lib/api-client.js.map +1 -1
  97. package/dist/src/lib/app-settings-descriptor.d.ts +1 -1
  98. package/dist/src/lib/app-settings-descriptor.js +9 -1
  99. package/dist/src/lib/app-settings-descriptor.js.map +1 -1
  100. package/dist/src/lib/block-selector.d.ts +58 -0
  101. package/dist/src/lib/block-selector.js +92 -0
  102. package/dist/src/lib/block-selector.js.map +1 -0
  103. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +24 -54
  104. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +31 -153
  105. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -1
  106. package/dist/src/lib/collection-export.d.ts +184 -0
  107. package/dist/src/lib/collection-export.js +252 -0
  108. package/dist/src/lib/collection-export.js.map +1 -0
  109. package/dist/src/lib/config-json-field.d.ts +28 -0
  110. package/dist/src/lib/config-json-field.js +56 -0
  111. package/dist/src/lib/config-json-field.js.map +1 -0
  112. package/dist/src/lib/config-object-descriptor.js +88 -6
  113. package/dist/src/lib/config-object-descriptor.js.map +1 -1
  114. package/dist/src/lib/config-payload.d.ts +8 -1
  115. package/dist/src/lib/config-payload.js +49 -4
  116. package/dist/src/lib/config-payload.js.map +1 -1
  117. package/dist/src/lib/config-surface.d.ts +12 -1
  118. package/dist/src/lib/config-surface.js +71 -3
  119. package/dist/src/lib/config-surface.js.map +1 -1
  120. package/dist/src/lib/config.d.ts +31 -13
  121. package/dist/src/lib/config.js +34 -70
  122. package/dist/src/lib/config.js.map +1 -1
  123. package/dist/src/lib/credentials-store.d.ts +37 -22
  124. package/dist/src/lib/credentials-store.js +103 -97
  125. package/dist/src/lib/credentials-store.js.map +1 -1
  126. package/dist/src/lib/db-codegen/dbTypeIR.js +8 -0
  127. package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -1
  128. package/dist/src/lib/deprecation.d.ts +22 -0
  129. package/dist/src/lib/deprecation.js +43 -0
  130. package/dist/src/lib/deprecation.js.map +1 -0
  131. package/dist/src/lib/document-export-permissions.d.ts +30 -0
  132. package/dist/src/lib/document-export-permissions.js +54 -0
  133. package/dist/src/lib/document-export-permissions.js.map +1 -0
  134. package/dist/src/lib/document-ingest-artifact.d.ts +120 -0
  135. package/dist/src/lib/document-ingest-artifact.js +505 -0
  136. package/dist/src/lib/document-ingest-artifact.js.map +1 -0
  137. package/dist/src/lib/document-ingest-input.d.ts +51 -0
  138. package/dist/src/lib/document-ingest-input.js +132 -0
  139. package/dist/src/lib/document-ingest-input.js.map +1 -0
  140. package/dist/src/lib/document-ingest-rows.d.ts +137 -0
  141. package/dist/src/lib/document-ingest-rows.js +181 -0
  142. package/dist/src/lib/document-ingest-rows.js.map +1 -0
  143. package/dist/src/lib/document-ingest.d.ts +101 -0
  144. package/dist/src/lib/document-ingest.js +384 -0
  145. package/dist/src/lib/document-ingest.js.map +1 -0
  146. package/dist/src/lib/env-resolver-core.d.ts +128 -17
  147. package/dist/src/lib/env-resolver-core.js +204 -22
  148. package/dist/src/lib/env-resolver-core.js.map +1 -1
  149. package/dist/src/lib/env-resolver.d.ts +18 -3
  150. package/dist/src/lib/env-resolver.js +23 -3
  151. package/dist/src/lib/env-resolver.js.map +1 -1
  152. package/dist/src/lib/function-bundle.d.ts +147 -0
  153. package/dist/src/lib/function-bundle.js +341 -0
  154. package/dist/src/lib/function-bundle.js.map +1 -0
  155. package/dist/src/lib/function-collect.d.ts +123 -0
  156. package/dist/src/lib/function-collect.js +610 -0
  157. package/dist/src/lib/function-collect.js.map +1 -0
  158. package/dist/src/lib/function-db-types.d.ts +202 -0
  159. package/dist/src/lib/function-db-types.js +870 -0
  160. package/dist/src/lib/function-db-types.js.map +1 -0
  161. package/dist/src/lib/function-document-types.d.ts +144 -0
  162. package/dist/src/lib/function-document-types.js +370 -0
  163. package/dist/src/lib/function-document-types.js.map +1 -0
  164. package/dist/src/lib/function-grants-preflight.d.ts +64 -0
  165. package/dist/src/lib/function-grants-preflight.js +105 -0
  166. package/dist/src/lib/function-grants-preflight.js.map +1 -0
  167. package/dist/src/lib/function-log-lines.d.ts +76 -0
  168. package/dist/src/lib/function-log-lines.js +160 -0
  169. package/dist/src/lib/function-log-lines.js.map +1 -0
  170. package/dist/src/lib/function-log-row.d.ts +29 -0
  171. package/dist/src/lib/function-log-row.js +73 -0
  172. package/dist/src/lib/function-log-row.js.map +1 -0
  173. package/dist/src/lib/function-log-tail.d.ts +132 -0
  174. package/dist/src/lib/function-log-tail.js +262 -0
  175. package/dist/src/lib/function-log-tail.js.map +1 -0
  176. package/dist/src/lib/function-run.d.ts +289 -0
  177. package/dist/src/lib/function-run.js +389 -0
  178. package/dist/src/lib/function-run.js.map +1 -0
  179. package/dist/src/lib/function-schema-codegen.d.ts +143 -0
  180. package/dist/src/lib/function-schema-codegen.js +420 -0
  181. package/dist/src/lib/function-schema-codegen.js.map +1 -0
  182. package/dist/src/lib/function-sync.d.ts +459 -0
  183. package/dist/src/lib/function-sync.js +1258 -0
  184. package/dist/src/lib/function-sync.js.map +1 -0
  185. package/dist/src/lib/function-trigger-listing.d.ts +27 -0
  186. package/dist/src/lib/function-trigger-listing.js +70 -0
  187. package/dist/src/lib/function-trigger-listing.js.map +1 -0
  188. package/dist/src/lib/function-typecheck.d.ts +86 -0
  189. package/dist/src/lib/function-typecheck.js +370 -0
  190. package/dist/src/lib/function-typecheck.js.map +1 -0
  191. package/dist/src/lib/function-versions.d.ts +122 -0
  192. package/dist/src/lib/function-versions.js +182 -0
  193. package/dist/src/lib/function-versions.js.map +1 -0
  194. package/dist/src/lib/generated-allowlist.js +4 -0
  195. package/dist/src/lib/generated-allowlist.js.map +1 -1
  196. package/dist/src/lib/generated-config-surfaces.d.ts +2332 -78
  197. package/dist/src/lib/generated-config-surfaces.js +6979 -468
  198. package/dist/src/lib/generated-config-surfaces.js.map +1 -1
  199. package/dist/src/lib/generated-sdk-types.d.ts +12 -0
  200. package/dist/src/lib/generated-sdk-types.js +13 -0
  201. package/dist/src/lib/generated-sdk-types.js.map +1 -0
  202. package/dist/src/lib/init-adopt.js +2 -2
  203. package/dist/src/lib/init-adopt.js.map +1 -1
  204. package/dist/src/lib/init-assets.js +3 -3
  205. package/dist/src/lib/init-client-platforms.d.ts +14 -0
  206. package/dist/src/lib/init-client-platforms.js +71 -0
  207. package/dist/src/lib/init-client-platforms.js.map +1 -0
  208. package/dist/src/lib/init-config.d.ts +1 -1
  209. package/dist/src/lib/init-email-redirect-uris.d.ts +37 -0
  210. package/dist/src/lib/init-email-redirect-uris.js +46 -0
  211. package/dist/src/lib/init-email-redirect-uris.js.map +1 -0
  212. package/dist/src/lib/init-ios-links.d.ts +41 -0
  213. package/dist/src/lib/init-ios-links.js +67 -1
  214. package/dist/src/lib/init-ios-links.js.map +1 -1
  215. package/dist/src/lib/init-plan.d.ts +1 -1
  216. package/dist/src/lib/init-plan.js +1 -1
  217. package/dist/src/lib/init-xcode.d.ts +6 -5
  218. package/dist/src/lib/init-xcode.js +34 -10
  219. package/dist/src/lib/init-xcode.js.map +1 -1
  220. package/dist/src/lib/integration-selector.d.ts +42 -0
  221. package/dist/src/lib/integration-selector.js +46 -0
  222. package/dist/src/lib/integration-selector.js.map +1 -0
  223. package/dist/src/lib/ios-app-id.d.ts +34 -0
  224. package/dist/src/lib/ios-app-id.js +69 -0
  225. package/dist/src/lib/ios-app-id.js.map +1 -0
  226. package/dist/src/lib/list-options.d.ts +68 -0
  227. package/dist/src/lib/list-options.js +89 -0
  228. package/dist/src/lib/list-options.js.map +1 -0
  229. package/dist/src/lib/local-state.d.ts +1 -1
  230. package/dist/src/lib/local-state.js +1 -1
  231. package/dist/src/lib/local-test-cases.d.ts +63 -0
  232. package/dist/src/lib/local-test-cases.js +136 -0
  233. package/dist/src/lib/local-test-cases.js.map +1 -0
  234. package/dist/src/lib/log-inspection.d.ts +152 -5
  235. package/dist/src/lib/log-inspection.js +179 -2
  236. package/dist/src/lib/log-inspection.js.map +1 -1
  237. package/dist/src/lib/logout-admin-session.d.ts +33 -0
  238. package/dist/src/lib/logout-admin-session.js +70 -0
  239. package/dist/src/lib/logout-admin-session.js.map +1 -0
  240. package/dist/src/lib/output.d.ts +15 -0
  241. package/dist/src/lib/output.js +28 -0
  242. package/dist/src/lib/output.js.map +1 -1
  243. package/dist/src/lib/paginate.d.ts +15 -0
  244. package/dist/src/lib/paginate.js +17 -0
  245. package/dist/src/lib/paginate.js.map +1 -1
  246. package/dist/src/lib/project-config.d.ts +46 -21
  247. package/dist/src/lib/project-config.js +74 -47
  248. package/dist/src/lib/project-config.js.map +1 -1
  249. package/dist/src/lib/prompt-cost-format.d.ts +11 -0
  250. package/dist/src/lib/prompt-cost-format.js +41 -0
  251. package/dist/src/lib/prompt-cost-format.js.map +1 -0
  252. package/dist/src/lib/prompt-schema-codegen.d.ts +147 -0
  253. package/dist/src/lib/prompt-schema-codegen.js +462 -0
  254. package/dist/src/lib/prompt-schema-codegen.js.map +1 -0
  255. package/dist/src/lib/refresh-admin-credentials.d.ts +9 -1
  256. package/dist/src/lib/refresh-admin-credentials.js +22 -2
  257. package/dist/src/lib/refresh-admin-credentials.js.map +1 -1
  258. package/dist/src/lib/resolve-owner.d.ts +19 -0
  259. package/dist/src/lib/resolve-owner.js +20 -0
  260. package/dist/src/lib/resolve-owner.js.map +1 -0
  261. package/dist/src/lib/skill-installer.d.ts +49 -3
  262. package/dist/src/lib/skill-installer.js +275 -100
  263. package/dist/src/lib/skill-installer.js.map +1 -1
  264. package/dist/src/lib/snapshot-audit-source.d.ts +45 -0
  265. package/dist/src/lib/snapshot-audit-source.js +58 -0
  266. package/dist/src/lib/snapshot-audit-source.js.map +1 -0
  267. package/dist/src/lib/snapshot-audit-store.d.ts +52 -0
  268. package/dist/src/lib/snapshot-audit-store.js +196 -0
  269. package/dist/src/lib/snapshot-audit-store.js.map +1 -0
  270. package/dist/src/lib/snapshot-audit.d.ts +207 -0
  271. package/dist/src/lib/snapshot-audit.js +431 -0
  272. package/dist/src/lib/snapshot-audit.js.map +1 -0
  273. package/dist/src/lib/snapshot-build-rows.d.ts +60 -0
  274. package/dist/src/lib/snapshot-build-rows.js +87 -0
  275. package/dist/src/lib/snapshot-build-rows.js.map +1 -0
  276. package/dist/src/lib/snapshot-build.d.ts +50 -0
  277. package/dist/src/lib/snapshot-build.js +111 -0
  278. package/dist/src/lib/snapshot-build.js.map +1 -0
  279. package/dist/src/lib/snapshot-manifest-layout.d.ts +61 -0
  280. package/dist/src/lib/snapshot-manifest-layout.js +70 -0
  281. package/dist/src/lib/snapshot-manifest-layout.js.map +1 -0
  282. package/dist/src/lib/snapshots.d.ts +8 -9
  283. package/dist/src/lib/snapshots.js +20 -83
  284. package/dist/src/lib/snapshots.js.map +1 -1
  285. package/dist/src/lib/step-run-table.d.ts +43 -0
  286. package/dist/src/lib/step-run-table.js +129 -0
  287. package/dist/src/lib/step-run-table.js.map +1 -0
  288. package/dist/src/lib/storage-pending-retry.d.ts +26 -0
  289. package/dist/src/lib/storage-pending-retry.js +42 -0
  290. package/dist/src/lib/storage-pending-retry.js.map +1 -0
  291. package/dist/src/lib/swift-codegen/agentGenerator.d.ts +42 -0
  292. package/dist/src/lib/swift-codegen/agentGenerator.js +118 -0
  293. package/dist/src/lib/swift-codegen/agentGenerator.js.map +1 -0
  294. package/dist/src/lib/swift-codegen/banners.d.ts +24 -0
  295. package/dist/src/lib/swift-codegen/banners.js +25 -0
  296. package/dist/src/lib/swift-codegen/banners.js.map +1 -0
  297. package/dist/src/lib/swift-codegen/dbGenerator.js +19 -7
  298. package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
  299. package/dist/src/lib/swift-codegen/functionGenerator.d.ts +139 -0
  300. package/dist/src/lib/swift-codegen/functionGenerator.js +462 -0
  301. package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -0
  302. package/dist/src/lib/swift-codegen/generator.d.ts +7 -1
  303. package/dist/src/lib/swift-codegen/generator.js +28 -11
  304. package/dist/src/lib/swift-codegen/generator.js.map +1 -1
  305. package/dist/src/lib/swift-codegen/schemaToSwift.d.ts +15 -0
  306. package/dist/src/lib/swift-codegen/schemaToSwift.js +21 -4
  307. package/dist/src/lib/swift-codegen/schemaToSwift.js.map +1 -1
  308. package/dist/src/lib/sync-paths.d.ts +72 -55
  309. package/dist/src/lib/sync-paths.js +97 -100
  310. package/dist/src/lib/sync-paths.js.map +1 -1
  311. package/dist/src/lib/sync-resource-types.d.ts +23 -4
  312. package/dist/src/lib/sync-resource-types.js +106 -8
  313. package/dist/src/lib/sync-resource-types.js.map +1 -1
  314. package/dist/src/lib/sync-selectors.d.ts +45 -2
  315. package/dist/src/lib/sync-selectors.js +66 -5
  316. package/dist/src/lib/sync-selectors.js.map +1 -1
  317. package/dist/src/lib/template.d.ts +2 -2
  318. package/dist/src/lib/template.js +2 -2
  319. package/dist/src/lib/test-case-file-names.d.ts +40 -0
  320. package/dist/src/lib/test-case-file-names.js +91 -0
  321. package/dist/src/lib/test-case-file-names.js.map +1 -0
  322. package/dist/src/lib/test-case-keys.d.ts +1 -1
  323. package/dist/src/lib/test-case-keys.js +1 -1
  324. package/dist/src/lib/test-case-variables.d.ts +14 -0
  325. package/dist/src/lib/test-case-variables.js +42 -0
  326. package/dist/src/lib/test-case-variables.js.map +1 -1
  327. package/dist/src/lib/toml-database-config.js +19 -2
  328. package/dist/src/lib/toml-database-config.js.map +1 -1
  329. package/dist/src/lib/web-url.d.ts +40 -0
  330. package/dist/src/lib/web-url.js +76 -0
  331. package/dist/src/lib/web-url.js.map +1 -0
  332. package/dist/src/lib/webhook-deliver.d.ts +209 -0
  333. package/dist/src/lib/webhook-deliver.js +519 -0
  334. package/dist/src/lib/webhook-deliver.js.map +1 -0
  335. package/dist/src/lib/workflow-codegen/generator.d.ts +1 -1
  336. package/dist/src/lib/workflow-codegen/generator.js +1 -1
  337. package/dist/src/lib/workflow-toml-validator.d.ts +17 -8
  338. package/dist/src/lib/workflow-toml-validator.js +25 -12
  339. package/dist/src/lib/workflow-toml-validator.js.map +1 -1
  340. package/dist/src/lib/workflow-usage.d.ts +196 -0
  341. package/dist/src/lib/workflow-usage.js +310 -0
  342. package/dist/src/lib/workflow-usage.js.map +1 -0
  343. package/dist/src/types/index.d.ts +12 -2
  344. package/package.json +5 -4
@@ -0,0 +1,384 @@
1
+ /**
2
+ * Driving one bulk-load session: create, upload, commit, poll.
3
+ *
4
+ * The artifact is already built and checked by the time anything here runs
5
+ * (`document-ingest-artifact`), so every refusal this module reports comes
6
+ * from the server. What it decides is what to do about one:
7
+ *
8
+ * - a create the server refuses uploads nothing;
9
+ * - an upload that fails ABORTS the session rather than retrying a slot whose
10
+ * reservation this side cannot see — the artifact is deterministic and a
11
+ * re-run is cheap;
12
+ * - Ctrl-C during the uploads aborts too, because the session it was filling
13
+ * is not going to be committed; Ctrl-C while POLLING leaves the session
14
+ * alone, because by then the document is already being changed and giving up
15
+ * on watching is not giving up on the load;
16
+ * - a session READ that fails while polling is retried on a bounded schedule
17
+ * (#3597), for the same reason: the read failing says nothing about the load,
18
+ * and a multi-hour load will meet transient reads. When the bound is spent
19
+ * the wait stops and says the session is still running — it never reports it
20
+ * as cancelled or failed.
21
+ *
22
+ * Exit codes follow `functions runs wait`/`invoke`, as `docs/cli-design.md`
23
+ * records them: 0 completed, 1 failed or refused, 124 a wait that gave up,
24
+ * 130 Ctrl-C. Both ways of giving up on the WATCHING are 124 and both say the
25
+ * session keeps running; only the load itself failing is 1.
26
+ */
27
+ import fs from "fs";
28
+ /** A plan the operator did not confirm, or cannot be asked about. */
29
+ export class IngestRefusedError extends Error {
30
+ constructor(message) {
31
+ super(message);
32
+ this.name = "IngestRefusedError";
33
+ }
34
+ }
35
+ const POLL_FAST_MS = 2_000;
36
+ const POLL_SLOW_MS = 5_000;
37
+ const POLL_SLOWS_AFTER_MS = 60_000;
38
+ /**
39
+ * How long CONSECUTIVE failed session reads may run before the wait gives up
40
+ * (#3597), and the backoff between them.
41
+ *
42
+ * A bound measured in attempts would have been wrong for the load this is for:
43
+ * the 2026-09-18 2 GB validation attempts met reads that failed for MINUTES
44
+ * together through the same route while the session stayed alive and
45
+ * advancing. So the bound is a wall-clock window, and any successful read
46
+ * resets it — an isolated blip costs a multi-hour load nothing at all, and
47
+ * only an outage that never lifts ends the watching.
48
+ *
49
+ * Module constants like the poll cadence above, deliberately: there is no flag
50
+ * for them. The backoff caps rather than doubling forever, because a backoff
51
+ * that silently stops backing off is a poll storm (`function-run.ts`).
52
+ */
53
+ const POLL_READ_RETRY_WINDOW_MS = 10 * 60_000;
54
+ const POLL_READ_BACKOFF_MS = 1_000;
55
+ const POLL_READ_BACKOFF_CAP_MS = 15_000;
56
+ /**
57
+ * Whether a thrown session read is worth retrying.
58
+ *
59
+ * `ApiClient.request` throws an `ApiError` carrying `statusCode` on any
60
+ * non-2xx, and the fetch under it rejects with no status at all on a dropped
61
+ * connection — so a transport failure, a 5xx, a 408 and a 429 all describe a
62
+ * server that may well answer the next read, and the session goes on either
63
+ * way. Any OTHER 4xx is definitive: the session is gone, or unreadable to this
64
+ * caller, and retrying it only to call it "still running" would misstate it.
65
+ */
66
+ function isTransientReadError(error) {
67
+ const status = error?.statusCode ?? error?.status;
68
+ if (typeof status !== "number")
69
+ return true;
70
+ if (status >= 500)
71
+ return true;
72
+ return status === 408 || status === 429;
73
+ }
74
+ /** The capped doubling between consecutive failed reads. */
75
+ function readBackoffMs(failures) {
76
+ return Math.min(POLL_READ_BACKOFF_MS * 2 ** (failures - 1), POLL_READ_BACKOFF_CAP_MS);
77
+ }
78
+ /** The plan summary an operator confirms, one line per row. */
79
+ export function summariseIngestPlan(plan) {
80
+ const lines = plan.models.map((model) => ` ${model.model}: ${model.rows} rows, ${model.chunks} chunks, ` +
81
+ `${model.bytes} bytes` +
82
+ (model.deletes > 0 ? `, ${model.deletes} deletes` : ""));
83
+ return [
84
+ `${plan.rows} rows across ${plan.models.length} model(s), ` +
85
+ `${plan.chunks.length} chunks, ${plan.bytes} bytes` +
86
+ (plan.deletes > 0 ? `, ${plan.deletes} deletes` : ""),
87
+ ...lines,
88
+ ];
89
+ }
90
+ /**
91
+ * Refuse a run that cannot ask, before it asks the server anything.
92
+ *
93
+ * A bulk load deletes and overwrites a live document's records with no undo,
94
+ * which is what the CLI design's destructive default is for. A pipeline that
95
+ * meant to run one says so with `-y`.
96
+ */
97
+ export function assertIngestConfirmable(options) {
98
+ if (options.yes || options.isTTY)
99
+ return;
100
+ throw new IngestRefusedError("A bulk load replaces records in a live document and cannot be undone. " +
101
+ "Re-run with -y to confirm it without a prompt.");
102
+ }
103
+ export async function driveDocumentIngest(options) {
104
+ const { client, documentId, plan, concurrency = 4, wait = true, timeoutMs = null, signal, onProgress = () => { }, readChunk = async (chunk) => fs.readFileSync(chunk.file), now = () => Date.now(), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), } = options;
105
+ // A phase appears only once it has FINISHED (#3598). A create that threw
106
+ // cost time, but not a measurable amount of anything an operator can act
107
+ // on; half an upload has no rate; and a phase the run never reached has
108
+ // nothing to say rather than a zero to say it with.
109
+ const timings = {};
110
+ /** A whole number of milliseconds, never negative. */
111
+ const since = (at) => Math.max(0, now() - at);
112
+ // ── create ──
113
+ let session;
114
+ const createdAt = now();
115
+ try {
116
+ session = await client.createIngest(documentId);
117
+ }
118
+ catch (error) {
119
+ if (error?.code === "DOCUMENT_INGEST_ACTIVE") {
120
+ const active = (error.details && error.details.sessionId) ?? "the active session";
121
+ return {
122
+ exitCode: 1,
123
+ sessionId: null,
124
+ session: null,
125
+ timings,
126
+ message: `${error.message}\nThis document already has bulk load ${active}. ` +
127
+ `Watch it with \`primitive documents ingests get ${documentId} ${active}\`, ` +
128
+ `or give it up with \`primitive documents ingests abort\`.`,
129
+ };
130
+ }
131
+ return {
132
+ exitCode: 1,
133
+ sessionId: null,
134
+ session: null,
135
+ timings,
136
+ message: String(error?.message ?? error),
137
+ };
138
+ }
139
+ timings.createMs = since(createdAt);
140
+ const sessionId = String(session.sessionId);
141
+ onProgress(`Session ${sessionId} opened.`);
142
+ const giveUp = async (message, exitCode) => {
143
+ try {
144
+ await client.abortIngest(documentId, sessionId);
145
+ onProgress(`Session ${sessionId} aborted; nothing was applied.`);
146
+ }
147
+ catch (error) {
148
+ onProgress(`Session ${sessionId} could not be aborted: ${error?.message ?? error}`);
149
+ }
150
+ return { exitCode, sessionId, session: null, timings, message };
151
+ };
152
+ // ── upload ──
153
+ const uploadStartedAt = now();
154
+ const queue = [...plan.chunks];
155
+ let uploaded = 0;
156
+ let failure = null;
157
+ const worker = async () => {
158
+ for (;;) {
159
+ if (failure !== null || signal?.aborted)
160
+ return;
161
+ const chunk = queue.shift();
162
+ if (!chunk)
163
+ return;
164
+ try {
165
+ const body = await readChunk(chunk);
166
+ await client.uploadIngestChunk(documentId, sessionId, { ...chunk, body });
167
+ uploaded += 1;
168
+ onProgress(`Uploaded ${uploaded}/${plan.chunks.length} chunks ` +
169
+ `(${chunk.model} ${chunk.index}).`);
170
+ }
171
+ catch (error) {
172
+ // The first failure wins: the others are the same session going away.
173
+ if (failure === null)
174
+ failure = String(error?.message ?? error);
175
+ return;
176
+ }
177
+ }
178
+ };
179
+ await Promise.all(Array.from({ length: Math.max(1, Math.min(concurrency, queue.length || 1)) }, worker));
180
+ if (failure !== null)
181
+ return giveUp(failure, 1);
182
+ if (signal?.aborted) {
183
+ return giveUp("Interrupted while uploading; the session was aborted and nothing was applied.", 130);
184
+ }
185
+ // Every chunk is up, so there is a rate to state. It is measured over the
186
+ // PLAN's stored chunk sizes — what actually went over the wire — rather
187
+ // than over the decompressed total, which is not what was sent.
188
+ const uploadMs = since(uploadStartedAt);
189
+ const uploadedBytes = plan.chunks.reduce((sum, chunk) => sum + chunk.bytes, 0);
190
+ timings.uploadMs = uploadMs;
191
+ timings.uploadedBytes = uploadedBytes;
192
+ onProgress(`Uploaded ${plan.chunks.length} chunks (${uploadedBytes} bytes) in ` +
193
+ `${seconds(uploadMs)}` +
194
+ (uploadMs > 0 ? ` (${megabytesPerSecond(uploadedBytes, uploadMs)}).` : "."));
195
+ // ── commit ──
196
+ const committedAt = now();
197
+ try {
198
+ session = await client.commitIngest(documentId, sessionId);
199
+ }
200
+ catch (error) {
201
+ return giveUp(String(error?.message ?? error), 1);
202
+ }
203
+ const commitMs = since(committedAt);
204
+ timings.commitMs = commitMs;
205
+ onProgress(`Session ${sessionId} committed.`);
206
+ onProgress(`Committed in ${seconds(commitMs)}.`);
207
+ if (!wait) {
208
+ // Nothing was waited for, so the wait is zero rather than unknown: the
209
+ // session keeps running and `ingests get` is where its own time is.
210
+ timings.waitMs = 0;
211
+ return { exitCode: 0, sessionId, session, timings, message: undefined };
212
+ }
213
+ // ── poll ──
214
+ //
215
+ // Past this point the session is the server's business: the copy has begun
216
+ // and the document's tables will be swapped whatever this process does. So
217
+ // neither a timeout nor a Ctrl-C nor a read that keeps failing aborts it —
218
+ // each says so and leaves.
219
+ const started = now();
220
+ let reported = null;
221
+ let reportedProgress = "";
222
+ // The current run of consecutive failed reads: how many, when it started on
223
+ // the injected clock, and the last thing the server said. All three reset on
224
+ // any read that answers.
225
+ let readFailures = 0;
226
+ let failingSince = 0;
227
+ let lastReadError = "";
228
+ const stillRunning = () => `The bulk load is still running on the server; read it with ` +
229
+ `\`primitive documents ingests get ${documentId} ${sessionId}\`.`;
230
+ // Every door out of the WATCHING records the wait it is ending (#3598): the
231
+ // time was really spent on the server whichever way the watching stopped,
232
+ // and a load that gave up is exactly when somebody needs that number.
233
+ const interrupted = (view) => {
234
+ timings.waitMs = since(started);
235
+ return {
236
+ exitCode: 130,
237
+ sessionId,
238
+ session: view,
239
+ timings,
240
+ message: `Interrupted. The session keeps running; watch it with ` +
241
+ `\`primitive documents ingests get ${documentId} ${sessionId}\`.`,
242
+ };
243
+ };
244
+ const timedOut = (view, elapsed) => {
245
+ timings.waitMs = elapsed;
246
+ return {
247
+ exitCode: 124,
248
+ sessionId,
249
+ session: view,
250
+ timings,
251
+ message: `Gave up waiting after ${Math.round(elapsed / 1000)}s. The session ` +
252
+ `keeps running; watch it with \`primitive documents ingests get ` +
253
+ `${documentId} ${sessionId}\`.`,
254
+ };
255
+ };
256
+ for (;;) {
257
+ let view;
258
+ try {
259
+ view = await client.getIngest(documentId, sessionId);
260
+ if (readFailures > 0) {
261
+ onProgress(`Session ${sessionId} answered again after ${readFailures} ` +
262
+ `unanswered read(s); polling resumed.`);
263
+ readFailures = 0;
264
+ lastReadError = "";
265
+ }
266
+ }
267
+ catch (error) {
268
+ const message = String(error?.message ?? error);
269
+ // A refusal is a refusal: it is reported now, exactly as before — and
270
+ // it still carries the wait that preceded it (finding 3598-FROZEN-01),
271
+ // because a load that polled for minutes before the reads stopped
272
+ // answering really did spend those minutes waiting on the server.
273
+ if (!isTransientReadError(error)) {
274
+ timings.waitMs = since(started);
275
+ return { exitCode: 1, sessionId, session: null, timings, message };
276
+ }
277
+ if (readFailures === 0)
278
+ failingSince = now();
279
+ readFailures += 1;
280
+ lastReadError = message;
281
+ const failingFor = now() - failingSince;
282
+ if (failingFor >= POLL_READ_RETRY_WINDOW_MS) {
283
+ // 124, the documented "a wait that gave up" code — never 1. Giving up
284
+ // on the watching is not the load failing, and the operator has to be
285
+ // able to tell them apart by the exit code alone.
286
+ timings.waitMs = since(started);
287
+ return {
288
+ exitCode: 124,
289
+ sessionId,
290
+ session: null,
291
+ timings,
292
+ message: `Stopped watching after ${Math.round(failingFor / 1000)}s of ` +
293
+ `unanswered session reads (${readFailures} attempts). ` +
294
+ `${stillRunning()} Last read error: ${lastReadError}`,
295
+ };
296
+ }
297
+ onProgress(`Session ${sessionId} did not answer (attempt ${readFailures}, ` +
298
+ `unanswered for ${Math.round(failingFor / 1000)}s); retrying` +
299
+ (readFailures === 1 ? `: ${message}` : "."));
300
+ await sleep(readBackoffMs(readFailures));
301
+ // The backoff sleep is part of the wait, so it honours the same two
302
+ // doors the success path does and with the same words: neither aborts.
303
+ if (signal?.aborted)
304
+ return interrupted(null);
305
+ const waited = now() - started;
306
+ if (timeoutMs !== null && waited >= timeoutMs)
307
+ return timedOut(null, waited);
308
+ continue;
309
+ }
310
+ if (view?.state !== reported) {
311
+ reported = view?.state ?? null;
312
+ onProgress(`Session ${sessionId} is ${reported}.`);
313
+ }
314
+ const progress = describeProgress(view?.progress);
315
+ if (progress && progress !== reportedProgress) {
316
+ reportedProgress = progress;
317
+ onProgress(` ${progress}`);
318
+ }
319
+ if (view?.state === "complete") {
320
+ const waitMs = since(started);
321
+ timings.waitMs = waitMs;
322
+ // What the PLATFORM spent, separable from what this side did. The
323
+ // session's own `timings` say where inside the platform it went.
324
+ onProgress(`Server finished in ${seconds(waitMs)}.`);
325
+ return { exitCode: 0, sessionId, session: view, timings };
326
+ }
327
+ if (view?.state === "failed" || view?.state === "aborted") {
328
+ const waitMs = since(started);
329
+ timings.waitMs = waitMs;
330
+ onProgress(`Server finished in ${seconds(waitMs)}.`);
331
+ return {
332
+ exitCode: 1,
333
+ sessionId,
334
+ session: view,
335
+ timings,
336
+ message: describeFailure(view),
337
+ };
338
+ }
339
+ const elapsed = now() - started;
340
+ if (timeoutMs !== null && elapsed >= timeoutMs)
341
+ return timedOut(view, elapsed);
342
+ await sleep(elapsed >= POLL_SLOWS_AFTER_MS ? POLL_SLOW_MS : POLL_FAST_MS);
343
+ if (signal?.aborted)
344
+ return interrupted(view);
345
+ }
346
+ }
347
+ /** A duration an operator reads at a glance, never a bare millisecond count. */
348
+ function seconds(ms) {
349
+ return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`;
350
+ }
351
+ /** The unit a bulk load's speed is discussed in. */
352
+ function megabytesPerSecond(bytes, ms) {
353
+ return `${((bytes / 1_000_000 / ms) * 1000).toFixed(2)} MB/s`;
354
+ }
355
+ /** The session's own words for why it stopped. */
356
+ function describeFailure(view) {
357
+ if (view?.state === "aborted")
358
+ return "The session was aborted.";
359
+ const failure = view?.failure;
360
+ if (!failure)
361
+ return "The session failed.";
362
+ // A failure's rows are `{ model, id }` on the wire, and a bounded sample of
363
+ // them; a string is accepted too so a stub or an older server still reads.
364
+ const rows = Array.isArray(failure.rows) && failure.rows.length > 0
365
+ ? `\n ${failure.rows
366
+ .map((row) => typeof row === "string" ? row : `${row?.model} ${row?.id}`)
367
+ .join("\n ")}`
368
+ : "";
369
+ return `${failure.code}: ${failure.reason}${rows}`;
370
+ }
371
+ /** One line of counters, or nothing while they are all still zero. */
372
+ function describeProgress(progress) {
373
+ if (!progress)
374
+ return "";
375
+ const copied = progress.rowsCopied ?? 0;
376
+ const chunks = progress.chunksApplied ?? 0;
377
+ const rows = progress.rowsApplied ?? 0;
378
+ const reconciled = progress.recordsReconciled ?? 0;
379
+ if (copied + chunks + rows + reconciled === 0)
380
+ return "";
381
+ return (`copied ${copied} rows, applied ${chunks} chunks / ${rows} rows, ` +
382
+ `reconciled ${reconciled} records`);
383
+ }
384
+ //# sourceMappingURL=document-ingest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-ingest.js","sourceRoot":"","sources":["../../../src/lib/document-ingest.ts"],"names":[],"mappings":"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"}
@@ -29,20 +29,62 @@
29
29
  * 4. defaultEnvironment (the committed team default)
30
30
  * 5. the sole environment, if there is exactly one
31
31
  */
32
- /** Schema version of `.primitive/config.json` this core understands. */
32
+ /** Schema version of `primitive/config.json` this core understands. */
33
33
  export declare const PRIMITIVE_CONFIG_VERSION = 1;
34
34
  /** Schema version of `.primitive/local.json` this core writes and reads. */
35
35
  export declare const PRIMITIVE_LOCAL_STATE_VERSION = 1;
36
- export declare const PROJECT_CONFIG_DIR = ".primitive";
36
+ /**
37
+ * The two directories, split apart by #3153 so one name never means two
38
+ * things: `primitive/` is the COMMITTED tree (the config and every
39
+ * environment's TOML), `.primitive/` is MACHINE-LOCAL state (credentials, this
40
+ * machine's selection, snapshot backups) and is gitignored whole.
41
+ */
42
+ export declare const PROJECT_TREE_DIR = "primitive";
43
+ export declare const PROJECT_LOCAL_STATE_DIR = ".primitive";
37
44
  export declare const PROJECT_CONFIG_FILENAME = "config.json";
38
45
  export declare const LOCAL_STATE_FILENAME = "local.json";
39
- export declare const PROJECT_CONFIG_DISPLAY_NAME = ".primitive/config.json";
46
+ export declare const PROJECT_CONFIG_DISPLAY_NAME = "primitive/config.json";
40
47
  export declare const LOCAL_STATE_DISPLAY_NAME = ".primitive/local.json";
48
+ /**
49
+ * The one refusal for a project that never migrated off the pre-#3153 layout:
50
+ * a `.primitive/config.json` with no `primitive/config.json` beside it.
51
+ *
52
+ * That shape is refused rather than walked past because the walk's next stop
53
+ * is the parent directory, and in a nested checkout the parent may be a
54
+ * DIFFERENT project — a command run in the unmigrated one would silently act
55
+ * on the other's app. Nothing else is refused (#3392): anything left under
56
+ * `.primitive/` beside a migrated tree is machine-local junk, not signal.
57
+ *
58
+ * One sentence, naming both halves — the file that was found and the file
59
+ * that is expected — and no document. The migration page this once pointed
60
+ * at was never published (#3155, won't-fix), and every reader that shares
61
+ * this refusal — this core, the generated Vite-plugin copy, the Swift
62
+ * pre-build script, the Vue template's deploy script — says the same thing.
63
+ */
64
+ export declare function unmigratedProjectMessage(found: string, expected: string): string;
65
+ /**
66
+ * The one refusal for a command run outside a Primitive project (#3154,
67
+ * intent criterion 5).
68
+ *
69
+ * There is exactly one way to resolve the configuration tree, the credentials
70
+ * and the app: the environment named by `primitive/config.json`. Outside a
71
+ * project there is no such thing, and the CLI says so once, in these words,
72
+ * wherever the question is asked — the resolver core, the CLI's
73
+ * `requireCurrentEnvironment()`, and the credentials store.
74
+ *
75
+ * Two constraints on the wording, both test-asserted:
76
+ *
77
+ * - it names `primitive init`, the one command that creates what is missing;
78
+ * - it never names `primitive env add`, which after this change also fails
79
+ * outside a project — an error may only offer an invocation it does not
80
+ * itself block.
81
+ */
82
+ export declare function missingConfigMessage(where: string): string;
41
83
  /**
42
84
  * Error kinds callers branch on. Every failure is one of these — no reader is
43
85
  * ever left guessing what a broken config meant.
44
86
  */
45
- export type PrimitiveEnvErrorKind = "missing-config" | "malformed-config" | "unsupported-version" | "corrupt-local-state" | "unknown-environment" | "no-selection" | "missing-app-id";
87
+ export type PrimitiveEnvErrorKind = "missing-config" | "malformed-config" | "unsupported-version" | "corrupt-local-state" | "unknown-environment" | "no-selection" | "missing-app-id" | "stale-layout";
46
88
  export declare class PrimitiveEnvError extends Error {
47
89
  readonly kind: PrimitiveEnvErrorKind;
48
90
  readonly path?: string;
@@ -50,10 +92,68 @@ export declare class PrimitiveEnvError extends Error {
50
92
  }
51
93
  export interface CoreEnvironmentEntry {
52
94
  apiUrl: string;
53
- appId?: string;
95
+ /**
96
+ * The one app this environment names (#3152). Required: selecting an
97
+ * environment selects exactly one app, so there is no per-machine "current
98
+ * app" for a second reader to disagree with.
99
+ */
100
+ appId: string;
54
101
  appName?: string;
102
+ /**
103
+ * The app's web counterpart for this environment, as a normalized origin
104
+ * (#2982). Read through `normalizeWebOrigin`, so a value this reader carries
105
+ * is always an origin — see that function for why the typed read other
106
+ * fields get is not enough here.
107
+ */
108
+ webUrl?: string;
55
109
  description?: string;
56
110
  }
111
+ /**
112
+ * The app's web counterpart as a normalized ORIGIN, or undefined when the
113
+ * value cannot be one (#2982).
114
+ *
115
+ * Unlike `appId`/`appName`, a typed read is not enough. This value is not
116
+ * merely carried: the Swift app points its emailed sign-in link at
117
+ * `<webUrl>/oauth/callback` and trusts incoming universal links from the same
118
+ * origin, and BOTH jobs need an origin —
119
+ *
120
+ * - https except loopback, the rule the server applies to every redirect URI
121
+ * (`src/auth/redirect-uri.ts`): an origin that cannot be allow-listed turns
122
+ * every sign-in request into a 400, and a plain-http link would carry the
123
+ * magic token in clear;
124
+ * - no path, query or fragment: the web client serves its callback on its own
125
+ * origin plus `/oauth/callback`, which is also the exact path the
126
+ * `apple-app-site-association` component claims;
127
+ * - no credentials, which have no business in a link that goes out by email.
128
+ *
129
+ * `primitive env add --web-url` and the project-config validation reject those
130
+ * shapes with a field-named error, but neither is on the path from a
131
+ * hand-edited `primitive/config.json` to an Xcode build: the Swift template's
132
+ * pre-build script reads the file directly. So every reader that feeds
133
+ * `primitive.json` applies the contract, and a value that misses it is read as
134
+ * "this environment has no web counterpart" — code-only email, no trusted
135
+ * origin, nothing silently pointed at the wrong URL.
136
+ *
137
+ * The message-producing twin of this rule lives in `cli/src/lib/web-url.ts`;
138
+ * this file cannot import it (see COPYABILITY above), and a test pins that the
139
+ * two agree.
140
+ */
141
+ export declare function normalizeWebOrigin(value: unknown): string | undefined;
142
+ /**
143
+ * The one remedy for an environment that names no app (#3152), shared by every
144
+ * reader that can hit it: this core, the CLI's `validateProjectConfig`, the
145
+ * Swift template's pre-build script, and the Vue template's cf-deploy.
146
+ *
147
+ * Two constraints on the wording, both test-asserted:
148
+ *
149
+ * - it never says `primitive env add <name>`, which REFUSES an environment
150
+ * name that already exists — the shape this error is always about;
151
+ * - it names no command the failure itself blocks. The CLI validates the
152
+ * project config before any command parses, so every `primitive` command
153
+ * run inside the broken project fails with this same error. The only
154
+ * invocation it may offer is one it says out loud to run elsewhere.
155
+ */
156
+ export declare function missingAppIdMessage(name: string, configPath?: string): string;
57
157
  export interface CoreProjectConfig {
58
158
  version: number;
59
159
  defaultEnvironment?: string;
@@ -66,8 +166,11 @@ export interface ResolvedPrimitiveEnv {
66
166
  apiUrl: string;
67
167
  /** Derived from apiUrl by scheme swap — never authored separately. */
68
168
  wsUrl: string;
69
- appId?: string;
169
+ /** The app this environment names. Always present — see #3152. */
170
+ appId: string;
70
171
  appName?: string;
172
+ /** The app's web counterpart for the SELECTED environment, if it has one. */
173
+ webUrl?: string;
71
174
  description?: string;
72
175
  source: PrimitiveEnvSource;
73
176
  configPath: string;
@@ -83,11 +186,6 @@ export interface ResolveOptions {
83
186
  explicitEnvName?: string | null;
84
187
  /** Use this config file instead of discovering one. */
85
188
  configPath?: string;
86
- /**
87
- * Identity consumers (the Vite plugin, cf-deploy, the Swift script) cannot
88
- * work without an app ID; plain CLI resolution can, and always could.
89
- */
90
- requireAppId?: boolean;
91
189
  }
92
190
  /**
93
191
  * Derives the WebSocket URL from the API URL. Every apiUrl/wsUrl pair in
@@ -96,26 +194,39 @@ export interface ResolveOptions {
96
194
  */
97
195
  export declare function deriveWsUrl(apiUrl: string): string;
98
196
  /**
99
- * Finds `.primitive/config.json` by walking up from `cwd`, or returns the
197
+ * Finds `primitive/config.json` by walking up from `cwd`, or returns the
100
198
  * PRIMITIVE_PROJECT_CONFIG override when it is set (and exists).
199
+ *
200
+ * The override is checked for provenance BEFORE the file is read (#3153, F2):
201
+ * it bypasses the walk, so without this an old-layout config could be handed
202
+ * straight to a reader and defeat the cutover. An override that points inside
203
+ * a `.primitive/` directory IS the old layout, named explicitly.
204
+ *
205
+ * This is the one function every reader goes through — `resolvePrimitiveEnv`
206
+ * and the CLI's own `loadProjectConfig`/`findProjectRoot` alike — so the
207
+ * unmigrated-project refusal lives HERE, in the walk, or the commands that
208
+ * never call `resolvePrimitiveEnv` would walk past the old anchor into the
209
+ * parent directory's project.
101
210
  */
102
211
  export declare function findPrimitiveConfigPath(options?: {
103
212
  cwd?: string;
104
213
  env?: Record<string, string | undefined>;
105
214
  }): string | null;
106
215
  /**
107
- * The project root for a config path: the parent of `.primitive/`, or the
216
+ * The project root for a config path: the parent of `primitive/`, or the
108
217
  * containing directory when the config was pointed at directly (a fixture via
109
- * PRIMITIVE_PROJECT_CONFIG, which is not inside a `.primitive/` directory).
218
+ * PRIMITIVE_PROJECT_CONFIG, which is not inside a `primitive/` directory).
110
219
  */
111
220
  export declare function projectRootForConfigPath(configPath: string): string;
112
221
  /**
113
- * Local state lives beside the config it belongs to, so a fixture-pointed
114
- * config never picks up a real project's selection and vice versa.
222
+ * Machine-local state lives under the project root's `.primitive/`, not beside
223
+ * the config — the config is committed now, and the two must not share a
224
+ * directory. A fixture-pointed bare config still gets its own `.primitive/`
225
+ * in its own directory, so it never picks up a real project's selection.
115
226
  */
116
227
  export declare function localStatePathForConfigPath(configPath: string): string;
117
228
  /**
118
- * Reads and structurally validates `.primitive/config.json`. Throws rather
229
+ * Reads and structurally validates `primitive/config.json`. Throws rather
119
230
  * than guessing: a config that cannot be understood is always louder than a
120
231
  * silent fallback to the wrong backend.
121
232
  */