primitive-admin 1.1.0-alpha.8 → 1.1.0-alpha.80

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 (522) hide show
  1. package/README.md +444 -106
  2. package/assets/skill/skills/primitive-platform/SKILL.md +811 -0
  3. package/dist/bin/primitive.d.ts +2 -0
  4. package/dist/bin/primitive.js +373 -21
  5. package/dist/bin/primitive.js.map +1 -1
  6. package/dist/src/commands/admins.d.ts +2 -0
  7. package/dist/src/commands/admins.js +128 -19
  8. package/dist/src/commands/admins.js.map +1 -1
  9. package/dist/src/commands/analytics.d.ts +2 -0
  10. package/dist/src/commands/analytics.js +700 -55
  11. package/dist/src/commands/analytics.js.map +1 -1
  12. package/dist/src/commands/apps.d.ts +2 -0
  13. package/dist/src/commands/apps.js +54 -96
  14. package/dist/src/commands/apps.js.map +1 -1
  15. package/dist/src/commands/auth.d.ts +2 -0
  16. package/dist/src/commands/auth.js +196 -105
  17. package/dist/src/commands/auth.js.map +1 -1
  18. package/dist/src/commands/blob-buckets.d.ts +2 -0
  19. package/dist/src/commands/blob-buckets.js +330 -0
  20. package/dist/src/commands/blob-buckets.js.map +1 -0
  21. package/dist/src/commands/catalog.d.ts +2 -0
  22. package/dist/src/commands/catalog.js +37 -38
  23. package/dist/src/commands/catalog.js.map +1 -1
  24. package/dist/src/commands/collection-type-configs.d.ts +2 -0
  25. package/dist/src/commands/collection-type-configs.js +84 -0
  26. package/dist/src/commands/collection-type-configs.js.map +1 -0
  27. package/dist/src/commands/collections.d.ts +2 -0
  28. package/dist/src/commands/collections.js +565 -0
  29. package/dist/src/commands/collections.js.map +1 -0
  30. package/dist/src/commands/comparisons.d.ts +2 -0
  31. package/dist/src/commands/comparisons.js +6 -6
  32. package/dist/src/commands/comparisons.js.map +1 -1
  33. package/dist/src/commands/config.d.ts +46 -0
  34. package/dist/src/commands/config.js +465 -0
  35. package/dist/src/commands/config.js.map +1 -0
  36. package/dist/src/commands/connections.d.ts +2 -0
  37. package/dist/src/commands/connections.js +100 -0
  38. package/dist/src/commands/connections.js.map +1 -0
  39. package/dist/src/commands/cron-triggers.d.ts +2 -0
  40. package/dist/src/commands/cron-triggers.js +265 -0
  41. package/dist/src/commands/cron-triggers.js.map +1 -0
  42. package/dist/src/commands/database-type-configs.d.ts +2 -0
  43. package/dist/src/commands/database-type-configs.js +163 -0
  44. package/dist/src/commands/database-type-configs.js.map +1 -0
  45. package/dist/src/commands/database-types.d.ts +2 -0
  46. package/dist/src/commands/database-types.js +171 -0
  47. package/dist/src/commands/database-types.js.map +1 -0
  48. package/dist/src/commands/databases.d.ts +65 -0
  49. package/dist/src/commands/databases.js +1859 -238
  50. package/dist/src/commands/databases.js.map +1 -1
  51. package/dist/src/commands/documents.d.ts +2 -0
  52. package/dist/src/commands/documents.js +2062 -19
  53. package/dist/src/commands/documents.js.map +1 -1
  54. package/dist/src/commands/email-templates.d.ts +2 -0
  55. package/dist/src/commands/email-templates.js +174 -0
  56. package/dist/src/commands/email-templates.js.map +1 -0
  57. package/dist/src/commands/env.d.ts +23 -0
  58. package/dist/src/commands/env.js +385 -0
  59. package/dist/src/commands/env.js.map +1 -0
  60. package/dist/src/commands/feature-flags.d.ts +14 -0
  61. package/dist/src/commands/feature-flags.js +116 -0
  62. package/dist/src/commands/feature-flags.js.map +1 -0
  63. package/dist/src/commands/functions.d.ts +20 -0
  64. package/dist/src/commands/functions.js +672 -0
  65. package/dist/src/commands/functions.js.map +1 -0
  66. package/dist/src/commands/group-type-configs.d.ts +2 -0
  67. package/dist/src/commands/group-type-configs.js +86 -0
  68. package/dist/src/commands/group-type-configs.js.map +1 -0
  69. package/dist/src/commands/groups.d.ts +2 -0
  70. package/dist/src/commands/groups.js +39 -108
  71. package/dist/src/commands/groups.js.map +1 -1
  72. package/dist/src/commands/guides.d.ts +223 -0
  73. package/dist/src/commands/guides.js +617 -65
  74. package/dist/src/commands/guides.js.map +1 -1
  75. package/dist/src/commands/init.d.ts +37 -0
  76. package/dist/src/commands/init.js +1693 -208
  77. package/dist/src/commands/init.js.map +1 -1
  78. package/dist/src/commands/integrations.d.ts +2 -0
  79. package/dist/src/commands/integrations.js +402 -182
  80. package/dist/src/commands/integrations.js.map +1 -1
  81. package/dist/src/commands/llm.d.ts +2 -0
  82. package/dist/src/commands/llm.js +13 -8
  83. package/dist/src/commands/llm.js.map +1 -1
  84. package/dist/src/commands/locks.d.ts +8 -0
  85. package/dist/src/commands/locks.js +160 -0
  86. package/dist/src/commands/locks.js.map +1 -0
  87. package/dist/src/commands/metadata-category-configs.d.ts +12 -0
  88. package/dist/src/commands/metadata-category-configs.js +112 -0
  89. package/dist/src/commands/metadata-category-configs.js.map +1 -0
  90. package/dist/src/commands/metadata.d.ts +2 -0
  91. package/dist/src/commands/metadata.js +281 -0
  92. package/dist/src/commands/metadata.js.map +1 -0
  93. package/dist/src/commands/prompts.d.ts +2 -0
  94. package/dist/src/commands/prompts.js +275 -621
  95. package/dist/src/commands/prompts.js.map +1 -1
  96. package/dist/src/commands/rule-sets.d.ts +3 -0
  97. package/dist/src/commands/rule-sets.js +137 -147
  98. package/dist/src/commands/rule-sets.js.map +1 -1
  99. package/dist/src/commands/scripts.d.ts +30 -0
  100. package/dist/src/commands/scripts.js +679 -0
  101. package/dist/src/commands/scripts.js.map +1 -0
  102. package/dist/src/commands/secrets.d.ts +2 -0
  103. package/dist/src/commands/secrets.js +108 -0
  104. package/dist/src/commands/secrets.js.map +1 -0
  105. package/dist/src/commands/sessions.d.ts +2 -0
  106. package/dist/src/commands/sessions.js +75 -0
  107. package/dist/src/commands/sessions.js.map +1 -0
  108. package/dist/src/commands/settings.d.ts +18 -0
  109. package/dist/src/commands/settings.js +54 -0
  110. package/dist/src/commands/settings.js.map +1 -0
  111. package/dist/src/commands/skill.d.ts +2 -0
  112. package/dist/src/commands/skill.js +29 -0
  113. package/dist/src/commands/skill.js.map +1 -0
  114. package/dist/src/commands/sync-app-settings.d.ts +158 -0
  115. package/dist/src/commands/sync-app-settings.js +328 -0
  116. package/dist/src/commands/sync-app-settings.js.map +1 -0
  117. package/dist/src/commands/sync.d.ts +2514 -0
  118. package/dist/src/commands/sync.js +16106 -848
  119. package/dist/src/commands/sync.js.map +1 -1
  120. package/dist/src/commands/tokens.d.ts +2 -0
  121. package/dist/src/commands/tokens.js +130 -21
  122. package/dist/src/commands/tokens.js.map +1 -1
  123. package/dist/src/commands/users.d.ts +2 -0
  124. package/dist/src/commands/users.js +532 -23
  125. package/dist/src/commands/users.js.map +1 -1
  126. package/dist/src/commands/vars.d.ts +8 -0
  127. package/dist/src/commands/vars.js +96 -0
  128. package/dist/src/commands/vars.js.map +1 -0
  129. package/dist/src/commands/waitlist.d.ts +2 -0
  130. package/dist/src/commands/waitlist.js +10 -10
  131. package/dist/src/commands/waitlist.js.map +1 -1
  132. package/dist/src/commands/webhooks.d.ts +2 -0
  133. package/dist/src/commands/webhooks.js +562 -0
  134. package/dist/src/commands/webhooks.js.map +1 -0
  135. package/dist/src/commands/workflows.d.ts +116 -0
  136. package/dist/src/commands/workflows.js +1638 -729
  137. package/dist/src/commands/workflows.js.map +1 -1
  138. package/dist/src/lib/access-rule-display.d.ts +21 -0
  139. package/dist/src/lib/access-rule-display.js +34 -0
  140. package/dist/src/lib/access-rule-display.js.map +1 -0
  141. package/dist/src/lib/api-client.d.ts +2147 -0
  142. package/dist/src/lib/api-client.js +2042 -160
  143. package/dist/src/lib/api-client.js.map +1 -1
  144. package/dist/src/lib/app-match-guard.d.ts +44 -0
  145. package/dist/src/lib/app-match-guard.js +72 -0
  146. package/dist/src/lib/app-match-guard.js.map +1 -0
  147. package/dist/src/lib/app-settings-descriptor.d.ts +263 -0
  148. package/dist/src/lib/app-settings-descriptor.js +584 -0
  149. package/dist/src/lib/app-settings-descriptor.js.map +1 -0
  150. package/dist/src/lib/auth-flow.d.ts +8 -0
  151. package/dist/src/lib/batch.d.ts +26 -0
  152. package/dist/src/lib/batch.js +32 -0
  153. package/dist/src/lib/batch.js.map +1 -0
  154. package/dist/src/lib/block-layout.d.ts +160 -0
  155. package/dist/src/lib/block-layout.js +451 -0
  156. package/dist/src/lib/block-layout.js.map +1 -0
  157. package/dist/src/lib/block-selector.d.ts +58 -0
  158. package/dist/src/lib/block-selector.js +92 -0
  159. package/dist/src/lib/block-selector.js.map +1 -0
  160. package/dist/src/lib/canonical-json.d.ts +12 -0
  161. package/dist/src/lib/canonical-json.js +35 -0
  162. package/dist/src/lib/canonical-json.js.map +1 -0
  163. package/dist/src/lib/channel.d.ts +30 -0
  164. package/dist/src/lib/channel.js +68 -0
  165. package/dist/src/lib/channel.js.map +1 -0
  166. package/dist/src/lib/cli-manifest.d.ts +68 -0
  167. package/dist/src/lib/cli-manifest.js +71 -0
  168. package/dist/src/lib/cli-manifest.js.map +1 -0
  169. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +101 -0
  170. package/dist/src/lib/codegen-shared/generatedFiles.js +191 -0
  171. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -0
  172. package/dist/src/lib/codegen-shared/prettierStable.d.ts +262 -0
  173. package/dist/src/lib/codegen-shared/prettierStable.js +610 -0
  174. package/dist/src/lib/codegen-shared/prettierStable.js.map +1 -0
  175. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +38 -0
  176. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +46 -0
  177. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -0
  178. package/dist/src/lib/config-json-field.d.ts +28 -0
  179. package/dist/src/lib/config-json-field.js +56 -0
  180. package/dist/src/lib/config-json-field.js.map +1 -0
  181. package/dist/src/lib/config-object-descriptor.d.ts +127 -0
  182. package/dist/src/lib/config-object-descriptor.js +696 -0
  183. package/dist/src/lib/config-object-descriptor.js.map +1 -0
  184. package/dist/src/lib/config-payload.d.ts +85 -0
  185. package/dist/src/lib/config-payload.js +116 -0
  186. package/dist/src/lib/config-payload.js.map +1 -0
  187. package/dist/src/lib/config-surface.d.ts +140 -0
  188. package/dist/src/lib/config-surface.js +317 -0
  189. package/dist/src/lib/config-surface.js.map +1 -0
  190. package/dist/src/lib/config-toml.d.ts +10 -0
  191. package/dist/src/lib/config-toml.js +42 -0
  192. package/dist/src/lib/config-toml.js.map +1 -0
  193. package/dist/src/lib/config.d.ts +71 -0
  194. package/dist/src/lib/config.js +71 -68
  195. package/dist/src/lib/config.js.map +1 -1
  196. package/dist/src/lib/confirm-prompt.d.ts +83 -0
  197. package/dist/src/lib/confirm-prompt.js +110 -0
  198. package/dist/src/lib/confirm-prompt.js.map +1 -0
  199. package/dist/src/lib/constants.d.ts +11 -0
  200. package/dist/src/lib/constants.js +12 -0
  201. package/dist/src/lib/constants.js.map +1 -0
  202. package/dist/src/lib/crash-handlers.d.ts +20 -0
  203. package/dist/src/lib/crash-handlers.js +49 -0
  204. package/dist/src/lib/crash-handlers.js.map +1 -0
  205. package/dist/src/lib/credentials-store.d.ts +104 -0
  206. package/dist/src/lib/credentials-store.js +336 -0
  207. package/dist/src/lib/credentials-store.js.map +1 -0
  208. package/dist/src/lib/csv.d.ts +47 -0
  209. package/dist/src/lib/csv.js +172 -0
  210. package/dist/src/lib/csv.js.map +1 -0
  211. package/dist/src/lib/data-input.d.ts +23 -0
  212. package/dist/src/lib/data-input.js +50 -0
  213. package/dist/src/lib/data-input.js.map +1 -0
  214. package/dist/src/lib/db-codegen/dbFingerprint.d.ts +10 -0
  215. package/dist/src/lib/db-codegen/dbFingerprint.js +17 -0
  216. package/dist/src/lib/db-codegen/dbFingerprint.js.map +1 -0
  217. package/dist/src/lib/db-codegen/dbGenerator.d.ts +67 -0
  218. package/dist/src/lib/db-codegen/dbGenerator.js +170 -0
  219. package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -0
  220. package/dist/src/lib/db-codegen/dbNaming.d.ts +87 -0
  221. package/dist/src/lib/db-codegen/dbNaming.js +180 -0
  222. package/dist/src/lib/db-codegen/dbNaming.js.map +1 -0
  223. package/dist/src/lib/db-codegen/dbTemplates.d.ts +272 -0
  224. package/dist/src/lib/db-codegen/dbTemplates.js +480 -0
  225. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -0
  226. package/dist/src/lib/db-codegen/dbTsTypes.d.ts +73 -0
  227. package/dist/src/lib/db-codegen/dbTsTypes.js +139 -0
  228. package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -0
  229. package/dist/src/lib/db-codegen/dbTypeIR.d.ts +146 -0
  230. package/dist/src/lib/db-codegen/dbTypeIR.js +517 -0
  231. package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -0
  232. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.d.ts +112 -0
  233. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js +211 -0
  234. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js.map +1 -0
  235. package/dist/src/lib/document-export-permissions.d.ts +30 -0
  236. package/dist/src/lib/document-export-permissions.js +54 -0
  237. package/dist/src/lib/document-export-permissions.js.map +1 -0
  238. package/dist/src/lib/env-resolver-core.d.ts +253 -0
  239. package/dist/src/lib/env-resolver-core.js +459 -0
  240. package/dist/src/lib/env-resolver-core.js.map +1 -0
  241. package/dist/src/lib/env-resolver.d.ts +99 -0
  242. package/dist/src/lib/env-resolver.js +153 -0
  243. package/dist/src/lib/env-resolver.js.map +1 -0
  244. package/dist/src/lib/fetch.d.ts +5 -0
  245. package/dist/src/lib/function-bundle.d.ts +141 -0
  246. package/dist/src/lib/function-bundle.js +333 -0
  247. package/dist/src/lib/function-bundle.js.map +1 -0
  248. package/dist/src/lib/function-collect.d.ts +123 -0
  249. package/dist/src/lib/function-collect.js +594 -0
  250. package/dist/src/lib/function-collect.js.map +1 -0
  251. package/dist/src/lib/function-db-types.d.ts +187 -0
  252. package/dist/src/lib/function-db-types.js +782 -0
  253. package/dist/src/lib/function-db-types.js.map +1 -0
  254. package/dist/src/lib/function-document-types.d.ts +50 -0
  255. package/dist/src/lib/function-document-types.js +163 -0
  256. package/dist/src/lib/function-document-types.js.map +1 -0
  257. package/dist/src/lib/function-grants-preflight.d.ts +64 -0
  258. package/dist/src/lib/function-grants-preflight.js +100 -0
  259. package/dist/src/lib/function-grants-preflight.js.map +1 -0
  260. package/dist/src/lib/function-log-tail.d.ts +83 -0
  261. package/dist/src/lib/function-log-tail.js +115 -0
  262. package/dist/src/lib/function-log-tail.js.map +1 -0
  263. package/dist/src/lib/function-schema-codegen.d.ts +118 -0
  264. package/dist/src/lib/function-schema-codegen.js +402 -0
  265. package/dist/src/lib/function-schema-codegen.js.map +1 -0
  266. package/dist/src/lib/function-sync.d.ts +263 -0
  267. package/dist/src/lib/function-sync.js +604 -0
  268. package/dist/src/lib/function-sync.js.map +1 -0
  269. package/dist/src/lib/function-triggers.d.ts +66 -0
  270. package/dist/src/lib/function-triggers.js +285 -0
  271. package/dist/src/lib/function-triggers.js.map +1 -0
  272. package/dist/src/lib/function-typecheck.d.ts +86 -0
  273. package/dist/src/lib/function-typecheck.js +370 -0
  274. package/dist/src/lib/function-typecheck.js.map +1 -0
  275. package/dist/src/lib/generated-allowlist.d.ts +28 -0
  276. package/dist/src/lib/generated-allowlist.js +281 -0
  277. package/dist/src/lib/generated-allowlist.js.map +1 -0
  278. package/dist/src/lib/generated-config-surfaces.d.ts +1445 -0
  279. package/dist/src/lib/generated-config-surfaces.js +5836 -0
  280. package/dist/src/lib/generated-config-surfaces.js.map +1 -0
  281. package/dist/src/lib/generated-sdk-types.d.ts +12 -0
  282. package/dist/src/lib/generated-sdk-types.js +13 -0
  283. package/dist/src/lib/generated-sdk-types.js.map +1 -0
  284. package/dist/src/lib/generated-template-lint.d.ts +212 -0
  285. package/dist/src/lib/generated-template-lint.js +624 -0
  286. package/dist/src/lib/generated-template-lint.js.map +1 -0
  287. package/dist/src/lib/generated-workflow-model-fields.d.ts +20 -0
  288. package/dist/src/lib/generated-workflow-model-fields.js +53 -0
  289. package/dist/src/lib/generated-workflow-model-fields.js.map +1 -0
  290. package/dist/src/lib/google-client-secret-status.d.ts +35 -0
  291. package/dist/src/lib/google-client-secret-status.js +56 -0
  292. package/dist/src/lib/google-client-secret-status.js.map +1 -0
  293. package/dist/src/lib/init-adopt.d.ts +16 -0
  294. package/dist/src/lib/init-adopt.js +34 -0
  295. package/dist/src/lib/init-adopt.js.map +1 -0
  296. package/dist/src/lib/init-assets.d.ts +39 -0
  297. package/dist/src/lib/init-assets.js +97 -0
  298. package/dist/src/lib/init-assets.js.map +1 -0
  299. package/dist/src/lib/init-client-platforms.d.ts +14 -0
  300. package/dist/src/lib/init-client-platforms.js +71 -0
  301. package/dist/src/lib/init-client-platforms.js.map +1 -0
  302. package/dist/src/lib/init-config.d.ts +98 -0
  303. package/dist/src/lib/init-config.js +186 -0
  304. package/dist/src/lib/init-config.js.map +1 -0
  305. package/dist/src/lib/init-email-redirect-uris.d.ts +37 -0
  306. package/dist/src/lib/init-email-redirect-uris.js +46 -0
  307. package/dist/src/lib/init-email-redirect-uris.js.map +1 -0
  308. package/dist/src/lib/init-ios-links.d.ts +91 -0
  309. package/dist/src/lib/init-ios-links.js +219 -0
  310. package/dist/src/lib/init-ios-links.js.map +1 -0
  311. package/dist/src/lib/init-plan.d.ts +80 -0
  312. package/dist/src/lib/init-plan.js +95 -0
  313. package/dist/src/lib/init-plan.js.map +1 -0
  314. package/dist/src/lib/init-production-env.d.ts +48 -0
  315. package/dist/src/lib/init-production-env.js +59 -0
  316. package/dist/src/lib/init-production-env.js.map +1 -0
  317. package/dist/src/lib/init-schema.d.ts +74 -0
  318. package/dist/src/lib/init-schema.js +358 -0
  319. package/dist/src/lib/init-schema.js.map +1 -0
  320. package/dist/src/lib/init-xcode.d.ts +34 -0
  321. package/dist/src/lib/init-xcode.js +138 -0
  322. package/dist/src/lib/init-xcode.js.map +1 -0
  323. package/dist/src/lib/integration-request-config.d.ts +30 -0
  324. package/dist/src/lib/integration-request-config.js +145 -0
  325. package/dist/src/lib/integration-request-config.js.map +1 -0
  326. package/dist/src/lib/integration-selector.d.ts +42 -0
  327. package/dist/src/lib/integration-selector.js +46 -0
  328. package/dist/src/lib/integration-selector.js.map +1 -0
  329. package/dist/src/lib/ios-app-id.d.ts +34 -0
  330. package/dist/src/lib/ios-app-id.js +69 -0
  331. package/dist/src/lib/ios-app-id.js.map +1 -0
  332. package/dist/src/lib/local-state.d.ts +55 -0
  333. package/dist/src/lib/local-state.js +167 -0
  334. package/dist/src/lib/local-state.js.map +1 -0
  335. package/dist/src/lib/local-test-cases.d.ts +63 -0
  336. package/dist/src/lib/local-test-cases.js +136 -0
  337. package/dist/src/lib/local-test-cases.js.map +1 -0
  338. package/dist/src/lib/log-inspection.d.ts +680 -0
  339. package/dist/src/lib/log-inspection.js +784 -0
  340. package/dist/src/lib/log-inspection.js.map +1 -0
  341. package/dist/src/lib/migration-nag.d.ts +49 -0
  342. package/dist/src/lib/migration-nag.js +164 -0
  343. package/dist/src/lib/migration-nag.js.map +1 -0
  344. package/dist/src/lib/object-status-filter.d.ts +22 -0
  345. package/dist/src/lib/object-status-filter.js +45 -0
  346. package/dist/src/lib/object-status-filter.js.map +1 -0
  347. package/dist/src/lib/output.d.ts +109 -0
  348. package/dist/src/lib/output.js +191 -8
  349. package/dist/src/lib/output.js.map +1 -1
  350. package/dist/src/lib/package-manager.d.ts +140 -0
  351. package/dist/src/lib/package-manager.js +305 -0
  352. package/dist/src/lib/package-manager.js.map +1 -0
  353. package/dist/src/lib/paginate.d.ts +83 -0
  354. package/dist/src/lib/paginate.js +95 -0
  355. package/dist/src/lib/paginate.js.map +1 -0
  356. package/dist/src/lib/platform-owned.d.ts +63 -0
  357. package/dist/src/lib/platform-owned.js +85 -0
  358. package/dist/src/lib/platform-owned.js.map +1 -0
  359. package/dist/src/lib/project-config.d.ts +122 -0
  360. package/dist/src/lib/project-config.js +244 -0
  361. package/dist/src/lib/project-config.js.map +1 -0
  362. package/dist/src/lib/query-operators.d.ts +43 -0
  363. package/dist/src/lib/query-operators.js +80 -0
  364. package/dist/src/lib/query-operators.js.map +1 -0
  365. package/dist/src/lib/record-filter.d.ts +18 -0
  366. package/dist/src/lib/record-filter.js +55 -0
  367. package/dist/src/lib/record-filter.js.map +1 -0
  368. package/dist/src/lib/refresh-admin-credentials.d.ts +65 -0
  369. package/dist/src/lib/refresh-admin-credentials.js +103 -0
  370. package/dist/src/lib/refresh-admin-credentials.js.map +1 -0
  371. package/dist/src/lib/resolve-init-dev-port.d.ts +56 -0
  372. package/dist/src/lib/resolve-init-dev-port.js +55 -0
  373. package/dist/src/lib/resolve-init-dev-port.js.map +1 -0
  374. package/dist/src/lib/resolve-init-server.d.ts +64 -0
  375. package/dist/src/lib/resolve-init-server.js +77 -0
  376. package/dist/src/lib/resolve-init-server.js.map +1 -0
  377. package/dist/src/lib/resolve-platform.d.ts +74 -0
  378. package/dist/src/lib/resolve-platform.js +105 -0
  379. package/dist/src/lib/resolve-platform.js.map +1 -0
  380. package/dist/src/lib/run-status.d.ts +19 -0
  381. package/dist/src/lib/run-status.generated.d.ts +39 -0
  382. package/dist/src/lib/run-status.generated.js +66 -0
  383. package/dist/src/lib/run-status.generated.js.map +1 -0
  384. package/dist/src/lib/run-status.js +19 -0
  385. package/dist/src/lib/run-status.js.map +1 -0
  386. package/dist/src/lib/server-text-normalization.d.ts +51 -0
  387. package/dist/src/lib/server-text-normalization.js +90 -0
  388. package/dist/src/lib/server-text-normalization.js.map +1 -0
  389. package/dist/src/lib/server-url.d.ts +22 -0
  390. package/dist/src/lib/server-url.js +33 -0
  391. package/dist/src/lib/server-url.js.map +1 -0
  392. package/dist/src/lib/signing-secret-status.d.ts +81 -0
  393. package/dist/src/lib/signing-secret-status.js +116 -0
  394. package/dist/src/lib/signing-secret-status.js.map +1 -0
  395. package/dist/src/lib/skill-installer.d.ts +25 -0
  396. package/dist/src/lib/skill-installer.js +266 -0
  397. package/dist/src/lib/skill-installer.js.map +1 -0
  398. package/dist/src/lib/snapshots.d.ts +98 -0
  399. package/dist/src/lib/snapshots.js +294 -0
  400. package/dist/src/lib/snapshots.js.map +1 -0
  401. package/dist/src/lib/swift-codegen/dbGenerator.d.ts +113 -0
  402. package/dist/src/lib/swift-codegen/dbGenerator.js +914 -0
  403. package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -0
  404. package/dist/src/lib/swift-codegen/dbSwiftTypes.d.ts +42 -0
  405. package/dist/src/lib/swift-codegen/dbSwiftTypes.js +100 -0
  406. package/dist/src/lib/swift-codegen/dbSwiftTypes.js.map +1 -0
  407. package/dist/src/lib/swift-codegen/generator.d.ts +94 -0
  408. package/dist/src/lib/swift-codegen/generator.js +440 -0
  409. package/dist/src/lib/swift-codegen/generator.js.map +1 -0
  410. package/dist/src/lib/swift-codegen/schemaToSwift.d.ts +72 -0
  411. package/dist/src/lib/swift-codegen/schemaToSwift.js +644 -0
  412. package/dist/src/lib/swift-codegen/schemaToSwift.js.map +1 -0
  413. package/dist/src/lib/swift-codegen/siblingSymbols.d.ts +94 -0
  414. package/dist/src/lib/swift-codegen/siblingSymbols.js +155 -0
  415. package/dist/src/lib/swift-codegen/siblingSymbols.js.map +1 -0
  416. package/dist/src/lib/swift-codegen/swiftNaming.d.ts +85 -0
  417. package/dist/src/lib/swift-codegen/swiftNaming.js +198 -0
  418. package/dist/src/lib/swift-codegen/swiftNaming.js.map +1 -0
  419. package/dist/src/lib/sync-dir-selector.d.ts +21 -0
  420. package/dist/src/lib/sync-dir-selector.js +30 -0
  421. package/dist/src/lib/sync-dir-selector.js.map +1 -0
  422. package/dist/src/lib/sync-paths.d.ts +123 -0
  423. package/dist/src/lib/sync-paths.js +193 -0
  424. package/dist/src/lib/sync-paths.js.map +1 -0
  425. package/dist/src/lib/sync-resource-types.d.ts +563 -0
  426. package/dist/src/lib/sync-resource-types.js +1073 -0
  427. package/dist/src/lib/sync-resource-types.js.map +1 -0
  428. package/dist/src/lib/sync-selectors.d.ts +95 -0
  429. package/dist/src/lib/sync-selectors.js +228 -0
  430. package/dist/src/lib/sync-selectors.js.map +1 -0
  431. package/dist/src/lib/template.d.ts +170 -0
  432. package/dist/src/lib/template.js +484 -68
  433. package/dist/src/lib/template.js.map +1 -1
  434. package/dist/src/lib/test-case-file-names.d.ts +40 -0
  435. package/dist/src/lib/test-case-file-names.js +91 -0
  436. package/dist/src/lib/test-case-file-names.js.map +1 -0
  437. package/dist/src/lib/test-case-keys.d.ts +29 -0
  438. package/dist/src/lib/test-case-keys.js +55 -0
  439. package/dist/src/lib/test-case-keys.js.map +1 -0
  440. package/dist/src/lib/test-case-variables.d.ts +29 -0
  441. package/dist/src/lib/test-case-variables.js +71 -0
  442. package/dist/src/lib/test-case-variables.js.map +1 -0
  443. package/dist/src/lib/token-inject.d.ts +56 -0
  444. package/dist/src/lib/token-inject.js +204 -0
  445. package/dist/src/lib/token-inject.js.map +1 -0
  446. package/dist/src/lib/toml-database-config.d.ts +123 -0
  447. package/dist/src/lib/toml-database-config.js +544 -0
  448. package/dist/src/lib/toml-database-config.js.map +1 -0
  449. package/dist/src/lib/toml-metadata-config.d.ts +151 -0
  450. package/dist/src/lib/toml-metadata-config.js +476 -0
  451. package/dist/src/lib/toml-metadata-config.js.map +1 -0
  452. package/dist/src/lib/toml-native-form.d.ts +46 -0
  453. package/dist/src/lib/toml-native-form.js +78 -0
  454. package/dist/src/lib/toml-native-form.js.map +1 -0
  455. package/dist/src/lib/toml-params-validator.d.ts +129 -0
  456. package/dist/src/lib/toml-params-validator.js +298 -0
  457. package/dist/src/lib/toml-params-validator.js.map +1 -0
  458. package/dist/src/lib/toml-scalar-edit.d.ts +43 -0
  459. package/dist/src/lib/toml-scalar-edit.js +283 -0
  460. package/dist/src/lib/toml-scalar-edit.js.map +1 -0
  461. package/dist/src/lib/user-selector.d.ts +24 -0
  462. package/dist/src/lib/user-selector.js +33 -0
  463. package/dist/src/lib/user-selector.js.map +1 -0
  464. package/dist/src/lib/version-check.d.ts +35 -0
  465. package/dist/src/lib/version-check.js +241 -0
  466. package/dist/src/lib/version-check.js.map +1 -0
  467. package/dist/src/lib/watch.d.ts +121 -0
  468. package/dist/src/lib/watch.js +169 -0
  469. package/dist/src/lib/watch.js.map +1 -0
  470. package/dist/src/lib/web-url.d.ts +40 -0
  471. package/dist/src/lib/web-url.js +76 -0
  472. package/dist/src/lib/web-url.js.map +1 -0
  473. package/dist/src/lib/workflow-apply.d.ts +110 -0
  474. package/dist/src/lib/workflow-apply.js +164 -0
  475. package/dist/src/lib/workflow-apply.js.map +1 -0
  476. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.d.ts +129 -0
  477. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js +269 -0
  478. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js.map +1 -0
  479. package/dist/src/lib/workflow-codegen/generator.d.ts +96 -0
  480. package/dist/src/lib/workflow-codegen/generator.js +361 -0
  481. package/dist/src/lib/workflow-codegen/generator.js.map +1 -0
  482. package/dist/src/lib/workflow-codegen/invokerIR.d.ts +94 -0
  483. package/dist/src/lib/workflow-codegen/invokerIR.js +76 -0
  484. package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -0
  485. package/dist/src/lib/workflow-codegen/naming.d.ts +33 -0
  486. package/dist/src/lib/workflow-codegen/naming.js +81 -0
  487. package/dist/src/lib/workflow-codegen/naming.js.map +1 -0
  488. package/dist/src/lib/workflow-codegen/schemaToTs.d.ts +80 -0
  489. package/dist/src/lib/workflow-codegen/schemaToTs.js +303 -0
  490. package/dist/src/lib/workflow-codegen/schemaToTs.js.map +1 -0
  491. package/dist/src/lib/workflow-config-apply.d.ts +70 -0
  492. package/dist/src/lib/workflow-config-apply.js +137 -0
  493. package/dist/src/lib/workflow-config-apply.js.map +1 -0
  494. package/dist/src/lib/workflow-config-sidecar.d.ts +63 -0
  495. package/dist/src/lib/workflow-config-sidecar.js +96 -0
  496. package/dist/src/lib/workflow-config-sidecar.js.map +1 -0
  497. package/dist/src/lib/workflow-defaults.d.ts +29 -0
  498. package/dist/src/lib/workflow-defaults.js +41 -0
  499. package/dist/src/lib/workflow-defaults.js.map +1 -0
  500. package/dist/src/lib/workflow-field-descriptor.d.ts +91 -0
  501. package/dist/src/lib/workflow-field-descriptor.js +153 -0
  502. package/dist/src/lib/workflow-field-descriptor.js.map +1 -0
  503. package/dist/src/lib/workflow-fragments.d.ts +64 -0
  504. package/dist/src/lib/workflow-fragments.js +342 -0
  505. package/dist/src/lib/workflow-fragments.js.map +1 -0
  506. package/dist/src/lib/workflow-include-preserve.d.ts +76 -0
  507. package/dist/src/lib/workflow-include-preserve.js +286 -0
  508. package/dist/src/lib/workflow-include-preserve.js.map +1 -0
  509. package/dist/src/lib/workflow-payload.d.ts +98 -0
  510. package/dist/src/lib/workflow-payload.js +178 -0
  511. package/dist/src/lib/workflow-payload.js.map +1 -0
  512. package/dist/src/lib/workflow-toml-validator.d.ts +207 -0
  513. package/dist/src/lib/workflow-toml-validator.js +762 -0
  514. package/dist/src/lib/workflow-toml-validator.js.map +1 -0
  515. package/dist/src/lib/workflow-usage.d.ts +198 -0
  516. package/dist/src/lib/workflow-usage.js +312 -0
  517. package/dist/src/lib/workflow-usage.js.map +1 -0
  518. package/dist/src/types/index.d.ts +591 -0
  519. package/dist/src/validators.d.ts +65 -0
  520. package/dist/src/validators.js +64 -0
  521. package/dist/src/validators.js.map +1 -0
  522. package/package.json +34 -9
@@ -0,0 +1,2514 @@
1
+ import { Command } from "commander";
2
+ import { ApiClient } from "../lib/api-client.js";
3
+ import { type OperationFormHints } from "../lib/toml-database-config.js";
4
+ import { type FieldForm } from "../lib/toml-native-form.js";
5
+ import { type PushMode } from "../lib/config-payload.js";
6
+ import { type ConfigObjectSurface, type ConfigTable } from "../lib/generated-config-surfaces.js";
7
+ import { type PresenceOutcome, type TestBlockType } from "../lib/sync-resource-types.js";
8
+ export { resolveSlugCollisions, slugifyTestCaseName, testCaseFileBasenames, } from "../lib/test-case-file-names.js";
9
+ import type { SyncState } from "../types/index.js";
10
+ import { type SyncSelection } from "../lib/sync-selectors.js";
11
+ /**
12
+ * Wrap a server-side error so the printed message identifies which entity
13
+ * was in flight. Used by every entity create/update/delete call site in the
14
+ * push loops (issue #684).
15
+ *
16
+ * Format: `Failed to ${action} ${kind} ${key}: ${serverMessage}`
17
+ *
18
+ * - `ConflictError` is rethrown unchanged so the existing
19
+ * `ConflictError → conflicts[]` accumulator in each loop still fires.
20
+ * - Other errors are rewrapped as a new `ApiError` carrying the wrapped
21
+ * message + the original `details[]` and `statusCode`.
22
+ */
23
+ export declare function wrapEntityError(err: unknown, action: "create" | "update" | "delete" | "activate", kind: string, key: string): Error;
24
+ /**
25
+ * Attribute a `[[configs]]` failure to the prompt AND the config (#2972).
26
+ *
27
+ * The server describes such a failure in terms of the entry alone ("Config
28
+ * name already exists for this prompt"), and push applies a prompt through
29
+ * several requests, so a run over a tree of prompts printed one bare line the
30
+ * operator could only attribute by reading the create log above it. The pair
31
+ * is the same identity the server stores the entry under (`promptId#configName`).
32
+ *
33
+ * The result is marked `entityNamed`, so the prompt-level wrapper around the
34
+ * shared update body leaves it alone.
35
+ */
36
+ export declare function wrapPromptConfigError(err: unknown, action: "create" | "update" | "activate", promptKey: string, configName: string | undefined): Error;
37
+ export declare function computeFileHash(filePath: string): string;
38
+ /**
39
+ * A transform's comparison hash: the script body itself (#2731 B7).
40
+ *
41
+ * Unlike the config types there is no projection to do — the `.rhai` bytes are
42
+ * exactly what push sends and exactly what diff compares — so the "semantic"
43
+ * hash is just a stable hash of the body, computable from a server response
44
+ * without going through the file.
45
+ */
46
+ export declare function computeScriptBodyHash(body: string): string;
47
+ export declare function shouldPushFile(filePath: string, storedHash: string | undefined): boolean;
48
+ /**
49
+ * Hash the *expanded* (post-fragment-splice) content of a workflow TOML.
50
+ *
51
+ * The raw-file hash (`computeFileHash`) misses fragment-only edits: if a
52
+ * workflow uses `include = ["x"]` and only `workflow-fragments/x.toml`
53
+ * changes, the workflow file is byte-identical and shouldPushFile would
54
+ * skip the push, leaving the server with stale expanded steps.
55
+ *
56
+ * This helper hashes the parsed-and-expanded data instead, so fragment
57
+ * edits change the hash and force a re-push. Pass the already-parsed
58
+ * `tomlData` (from `parseTomlFile`) — that function runs the expander,
59
+ * so `tomlData` is the canonical expanded shape the server will see.
60
+ */
61
+ export declare function computeExpandedContentHash(parsed: any): string;
62
+ /**
63
+ * Variant of `shouldPushFile` for workflow files: compares the stored hash
64
+ * against the hash of the *expanded* content rather than the raw file
65
+ * bytes. Required so that edits to included fragments invalidate the
66
+ * push-skip cache for any workflow that references them.
67
+ */
68
+ export declare function shouldPushExpandedFile(parsed: any, storedHash: string | undefined): boolean;
69
+ /**
70
+ * The `[workflow].activeConfigName` a file actually states, trimmed — or
71
+ * `undefined` when the file has no opinion about activation.
72
+ *
73
+ * Push reads the key the same way: `applyWorkflowConfigSidecars` activates a
74
+ * configuration only for a truthy name and otherwise leaves the running one
75
+ * alone (`cli/src/lib/workflow-config-apply.ts`). Every diff decision about
76
+ * activation keys off this one predicate so the comparison cannot claim a
77
+ * difference push has no way to reconcile (#2743, review follow-up).
78
+ */
79
+ export declare function authoredActiveConfigName(parsed: any): string | undefined;
80
+ /**
81
+ * The configuration a server workflow is RUNNING — its `activeConfigId`, or the
82
+ * first config as the fallback `serializeWorkflow` has always used, so pull's
83
+ * `activeConfigName` and the diff's reading of it can never disagree.
84
+ */
85
+ export declare function resolveActiveWorkflowConfig(workflow: any, configs: any[]): any;
86
+ /**
87
+ * Normalize a parsed workflow TOML object into the form the SERVER would hold
88
+ * after a `config push` of that file. Used by `diff` so a hand-authored workflow
89
+ * compares equal to the running state it already describes — while a server
90
+ * value that genuinely differs still shows as Modified.
91
+ *
92
+ * In order:
93
+ * 1. Trim the fields the server trims (`name`, `description`).
94
+ * 2. Drop explicit-empty authored values — a `""` string or an empty
95
+ * `capabilities` array. Push sends these as `null` (or the model default),
96
+ * and the pull serializer omits the key for a cleared field, so "spelled
97
+ * out as empty" and "absent" have to hash the same (#2743).
98
+ * 3. Lowercase the two enums and floor the five queue limits, mirroring the
99
+ * server's accepted-value canonicalization
100
+ * (`src/workflows/config/workflow-field-handlers.ts`). A value the server
101
+ * would REJECT is left alone: push fails loudly for those, so there is
102
+ * nothing to preview. #2743 — without this, `dequeueOrder = "LIFO"` or
103
+ * `perUserMaxRunning = 4.9` pushes fine and then reports Modified forever.
104
+ * 4. Fill the model defaults (`WORKFLOW_MODEL_DEFAULTS`) for what the file
105
+ * omits. `perAppMax*` / `queueTtlSeconds` joined that list in #1177: once
106
+ * the pull serializer emitted them (a GET always returns them, they carry
107
+ * a non-null model default), an unchanged workflow hashed unequal to a
108
+ * local TOML that omits them — a false `modified` that made `config pull`
109
+ * rewrite the file just to inject defaults. (`status` was on this list
110
+ * until #2803 took availability off the config wire entirely.)
111
+ * 5. Fill `activeConfigName` with the name the server itself creates
112
+ * (`default`), so a hand-authored file that never named a config compares
113
+ * equal to the workflow that pushing it produces (#2743). This is the
114
+ * compare-BY-NAME case only: when the local file names no configuration at
115
+ * all, `config diff` hashes both sides with `compareActivation: false` and
116
+ * the fill never decides anything.
117
+ * 6. Fill `key` from `resolvedKey` when the file omits it. `[workflow].key`
118
+ * is optional — push and diff both resolve it from the FILENAME
119
+ * (`tomlData.workflow?.key || basename(file, ".toml")`) — while the pull
120
+ * serializer always emits it. Without this a file relying on the filename
121
+ * hashes unequal to the very workflow it describes and reports Modified
122
+ * forever, which is exactly the non-convergence #2743 removes. Callers
123
+ * that already know the resolved key pass it; the rest leave the key as
124
+ * authored.
125
+ *
126
+ * Only the DIFF-hash path (`hashWorkflowTomlForDiff`) normalizes; the stored
127
+ * content-hash path that push's skip check compares is untouched, so upgrading
128
+ * forces no one-time re-push (same note as #1446's schema canonicalization).
129
+ *
130
+ * Returns a shallow clone with a normalized `workflow` table; the input is
131
+ * not mutated. Non-workflow TOML (no `workflow` table) is returned unchanged.
132
+ */
133
+ export declare function normalizeWorkflowTomlDefaults(parsed: any, resolvedKey?: string): any;
134
+ /**
135
+ * Canonical content hash of a workflow TOML for `diff`'s content comparison.
136
+ * Applies `normalizeWorkflowTomlDefaults` first so omitted-vs-defaulted fields
137
+ * don't spuriously diff, then runs the same `computeExpandedContentHash` that
138
+ * pull stores and push compares. Both the local file and the
139
+ * `serializeWorkflow`-produced remote form flow through this single function,
140
+ * so the two sides are normalized identically by construction.
141
+ *
142
+ * `resolvedKey` is the key the caller already paired the two sides on (the
143
+ * `[workflow].key`, or the filename when the file omits it). Passing it lets a
144
+ * file that relies on the filename fallback hash equal to the remote form,
145
+ * which always spells the key out — see `normalizeWorkflowTomlDefaults`.
146
+ *
147
+ * `compareActivation: false` drops `activeConfigName` from the hash. `config diff`
148
+ * passes it when the local file names no configuration: push activates nothing
149
+ * for such a file, so the configuration the server happens to be running is not
150
+ * a difference push could ever reconcile, and hashing it in would report the
151
+ * workflow Modified after every successful push — the non-convergence #2743
152
+ * exists to remove (review follow-up). The remote name stays visible as a hint
153
+ * on the Synced row instead. Defaults to comparing, so the include-reconcile
154
+ * path (`reconcileWorkflowIncludes`, which asks whether the local file already
155
+ * expands to the running state) keeps seeing activation as content.
156
+ */
157
+ export declare function hashWorkflowTomlForDiff(parsed: any, resolvedKey?: string, options?: {
158
+ compareActivation?: boolean;
159
+ }): string;
160
+ /**
161
+ * Build the canonical content hash for a *server* workflow (as returned by
162
+ * `getWorkflow` + active `getWorkflowConfig`), mirroring exactly what a fresh
163
+ * `config pull` would write to disk. Serializes via `serializeWorkflow`, parses
164
+ * the resulting TOML (remote serialized TOML never carries `include`s, so a
165
+ * plain `parseConfigToml` is sufficient — no fragment path needed), then hashes
166
+ * through `hashWorkflowTomlForDiff` so it lines up with the local-file hash.
167
+ *
168
+ * `logger` is optional but `config diff` passes it: the serializer's
169
+ * unrecognized-server-key warning (#2644) is the only signal that this CLI
170
+ * version cannot represent a field the server holds, and a diff that swallowed
171
+ * it would report the workflow as in sync with no hint that anything is
172
+ * missing.
173
+ */
174
+ export declare function hashRemoteWorkflowForDiff(workflow: any, draft: any, configs: any[], logger?: (message: string) => void, options?: {
175
+ compareActivation?: boolean;
176
+ }): string;
177
+ export interface ParsedAuthSettings {
178
+ settings: Record<string, any>;
179
+ warnings: string[];
180
+ }
181
+ /**
182
+ * Build the [auth] block of app.toml from server settings (pull direction).
183
+ *
184
+ * Thin wrapper over the shared field descriptor
185
+ * (`cli/src/lib/app-settings-descriptor.ts`), which now owns the [auth] field
186
+ * set that `AUTH_BOOLEAN_KEYS`/`RECOGNIZED_AUTH_KEYS` used to hard-code. The
187
+ * descriptor drives all four sections in one place, so pull, push, and the
188
+ * unrecognized-key warning can never drift apart.
189
+ */
190
+ export declare function serializeAuthBlock(settings: any): Record<string, any>;
191
+ /**
192
+ * Translate the [auth] block of app.toml into app-settings fields (push
193
+ * direction). Kept as a focused helper over the shared descriptor; the full
194
+ * push path uses `parseTomlToAppSettings` for every section. Only keys present
195
+ * in the TOML are forwarded, so an omitted key never overwrites server state.
196
+ * Descriptor errors (a type mismatch) are surfaced alongside the
197
+ * unrecognized-key warnings.
198
+ */
199
+ export declare function parseAppAuthSettings(auth: Record<string, any>): ParsedAuthSettings;
200
+ type VarEntity = {
201
+ modifiedAt: string;
202
+ contentHash?: string;
203
+ };
204
+ /**
205
+ * Decide whether `config pull` should (over)write `vars.toml`, given the outcome
206
+ * of fetching the app's config vars.
207
+ *
208
+ * The distinction that matters (issue #1423 review): a SUCCESSFUL fetch that
209
+ * returns zero vars is safe to write — the comment-only file is the intended
210
+ * empty state. A FAILED fetch (transient 401/500/network) must NOT write,
211
+ * because `serializeVars([])` would clobber a good local `vars.toml` with a
212
+ * comment-only file while the pull otherwise reports success. On failure we
213
+ * keep the prior manifest entries so the untouched file and the sync state stay
214
+ * consistent.
215
+ *
216
+ * Pure and side-effect-free (the caller owns the actual `writeFileSync`) so the
217
+ * clobber guard is unit-testable without a live server.
218
+ */
219
+ export declare function planVarsPull(outcome: {
220
+ ok: true;
221
+ vars: any[];
222
+ } | {
223
+ ok: false;
224
+ }, priorVars?: Record<string, VarEntity>): {
225
+ write: boolean;
226
+ content: string | null;
227
+ varEntities: Record<string, VarEntity>;
228
+ };
229
+ /**
230
+ * `config pull --only var/<KEY>` — the same plan, narrowed to the named keys
231
+ * (issue #2645).
232
+ *
233
+ * `vars.toml` is one file for the whole var surface, so a scoped pull cannot
234
+ * just re-emit what the server returned: that would drop every var the
235
+ * operator did not name, including local edits they have not pushed yet. The
236
+ * merge is therefore local-file-first — the file's current keys are kept as
237
+ * they are, and only the SELECTED keys are taken from the server (or removed,
238
+ * when the server no longer has them).
239
+ *
240
+ * Pure like `planVarsPull`, for the same reason: this is the one place a pull
241
+ * can silently discard an author's work, so it has to be testable without a
242
+ * live server.
243
+ */
244
+ export declare function planScopedVarsPull(serverVars: Array<{
245
+ key: string;
246
+ value?: string;
247
+ updatedAt?: string;
248
+ modifiedAt?: string;
249
+ }>, selectedKeys: ReadonlySet<string>, localVars: Record<string, string>, priorVars?: Record<string, VarEntity>): {
250
+ content: string;
251
+ varEntities: Record<string, VarEntity>;
252
+ };
253
+ /**
254
+ * Validate a `config push`'s webhook writes against the server's per-app cap.
255
+ * Returns a list of human-readable errors (empty when the push may proceed).
256
+ *
257
+ * Run in `config push`'s up-front preflight (the `planVarsPush` cap pattern) so a
258
+ * push that would run past the cap aborts BEFORE any mutation. Without it the
259
+ * push creates webhooks one at a time until the server rejects one at the cap,
260
+ * leaving a partial push.
261
+ *
262
+ * **Only creates are capped.** The server grandfathers apps that already hold
263
+ * more webhooks than the cap: it refuses new creates and nothing else. A plain
264
+ * file count would break that — `config pull` writes one toml per webhook, so a
265
+ * grandfathered app pulls more files than the cap and could then never push
266
+ * again, its workflows, databases and vars included. So the check compares the
267
+ * local keys against the keys already on the server: a push that creates
268
+ * nothing new is always allowed, however far over the cap the app is, and a
269
+ * push that does create is rejected only when the resulting row count would
270
+ * exceed the cap.
271
+ *
272
+ * The remote key list this runs against comes from `GET
273
+ * /admin/api/apps/{appId}/webhooks`, which applies no default status filter, so
274
+ * archived rows are in `remoteKeys` exactly as they are in the server's count.
275
+ * The two therefore agree on what is already on the server.
276
+ *
277
+ * Still advisory rather than authoritative: this counts creates the way the
278
+ * *config dir* implies, while the apply loop decides create-vs-update from
279
+ * local sync state, so a checkout with missing sync state issues creates this
280
+ * check did not predict. Those creates name keys the server already holds, and
281
+ * the server does not cap a create that adds no row — it answers 409
282
+ * `WEBHOOK_KEY_EXISTS`, which `adoptByKeyOnCreate409` converges on. A push
283
+ * allowed here can still be rejected server-side (concurrent writes, a stale
284
+ * list); that rejection carries `WEBHOOK_LIMIT_REACHED` and names the limit.
285
+ *
286
+ * Planned prunes are deliberately NOT netted out of the count: `--prune` runs
287
+ * after the webhook create loop, so a push that only fits once its prunes land
288
+ * would still fail mid-apply. Prune first, then push the additions.
289
+ */
290
+ export declare function validateWebhookCapForPush(localKeys: string[], remoteKeys: string[]): string[];
291
+ /**
292
+ * Validate a parsed `vars.toml` table against the same key/value constraints
293
+ * the server enforces (key format, string type, non-empty, size cap) plus the
294
+ * aggregate per-app var-count cap. Returns a list of human-readable errors
295
+ * (empty when valid).
296
+ *
297
+ * Run in `config push`'s up-front preflight pass (issue #1423 review) so an
298
+ * invalid entry — or a file that would exceed the server's `MAX_VARS_PER_APP`
299
+ * cap — aborts BEFORE any mutation is applied. Validating only individual
300
+ * entries let a 101-entry file create/update many vars before the server
301
+ * rejected a later create at the cap, leaving a partial push (issue #1423
302
+ * review pass 3). Pure and side-effect-free so it's unit-testable without a
303
+ * live server.
304
+ *
305
+ * `onlyKeys` narrows the check to the keys a `config push --only var/<key>` will
306
+ * actually write (issue #2645). A single-object push must not be aborted by an
307
+ * entry it is not going to touch, so the per-entry checks run over the selected
308
+ * keys and the file-wide count cap — a property of the whole file, not of these
309
+ * entries — is left to the plan-level check the caller makes against the final
310
+ * server count.
311
+ */
312
+ export declare function validateVarsFile(parsedVars: Record<string, unknown>, options?: {
313
+ onlyKeys?: ReadonlySet<string>;
314
+ }): string[];
315
+ /**
316
+ * Plan the config-var writes a `config push` should make, detecting concurrent
317
+ * remote edits before overwriting them (issue #1423 review).
318
+ *
319
+ * This is the client-side half of a two-layer concurrency guard. The vars
320
+ * PUT/DELETE endpoints now honor an `expectedModifiedAt` precondition (issue
321
+ * #1423 review r-2 P1) and raise a `ConflictError` when the server row changed
322
+ * since the caller's snapshot — the atomic, server-side check every other
323
+ * synced entity relies on. This function is the fast fail-before-mutate layer:
324
+ * it compares the recorded baseline against the current remote snapshot and, for
325
+ * a var whose remote value has drifted from what we last synced, reports a
326
+ * conflict (fed into the same `conflicts[]` accumulator the other entities use)
327
+ * before any write is attempted. A var tracked at the baseline but absent from
328
+ * the remote snapshot (deleted remotely while edited locally) is remote drift
329
+ * too, and also reported as a conflict rather than silently recreated.
330
+ * Conversely, a var removed locally that is already absent remotely is NOT
331
+ * planned for deletion — its desired (absent) state is already met, so a DELETE
332
+ * would 404 and fail the push. The same drift check guards deletions: a var
333
+ * removed locally whose remote value no longer matches the baseline was edited
334
+ * remotely since the last sync, and deleting it would silently discard that
335
+ * edit — reported as a conflict instead. `force` skips the conflict guard,
336
+ * matching `config push --force` for every other entity.
337
+ *
338
+ * Pure and side-effect-free (the caller owns the API calls and sync-state
339
+ * updates) so the guard is unit-testable without a live server. Assumes
340
+ * `parsedVars` has already passed `validateVarsFile` (values are strings). The
341
+ * caller normally fails the push closed when the remote snapshot can't be
342
+ * fetched, but under `--force` it falls back to an empty snapshot and sets
343
+ * `snapshotUnavailable` — which keeps removed-key deletions in the plan (they'd
344
+ * otherwise be mistaken for already-deleted and dropped) and leans on the apply
345
+ * loop's idempotent 404 handling (issue #1423 review r-2 P2).
346
+ */
347
+ export declare function planVarsPush(parsedVars: Record<string, string>, baseline: Record<string, VarEntity> | undefined, remoteVars: Array<{
348
+ key: string;
349
+ value?: string;
350
+ updatedAt?: string;
351
+ }>, options?: {
352
+ force?: boolean;
353
+ snapshotUnavailable?: boolean;
354
+ }): {
355
+ upserts: Array<{
356
+ action: "create" | "update";
357
+ key: string;
358
+ value: string;
359
+ valueHash: string;
360
+ }>;
361
+ deletions: string[];
362
+ conflicts: Array<{
363
+ key: string;
364
+ serverModifiedAt: string;
365
+ localModifiedAt: string;
366
+ }>;
367
+ skippedCount: number;
368
+ };
369
+ /**
370
+ * Compute the number of config vars the server would hold after a push plan
371
+ * is applied (issue #1423 review pass 5). Validating only the local file's
372
+ * entry count misses remote-only vars: 95 local entries plus 10 vars that
373
+ * exist only on the server passes a local-count check, and the creates then
374
+ * exceed the server's cap mid-push, after earlier writes were already
375
+ * applied. Counted from the fetched remote snapshot: planned deletions remove
376
+ * keys that exist remotely (the plan already omits deletes for absent keys),
377
+ * and only upserts whose key is NOT already on the server add to the total.
378
+ * Conflicted keys are not mutated, so they contribute nothing beyond their
379
+ * current remote presence. Pure and side-effect-free so it's unit-testable
380
+ * without a live server.
381
+ */
382
+ export declare function countVarsAfterPush(remoteVars: Array<{
383
+ key: string;
384
+ }>, plan: {
385
+ upserts: Array<{
386
+ key: string;
387
+ }>;
388
+ deletions: string[];
389
+ }): number;
390
+ /**
391
+ * Compute the `config diff` rows for config vars (issue #1423 review). Before
392
+ * this, `config diff` ignored vars entirely, so an add/remove/value-drift between
393
+ * the local `vars.toml` and the server read as no difference. Value-aware: a
394
+ * var present on both sides whose value differs is reported as `modified`
395
+ * (framed like the other content-aware entities — `config pull` would rewrite the
396
+ * local value). Pure and side-effect-free so it's unit-testable without a live
397
+ * server.
398
+ */
399
+ export declare function diffVars(localVars: Map<string, string>, remoteVars: Map<string, string>): Array<{
400
+ type: "var";
401
+ key: string;
402
+ status: string;
403
+ hint?: string;
404
+ }>;
405
+ /**
406
+ * Which of a workflow's configs `config pull` writes as sidecar files
407
+ * (issue #2645, spec §Contracts/Named configs).
408
+ *
409
+ * Every config EXCEPT the active one. The active config's body is already the
410
+ * `steps` at the top of `workflows/<key>.toml` — that is where authors edit it
411
+ * and where `applyWorkflowBody` writes it — so emitting it again as a sidecar
412
+ * would put the same body in two files and create a mismatch to adjudicate on
413
+ * the next push. Two declarations of one piece of state is the shape of problem
414
+ * this whole issue removes; it would be perverse to introduce one here.
415
+ *
416
+ * A config whose steps were not fetched is SKIPPED rather than written empty.
417
+ * A sidecar is a claim about the server, and `steps = []` is a claim that would
418
+ * tell the next push to blank a live configuration.
419
+ *
420
+ * Pure, so the selection is unit-testable without a live server.
421
+ */
422
+ export declare function workflowConfigSidecarsToWrite(workflow: any, configs: any[]): Array<{
423
+ name: string;
424
+ content: string;
425
+ }>;
426
+ /**
427
+ * The names a workflow's configs occupy on the server, whatever their body.
428
+ *
429
+ * `workflowConfigSidecarsToWrite` deliberately skips a config whose steps were
430
+ * not fetched, so it cannot answer "which sidecar files are stale". This can:
431
+ * a local file whose name is absent HERE names a config the server no longer
432
+ * has, which is the only safe basis for deleting it (issue #2645, review
433
+ * follow-up).
434
+ */
435
+ export declare function workflowConfigNamesOnServer(workflow: any, configs: any[]): Set<string>;
436
+ /**
437
+ * Delete the sidecars naming a config the server no longer has (#2645).
438
+ *
439
+ * Pull is what makes the repo describe the server, and a sidecar left behind
440
+ * after its config was archived — or after that config became the active one,
441
+ * whose body is the workflow file's `steps` — is a file that would RE-CREATE it
442
+ * on the next push. The repo would then be silently undoing the archive.
443
+ *
444
+ * Returns the file names removed, so pull can say what it did.
445
+ */
446
+ export declare function removeStaleWorkflowConfigSidecars(configDir: string, key: string, namesOnServer: Set<string>): string[];
447
+ /**
448
+ * The content hash `config push` skips a workflow on — sidecars included.
449
+ *
450
+ * The stored hash used to cover `workflows/<key>.toml` alone, so editing or
451
+ * adding a named config sidecar without touching the workflow file left the
452
+ * hash unchanged: push reported success and made no call at all, which is
453
+ * worse than failing (issue #2645, review follow-up). The named configs are
454
+ * part of what push applies, so they are part of what push compares.
455
+ *
456
+ * A workflow with no sidecars hashes exactly as before, so the common case
457
+ * does not re-push once on upgrade. An unreadable sidecar hashes to a distinct
458
+ * value rather than throwing: a hash is a comparison, and the parse error
459
+ * belongs to the apply path, which raises it with the file name attached.
460
+ */
461
+ export declare function workflowHashInput(configDir: string, key: string, parsedWorkflowToml: any): any;
462
+ /** See `workflowHashInput`. */
463
+ export declare function computeWorkflowContentHash(configDir: string, key: string, parsedWorkflowToml: any): string;
464
+ /**
465
+ * Canonical hash of a named config sidecar, for `config diff` (#2645).
466
+ *
467
+ * Hashes the parsed body rather than the bytes so comment and key-order
468
+ * differences do not read as drift — the same semantic comparison `config diff`
469
+ * makes everywhere else (spec criterion 3).
470
+ */
471
+ export declare function hashWorkflowConfigSidecar(parsed: any): string;
472
+ /**
473
+ * Does the file's `[workflow].activeConfigName` name a configuration that the
474
+ * push could actually activate (#2743, review follow-up)?
475
+ *
476
+ * The diff normalizer equates an omitted `activeConfigName` with `default` —
477
+ * the name the server itself gives the config it creates — so a file that
478
+ * spells `default` out hashes equal to the omitted form. For a workflow with no
479
+ * configuration at all (a legacy row the GET's auto-migration could not heal),
480
+ * the remote side serializes an EMPTY active config name and normalizes to
481
+ * `default` too, so the pair hashes equal while `config push` would stop on
482
+ * `activeConfigName "default" names no configuration`. Reporting Synced for a
483
+ * file whose next push fails is the one drift a change gate must not skip.
484
+ *
485
+ * A name the repo carries as a sidecar resolves: push creates that config and
486
+ * activates it (and the sidecar comparison already reports the difference).
487
+ * Read-only and non-throwing, like every other diff predicate.
488
+ */
489
+ export declare function workflowActiveConfigNameUnresolvable(configDir: string, key: string, localParsed: any, configs: any[]): boolean;
490
+ /**
491
+ * The `config diff` note for a workflow whose file names no configuration while
492
+ * the server runs one other than the `default` it creates itself (#2743, review
493
+ * follow-up).
494
+ *
495
+ * Such a file is not drift push can act on: activation is preserved, not
496
+ * converged, so the row is Synced — the alternative, reporting Modified,
497
+ * repeats forever because no push changes it. But the repo does not describe
498
+ * which configuration is live, and only `config pull` records it, so the fact is
499
+ * carried as a hint on the Synced row — the same way an operational disable is
500
+ * (`outOfServiceHint`). The server-created `default` is the shape a
501
+ * hand-authored file already means, so it is silent.
502
+ *
503
+ * Pure and non-throwing, like every other diff predicate. Returns the existing
504
+ * hint untouched when there is nothing to add.
505
+ */
506
+ export declare function unmanagedActiveConfigHint(localParsed: any, workflow: any, configs: any[], existingHint?: string): string | undefined;
507
+ /**
508
+ * Drop the archived rows from a pull listing (#2803).
509
+ *
510
+ * A tombstone is retired, not configuration. Exporting one wrote a file the
511
+ * repo then claimed to describe — and, once `status` left the TOML surface, a
512
+ * file whose very next push would fail on the `status` line it carried.
513
+ * `config push --prune` is the path that reclaims an archived row; pull's job
514
+ * is to describe what is configured.
515
+ */
516
+ export declare function skipArchivedForPull<T extends {
517
+ status?: unknown;
518
+ }>(items: T[]): T[];
519
+ /**
520
+ * The sync-state entries a pull keeps for the archived rows it did NOT export
521
+ * (#2803).
522
+ *
523
+ * Pull removes a tombstone's file, and prune candidates come only from prior
524
+ * sync state — so dropping the state entry with the file would strand the row:
525
+ * no file to delete, no managed key to prune, and (for webhooks and cron
526
+ * triggers) a cap slot and a reserved key nothing could ever reclaim, which is
527
+ * the exact failure #2232 fixed. So the pull records the tombstone instead: no
528
+ * file, but a managed entry carrying the archived row's CURRENT `modifiedAt`,
529
+ * so the next opt-in, confirmed `config push --prune` point-reads it, finds it
530
+ * unchanged, and hard-deletes it — the reclamation path the model promises.
531
+ *
532
+ * Only keys the config tree already managed are recorded. A row archived
533
+ * server-side that this repo never described is not this repo's to delete.
534
+ *
535
+ * Pure, so it is unit-testable without a live server.
536
+ */
537
+ export declare function archivedTombstoneEntries(items: any[], select: {
538
+ keyOf: (item: any) => string;
539
+ idOf: (item: any) => string;
540
+ }, managedKeys: Iterable<string>): Record<string, {
541
+ id: string;
542
+ modifiedAt: string;
543
+ }>;
544
+ /**
545
+ * Fold one type's tombstone entries into the sync state this pull is building
546
+ * (#2803, corrected by #2887).
547
+ *
548
+ * Two rules, and the second one is the fix. A tombstone is recorded only for a
549
+ * key this pull did NOT export (an exported key is a live row and its own
550
+ * record) and only when the selection covers it. And a recorded key is marked
551
+ * as MATCHED: archived rows are dropped before `selectPull` runs, so the
552
+ * selector that named one never reached the set that decides whether the pull
553
+ * found what it was asked for. `config pull --only workflow/<key>` against an
554
+ * archived managed row therefore removed the file, saved the tombstone, and
555
+ * then exited 1 saying "the server has no workflow/<key>" — a failure report
556
+ * about a mutation that had already succeeded, which is the worst thing an
557
+ * automation can be told. The fix is generic, so integrations and webhooks
558
+ * (which had the bug first) get it too.
559
+ *
560
+ * Mutates `built` and `matchedSelectors` and returns the keys it recorded, so
561
+ * the caller can report each one. Pure otherwise, and unit-testable.
562
+ */
563
+ export declare function recordArchivedTombstones(built: Record<string, {
564
+ id: string;
565
+ modifiedAt: string;
566
+ }>, tombstones: Record<string, {
567
+ id: string;
568
+ modifiedAt: string;
569
+ }>, context: {
570
+ label: string;
571
+ selection: SyncSelection | null;
572
+ matchedSelectors: Set<string>;
573
+ }): string[];
574
+ /**
575
+ * The `config diff` annotation for an object an operator took out of service
576
+ * (issue #2645, criterion 9; #2803).
577
+ *
578
+ * Availability is deliberately not a TOML key, so an object that is out of
579
+ * service is NOT drift — the committed configuration still describes it
580
+ * exactly. Saying so out loud is the point: the reason the control exists is
581
+ * that an out-of-band write used to make `config diff` report a difference
582
+ * nobody intentionally created (#1976). Reporting it as a hint on an
583
+ * otherwise-`Synced` row is what keeps that promise visible instead of merely
584
+ * true.
585
+ *
586
+ * Reads either spelling: the canonical `status: "inactive"` for a type that has
587
+ * cut over to #2803's single control, and the superseded
588
+ * `operationallyDisabled` boolean for one that has not yet.
589
+ *
590
+ * An ARCHIVED row gets its own annotation. `config pull` no longer exports
591
+ * tombstones, so without one an archived webhook or integration whose file is
592
+ * still in the tree read as plain `Synced` — the one status where "the server
593
+ * has exactly what the file says" is most misleading.
594
+ *
595
+ * When the row already carries a hint — a real content difference — the toggle
596
+ * is APPENDED rather than substituted: the drift is the part that needs acting
597
+ * on, and dropping it to announce the pause would hide the more urgent fact.
598
+ *
599
+ * Pure, so it is unit-testable without a live server.
600
+ */
601
+ export declare function outOfServiceHint(remote: any, existingHint?: string): string | undefined;
602
+ /**
603
+ * Serialize an integration DETAIL response to its `integrations/<key>.toml`.
604
+ *
605
+ * #2631: `accessRule` is the CEL expression gating who may call the
606
+ * integration. It comes back on the detail only (not the list summary), and it
607
+ * is written to TOML so `config pull` → `config push` round-trips the rule instead
608
+ * of clearing it. A null/empty rule has no TOML representation and is simply
609
+ * omitted; the push path sends `null` for an omitted rule, which matches the
610
+ * server state it came from.
611
+ */
612
+ export declare function serializeIntegration(integration: any, logger?: (message: string) => void): string;
613
+ /**
614
+ * Build the `config push` body for one integration TOML.
615
+ *
616
+ * `accessRule` follows the #1567 TOML-owned rule: send value-or-`null` so
617
+ * removing the line from the file clears the rule server-side rather than
618
+ * leaving a stale one live. There is deliberately NO local "rule is required"
619
+ * check here — a remote integration missing from local sync state is pushed as
620
+ * a create, expected to 409, and adopted by key (#1006); a local check would
621
+ * strand that path. The server decides (#2631).
622
+ */
623
+ export declare function buildIntegrationPushPayload(key: string, tomlData: any, { mode }?: {
624
+ mode?: PushMode;
625
+ }): Record<string, any>;
626
+ export declare function webhookConfigToTomlTable(scheme: string, config: any): Record<string, any> | undefined;
627
+ export declare function tomlTableToWebhookConfig(scheme: string, table: any): Record<string, any> | undefined;
628
+ export declare function serializeWebhook(webhook: any, logger?: (message: string) => void): string;
629
+ /**
630
+ * Build the `config push` body for one webhook TOML.
631
+ *
632
+ * The `[webhook]`-derived half comes from the vendored definition; the four
633
+ * structural tables the file also carries (`[allowedIps]`, `[inputMapping]`,
634
+ * `[metadata]`, `[verification.<scheme>]`) keep their own conversion, which is
635
+ * why the definition declares them `structural` rather than as fields.
636
+ */
637
+ export declare function buildWebhookPushPayload(key: string, tomlData: any, { mode }?: {
638
+ mode?: PushMode;
639
+ }): Record<string, any>;
640
+ export declare function serializeCronTrigger(trigger: any, logger?: (message: string) => void): string;
641
+ /**
642
+ * Build the `config push` body for one cron-trigger TOML.
643
+ *
644
+ * `state` is absent from the create body because the definition says the create
645
+ * handler assigns it (`writableOn: ["update"]`) — the CLI used to send it and
646
+ * the server used to drop it silently.
647
+ */
648
+ export declare function buildCronTriggerPushPayload(key: string, tomlData: any, { mode }?: {
649
+ mode?: PushMode;
650
+ }): Record<string, any>;
651
+ export declare function serializeBlobBucket(bucket: any, logger?: (message: string) => void): string;
652
+ /**
653
+ * Build the PATCH payload for a blob-bucket update from local TOML.
654
+ *
655
+ * EXACT extraction of the inline builder shared by the existing-update and
656
+ * 409-adopt branches — do NOT "clean up" the truthiness checks on the access
657
+ * model. The server treats `preset` and `accessPolicy` as mutually exclusive,
658
+ * only clears `ruleSetId` when a preset/accessPolicy is also present, and
659
+ * refuses to leave a bucket with no access model or a blank name — so those
660
+ * fields are not clearable. `bucketKey` and `ttlTier` are immutable and never
661
+ * sent. Both call sites must go through this helper so their field sets never
662
+ * drift.
663
+ */
664
+ export declare function buildBlobBucketUpdatePayload(bucket: any): any;
665
+ /**
666
+ * The status a freshly CREATED prompt config still owes the file, or null
667
+ * (#3201).
668
+ *
669
+ * `status` is update-only — `POST /prompts` and `POST /prompts/:id/configs`
670
+ * both assign `active` whatever the body says — so an authored
671
+ * `status = "archived"` was silently dropped on every create path: a pulled
672
+ * tree pushed from a clean clone into a fresh environment brought every
673
+ * retired config back into service. Read through the push payload builder, so
674
+ * a file that omits the key asks for the default and needs no request.
675
+ */
676
+ export declare function createdConfigStatusPatch(tomlConfig: any): {
677
+ status: string;
678
+ } | null;
679
+ /**
680
+ * Which `[[configs]]` entry a prompt file marks as live (issue #2645).
681
+ *
682
+ * Exactly one entry may carry `active = true`. Two is an error rather than a
683
+ * first-wins rule: making the file's meaning depend on entry order is the kind
684
+ * of implicit behavior this issue exists to remove, and silently picking one
685
+ * would let the repo claim something the server does not do.
686
+ *
687
+ * `isActive` is accepted as a legacy spelling — the create leg read it before
688
+ * pull ever wrote a marker, so a hand-authored file carrying it keeps working.
689
+ */
690
+ export declare function promptActiveConfigName(configs: any[]): string | undefined;
691
+ /**
692
+ * The push payload for one `[[configs]]` entry of a prompt (issue #2645).
693
+ *
694
+ * One builder for both directions so create and update cannot drift: a field
695
+ * the file owns has to reach the server whether the config is new or already
696
+ * there, which is what "removing a flag removes no capability" means in
697
+ * practice.
698
+ *
699
+ * `description` is sent as `?? null` on update and omitted on create (#1567):
700
+ * dropping the key from a `[[configs]]` block must CLEAR it on an existing
701
+ * config, and the PATCH nulls on an explicit `null` while preserving on omit.
702
+ * `status` is the opposite — omitted means "unchanged", because the server
703
+ * defaults a new config to active and an existing one keeps what it has.
704
+ */
705
+ export declare function promptConfigPayload(tomlConfig: any, options?: {
706
+ create?: boolean;
707
+ }): Record<string, any>;
708
+ /**
709
+ * Build the POST body for a blob-bucket create from local TOML.
710
+ *
711
+ * `ttlTier` is here and absent from the update body because the definition says
712
+ * so (`writableOn: ["create"]`) — the retention tier is fixed at creation.
713
+ */
714
+ export declare function buildBlobBucketCreatePayload(key: string, bucket: any): Record<string, any>;
715
+ export declare function serializePrompt(prompt: any, logger?: (message: string) => void): string;
716
+ /**
717
+ * The `config push` warning for a resource pushed with no `accessRule` (#2652).
718
+ *
719
+ * Returns `null` when a rule is declared — and for a `runAs = "system"`
720
+ * workflow, which rejects any rule and cannot be started by a member at all.
721
+ * Otherwise it names the resource, the consequence, and the one-line restore,
722
+ * because after the #2652 flip a ruleless prompt or caller workflow denies
723
+ * every non-admin caller from the deploy onward.
724
+ *
725
+ * Both kinds now push `accessRule` as value-or-`null` — the prompt since #1567,
726
+ * the workflow since #2743's always-send conversion — so for either one an
727
+ * omitted key really does leave the resource ruleless on the server. The
728
+ * workflow wording says so explicitly, because it used to say the opposite
729
+ * (the update path preserved a stored rule) and an operator who learned that
730
+ * caveat needs to see it retracted.
731
+ */
732
+ export declare function missingAccessRuleWarning(opts: {
733
+ kind: "prompt" | "workflow";
734
+ key: string;
735
+ accessRule: unknown;
736
+ runAs?: unknown;
737
+ }): string | null;
738
+ /**
739
+ * Build the `config push` body for one prompt TOML's `[prompt]` table.
740
+ *
741
+ * The prompt's own fields only — its `[[configs]]` entries are separate
742
+ * requests, built by `buildPromptConfigPayload`.
743
+ */
744
+ export declare function buildPromptPushPayload(key: string, tomlData: any, { mode }?: {
745
+ mode?: PushMode;
746
+ }): Record<string, any>;
747
+ /**
748
+ * Build the POST body for a prompt create.
749
+ *
750
+ * `POST /prompts` is the one endpoint that writes BOTH models: the prompt, and
751
+ * the "default" `AppPromptConfig` it seeds from the same body. So this body is
752
+ * the `[prompt]` table's create half plus the FIRST `[[configs]]` entry's LLM
753
+ * settings — minus the three keys the prompt half owns or the handler assigns
754
+ * itself:
755
+ *
756
+ * - `configName` — the seeded config is always named "default".
757
+ * - `description` — the body's slot belongs to the PROMPT, and the handler
758
+ * hard-codes the seeded config's to "Default configuration". An authored
759
+ * `[[configs]]` description therefore has nowhere to go in this body, so
760
+ * push applies it right after the create rather than losing it.
761
+ * - `outputSchema` — same shape: the body's slot is the prompt's, so the
762
+ * seeded config's own value is applied by the follow-up PATCH
763
+ * (`seededOnlyPromptConfigFields`) instead of being dropped.
764
+ *
765
+ * Every other config field rides along from the definition, so a new LLM
766
+ * setting reaches a brand-new prompt without an edit at this call site.
767
+ */
768
+ export declare function buildPromptCreatePayload(key: string, tomlData: any): Record<string, any>;
769
+ /**
770
+ * The PATCH body reconciling the seeded config with the first `[[configs]]`
771
+ * entry, or `null` when the entry needs none of it.
772
+ *
773
+ * #2972 — the entry's NAME belongs in this patch too. The create handler names
774
+ * the seeded config "default" whatever the entry is called, so a file whose
775
+ * first entry is named anything else got that entry's settings stored under
776
+ * "default": a later entry actually named "default" then collided with it and
777
+ * the push died half-applied, and a file naming no entry "default" quietly
778
+ * landed a config it never mentions while its first entry never existed. Both
779
+ * end here, because the rename runs before any further config is created.
780
+ *
781
+ * `seededConfigName` is what the server called it — read from the create
782
+ * response rather than assumed, so a handler that one day honors the name
783
+ * produces no needless PATCH.
784
+ *
785
+ * #3201 — the entry's `status` is deferred the OTHER way and is deliberately
786
+ * not here. The create points the prompt's `activeConfigId` at this seeded
787
+ * config, so retiring it in this early patch would archive the config the
788
+ * prompt is serving from and leave it there until the marked entry is created
789
+ * and activated — a window a failed create or activation freezes. It is
790
+ * applied after activation instead (`createdConfigStatusPatch`).
791
+ */
792
+ export declare function seededOnlyPromptConfigFields(firstConfig: any, seededConfigName?: string): Record<string, any> | null;
793
+ /**
794
+ * Build the create/update body for ONE `[[configs]]` entry.
795
+ *
796
+ * `create` carries the provider/model defaults a brand-new config needs; on
797
+ * update those defaults must NOT be applied, or editing an unrelated key would
798
+ * silently re-point a config at the default model.
799
+ */
800
+ export declare function buildPromptConfigPayload(tomlConfig: any, { mode }?: {
801
+ mode?: PushMode;
802
+ }): Record<string, any>;
803
+ /**
804
+ * Rewrite a workflow TOML file's string-form `inputSchema` / `outputSchema` to
805
+ * native object form for `config migrate-toml` (issue #1446).
806
+ *
807
+ * Purely local, no server fetch. Parses the raw source WITHOUT expanding
808
+ * fragment `include`s (so those directives survive the rewrite), swaps a
809
+ * JSON-string schema for its parsed object when it round-trips faithfully
810
+ * (`canEmitNative === null`), and re-serializes via `stringifyConfigToml`.
811
+ *
812
+ * Only rewrites when a field is actually converted: a file whose schemas are
813
+ * already native, absent, or un-representable (a `null` default) is returned
814
+ * with `changed: false` and its original bytes untouched — so migrate is
815
+ * idempotent and never reports a spurious migration or reformats a file it
816
+ * didn't need to touch.
817
+ */
818
+ export declare function rewriteWorkflowSchemasToNative(rawToml: string, workflowKey?: string, log?: (message: string) => void): {
819
+ content: string;
820
+ changed: boolean;
821
+ };
822
+ /**
823
+ * #2644 — `config push` rejects any key the vendored definition does not
824
+ * recognize, so a mistyped or newer-server key fails locally instead of being
825
+ * silently dropped on the way to the server (design gate, 2026-08-12). Returns
826
+ * the error message, or null when every key is known.
827
+ *
828
+ * Works for any migrated type, single or repeated: a repeated table's entries
829
+ * are checked one by one and the unknown keys reported together, so a typo in
830
+ * the third `[[configs]]` block is as loud as one in the first.
831
+ */
832
+ export declare function configUnknownKeyError(filePath: string, tomlData: any, table: ConfigTable): string | null;
833
+ /**
834
+ * #2644 — the document-level half of the same rejection: a top-level table the
835
+ * object's definition does not declare, or a field table written with the wrong
836
+ * array markers. Returns one message per problem, or `[]` when the file's shape
837
+ * is recognized.
838
+ *
839
+ * Unrecognized keys INSIDE a table were already rejected; a mistyped table
840
+ * HEADER was not, and it is the more destructive of the two — `[integraton]`
841
+ * leaves `[integration]` absent, so the push builder reads an empty table and
842
+ * sends `description: null` / `accessRule: null`, CLEARING them server-side.
843
+ * The design gate settled the posture (2026-08-12): push rejects everything
844
+ * unknown, tables included.
845
+ */
846
+ export declare function configDocumentShapeErrors(filePath: string, tomlData: any, surface: ConfigObjectSurface): string[];
847
+ /**
848
+ * Which directories `config push` checks for unrecognized keys, and against which
849
+ * tables.
850
+ *
851
+ * Derived from the registry crossed with `SYNC_RESOURCE_TYPES`, so a type
852
+ * gains the check by being defined — nobody has to remember to list it here.
853
+ *
854
+ * Workflows carry an EMPTY `tables` list: `validateWorkflowToml` already
855
+ * reports their `[workflow]` keys alongside the #685 misnest check, in one
856
+ * message per file, but nothing checked a workflow file's top-level tables —
857
+ * so the surface is listed here for the document-level check and skipped for
858
+ * the per-table one.
859
+ */
860
+ export declare function unknownKeyPreflightTargets(): Array<{
861
+ dir: string;
862
+ surface: ConfigObjectSurface;
863
+ tables: ConfigTable[];
864
+ }>;
865
+ /** One config file the TOML preflight rejects, and the row it belongs to. */
866
+ export interface ConfigFileValidationError {
867
+ /** The `config diff` row type, e.g. `prompt`. */
868
+ type: string;
869
+ /** The row key the local file pairs on. */
870
+ key: string;
871
+ filePath: string;
872
+ /** The messages, verbatim — the same text `config push` aborts with. */
873
+ messages: string[];
874
+ }
875
+ /**
876
+ * Every per-entity config file whose TOML `config push` would refuse (#2880
877
+ * criterion 1, symmetry).
878
+ *
879
+ * One collector, two commands. Push aborts on the messages before issuing a
880
+ * request; `config diff` renders them as their own rows, because the failure
881
+ * this closes is a file diff calls Synced and push then rejects — the state the
882
+ * issue's reporter hit after #2803 retired `[prompt].status`, where a local key
883
+ * with no server counterpart is invisible to a comparison and fatal to a push.
884
+ *
885
+ * Three checks, all definition-driven: the document's table shape, keys no
886
+ * definition declares (retired keys among them), and — since #2880 — values
887
+ * whose spelling is not the declared type. A file that does not parse is left
888
+ * to the per-type loop that reads it, which names the parse error itself.
889
+ */
890
+ export declare function collectConfigFileValidationErrors(configDir: string): ConfigFileValidationError[];
891
+ /**
892
+ * Every `functions/<key>.toml` whose file-decidable errors `config push` would
893
+ * refuse (#3320).
894
+ *
895
+ * A second collector rather than a branch inside
896
+ * `collectConfigFileValidationErrors`, and deliberately so: push calls that one
897
+ * UNBOUNDED over the whole tree, which is right for the checks it runs (a
898
+ * mistyped key anywhere means the tree is not pushable) and wrong for these — a
899
+ * `config push --only webhook/<key>` must not be blocked by a broken function
900
+ * the operator did not select (#2645). The push path therefore runs these
901
+ * inside its own `selectFiles`-bounded function loop, and `config diff`, which
902
+ * compares the whole tree by construction, runs them from here.
903
+ *
904
+ * A file that does not parse is skipped: the per-type loop that reads it names
905
+ * the parse error itself, and reporting it twice helps nobody.
906
+ */
907
+ export declare function collectFunctionTomlValidationErrors(configDir: string): ConfigFileValidationError[];
908
+ /**
909
+ * The collectors' rows, one per diff row (#2880 symmetry, #3320).
910
+ *
911
+ * Two collectors reach the same function file — the definition-driven one
912
+ * every per-entity type runs, and the function preflight — and a file can
913
+ * break both at once (a mistyped `entrty`, which then leaves no `entry`).
914
+ * Push reports every message from both, so an overlay that let one row's
915
+ * messages REPLACE the other's would drop a diagnostic push prints, which is
916
+ * exactly the asymmetry #2880 forbids. Merging by row keeps the two commands
917
+ * reading identically.
918
+ */
919
+ export declare function mergeConfigFileValidationErrors(collected: ConfigFileValidationError[]): ConfigFileValidationError[];
920
+ /**
921
+ * Every `<key>.tests/*.toml` sidecar under a config directory.
922
+ *
923
+ * Test cases are a registered surface (#2644 phase 3) but they are not a
924
+ * `SYNC_RESOURCE_TYPES` directory — they live beside the block they test, in
925
+ * `prompts/<key>.tests/`, `workflows/<key>.tests/`, `transforms/<name>.tests/`
926
+ * and (since #2769) `integrations/<key>.tests/` — so
927
+ * `unknownKeyPreflightTargets`, which crosses
928
+ * the registry with that table, never reaches them. Listing them here gives the
929
+ * sidecars the same unrecognized-key rejection every per-entity file has
930
+ * (criterion 6): a mistyped `[test]` key fails the push instead of being
931
+ * dropped on the way to the server.
932
+ */
933
+ export declare function testCaseTomlFiles(configDir: string): string[];
934
+ /** The `[workflow]` case of `configUnknownKeyError` (#2644 phase 1). */
935
+ export declare function workflowUnknownKeyError(filePath: string, tomlData: any): string | null;
936
+ /**
937
+ * Will `config push` reach this workflow key through its UPDATE path?
938
+ *
939
+ * The push takes that path when sync state already holds an id for the key —
940
+ * the `existingId` the apply loop reads. Everything else is a create (including
941
+ * a create that 409s and adopts by key, which cannot be known before the server
942
+ * answers). Exported so the front-loaded preflight can decide, before the first
943
+ * mutating call, which files the update-only `name` pre-check applies to.
944
+ */
945
+ export declare function isWorkflowUpdateTarget(key: string, syncState: {
946
+ entities?: {
947
+ workflows?: Record<string, any>;
948
+ };
949
+ } | null): boolean;
950
+ /**
951
+ * Will the apply loop actually SEND this workflow, or skip it as unchanged?
952
+ *
953
+ * Mirrors the loop's skip condition (`!force && existingId &&
954
+ * !shouldPushExpandedFile(...)`): a create always sends, `--force` always
955
+ * sends, and an update sends only when the expanded content (workflow file plus
956
+ * its config sidecars) differs from the hash sync state recorded for it.
957
+ *
958
+ * Exported so the front-loaded preflight can scope the update-only `name`
959
+ * pre-check to the files this push will really update. Without that scoping a
960
+ * workflow created through the manifest-key `name` fallback — an authoring
961
+ * shape the create path deliberately supports — would push once and then fail
962
+ * every later push, including a no-change one and its `--dry-run`, on a file
963
+ * the push was never going to send (#2743, review follow-up).
964
+ */
965
+ export declare function workflowPushSendsUpdate(configDir: string, key: string, parsedWorkflowToml: any, syncState: {
966
+ entities?: {
967
+ workflows?: Record<string, any>;
968
+ };
969
+ } | null, force?: boolean): boolean;
970
+ /**
971
+ * The UPDATE path's `name` pre-check (#2743).
972
+ *
973
+ * Always-send means the update payload carries `name: value ?? null`, and the
974
+ * server rejects a `null` name — it is required and cannot be cleared. Caught
975
+ * here, the operator gets an error naming their file before any PATCH is sent;
976
+ * caught server-side, they get a bare 400 part-way through a push. Pull always
977
+ * writes `name`, so a file reaching the update path without one is a deliberate
978
+ * authoring state, not a round-trip artifact.
979
+ *
980
+ * Called from the front-loaded preflight (#976) for every workflow this push
981
+ * will SEND through the update path (`workflowPushSendsUpdate` — an unchanged
982
+ * file the apply loop skips is not one) — so it aborts before the first
983
+ * mutating call and reports under `--dry-run` too — and again inside the update
984
+ * closure as defense-in-depth, which is also where the adopt-by-key path (a
985
+ * create the server 409s, unknowable up front) meets it.
986
+ *
987
+ * The CREATE path deliberately does NOT call this: it falls back to the
988
+ * manifest key (`name: workflow.name || key`), which is the accepted way to
989
+ * author a new workflow whose display name is its key.
990
+ *
991
+ * Returns `null` when the name is present.
992
+ */
993
+ export declare function workflowUpdateNameError(filePath: string, workflow: any): string | null;
994
+ export declare function serializeWorkflow(workflow: any, draft: any, configs: any[], logger?: (message: string) => void): string;
995
+ export declare function serializeDatabaseType(typeConfig: any, operations: any[], ruleSetIdToName: Map<string, string>, options?: {
996
+ /** Per-op form hints derived from the existing file. */
997
+ hints?: OperationFormHints;
998
+ /** Default form for ops with no hint. New files → "native". */
999
+ defaultForm?: FieldForm;
1000
+ /** Sink for human-readable fallback messages (logged via `info`). */
1001
+ logger?: (message: string) => void;
1002
+ /** Server subscription rows to emit as `[[subscriptions]]` (issue #803). */
1003
+ subscriptions?: any[];
1004
+ }): string;
1005
+ /**
1006
+ * Flatten an email-template detail response onto its field surface.
1007
+ *
1008
+ * The API returns `{ emailType, hasOverride, override: {…}, default: {…} }` —
1009
+ * only the `override` half is the field surface (the `default` half is what the
1010
+ * platform ships), which is why both envelope keys are declared
1011
+ * `responseOnlyKeys` in the definition.
1012
+ */
1013
+ export declare function emailTemplateOverrideRecord(template: any): Record<string, any>;
1014
+ export declare function serializeEmailTemplate(template: any, logger?: (message: string) => void): string;
1015
+ /**
1016
+ * Build the upsert body for one email-template TOML.
1017
+ *
1018
+ * `emailType` is deliberately absent: it identifies WHICH built-in template the
1019
+ * override replaces and travels in the URL path, which is what the definition's
1020
+ * `notExposed`/`tomlOnlyKeys` pair records.
1021
+ */
1022
+ export declare function buildEmailTemplatePushPayload(tomlData: any): {
1023
+ subject: string;
1024
+ htmlBody: string;
1025
+ textBody: string;
1026
+ };
1027
+ export declare function serializeRuleSet(ruleSet: any, logger?: (message: string) => void): string;
1028
+ /**
1029
+ * The create/update body for one rule-set TOML (#2644).
1030
+ *
1031
+ * The field half comes from the definition — which is what keeps
1032
+ * `resourceType` off the update body, since the update handler never reads it —
1033
+ * and the structural `[rules]` tree is attached beside it.
1034
+ *
1035
+ * `name` is trimmed here because the server trims it before storing
1036
+ * (`rule-sets-controller.ts`, create and update). This body is also what the
1037
+ * comparator projects the local side through (#2731 B1), so leaving the
1038
+ * untrimmed spelling in would compare a name the server will never hold: the
1039
+ * push would "succeed", change nothing, and be planned again on every run.
1040
+ */
1041
+ export declare function buildRuleSetPushPayload(tomlData: any, mode: PushMode): Record<string, any>;
1042
+ export declare function serializeGroupTypeConfig(config: any, ruleSetIdToName: Map<string, string>, logger?: (message: string) => void): string;
1043
+ /**
1044
+ * The create/update body for one group-type config (#2644).
1045
+ *
1046
+ * Takes the entity `parseGroupTypeConfigToml` produced, AFTER the rule-set
1047
+ * name → id resolution: the definition says which fields the server accepts in
1048
+ * each mode, so `groupType` rides the create body and the URL on update.
1049
+ */
1050
+ export declare function buildGroupTypeConfigPayload(configData: any, mode: PushMode): Record<string, any>;
1051
+ export declare function serializeCollectionTypeConfig(config: any, ruleSetIdToName: Map<string, string>, logger?: (message: string) => void): string;
1052
+ /** The create/update body for one collection-type config (#2644). */
1053
+ export declare function buildCollectionTypeConfigPayload(configData: any, mode: PushMode): Record<string, any>;
1054
+ /**
1055
+ * The upsert body for one metadata-category config (#2644).
1056
+ *
1057
+ * One endpoint serves create and update, so every field is writable in both
1058
+ * modes and the body carries the whole definition-declared surface. The
1059
+ * identity pair rides the URL too (`PUT …/{resourceType}/{category}`); sending
1060
+ * it in the body as well is what the POST form has always taken.
1061
+ */
1062
+ export declare function buildMetadataCategoryPayload(configData: any): Record<string, any>;
1063
+ /**
1064
+ * Issue #1567 — the TOML-owned fields of a database-type config, derived from
1065
+ * the definition (#2644 phase 3).
1066
+ *
1067
+ * These are fields the `database-type-configs/*.toml` file OWNS: the local file is the
1068
+ * source of truth, so removing one from the TOML must clear it server-side
1069
+ * (config-as-code), not silently preserve the stale value.
1070
+ *
1071
+ * The list used to be written out here, a second statement of the `[type]`
1072
+ * field surface that a new field would have had to be added to by hand. It is
1073
+ * now every exposed `[type]` scalar except the immutable identity, plus the two
1074
+ * sub-trees the definition classifies `structural` and the file nonetheless
1075
+ * owns whole (`triggers`, `metadataManifest`). `schema` is deliberately NOT
1076
+ * owned this way — it keeps its own `hasSchema` prior-state discriminator,
1077
+ * since a schema is a large sub-tree whose absence is ambiguous.
1078
+ */
1079
+ export declare function dbTypeOwnedScalars(table?: ConfigTable): string[];
1080
+ export declare const DB_TYPE_OWNED_SCALARS: readonly string[];
1081
+ /** The user-facing TOML key for a wire field (for push output). */
1082
+ export declare function dbTypeFieldLabel(key: string): string;
1083
+ /**
1084
+ * True when a value carries real content. null/undefined, blank/whitespace
1085
+ * strings, and empty objects/arrays all count as "no value" — the same
1086
+ * normalization the server applies (`value || null`) when it decides whether a
1087
+ * field is set.
1088
+ */
1089
+ export declare function hasMeaningfulValue(v: any): boolean;
1090
+ /**
1091
+ * Issue #1567 — always-send owned db-type scalars as value-or-`null`.
1092
+ *
1093
+ * The server PATCH endpoint distinguishes an omitted key (preserve) from an
1094
+ * explicit `null` (clear), so sending every owned scalar as `value ?? null`
1095
+ * converges the server to the local TOML: a field removed from the file lands
1096
+ * as `null` and is cleared. Matches the always-send pattern the sibling
1097
+ * `collection-type-config` push already uses.
1098
+ */
1099
+ export declare function buildOwnedScalarUpdate(typeConfig: any): Record<string, any>;
1100
+ /**
1101
+ * Issue #1567 — the owned scalars this push genuinely clears: the outgoing
1102
+ * value is empty but the server currently holds a real value (a non-null →
1103
+ * null transition). Used for the "Cleared <field>" report so removing an
1104
+ * access gate is never silent, and to decide whether an otherwise
1105
+ * type-field-less push still needs a type-config PATCH. Returns wire field
1106
+ * names (map with `dbTypeFieldLabel` for output). `serverConfig` is the live
1107
+ * GET; without it (fresh type or a failed fetch) nothing is reported cleared.
1108
+ */
1109
+ export declare function ownedScalarsBeingCleared(updateData: Record<string, any>, serverConfig: any): string[];
1110
+ /** The look-ahead a type-config PATCH carries about this push's operations. */
1111
+ export interface PendingOpClaims {
1112
+ pendingOpDeletes: string[];
1113
+ finalOpNames: string[];
1114
+ pendingOpUpdates: Array<{
1115
+ name: string;
1116
+ access: string | null;
1117
+ params: any;
1118
+ }>;
1119
+ pendingOpUpserts: Array<Record<string, any>>;
1120
+ }
1121
+ /**
1122
+ * What this push's operation set will BE, stated to the server (issues #813,
1123
+ * #1336, #2732).
1124
+ *
1125
+ * The type-config PATCH runs its gates BEFORE the same push's operation calls,
1126
+ * so without a look-ahead every gate judges the pre-push operations: a push
1127
+ * that removes a model is blocked by the very references it is deleting. The
1128
+ * file IS the target state (config-as-code), so the claims are derived from it:
1129
+ *
1130
+ * - `finalOpNames` — the names the type ends with. Both other claims are
1131
+ * verified against it server-side, which is what keeps them from being a
1132
+ * gate bypass for a direct-API caller.
1133
+ * - `pendingOpDeletes` — its complement among the ops last sync recorded, so
1134
+ * an op this push deletes is excluded from the schema / OPS_EXIST gates.
1135
+ * - `pendingOpUpserts` — the post-push BODY of every declared op, so the
1136
+ * schema-edit gate lints a rewritten op as rewritten and a model removal
1137
+ * plus its operation rewrites lands in one push (#2732).
1138
+ * - `pendingOpUpdates` — the rule-only form (`access`/`params`) the manifest
1139
+ * re-lint reads (#1336).
1140
+ *
1141
+ * All four are built here and sent together, on the dry-run and on the real
1142
+ * PATCH alike. They are never conditioned on the push having a deletion: a push
1143
+ * that only REWRITES operations is exactly the case #2732 exists for.
1144
+ */
1145
+ export declare function buildPendingOpClaims(operations: any[], existingOpNames: string[]): PendingOpClaims;
1146
+ export declare function parseDatabaseTypeToml(tomlData: any): {
1147
+ typeConfig: any;
1148
+ operations: any[];
1149
+ subscriptions: any[];
1150
+ };
1151
+ /**
1152
+ * The parsed shape of a rule-set TOML: the definition's fields plus the
1153
+ * structural `[rules]` tree. Identical to the create body, which is what
1154
+ * `config diff` hashes both sides through.
1155
+ */
1156
+ export declare function parseRuleSetToml(tomlData: any): any;
1157
+ export declare function parseGroupTypeConfigToml(tomlData: any): any;
1158
+ export declare function parseCollectionTypeConfigToml(tomlData: any): any;
1159
+ /**
1160
+ * `config diff`'s hash for one configuration object, over the field set its
1161
+ * DEFINITION declares (#2644 criterion 8).
1162
+ *
1163
+ * Both sides — the local file and the server entity — run through this one
1164
+ * function, so a field is visible to `config diff` exactly when the definition
1165
+ * exposes it. Each type used to carry a hand-written
1166
+ * `hashLocal*ForDiff` / `hashRemote*ForDiff` pair naming its own fields: a
1167
+ * third copy of the field surface, with the same failure mode as the other two
1168
+ * (a field pushed and pulled but missing from the hash is invisible to diff, so
1169
+ * an edit reads as "nothing differs" until push applies it).
1170
+ *
1171
+ * `extras` carries the values that are NOT definition fields and must still be
1172
+ * compared: the authored sub-trees the definition classifies `structural`
1173
+ * (`[rules]`, `[[operations]]`, the `[metadata]` manifest) and the
1174
+ * `_unresolvedRuleSetName` marker below. `undefined` entries are dropped so an
1175
+ * absent extra and an omitted one hash the same.
1176
+ */
1177
+ export declare function projectConfigForComparison(table: ConfigTable, entity: any, extras?: Record<string, any>): Record<string, any>;
1178
+ export declare function hashConfigForDiff(table: ConfigTable, entity: any, extras?: Record<string, any>): string;
1179
+ /**
1180
+ * The fields two projected records disagree on — what a conflict report prints
1181
+ * (#2731 B5) and what the immutable-field pre-check reads (B8).
1182
+ *
1183
+ * Compared over the UNION of keys, so a key present on one side only is a
1184
+ * difference rather than a silently ignored one. Values compare by canonical
1185
+ * JSON, which is key-order insensitive for objects and order-SENSITIVE for
1186
+ * arrays — a reordered array is a real change, per the comparator caveats.
1187
+ * Structural sub-trees (`rules`, `schema`, `metadataManifest`, operations,
1188
+ * subscriptions) arrive as single extras keys and therefore compare as units.
1189
+ */
1190
+ export declare function diffProjectedRecords(local: Record<string, any>, remote: Record<string, any>): Array<{
1191
+ field: string;
1192
+ local: any;
1193
+ server: any;
1194
+ }>;
1195
+ /**
1196
+ * The paths at which two projected records disagree, named the way the FILE
1197
+ * spells them (#3267) — what a `config diff` Modified row prints.
1198
+ *
1199
+ * `diffProjectedRecords` answers at the granularity of the projection, and
1200
+ * for a database type that is too coarse to act on: the 27 operations arrive
1201
+ * as one `operations` extra, the whole `[models.*]` tree as one `schema`
1202
+ * string. A row that read Modified for a reason the operator could not see —
1203
+ * an empty params set spelled two ways, an operation block the file declares
1204
+ * twice — was undiagnosable from the CLI. So this walks INTO each differing
1205
+ * field: a keyed child collection (`spec.childKeys`) is compared row by row
1206
+ * and named `operations["getInstitution"].params`, with a row only one side
1207
+ * has or that the file repeats named as such; a TOML-string field
1208
+ * (`spec.tomlFields`) is parsed on both sides and named by the path inside it
1209
+ * (`schema (models.Institution.fields.name.required)`); everything else is
1210
+ * walked as a value to its first differing leaf.
1211
+ *
1212
+ * Paths come back in projected-key order, keyed rows in key order, so the
1213
+ * list is stable across runs, and every leaf is its own entry — a schema
1214
+ * that differs in seven model fields is seven paths, so the caller's "+N
1215
+ * more" cap counts them the way it counts operations. A value the walk
1216
+ * cannot descend into (an array of a different length, a scalar against an
1217
+ * object) is named at the level it stopped.
1218
+ */
1219
+ export declare function describeProjectedDifferences(spec: Pick<ConfigDiffSpec, "childKeys" | "tomlFields">, local: Record<string, any>, remote: Record<string, any>): string[];
1220
+ /**
1221
+ * The differing fields the definition says an UPDATE does not accept (#2731 B8).
1222
+ *
1223
+ * A local edit to such a field (rule-set `resourceType`, `writableOn:
1224
+ * CREATE_ONLY`) would PATCH "successfully" while changing nothing server-side,
1225
+ * and then re-report forever — the exact shape of non-convergence this issue
1226
+ * exists to end. Extras keys are not definition fields and are never named.
1227
+ */
1228
+ export declare function findImmutableFieldDiffs(table: ConfigTable, fieldDiffs: ReadonlyArray<{
1229
+ field: string;
1230
+ }>): string[];
1231
+ /**
1232
+ * Which side of a converted resource changed (#2731 B4).
1233
+ *
1234
+ * `baselineHash` is the manifest's `semanticHash`: the comparator's hash of the
1235
+ * state both sides last agreed on. With it the answer is exact. Without it —
1236
+ * a manifest written before this issue landed — the direction is INFERRED from
1237
+ * the legacy signals rather than defaulting to "apply": a stored `contentHash`
1238
+ * still matching the file's bytes proves the local side did not move, and a
1239
+ * stored `modifiedAt` still matching the live one proves the server did not.
1240
+ * When neither signal establishes a side (or the row was just adopted), the
1241
+ * answer is `unknown-direction`, which callers treat as a conflict needing
1242
+ * `config pull` or `--force`. An upgraded installation's first push must never
1243
+ * silently overwrite server drift.
1244
+ */
1245
+ export type PushChangeClass = "unchanged" | "local-edited" | "server-drifted" | "both-changed" | "unknown-direction";
1246
+ export declare function classifyPushChange(input: {
1247
+ localHash: string;
1248
+ remoteHash: string;
1249
+ baselineHash?: string;
1250
+ /**
1251
+ * The server record has NOT been written since the last sync (#3317).
1252
+ *
1253
+ * Its own `modifiedAt` is what says so, and that is evidence no hash can
1254
+ * contradict: a record nothing has written cannot have drifted, so the local
1255
+ * file is the only side that can have moved. A baseline hash disagreeing
1256
+ * with the current remote projection then says something about the BASELINE
1257
+ * — recorded by a CLI whose projection of the same record differed — and not
1258
+ * about the server. Left false by callers whose row timestamp does not cover
1259
+ * everything the projection compares; `decidePushForConfig` owns that
1260
+ * judgement.
1261
+ */
1262
+ serverUnchangedSinceSync?: boolean;
1263
+ legacy?: {
1264
+ localBytesMatchStoredContentHash?: boolean;
1265
+ liveModifiedAtMatchesStored?: boolean;
1266
+ };
1267
+ }): PushChangeClass;
1268
+ /** The rule-set name/id maps `config diff` resolves references through. */
1269
+ export interface ConfigDiffMaps {
1270
+ ruleSetIdToName: Map<string, string>;
1271
+ ruleSetNameToId: Map<string, string>;
1272
+ }
1273
+ /**
1274
+ * How one configuration type is hashed for `config diff`: its definition, the
1275
+ * definition-driven parse both sides go through, the pull serializer that turns
1276
+ * a server entity into the file pull would have written, and the structural
1277
+ * values hashed alongside the definition's fields.
1278
+ */
1279
+ export interface ConfigDiffSpec {
1280
+ label: string;
1281
+ table: ConfigTable;
1282
+ /**
1283
+ * TOML doc -> the wire-shaped entity (the same parse `config push` uses).
1284
+ *
1285
+ * `extra` is the per-type context the projection needs and the document does
1286
+ * not carry (#2880): a test case's id→name lookups, through which the two
1287
+ * spellings of a reference meet. Mirrors `serialize`, which has taken one
1288
+ * since the database types joined.
1289
+ */
1290
+ parse(doc: any, extra?: any): any;
1291
+ /** Server entity -> the TOML `config pull` would write. */
1292
+ serialize(record: any, maps: ConfigDiffMaps, extra?: any): string;
1293
+ /** Structural values to hash beside the definition's fields. */
1294
+ extras?(entity: any, doc: any, extra?: any): Record<string, any>;
1295
+ /**
1296
+ * Rewrite the parsed entity the way the SERVER rewrites an accepted one
1297
+ * (#2880 DSO-003).
1298
+ *
1299
+ * Some handlers canonicalize on the way in — an integration's base URL gains
1300
+ * a trailing slash, its methods are upper-cased — so the state the server
1301
+ * returns is not the text the file holds. Comparing them raw makes an
1302
+ * authored-but-noncanonical value `local-edited` on every run and push
1303
+ * re-apply the same update forever; restamping the baseline cannot fix it,
1304
+ * because the local side never moves. Applied to both sides (the remote
1305
+ * reaches it through the same projection), so it must be idempotent.
1306
+ */
1307
+ canonicalize?(entity: any): void;
1308
+ /**
1309
+ * The projected keys THIS FILE does not manage (#2880 criterion 3).
1310
+ *
1311
+ * A handful of fields are present-only by design: push sends them when the
1312
+ * file spells them and leaves the server's value alone when it does not,
1313
+ * because "absent" here means "not authored here" rather than "cleared" — a
1314
+ * webhook with no `[verification]` section at all, whose signing material a
1315
+ * push must not revoke as a side effect. Comparing such a field against a
1316
+ * server that holds one reports a difference push will never act on: the
1317
+ * update omits the key, the server keeps its value, and the row comes back
1318
+ * Modified on every run. So the local side inherits the remote value for
1319
+ * exactly the keys the file leaves unmanaged, and the two commands agree
1320
+ * that there is nothing to do.
1321
+ */
1322
+ unmanaged?(doc: any): string[];
1323
+ /**
1324
+ * The projected extras that are KEYED child collections, by the key each
1325
+ * row carries (#3267) — a database type's `operations` by `name`. The
1326
+ * comparison already sorts these by key (`byKey`); this is what lets a
1327
+ * difference in one be NAMED as the row and the sub-field it is in, rather
1328
+ * than as the whole collection.
1329
+ */
1330
+ childKeys?: Record<string, string>;
1331
+ /**
1332
+ * The projected extras that are TOML documents held as strings (#3267) —
1333
+ * a database type's `[models.*]` `schema`. Named as the path inside the
1334
+ * document that differs, when both sides parse.
1335
+ */
1336
+ tomlFields?: string[];
1337
+ /** Whether the entity carries a rule-set reference needing resolution. */
1338
+ ruleSetRef?: boolean;
1339
+ /** How the type names itself in the rule-set resolution message. */
1340
+ describe(entity: any): string;
1341
+ }
1342
+ /**
1343
+ * Every type the shared comparator serves — the types `config diff` compares
1344
+ * field-for-field, and that `config push` gates on the same projection
1345
+ * (#2731 B1/B2; webhooks joined the comparator in #2757 and their push joined
1346
+ * the same gate in #2880, so no registered type answers "changed?" from the
1347
+ * file's bytes any more).
1348
+ *
1349
+ * Published so the round-trip acceptance bar (#2731 B9) can require a fixture
1350
+ * per type instead of listing them a second time by hand: converting a type is
1351
+ * then one edit here, and the bar says so if its round trip is untested.
1352
+ */
1353
+ export declare function configDiffSpecLabels(): string[];
1354
+ /** The diff spec for a configuration type. Throws rather than skipping a check. */
1355
+ export declare function configDiffSpec(label: string): ConfigDiffSpec;
1356
+ /**
1357
+ * Project a LOCAL config file onto the record both `config diff` and `config push`
1358
+ * compare (#2644 field set, #2731 shared gate). The hash is a thin wrapper, so
1359
+ * a push that needs to SAY what differs and a diff that only needs to know THAT
1360
+ * something differs read the same projection.
1361
+ */
1362
+ export declare function projectLocalConfig(spec: ConfigDiffSpec, parsedToml: any, maps: ConfigDiffMaps, extra?: any): Record<string, any>;
1363
+ /** The same projection for a SERVER entity — see `hashRemoteConfigForDiff`. */
1364
+ export declare function projectRemoteConfig(spec: ConfigDiffSpec, record: any, maps: ConfigDiffMaps, extra?: any): Record<string, any>;
1365
+ /**
1366
+ * BOTH sides of one comparison, with the file's unmanaged keys reconciled
1367
+ * (#2880) — the one place `spec.unmanaged` is honored, so `config diff` and
1368
+ * `config push` cannot read the same file differently.
1369
+ */
1370
+ export declare function projectConfigPair(spec: ConfigDiffSpec, localParsed: any, remoteRecord: any, maps: ConfigDiffMaps, extra?: any, localExtra?: any): {
1371
+ local: Record<string, any>;
1372
+ remote: Record<string, any>;
1373
+ };
1374
+ /**
1375
+ * One `config diff` row's content verdict for a registered type (#2880).
1376
+ *
1377
+ * The three outcomes the per-type blocks were each spelling out by hand:
1378
+ * equal (`exists`), different (`modified`, framed as a preview of
1379
+ * `config pull`), and "could not tell" — a missing record or a comparison that
1380
+ * threw, which degrades THIS row and never the whole diff. Written once so a
1381
+ * type joining the comparator cannot accidentally report a fourth thing.
1382
+ *
1383
+ * The two `extra` arguments are the same split `decidePushForConfig` makes:
1384
+ * `extra` carries what only the SERVER side has (a database type's operation
1385
+ * rows), while `localExtra` is context BOTH sides read a value through — a test
1386
+ * case's id→name lookups. Giving the local side nothing was a silent
1387
+ * mistranslation: a sidecar pinned by a resolvable `configId` projected the id
1388
+ * while the server's projection resolved it to the name, so an untouched file
1389
+ * reported Modified in diff and drifted in push.
1390
+ */
1391
+ export declare function compareLocalToRemote(spec: ConfigDiffSpec, localParsed: any, remoteRecord: any, maps?: ConfigDiffMaps, extra?: any, localExtra?: any): {
1392
+ status: string;
1393
+ hint?: string;
1394
+ };
1395
+ /** Hash a LOCAL config file's definition-projected field set (#2644). */
1396
+ export declare function hashLocalConfigForDiff(spec: ConfigDiffSpec, parsedToml: any, maps: ConfigDiffMaps, extra?: any): string;
1397
+ /**
1398
+ * Hash a SERVER entity the same way, by serializing it into the file `sync
1399
+ * pull` would write and hashing that — so the two sides are normalized
1400
+ * identically by construction, and a legacy encoding on disk (a JSON-string
1401
+ * operation, an id-based rule-set reference) is not a false `Modified`.
1402
+ *
1403
+ * `extra` carries the sibling rows a type's file also holds: a database type's
1404
+ * operations and subscriptions, which the list response does not include.
1405
+ */
1406
+ export declare function hashRemoteConfigForDiff(spec: ConfigDiffSpec, record: any, maps: ConfigDiffMaps, extra?: any): string;
1407
+ /** One field's disagreement between the local file and the live server entity. */
1408
+ export interface PushFieldDiff {
1409
+ field: string;
1410
+ local: any;
1411
+ server: any;
1412
+ }
1413
+ /**
1414
+ * What `config push` should do with one converted-type file, and why.
1415
+ *
1416
+ * `create`/`skip`/`update` are the applying outcomes; `drift`, `conflict`,
1417
+ * `immutable` and `live-unavailable` are the four ways push declines to apply
1418
+ * and says so.
1419
+ */
1420
+ export type PushGateOutcome = {
1421
+ action: "live-unavailable";
1422
+ } | {
1423
+ action: "create";
1424
+ } | {
1425
+ action: "skip";
1426
+ localHash: string;
1427
+ remoteHash: string;
1428
+ } | {
1429
+ action: "update";
1430
+ direction: "local-edited" | "forced" | "adopt-untracked";
1431
+ localHash: string;
1432
+ remoteHash?: string;
1433
+ expectedModifiedAt?: string;
1434
+ fields: PushFieldDiff[];
1435
+ } | {
1436
+ action: "drift";
1437
+ localHash: string;
1438
+ remoteHash: string;
1439
+ fields: PushFieldDiff[];
1440
+ } | {
1441
+ action: "conflict";
1442
+ direction: "both-changed" | "unknown-direction";
1443
+ localHash: string;
1444
+ remoteHash: string;
1445
+ fields: PushFieldDiff[];
1446
+ serverModifiedAt?: string;
1447
+ } | {
1448
+ action: "immutable";
1449
+ localHash: string;
1450
+ remoteHash: string;
1451
+ fields: PushFieldDiff[];
1452
+ immutableFields: string[];
1453
+ };
1454
+ /** The manifest entry a converted type's gate reads (all fields optional). */
1455
+ export interface PushBaselineEntry {
1456
+ modifiedAt?: string;
1457
+ contentHash?: string;
1458
+ semanticHash?: string;
1459
+ /**
1460
+ * The version the manifest last saw active, for a versioned type (#3317).
1461
+ * Read only beside `serverModifiedAtCoversProjection`: a recorded pointer
1462
+ * that is not the live one denies the timestamp's claim that the server
1463
+ * stood still.
1464
+ */
1465
+ activeConfigId?: string;
1466
+ }
1467
+ /**
1468
+ * `config push`'s change decision for one configuration file (#2731 B1/B3/B4/B8).
1469
+ *
1470
+ * This replaces `shouldPushFile` for the converted types. The differences that
1471
+ * matter, in order:
1472
+ *
1473
+ * - the comparison is `config diff`'s — the definition-projected field set on
1474
+ * both sides — so "skip as unchanged" and "Synced" cannot disagree, and a
1475
+ * comment-only or formatting-only edit is not a change;
1476
+ * - the right-hand side is the LIVE entity, not the manifest, so out-of-band
1477
+ * server drift is visible instead of being overwritten;
1478
+ * - the manifest becomes a baseline that answers WHICH side moved, so a
1479
+ * difference is attributed rather than assumed to be a local edit.
1480
+ *
1481
+ * Declining is a first-class outcome here. `drift`, `conflict` and `immutable`
1482
+ * each carry the field diff their report prints, and nothing is applied.
1483
+ *
1484
+ * `adoptsUntrackedByKey` is the one exception a type can ask for, and it is
1485
+ * documented on the field: an object the manifest has never recorded is the
1486
+ * adopt its create path would have performed via a 409 (#1006/#2909), not a
1487
+ * difference to refuse.
1488
+ */
1489
+ /**
1490
+ * What the DEGRADED gate does with one file — the path a converted type takes
1491
+ * when its live read failed (#2880 criterion 7).
1492
+ *
1493
+ * Falling back to the manifest's byte hash is right while there is one to fall
1494
+ * back to. Without one, `shouldPushFile(file, undefined)` answers "push it",
1495
+ * so a fresh checkout or a lost manifest turned "we could not read the server"
1496
+ * into "overwrite the server", unconditionally and silently — the one shape of
1497
+ * blind write this issue's direction attribution exists to prevent. So a
1498
+ * baseline-less update DECLINES: the resource is not written and the operator
1499
+ * is told which of `config pull` / `--force` clears it.
1500
+ *
1501
+ * A file the manifest does not name is declined for the same reason, and this
1502
+ * is the part that is easy to get wrong: an unrecorded file looks like a
1503
+ * create, but only LIVE STATE can say the server has nothing under that key —
1504
+ * and live state is exactly what this path could not read. The create that
1505
+ * follows is adopted by key on the 409 and re-issued as an update, so
1506
+ * "there is no recorded entity to overwrite" would have been a blind overwrite
1507
+ * of a resource this run never compared. Every section's degraded gate is
1508
+ * reached only when the live read failed, so there is no case here where
1509
+ * absence was proved.
1510
+ */
1511
+ export type DegradedPushDecision = {
1512
+ action: "skip";
1513
+ } | {
1514
+ action: "send";
1515
+ } | {
1516
+ action: "decline";
1517
+ reason: "no-baseline";
1518
+ };
1519
+ export declare function decideDegradedPush(input: {
1520
+ force?: boolean;
1521
+ storedContentHash?: string;
1522
+ currentFileHash?: string;
1523
+ }): DegradedPushDecision;
1524
+ export declare function decidePushForConfig(input: {
1525
+ spec: ConfigDiffSpec;
1526
+ maps: ConfigDiffMaps;
1527
+ localParsed: any;
1528
+ /** The live server entity, or null/undefined when the server has none. */
1529
+ live?: {
1530
+ record: any;
1531
+ extra?: any;
1532
+ modifiedAt?: string;
1533
+ } | null;
1534
+ /** The manifest entry, absent when this file has never been synced. */
1535
+ entry?: PushBaselineEntry | null;
1536
+ /** Byte hash of the local file — the legacy direction signal. */
1537
+ localFileHash?: string;
1538
+ /**
1539
+ * Per-type context for the LOCAL projection (#2880) — a test case's id→name
1540
+ * lookups. Separate from `live.extra`, which carries remote-only rows (a
1541
+ * database type's operations) the local parse must never see.
1542
+ */
1543
+ localExtra?: any;
1544
+ force?: boolean;
1545
+ /**
1546
+ * Whether the live record's `modifiedAt` covers EVERYTHING this type's
1547
+ * projection compares (#3317).
1548
+ *
1549
+ * Opt-in, because it is a claim about the data model rather than about this
1550
+ * push. A server function's header row carries its fields and the
1551
+ * `activeConfigId` pointer, and a version is immutable, so any change to
1552
+ * what the comparison reads moves that one timestamp — an equal timestamp
1553
+ * therefore PROVES the server stood still, and a baseline hash that
1554
+ * disagrees is stale rather than evidence of drift (the reporter's conflict,
1555
+ * printed over two identical timestamps).
1556
+ *
1557
+ * A database type's is the counter-example and the reason this is not the
1558
+ * default: its operations and subscriptions are rows of their own, so an
1559
+ * out-of-band edit to one changes the projection while the type's own
1560
+ * `modifiedAt` stands still. Claiming coverage there would turn a real
1561
+ * server change into a silent overwrite.
1562
+ *
1563
+ * The timestamp is trusted only alongside the version pointer: when the
1564
+ * manifest recorded an `activeConfigId`, the live record must still point at
1565
+ * it. The baseline is written from a header refetched AFTER the push, and a
1566
+ * concurrent activation landing in that window leaves the manifest carrying
1567
+ * the other operator's timestamp beside the version this push activated —
1568
+ * two timestamps that agree about a server that moved to a version the local
1569
+ * file never saw. The pointer is what moved, so it is what settles it.
1570
+ */
1571
+ serverModifiedAtCoversProjection?: boolean;
1572
+ /**
1573
+ * Whether this type's push path ADOPTS an existing object by key (#2909).
1574
+ *
1575
+ * Opt-in, and it changes exactly one outcome: a live entity the manifest has
1576
+ * no row for at all. Prompts (#1006), and the other types whose create path
1577
+ * recovers a 409 through `adoptByKeyOnCreate409`, treat that as the adopt it
1578
+ * has always been — an object pushed from another slot, created out of band,
1579
+ * or orphaned by a push that aborted before recording it, which the local
1580
+ * file is the declared intent for. Without this the gate reads that same
1581
+ * shape as a difference it cannot attribute, refuses, and the create the
1582
+ * adopt guard recovers from is never even sent (the #2909 regression).
1583
+ *
1584
+ * #2934 — the criterion is the CREATE PATH, not which issue converted the
1585
+ * type: cron triggers, webhooks, integrations, blob buckets and the
1586
+ * group/collection type configs all recover their create's key conflict the
1587
+ * same way, so they pass it too. Passing it at one call site left the other
1588
+ * five reporting a conflict for the adopt their own create documents.
1589
+ *
1590
+ * Rule sets keep it OFF, and that is a decision rather than an omission:
1591
+ * #2731 made a never-synced rule set differing from a same-named live one the
1592
+ * conflict the operator resolves with `config pull` or `--force`, its own
1593
+ * tests pin that, and `resourceType` — the field a wrong adopt would silently
1594
+ * strand — is one an update cannot repair.
1595
+ */
1596
+ adoptsUntrackedByKey?: boolean;
1597
+ }): PushGateOutcome;
1598
+ /**
1599
+ * The identity a file may leave to its name, written back into the parsed
1600
+ * document before the shared gate reads it (#2731 B1).
1601
+ *
1602
+ * Four types let the file name stand in for the identity field inside the file
1603
+ * (`orders.toml` for a database type, `group.profile.toml` for a metadata
1604
+ * category, and so on). The gate re-parses the DOCUMENT, so the derived value
1605
+ * has to be in it: an omitted `databaseType` otherwise projects as absent
1606
+ * against a server record that carries it, and — since these identities are
1607
+ * declared create-only — the file reports an immutable-field difference on
1608
+ * every push while `config diff`, which injects the same value before it hashes
1609
+ * (~sync.ts:11744), reads it Synced.
1610
+ *
1611
+ * A value the file states always wins; this only fills the gap the file name
1612
+ * was already filling for the rest of push.
1613
+ */
1614
+ export declare function withDerivedIdentity(doc: any, tomlPath: string, identity: Record<string, string | undefined>): any;
1615
+ /**
1616
+ * Which server-side entity this file's update targets (#2731 B3).
1617
+ *
1618
+ * The manifest is a cache, so it can be wrong in both directions: it can name
1619
+ * an entity the server no longer has (pointing an update at a 404 where the
1620
+ * operator asked for a re-create, #1659), and it can be silent about one that
1621
+ * is there (sending a create that comes back 409 to be adopted). When live
1622
+ * state was read, live state answers; the manifest is the fallback for the
1623
+ * degraded path only, where it is also what the byte-hash gate has always used.
1624
+ */
1625
+ export declare function resolveExistingId(input: {
1626
+ liveOk: boolean;
1627
+ liveId?: string;
1628
+ manifestId?: string;
1629
+ }): string | undefined;
1630
+ /**
1631
+ * Which CHILD rows a database type's push reconciles against (#2731 B3).
1632
+ *
1633
+ * `resolveExistingId` answers this for a whole entity; a database type's
1634
+ * operations and subscriptions are rows with the same question and the same
1635
+ * two possible answers. The manifest can name an operation the server no
1636
+ * longer has (the update PUTs a row that is gone) and be silent about one it
1637
+ * does have (the create POSTs a duplicate key) — both abort the push with a
1638
+ * server error, which is exactly the recovery `--force` is supposed to
1639
+ * perform.
1640
+ *
1641
+ * When the reconcile read the rows, they ARE the baseline: existence, the
1642
+ * timestamp each update guards with, and — as the complement of the file's own
1643
+ * list — which rows this push deletes. `undefined` means the read did not
1644
+ * happen (the degraded path, or a type live state says is absent), and the
1645
+ * manifest stays what it has always been.
1646
+ */
1647
+ export declare function liveChildBaseline(live: Map<string, any> | undefined, manifestChildren?: Record<string, {
1648
+ modifiedAt: string;
1649
+ }>): Record<string, {
1650
+ modifiedAt: string;
1651
+ }> | undefined;
1652
+ /**
1653
+ * The app's live rule sets, indexed the three ways push reads them (#2731 B3).
1654
+ *
1655
+ * One fetch answers three questions, and leaving any of them to the manifest
1656
+ * reintroduces the split this issue is about:
1657
+ *
1658
+ * - `byFileKey` — the rule set this file's own gate compares against;
1659
+ * - `idToName` — how a REFERENCING type's server record is serialized back
1660
+ * into the file pull would write, so a `ruleSetId` is compared as the name
1661
+ * the file spells;
1662
+ * - `nameToId` — how a referencing file's `ruleSetName` resolves to something
1663
+ * push can send. Seeding it from live is what lets `--only
1664
+ * group-type-config/team` resolve a rule set that exists on the server but
1665
+ * was never recorded locally, instead of failing as an unresolved
1666
+ * reference.
1667
+ */
1668
+ export declare function indexLiveRuleSets(ruleSets: any[]): {
1669
+ byFileKey: Map<string, any>;
1670
+ idToName: Map<string, string>;
1671
+ nameToId: Map<string, string>;
1672
+ };
1673
+ /**
1674
+ * The baseline a successful apply stamps, from the SERVER's own record
1675
+ * (#2731 B4).
1676
+ *
1677
+ * The baseline is compared against both sides on the next run, so it has to be
1678
+ * in the server's spelling: handlers normalize what they store (rule-set
1679
+ * create/update trims `name`), and a baseline taken from the local file would
1680
+ * make the very next legitimate local edit read as "both sides changed" — a
1681
+ * conflict the operator can only clear with `--force`.
1682
+ *
1683
+ * `undefined` — no baseline — is the honest answer when the response carries
1684
+ * nothing projectable and no refetch is available. The next run then falls back
1685
+ * to the legacy `contentHash`/`modifiedAt` signals, which is what an upgraded
1686
+ * installation runs on anyway; an invented baseline would instead assert
1687
+ * agreement that was never observed.
1688
+ */
1689
+ export declare function remoteSemanticBaseline(input: {
1690
+ spec: ConfigDiffSpec;
1691
+ maps: ConfigDiffMaps;
1692
+ /** What the write returned. Usable only if it is the stored entity. */
1693
+ returned?: any;
1694
+ extra?: any;
1695
+ /**
1696
+ * How this type recognizes its own stored record. The default — it carries a
1697
+ * `modifiedAt` — is what separates the stored entity from a bare
1698
+ * `{ success: true }` acknowledgement, whose empty projection would otherwise
1699
+ * be stamped as the state both sides agreed on. Types whose comparator reads
1700
+ * an envelope (an email template's `{ emailType, hasOverride, override }`)
1701
+ * say where their timestamp actually lives.
1702
+ */
1703
+ isStoredRecord?: (returned: any) => boolean;
1704
+ }): string | undefined;
1705
+ /** What `config push` decides for one transform (#2731 B7). */
1706
+ export type ScriptPushOutcome = {
1707
+ action: "skip";
1708
+ localHash: string;
1709
+ remoteHash: string;
1710
+ } | {
1711
+ action: "update";
1712
+ direction: "local-edited" | "forced" | "no-active-body";
1713
+ localHash: string;
1714
+ } | {
1715
+ action: "drift";
1716
+ localHash: string;
1717
+ remoteHash: string;
1718
+ } | {
1719
+ action: "conflict";
1720
+ direction: "both-changed" | "unknown-direction";
1721
+ localHash: string;
1722
+ remoteHash: string;
1723
+ };
1724
+ /**
1725
+ * `config push`'s change decision for one transform (#2731 B7).
1726
+ *
1727
+ * A transform's comparator IS its bytes: the body push sends is the body diff
1728
+ * compares, so there is no projection to reconcile — only the same three
1729
+ * questions every converted type asks. Local versus the LIVE active body, with
1730
+ * the manifest demoted to the baseline that says which side moved.
1731
+ *
1732
+ * A script the manifest has never seen reaches here too (adopted by name from
1733
+ * the live index). It has no baseline and no legacy signal, so an unequal body
1734
+ * classifies as `unknown-direction` — a conflict to report rather than a server
1735
+ * body to overwrite on a guess.
1736
+ */
1737
+ export declare function decidePushForScript(input: {
1738
+ localBody: string;
1739
+ /** Byte hash of the local file — the legacy direction signal. */
1740
+ localFileHash?: string;
1741
+ /**
1742
+ * The live active body, and the script row's timestamp beside it — the other
1743
+ * legacy direction signal (#2731 B4). Activating a config updates the script
1744
+ * row, so a timestamp still equal to the manifest's proves the server body
1745
+ * has not moved: an upgraded installation's first push applies a genuine
1746
+ * local edit instead of reporting a direction it could have known.
1747
+ *
1748
+ * `null` is an ANSWER, not a failed read: the script row exists and nothing
1749
+ * is active on it. See below.
1750
+ */
1751
+ live: {
1752
+ body: string;
1753
+ modifiedAt?: string;
1754
+ } | null;
1755
+ entry?: {
1756
+ contentHash?: string;
1757
+ semanticHash?: string;
1758
+ modifiedAt?: string;
1759
+ } | null;
1760
+ force?: boolean;
1761
+ }): ScriptPushOutcome;
1762
+ /**
1763
+ * One side of a field diff, rendered for a conflict report (#2731 B5).
1764
+ *
1765
+ * A rule set's `rules` tree or a template's HTML body can be kilobytes; the
1766
+ * useful statement about them is that they differ and how big they are, not
1767
+ * their contents scrolling past.
1768
+ */
1769
+ export declare function summarizeFieldValue(value: any): string;
1770
+ /**
1771
+ * The push summary line (#2731 criterion 10).
1772
+ *
1773
+ * The two established spellings are preserved exactly; drift — resources push
1774
+ * deliberately did NOT apply — is appended only when there is some, so an
1775
+ * operator never has to infer it from a change count that stayed put.
1776
+ */
1777
+ export declare function formatPushSummary(input: {
1778
+ pushed: number;
1779
+ skipped: number;
1780
+ drifted: number;
1781
+ }): string;
1782
+ /**
1783
+ * The summary line a FAILED push ends on (#2731 B5).
1784
+ *
1785
+ * "Re-run `config push` to converge" is true of one failure only: a database
1786
+ * type the validate-first gate blocked (#813), where the next push carries the
1787
+ * corrected state. A conflict is the opposite — the run declined to apply
1788
+ * precisely because both sides moved, so repeating it reports the same
1789
+ * conflict forever (the alpha.62 field report ends in that loop). Apply
1790
+ * failures, which used to be absent from this line entirely, need the file
1791
+ * fixed rather than either.
1792
+ */
1793
+ export declare function formatPushFailureSummary(input: {
1794
+ pushed: number;
1795
+ blockedDatabaseTypes: number;
1796
+ conflicts: number;
1797
+ applyFailures: number;
1798
+ }): string;
1799
+ /**
1800
+ * How many of a failed push's planned changes actually applied.
1801
+ *
1802
+ * `changes` records what push SET OUT to do, so the honest success count is
1803
+ * that list minus everything the run then refused or the server rejected:
1804
+ *
1805
+ * - a validate-first blocked database type (issue #813) keeps its labels for
1806
+ * visibility, but none of them landed — including its operations;
1807
+ * - a conflict on a change that was already counted (a late 409, recorded
1808
+ * before the write) cancels that change.
1809
+ *
1810
+ * A conflict the GATE declined (`planned: false`) is deliberately NOT
1811
+ * subtracted: it never added a `changes` entry, so charging the count for it
1812
+ * reports a push that did apply something as having applied nothing.
1813
+ */
1814
+ export declare function countAppliedChanges(input: {
1815
+ changes: Array<{
1816
+ type: string;
1817
+ key: string;
1818
+ }>;
1819
+ conflicts: Array<{
1820
+ planned?: boolean;
1821
+ }>;
1822
+ blockedDatabaseTypes: string[];
1823
+ schemaBlockedCount: number;
1824
+ }): number;
1825
+ /**
1826
+ * The semantic baseline to stamp for a file both sides now agree on (#2731 B4).
1827
+ *
1828
+ * `contentHash` records what the local BYTES were; this records what the
1829
+ * comparator SAW, which is what makes "who changed?" answerable: after a pull
1830
+ * or a successful push, local and server agree, so one hash is the baseline for
1831
+ * both. Best-effort by design — an unreadable or unparseable file yields
1832
+ * `undefined`, and a missing baseline degrades to the legacy inference rather
1833
+ * than taking the command down.
1834
+ */
1835
+ export declare function configSemanticHash(label: string, filePath: string, maps: ConfigDiffMaps): string | undefined;
1836
+ /**
1837
+ * The sync state `config push` writes into (#2731 B6, closing #2374).
1838
+ *
1839
+ * `loadSyncState` answers `null` for an absent sync-state baseline, and
1840
+ * every per-entity state write in the push loops is guarded by `if (syncState)`
1841
+ * — so a first push created every resource and recorded none of them. The next
1842
+ * push then re-entered the create path for everything and depended on 409
1843
+ * adoption to recover, which for database types hard-failed on operations.
1844
+ *
1845
+ * Building the state up front (the same shape the cross-app-push branch
1846
+ * already builds) makes those guards hold from the first run, so the
1847
+ * end-of-push and catch-path saves persist what the push actually did.
1848
+ */
1849
+ export declare function ensurePushSyncState(existing: SyncState | null, appId: string, serverUrl: string): SyncState;
1850
+ export declare function parseTomlFile(filePath: string): any;
1851
+ /**
1852
+ * Paginate through a list endpoint, collecting all items.
1853
+ */
1854
+ export declare function fetchAll<T>(listFn: (params: {
1855
+ limit: number;
1856
+ cursor?: string;
1857
+ }) => Promise<{
1858
+ items: T[];
1859
+ nextCursor?: string | null;
1860
+ }>, pageSize?: number, maxPages?: number): Promise<T[]>;
1861
+ /**
1862
+ * Issue #976 / #1006: shared 409 → adopt-by-key recovery for `config push`
1863
+ * create paths. When a CREATE hits a per-app unique-key constraint, the resource
1864
+ * is already on the server but missing from local sync state: orphaned by a
1865
+ * prior push that aborted before recording it, a mid-apply crash, or an
1866
+ * out-of-band create with the same key. Rather than hard-fail forever, this
1867
+ * helper looks the resource up by its unique key and hands the matched item
1868
+ * back so the caller can re-issue the create as an UPDATE and converge.
1869
+ *
1870
+ * The conflict surfaces inconsistently across create routes, so detection is
1871
+ * deliberately broad (see `isConflict` below):
1872
+ * - a 409 status (webhooks, cron triggers, the standard case);
1873
+ * - an "already exists" message — some routes surface the 409 as HTTP 400,
1874
+ * e.g. workflows;
1875
+ * - a "must be unique" message — routes with no explicit conflict catch let
1876
+ * dynamo-bao's per-app unique-constraint error propagate verbatim (e.g. rule
1877
+ * sets, whose create route has no 409 translation). This matches the
1878
+ * server's own admin-api convention (`src/admin-api.ts`), which treats
1879
+ * "must be unique" as a duplicate-key conflict.
1880
+ * Any other error is NOT a conflict and re-throws unchanged (see below).
1881
+ *
1882
+ * Safety (load-bearing): exact-key match only. The `lookup` is app-scoped, so a
1883
+ * key match is also an ownership match — an unrelated resource is never
1884
+ * overwritten. The three non-adopt outcomes each surface a clear error and do
1885
+ * NOT touch the server:
1886
+ * - a non-409 create error is re-wrapped via `wrapEntityError` and re-thrown
1887
+ * (no lookup is attempted);
1888
+ * - a lookup failure throws a clear "already exists but could not be adopted
1889
+ * (lookup failed: …)";
1890
+ * - a 409 with no matching key re-throws the ORIGINAL create error rather
1891
+ * than silently proceeding.
1892
+ *
1893
+ * Pagination is the caller's concern: `lookup` must return the COMPLETE
1894
+ * candidate set. For cursor-based lists (webhooks, integrations, prompts,
1895
+ * workflows) wrap the list call in `fetchAll`; the cron-triggers route already
1896
+ * drains every page server-side (#1668) so its flat `{ items }` is complete.
1897
+ * The UPDATE re-issue and sync-state stamping stay with the caller because they
1898
+ * differ per entity.
1899
+ *
1900
+ * @returns the matched existing item (guaranteed non-null on return).
1901
+ */
1902
+ export declare function adoptByKeyOnCreate409<T>(opts: {
1903
+ err: unknown;
1904
+ kind: string;
1905
+ key: string;
1906
+ lookup: () => Promise<T[]>;
1907
+ matchKey: (item: T) => boolean;
1908
+ }): Promise<T>;
1909
+ /**
1910
+ * Resolve a key-based rule set name reference to an ID.
1911
+ * Throws if the name cannot be resolved and throwOnMissing is true.
1912
+ */
1913
+ export declare function resolveRuleSetReference(entityConfig: any, ruleSetNameToId: Map<string, string>, entityLabel: string, options?: {
1914
+ throwOnMissing?: boolean;
1915
+ hint?: string;
1916
+ }): void;
1917
+ /**
1918
+ * Rule-set name→ID for the rule sets a scoped push is NOT applying
1919
+ * (issue #2645, review follow-up).
1920
+ *
1921
+ * Database, group and collection type configs name their rule set by NAME and
1922
+ * resolve it through the map the rule-set apply loop fills in as it goes. Under
1923
+ * `--only` that loop iterates the selected files alone, so `config push --only
1924
+ * database-type-config/orders` threw "Rule set … not found" for a type whose rule set
1925
+ * the operator had no reason to select — the single-object apply path refusing
1926
+ * an ordinary object, which is the opposite of what `--only` is for.
1927
+ *
1928
+ * A rule set the sync state already carries an id for is resolvable without
1929
+ * being applied, which is exactly what a reference needs. One that has never
1930
+ * been pushed still cannot resolve: there is no id to point at, and inventing
1931
+ * one would silently create the reference against nothing. That case keeps
1932
+ * throwing, with the hint to widen the selection.
1933
+ *
1934
+ * Pure, so the seeding is unit-testable without a server or a filesystem.
1935
+ */
1936
+ export declare function unselectedRuleSetIds(params: {
1937
+ /** Every rule-set file in the slot. */
1938
+ files: string[];
1939
+ /** The subset this push is applying — seeded entries never shadow these. */
1940
+ selected: string[];
1941
+ /** The id sync state holds for a rule-set file's key, if it has one. */
1942
+ idForFileKey: (fileKey: string) => string | undefined;
1943
+ /** The `ruleSet.name` a file declares — the name references use. */
1944
+ nameForFile: (file: string) => string | undefined;
1945
+ }): Map<string, string>;
1946
+ export declare function getTestsDir(configDir: string, blockType: string, blockKey: string): string;
1947
+ export interface TestCaseLookupMaps {
1948
+ configIdToName: Map<string, string>;
1949
+ promptIdToKey: Map<string, string>;
1950
+ }
1951
+ export declare function serializeTestCase(testCase: any, lookupMaps?: TestCaseLookupMaps, options?: {
1952
+ /** The sidecar path the warning names, e.g. `prompts/greet.tests/a.toml`. */
1953
+ file?: string;
1954
+ logger?: (message: string) => void;
1955
+ }): string;
1956
+ export declare function parseTestCaseToml(tomlData: any): any;
1957
+ /**
1958
+ * Pull server-side `Script` rows into `transforms/*.rhai` and record
1959
+ * each in the returned sync-state map. Issue #892 slice 7 + codex
1960
+ * follow-up on PR #893; body materialization fixed in issue #1196.
1961
+ * Exported so the unit test can drive it against stubbed
1962
+ * `client.listScripts` / `client.getScript`.
1963
+ *
1964
+ * Two-call contract: `listScripts` returns the `Script` HEADER only
1965
+ * (no `body` — see `serializeScript` in `src/admin-api.ts`). The Rhai
1966
+ * source lives on a versioned `ScriptConfig`. So for each listed
1967
+ * script we fetch the full record with `getScript`, which returns the
1968
+ * header plus serialized `configs` (each carrying its `body`), and
1969
+ * write the ACTIVE config's body. Reading the body off the list shape
1970
+ * (the old behavior) always produced 0-byte files.
1971
+ *
1972
+ * Idempotency: every call writes the active-config body the server
1973
+ * returned, so re-running `config pull` on an unchanged server overwrites
1974
+ * with the same bytes and produces the same `contentHash`. The result
1975
+ * map always reflects the current server state for the writes performed.
1976
+ *
1977
+ * `count` is the number of files actually written, not the number of
1978
+ * scripts listed: scripts with no active config and scripts whose
1979
+ * config fetch fails are skipped (we never clobber a `.rhai` with an
1980
+ * empty file).
1981
+ *
1982
+ * Older-server graceful path: if `listScripts` rejects (e.g. older
1983
+ * server without the route), the caller catches and treats it as
1984
+ * "no scripts to pull", leaving the directory empty. A per-script
1985
+ * `getScript` failure is caught and that one script is skipped without
1986
+ * aborting the rest of the pull.
1987
+ */
1988
+ export declare function pullScripts(client: ApiClient, appId: string, configDir: string, logger?: (msg: string) => void,
1989
+ /**
1990
+ * `config pull --only` (issue #2645): which transform names this pull may
1991
+ * write. Defaults to every one the server lists.
1992
+ */
1993
+ selects?: (name: string) => boolean): Promise<{
1994
+ scriptEntities: Record<string, {
1995
+ id: string;
1996
+ modifiedAt: string;
1997
+ contentHash?: string;
1998
+ semanticHash?: string;
1999
+ }>;
2000
+ count: number;
2001
+ /**
2002
+ * Which scripts the server listed, for the #1659 prune pass. `ok: false` when
2003
+ * the listing failed (an older server without the route, or a transient
2004
+ * error) or when any per-script fetch failed — in either case this pull's
2005
+ * picture of the type is incomplete and must not drive deletions. Scripts
2006
+ * skipped for having no active config are still *present*, so their keys stay
2007
+ * in the set and their local `.rhai` survives.
2008
+ */
2009
+ presence: PresenceOutcome;
2010
+ /**
2011
+ * Whether the script LISTING succeeded. Distinct from `presence`: a failed
2012
+ * per-script fetch blocks pruning but still leaves the other scripts written,
2013
+ * so their fresh state entries must be kept. Only a failed listing means
2014
+ * nothing was written and the prior state slot should be preserved whole.
2015
+ */
2016
+ listOk: boolean;
2017
+ /** Names the server listed, empty when the listing failed. */
2018
+ serverKeys: string[];
2019
+ }>;
2020
+ /**
2021
+ * The manifest row `config push` records for a function it just pushed
2022
+ * (#3317), and whether the server had already moved past that push.
2023
+ *
2024
+ * The header is refetched AFTER the version push, so the row's `modifiedAt`
2025
+ * is the live timestamp — but the version pointer and the envelope are the
2026
+ * push's own: the manifest names what THIS run activated, not whatever the
2027
+ * refetch happened to find. A concurrent activation landing between the push
2028
+ * and the refetch is exactly the case those two must not be conflated in:
2029
+ * recording the refetched pointer would make the other operator's version the
2030
+ * baseline the local file is then read as a plain edit of, and the next push
2031
+ * would roll it back without `--force`. Recording the pushed pointer beside
2032
+ * the live timestamp is what `decidePushForConfig` reads as "the pointer
2033
+ * moved" — a refusal, until the operator pulls or forces.
2034
+ *
2035
+ * `movedTo` names the version the refetched header points at when it is not
2036
+ * the pushed one, so the caller can say so at the moment it is known rather
2037
+ * than leaving it for the next push to report.
2038
+ */
2039
+ export declare function pushedFunctionBaseline(input: {
2040
+ functionId: string;
2041
+ /** The header refetched after the push, or the pre-push listing row. */
2042
+ refreshed: any;
2043
+ existingModifiedAt?: string;
2044
+ contentHash: string;
2045
+ /** The version the push activated (created, or the matched no-op). */
2046
+ pushedVersion: any;
2047
+ envelopeHash: string;
2048
+ sources: string[];
2049
+ spec: ConfigDiffSpec;
2050
+ maps: ConfigDiffMaps;
2051
+ }): {
2052
+ entity: {
2053
+ id: string;
2054
+ modifiedAt: string;
2055
+ contentHash: string;
2056
+ semanticHash?: string;
2057
+ envelopeHash: string;
2058
+ activeConfigId?: string;
2059
+ sources: string[];
2060
+ };
2061
+ movedTo: string | null;
2062
+ };
2063
+ /**
2064
+ * Pull server-side `ServerFunction` rows into `functions/` (#3179).
2065
+ *
2066
+ * Two things make this different from every other type's pull:
2067
+ *
2068
+ * 1. **A function is more than its file.** The active config VERSION holds an
2069
+ * envelope with the authored TOML bytes and every source file, so the pull
2070
+ * writes `functions/<key>.toml` plus the TypeScript beside it, and records
2071
+ * the source paths so prune can remove the code with the function.
2072
+ * 2. **Bytes, not a re-serialization.** The stored TOML is written verbatim,
2073
+ * so comments, key order and line endings survive the round trip (F-009).
2074
+ * A function that has never had code pushed has no stored bytes; that one
2075
+ * gets a canonical serialization of its header, which is still a pushable
2076
+ * file.
2077
+ *
2078
+ * A stored source path is never trusted: `planFunctionPull` refuses one that
2079
+ * normalizes outside the tree, and the refusals are reported rather than
2080
+ * swallowed.
2081
+ */
2082
+ export declare function pullFunctions(client: ApiClient, appId: string, configDir: string, logger?: (msg: string) => void, selects?: (key: string) => boolean): Promise<{
2083
+ functionEntities: Record<string, {
2084
+ id: string;
2085
+ modifiedAt: string;
2086
+ contentHash?: string;
2087
+ semanticHash?: string;
2088
+ envelopeHash?: string;
2089
+ activeConfigId?: string;
2090
+ sources?: string[];
2091
+ }>;
2092
+ count: number;
2093
+ presence: PresenceOutcome;
2094
+ listOk: boolean;
2095
+ serverKeys: string[];
2096
+ /**
2097
+ * Everything the server listed, archived rows included. Pull exports no file
2098
+ * for a tombstone, so without the row here the archived function would be
2099
+ * stranded: no file to delete and no managed key to prune, and its key would
2100
+ * go on blocking the app's whole namespace with nothing able to reclaim it.
2101
+ * The caller folds these into the state through `archivedTombstoneEntries`.
2102
+ */
2103
+ items: any[];
2104
+ }>;
2105
+ /** The outcome of one block's test-case pull (#2769). */
2106
+ export type TestCasePullOutcome =
2107
+ /** The listing succeeded; `count` cases were written and stale files removed. */
2108
+ {
2109
+ ok: true;
2110
+ count: number;
2111
+ }
2112
+ /** The listing failed: nothing was written, and the caller preserves state. */
2113
+ | {
2114
+ ok: false;
2115
+ };
2116
+ /**
2117
+ * Every test case a block has, draining the cursor (#2769).
2118
+ *
2119
+ * The endpoint returns 50 per page. Reconciling the sidecar against page one
2120
+ * alone would delete every file past it and drop the ids that keep push from
2121
+ * duplicating them, so the whole set is collected BEFORE anything is written.
2122
+ */
2123
+ export declare function listAllTestCases(client: ApiClient, appId: string, blockType: TestBlockType, blockId: string): Promise<{
2124
+ ok: true;
2125
+ items: any[];
2126
+ } | {
2127
+ ok: false;
2128
+ }>;
2129
+ /**
2130
+ * Copy a block's prior `entities.testCases` records into the pull's fresh map
2131
+ * (#2769), returning how many were carried.
2132
+ *
2133
+ * `config pull` rebuilds the test-case slot from scratch, so a block whose
2134
+ * listing failed would silently lose its ids — and the next push would create a
2135
+ * second copy of every case it could no longer recognize. A failed fetch means
2136
+ * "unknown", so the prior picture stands.
2137
+ */
2138
+ export declare function carryForwardTestCaseEntities(params: {
2139
+ prior: Record<string, any> | undefined;
2140
+ target: Record<string, any>;
2141
+ blockType: string;
2142
+ blockKey: string;
2143
+ }): number;
2144
+ /**
2145
+ * Write one block's test cases into its `<key>.tests/` sidecar and record them
2146
+ * in sync state. Exported so the unit tests can drive it against a stubbed
2147
+ * client on a temp directory.
2148
+ */
2149
+ export declare function pullTestCasesForBlock(params: {
2150
+ client: ApiClient;
2151
+ appId: string;
2152
+ blockType: TestBlockType;
2153
+ blockId: string;
2154
+ blockKey: string;
2155
+ configDir: string;
2156
+ testCaseEntities: Record<string, any>;
2157
+ /** Last sync's records, for the fallbacks a partial failure falls back to. */
2158
+ priorTestCaseEntities?: Record<string, any>;
2159
+ lookupMaps?: TestCaseLookupMaps;
2160
+ logger?: (message: string) => void;
2161
+ }): Promise<TestCasePullOutcome>;
2162
+ /**
2163
+ * One pull leg: every block of a type the pull selected (#2769).
2164
+ *
2165
+ * The selection is the caller's — a leg is handed the blocks `--only` left in,
2166
+ * so a scoped pull never reads, writes or removes a sidecar it was not asked
2167
+ * about. A block whose listing failed keeps its prior state entries and is
2168
+ * reported as skipped rather than silently reconciled to empty.
2169
+ */
2170
+ export declare function pullTestCasesForBlocks(params: {
2171
+ client: ApiClient;
2172
+ appId: string;
2173
+ configDir: string;
2174
+ blockType: TestBlockType;
2175
+ blocks: Array<{
2176
+ id: string;
2177
+ key: string;
2178
+ }>;
2179
+ testCaseEntities: Record<string, any>;
2180
+ priorTestCaseEntities?: Record<string, any>;
2181
+ lookupMaps?: TestCaseLookupMaps;
2182
+ logger?: (message: string) => void;
2183
+ }): Promise<{
2184
+ count: number;
2185
+ skippedBlocks: string[];
2186
+ }>;
2187
+ interface PushResolutionMaps {
2188
+ promptKeyToId: Map<string, string>;
2189
+ promptConfigNameToId: Map<string, string>;
2190
+ workflowConfigNameToId: Map<string, string>;
2191
+ scriptConfigNameToId?: Map<string, string>;
2192
+ integrationConfigNameToId?: Map<string, string>;
2193
+ /**
2194
+ * Fill one block's config name→id entries from the server on demand (#2769),
2195
+ * resolving `false` when the block has no id yet.
2196
+ *
2197
+ * A test case may pin a config on a block this push did not select, or run
2198
+ * under `--dry-run`, where nothing was written to learn the ids from. The
2199
+ * lookup is READ-ONLY and independent of both, so a name that a real push
2200
+ * resolves never reads as broken.
2201
+ */
2202
+ loadBlockConfigs?: (blockType: TestBlockType, blockKey: string) => Promise<boolean>;
2203
+ /** Whether a block is authored locally but not on the server yet. */
2204
+ isPlannedBlock?: (blockType: TestBlockType, blockKey: string) => boolean;
2205
+ }
2206
+ /** A failure row the push reports and exits nonzero on (`applyFailures`). */
2207
+ type ApplyFailure = {
2208
+ type: string;
2209
+ key: string;
2210
+ message: string;
2211
+ };
2212
+ /** The attachment bookkeeping a test case's sync-state record carries. */
2213
+ type RecordedAttachments = {
2214
+ attachments?: Record<string, string>;
2215
+ attachmentFilenames?: string[];
2216
+ };
2217
+ /**
2218
+ * Which attachments a push must upload, and which the SERVER holds that the
2219
+ * sidecar no longer does (#2769).
2220
+ *
2221
+ * Comparison is by content hash: the pre-#2769 state recorded names only, so a
2222
+ * byte change under an unchanged name was invisible. A state entry still in the
2223
+ * old format has no hash to compare against, so every file counts as changed
2224
+ * and re-uploads once (an idempotent overwrite) — after which the state carries
2225
+ * hashes and the next push skips them.
2226
+ *
2227
+ * The removed set is a REPORT, not an action: deleting a remote attachment
2228
+ * because a local file is missing waits for `--prune`.
2229
+ */
2230
+ export declare function planAttachmentPush(params: {
2231
+ local: Array<{
2232
+ filename: string;
2233
+ hash: string;
2234
+ }>;
2235
+ recorded: RecordedAttachments | undefined;
2236
+ }): {
2237
+ upload: string[];
2238
+ removedRemotely: string[];
2239
+ };
2240
+ /**
2241
+ * Create a test case carrying the identity its file name asserts (#2896).
2242
+ *
2243
+ * The one case protocol detection cannot answer is a create into an EMPTY
2244
+ * block: there is no listed record to read `key` off. Push is optimistic there,
2245
+ * and an older server's 400 is the answer — retried once without the key, and
2246
+ * named, because a case created without one will duplicate on the next clone.
2247
+ */
2248
+ export declare function createTestCaseWithIdentity(params: {
2249
+ client: ApiClient;
2250
+ appId: string;
2251
+ blockType: TestBlockType;
2252
+ blockId: string;
2253
+ payload: any;
2254
+ /** Omitted when the server has no keys; then this is exactly the old call. */
2255
+ key?: string;
2256
+ logger?: (message: string) => void;
2257
+ }): Promise<any>;
2258
+ /**
2259
+ * Push one block's authored test-case sidecar. Exported so the unit tests can
2260
+ * drive it against a stubbed client on a temp directory.
2261
+ *
2262
+ * Plain push creates and updates only: a file the operator removed is reported
2263
+ * as a pending deletion and handled by `--prune` (`applyTestCasePrune`).
2264
+ */
2265
+ export declare function pushTestCasesForBlock(params: {
2266
+ client: ApiClient;
2267
+ appId: string;
2268
+ blockType: TestBlockType;
2269
+ blockId: string;
2270
+ blockKey: string;
2271
+ configDir: string;
2272
+ syncState: SyncState | null;
2273
+ dryRun: boolean;
2274
+ changes: Array<{
2275
+ type: string;
2276
+ action: string;
2277
+ key: string;
2278
+ }>;
2279
+ /** Where a rejected create/update/upload goes — it fails the push (#2731 B2). */
2280
+ failures: ApplyFailure[];
2281
+ /**
2282
+ * What push DECLINED to apply (#2880): server drift, a conflict, a live read
2283
+ * that failed with no baseline. Reported exactly as every other converted
2284
+ * type reports it — the caller passes its `recordDeclined`.
2285
+ */
2286
+ declined?: (type: string, key: string, outcome: Extract<PushGateOutcome, {
2287
+ action: "drift" | "conflict" | "immutable" | "live-unavailable";
2288
+ }>, storedModifiedAt?: string) => void;
2289
+ /** The id→name lookups the comparison reads a reference through (#2880). */
2290
+ lookupMaps?: TestCaseLookupMaps;
2291
+ /**
2292
+ * Re-derive `lookupMaps` from the caller's name→id maps (#2880). Called after
2293
+ * this case's references are resolved and before it is compared: resolution
2294
+ * is what LOADS a block's configs, so the comparison would otherwise read a
2295
+ * reference the same push just learned how to read.
2296
+ */
2297
+ refreshLookupMaps?: () => void;
2298
+ resolutionMaps?: PushResolutionMaps;
2299
+ options?: {
2300
+ force?: boolean;
2301
+ };
2302
+ }): Promise<{
2303
+ skipped: number;
2304
+ }>;
2305
+ /**
2306
+ * Whether a sidecar and a live case say the same thing (#2896).
2307
+ *
2308
+ * The corroboration the adoption passes below need: identity nothing states can
2309
+ * only be inferred from content, and only when the inference is unique. It is
2310
+ * the SAME projection `config diff` compares with, memoized per file and per
2311
+ * record, so pairing and the change verdict can never disagree. A file that
2312
+ * cannot be read matches nothing — an unparseable sidecar fails its own push
2313
+ * with a message that names it.
2314
+ */
2315
+ export declare function testCaseContentMatcher(params: {
2316
+ testsDir: string;
2317
+ spec: ConfigDiffSpec;
2318
+ extra?: any;
2319
+ }): (localSlug: string, live: any) => boolean;
2320
+ /**
2321
+ * Which live test case each sidecar manages (#2880 behavior 16, #2896).
2322
+ *
2323
+ * The manifest id comes FIRST, then the identity the COMMITTED tree carries —
2324
+ * the file's basename, matched against the case's stored `key`. Everything
2325
+ * after that is adoption of a case whose identity nothing states: a rename the
2326
+ * manifest still remembers under the old name, a legacy case matched by
2327
+ * content, and finally the slug-of-name rule #2880 shipped.
2328
+ *
2329
+ * The order matters because every pass consumes its claims: identity that IS
2330
+ * recorded always wins over a guess, and a guess is only made when it is
2331
+ * unambiguous in BOTH directions. Where it is not, the file stays unpaired and
2332
+ * is barred from the create path — push refusing to guess is the whole point,
2333
+ * since the failure it replaces is a silently duplicated test case.
2334
+ *
2335
+ * Against a server with no keys the passes that depend on them are skipped
2336
+ * entirely, so pairing is bit-for-bit what #2880 shipped.
2337
+ *
2338
+ * One function for both commands, because a diff row and a push decision that
2339
+ * pair differently are two answers to the same question.
2340
+ */
2341
+ export declare function pairTestCases(input: {
2342
+ blockType: string;
2343
+ blockKey: string;
2344
+ localSlugs: string[];
2345
+ liveCases: any[];
2346
+ managed: Record<string, {
2347
+ id?: string;
2348
+ slug?: string;
2349
+ blockKey?: string;
2350
+ }> | undefined;
2351
+ /** Whether the server carries identity keys at all (#2896). */
2352
+ serverSupportsKeys?: boolean;
2353
+ /** Whether a local sidecar's content equals a live case's projection. */
2354
+ contentMatches?: (localSlug: string, live: any) => boolean;
2355
+ }): {
2356
+ bySlug: Map<string, any>;
2357
+ localOnly: string[];
2358
+ remoteOnly: any[];
2359
+ /** Files adopted from a stale manifest entry: basename → that entry's key. */
2360
+ renamedFrom: Map<string, string>;
2361
+ /** Files barred from the create path this run, with the reason to report. */
2362
+ refused: Map<string, string>;
2363
+ };
2364
+ /**
2365
+ * `config diff` for one block's test-case sidecar (#2769). A failed listing is
2366
+ * an OUTCOME the caller reports as "not compared" — the old helper returned
2367
+ * silently, so a block whose tests could not be fetched simply disappeared from
2368
+ * a report that still read as exhaustive.
2369
+ */
2370
+ export interface TestCaseDiffRow {
2371
+ blockType: string;
2372
+ blockKey: string;
2373
+ slug: string;
2374
+ status: string;
2375
+ /** What a validation error or a degraded comparison has to say. */
2376
+ hint?: string;
2377
+ /** Attachments push would upload from this sidecar (#2880 behavior 19). */
2378
+ attachmentUploads?: string[];
2379
+ /** Managed attachments `push --prune` would delete (never a plain push). */
2380
+ attachmentDeletions?: string[];
2381
+ }
2382
+ export declare function compareTestCasesForBlock(params: {
2383
+ client: ApiClient;
2384
+ appId: string;
2385
+ blockType: TestBlockType;
2386
+ blockId: string;
2387
+ blockKey: string;
2388
+ configDir: string;
2389
+ /** The manifest — identity, and the attachment hashes the plan reads. */
2390
+ syncState?: SyncState | null;
2391
+ lookupMaps?: TestCaseLookupMaps;
2392
+ }): Promise<{
2393
+ ok: true;
2394
+ rows: TestCaseDiffRow[];
2395
+ } | {
2396
+ ok: false;
2397
+ }>;
2398
+ /**
2399
+ * Shape errors in an authored test-case sidecar, for the push preflight
2400
+ * (#2769). The unknown-key check beside it says which keys may appear; this
2401
+ * says whether the ones that are there can be pushed at all — a missing `name`
2402
+ * is a 400 from the server, and malformed JSON text used to be silently dropped
2403
+ * on the way to it, pushing a test case that tested something else.
2404
+ */
2405
+ export declare function testCaseSidecarErrors(filePath: string, tomlData: any): string[];
2406
+ /**
2407
+ * EVERY reason `config push` refuses one test-case sidecar (#2880 criterion 1).
2408
+ *
2409
+ * The four checks the push preflight ran inline: the document's shape, the keys
2410
+ * the definition recognizes, the declared type of each value, and the sidecar's
2411
+ * own authoring rules (#2769). Collected in one place because `config diff`
2412
+ * runs the identical list — it used to run only the last of the four, so a
2413
+ * sidecar with an unknown key or a mistyped ordinary field was reported Synced
2414
+ * by the command whose whole promise is that a Synced file pushes.
2415
+ */
2416
+ export declare function testCaseSidecarPreflightErrors(filePath: string, tomlData: any): string[];
2417
+ /** A managed test case whose authored file is gone (#2769). */
2418
+ export interface TestCaseDeletionCandidate {
2419
+ stateKey: string;
2420
+ blockType: string;
2421
+ blockId: string;
2422
+ blockKey: string;
2423
+ slug: string;
2424
+ /** Absent when the case was never successfully created server-side. */
2425
+ id?: string;
2426
+ /**
2427
+ * The state entry — with a file still on disk — that holds this same id
2428
+ * (#2896). Set when the missing file was RENAMED rather than deleted: the
2429
+ * case is alive under another name, so the row is cleared and nothing is
2430
+ * deleted server-side.
2431
+ */
2432
+ supersededBy?: string;
2433
+ }
2434
+ /** A managed attachment whose local file is gone (#2769). */
2435
+ export interface AttachmentDeletionCandidate extends TestCaseDeletionCandidate {
2436
+ filename: string;
2437
+ }
2438
+ /**
2439
+ * Managed test cases whose `<slug>.toml` the operator removed (#2769).
2440
+ *
2441
+ * Only under a block that is itself still managed: when the BLOCK's file is
2442
+ * gone too, the block's own prune owns the whole sidecar (`removePrunedSidecar`)
2443
+ * and listing its cases here would delete them twice over.
2444
+ */
2445
+ export declare function collectTestCaseDeletions(params: {
2446
+ configDir: string;
2447
+ testCaseEntities: Record<string, any> | undefined;
2448
+ blockExists?: (blockType: string, blockKey: string) => boolean;
2449
+ }): TestCaseDeletionCandidate[];
2450
+ /**
2451
+ * Managed attachments whose local file is gone, for a test case that survives
2452
+ * (#2769). An absent attachment DIRECTORY is the authored spelling of "no
2453
+ * attachments" — Git cannot carry an empty directory — so its recorded files
2454
+ * are candidates too. Still `--prune`-gated: plain push deletes nothing.
2455
+ */
2456
+ export declare function collectAttachmentDeletions(params: {
2457
+ configDir: string;
2458
+ testCaseEntities: Record<string, any> | undefined;
2459
+ blockExists?: (blockType: string, blockKey: string) => boolean;
2460
+ }): AttachmentDeletionCandidate[];
2461
+ /**
2462
+ * What a plain `config push` says about the sidecar deletions it is NOT
2463
+ * applying (#2769), one report for the whole push.
2464
+ *
2465
+ * Attachments belong here rather than in the per-block push: deleting only a
2466
+ * fixture leaves its test-case TOML untouched, so that push skips the case by
2467
+ * hash and never reaches a message of its own — exactly the case an operator
2468
+ * needs told, since the test keeps running against a file the tree no longer
2469
+ * has. Returns the lines to print; empty when nothing is pending.
2470
+ */
2471
+ export declare function formatPendingTestDeletions(params: {
2472
+ testCases: TestCaseDeletionCandidate[];
2473
+ attachments: AttachmentDeletionCandidate[];
2474
+ }): string[];
2475
+ /**
2476
+ * Apply the test-case half of `config push --prune` (#2769).
2477
+ *
2478
+ * Runs after the batch confirmation, alongside the entity prunes: deletes the
2479
+ * cases and attachments whose authored files are gone, drops their state, and
2480
+ * routes failures into the push's failure list. Under `--dry-run` it counts the
2481
+ * same rows and calls nothing.
2482
+ */
2483
+ export declare function applyTestCasePrune(params: {
2484
+ client: ApiClient;
2485
+ appId: string;
2486
+ testCaseEntities: Record<string, any>;
2487
+ candidates: TestCaseDeletionCandidate[];
2488
+ attachmentCandidates: AttachmentDeletionCandidate[];
2489
+ dryRun: boolean;
2490
+ changes: Array<{
2491
+ type: string;
2492
+ action: string;
2493
+ key: string;
2494
+ }>;
2495
+ failures: ApplyFailure[];
2496
+ logger?: (message: string) => void;
2497
+ }): Promise<{
2498
+ deleted: number;
2499
+ deletedAttachments: number;
2500
+ }>;
2501
+ /**
2502
+ * Attach the server-reconciling verbs to the `config` group (issue #2759).
2503
+ *
2504
+ * They used to be a top-level `sync` noun, which split one workflow — managing
2505
+ * the TOML configuration tree — across two nouns by whether a verb happened to
2506
+ * call the API. Users do not think in that distinction; every real workflow
2507
+ * crosses it. So `config` owns both halves, and this function takes the group
2508
+ * `registerConfigCommands` created rather than making one of its own.
2509
+ *
2510
+ * The module keeps its name and its ~50 exported helpers: `sync.ts`,
2511
+ * `resolveSyncDir`, the sync-state baseline and `primitive/<env>/`
2512
+ * are internal identifiers, deliberately left alone (#2759 scope).
2513
+ */
2514
+ export declare function registerConfigSyncCommands(sync: Command): void;