primitive-admin 1.0.61 → 1.0.62

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 (300) hide show
  1. package/README.md +125 -69
  2. package/assets/skill/skills/primitive-platform/SKILL.md +336 -37
  3. package/dist/bin/primitive.js +25 -14
  4. package/dist/bin/primitive.js.map +1 -1
  5. package/dist/src/commands/analytics.js +36 -16
  6. package/dist/src/commands/analytics.js.map +1 -1
  7. package/dist/src/commands/apps.js +49 -188
  8. package/dist/src/commands/apps.js.map +1 -1
  9. package/dist/src/commands/blob-buckets.js +13 -153
  10. package/dist/src/commands/blob-buckets.js.map +1 -1
  11. package/dist/src/commands/collection-type-configs.js +14 -100
  12. package/dist/src/commands/collection-type-configs.js.map +1 -1
  13. package/dist/src/commands/collections.js +47 -8
  14. package/dist/src/commands/collections.js.map +1 -1
  15. package/dist/src/commands/config.d.ts +46 -0
  16. package/dist/src/commands/config.js +479 -0
  17. package/dist/src/commands/config.js.map +1 -0
  18. package/dist/src/commands/connections.js +12 -7
  19. package/dist/src/commands/connections.js.map +1 -1
  20. package/dist/src/commands/cron-triggers.js +84 -176
  21. package/dist/src/commands/cron-triggers.js.map +1 -1
  22. package/dist/src/commands/database-type-configs.d.ts +2 -0
  23. package/dist/src/commands/database-type-configs.js +171 -0
  24. package/dist/src/commands/database-type-configs.js.map +1 -0
  25. package/dist/src/commands/database-types.js +4 -4
  26. package/dist/src/commands/database-types.js.map +1 -1
  27. package/dist/src/commands/databases.d.ts +63 -0
  28. package/dist/src/commands/databases.js +649 -87
  29. package/dist/src/commands/databases.js.map +1 -1
  30. package/dist/src/commands/documents.js +869 -12
  31. package/dist/src/commands/documents.js.map +1 -1
  32. package/dist/src/commands/email-templates.js +18 -125
  33. package/dist/src/commands/email-templates.js.map +1 -1
  34. package/dist/src/commands/env.d.ts +12 -1
  35. package/dist/src/commands/env.js +98 -25
  36. package/dist/src/commands/env.js.map +1 -1
  37. package/dist/src/commands/feature-flags.d.ts +14 -0
  38. package/dist/src/commands/feature-flags.js +116 -0
  39. package/dist/src/commands/feature-flags.js.map +1 -0
  40. package/dist/src/commands/group-type-configs.js +14 -109
  41. package/dist/src/commands/group-type-configs.js.map +1 -1
  42. package/dist/src/commands/init.d.ts +9 -1
  43. package/dist/src/commands/init.js +1056 -277
  44. package/dist/src/commands/init.js.map +1 -1
  45. package/dist/src/commands/integrations.js +136 -421
  46. package/dist/src/commands/integrations.js.map +1 -1
  47. package/dist/src/commands/metadata-category-configs.d.ts +12 -0
  48. package/dist/src/commands/metadata-category-configs.js +112 -0
  49. package/dist/src/commands/metadata-category-configs.js.map +1 -0
  50. package/dist/src/commands/metadata.js +33 -141
  51. package/dist/src/commands/metadata.js.map +1 -1
  52. package/dist/src/commands/prompts.js +206 -563
  53. package/dist/src/commands/prompts.js.map +1 -1
  54. package/dist/src/commands/rule-sets.d.ts +1 -1
  55. package/dist/src/commands/rule-sets.js +16 -132
  56. package/dist/src/commands/rule-sets.js.map +1 -1
  57. package/dist/src/commands/scripts.js +67 -234
  58. package/dist/src/commands/scripts.js.map +1 -1
  59. package/dist/src/commands/sessions.js +17 -5
  60. package/dist/src/commands/sessions.js.map +1 -1
  61. package/dist/src/commands/sync-app-settings.d.ts +113 -70
  62. package/dist/src/commands/sync-app-settings.js +193 -242
  63. package/dist/src/commands/sync-app-settings.js.map +1 -1
  64. package/dist/src/commands/sync.d.ts +1932 -49
  65. package/dist/src/commands/sync.js +9521 -1769
  66. package/dist/src/commands/sync.js.map +1 -1
  67. package/dist/src/commands/tokens.js +8 -2
  68. package/dist/src/commands/tokens.js.map +1 -1
  69. package/dist/src/commands/users.js +92 -1
  70. package/dist/src/commands/users.js.map +1 -1
  71. package/dist/src/commands/vars.js +44 -58
  72. package/dist/src/commands/vars.js.map +1 -1
  73. package/dist/src/commands/webhooks.js +318 -156
  74. package/dist/src/commands/webhooks.js.map +1 -1
  75. package/dist/src/commands/workflows.d.ts +49 -12
  76. package/dist/src/commands/workflows.js +652 -1086
  77. package/dist/src/commands/workflows.js.map +1 -1
  78. package/dist/src/lib/access-rule-display.d.ts +21 -0
  79. package/dist/src/lib/access-rule-display.js +34 -0
  80. package/dist/src/lib/access-rule-display.js.map +1 -0
  81. package/dist/src/lib/api-client.d.ts +541 -61
  82. package/dist/src/lib/api-client.js +559 -102
  83. package/dist/src/lib/api-client.js.map +1 -1
  84. package/dist/src/lib/app-settings-descriptor.d.ts +180 -25
  85. package/dist/src/lib/app-settings-descriptor.js +403 -82
  86. package/dist/src/lib/app-settings-descriptor.js.map +1 -1
  87. package/dist/src/lib/canonical-json.d.ts +12 -0
  88. package/dist/src/lib/canonical-json.js +35 -0
  89. package/dist/src/lib/canonical-json.js.map +1 -0
  90. package/dist/src/lib/cli-manifest.d.ts +8 -0
  91. package/dist/src/lib/cli-manifest.js +1 -0
  92. package/dist/src/lib/cli-manifest.js.map +1 -1
  93. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +6 -2
  94. package/dist/src/lib/codegen-shared/generatedFiles.js +9 -2
  95. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -1
  96. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +6 -6
  97. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +8 -8
  98. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -1
  99. package/dist/src/lib/config-object-descriptor.d.ts +127 -0
  100. package/dist/src/lib/config-object-descriptor.js +658 -0
  101. package/dist/src/lib/config-object-descriptor.js.map +1 -0
  102. package/dist/src/lib/config-payload.d.ts +85 -0
  103. package/dist/src/lib/config-payload.js +116 -0
  104. package/dist/src/lib/config-payload.js.map +1 -0
  105. package/dist/src/lib/config-surface.d.ts +130 -0
  106. package/dist/src/lib/config-surface.js +300 -0
  107. package/dist/src/lib/config-surface.js.map +1 -0
  108. package/dist/src/lib/credentials-store.d.ts +10 -0
  109. package/dist/src/lib/credentials-store.js +23 -0
  110. package/dist/src/lib/credentials-store.js.map +1 -1
  111. package/dist/src/lib/data-input.d.ts +23 -0
  112. package/dist/src/lib/data-input.js +50 -0
  113. package/dist/src/lib/data-input.js.map +1 -0
  114. package/dist/src/lib/db-codegen/dbTemplates.js +1 -1
  115. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
  116. package/dist/src/lib/db-codegen/dbTypeIR.d.ts +1 -1
  117. package/dist/src/lib/env-resolver-core.d.ts +147 -0
  118. package/dist/src/lib/env-resolver-core.js +265 -0
  119. package/dist/src/lib/env-resolver-core.js.map +1 -0
  120. package/dist/src/lib/env-resolver.d.ts +28 -6
  121. package/dist/src/lib/env-resolver.js +44 -32
  122. package/dist/src/lib/env-resolver.js.map +1 -1
  123. package/dist/src/lib/generated-allowlist.js +12 -0
  124. package/dist/src/lib/generated-allowlist.js.map +1 -1
  125. package/dist/src/lib/generated-config-surfaces.d.ts +682 -0
  126. package/dist/src/lib/generated-config-surfaces.js +4058 -0
  127. package/dist/src/lib/generated-config-surfaces.js.map +1 -0
  128. package/dist/src/lib/generated-template-lint.d.ts +212 -0
  129. package/dist/src/lib/generated-template-lint.js +624 -0
  130. package/dist/src/lib/generated-template-lint.js.map +1 -0
  131. package/dist/src/lib/init-adopt.d.ts +16 -0
  132. package/dist/src/lib/init-adopt.js +34 -0
  133. package/dist/src/lib/init-adopt.js.map +1 -0
  134. package/dist/src/lib/init-assets.d.ts +39 -0
  135. package/dist/src/lib/init-assets.js +97 -0
  136. package/dist/src/lib/init-assets.js.map +1 -0
  137. package/dist/src/lib/init-config.d.ts +44 -5
  138. package/dist/src/lib/init-config.js +80 -7
  139. package/dist/src/lib/init-config.js.map +1 -1
  140. package/dist/src/lib/init-ios-links.d.ts +50 -0
  141. package/dist/src/lib/init-ios-links.js +153 -0
  142. package/dist/src/lib/init-ios-links.js.map +1 -0
  143. package/dist/src/lib/init-plan.d.ts +80 -0
  144. package/dist/src/lib/init-plan.js +95 -0
  145. package/dist/src/lib/init-plan.js.map +1 -0
  146. package/dist/src/lib/init-production-env.d.ts +48 -0
  147. package/dist/src/lib/init-production-env.js +59 -0
  148. package/dist/src/lib/init-production-env.js.map +1 -0
  149. package/dist/src/lib/init-schema.d.ts +74 -0
  150. package/dist/src/lib/init-schema.js +358 -0
  151. package/dist/src/lib/init-schema.js.map +1 -0
  152. package/dist/src/lib/init-xcode.d.ts +33 -0
  153. package/dist/src/lib/init-xcode.js +114 -0
  154. package/dist/src/lib/init-xcode.js.map +1 -0
  155. package/dist/src/lib/integration-request-config.d.ts +30 -0
  156. package/dist/src/lib/integration-request-config.js +145 -0
  157. package/dist/src/lib/integration-request-config.js.map +1 -0
  158. package/dist/src/lib/local-state.d.ts +55 -0
  159. package/dist/src/lib/local-state.js +167 -0
  160. package/dist/src/lib/local-state.js.map +1 -0
  161. package/dist/src/lib/log-inspection.d.ts +568 -0
  162. package/dist/src/lib/log-inspection.js +639 -0
  163. package/dist/src/lib/log-inspection.js.map +1 -0
  164. package/dist/src/lib/migration-nag.d.ts +2 -2
  165. package/dist/src/lib/migration-nag.js +3 -3
  166. package/dist/src/lib/object-status-filter.d.ts +22 -0
  167. package/dist/src/lib/object-status-filter.js +45 -0
  168. package/dist/src/lib/object-status-filter.js.map +1 -0
  169. package/dist/src/lib/output.d.ts +24 -1
  170. package/dist/src/lib/output.js +42 -1
  171. package/dist/src/lib/output.js.map +1 -1
  172. package/dist/src/lib/package-manager.d.ts +140 -0
  173. package/dist/src/lib/package-manager.js +305 -0
  174. package/dist/src/lib/package-manager.js.map +1 -0
  175. package/dist/src/lib/paginate.d.ts +50 -0
  176. package/dist/src/lib/paginate.js +53 -0
  177. package/dist/src/lib/paginate.js.map +1 -1
  178. package/dist/src/lib/platform-owned.d.ts +29 -3
  179. package/dist/src/lib/platform-owned.js +42 -3
  180. package/dist/src/lib/platform-owned.js.map +1 -1
  181. package/dist/src/lib/project-config.js +13 -5
  182. package/dist/src/lib/project-config.js.map +1 -1
  183. package/dist/src/lib/query-operators.d.ts +1 -1
  184. package/dist/src/lib/query-operators.js +1 -1
  185. package/dist/src/lib/record-filter.d.ts +18 -0
  186. package/dist/src/lib/record-filter.js +55 -0
  187. package/dist/src/lib/record-filter.js.map +1 -0
  188. package/dist/src/lib/resolve-init-dev-port.d.ts +56 -0
  189. package/dist/src/lib/resolve-init-dev-port.js +55 -0
  190. package/dist/src/lib/resolve-init-dev-port.js.map +1 -0
  191. package/dist/src/lib/resolve-init-server.d.ts +64 -0
  192. package/dist/src/lib/resolve-init-server.js +77 -0
  193. package/dist/src/lib/resolve-init-server.js.map +1 -0
  194. package/dist/src/lib/resolve-platform.d.ts +43 -14
  195. package/dist/src/lib/resolve-platform.js +74 -12
  196. package/dist/src/lib/resolve-platform.js.map +1 -1
  197. package/dist/src/lib/run-status.d.ts +19 -0
  198. package/dist/src/lib/run-status.generated.d.ts +39 -0
  199. package/dist/src/lib/run-status.generated.js +66 -0
  200. package/dist/src/lib/run-status.generated.js.map +1 -0
  201. package/dist/src/lib/run-status.js +19 -0
  202. package/dist/src/lib/run-status.js.map +1 -0
  203. package/dist/src/lib/server-text-normalization.d.ts +51 -0
  204. package/dist/src/lib/server-text-normalization.js +90 -0
  205. package/dist/src/lib/server-text-normalization.js.map +1 -0
  206. package/dist/src/lib/server-url.d.ts +22 -0
  207. package/dist/src/lib/server-url.js +33 -0
  208. package/dist/src/lib/server-url.js.map +1 -0
  209. package/dist/src/lib/signing-secret-status.d.ts +81 -0
  210. package/dist/src/lib/signing-secret-status.js +116 -0
  211. package/dist/src/lib/signing-secret-status.js.map +1 -0
  212. package/dist/src/lib/skill-installer.js +6 -3
  213. package/dist/src/lib/skill-installer.js.map +1 -1
  214. package/dist/src/lib/snapshots.d.ts +2 -2
  215. package/dist/src/lib/snapshots.js +2 -2
  216. package/dist/src/lib/swift-codegen/dbGenerator.d.ts +15 -2
  217. package/dist/src/lib/swift-codegen/dbGenerator.js +250 -18
  218. package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
  219. package/dist/src/lib/swift-codegen/generator.d.ts +11 -2
  220. package/dist/src/lib/swift-codegen/generator.js +181 -7
  221. package/dist/src/lib/swift-codegen/generator.js.map +1 -1
  222. package/dist/src/lib/swift-codegen/siblingSymbols.d.ts +7 -3
  223. package/dist/src/lib/swift-codegen/siblingSymbols.js +4 -1
  224. package/dist/src/lib/swift-codegen/siblingSymbols.js.map +1 -1
  225. package/dist/src/lib/sync-dir-selector.d.ts +21 -0
  226. package/dist/src/lib/sync-dir-selector.js +30 -0
  227. package/dist/src/lib/sync-dir-selector.js.map +1 -0
  228. package/dist/src/lib/sync-paths.d.ts +39 -0
  229. package/dist/src/lib/sync-paths.js +70 -2
  230. package/dist/src/lib/sync-paths.js.map +1 -1
  231. package/dist/src/lib/sync-resource-types.d.ts +138 -31
  232. package/dist/src/lib/sync-resource-types.js +241 -47
  233. package/dist/src/lib/sync-resource-types.js.map +1 -1
  234. package/dist/src/lib/sync-selectors.d.ts +95 -0
  235. package/dist/src/lib/sync-selectors.js +228 -0
  236. package/dist/src/lib/sync-selectors.js.map +1 -0
  237. package/dist/src/lib/template.d.ts +62 -4
  238. package/dist/src/lib/template.js +147 -39
  239. package/dist/src/lib/template.js.map +1 -1
  240. package/dist/src/lib/test-case-keys.d.ts +29 -0
  241. package/dist/src/lib/test-case-keys.js +55 -0
  242. package/dist/src/lib/test-case-keys.js.map +1 -0
  243. package/dist/src/lib/test-case-variables.d.ts +15 -0
  244. package/dist/src/lib/test-case-variables.js +29 -0
  245. package/dist/src/lib/test-case-variables.js.map +1 -0
  246. package/dist/src/lib/toml-database-config.d.ts +4 -4
  247. package/dist/src/lib/toml-database-config.js +27 -34
  248. package/dist/src/lib/toml-database-config.js.map +1 -1
  249. package/dist/src/lib/toml-metadata-config.d.ts +9 -5
  250. package/dist/src/lib/toml-metadata-config.js +40 -45
  251. package/dist/src/lib/toml-metadata-config.js.map +1 -1
  252. package/dist/src/lib/toml-native-form.d.ts +1 -1
  253. package/dist/src/lib/toml-native-form.js +1 -1
  254. package/dist/src/lib/toml-params-validator.d.ts +1 -1
  255. package/dist/src/lib/toml-params-validator.js +1 -1
  256. package/dist/src/lib/toml-scalar-edit.d.ts +43 -0
  257. package/dist/src/lib/toml-scalar-edit.js +283 -0
  258. package/dist/src/lib/toml-scalar-edit.js.map +1 -0
  259. package/dist/src/lib/user-selector.d.ts +24 -0
  260. package/dist/src/lib/user-selector.js +33 -0
  261. package/dist/src/lib/user-selector.js.map +1 -0
  262. package/dist/src/lib/version-check.d.ts +25 -0
  263. package/dist/src/lib/version-check.js +77 -8
  264. package/dist/src/lib/version-check.js.map +1 -1
  265. package/dist/src/lib/watch.d.ts +121 -0
  266. package/dist/src/lib/watch.js +169 -0
  267. package/dist/src/lib/watch.js.map +1 -0
  268. package/dist/src/lib/workflow-apply.d.ts +69 -45
  269. package/dist/src/lib/workflow-apply.js +99 -72
  270. package/dist/src/lib/workflow-apply.js.map +1 -1
  271. package/dist/src/lib/workflow-codegen/generator.js +4 -4
  272. package/dist/src/lib/workflow-codegen/generator.js.map +1 -1
  273. package/dist/src/lib/workflow-codegen/invokerIR.d.ts +11 -0
  274. package/dist/src/lib/workflow-codegen/invokerIR.js +5 -0
  275. package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -1
  276. package/dist/src/lib/workflow-config-apply.d.ts +70 -0
  277. package/dist/src/lib/workflow-config-apply.js +137 -0
  278. package/dist/src/lib/workflow-config-apply.js.map +1 -0
  279. package/dist/src/lib/workflow-config-sidecar.d.ts +63 -0
  280. package/dist/src/lib/workflow-config-sidecar.js +96 -0
  281. package/dist/src/lib/workflow-config-sidecar.js.map +1 -0
  282. package/dist/src/lib/workflow-defaults.d.ts +29 -0
  283. package/dist/src/lib/workflow-defaults.js +41 -0
  284. package/dist/src/lib/workflow-defaults.js.map +1 -0
  285. package/dist/src/lib/workflow-include-preserve.d.ts +76 -0
  286. package/dist/src/lib/workflow-include-preserve.js +286 -0
  287. package/dist/src/lib/workflow-include-preserve.js.map +1 -0
  288. package/dist/src/lib/workflow-payload.d.ts +31 -20
  289. package/dist/src/lib/workflow-payload.js +100 -110
  290. package/dist/src/lib/workflow-payload.js.map +1 -1
  291. package/dist/src/lib/workflow-toml-validator.d.ts +37 -16
  292. package/dist/src/lib/workflow-toml-validator.js +141 -16
  293. package/dist/src/lib/workflow-toml-validator.js.map +1 -1
  294. package/dist/src/types/index.d.ts +78 -22
  295. package/dist/src/validators.d.ts +7 -7
  296. package/dist/src/validators.js +6 -6
  297. package/package.json +11 -6
  298. package/dist/src/commands/settings.d.ts +0 -15
  299. package/dist/src/commands/settings.js +0 -102
  300. package/dist/src/commands/settings.js.map +0 -1
@@ -1,23 +1,35 @@
1
- import { readFileSync } from "fs";
2
- import { parseConfigToml } from "../lib/config-toml.js";
3
1
  import chalk from "chalk";
4
2
  import { ApiClient } from "../lib/api-client.js";
5
3
  import { resolveAppId } from "../lib/config.js";
6
- import { success, error, info, warn, keyValue, result as printResult, formatTable, formatId, formatDate, formatStatus, formatDuration, json, divider, } from "../lib/output.js";
4
+ import { success, error, info, keyValue, result as printResult, formatTable, formatId, formatDate, formatStatus, formatDuration, json, divider, } from "../lib/output.js";
5
+ import { buildIntegrationEnvelope } from "../lib/log-inspection.js";
7
6
  import { confirmPrompt } from "../lib/confirm-prompt.js";
7
+ import { parseStatusFilter } from "../lib/object-status-filter.js";
8
+ import { testCaseInputVariables } from "../lib/test-case-variables.js";
8
9
  export function registerIntegrationsCommands(program) {
9
10
  const integrations = program
10
11
  .command("integrations")
11
- .description("Configure external HTTP API connections and manage secrets")
12
+ .description("Configure external HTTP API connections")
12
13
  .addHelpText("after", `
13
14
  Examples:
14
15
  $ primitive integrations list
15
- $ primitive integrations create --from-file weather-api.toml
16
+ $ primitive config create integration weather-api # scaffold integrations/weather-api.toml
17
+ $ primitive config push --only integration/weather-api # apply it
16
18
  $ primitive integrations test 01HXY...
19
+ $ primitive integrations archive 01HXY... # retire it (soft delete)
17
20
 
18
- Storing secrets for an integration (integration secrets are deprecated):
21
+ Storing credentials for an integration:
19
22
  $ primitive secrets set MY_API_KEY --value <value>
20
23
  # then reference it in integration config via {{secrets.MY_API_KEY}}
24
+ `);
25
+ // Server-side configuration is authored in TOML and applied with `config push`
26
+ // (issue #2645) — this group no longer carries config-setting flags.
27
+ integrations.addHelpText("after", `
28
+ Configuration lives in integrations/<key>.toml:
29
+ $ primitive config fields integration # the file's keys, types and defaults
30
+ $ primitive config create integration <key> # scaffold one locally
31
+ $ primitive config set integration/<key> <path>=<value>
32
+ $ primitive config push --only integration/<key>
21
33
  `);
22
34
  // List integrations
23
35
  integrations
@@ -25,14 +37,20 @@ Storing secrets for an integration (integration secrets are deprecated):
25
37
  .description("List integrations")
26
38
  .argument("[app-id]", "App ID (uses current app if not specified)")
27
39
  .option("--app <app-id>", "App ID")
28
- .option("--status <status>", "Filter by status: draft, active, archived")
40
+ .option("--status <status>", "Filter by status: active, inactive, archived")
29
41
  .option("--json", "Output as JSON")
30
42
  .action(async (appId, options) => {
31
43
  const resolvedAppId = resolveAppId(appId, options);
32
44
  const client = new ApiClient();
33
45
  try {
46
+ // #2803 — `draft` is gone; refuse it by name rather than returning the
47
+ // empty list a canonical filter would produce.
34
48
  const { items } = await client.listIntegrations(resolvedAppId, {
35
- status: options.status,
49
+ status: parseStatusFilter("integrations", options.status, [
50
+ "active",
51
+ "inactive",
52
+ "archived",
53
+ ]),
36
54
  });
37
55
  if (options.json) {
38
56
  json(items);
@@ -55,75 +73,112 @@ Storing secrets for an integration (integration secrets are deprecated):
55
73
  process.exit(1);
56
74
  }
57
75
  });
58
- // Create integration
76
+ // #2645 criterion 9 / #2803 — the operational verbs. They are the only
77
+ // writers of `status`, which is server-owned and no longer a TOML key.
59
78
  integrations
60
- .command("create")
61
- .description("Create a new integration")
62
- .argument("[app-id]", "App ID (uses current app if not specified)")
63
- .option("--app <app-id>", "App ID")
64
- .option("--key <key>", "Integration key (unique identifier)")
65
- .option("--name <name>", "Display name")
66
- .option("--description <desc>", "Description")
67
- .option("--base-url <url>", "Base URL for API requests")
68
- .option("--allowed-methods <methods>", "Comma-separated list of allowed HTTP methods")
69
- .option("--allowed-paths <paths>", "Comma-separated list of allowed paths")
70
- .option("--timeout-ms <ms>", "Request timeout in milliseconds")
71
- .option("--from-file <path>", "Load integration from TOML file")
79
+ .command("disable")
80
+ .description("Take integration out of service now. Operational, not configuration: `status` is server-owned and 'config push' can neither set nor clear it.")
81
+ .argument("<integration-id>", "Integration ID")
82
+ .option("--app <app-id>", "App ID (uses current app if not specified)")
72
83
  .option("--json", "Output as JSON")
73
- .action(async (appId, options) => {
74
- const resolvedAppId = resolveAppId(appId, options);
84
+ .action(async (id, options) => {
85
+ const resolvedAppId = resolveAppId(undefined, options);
86
+ const client = new ApiClient();
87
+ try {
88
+ const result = await client.setIntegrationAvailability(resolvedAppId, id, "disable");
89
+ if (options.json) {
90
+ json(result);
91
+ return;
92
+ }
93
+ success(`Integration disabled: ${result.integrationKey || id}`);
94
+ }
95
+ catch (err) {
96
+ error(err.message);
97
+ process.exit(1);
98
+ }
99
+ });
100
+ integrations
101
+ .command("enable")
102
+ .description("Put integration back in service.")
103
+ .argument("<integration-id>", "Integration ID")
104
+ .option("--app <app-id>", "App ID (uses current app if not specified)")
105
+ .option("--json", "Output as JSON")
106
+ .action(async (id, options) => {
107
+ const resolvedAppId = resolveAppId(undefined, options);
75
108
  const client = new ApiClient();
76
- let payload;
77
- if (options.fromFile) {
109
+ try {
110
+ const result = await client.setIntegrationAvailability(resolvedAppId, id, "enable");
111
+ if (options.json) {
112
+ json(result);
113
+ return;
114
+ }
115
+ success(`Integration enabled: ${result.integrationKey || id}`);
116
+ }
117
+ catch (err) {
118
+ error(err.message);
119
+ process.exit(1);
120
+ }
121
+ });
122
+ // #2907 — the delete flow's CLI spelling. Not a second writer of `status`:
123
+ // this is the plain `DELETE` the server has always answered by writing the
124
+ // tombstone, which until now only the admin API and the console could reach.
125
+ // Named `archive`, not `delete`, because a configuration noun gets no
126
+ // `delete` verb (docs/cli-design.md) and prune-by-push stays the only CLI
127
+ // hard-delete path.
128
+ integrations
129
+ .command("archive")
130
+ .description("Retire an integration (soft delete). Delete-lifecycle, not `disable`: the row is kept, goes on holding its key, and there is no un-archive.")
131
+ .argument("<integration-id>", "Integration ID")
132
+ .option("--app <app-id>", "App ID (uses current app if not specified)")
133
+ .option("-y, --yes", "Skip confirmation prompt")
134
+ .option("--json", "Output as JSON")
135
+ .addHelpText("after", `
136
+ What archiving an integration costs:
137
+ The proxy refuses it, and so does every workflow step that calls it.
138
+ The archived row still holds its integrationKey, so a push cannot re-create
139
+ one under the same key.
140
+ \`primitive integrations enable\` refuses an archived integration; there is no
141
+ un-archive.
142
+
143
+ Reclaiming the key means hard-deleting the row:
144
+ $ rm integrations/<key>.toml
145
+ $ primitive config push --prune # confirmed; hard-deletes the row
146
+ # or hard-delete it in the console, then re-add the file and push.
147
+ `)
148
+ .action(async (id, options) => {
149
+ const resolvedAppId = resolveAppId(undefined, options);
150
+ if (!options.yes) {
151
+ let confirmed;
78
152
  try {
79
- const content = readFileSync(options.fromFile, "utf-8");
80
- const tomlData = parseConfigToml(content);
81
- payload = {
82
- integrationKey: tomlData.integration?.key || tomlData.key,
83
- displayName: tomlData.integration?.displayName || tomlData.displayName,
84
- description: tomlData.integration?.description || tomlData.description,
85
- requestConfig: tomlData.requestConfig || {},
86
- timeoutMs: tomlData.integration?.timeoutMs || tomlData.timeoutMs,
87
- };
153
+ confirmed = await confirmPrompt(`Archive integration ${id}? It is retired, goes on holding its key, ` +
154
+ `and there is no un-archive.`);
88
155
  }
89
156
  catch (err) {
90
- error(`Failed to read TOML file: ${err.message}`);
157
+ error(err.message);
91
158
  process.exit(1);
92
159
  }
93
- }
94
- else {
95
- if (!options.key || !options.name || !options.baseUrl) {
96
- error("Required: --key, --name, --base-url (or use --from-file)");
97
- process.exit(1);
160
+ if (!confirmed) {
161
+ info("Cancelled.");
162
+ return;
98
163
  }
99
- payload = {
100
- integrationKey: options.key,
101
- displayName: options.name,
102
- description: options.description,
103
- requestConfig: {
104
- baseUrl: options.baseUrl,
105
- allowedMethods: options.allowedMethods?.split(",").map((s) => s.trim()),
106
- allowedPaths: options.allowedPaths?.split(",").map((s) => s.trim()),
107
- },
108
- timeoutMs: options.timeoutMs ? parseInt(options.timeoutMs) : undefined,
109
- };
110
164
  }
165
+ const client = new ApiClient();
111
166
  try {
112
- const result = await client.createIntegration(resolvedAppId, payload);
167
+ // The soft delete: no `hard`, so the server writes the tombstone and
168
+ // keeps the row (and its canonical block mirror) in step.
169
+ const result = await client.deleteIntegration(resolvedAppId, id);
113
170
  if (options.json) {
114
171
  json(result);
115
172
  return;
116
173
  }
117
- success(`Integration created: ${result.displayName}`);
118
- keyValue("Integration ID", result.integrationId);
119
- keyValue("Key", result.integrationKey);
174
+ success(`Integration archived: ${id}`);
175
+ info("It is retired and still holds its key — reclaim the key by removing its file and running a confirmed `primitive config push --prune`.");
120
176
  }
121
177
  catch (err) {
122
178
  error(err.message);
123
179
  process.exit(1);
124
180
  }
125
181
  });
126
- // Get integration
127
182
  integrations
128
183
  .command("get")
129
184
  .description("Get integration details")
@@ -145,6 +200,12 @@ Storing secrets for an integration (integration secrets are deprecated):
145
200
  printResult("Description", result.description);
146
201
  printResult("Status", formatStatus(result.status));
147
202
  printResult("Timeout", result.timeoutMs ? `${result.timeoutMs}ms` : "-");
203
+ // #2631: no rule means every non-admin call is denied. Print that in
204
+ // words — a blank or "-" would read as "unrestricted" and hide an
205
+ // integration that was never given a rule.
206
+ printResult("Access Rule", typeof result.accessRule === "string" && result.accessRule !== ""
207
+ ? result.accessRule
208
+ : "(none — all non-admin calls are denied; set one to allow callers)");
148
209
  if (result.requestConfig) {
149
210
  divider();
150
211
  info("Request Config:");
@@ -167,82 +228,6 @@ Storing secrets for an integration (integration secrets are deprecated):
167
228
  process.exit(1);
168
229
  }
169
230
  });
170
- // Update integration
171
- integrations
172
- .command("update")
173
- .description("Update an integration")
174
- .argument("<integration-id>", "Integration ID")
175
- .option("--app <app-id>", "App ID (uses current app if not specified)")
176
- .option("--name <name>", "Display name")
177
- .option("--description <desc>", "Description")
178
- .option("--status <status>", "Status: draft, active, archived")
179
- .option("--timeout-ms <ms>", "Request timeout in milliseconds")
180
- .option("--json", "Output as JSON")
181
- .action(async (integrationId, options) => {
182
- const resolvedAppId = resolveAppId(undefined, options);
183
- const payload = {};
184
- if (options.name)
185
- payload.displayName = options.name;
186
- if (options.description)
187
- payload.description = options.description;
188
- if (options.status)
189
- payload.status = options.status;
190
- if (options.timeoutMs)
191
- payload.timeoutMs = parseInt(options.timeoutMs);
192
- if (Object.keys(payload).length === 0) {
193
- error("No update options specified.");
194
- process.exit(1);
195
- }
196
- const client = new ApiClient();
197
- try {
198
- const result = await client.updateIntegration(resolvedAppId, integrationId, payload);
199
- if (options.json) {
200
- json(result);
201
- return;
202
- }
203
- success("Integration updated.");
204
- }
205
- catch (err) {
206
- error(err.message);
207
- process.exit(1);
208
- }
209
- });
210
- // Delete integration
211
- integrations
212
- .command("delete")
213
- .description("Delete or archive an integration")
214
- .argument("<integration-id>", "Integration ID")
215
- .option("--app <app-id>", "App ID (uses current app if not specified)")
216
- .option("--hard", "Permanently delete instead of archive")
217
- .option("-y, --yes", "Skip confirmation prompt")
218
- .action(async (integrationId, options) => {
219
- const resolvedAppId = resolveAppId(undefined, options);
220
- if (!options.yes) {
221
- const action = options.hard ? "permanently delete" : "archive";
222
- let confirm;
223
- try {
224
- confirm = await confirmPrompt(`Are you sure you want to ${action} integration ${integrationId}?`);
225
- }
226
- catch (err) {
227
- error(err.message);
228
- process.exit(1);
229
- }
230
- if (!confirm) {
231
- info("Cancelled.");
232
- return;
233
- }
234
- }
235
- const client = new ApiClient();
236
- try {
237
- await client.deleteIntegration(resolvedAppId, integrationId, options.hard);
238
- success(`Integration ${options.hard ? "deleted" : "archived"}.`);
239
- }
240
- catch (err) {
241
- error(err.message);
242
- process.exit(1);
243
- }
244
- });
245
- // Test integration
246
231
  integrations
247
232
  .command("test")
248
233
  .description("Test an integration by making a request")
@@ -313,6 +298,9 @@ Storing secrets for an integration (integration secrets are deprecated):
313
298
  .option("--app <app-id>", "App ID (uses current app if not specified)")
314
299
  .option("--limit <n>", "Number of logs to show", "20")
315
300
  .option("--source <source>", "Filter by call source: user, admin, test, or workflow")
301
+ .option("--status <status>", "Filter by upstream HTTP status (e.g. 200, 500)")
302
+ .option("--from <iso>", "Only logs at or after this ISO timestamp")
303
+ .option("--to <iso>", "Only logs at or before this ISO timestamp")
316
304
  .option("--json", "Output as JSON")
317
305
  .action(async (integrationId, options) => {
318
306
  const resolvedAppId = resolveAppId(undefined, options);
@@ -326,9 +314,12 @@ Storing secrets for an integration (integration secrets are deprecated):
326
314
  const logs = await client.listIntegrationLogs(resolvedAppId, integrationId, {
327
315
  limit: parseInt(options.limit),
328
316
  ...(options.source && { source: options.source }),
317
+ ...(options.status && { status: options.status }),
318
+ ...(options.from && { from: options.from }),
319
+ ...(options.to && { to: options.to }),
329
320
  });
330
321
  if (options.json) {
331
- json(logs);
322
+ json(buildIntegrationEnvelope(logs));
332
323
  return;
333
324
  }
334
325
  if (!logs || logs.length === 0) {
@@ -352,100 +343,20 @@ Storing secrets for an integration (integration secrets are deprecated):
352
343
  process.exit(1);
353
344
  }
354
345
  });
355
- // Secrets subcommand
356
- //
357
- // Integration secrets are deprecated. New secrets can no longer be created
358
- // (the `add` subcommand below is hidden from help and the command manifest,
359
- // but kept registered so scripted callers still get a clear deprecation
360
- // error — see issue #1026). The modern path is App Secrets:
361
- // primitive secrets set <key> --value <value>
362
- // then reference them in integration config via {{secrets.KEY}}.
363
- const secrets = integrations
364
- .command("secrets")
365
- .description("Manage integration secrets (deprecated — use `primitive secrets set` and reference {{secrets.KEY}} in integration config instead)");
366
- // List secrets
367
- secrets
368
- .command("list")
369
- .description("List secrets for an integration")
370
- .argument("<integration-id>", "Integration ID")
371
- .option("--app <app-id>", "App ID (uses current app if not specified)")
372
- .option("--json", "Output as JSON")
373
- .action(async (integrationId, options) => {
374
- const resolvedAppId = resolveAppId(undefined, options);
375
- const client = new ApiClient();
376
- try {
377
- warn("Integration secrets are deprecated. Use App Secrets with {{secrets.KEY}} templates instead.");
378
- const secretsList = await client.listIntegrationSecrets(resolvedAppId, integrationId);
379
- if (options.json) {
380
- json(secretsList);
381
- return;
382
- }
383
- if (!secretsList || secretsList.length === 0) {
384
- info("No secrets found.");
385
- return;
386
- }
387
- console.log(formatTable(secretsList, [
388
- { header: "ID", key: "secretId", format: formatId },
389
- { header: "VERSION", key: "version" },
390
- { header: "SUMMARY", key: "secretSummary" },
391
- { header: "STATUS", key: "status", format: formatStatus },
392
- { header: "CREATED", key: "createdAt", format: formatDate },
393
- ]));
394
- }
395
- catch (err) {
396
- error(err.message);
397
- process.exit(1);
398
- }
399
- });
400
- // Add secret
401
- //
402
- // Deprecated and disabled: hidden from `--help` and the command manifest
403
- // (issue #1026) so the CLI no longer advertises a command that can never
404
- // succeed, but still registered so an explicit/scripted invocation gets the
405
- // clear deprecation error below rather than an "unknown command" error.
406
- secrets
407
- .command("add", { hidden: true })
408
- .description("Add a secret to an integration (deprecated — use `primitive secrets set`)")
409
- .argument("<integration-id>", "Integration ID")
410
- .option("--app <app-id>", "App ID (uses current app if not specified)")
411
- .option("--data <json>", "Secret data as JSON (e.g., '{\"apiKey\":\"...\"}' )")
412
- .option("--summary <text>", "Description of the secret")
413
- .option("--json", "Output as JSON")
414
- .action(async () => {
415
- error("Integration secrets are deprecated and can no longer be created.");
416
- info("Use App Secrets with {{secrets.KEY}} templates in defaultHeaders or staticQuery instead.");
417
- info("See: primitive secrets set <key> --value <value>");
418
- process.exit(1);
419
- });
420
- // Archive secret
421
- secrets
422
- .command("archive")
423
- .description("Archive a secret")
424
- .argument("<integration-id>", "Integration ID")
425
- .argument("<secret-id>", "Secret ID")
426
- .option("--app <app-id>", "App ID (uses current app if not specified)")
427
- .action(async (integrationId, secretId, options) => {
428
- const resolvedAppId = resolveAppId(undefined, options);
429
- const client = new ApiClient();
430
- try {
431
- await client.archiveIntegrationSecret(resolvedAppId, integrationId, secretId);
432
- success("Secret archived.");
433
- }
434
- catch (err) {
435
- error(err.message);
436
- process.exit(1);
437
- }
438
- });
439
346
  // ============================================
440
347
  // TESTS SUBCOMMAND
441
348
  // ============================================
442
349
  const tests = integrations
443
350
  .command("tests")
444
- .description("Manage and run integration test cases")
351
+ .description("Run and inspect integration test cases")
445
352
  .addHelpText("after", `
353
+ Test cases are authored in TOML at integrations/<key>.tests/<name>.toml and
354
+ applied with \`primitive config push\` (issue #2769); \`config fields integration\`
355
+ lists the fields. Deleting a case is removing its file and running
356
+ \`config push --prune\`.
357
+
446
358
  Examples:
447
359
  $ primitive integrations tests list <integration-id>
448
- $ primitive integrations tests create <integration-id> --name "Basic test" --input '{"method":"GET","path":"/get"}'
449
360
  $ primitive integrations tests run <integration-id> <test-case-id>
450
361
  $ primitive integrations tests run-all <integration-id>
451
362
  $ primitive integrations tests run-all <integration-id> --test-cases 01ABC,01DEF,01GHI
@@ -482,79 +393,6 @@ Examples:
482
393
  process.exit(1);
483
394
  }
484
395
  });
485
- // Create test case
486
- tests
487
- .command("create")
488
- .description("Create a test case for an integration")
489
- .argument("<integration-id>", "Integration ID")
490
- .option("--app <app-id>", "App ID (uses current app if not specified)")
491
- .option("--name <name>", "Test case name (required)")
492
- .option("--input <json>", "Input variables as JSON (required)")
493
- .option("--pattern <regex>", "Expected output pattern (regex)")
494
- .option("--contains <json>", "Expected strings to contain (JSON array)")
495
- .option("--json-subset <json>", "Expected JSON subset to match")
496
- .option("--config <config-id>", "Config ID to use for this test")
497
- .option("--evaluator-prompt <prompt-id>", "Evaluator prompt ID")
498
- .option("--evaluator-config <config-id>", "Evaluator config ID")
499
- .option("--json", "Output as JSON")
500
- .action(async (integrationId, options) => {
501
- const resolvedAppId = resolveAppId(undefined, options);
502
- if (!options.name || !options.input) {
503
- error("Required: --name and --input");
504
- process.exit(1);
505
- }
506
- let inputVariables;
507
- try {
508
- inputVariables = JSON.parse(options.input);
509
- }
510
- catch {
511
- error("Invalid JSON in --input");
512
- process.exit(1);
513
- }
514
- let expectedOutputContains;
515
- if (options.contains) {
516
- try {
517
- expectedOutputContains = JSON.parse(options.contains);
518
- }
519
- catch {
520
- error("Invalid JSON in --contains");
521
- process.exit(1);
522
- }
523
- }
524
- let expectedJsonSubset;
525
- if (options.jsonSubset) {
526
- try {
527
- expectedJsonSubset = JSON.parse(options.jsonSubset);
528
- }
529
- catch {
530
- error("Invalid JSON in --json-subset");
531
- process.exit(1);
532
- }
533
- }
534
- const client = new ApiClient();
535
- try {
536
- const result = await client.createTestCase(resolvedAppId, "integration", integrationId, {
537
- name: options.name,
538
- inputVariables,
539
- expectedOutputPattern: options.pattern,
540
- expectedOutputContains,
541
- expectedJsonSubset,
542
- configId: options.config,
543
- evaluatorPromptId: options.evaluatorPrompt,
544
- evaluatorConfigId: options.evaluatorConfig,
545
- });
546
- if (options.json) {
547
- json(result);
548
- return;
549
- }
550
- success(`Test case created: ${result.name}`);
551
- keyValue("Test Case ID", result.testCaseId);
552
- }
553
- catch (err) {
554
- error(err.message);
555
- process.exit(1);
556
- }
557
- });
558
396
  // Get test case
559
397
  tests
560
398
  .command("get")
@@ -578,13 +416,7 @@ Examples:
578
416
  printResult("Created", formatDate(result.createdAt));
579
417
  divider();
580
418
  info("Input Variables:");
581
- try {
582
- const vars = JSON.parse(result.inputVariables || "{}");
583
- console.log(JSON.stringify(vars, null, 2));
584
- }
585
- catch {
586
- console.log(result.inputVariables || "{}");
587
- }
419
+ console.log(JSON.stringify(testCaseInputVariables(result.inputVariables), null, 2));
588
420
  if (result.expectedOutputPattern) {
589
421
  divider();
590
422
  printResult("Expected Pattern", result.expectedOutputPattern);
@@ -620,121 +452,6 @@ Examples:
620
452
  process.exit(1);
621
453
  }
622
454
  });
623
- // Update test case
624
- tests
625
- .command("update")
626
- .description("Update a test case")
627
- .argument("<integration-id>", "Integration ID")
628
- .argument("<test-case-id>", "Test Case ID")
629
- .option("--app <app-id>", "App ID (uses current app if not specified)")
630
- .option("--name <name>", "Test case name")
631
- .option("--input <json>", "Input variables as JSON")
632
- .option("--pattern <regex>", "Expected output pattern (regex)")
633
- .option("--contains <json>", "Expected strings to contain (JSON array)")
634
- .option("--json-subset <json>", "Expected JSON subset to match")
635
- .option("--config <config-id>", "Config ID to use for this test")
636
- .option("--clear-pattern", "Clear expected pattern")
637
- .option("--clear-contains", "Clear expected contains")
638
- .option("--clear-json-subset", "Clear expected JSON subset")
639
- .option("--json", "Output as JSON")
640
- .action(async (integrationId, testCaseId, options) => {
641
- const resolvedAppId = resolveAppId(undefined, options);
642
- const payload = {};
643
- if (options.name)
644
- payload.name = options.name;
645
- if (options.input) {
646
- try {
647
- payload.inputVariables = JSON.parse(options.input);
648
- }
649
- catch {
650
- error("Invalid JSON in --input");
651
- process.exit(1);
652
- }
653
- }
654
- if (options.clearPattern) {
655
- payload.expectedOutputPattern = null;
656
- }
657
- else if (options.pattern) {
658
- payload.expectedOutputPattern = options.pattern;
659
- }
660
- if (options.clearContains) {
661
- payload.expectedOutputContains = null;
662
- }
663
- else if (options.contains) {
664
- try {
665
- payload.expectedOutputContains = JSON.parse(options.contains);
666
- }
667
- catch {
668
- error("Invalid JSON in --contains");
669
- process.exit(1);
670
- }
671
- }
672
- if (options.clearJsonSubset) {
673
- payload.expectedJsonSubset = null;
674
- }
675
- else if (options.jsonSubset) {
676
- try {
677
- payload.expectedJsonSubset = JSON.parse(options.jsonSubset);
678
- }
679
- catch {
680
- error("Invalid JSON in --json-subset");
681
- process.exit(1);
682
- }
683
- }
684
- if (options.config)
685
- payload.configId = options.config;
686
- if (Object.keys(payload).length === 0) {
687
- error("No update options specified.");
688
- process.exit(1);
689
- }
690
- const client = new ApiClient();
691
- try {
692
- const result = await client.updateTestCase(resolvedAppId, "integration", integrationId, testCaseId, payload);
693
- if (options.json) {
694
- json(result);
695
- return;
696
- }
697
- success("Test case updated.");
698
- }
699
- catch (err) {
700
- error(err.message);
701
- process.exit(1);
702
- }
703
- });
704
- // Delete test case
705
- tests
706
- .command("delete")
707
- .description("Delete a test case")
708
- .argument("<integration-id>", "Integration ID")
709
- .argument("<test-case-id>", "Test Case ID")
710
- .option("--app <app-id>", "App ID (uses current app if not specified)")
711
- .option("-y, --yes", "Skip confirmation prompt")
712
- .action(async (integrationId, testCaseId, options) => {
713
- const resolvedAppId = resolveAppId(undefined, options);
714
- if (!options.yes) {
715
- let confirm;
716
- try {
717
- confirm = await confirmPrompt(`Are you sure you want to delete test case ${testCaseId}?`);
718
- }
719
- catch (err) {
720
- error(err.message);
721
- process.exit(1);
722
- }
723
- if (!confirm) {
724
- info("Cancelled.");
725
- return;
726
- }
727
- }
728
- const client = new ApiClient();
729
- try {
730
- await client.deleteTestCase(resolvedAppId, "integration", integrationId, testCaseId);
731
- success("Test case deleted.");
732
- }
733
- catch (err) {
734
- error(err.message);
735
- process.exit(1);
736
- }
737
- });
738
455
  // Run a single test case
739
456
  tests
740
457
  .command("run")
@@ -750,13 +467,11 @@ Examples:
750
467
  try {
751
468
  // Get test case to retrieve its variables
752
469
  const testCase = await client.getTestCase(resolvedAppId, "integration", integrationId, testCaseId);
753
- let variables = {};
754
- try {
755
- variables = JSON.parse(testCase.inputVariables || "{}");
756
- }
757
- catch {
758
- // Use empty object if parsing fails
759
- }
470
+ // #2968 — the case's variables ARE the request the run issues, so a
471
+ // dropped `inputVariables` here is the config's bare defaults instead
472
+ // of the case's method, path and body. The server hands them back
473
+ // parsed; older servers hand back JSON text.
474
+ const variables = testCaseInputVariables(testCase.inputVariables);
760
475
  const result = await client.executeBlockTest(resolvedAppId, "integration", integrationId, {
761
476
  variables,
762
477
  testCaseId,