primitive-admin 1.1.0-alpha.7 → 1.1.0-alpha.71

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 (438) hide show
  1. package/README.md +404 -80
  2. package/assets/skill/skills/primitive-platform/SKILL.md +808 -0
  3. package/dist/bin/primitive.d.ts +2 -0
  4. package/dist/bin/primitive.js +294 -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 +138 -19
  8. package/dist/src/commands/admins.js.map +1 -1
  9. package/dist/src/commands/analytics.d.ts +2 -0
  10. package/dist/src/commands/analytics.js +544 -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 +51 -96
  14. package/dist/src/commands/apps.js.map +1 -1
  15. package/dist/src/commands/auth.d.ts +2 -0
  16. package/dist/src/commands/auth.js +177 -7
  17. package/dist/src/commands/auth.js.map +1 -1
  18. package/dist/src/commands/blob-buckets.d.ts +2 -0
  19. package/dist/src/commands/blob-buckets.js +330 -0
  20. package/dist/src/commands/blob-buckets.js.map +1 -0
  21. package/dist/src/commands/catalog.d.ts +2 -0
  22. package/dist/src/commands/catalog.js +37 -38
  23. package/dist/src/commands/catalog.js.map +1 -1
  24. package/dist/src/commands/collection-type-configs.d.ts +2 -0
  25. package/dist/src/commands/collection-type-configs.js +92 -0
  26. package/dist/src/commands/collection-type-configs.js.map +1 -0
  27. package/dist/src/commands/collections.d.ts +2 -0
  28. package/dist/src/commands/collections.js +565 -0
  29. package/dist/src/commands/collections.js.map +1 -0
  30. package/dist/src/commands/comparisons.d.ts +2 -0
  31. package/dist/src/commands/comparisons.js +6 -6
  32. package/dist/src/commands/comparisons.js.map +1 -1
  33. package/dist/src/commands/config.d.ts +46 -0
  34. package/dist/src/commands/config.js +479 -0
  35. package/dist/src/commands/config.js.map +1 -0
  36. package/dist/src/commands/connections.d.ts +2 -0
  37. package/dist/src/commands/connections.js +100 -0
  38. package/dist/src/commands/connections.js.map +1 -0
  39. package/dist/src/commands/cron-triggers.d.ts +2 -0
  40. package/dist/src/commands/cron-triggers.js +265 -0
  41. package/dist/src/commands/cron-triggers.js.map +1 -0
  42. package/dist/src/commands/database-type-configs.d.ts +2 -0
  43. package/dist/src/commands/database-type-configs.js +171 -0
  44. package/dist/src/commands/database-type-configs.js.map +1 -0
  45. package/dist/src/commands/database-types.d.ts +2 -0
  46. package/dist/src/commands/database-types.js +471 -0
  47. package/dist/src/commands/database-types.js.map +1 -0
  48. package/dist/src/commands/databases.d.ts +65 -0
  49. package/dist/src/commands/databases.js +2140 -112
  50. package/dist/src/commands/databases.js.map +1 -1
  51. package/dist/src/commands/documents.d.ts +2 -0
  52. package/dist/src/commands/documents.js +1357 -19
  53. package/dist/src/commands/documents.js.map +1 -1
  54. package/dist/src/commands/email-templates.d.ts +2 -0
  55. package/dist/src/commands/email-templates.js +174 -0
  56. package/dist/src/commands/email-templates.js.map +1 -0
  57. package/dist/src/commands/env.d.ts +23 -0
  58. package/dist/src/commands/env.js +333 -0
  59. package/dist/src/commands/env.js.map +1 -0
  60. package/dist/src/commands/feature-flags.d.ts +14 -0
  61. package/dist/src/commands/feature-flags.js +116 -0
  62. package/dist/src/commands/feature-flags.js.map +1 -0
  63. package/dist/src/commands/group-type-configs.d.ts +2 -0
  64. package/dist/src/commands/group-type-configs.js +86 -0
  65. package/dist/src/commands/group-type-configs.js.map +1 -0
  66. package/dist/src/commands/groups.d.ts +2 -0
  67. package/dist/src/commands/groups.js +38 -99
  68. package/dist/src/commands/groups.js.map +1 -1
  69. package/dist/src/commands/guides.d.ts +223 -0
  70. package/dist/src/commands/guides.js +617 -65
  71. package/dist/src/commands/guides.js.map +1 -1
  72. package/dist/src/commands/init.d.ts +25 -0
  73. package/dist/src/commands/init.js +1605 -208
  74. package/dist/src/commands/init.js.map +1 -1
  75. package/dist/src/commands/integrations.d.ts +2 -0
  76. package/dist/src/commands/integrations.js +380 -178
  77. package/dist/src/commands/integrations.js.map +1 -1
  78. package/dist/src/commands/llm.d.ts +2 -0
  79. package/dist/src/commands/llm.js +4 -2
  80. package/dist/src/commands/llm.js.map +1 -1
  81. package/dist/src/commands/locks.d.ts +8 -0
  82. package/dist/src/commands/locks.js +160 -0
  83. package/dist/src/commands/locks.js.map +1 -0
  84. package/dist/src/commands/metadata-category-configs.d.ts +12 -0
  85. package/dist/src/commands/metadata-category-configs.js +112 -0
  86. package/dist/src/commands/metadata-category-configs.js.map +1 -0
  87. package/dist/src/commands/metadata.d.ts +2 -0
  88. package/dist/src/commands/metadata.js +281 -0
  89. package/dist/src/commands/metadata.js.map +1 -0
  90. package/dist/src/commands/prompts.d.ts +2 -0
  91. package/dist/src/commands/prompts.js +225 -584
  92. package/dist/src/commands/prompts.js.map +1 -1
  93. package/dist/src/commands/rule-sets.d.ts +3 -0
  94. package/dist/src/commands/rule-sets.js +272 -0
  95. package/dist/src/commands/rule-sets.js.map +1 -0
  96. package/dist/src/commands/scripts.d.ts +20 -0
  97. package/dist/src/commands/scripts.js +554 -0
  98. package/dist/src/commands/scripts.js.map +1 -0
  99. package/dist/src/commands/secrets.d.ts +2 -0
  100. package/dist/src/commands/secrets.js +108 -0
  101. package/dist/src/commands/secrets.js.map +1 -0
  102. package/dist/src/commands/sessions.d.ts +2 -0
  103. package/dist/src/commands/sessions.js +75 -0
  104. package/dist/src/commands/sessions.js.map +1 -0
  105. package/dist/src/commands/skill.d.ts +2 -0
  106. package/dist/src/commands/skill.js +29 -0
  107. package/dist/src/commands/skill.js.map +1 -0
  108. package/dist/src/commands/sync-app-settings.d.ts +158 -0
  109. package/dist/src/commands/sync-app-settings.js +330 -0
  110. package/dist/src/commands/sync-app-settings.js.map +1 -0
  111. package/dist/src/commands/sync.d.ts +2323 -0
  112. package/dist/src/commands/sync.js +15065 -843
  113. package/dist/src/commands/sync.js.map +1 -1
  114. package/dist/src/commands/tokens.d.ts +2 -0
  115. package/dist/src/commands/tokens.js +130 -21
  116. package/dist/src/commands/tokens.js.map +1 -1
  117. package/dist/src/commands/users.d.ts +2 -0
  118. package/dist/src/commands/users.js +532 -23
  119. package/dist/src/commands/users.js.map +1 -1
  120. package/dist/src/commands/vars.d.ts +8 -0
  121. package/dist/src/commands/vars.js +96 -0
  122. package/dist/src/commands/vars.js.map +1 -0
  123. package/dist/src/commands/waitlist.d.ts +2 -0
  124. package/dist/src/commands/waitlist.js +10 -10
  125. package/dist/src/commands/waitlist.js.map +1 -1
  126. package/dist/src/commands/webhooks.d.ts +2 -0
  127. package/dist/src/commands/webhooks.js +562 -0
  128. package/dist/src/commands/webhooks.js.map +1 -0
  129. package/dist/src/commands/workflows.d.ts +116 -0
  130. package/dist/src/commands/workflows.js +1583 -681
  131. package/dist/src/commands/workflows.js.map +1 -1
  132. package/dist/src/lib/access-rule-display.d.ts +21 -0
  133. package/dist/src/lib/access-rule-display.js +34 -0
  134. package/dist/src/lib/access-rule-display.js.map +1 -0
  135. package/dist/src/lib/api-client.d.ts +1936 -0
  136. package/dist/src/lib/api-client.js +1826 -138
  137. package/dist/src/lib/api-client.js.map +1 -1
  138. package/dist/src/lib/app-settings-descriptor.d.ts +263 -0
  139. package/dist/src/lib/app-settings-descriptor.js +575 -0
  140. package/dist/src/lib/app-settings-descriptor.js.map +1 -0
  141. package/dist/src/lib/auth-flow.d.ts +8 -0
  142. package/dist/src/lib/batch.d.ts +26 -0
  143. package/dist/src/lib/batch.js +32 -0
  144. package/dist/src/lib/batch.js.map +1 -0
  145. package/dist/src/lib/block-layout.d.ts +160 -0
  146. package/dist/src/lib/block-layout.js +451 -0
  147. package/dist/src/lib/block-layout.js.map +1 -0
  148. package/dist/src/lib/canonical-json.d.ts +12 -0
  149. package/dist/src/lib/canonical-json.js +35 -0
  150. package/dist/src/lib/canonical-json.js.map +1 -0
  151. package/dist/src/lib/channel.d.ts +30 -0
  152. package/dist/src/lib/channel.js +68 -0
  153. package/dist/src/lib/channel.js.map +1 -0
  154. package/dist/src/lib/cli-manifest.d.ts +68 -0
  155. package/dist/src/lib/cli-manifest.js +71 -0
  156. package/dist/src/lib/cli-manifest.js.map +1 -0
  157. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +101 -0
  158. package/dist/src/lib/codegen-shared/generatedFiles.js +191 -0
  159. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -0
  160. package/dist/src/lib/codegen-shared/prettierStable.d.ts +262 -0
  161. package/dist/src/lib/codegen-shared/prettierStable.js +610 -0
  162. package/dist/src/lib/codegen-shared/prettierStable.js.map +1 -0
  163. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +68 -0
  164. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +168 -0
  165. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -0
  166. package/dist/src/lib/config-object-descriptor.d.ts +127 -0
  167. package/dist/src/lib/config-object-descriptor.js +658 -0
  168. package/dist/src/lib/config-object-descriptor.js.map +1 -0
  169. package/dist/src/lib/config-payload.d.ts +85 -0
  170. package/dist/src/lib/config-payload.js +116 -0
  171. package/dist/src/lib/config-payload.js.map +1 -0
  172. package/dist/src/lib/config-surface.d.ts +130 -0
  173. package/dist/src/lib/config-surface.js +300 -0
  174. package/dist/src/lib/config-surface.js.map +1 -0
  175. package/dist/src/lib/config-toml.d.ts +10 -0
  176. package/dist/src/lib/config-toml.js +42 -0
  177. package/dist/src/lib/config-toml.js.map +1 -0
  178. package/dist/src/lib/config.d.ts +53 -0
  179. package/dist/src/lib/config.js +92 -53
  180. package/dist/src/lib/config.js.map +1 -1
  181. package/dist/src/lib/confirm-prompt.d.ts +83 -0
  182. package/dist/src/lib/confirm-prompt.js +110 -0
  183. package/dist/src/lib/confirm-prompt.js.map +1 -0
  184. package/dist/src/lib/constants.d.ts +11 -0
  185. package/dist/src/lib/constants.js +12 -0
  186. package/dist/src/lib/constants.js.map +1 -0
  187. package/dist/src/lib/crash-handlers.d.ts +20 -0
  188. package/dist/src/lib/crash-handlers.js +49 -0
  189. package/dist/src/lib/crash-handlers.js.map +1 -0
  190. package/dist/src/lib/credentials-store.d.ts +89 -0
  191. package/dist/src/lib/credentials-store.js +330 -0
  192. package/dist/src/lib/credentials-store.js.map +1 -0
  193. package/dist/src/lib/csv.d.ts +47 -0
  194. package/dist/src/lib/csv.js +172 -0
  195. package/dist/src/lib/csv.js.map +1 -0
  196. package/dist/src/lib/data-input.d.ts +23 -0
  197. package/dist/src/lib/data-input.js +50 -0
  198. package/dist/src/lib/data-input.js.map +1 -0
  199. package/dist/src/lib/db-codegen/dbFingerprint.d.ts +10 -0
  200. package/dist/src/lib/db-codegen/dbFingerprint.js +17 -0
  201. package/dist/src/lib/db-codegen/dbFingerprint.js.map +1 -0
  202. package/dist/src/lib/db-codegen/dbGenerator.d.ts +67 -0
  203. package/dist/src/lib/db-codegen/dbGenerator.js +170 -0
  204. package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -0
  205. package/dist/src/lib/db-codegen/dbNaming.d.ts +87 -0
  206. package/dist/src/lib/db-codegen/dbNaming.js +180 -0
  207. package/dist/src/lib/db-codegen/dbNaming.js.map +1 -0
  208. package/dist/src/lib/db-codegen/dbTemplates.d.ts +272 -0
  209. package/dist/src/lib/db-codegen/dbTemplates.js +480 -0
  210. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -0
  211. package/dist/src/lib/db-codegen/dbTsTypes.d.ts +73 -0
  212. package/dist/src/lib/db-codegen/dbTsTypes.js +139 -0
  213. package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -0
  214. package/dist/src/lib/db-codegen/dbTypeIR.d.ts +146 -0
  215. package/dist/src/lib/db-codegen/dbTypeIR.js +517 -0
  216. package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -0
  217. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.d.ts +112 -0
  218. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js +211 -0
  219. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js.map +1 -0
  220. package/dist/src/lib/env-resolver-core.d.ts +147 -0
  221. package/dist/src/lib/env-resolver-core.js +265 -0
  222. package/dist/src/lib/env-resolver-core.js.map +1 -0
  223. package/dist/src/lib/env-resolver.d.ts +84 -0
  224. package/dist/src/lib/env-resolver.js +133 -0
  225. package/dist/src/lib/env-resolver.js.map +1 -0
  226. package/dist/src/lib/fetch.d.ts +5 -0
  227. package/dist/src/lib/generated-allowlist.d.ts +28 -0
  228. package/dist/src/lib/generated-allowlist.js +277 -0
  229. package/dist/src/lib/generated-allowlist.js.map +1 -0
  230. package/dist/src/lib/generated-config-surfaces.d.ts +682 -0
  231. package/dist/src/lib/generated-config-surfaces.js +4058 -0
  232. package/dist/src/lib/generated-config-surfaces.js.map +1 -0
  233. package/dist/src/lib/generated-template-lint.d.ts +212 -0
  234. package/dist/src/lib/generated-template-lint.js +624 -0
  235. package/dist/src/lib/generated-template-lint.js.map +1 -0
  236. package/dist/src/lib/init-adopt.d.ts +16 -0
  237. package/dist/src/lib/init-adopt.js +34 -0
  238. package/dist/src/lib/init-adopt.js.map +1 -0
  239. package/dist/src/lib/init-assets.d.ts +39 -0
  240. package/dist/src/lib/init-assets.js +97 -0
  241. package/dist/src/lib/init-assets.js.map +1 -0
  242. package/dist/src/lib/init-config.d.ts +98 -0
  243. package/dist/src/lib/init-config.js +186 -0
  244. package/dist/src/lib/init-config.js.map +1 -0
  245. package/dist/src/lib/init-ios-links.d.ts +50 -0
  246. package/dist/src/lib/init-ios-links.js +153 -0
  247. package/dist/src/lib/init-ios-links.js.map +1 -0
  248. package/dist/src/lib/init-plan.d.ts +80 -0
  249. package/dist/src/lib/init-plan.js +95 -0
  250. package/dist/src/lib/init-plan.js.map +1 -0
  251. package/dist/src/lib/init-production-env.d.ts +48 -0
  252. package/dist/src/lib/init-production-env.js +59 -0
  253. package/dist/src/lib/init-production-env.js.map +1 -0
  254. package/dist/src/lib/init-schema.d.ts +74 -0
  255. package/dist/src/lib/init-schema.js +358 -0
  256. package/dist/src/lib/init-schema.js.map +1 -0
  257. package/dist/src/lib/init-xcode.d.ts +33 -0
  258. package/dist/src/lib/init-xcode.js +114 -0
  259. package/dist/src/lib/init-xcode.js.map +1 -0
  260. package/dist/src/lib/integration-request-config.d.ts +30 -0
  261. package/dist/src/lib/integration-request-config.js +145 -0
  262. package/dist/src/lib/integration-request-config.js.map +1 -0
  263. package/dist/src/lib/local-state.d.ts +55 -0
  264. package/dist/src/lib/local-state.js +167 -0
  265. package/dist/src/lib/local-state.js.map +1 -0
  266. package/dist/src/lib/log-inspection.d.ts +568 -0
  267. package/dist/src/lib/log-inspection.js +639 -0
  268. package/dist/src/lib/log-inspection.js.map +1 -0
  269. package/dist/src/lib/migration-nag.d.ts +49 -0
  270. package/dist/src/lib/migration-nag.js +163 -0
  271. package/dist/src/lib/migration-nag.js.map +1 -0
  272. package/dist/src/lib/object-status-filter.d.ts +22 -0
  273. package/dist/src/lib/object-status-filter.js +45 -0
  274. package/dist/src/lib/object-status-filter.js.map +1 -0
  275. package/dist/src/lib/output.d.ts +109 -0
  276. package/dist/src/lib/output.js +191 -8
  277. package/dist/src/lib/output.js.map +1 -1
  278. package/dist/src/lib/package-manager.d.ts +140 -0
  279. package/dist/src/lib/package-manager.js +305 -0
  280. package/dist/src/lib/package-manager.js.map +1 -0
  281. package/dist/src/lib/paginate.d.ts +83 -0
  282. package/dist/src/lib/paginate.js +95 -0
  283. package/dist/src/lib/paginate.js.map +1 -0
  284. package/dist/src/lib/platform-owned.d.ts +63 -0
  285. package/dist/src/lib/platform-owned.js +85 -0
  286. package/dist/src/lib/platform-owned.js.map +1 -0
  287. package/dist/src/lib/project-config.d.ts +97 -0
  288. package/dist/src/lib/project-config.js +217 -0
  289. package/dist/src/lib/project-config.js.map +1 -0
  290. package/dist/src/lib/query-operators.d.ts +43 -0
  291. package/dist/src/lib/query-operators.js +80 -0
  292. package/dist/src/lib/query-operators.js.map +1 -0
  293. package/dist/src/lib/record-filter.d.ts +18 -0
  294. package/dist/src/lib/record-filter.js +55 -0
  295. package/dist/src/lib/record-filter.js.map +1 -0
  296. package/dist/src/lib/refresh-admin-credentials.d.ts +65 -0
  297. package/dist/src/lib/refresh-admin-credentials.js +103 -0
  298. package/dist/src/lib/refresh-admin-credentials.js.map +1 -0
  299. package/dist/src/lib/resolve-init-dev-port.d.ts +56 -0
  300. package/dist/src/lib/resolve-init-dev-port.js +55 -0
  301. package/dist/src/lib/resolve-init-dev-port.js.map +1 -0
  302. package/dist/src/lib/resolve-init-server.d.ts +64 -0
  303. package/dist/src/lib/resolve-init-server.js +77 -0
  304. package/dist/src/lib/resolve-init-server.js.map +1 -0
  305. package/dist/src/lib/resolve-platform.d.ts +74 -0
  306. package/dist/src/lib/resolve-platform.js +105 -0
  307. package/dist/src/lib/resolve-platform.js.map +1 -0
  308. package/dist/src/lib/run-status.d.ts +19 -0
  309. package/dist/src/lib/run-status.generated.d.ts +39 -0
  310. package/dist/src/lib/run-status.generated.js +66 -0
  311. package/dist/src/lib/run-status.generated.js.map +1 -0
  312. package/dist/src/lib/run-status.js +19 -0
  313. package/dist/src/lib/run-status.js.map +1 -0
  314. package/dist/src/lib/server-text-normalization.d.ts +51 -0
  315. package/dist/src/lib/server-text-normalization.js +90 -0
  316. package/dist/src/lib/server-text-normalization.js.map +1 -0
  317. package/dist/src/lib/server-url.d.ts +22 -0
  318. package/dist/src/lib/server-url.js +33 -0
  319. package/dist/src/lib/server-url.js.map +1 -0
  320. package/dist/src/lib/signing-secret-status.d.ts +81 -0
  321. package/dist/src/lib/signing-secret-status.js +116 -0
  322. package/dist/src/lib/signing-secret-status.js.map +1 -0
  323. package/dist/src/lib/skill-installer.d.ts +25 -0
  324. package/dist/src/lib/skill-installer.js +266 -0
  325. package/dist/src/lib/skill-installer.js.map +1 -0
  326. package/dist/src/lib/snapshots.d.ts +99 -0
  327. package/dist/src/lib/snapshots.js +357 -0
  328. package/dist/src/lib/snapshots.js.map +1 -0
  329. package/dist/src/lib/swift-codegen/dbGenerator.d.ts +113 -0
  330. package/dist/src/lib/swift-codegen/dbGenerator.js +914 -0
  331. package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -0
  332. package/dist/src/lib/swift-codegen/dbSwiftTypes.d.ts +42 -0
  333. package/dist/src/lib/swift-codegen/dbSwiftTypes.js +100 -0
  334. package/dist/src/lib/swift-codegen/dbSwiftTypes.js.map +1 -0
  335. package/dist/src/lib/swift-codegen/generator.d.ts +94 -0
  336. package/dist/src/lib/swift-codegen/generator.js +440 -0
  337. package/dist/src/lib/swift-codegen/generator.js.map +1 -0
  338. package/dist/src/lib/swift-codegen/schemaToSwift.d.ts +72 -0
  339. package/dist/src/lib/swift-codegen/schemaToSwift.js +644 -0
  340. package/dist/src/lib/swift-codegen/schemaToSwift.js.map +1 -0
  341. package/dist/src/lib/swift-codegen/siblingSymbols.d.ts +94 -0
  342. package/dist/src/lib/swift-codegen/siblingSymbols.js +155 -0
  343. package/dist/src/lib/swift-codegen/siblingSymbols.js.map +1 -0
  344. package/dist/src/lib/swift-codegen/swiftNaming.d.ts +85 -0
  345. package/dist/src/lib/swift-codegen/swiftNaming.js +198 -0
  346. package/dist/src/lib/swift-codegen/swiftNaming.js.map +1 -0
  347. package/dist/src/lib/sync-dir-selector.d.ts +21 -0
  348. package/dist/src/lib/sync-dir-selector.js +30 -0
  349. package/dist/src/lib/sync-dir-selector.js.map +1 -0
  350. package/dist/src/lib/sync-paths.d.ts +111 -0
  351. package/dist/src/lib/sync-paths.js +198 -0
  352. package/dist/src/lib/sync-paths.js.map +1 -0
  353. package/dist/src/lib/sync-resource-types.d.ts +544 -0
  354. package/dist/src/lib/sync-resource-types.js +975 -0
  355. package/dist/src/lib/sync-resource-types.js.map +1 -0
  356. package/dist/src/lib/sync-selectors.d.ts +95 -0
  357. package/dist/src/lib/sync-selectors.js +228 -0
  358. package/dist/src/lib/sync-selectors.js.map +1 -0
  359. package/dist/src/lib/template.d.ts +170 -0
  360. package/dist/src/lib/template.js +484 -68
  361. package/dist/src/lib/template.js.map +1 -1
  362. package/dist/src/lib/test-case-keys.d.ts +29 -0
  363. package/dist/src/lib/test-case-keys.js +55 -0
  364. package/dist/src/lib/test-case-keys.js.map +1 -0
  365. package/dist/src/lib/test-case-variables.d.ts +15 -0
  366. package/dist/src/lib/test-case-variables.js +29 -0
  367. package/dist/src/lib/test-case-variables.js.map +1 -0
  368. package/dist/src/lib/token-inject.d.ts +56 -0
  369. package/dist/src/lib/token-inject.js +204 -0
  370. package/dist/src/lib/token-inject.js.map +1 -0
  371. package/dist/src/lib/toml-database-config.d.ts +123 -0
  372. package/dist/src/lib/toml-database-config.js +527 -0
  373. package/dist/src/lib/toml-database-config.js.map +1 -0
  374. package/dist/src/lib/toml-metadata-config.d.ts +151 -0
  375. package/dist/src/lib/toml-metadata-config.js +476 -0
  376. package/dist/src/lib/toml-metadata-config.js.map +1 -0
  377. package/dist/src/lib/toml-native-form.d.ts +46 -0
  378. package/dist/src/lib/toml-native-form.js +78 -0
  379. package/dist/src/lib/toml-native-form.js.map +1 -0
  380. package/dist/src/lib/toml-params-validator.d.ts +129 -0
  381. package/dist/src/lib/toml-params-validator.js +298 -0
  382. package/dist/src/lib/toml-params-validator.js.map +1 -0
  383. package/dist/src/lib/toml-scalar-edit.d.ts +43 -0
  384. package/dist/src/lib/toml-scalar-edit.js +283 -0
  385. package/dist/src/lib/toml-scalar-edit.js.map +1 -0
  386. package/dist/src/lib/user-selector.d.ts +24 -0
  387. package/dist/src/lib/user-selector.js +33 -0
  388. package/dist/src/lib/user-selector.js.map +1 -0
  389. package/dist/src/lib/version-check.d.ts +35 -0
  390. package/dist/src/lib/version-check.js +241 -0
  391. package/dist/src/lib/version-check.js.map +1 -0
  392. package/dist/src/lib/watch.d.ts +121 -0
  393. package/dist/src/lib/watch.js +169 -0
  394. package/dist/src/lib/watch.js.map +1 -0
  395. package/dist/src/lib/workflow-apply.d.ts +110 -0
  396. package/dist/src/lib/workflow-apply.js +164 -0
  397. package/dist/src/lib/workflow-apply.js.map +1 -0
  398. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.d.ts +129 -0
  399. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js +269 -0
  400. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js.map +1 -0
  401. package/dist/src/lib/workflow-codegen/generator.d.ts +96 -0
  402. package/dist/src/lib/workflow-codegen/generator.js +361 -0
  403. package/dist/src/lib/workflow-codegen/generator.js.map +1 -0
  404. package/dist/src/lib/workflow-codegen/invokerIR.d.ts +94 -0
  405. package/dist/src/lib/workflow-codegen/invokerIR.js +76 -0
  406. package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -0
  407. package/dist/src/lib/workflow-codegen/naming.d.ts +33 -0
  408. package/dist/src/lib/workflow-codegen/naming.js +81 -0
  409. package/dist/src/lib/workflow-codegen/naming.js.map +1 -0
  410. package/dist/src/lib/workflow-codegen/schemaToTs.d.ts +80 -0
  411. package/dist/src/lib/workflow-codegen/schemaToTs.js +303 -0
  412. package/dist/src/lib/workflow-codegen/schemaToTs.js.map +1 -0
  413. package/dist/src/lib/workflow-config-apply.d.ts +70 -0
  414. package/dist/src/lib/workflow-config-apply.js +137 -0
  415. package/dist/src/lib/workflow-config-apply.js.map +1 -0
  416. package/dist/src/lib/workflow-config-sidecar.d.ts +63 -0
  417. package/dist/src/lib/workflow-config-sidecar.js +96 -0
  418. package/dist/src/lib/workflow-config-sidecar.js.map +1 -0
  419. package/dist/src/lib/workflow-defaults.d.ts +29 -0
  420. package/dist/src/lib/workflow-defaults.js +41 -0
  421. package/dist/src/lib/workflow-defaults.js.map +1 -0
  422. package/dist/src/lib/workflow-fragments.d.ts +64 -0
  423. package/dist/src/lib/workflow-fragments.js +342 -0
  424. package/dist/src/lib/workflow-fragments.js.map +1 -0
  425. package/dist/src/lib/workflow-include-preserve.d.ts +76 -0
  426. package/dist/src/lib/workflow-include-preserve.js +286 -0
  427. package/dist/src/lib/workflow-include-preserve.js.map +1 -0
  428. package/dist/src/lib/workflow-payload.d.ts +98 -0
  429. package/dist/src/lib/workflow-payload.js +178 -0
  430. package/dist/src/lib/workflow-payload.js.map +1 -0
  431. package/dist/src/lib/workflow-toml-validator.d.ts +202 -0
  432. package/dist/src/lib/workflow-toml-validator.js +757 -0
  433. package/dist/src/lib/workflow-toml-validator.js.map +1 -0
  434. package/dist/src/types/index.d.ts +581 -0
  435. package/dist/src/validators.d.ts +65 -0
  436. package/dist/src/validators.js +64 -0
  437. package/dist/src/validators.js.map +1 -0
  438. package/package.json +32 -8
@@ -1,11 +1,30 @@
1
- import { readFileSync, writeFileSync, statSync } from "fs";
2
- import { basename } from "path";
3
- import * as TOML from "@iarna/toml";
4
- import { lookup as mimeLookup } from "mime-types";
1
+ import { writeFileSync } from "fs";
5
2
  import { ApiClient } from "../lib/api-client.js";
6
3
  import { resolveAppId } from "../lib/config.js";
7
4
  import chalk from "chalk";
8
- import { success, error, info, warn, keyValue, formatTable, formatId, formatDate, formatStatus, formatDuration, json, divider, } from "../lib/output.js";
5
+ import { success, error, info, warn, keyValue, result as printResult, formatTable, formatId, formatDate, formatStatus, formatDuration, json, divider, progress, progressEnd, } from "../lib/output.js";
6
+ import { pollUntil } from "../lib/watch.js";
7
+ import { confirmPrompt } from "../lib/confirm-prompt.js";
8
+ import { parseStatusFilter } from "../lib/object-status-filter.js";
9
+ import { formatAccessRuleForDisplay } from "../lib/access-rule-display.js";
10
+ /**
11
+ * Resolve a prompt reference — its server id, or the author-chosen key its
12
+ * TOML file is named for.
13
+ *
14
+ * Under TOML-only authoring the key is what an operator has: `prompts/<key>.toml`
15
+ * is the object, and the id is a server detail they never wrote down. Mirrors
16
+ * `resolveScriptId` in `scripts.ts`.
17
+ */
18
+ async function resolvePromptId(client, appId, ref) {
19
+ const { items } = await client.listPrompts(appId, { limit: 100 });
20
+ const byId = (items ?? []).find((p) => p.promptId === ref);
21
+ if (byId)
22
+ return byId.promptId;
23
+ const byKey = (items ?? []).find((p) => p.promptKey === ref);
24
+ if (byKey)
25
+ return byKey.promptId;
26
+ throw new Error(`Prompt not found: ${ref}`);
27
+ }
9
28
  export function registerPromptsCommands(program) {
10
29
  const prompts = program
11
30
  .command("prompts")
@@ -13,9 +32,19 @@ export function registerPromptsCommands(program) {
13
32
  .addHelpText("after", `
14
33
  Examples:
15
34
  $ primitive prompts list
16
- $ primitive prompts create --from-file summarizer.toml
35
+ $ primitive config create prompt summarizer # scaffold prompts/summarizer.toml
36
+ $ primitive config push --only prompt/summarizer # apply it
17
37
  $ primitive prompts execute 01HXY... --vars '{"text":"Hello world"}'
18
- $ primitive prompts configs activate 01HXY... 01ABC...
38
+ $ primitive prompts configs list summarizer # what the server runs
39
+ `);
40
+ // Server-side configuration is authored in TOML and applied with `config push`
41
+ // (issue #2645) — this group no longer carries config-setting flags.
42
+ prompts.addHelpText("after", `
43
+ Configuration lives in prompts/<key>.toml:
44
+ $ primitive config fields prompt # the file's keys, types and defaults
45
+ $ primitive config create prompt <key> # scaffold one locally
46
+ $ primitive config set prompt/<key> <path>=<value>
47
+ $ primitive config push --only prompt/<key>
19
48
  `);
20
49
  // List prompts
21
50
  prompts
@@ -23,14 +52,22 @@ Examples:
23
52
  .description("List prompts")
24
53
  .argument("[app-id]", "App ID (uses current app if not specified)")
25
54
  .option("--app <app-id>", "App ID")
26
- .option("--status <status>", "Filter by status: draft, active, archived")
55
+ .option("--status <status>", "Filter by status: active, inactive, archived")
27
56
  .option("--json", "Output as JSON")
28
57
  .action(async (appId, options) => {
29
58
  const resolvedAppId = resolveAppId(appId, options);
30
59
  const client = new ApiClient();
31
60
  try {
61
+ // #2803 — `draft` is gone; refuse it by name rather than returning the
62
+ // empty list a canonical filter would produce. #2887 added `archived`:
63
+ // the delete flow writes one now, so asking for the retired rows is a
64
+ // question with an answer rather than a 400.
32
65
  const { items } = await client.listPrompts(resolvedAppId, {
33
- status: options.status,
66
+ status: parseStatusFilter("prompts", options.status, [
67
+ "active",
68
+ "inactive",
69
+ "archived",
70
+ ]),
34
71
  });
35
72
  if (options.json) {
36
73
  json(items);
@@ -53,209 +90,154 @@ Examples:
53
90
  process.exit(1);
54
91
  }
55
92
  });
56
- // Create prompt
93
+ // #2645 criterion 9 / #2803 — the operational verbs. They are the only
94
+ // writers of `status`, which is server-owned and no longer a TOML key.
57
95
  prompts
58
- .command("create")
59
- .description("Create a new prompt")
60
- .argument("[app-id]", "App ID (uses current app if not specified)")
61
- .option("--app <app-id>", "App ID")
62
- .option("--key <key>", "Prompt key (unique identifier)")
63
- .option("--name <name>", "Display name")
64
- .option("--description <desc>", "Description")
65
- .option("--provider <provider>", "LLM provider: openrouter, gemini", "openrouter")
66
- .option("--model <model>", "Model name")
67
- .option("--system-prompt <prompt>", "System prompt")
68
- .option("--user-template <template>", "User prompt template")
69
- .option("--temperature <temp>", "Temperature (0-1)")
70
- .option("--max-tokens <n>", "Max output tokens")
71
- .option("--output-format <format>", "Output format: text, json")
72
- .option("--input-schema <json>", "Input schema as JSON")
73
- .option("--output-schema <json>", "Output schema as JSON")
74
- .option("--from-file <path>", "Load prompt from TOML file")
96
+ .command("disable")
97
+ .description("Take prompt out of service now. Operational, not configuration: `status` is server-owned and 'config push' can neither set nor clear it.")
98
+ .argument("<prompt-id>", "Prompt ID")
99
+ .option("--app <app-id>", "App ID (uses current app if not specified)")
75
100
  .option("--json", "Output as JSON")
76
- .action(async (appId, options) => {
77
- const resolvedAppId = resolveAppId(appId, options);
101
+ .action(async (id, options) => {
102
+ const resolvedAppId = resolveAppId(undefined, options);
78
103
  const client = new ApiClient();
79
- let payload;
80
- if (options.fromFile) {
81
- try {
82
- const content = readFileSync(options.fromFile, "utf-8");
83
- const tomlData = TOML.parse(content);
84
- const prompt = tomlData.prompt || tomlData;
85
- const config = tomlData.configs?.[0] || {};
86
- payload = {
87
- promptKey: prompt.key || prompt.promptKey,
88
- displayName: prompt.displayName || prompt.name,
89
- description: prompt.description,
90
- provider: config.provider || prompt.provider || "openrouter",
91
- model: config.model || prompt.model,
92
- systemPrompt: config.systemPrompt || prompt.systemPrompt,
93
- userPromptTemplate: config.userPromptTemplate || prompt.userPromptTemplate,
94
- temperature: config.temperature || prompt.temperature,
95
- maxTokens: config.maxTokens || prompt.maxTokens,
96
- outputFormat: config.outputFormat || prompt.outputFormat,
97
- inputSchema: prompt.inputSchema,
98
- outputSchema: prompt.outputSchema,
99
- };
100
- }
101
- catch (err) {
102
- error(`Failed to read TOML file: ${err.message}`);
103
- process.exit(1);
104
- }
105
- }
106
- else {
107
- if (!options.key || !options.name || !options.model || !options.userTemplate) {
108
- error("Required: --key, --name, --model, --user-template (or use --from-file)");
109
- process.exit(1);
110
- }
111
- payload = {
112
- promptKey: options.key,
113
- displayName: options.name,
114
- description: options.description,
115
- provider: options.provider,
116
- model: options.model,
117
- systemPrompt: options.systemPrompt,
118
- userPromptTemplate: options.userTemplate,
119
- temperature: options.temperature,
120
- maxTokens: options.maxTokens ? parseInt(options.maxTokens) : undefined,
121
- outputFormat: options.outputFormat,
122
- inputSchema: options.inputSchema,
123
- outputSchema: options.outputSchema,
124
- };
125
- }
126
104
  try {
127
- const result = await client.createPrompt(resolvedAppId, payload);
105
+ const result = await client.setPromptAvailability(resolvedAppId, id, "disable");
128
106
  if (options.json) {
129
107
  json(result);
130
108
  return;
131
109
  }
132
- success(`Prompt created: ${result.displayName}`);
133
- keyValue("Prompt ID", result.promptId);
134
- keyValue("Key", result.promptKey);
110
+ success(`Prompt disabled: ${result.promptKey || id}`);
135
111
  }
136
112
  catch (err) {
137
113
  error(err.message);
138
114
  process.exit(1);
139
115
  }
140
116
  });
141
- // Get prompt
142
117
  prompts
143
- .command("get")
144
- .description("Get prompt details")
118
+ .command("enable")
119
+ .description("Put prompt back in service.")
145
120
  .argument("<prompt-id>", "Prompt ID")
146
121
  .option("--app <app-id>", "App ID (uses current app if not specified)")
147
122
  .option("--json", "Output as JSON")
148
- .action(async (promptId, options) => {
123
+ .action(async (id, options) => {
149
124
  const resolvedAppId = resolveAppId(undefined, options);
150
125
  const client = new ApiClient();
151
126
  try {
152
- const result = await client.getPrompt(resolvedAppId, promptId);
127
+ const result = await client.setPromptAvailability(resolvedAppId, id, "enable");
153
128
  if (options.json) {
154
129
  json(result);
155
130
  return;
156
131
  }
157
- keyValue("Prompt ID", result.promptId);
158
- keyValue("Key", result.promptKey);
159
- keyValue("Name", result.displayName);
160
- keyValue("Description", result.description);
161
- keyValue("Status", formatStatus(result.status));
162
- keyValue("Active Config", result.activeConfigId || "-");
163
- if (result.configs && result.configs.length > 0) {
164
- divider();
165
- info(`Configs (${result.configs.length}):`);
166
- console.log(formatTable(result.configs, [
167
- { header: "ID", key: "configId", format: formatId },
168
- { header: "NAME", key: "configName" },
169
- { header: "MODEL", key: "model" },
170
- { header: "STATUS", key: "status", format: formatStatus },
171
- ]));
172
- }
132
+ success(`Prompt enabled: ${result.promptKey || id}`);
173
133
  }
174
134
  catch (err) {
175
135
  error(err.message);
176
136
  process.exit(1);
177
137
  }
178
138
  });
179
- // Update prompt
139
+ // #2907 — the delete flow's CLI spelling. Not a second writer of `status`:
140
+ // this is the plain `DELETE` #2887 made retire-with-history, which until now
141
+ // only the admin API and the console could reach. Named `archive`, not
142
+ // `delete`, because a configuration noun gets no `delete` verb
143
+ // (docs/cli-design.md) and prune-by-push stays the only CLI hard-delete path.
180
144
  prompts
181
- .command("update")
182
- .description("Update a prompt")
145
+ .command("archive")
146
+ .description("Retire a prompt (soft delete). Delete-lifecycle, not `disable`: the row and its configs are kept so executions still resolve, it goes on holding its key, and there is no un-archive.")
183
147
  .argument("<prompt-id>", "Prompt ID")
184
148
  .option("--app <app-id>", "App ID (uses current app if not specified)")
185
- .option("--name <name>", "Display name")
186
- .option("--description <desc>", "Description")
187
- .option("--status <status>", "Status: draft, active, archived")
188
- .option("--input-schema <json>", "Input schema as JSON")
189
- .option("--output-schema <json>", "Output schema as JSON")
149
+ .option("-y, --yes", "Skip confirmation prompt")
190
150
  .option("--json", "Output as JSON")
191
- .action(async (promptId, options) => {
151
+ .addHelpText("after", `
152
+ What archiving a prompt costs (retire-with-history):
153
+ The member endpoint refuses it, and so does admin execution — the diagnostic
154
+ runs an INACTIVE prompt on purpose, but not an archived one.
155
+ Its configs, executions and analytics keep resolving what they point at.
156
+ The archived row still holds its promptKey.
157
+ \`primitive prompts enable\` refuses an archived prompt; there is no
158
+ un-archive.
159
+ The per-version \`[[configs]] status\` is a different key and is untouched.
160
+
161
+ Reclaiming the key means hard-deleting the row:
162
+ $ rm prompts/<key>.toml
163
+ $ primitive config push --prune # confirmed; hard-deletes the row
164
+ # or hard-delete it in the console, then re-add the file and push.
165
+ `)
166
+ .action(async (id, options) => {
192
167
  const resolvedAppId = resolveAppId(undefined, options);
193
- const payload = {};
194
- if (options.name)
195
- payload.displayName = options.name;
196
- if (options.description)
197
- payload.description = options.description;
198
- if (options.status)
199
- payload.status = options.status;
200
- if (options.inputSchema)
201
- payload.inputSchema = options.inputSchema;
202
- if (options.outputSchema)
203
- payload.outputSchema = options.outputSchema;
204
- if (Object.keys(payload).length === 0) {
205
- error("No update options specified.");
206
- process.exit(1);
168
+ if (!options.yes) {
169
+ let confirmed;
170
+ try {
171
+ confirmed = await confirmPrompt(`Archive prompt ${id}? It is retired, its configs and executions are ` +
172
+ `kept, it goes on holding its key, and there is no un-archive.`);
173
+ }
174
+ catch (err) {
175
+ error(err.message);
176
+ process.exit(1);
177
+ }
178
+ if (!confirmed) {
179
+ info("Cancelled.");
180
+ return;
181
+ }
207
182
  }
208
183
  const client = new ApiClient();
209
184
  try {
210
- const result = await client.updatePrompt(resolvedAppId, promptId, payload);
185
+ // The soft delete: no `hard`, so the configs and stored prompt bodies
186
+ // survive and every execution row keeps resolving.
187
+ const result = await client.deletePrompt(resolvedAppId, id);
211
188
  if (options.json) {
212
189
  json(result);
213
190
  return;
214
191
  }
215
- success("Prompt updated.");
192
+ success(`Prompt archived: ${id}`);
193
+ info("Its configs and executions keep resolving. The row still holds its key — reclaim it by removing its file and running a confirmed `primitive config push --prune`.");
216
194
  }
217
195
  catch (err) {
218
196
  error(err.message);
219
197
  process.exit(1);
220
198
  }
221
199
  });
222
- // Delete prompt
223
200
  prompts
224
- .command("delete")
225
- .description("Delete or archive a prompt")
201
+ .command("get")
202
+ .description("Get prompt details")
226
203
  .argument("<prompt-id>", "Prompt ID")
227
204
  .option("--app <app-id>", "App ID (uses current app if not specified)")
228
- .option("--hard", "Permanently delete instead of archive")
229
- .option("-y, --yes", "Skip confirmation prompt")
205
+ .option("--json", "Output as JSON")
230
206
  .action(async (promptId, options) => {
231
207
  const resolvedAppId = resolveAppId(undefined, options);
232
- if (!options.yes) {
233
- const action = options.hard ? "permanently delete" : "archive";
234
- const inquirer = await import("inquirer");
235
- const { confirm } = await inquirer.default.prompt([
236
- {
237
- type: "confirm",
238
- name: "confirm",
239
- message: `Are you sure you want to ${action} prompt ${promptId}?`,
240
- default: false,
241
- },
242
- ]);
243
- if (!confirm) {
244
- info("Cancelled.");
245
- return;
246
- }
247
- }
248
208
  const client = new ApiClient();
249
209
  try {
250
- await client.deletePrompt(resolvedAppId, promptId, options.hard);
251
- success(`Prompt ${options.hard ? "deleted" : "archived"}.`);
210
+ const result = await client.getPrompt(resolvedAppId, promptId);
211
+ if (options.json) {
212
+ json(result);
213
+ return;
214
+ }
215
+ printResult("Prompt ID", result.promptId);
216
+ printResult("Key", result.promptKey);
217
+ printResult("Name", result.displayName);
218
+ printResult("Description", result.description);
219
+ printResult("Status", formatStatus(result.status));
220
+ // #2652: no rule means every non-admin execution is denied. Print that
221
+ // in words — a blank or "-" would read as "unrestricted" and hide a
222
+ // prompt that was never given a rule.
223
+ printResult("Access Rule", formatAccessRuleForDisplay(result.accessRule, "prompt"));
224
+ printResult("Active Config", result.activeConfigId || "-");
225
+ if (result.configs && result.configs.length > 0) {
226
+ divider();
227
+ info(`Configs (${result.configs.length}):`);
228
+ console.log(formatTable(result.configs, [
229
+ { header: "ID", key: "configId", format: formatId },
230
+ { header: "NAME", key: "configName" },
231
+ { header: "MODEL", key: "model" },
232
+ { header: "STATUS", key: "status", format: formatStatus },
233
+ ]));
234
+ }
252
235
  }
253
236
  catch (err) {
254
237
  error(err.message);
255
238
  process.exit(1);
256
239
  }
257
240
  });
258
- // Execute prompt
259
241
  prompts
260
242
  .command("execute")
261
243
  .description("Execute a prompt with variables")
@@ -397,8 +379,25 @@ Examples:
397
379
  process.exit(1);
398
380
  }
399
381
  });
400
- // Configs subcommand
401
- const configs = prompts.command("configs").description("Manage prompt configs");
382
+ // Configs subcommand — reads only. A prompt's configs are `[[configs]]`
383
+ // entries in its own TOML file (#2645), so the verbs that used to write them
384
+ // were a second writer for state the file already owns.
385
+ const configs = prompts
386
+ .command("configs")
387
+ .description("Inspect a prompt's configs (authored in prompts/<key>.toml)")
388
+ .addHelpText("after", `
389
+ A prompt's configs are the \`[[configs]]\` entries of prompts/<key>.toml. Add,
390
+ edit, retire (\`status = "archived"\`) or mark one live (\`active = true\`) in that
391
+ file, then apply it:
392
+
393
+ $ primitive config push --only prompt/<key>
394
+
395
+ That per-config \`status\` says which VERSION is retired and is yours to author.
396
+ The PROMPT's own availability is server-owned and has no TOML key: use
397
+ 'primitive prompts disable' / 'enable'.
398
+
399
+ Duplicating a config is copying its \`[[configs]]\` block and renaming it.
400
+ `);
402
401
  // List configs
403
402
  configs
404
403
  .command("list")
@@ -432,154 +431,51 @@ Examples:
432
431
  process.exit(1);
433
432
  }
434
433
  });
435
- // Create config
434
+ // Get one config. `list` says which configs exist; this says what one of
435
+ // them actually runs — the body, provider and model the server would use.
436
+ // With `prompts configs create/update` gone, this is where that content is
437
+ // read back from.
436
438
  configs
437
- .command("create")
438
- .description("Create a new config for a prompt")
439
- .argument("<prompt-id>", "Prompt ID")
439
+ .command("get")
440
+ .description("Show one config of a prompt as the server has it")
441
+ .argument("<prompt>", "Prompt ID or key")
442
+ .argument("<config>", "Config ID or name")
440
443
  .option("--app <app-id>", "App ID (uses current app if not specified)")
441
- .option("--name <name>", "Config name")
442
- .option("--description <desc>", "Description")
443
- .option("--provider <provider>", "LLM provider: openrouter, gemini", "openrouter")
444
- .option("--model <model>", "Model name")
445
- .option("--system-prompt <prompt>", "System prompt")
446
- .option("--user-template <template>", "User prompt template")
447
- .option("--temperature <temp>", "Temperature (0-1)")
448
- .option("--max-tokens <n>", "Max output tokens")
449
- .option("--output-format <format>", "Output format: text, json")
450
444
  .option("--json", "Output as JSON")
451
- .action(async (promptId, options) => {
445
+ .action(async (prompt, config, options) => {
452
446
  const resolvedAppId = resolveAppId(undefined, options);
453
- if (!options.name || !options.model || !options.userTemplate) {
454
- error("Required: --name, --model, --user-template");
455
- process.exit(1);
456
- }
457
- const payload = {
458
- configName: options.name,
459
- description: options.description,
460
- provider: options.provider,
461
- model: options.model,
462
- systemPrompt: options.systemPrompt,
463
- userPromptTemplate: options.userTemplate,
464
- temperature: options.temperature,
465
- maxTokens: options.maxTokens ? parseInt(options.maxTokens) : undefined,
466
- outputFormat: options.outputFormat,
467
- };
468
447
  const client = new ApiClient();
469
448
  try {
470
- const result = await client.createPromptConfig(resolvedAppId, promptId, payload);
471
- if (options.json) {
472
- json(result);
473
- return;
449
+ const promptId = await resolvePromptId(client, resolvedAppId, prompt);
450
+ const { items } = await client.listPromptConfigs(resolvedAppId, promptId);
451
+ const summary = (items ?? []).find((c) => c.configId === config || c.configName === config);
452
+ if (!summary) {
453
+ error(`Prompt '${prompt}' has no config '${config}'. Run 'primitive prompts configs list ${prompt}' to see them.`);
454
+ process.exit(1);
474
455
  }
475
- success(`Config created: ${result.configName}`);
476
- keyValue("Config ID", result.configId);
477
- }
478
- catch (err) {
479
- error(err.message);
480
- process.exit(1);
481
- }
482
- });
483
- // Activate config
484
- configs
485
- .command("activate")
486
- .description("Set a config as the active config for a prompt")
487
- .argument("<prompt-id>", "Prompt ID")
488
- .argument("<config-id>", "Config ID")
489
- .option("--app <app-id>", "App ID (uses current app if not specified)")
490
- .action(async (promptId, configId, options) => {
491
- const resolvedAppId = resolveAppId(undefined, options);
492
- const client = new ApiClient();
493
- try {
494
- await client.activatePromptConfig(resolvedAppId, promptId, configId);
495
- success(`Config ${configId} activated.`);
496
- }
497
- catch (err) {
498
- error(err.message);
499
- process.exit(1);
500
- }
501
- });
502
- // Duplicate config
503
- configs
504
- .command("duplicate")
505
- .description("Duplicate a config")
506
- .argument("<prompt-id>", "Prompt ID")
507
- .argument("<config-id>", "Config ID to duplicate")
508
- .option("--app <app-id>", "App ID (uses current app if not specified)")
509
- .option("--name <name>", "New config name")
510
- .option("--json", "Output as JSON")
511
- .action(async (promptId, configId, options) => {
512
- const resolvedAppId = resolveAppId(undefined, options);
513
- const client = new ApiClient();
514
- try {
515
- const result = await client.duplicatePromptConfig(resolvedAppId, promptId, configId, {
516
- configName: options.name,
517
- });
456
+ const full = (await client.getPromptConfig(resolvedAppId, promptId, summary.configId)) ??
457
+ summary;
518
458
  if (options.json) {
519
- json(result);
459
+ json(full);
520
460
  return;
521
461
  }
522
- success(`Config duplicated: ${result.configName}`);
523
- keyValue("New Config ID", result.configId);
524
- }
525
- catch (err) {
526
- error(err.message);
527
- process.exit(1);
528
- }
529
- });
530
- // Update config
531
- configs
532
- .command("update")
533
- .description("Update a config")
534
- .argument("<prompt-id>", "Prompt ID")
535
- .argument("<config-id>", "Config ID")
536
- .option("--app <app-id>", "App ID (uses current app if not specified)")
537
- .option("--name <name>", "Config name")
538
- .option("--description <desc>", "Description")
539
- .option("--provider <provider>", "LLM provider: openrouter, gemini")
540
- .option("--model <model>", "Model name")
541
- .option("--system-prompt <prompt>", "System prompt")
542
- .option("--user-template <template>", "User prompt template")
543
- .option("--temperature <temp>", "Temperature (0-1)")
544
- .option("--max-tokens <n>", "Max output tokens")
545
- .option("--output-format <format>", "Output format: text, json")
546
- .option("--status <status>", "Status: active, archived")
547
- .option("--json", "Output as JSON")
548
- .action(async (promptId, configId, options) => {
549
- const resolvedAppId = resolveAppId(undefined, options);
550
- const payload = {};
551
- if (options.name)
552
- payload.configName = options.name;
553
- if (options.description)
554
- payload.description = options.description;
555
- if (options.provider)
556
- payload.provider = options.provider;
557
- if (options.model)
558
- payload.model = options.model;
559
- if (options.systemPrompt)
560
- payload.systemPrompt = options.systemPrompt;
561
- if (options.userTemplate)
562
- payload.userPromptTemplate = options.userTemplate;
563
- if (options.temperature)
564
- payload.temperature = options.temperature;
565
- if (options.maxTokens)
566
- payload.maxTokens = parseInt(options.maxTokens);
567
- if (options.outputFormat)
568
- payload.outputFormat = options.outputFormat;
569
- if (options.status)
570
- payload.status = options.status;
571
- if (Object.keys(payload).length === 0) {
572
- error("No update options specified.");
573
- process.exit(1);
574
- }
575
- const client = new ApiClient();
576
- try {
577
- const result = await client.updatePromptConfig(resolvedAppId, promptId, configId, payload);
578
- if (options.json) {
579
- json(result);
580
- return;
462
+ printResult("Config ID", full.configId);
463
+ printResult("Name", full.configName);
464
+ printResult("Status", formatStatus(full.status));
465
+ printResult("Provider", full.provider || "-");
466
+ printResult("Model", full.model || "-");
467
+ if (full.description)
468
+ printResult("Description", full.description);
469
+ if (full.systemPrompt) {
470
+ divider();
471
+ info("System prompt:");
472
+ console.log(full.systemPrompt);
473
+ }
474
+ if (full.userPrompt) {
475
+ divider();
476
+ info("User prompt:");
477
+ console.log(full.userPrompt);
581
478
  }
582
- success("Config updated.");
583
479
  }
584
480
  catch (err) {
585
481
  error(err.message);
@@ -591,11 +487,14 @@ Examples:
591
487
  // ============================================
592
488
  const tests = prompts
593
489
  .command("tests")
594
- .description("Manage and run prompt test cases")
490
+ .description("Run and inspect prompt test cases")
595
491
  .addHelpText("after", `
492
+ Test cases are authored in TOML at prompts/<key>.tests/<name>.toml and applied
493
+ with \`primitive config push\` (issue #2769); \`config fields prompt\` lists the
494
+ fields. Deleting a case is removing its file and running \`config push --prune\`.
495
+
596
496
  Examples:
597
497
  $ primitive prompts tests list <prompt-id>
598
- $ primitive prompts tests create <prompt-id> --name "Basic test" --vars '{"input":"hello"}'
599
498
  $ primitive prompts tests run <prompt-id> <test-case-id>
600
499
  $ primitive prompts tests run-all <prompt-id>
601
500
  $ primitive prompts tests run-all <prompt-id> --test-cases 01ABC,01DEF,01GHI
@@ -632,79 +531,6 @@ Examples:
632
531
  process.exit(1);
633
532
  }
634
533
  });
635
- // Create test case
636
- tests
637
- .command("create")
638
- .description("Create a test case for a prompt")
639
- .argument("<prompt-id>", "Prompt ID")
640
- .option("--app <app-id>", "App ID (uses current app if not specified)")
641
- .option("--name <name>", "Test case name (required)")
642
- .option("--vars <json>", "Input variables as JSON (required)")
643
- .option("--pattern <regex>", "Expected output pattern (regex)")
644
- .option("--contains <json>", "Expected strings to contain (JSON array)")
645
- .option("--json-subset <json>", "Expected JSON subset to match")
646
- .option("--config <config-id>", "Config ID to use for this test")
647
- .option("--evaluator-prompt <prompt-id>", "Evaluator prompt ID")
648
- .option("--evaluator-config <config-id>", "Evaluator config ID")
649
- .option("--json", "Output as JSON")
650
- .action(async (promptId, options) => {
651
- const resolvedAppId = resolveAppId(undefined, options);
652
- if (!options.name || !options.vars) {
653
- error("Required: --name and --vars");
654
- process.exit(1);
655
- }
656
- let inputVariables;
657
- try {
658
- inputVariables = JSON.parse(options.vars);
659
- }
660
- catch {
661
- error("Invalid JSON in --vars");
662
- process.exit(1);
663
- }
664
- let expectedOutputContains;
665
- if (options.contains) {
666
- try {
667
- expectedOutputContains = JSON.parse(options.contains);
668
- }
669
- catch {
670
- error("Invalid JSON in --contains");
671
- process.exit(1);
672
- }
673
- }
674
- let expectedJsonSubset;
675
- if (options.jsonSubset) {
676
- try {
677
- expectedJsonSubset = JSON.parse(options.jsonSubset);
678
- }
679
- catch {
680
- error("Invalid JSON in --json-subset");
681
- process.exit(1);
682
- }
683
- }
684
- const client = new ApiClient();
685
- try {
686
- const result = await client.createTestCase(resolvedAppId, "prompt", promptId, {
687
- name: options.name,
688
- inputVariables,
689
- expectedOutputPattern: options.pattern,
690
- expectedOutputContains,
691
- expectedJsonSubset,
692
- configId: options.config,
693
- evaluatorPromptId: options.evaluatorPrompt,
694
- evaluatorConfigId: options.evaluatorConfig,
695
- });
696
- if (options.json) {
697
- json(result);
698
- return;
699
- }
700
- success(`Test case created: ${result.name}`);
701
- keyValue("Test Case ID", result.testCaseId);
702
- }
703
- catch (err) {
704
- error(err.message);
705
- process.exit(1);
706
- }
707
- });
708
534
  // Get test case
709
535
  tests
710
536
  .command("get")
@@ -722,10 +548,10 @@ Examples:
722
548
  json(result);
723
549
  return;
724
550
  }
725
- keyValue("Test Case ID", result.testCaseId);
726
- keyValue("Name", result.name);
727
- keyValue("Config ID", result.configId || "-");
728
- keyValue("Created", formatDate(result.createdAt));
551
+ printResult("Test Case ID", result.testCaseId);
552
+ printResult("Name", result.name);
553
+ printResult("Config ID", result.configId || "-");
554
+ printResult("Created", formatDate(result.createdAt));
729
555
  divider();
730
556
  info("Input Variables:");
731
557
  try {
@@ -737,7 +563,7 @@ Examples:
737
563
  }
738
564
  if (result.expectedOutputPattern) {
739
565
  divider();
740
- keyValue("Expected Pattern", result.expectedOutputPattern);
566
+ printResult("Expected Pattern", result.expectedOutputPattern);
741
567
  }
742
568
  if (result.expectedOutputContains) {
743
569
  divider();
@@ -761,8 +587,8 @@ Examples:
761
587
  }
762
588
  if (result.evaluatorPromptId) {
763
589
  divider();
764
- keyValue("Evaluator Prompt", result.evaluatorPromptId);
765
- keyValue("Evaluator Config", result.evaluatorConfigId || "-");
590
+ printResult("Evaluator Prompt", result.evaluatorPromptId);
591
+ printResult("Evaluator Config", result.evaluatorConfigId || "-");
766
592
  }
767
593
  }
768
594
  catch (err) {
@@ -770,122 +596,6 @@ Examples:
770
596
  process.exit(1);
771
597
  }
772
598
  });
773
- // Update test case
774
- tests
775
- .command("update")
776
- .description("Update a test case")
777
- .argument("<prompt-id>", "Prompt ID")
778
- .argument("<test-case-id>", "Test Case ID")
779
- .option("--app <app-id>", "App ID (uses current app if not specified)")
780
- .option("--name <name>", "Test case name")
781
- .option("--vars <json>", "Input variables as JSON")
782
- .option("--pattern <regex>", "Expected output pattern (regex)")
783
- .option("--contains <json>", "Expected strings to contain (JSON array)")
784
- .option("--json-subset <json>", "Expected JSON subset to match")
785
- .option("--config <config-id>", "Config ID to use for this test")
786
- .option("--clear-pattern", "Clear expected pattern")
787
- .option("--clear-contains", "Clear expected contains")
788
- .option("--clear-json-subset", "Clear expected JSON subset")
789
- .option("--json", "Output as JSON")
790
- .action(async (promptId, testCaseId, options) => {
791
- const resolvedAppId = resolveAppId(undefined, options);
792
- const payload = {};
793
- if (options.name)
794
- payload.name = options.name;
795
- if (options.vars) {
796
- try {
797
- payload.inputVariables = JSON.parse(options.vars);
798
- }
799
- catch {
800
- error("Invalid JSON in --vars");
801
- process.exit(1);
802
- }
803
- }
804
- if (options.clearPattern) {
805
- payload.expectedOutputPattern = null;
806
- }
807
- else if (options.pattern) {
808
- payload.expectedOutputPattern = options.pattern;
809
- }
810
- if (options.clearContains) {
811
- payload.expectedOutputContains = null;
812
- }
813
- else if (options.contains) {
814
- try {
815
- payload.expectedOutputContains = JSON.parse(options.contains);
816
- }
817
- catch {
818
- error("Invalid JSON in --contains");
819
- process.exit(1);
820
- }
821
- }
822
- if (options.clearJsonSubset) {
823
- payload.expectedJsonSubset = null;
824
- }
825
- else if (options.jsonSubset) {
826
- try {
827
- payload.expectedJsonSubset = JSON.parse(options.jsonSubset);
828
- }
829
- catch {
830
- error("Invalid JSON in --json-subset");
831
- process.exit(1);
832
- }
833
- }
834
- if (options.config)
835
- payload.configId = options.config;
836
- if (Object.keys(payload).length === 0) {
837
- error("No update options specified.");
838
- process.exit(1);
839
- }
840
- const client = new ApiClient();
841
- try {
842
- const result = await client.updateTestCase(resolvedAppId, "prompt", promptId, testCaseId, payload);
843
- if (options.json) {
844
- json(result);
845
- return;
846
- }
847
- success("Test case updated.");
848
- }
849
- catch (err) {
850
- error(err.message);
851
- process.exit(1);
852
- }
853
- });
854
- // Delete test case
855
- tests
856
- .command("delete")
857
- .description("Delete a test case")
858
- .argument("<prompt-id>", "Prompt ID")
859
- .argument("<test-case-id>", "Test Case ID")
860
- .option("--app <app-id>", "App ID (uses current app if not specified)")
861
- .option("-y, --yes", "Skip confirmation prompt")
862
- .action(async (promptId, testCaseId, options) => {
863
- const resolvedAppId = resolveAppId(undefined, options);
864
- if (!options.yes) {
865
- const inquirer = await import("inquirer");
866
- const { confirm } = await inquirer.default.prompt([
867
- {
868
- type: "confirm",
869
- name: "confirm",
870
- message: `Are you sure you want to delete test case ${testCaseId}?`,
871
- default: false,
872
- },
873
- ]);
874
- if (!confirm) {
875
- info("Cancelled.");
876
- return;
877
- }
878
- }
879
- const client = new ApiClient();
880
- try {
881
- await client.deleteTestCase(resolvedAppId, "prompt", promptId, testCaseId);
882
- success("Test case deleted.");
883
- }
884
- catch (err) {
885
- error(err.message);
886
- process.exit(1);
887
- }
888
- });
889
599
  // Run a single test case
890
600
  tests
891
601
  .command("run")
@@ -1147,12 +857,21 @@ Examples:
1147
857
  let result = await fetchStatus();
1148
858
  if (options.wait && result.status === "running") {
1149
859
  info("Waiting for batch completion...");
1150
- while (result.status === "running") {
1151
- await new Promise((r) => setTimeout(r, 2000));
1152
- result = await fetchStatus();
1153
- process.stdout.write(`\r Completed: ${result.completed}/${result.results?.length || 0} `);
1154
- }
1155
- console.log();
860
+ // #1969 — shared poll primitive (delay-then-probe every 2s, honoring
861
+ // an AbortSignal). `onError: "throw"` preserves the prior loop's
862
+ // fail-fast behavior: a throwing fetchStatus (expired token, deleted
863
+ // batch → 401/404) propagates to the command-level catch below and
864
+ // exits non-zero, rather than spinning forever with a frozen progress
865
+ // line. (The follow/watch tail loops legitimately keep "continue".)
866
+ const controller = new AbortController();
867
+ result = await pollUntil(fetchStatus, (r) => r.status !== "running", {
868
+ intervalMs: 2000,
869
+ signal: controller.signal,
870
+ onError: "throw",
871
+ // Progress goes to stderr so it can't corrupt JSON on stdout under --json.
872
+ onValue: (r) => progress(` Completed: ${r.completed}/${r.results?.length || 0} `),
873
+ });
874
+ progressEnd();
1156
875
  }
1157
876
  if (options.json) {
1158
877
  json(result);
@@ -1196,15 +915,14 @@ Examples:
1196
915
  .action(async (promptId, batchId, options) => {
1197
916
  const resolvedAppId = resolveAppId(undefined, options);
1198
917
  if (!options.yes) {
1199
- const inquirer = await import("inquirer");
1200
- const { confirm } = await inquirer.default.prompt([
1201
- {
1202
- type: "confirm",
1203
- name: "confirm",
1204
- message: `Are you sure you want to cancel batch ${batchId}?`,
1205
- default: false,
1206
- },
1207
- ]);
918
+ let confirm;
919
+ try {
920
+ confirm = await confirmPrompt(`Are you sure you want to cancel batch ${batchId}?`);
921
+ }
922
+ catch (err) {
923
+ error(err.message);
924
+ process.exit(1);
925
+ }
1208
926
  if (!confirm) {
1209
927
  info("Cancelled.");
1210
928
  return;
@@ -1244,13 +962,15 @@ Examples:
1244
962
  // ============================================
1245
963
  const attachments = tests
1246
964
  .command("attachments")
1247
- .description("Manage test case file attachments")
965
+ .description("Inspect test case file attachments")
1248
966
  .addHelpText("after", `
967
+ Attachments are authored as files in prompts/<key>.tests/<name>/ and applied
968
+ with \`primitive config push\` (#2769); removing one and running
969
+ \`config push --prune\` deletes it server-side. These verbs read server state.
970
+
1249
971
  Examples:
1250
972
  $ primitive prompts tests attachments list <prompt-id> <test-case-id>
1251
- $ primitive prompts tests attachments upload <prompt-id> <test-case-id> ./document.pdf
1252
973
  $ primitive prompts tests attachments download <prompt-id> <test-case-id> document.pdf
1253
- $ primitive prompts tests attachments delete <prompt-id> <test-case-id> document.pdf
1254
974
  `);
1255
975
  // List attachments
1256
976
  attachments
@@ -1288,49 +1008,6 @@ Examples:
1288
1008
  process.exit(1);
1289
1009
  }
1290
1010
  });
1291
- // Upload attachment
1292
- attachments
1293
- .command("upload")
1294
- .description("Upload a file attachment to a test case")
1295
- .argument("<prompt-id>", "Prompt ID")
1296
- .argument("<test-case-id>", "Test Case ID")
1297
- .argument("<file-path>", "Path to the file to upload")
1298
- .option("--app <app-id>", "App ID (uses current app if not specified)")
1299
- .option("--name <name>", "Override the filename")
1300
- .option("--json", "Output as JSON")
1301
- .action(async (promptId, testCaseId, filePath, options) => {
1302
- const resolvedAppId = resolveAppId(undefined, options);
1303
- const client = new ApiClient();
1304
- try {
1305
- // Check file exists and get stats
1306
- const stats = statSync(filePath);
1307
- if (!stats.isFile()) {
1308
- error(`Not a file: ${filePath}`);
1309
- process.exit(1);
1310
- }
1311
- // Check file size (10 MB limit)
1312
- const TEN_MB = 10 * 1024 * 1024;
1313
- if (stats.size > TEN_MB) {
1314
- error(`File too large (max 10MB): ${formatFileSize(stats.size)}`);
1315
- process.exit(1);
1316
- }
1317
- const filename = options.name || basename(filePath);
1318
- const data = readFileSync(filePath);
1319
- const contentType = mimeLookup(filePath) || "application/octet-stream";
1320
- const result = await client.uploadTestCaseAttachment(resolvedAppId, "prompt", promptId, testCaseId, filename, data, contentType);
1321
- if (options.json) {
1322
- json(result);
1323
- return;
1324
- }
1325
- success(`Uploaded: ${filename}`);
1326
- keyValue("Size", formatFileSize(result.attachment.size));
1327
- keyValue("Type", result.attachment.contentType);
1328
- }
1329
- catch (err) {
1330
- error(err.message);
1331
- process.exit(1);
1332
- }
1333
- });
1334
1011
  // Download attachment
1335
1012
  attachments
1336
1013
  .command("download")
@@ -1356,42 +1033,6 @@ Examples:
1356
1033
  process.exit(1);
1357
1034
  }
1358
1035
  });
1359
- // Delete attachment
1360
- attachments
1361
- .command("delete")
1362
- .description("Delete a file attachment from a test case")
1363
- .argument("<prompt-id>", "Prompt ID")
1364
- .argument("<test-case-id>", "Test Case ID")
1365
- .argument("<filename>", "Name of the file to delete")
1366
- .option("--app <app-id>", "App ID (uses current app if not specified)")
1367
- .option("-y, --yes", "Skip confirmation prompt")
1368
- .action(async (promptId, testCaseId, filename, options) => {
1369
- const resolvedAppId = resolveAppId(undefined, options);
1370
- if (!options.yes) {
1371
- const inquirer = await import("inquirer");
1372
- const { confirm } = await inquirer.default.prompt([
1373
- {
1374
- type: "confirm",
1375
- name: "confirm",
1376
- message: `Are you sure you want to delete attachment "${filename}"?`,
1377
- default: false,
1378
- },
1379
- ]);
1380
- if (!confirm) {
1381
- info("Cancelled.");
1382
- return;
1383
- }
1384
- }
1385
- const client = new ApiClient();
1386
- try {
1387
- await client.deleteTestCaseAttachment(resolvedAppId, "prompt", promptId, testCaseId, filename);
1388
- success(`Deleted: ${filename}`);
1389
- }
1390
- catch (err) {
1391
- error(err.message);
1392
- process.exit(1);
1393
- }
1394
- });
1395
1036
  }
1396
1037
  function formatFileSize(bytes) {
1397
1038
  if (bytes < 1024)