primitive-admin 1.1.0-alpha.9 → 1.1.0

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