primitive-admin 1.0.60 → 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.
- package/README.md +125 -69
- package/assets/skill/skills/primitive-platform/SKILL.md +336 -37
- package/dist/bin/primitive.js +29 -14
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/admins.js +22 -8
- package/dist/src/commands/admins.js.map +1 -1
- package/dist/src/commands/analytics.js +96 -16
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/apps.js +49 -188
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/blob-buckets.js +50 -155
- package/dist/src/commands/blob-buckets.js.map +1 -1
- package/dist/src/commands/collection-type-configs.js +14 -100
- package/dist/src/commands/collection-type-configs.js.map +1 -1
- package/dist/src/commands/collections.js +47 -8
- package/dist/src/commands/collections.js.map +1 -1
- package/dist/src/commands/config.d.ts +46 -0
- package/dist/src/commands/config.js +479 -0
- package/dist/src/commands/config.js.map +1 -0
- package/dist/src/commands/connections.d.ts +2 -0
- package/dist/src/commands/connections.js +100 -0
- package/dist/src/commands/connections.js.map +1 -0
- package/dist/src/commands/cron-triggers.js +84 -176
- package/dist/src/commands/cron-triggers.js.map +1 -1
- package/dist/src/commands/database-type-configs.d.ts +2 -0
- package/dist/src/commands/database-type-configs.js +171 -0
- package/dist/src/commands/database-type-configs.js.map +1 -0
- package/dist/src/commands/database-types.js +4 -4
- package/dist/src/commands/database-types.js.map +1 -1
- package/dist/src/commands/databases.d.ts +63 -0
- package/dist/src/commands/databases.js +649 -87
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.js +869 -12
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/email-templates.js +18 -125
- package/dist/src/commands/email-templates.js.map +1 -1
- package/dist/src/commands/env.d.ts +12 -1
- package/dist/src/commands/env.js +98 -25
- package/dist/src/commands/env.js.map +1 -1
- package/dist/src/commands/feature-flags.d.ts +14 -0
- package/dist/src/commands/feature-flags.js +116 -0
- package/dist/src/commands/feature-flags.js.map +1 -0
- package/dist/src/commands/group-type-configs.js +14 -109
- package/dist/src/commands/group-type-configs.js.map +1 -1
- package/dist/src/commands/init.d.ts +9 -1
- package/dist/src/commands/init.js +1058 -279
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/integrations.js +136 -421
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/metadata-category-configs.d.ts +12 -0
- package/dist/src/commands/metadata-category-configs.js +112 -0
- package/dist/src/commands/metadata-category-configs.js.map +1 -0
- package/dist/src/commands/metadata.js +33 -141
- package/dist/src/commands/metadata.js.map +1 -1
- package/dist/src/commands/prompts.js +206 -563
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/rule-sets.d.ts +1 -1
- package/dist/src/commands/rule-sets.js +16 -132
- package/dist/src/commands/rule-sets.js.map +1 -1
- package/dist/src/commands/scripts.js +67 -234
- package/dist/src/commands/scripts.js.map +1 -1
- package/dist/src/commands/sessions.d.ts +2 -0
- package/dist/src/commands/sessions.js +75 -0
- package/dist/src/commands/sessions.js.map +1 -0
- package/dist/src/commands/skill.js +2 -2
- package/dist/src/commands/skill.js.map +1 -1
- package/dist/src/commands/sync-app-settings.d.ts +113 -70
- package/dist/src/commands/sync-app-settings.js +193 -242
- package/dist/src/commands/sync-app-settings.js.map +1 -1
- package/dist/src/commands/sync.d.ts +1932 -49
- package/dist/src/commands/sync.js +9584 -1725
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/tokens.js +8 -2
- package/dist/src/commands/tokens.js.map +1 -1
- package/dist/src/commands/users.js +92 -1
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/commands/vars.js +44 -58
- package/dist/src/commands/vars.js.map +1 -1
- package/dist/src/commands/webhooks.js +318 -156
- package/dist/src/commands/webhooks.js.map +1 -1
- package/dist/src/commands/workflows.d.ts +49 -12
- package/dist/src/commands/workflows.js +652 -1043
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/access-rule-display.d.ts +21 -0
- package/dist/src/lib/access-rule-display.js +34 -0
- package/dist/src/lib/access-rule-display.js.map +1 -0
- package/dist/src/lib/api-client.d.ts +630 -61
- package/dist/src/lib/api-client.js +676 -116
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/app-settings-descriptor.d.ts +180 -25
- package/dist/src/lib/app-settings-descriptor.js +403 -82
- package/dist/src/lib/app-settings-descriptor.js.map +1 -1
- package/dist/src/lib/canonical-json.d.ts +12 -0
- package/dist/src/lib/canonical-json.js +35 -0
- package/dist/src/lib/canonical-json.js.map +1 -0
- package/dist/src/lib/cli-manifest.d.ts +8 -0
- package/dist/src/lib/cli-manifest.js +1 -0
- package/dist/src/lib/cli-manifest.js.map +1 -1
- package/dist/src/lib/codegen-shared/generatedFiles.d.ts +6 -2
- package/dist/src/lib/codegen-shared/generatedFiles.js +9 -2
- package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -1
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +6 -6
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +8 -8
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -1
- package/dist/src/lib/config-object-descriptor.d.ts +127 -0
- package/dist/src/lib/config-object-descriptor.js +658 -0
- package/dist/src/lib/config-object-descriptor.js.map +1 -0
- package/dist/src/lib/config-payload.d.ts +85 -0
- package/dist/src/lib/config-payload.js +116 -0
- package/dist/src/lib/config-payload.js.map +1 -0
- package/dist/src/lib/config-surface.d.ts +130 -0
- package/dist/src/lib/config-surface.js +300 -0
- package/dist/src/lib/config-surface.js.map +1 -0
- package/dist/src/lib/credentials-store.d.ts +10 -0
- package/dist/src/lib/credentials-store.js +23 -0
- package/dist/src/lib/credentials-store.js.map +1 -1
- package/dist/src/lib/data-input.d.ts +23 -0
- package/dist/src/lib/data-input.js +50 -0
- package/dist/src/lib/data-input.js.map +1 -0
- package/dist/src/lib/db-codegen/dbTemplates.js +1 -1
- package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
- package/dist/src/lib/db-codegen/dbTypeIR.d.ts +1 -1
- package/dist/src/lib/env-resolver-core.d.ts +147 -0
- package/dist/src/lib/env-resolver-core.js +265 -0
- package/dist/src/lib/env-resolver-core.js.map +1 -0
- package/dist/src/lib/env-resolver.d.ts +28 -6
- package/dist/src/lib/env-resolver.js +44 -32
- package/dist/src/lib/env-resolver.js.map +1 -1
- package/dist/src/lib/generated-allowlist.js +13 -0
- package/dist/src/lib/generated-allowlist.js.map +1 -1
- package/dist/src/lib/generated-config-surfaces.d.ts +682 -0
- package/dist/src/lib/generated-config-surfaces.js +4058 -0
- package/dist/src/lib/generated-config-surfaces.js.map +1 -0
- package/dist/src/lib/generated-template-lint.d.ts +212 -0
- package/dist/src/lib/generated-template-lint.js +624 -0
- package/dist/src/lib/generated-template-lint.js.map +1 -0
- package/dist/src/lib/init-adopt.d.ts +16 -0
- package/dist/src/lib/init-adopt.js +34 -0
- package/dist/src/lib/init-adopt.js.map +1 -0
- package/dist/src/lib/init-assets.d.ts +39 -0
- package/dist/src/lib/init-assets.js +97 -0
- package/dist/src/lib/init-assets.js.map +1 -0
- package/dist/src/lib/init-config.d.ts +44 -5
- package/dist/src/lib/init-config.js +80 -7
- package/dist/src/lib/init-config.js.map +1 -1
- package/dist/src/lib/init-ios-links.d.ts +50 -0
- package/dist/src/lib/init-ios-links.js +153 -0
- package/dist/src/lib/init-ios-links.js.map +1 -0
- package/dist/src/lib/init-plan.d.ts +80 -0
- package/dist/src/lib/init-plan.js +95 -0
- package/dist/src/lib/init-plan.js.map +1 -0
- package/dist/src/lib/init-production-env.d.ts +48 -0
- package/dist/src/lib/init-production-env.js +59 -0
- package/dist/src/lib/init-production-env.js.map +1 -0
- package/dist/src/lib/init-schema.d.ts +74 -0
- package/dist/src/lib/init-schema.js +358 -0
- package/dist/src/lib/init-schema.js.map +1 -0
- package/dist/src/lib/init-xcode.d.ts +33 -0
- package/dist/src/lib/init-xcode.js +114 -0
- package/dist/src/lib/init-xcode.js.map +1 -0
- package/dist/src/lib/integration-request-config.d.ts +30 -0
- package/dist/src/lib/integration-request-config.js +145 -0
- package/dist/src/lib/integration-request-config.js.map +1 -0
- package/dist/src/lib/local-state.d.ts +55 -0
- package/dist/src/lib/local-state.js +167 -0
- package/dist/src/lib/local-state.js.map +1 -0
- package/dist/src/lib/log-inspection.d.ts +568 -0
- package/dist/src/lib/log-inspection.js +639 -0
- package/dist/src/lib/log-inspection.js.map +1 -0
- package/dist/src/lib/migration-nag.d.ts +2 -2
- package/dist/src/lib/migration-nag.js +3 -3
- package/dist/src/lib/object-status-filter.d.ts +22 -0
- package/dist/src/lib/object-status-filter.js +45 -0
- package/dist/src/lib/object-status-filter.js.map +1 -0
- package/dist/src/lib/output.d.ts +24 -1
- package/dist/src/lib/output.js +42 -1
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/package-manager.d.ts +140 -0
- package/dist/src/lib/package-manager.js +305 -0
- package/dist/src/lib/package-manager.js.map +1 -0
- package/dist/src/lib/paginate.d.ts +50 -0
- package/dist/src/lib/paginate.js +53 -0
- package/dist/src/lib/paginate.js.map +1 -1
- package/dist/src/lib/platform-owned.d.ts +29 -3
- package/dist/src/lib/platform-owned.js +42 -3
- package/dist/src/lib/platform-owned.js.map +1 -1
- package/dist/src/lib/project-config.js +13 -5
- package/dist/src/lib/project-config.js.map +1 -1
- package/dist/src/lib/query-operators.d.ts +1 -1
- package/dist/src/lib/query-operators.js +1 -1
- package/dist/src/lib/record-filter.d.ts +18 -0
- package/dist/src/lib/record-filter.js +55 -0
- package/dist/src/lib/record-filter.js.map +1 -0
- package/dist/src/lib/resolve-init-dev-port.d.ts +56 -0
- package/dist/src/lib/resolve-init-dev-port.js +55 -0
- package/dist/src/lib/resolve-init-dev-port.js.map +1 -0
- package/dist/src/lib/resolve-init-server.d.ts +64 -0
- package/dist/src/lib/resolve-init-server.js +77 -0
- package/dist/src/lib/resolve-init-server.js.map +1 -0
- package/dist/src/lib/resolve-platform.d.ts +43 -14
- package/dist/src/lib/resolve-platform.js +74 -12
- package/dist/src/lib/resolve-platform.js.map +1 -1
- package/dist/src/lib/run-status.d.ts +19 -0
- package/dist/src/lib/run-status.generated.d.ts +39 -0
- package/dist/src/lib/run-status.generated.js +66 -0
- package/dist/src/lib/run-status.generated.js.map +1 -0
- package/dist/src/lib/run-status.js +19 -0
- package/dist/src/lib/run-status.js.map +1 -0
- package/dist/src/lib/server-text-normalization.d.ts +51 -0
- package/dist/src/lib/server-text-normalization.js +90 -0
- package/dist/src/lib/server-text-normalization.js.map +1 -0
- package/dist/src/lib/server-url.d.ts +22 -0
- package/dist/src/lib/server-url.js +33 -0
- package/dist/src/lib/server-url.js.map +1 -0
- package/dist/src/lib/signing-secret-status.d.ts +81 -0
- package/dist/src/lib/signing-secret-status.js +116 -0
- package/dist/src/lib/signing-secret-status.js.map +1 -0
- package/dist/src/lib/skill-installer.d.ts +4 -2
- package/dist/src/lib/skill-installer.js +143 -12
- package/dist/src/lib/skill-installer.js.map +1 -1
- package/dist/src/lib/snapshots.d.ts +2 -2
- package/dist/src/lib/snapshots.js +2 -2
- package/dist/src/lib/swift-codegen/dbGenerator.d.ts +15 -2
- package/dist/src/lib/swift-codegen/dbGenerator.js +250 -18
- package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/generator.d.ts +11 -2
- package/dist/src/lib/swift-codegen/generator.js +181 -7
- package/dist/src/lib/swift-codegen/generator.js.map +1 -1
- package/dist/src/lib/swift-codegen/siblingSymbols.d.ts +7 -3
- package/dist/src/lib/swift-codegen/siblingSymbols.js +4 -1
- package/dist/src/lib/swift-codegen/siblingSymbols.js.map +1 -1
- package/dist/src/lib/sync-dir-selector.d.ts +21 -0
- package/dist/src/lib/sync-dir-selector.js +30 -0
- package/dist/src/lib/sync-dir-selector.js.map +1 -0
- package/dist/src/lib/sync-paths.d.ts +39 -0
- package/dist/src/lib/sync-paths.js +70 -2
- package/dist/src/lib/sync-paths.js.map +1 -1
- package/dist/src/lib/sync-resource-types.d.ts +138 -31
- package/dist/src/lib/sync-resource-types.js +241 -47
- package/dist/src/lib/sync-resource-types.js.map +1 -1
- package/dist/src/lib/sync-selectors.d.ts +95 -0
- package/dist/src/lib/sync-selectors.js +228 -0
- package/dist/src/lib/sync-selectors.js.map +1 -0
- package/dist/src/lib/template.d.ts +62 -4
- package/dist/src/lib/template.js +147 -39
- package/dist/src/lib/template.js.map +1 -1
- package/dist/src/lib/test-case-keys.d.ts +29 -0
- package/dist/src/lib/test-case-keys.js +55 -0
- package/dist/src/lib/test-case-keys.js.map +1 -0
- package/dist/src/lib/test-case-variables.d.ts +15 -0
- package/dist/src/lib/test-case-variables.js +29 -0
- package/dist/src/lib/test-case-variables.js.map +1 -0
- package/dist/src/lib/toml-database-config.d.ts +4 -4
- package/dist/src/lib/toml-database-config.js +27 -34
- package/dist/src/lib/toml-database-config.js.map +1 -1
- package/dist/src/lib/toml-metadata-config.d.ts +9 -5
- package/dist/src/lib/toml-metadata-config.js +40 -45
- package/dist/src/lib/toml-metadata-config.js.map +1 -1
- package/dist/src/lib/toml-native-form.d.ts +1 -1
- package/dist/src/lib/toml-native-form.js +1 -1
- package/dist/src/lib/toml-params-validator.d.ts +1 -1
- package/dist/src/lib/toml-params-validator.js +1 -1
- package/dist/src/lib/toml-scalar-edit.d.ts +43 -0
- package/dist/src/lib/toml-scalar-edit.js +283 -0
- package/dist/src/lib/toml-scalar-edit.js.map +1 -0
- package/dist/src/lib/user-selector.d.ts +24 -0
- package/dist/src/lib/user-selector.js +33 -0
- package/dist/src/lib/user-selector.js.map +1 -0
- package/dist/src/lib/version-check.d.ts +25 -0
- package/dist/src/lib/version-check.js +77 -8
- package/dist/src/lib/version-check.js.map +1 -1
- package/dist/src/lib/watch.d.ts +121 -0
- package/dist/src/lib/watch.js +169 -0
- package/dist/src/lib/watch.js.map +1 -0
- package/dist/src/lib/workflow-apply.d.ts +69 -45
- package/dist/src/lib/workflow-apply.js +99 -72
- package/dist/src/lib/workflow-apply.js.map +1 -1
- package/dist/src/lib/workflow-codegen/generator.js +4 -4
- package/dist/src/lib/workflow-codegen/generator.js.map +1 -1
- package/dist/src/lib/workflow-codegen/invokerIR.d.ts +11 -0
- package/dist/src/lib/workflow-codegen/invokerIR.js +5 -0
- package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -1
- package/dist/src/lib/workflow-config-apply.d.ts +70 -0
- package/dist/src/lib/workflow-config-apply.js +137 -0
- package/dist/src/lib/workflow-config-apply.js.map +1 -0
- package/dist/src/lib/workflow-config-sidecar.d.ts +63 -0
- package/dist/src/lib/workflow-config-sidecar.js +96 -0
- package/dist/src/lib/workflow-config-sidecar.js.map +1 -0
- package/dist/src/lib/workflow-defaults.d.ts +29 -0
- package/dist/src/lib/workflow-defaults.js +41 -0
- package/dist/src/lib/workflow-defaults.js.map +1 -0
- package/dist/src/lib/workflow-fragments.d.ts +23 -0
- package/dist/src/lib/workflow-fragments.js +229 -8
- package/dist/src/lib/workflow-fragments.js.map +1 -1
- package/dist/src/lib/workflow-include-preserve.d.ts +76 -0
- package/dist/src/lib/workflow-include-preserve.js +286 -0
- package/dist/src/lib/workflow-include-preserve.js.map +1 -0
- package/dist/src/lib/workflow-payload.d.ts +31 -20
- package/dist/src/lib/workflow-payload.js +100 -93
- package/dist/src/lib/workflow-payload.js.map +1 -1
- package/dist/src/lib/workflow-toml-validator.d.ts +80 -3
- package/dist/src/lib/workflow-toml-validator.js +231 -3
- package/dist/src/lib/workflow-toml-validator.js.map +1 -1
- package/dist/src/types/index.d.ts +78 -22
- package/dist/src/validators.d.ts +7 -7
- package/dist/src/validators.js +6 -6
- package/package.json +11 -6
- package/dist/src/commands/settings.d.ts +0 -15
- package/dist/src/commands/settings.js +0 -102
- 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,
|
|
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
|
|
12
|
+
.description("Configure external HTTP API connections")
|
|
12
13
|
.addHelpText("after", `
|
|
13
14
|
Examples:
|
|
14
15
|
$ primitive integrations list
|
|
15
|
-
$ primitive
|
|
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
|
|
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:
|
|
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
|
-
//
|
|
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("
|
|
61
|
-
.description("
|
|
62
|
-
.argument("
|
|
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 (
|
|
74
|
-
const resolvedAppId = resolveAppId(
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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(
|
|
157
|
+
error(err.message);
|
|
91
158
|
process.exit(1);
|
|
92
159
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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
|
|
118
|
-
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
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,
|