primitive-admin 1.1.0-alpha.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +523 -109
- package/assets/skill/skills/primitive-platform/SKILL.md +289 -0
- package/dist/bin/primitive.d.ts +2 -0
- package/dist/bin/primitive.js +372 -21
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/admins.d.ts +2 -0
- package/dist/src/commands/admins.js +136 -30
- package/dist/src/commands/admins.js.map +1 -1
- package/dist/src/commands/analytics.d.ts +2 -0
- package/dist/src/commands/analytics.js +713 -55
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/apps.d.ts +2 -0
- package/dist/src/commands/apps.js +65 -100
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/auth-sessions.d.ts +7 -0
- package/dist/src/commands/auth-sessions.js +144 -0
- package/dist/src/commands/auth-sessions.js.map +1 -0
- package/dist/src/commands/auth.d.ts +2 -0
- package/dist/src/commands/auth.js +238 -108
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/blob-buckets.d.ts +2 -0
- package/dist/src/commands/blob-buckets.js +331 -0
- package/dist/src/commands/blob-buckets.js.map +1 -0
- package/dist/src/commands/catalog.d.ts +2 -0
- package/dist/src/commands/catalog.js +63 -48
- package/dist/src/commands/catalog.js.map +1 -1
- package/dist/src/commands/collection-type-configs.d.ts +2 -0
- package/dist/src/commands/collection-type-configs.js +85 -0
- package/dist/src/commands/collection-type-configs.js.map +1 -0
- package/dist/src/commands/collections.d.ts +2 -0
- package/dist/src/commands/collections.js +1282 -0
- package/dist/src/commands/collections.js.map +1 -0
- package/dist/src/commands/comparisons.d.ts +2 -0
- package/dist/src/commands/comparisons.js +6 -6
- package/dist/src/commands/comparisons.js.map +1 -1
- package/dist/src/commands/config.d.ts +46 -0
- package/dist/src/commands/config.js +465 -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 +99 -0
- package/dist/src/commands/connections.js.map +1 -0
- package/dist/src/commands/cron-triggers.d.ts +2 -0
- package/dist/src/commands/cron-triggers.js +266 -0
- package/dist/src/commands/cron-triggers.js.map +1 -0
- package/dist/src/commands/database-type-configs.d.ts +2 -0
- package/dist/src/commands/database-type-configs.js +164 -0
- package/dist/src/commands/database-type-configs.js.map +1 -0
- package/dist/src/commands/database-types.d.ts +2 -0
- package/dist/src/commands/database-types.js +471 -0
- package/dist/src/commands/database-types.js.map +1 -0
- package/dist/src/commands/databases.d.ts +65 -0
- package/dist/src/commands/databases.js +1887 -243
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.d.ts +60 -0
- package/dist/src/commands/documents.js +2888 -22
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/email-templates.d.ts +2 -0
- package/dist/src/commands/email-templates.js +175 -0
- package/dist/src/commands/email-templates.js.map +1 -0
- package/dist/src/commands/env.d.ts +23 -0
- package/dist/src/commands/env.js +395 -0
- package/dist/src/commands/env.js.map +1 -0
- package/dist/src/commands/feature-flags.d.ts +14 -0
- package/dist/src/commands/feature-flags.js +117 -0
- package/dist/src/commands/feature-flags.js.map +1 -0
- package/dist/src/commands/functions.d.ts +20 -0
- package/dist/src/commands/functions.js +1630 -0
- package/dist/src/commands/functions.js.map +1 -0
- package/dist/src/commands/group-type-configs.d.ts +2 -0
- package/dist/src/commands/group-type-configs.js +87 -0
- package/dist/src/commands/group-type-configs.js.map +1 -0
- package/dist/src/commands/groups.d.ts +2 -0
- package/dist/src/commands/groups.js +65 -113
- package/dist/src/commands/groups.js.map +1 -1
- package/dist/src/commands/guides.d.ts +223 -0
- package/dist/src/commands/guides.js +627 -69
- package/dist/src/commands/guides.js.map +1 -1
- package/dist/src/commands/init.d.ts +37 -0
- package/dist/src/commands/init.js +1693 -208
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/integrations.d.ts +2 -0
- package/dist/src/commands/integrations.js +428 -187
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/llm.d.ts +2 -0
- package/dist/src/commands/llm.js +4 -2
- package/dist/src/commands/llm.js.map +1 -1
- package/dist/src/commands/locks.d.ts +8 -0
- package/dist/src/commands/locks.js +175 -0
- package/dist/src/commands/locks.js.map +1 -0
- package/dist/src/commands/metadata-category-configs.d.ts +12 -0
- package/dist/src/commands/metadata-category-configs.js +113 -0
- package/dist/src/commands/metadata-category-configs.js.map +1 -0
- package/dist/src/commands/metadata.d.ts +2 -0
- package/dist/src/commands/metadata.js +288 -0
- package/dist/src/commands/metadata.js.map +1 -0
- package/dist/src/commands/prompts.d.ts +2 -0
- package/dist/src/commands/prompts.js +322 -634
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/rule-sets.d.ts +3 -0
- package/dist/src/commands/rule-sets.js +139 -148
- package/dist/src/commands/rule-sets.js.map +1 -1
- package/dist/src/commands/scripts.d.ts +30 -0
- package/dist/src/commands/scripts.js +688 -0
- package/dist/src/commands/scripts.js.map +1 -0
- package/dist/src/commands/secrets.d.ts +2 -0
- package/dist/src/commands/secrets.js +109 -0
- package/dist/src/commands/secrets.js.map +1 -0
- package/dist/src/commands/sessions.d.ts +2 -0
- package/dist/src/commands/sessions.js +76 -0
- package/dist/src/commands/sessions.js.map +1 -0
- package/dist/src/commands/skill.d.ts +2 -0
- package/dist/src/commands/skill.js +29 -0
- package/dist/src/commands/skill.js.map +1 -0
- package/dist/src/commands/sync-app-settings.d.ts +158 -0
- package/dist/src/commands/sync-app-settings.js +328 -0
- package/dist/src/commands/sync-app-settings.js.map +1 -0
- package/dist/src/commands/sync.d.ts +2670 -0
- package/dist/src/commands/sync.js +17144 -834
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/tokens.d.ts +2 -0
- package/dist/src/commands/tokens.js +132 -22
- package/dist/src/commands/tokens.js.map +1 -1
- package/dist/src/commands/users.d.ts +2 -0
- package/dist/src/commands/users.js +542 -24
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/commands/vars.d.ts +8 -0
- package/dist/src/commands/vars.js +97 -0
- package/dist/src/commands/vars.js.map +1 -0
- package/dist/src/commands/waitlist.d.ts +2 -0
- package/dist/src/commands/waitlist.js +12 -11
- package/dist/src/commands/waitlist.js.map +1 -1
- package/dist/src/commands/webhooks.d.ts +31 -0
- package/dist/src/commands/webhooks.js +633 -0
- package/dist/src/commands/webhooks.js.map +1 -0
- package/dist/src/commands/workflows.d.ts +88 -0
- package/dist/src/commands/workflows.js +1568 -742
- 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 +2550 -0
- package/dist/src/lib/api-client.js +2503 -160
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/app-settings-descriptor.d.ts +263 -0
- package/dist/src/lib/app-settings-descriptor.js +583 -0
- package/dist/src/lib/app-settings-descriptor.js.map +1 -0
- package/dist/src/lib/auth-flow.d.ts +8 -0
- package/dist/src/lib/batch.d.ts +26 -0
- package/dist/src/lib/batch.js +32 -0
- package/dist/src/lib/batch.js.map +1 -0
- package/dist/src/lib/block-layout.d.ts +160 -0
- package/dist/src/lib/block-layout.js +451 -0
- package/dist/src/lib/block-layout.js.map +1 -0
- package/dist/src/lib/block-selector.d.ts +58 -0
- package/dist/src/lib/block-selector.js +92 -0
- package/dist/src/lib/block-selector.js.map +1 -0
- 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/channel.d.ts +30 -0
- package/dist/src/lib/channel.js +68 -0
- package/dist/src/lib/channel.js.map +1 -0
- package/dist/src/lib/cli-manifest.d.ts +68 -0
- package/dist/src/lib/cli-manifest.js +71 -0
- package/dist/src/lib/cli-manifest.js.map +1 -0
- package/dist/src/lib/codegen-shared/generatedFiles.d.ts +101 -0
- package/dist/src/lib/codegen-shared/generatedFiles.js +191 -0
- package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -0
- package/dist/src/lib/codegen-shared/prettierStable.d.ts +262 -0
- package/dist/src/lib/codegen-shared/prettierStable.js +610 -0
- package/dist/src/lib/codegen-shared/prettierStable.js.map +1 -0
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +38 -0
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +46 -0
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -0
- package/dist/src/lib/collection-export.d.ts +184 -0
- package/dist/src/lib/collection-export.js +252 -0
- package/dist/src/lib/collection-export.js.map +1 -0
- package/dist/src/lib/config-json-field.d.ts +28 -0
- package/dist/src/lib/config-json-field.js +56 -0
- package/dist/src/lib/config-json-field.js.map +1 -0
- package/dist/src/lib/config-object-descriptor.d.ts +127 -0
- package/dist/src/lib/config-object-descriptor.js +740 -0
- package/dist/src/lib/config-object-descriptor.js.map +1 -0
- package/dist/src/lib/config-payload.d.ts +92 -0
- package/dist/src/lib/config-payload.js +161 -0
- package/dist/src/lib/config-payload.js.map +1 -0
- package/dist/src/lib/config-surface.d.ts +141 -0
- package/dist/src/lib/config-surface.js +368 -0
- package/dist/src/lib/config-surface.js.map +1 -0
- package/dist/src/lib/config-toml.d.ts +10 -0
- package/dist/src/lib/config-toml.js +42 -0
- package/dist/src/lib/config-toml.js.map +1 -0
- package/dist/src/lib/config.d.ts +71 -0
- package/dist/src/lib/config.js +71 -68
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/confirm-prompt.d.ts +83 -0
- package/dist/src/lib/confirm-prompt.js +110 -0
- package/dist/src/lib/confirm-prompt.js.map +1 -0
- package/dist/src/lib/constants.d.ts +11 -0
- package/dist/src/lib/constants.js +12 -0
- package/dist/src/lib/constants.js.map +1 -0
- package/dist/src/lib/crash-handlers.d.ts +20 -0
- package/dist/src/lib/crash-handlers.js +49 -0
- package/dist/src/lib/crash-handlers.js.map +1 -0
- package/dist/src/lib/credentials-store.d.ts +104 -0
- package/dist/src/lib/credentials-store.js +336 -0
- package/dist/src/lib/credentials-store.js.map +1 -0
- package/dist/src/lib/csv.d.ts +47 -0
- package/dist/src/lib/csv.js +172 -0
- package/dist/src/lib/csv.js.map +1 -0
- 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/dbFingerprint.d.ts +10 -0
- package/dist/src/lib/db-codegen/dbFingerprint.js +17 -0
- package/dist/src/lib/db-codegen/dbFingerprint.js.map +1 -0
- package/dist/src/lib/db-codegen/dbGenerator.d.ts +67 -0
- package/dist/src/lib/db-codegen/dbGenerator.js +170 -0
- package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -0
- package/dist/src/lib/db-codegen/dbNaming.d.ts +87 -0
- package/dist/src/lib/db-codegen/dbNaming.js +180 -0
- package/dist/src/lib/db-codegen/dbNaming.js.map +1 -0
- package/dist/src/lib/db-codegen/dbTemplates.d.ts +272 -0
- package/dist/src/lib/db-codegen/dbTemplates.js +480 -0
- package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -0
- package/dist/src/lib/db-codegen/dbTsTypes.d.ts +73 -0
- package/dist/src/lib/db-codegen/dbTsTypes.js +139 -0
- package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -0
- package/dist/src/lib/db-codegen/dbTypeIR.d.ts +146 -0
- package/dist/src/lib/db-codegen/dbTypeIR.js +525 -0
- package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -0
- package/dist/src/lib/db-codegen/generated-operation-def-descriptor.d.ts +112 -0
- package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js +211 -0
- package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js.map +1 -0
- package/dist/src/lib/deprecation.d.ts +22 -0
- package/dist/src/lib/deprecation.js +43 -0
- package/dist/src/lib/deprecation.js.map +1 -0
- package/dist/src/lib/document-export-permissions.d.ts +30 -0
- package/dist/src/lib/document-export-permissions.js +54 -0
- package/dist/src/lib/document-export-permissions.js.map +1 -0
- package/dist/src/lib/document-ingest-artifact.d.ts +120 -0
- package/dist/src/lib/document-ingest-artifact.js +505 -0
- package/dist/src/lib/document-ingest-artifact.js.map +1 -0
- package/dist/src/lib/document-ingest-input.d.ts +51 -0
- package/dist/src/lib/document-ingest-input.js +132 -0
- package/dist/src/lib/document-ingest-input.js.map +1 -0
- package/dist/src/lib/document-ingest-rows.d.ts +137 -0
- package/dist/src/lib/document-ingest-rows.js +181 -0
- package/dist/src/lib/document-ingest-rows.js.map +1 -0
- package/dist/src/lib/document-ingest.d.ts +101 -0
- package/dist/src/lib/document-ingest.js +384 -0
- package/dist/src/lib/document-ingest.js.map +1 -0
- package/dist/src/lib/env-resolver-core.d.ts +258 -0
- package/dist/src/lib/env-resolver-core.js +447 -0
- package/dist/src/lib/env-resolver-core.js.map +1 -0
- package/dist/src/lib/env-resolver.d.ts +99 -0
- package/dist/src/lib/env-resolver.js +153 -0
- package/dist/src/lib/env-resolver.js.map +1 -0
- package/dist/src/lib/fetch.d.ts +5 -0
- package/dist/src/lib/function-bundle.d.ts +147 -0
- package/dist/src/lib/function-bundle.js +341 -0
- package/dist/src/lib/function-bundle.js.map +1 -0
- package/dist/src/lib/function-collect.d.ts +123 -0
- package/dist/src/lib/function-collect.js +610 -0
- package/dist/src/lib/function-collect.js.map +1 -0
- package/dist/src/lib/function-db-types.d.ts +202 -0
- package/dist/src/lib/function-db-types.js +870 -0
- package/dist/src/lib/function-db-types.js.map +1 -0
- package/dist/src/lib/function-document-types.d.ts +144 -0
- package/dist/src/lib/function-document-types.js +370 -0
- package/dist/src/lib/function-document-types.js.map +1 -0
- package/dist/src/lib/function-grants-preflight.d.ts +64 -0
- package/dist/src/lib/function-grants-preflight.js +105 -0
- package/dist/src/lib/function-grants-preflight.js.map +1 -0
- package/dist/src/lib/function-log-lines.d.ts +76 -0
- package/dist/src/lib/function-log-lines.js +160 -0
- package/dist/src/lib/function-log-lines.js.map +1 -0
- package/dist/src/lib/function-log-row.d.ts +29 -0
- package/dist/src/lib/function-log-row.js +73 -0
- package/dist/src/lib/function-log-row.js.map +1 -0
- package/dist/src/lib/function-log-tail.d.ts +132 -0
- package/dist/src/lib/function-log-tail.js +262 -0
- package/dist/src/lib/function-log-tail.js.map +1 -0
- package/dist/src/lib/function-run.d.ts +289 -0
- package/dist/src/lib/function-run.js +389 -0
- package/dist/src/lib/function-run.js.map +1 -0
- package/dist/src/lib/function-schema-codegen.d.ts +143 -0
- package/dist/src/lib/function-schema-codegen.js +420 -0
- package/dist/src/lib/function-schema-codegen.js.map +1 -0
- package/dist/src/lib/function-sync.d.ts +459 -0
- package/dist/src/lib/function-sync.js +1258 -0
- package/dist/src/lib/function-sync.js.map +1 -0
- package/dist/src/lib/function-trigger-listing.d.ts +27 -0
- package/dist/src/lib/function-trigger-listing.js +70 -0
- package/dist/src/lib/function-trigger-listing.js.map +1 -0
- package/dist/src/lib/function-typecheck.d.ts +86 -0
- package/dist/src/lib/function-typecheck.js +370 -0
- package/dist/src/lib/function-typecheck.js.map +1 -0
- package/dist/src/lib/function-versions.d.ts +122 -0
- package/dist/src/lib/function-versions.js +182 -0
- package/dist/src/lib/function-versions.js.map +1 -0
- package/dist/src/lib/generated-allowlist.d.ts +28 -0
- package/dist/src/lib/generated-allowlist.js +281 -0
- package/dist/src/lib/generated-allowlist.js.map +1 -0
- package/dist/src/lib/generated-config-surfaces.d.ts +2936 -0
- package/dist/src/lib/generated-config-surfaces.js +10569 -0
- package/dist/src/lib/generated-config-surfaces.js.map +1 -0
- package/dist/src/lib/generated-sdk-types.d.ts +12 -0
- package/dist/src/lib/generated-sdk-types.js +13 -0
- package/dist/src/lib/generated-sdk-types.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-client-platforms.d.ts +14 -0
- package/dist/src/lib/init-client-platforms.js +71 -0
- package/dist/src/lib/init-client-platforms.js.map +1 -0
- package/dist/src/lib/init-config.d.ts +98 -0
- package/dist/src/lib/init-config.js +186 -0
- package/dist/src/lib/init-config.js.map +1 -0
- package/dist/src/lib/init-email-redirect-uris.d.ts +37 -0
- package/dist/src/lib/init-email-redirect-uris.js +46 -0
- package/dist/src/lib/init-email-redirect-uris.js.map +1 -0
- package/dist/src/lib/init-ios-links.d.ts +91 -0
- package/dist/src/lib/init-ios-links.js +219 -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 +34 -0
- package/dist/src/lib/init-xcode.js +138 -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/integration-selector.d.ts +42 -0
- package/dist/src/lib/integration-selector.js +46 -0
- package/dist/src/lib/integration-selector.js.map +1 -0
- package/dist/src/lib/ios-app-id.d.ts +34 -0
- package/dist/src/lib/ios-app-id.js +69 -0
- package/dist/src/lib/ios-app-id.js.map +1 -0
- package/dist/src/lib/list-options.d.ts +68 -0
- package/dist/src/lib/list-options.js +89 -0
- package/dist/src/lib/list-options.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/local-test-cases.d.ts +63 -0
- package/dist/src/lib/local-test-cases.js +136 -0
- package/dist/src/lib/local-test-cases.js.map +1 -0
- package/dist/src/lib/log-inspection.d.ts +715 -0
- package/dist/src/lib/log-inspection.js +816 -0
- package/dist/src/lib/log-inspection.js.map +1 -0
- package/dist/src/lib/logout-admin-session.d.ts +33 -0
- package/dist/src/lib/logout-admin-session.js +70 -0
- package/dist/src/lib/logout-admin-session.js.map +1 -0
- package/dist/src/lib/migration-nag.d.ts +49 -0
- package/dist/src/lib/migration-nag.js +163 -0
- package/dist/src/lib/migration-nag.js.map +1 -0
- 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 +124 -0
- package/dist/src/lib/output.js +219 -8
- 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 +98 -0
- package/dist/src/lib/paginate.js +112 -0
- package/dist/src/lib/paginate.js.map +1 -0
- package/dist/src/lib/platform-owned.d.ts +63 -0
- package/dist/src/lib/platform-owned.js +85 -0
- package/dist/src/lib/platform-owned.js.map +1 -0
- package/dist/src/lib/project-config.d.ts +122 -0
- package/dist/src/lib/project-config.js +244 -0
- package/dist/src/lib/project-config.js.map +1 -0
- package/dist/src/lib/prompt-cost-format.d.ts +11 -0
- package/dist/src/lib/prompt-cost-format.js +41 -0
- package/dist/src/lib/prompt-cost-format.js.map +1 -0
- package/dist/src/lib/prompt-schema-codegen.d.ts +147 -0
- package/dist/src/lib/prompt-schema-codegen.js +462 -0
- package/dist/src/lib/prompt-schema-codegen.js.map +1 -0
- package/dist/src/lib/query-operators.d.ts +43 -0
- package/dist/src/lib/query-operators.js +80 -0
- package/dist/src/lib/query-operators.js.map +1 -0
- 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/refresh-admin-credentials.d.ts +73 -0
- package/dist/src/lib/refresh-admin-credentials.js +123 -0
- package/dist/src/lib/refresh-admin-credentials.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-owner.d.ts +19 -0
- package/dist/src/lib/resolve-owner.js +20 -0
- package/dist/src/lib/resolve-owner.js.map +1 -0
- package/dist/src/lib/resolve-platform.d.ts +74 -0
- package/dist/src/lib/resolve-platform.js +105 -0
- package/dist/src/lib/resolve-platform.js.map +1 -0
- 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 +71 -0
- package/dist/src/lib/skill-installer.js +441 -0
- package/dist/src/lib/skill-installer.js.map +1 -0
- package/dist/src/lib/snapshot-audit-source.d.ts +45 -0
- package/dist/src/lib/snapshot-audit-source.js +58 -0
- package/dist/src/lib/snapshot-audit-source.js.map +1 -0
- package/dist/src/lib/snapshot-audit-store.d.ts +52 -0
- package/dist/src/lib/snapshot-audit-store.js +196 -0
- package/dist/src/lib/snapshot-audit-store.js.map +1 -0
- package/dist/src/lib/snapshot-audit.d.ts +207 -0
- package/dist/src/lib/snapshot-audit.js +431 -0
- package/dist/src/lib/snapshot-audit.js.map +1 -0
- package/dist/src/lib/snapshot-build-rows.d.ts +60 -0
- package/dist/src/lib/snapshot-build-rows.js +87 -0
- package/dist/src/lib/snapshot-build-rows.js.map +1 -0
- package/dist/src/lib/snapshot-build.d.ts +50 -0
- package/dist/src/lib/snapshot-build.js +111 -0
- package/dist/src/lib/snapshot-build.js.map +1 -0
- package/dist/src/lib/snapshot-manifest-layout.d.ts +61 -0
- package/dist/src/lib/snapshot-manifest-layout.js +70 -0
- package/dist/src/lib/snapshot-manifest-layout.js.map +1 -0
- package/dist/src/lib/snapshots.d.ts +98 -0
- package/dist/src/lib/snapshots.js +294 -0
- package/dist/src/lib/snapshots.js.map +1 -0
- package/dist/src/lib/step-run-table.d.ts +43 -0
- package/dist/src/lib/step-run-table.js +129 -0
- package/dist/src/lib/step-run-table.js.map +1 -0
- package/dist/src/lib/storage-pending-retry.d.ts +26 -0
- package/dist/src/lib/storage-pending-retry.js +42 -0
- package/dist/src/lib/storage-pending-retry.js.map +1 -0
- package/dist/src/lib/swift-codegen/agentGenerator.d.ts +42 -0
- package/dist/src/lib/swift-codegen/agentGenerator.js +118 -0
- package/dist/src/lib/swift-codegen/agentGenerator.js.map +1 -0
- package/dist/src/lib/swift-codegen/banners.d.ts +24 -0
- package/dist/src/lib/swift-codegen/banners.js +25 -0
- package/dist/src/lib/swift-codegen/banners.js.map +1 -0
- package/dist/src/lib/swift-codegen/dbGenerator.d.ts +113 -0
- package/dist/src/lib/swift-codegen/dbGenerator.js +926 -0
- package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -0
- package/dist/src/lib/swift-codegen/dbSwiftTypes.d.ts +42 -0
- package/dist/src/lib/swift-codegen/dbSwiftTypes.js +100 -0
- package/dist/src/lib/swift-codegen/dbSwiftTypes.js.map +1 -0
- package/dist/src/lib/swift-codegen/functionGenerator.d.ts +139 -0
- package/dist/src/lib/swift-codegen/functionGenerator.js +462 -0
- package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -0
- package/dist/src/lib/swift-codegen/generator.d.ts +100 -0
- package/dist/src/lib/swift-codegen/generator.js +457 -0
- package/dist/src/lib/swift-codegen/generator.js.map +1 -0
- package/dist/src/lib/swift-codegen/schemaToSwift.d.ts +87 -0
- package/dist/src/lib/swift-codegen/schemaToSwift.js +661 -0
- package/dist/src/lib/swift-codegen/schemaToSwift.js.map +1 -0
- package/dist/src/lib/swift-codegen/siblingSymbols.d.ts +94 -0
- package/dist/src/lib/swift-codegen/siblingSymbols.js +155 -0
- package/dist/src/lib/swift-codegen/siblingSymbols.js.map +1 -0
- package/dist/src/lib/swift-codegen/swiftNaming.d.ts +85 -0
- package/dist/src/lib/swift-codegen/swiftNaming.js +198 -0
- package/dist/src/lib/swift-codegen/swiftNaming.js.map +1 -0
- 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 +128 -0
- package/dist/src/lib/sync-paths.js +195 -0
- package/dist/src/lib/sync-paths.js.map +1 -0
- package/dist/src/lib/sync-resource-types.d.ts +563 -0
- package/dist/src/lib/sync-resource-types.js +1073 -0
- package/dist/src/lib/sync-resource-types.js.map +1 -0
- package/dist/src/lib/sync-selectors.d.ts +138 -0
- package/dist/src/lib/sync-selectors.js +289 -0
- package/dist/src/lib/sync-selectors.js.map +1 -0
- package/dist/src/lib/template.d.ts +170 -0
- package/dist/src/lib/template.js +484 -68
- package/dist/src/lib/template.js.map +1 -1
- package/dist/src/lib/test-case-file-names.d.ts +40 -0
- package/dist/src/lib/test-case-file-names.js +91 -0
- package/dist/src/lib/test-case-file-names.js.map +1 -0
- 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 +29 -0
- package/dist/src/lib/test-case-variables.js +71 -0
- package/dist/src/lib/test-case-variables.js.map +1 -0
- package/dist/src/lib/token-inject.d.ts +56 -0
- package/dist/src/lib/token-inject.js +204 -0
- package/dist/src/lib/token-inject.js.map +1 -0
- package/dist/src/lib/toml-database-config.d.ts +123 -0
- package/dist/src/lib/toml-database-config.js +544 -0
- package/dist/src/lib/toml-database-config.js.map +1 -0
- package/dist/src/lib/toml-metadata-config.d.ts +151 -0
- package/dist/src/lib/toml-metadata-config.js +476 -0
- package/dist/src/lib/toml-metadata-config.js.map +1 -0
- package/dist/src/lib/toml-native-form.d.ts +46 -0
- package/dist/src/lib/toml-native-form.js +78 -0
- package/dist/src/lib/toml-native-form.js.map +1 -0
- package/dist/src/lib/toml-params-validator.d.ts +129 -0
- package/dist/src/lib/toml-params-validator.js +298 -0
- package/dist/src/lib/toml-params-validator.js.map +1 -0
- 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 +35 -0
- package/dist/src/lib/version-check.js +241 -0
- package/dist/src/lib/version-check.js.map +1 -0
- 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/web-url.d.ts +40 -0
- package/dist/src/lib/web-url.js +76 -0
- package/dist/src/lib/web-url.js.map +1 -0
- package/dist/src/lib/webhook-deliver.d.ts +209 -0
- package/dist/src/lib/webhook-deliver.js +519 -0
- package/dist/src/lib/webhook-deliver.js.map +1 -0
- package/dist/src/lib/workflow-apply.d.ts +110 -0
- package/dist/src/lib/workflow-apply.js +164 -0
- package/dist/src/lib/workflow-apply.js.map +1 -0
- package/dist/src/lib/workflow-codegen/generated-schema-descriptor.d.ts +129 -0
- package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js +269 -0
- package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js.map +1 -0
- package/dist/src/lib/workflow-codegen/generator.d.ts +96 -0
- package/dist/src/lib/workflow-codegen/generator.js +361 -0
- package/dist/src/lib/workflow-codegen/generator.js.map +1 -0
- package/dist/src/lib/workflow-codegen/invokerIR.d.ts +94 -0
- package/dist/src/lib/workflow-codegen/invokerIR.js +76 -0
- package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -0
- package/dist/src/lib/workflow-codegen/naming.d.ts +33 -0
- package/dist/src/lib/workflow-codegen/naming.js +81 -0
- package/dist/src/lib/workflow-codegen/naming.js.map +1 -0
- package/dist/src/lib/workflow-codegen/schemaToTs.d.ts +80 -0
- package/dist/src/lib/workflow-codegen/schemaToTs.js +303 -0
- package/dist/src/lib/workflow-codegen/schemaToTs.js.map +1 -0
- 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 +64 -0
- package/dist/src/lib/workflow-fragments.js +342 -0
- package/dist/src/lib/workflow-fragments.js.map +1 -0
- 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 +98 -0
- package/dist/src/lib/workflow-payload.js +178 -0
- package/dist/src/lib/workflow-payload.js.map +1 -0
- package/dist/src/lib/workflow-toml-validator.d.ts +211 -0
- package/dist/src/lib/workflow-toml-validator.js +770 -0
- package/dist/src/lib/workflow-toml-validator.js.map +1 -0
- package/dist/src/lib/workflow-usage.d.ts +196 -0
- package/dist/src/lib/workflow-usage.js +310 -0
- package/dist/src/lib/workflow-usage.js.map +1 -0
- package/dist/src/types/index.d.ts +591 -0
- package/dist/src/validators.d.ts +65 -0
- package/dist/src/validators.js +64 -0
- package/dist/src/validators.js.map +1 -0
- package/package.json +34 -9
|
@@ -1,11 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { basename } from "path";
|
|
3
|
-
import * as TOML from "@iarna/toml";
|
|
4
|
-
import { lookup as mimeLookup } from "mime-types";
|
|
1
|
+
import { writeFileSync } from "fs";
|
|
5
2
|
import { ApiClient } from "../lib/api-client.js";
|
|
6
3
|
import { resolveAppId } from "../lib/config.js";
|
|
7
4
|
import chalk from "chalk";
|
|
8
|
-
import { success, error, info, warn, keyValue, formatTable, formatId, formatDate, formatStatus, formatDuration, json, divider, } from "../lib/output.js";
|
|
5
|
+
import { success, error, info, warn, keyValue, result as printResult, formatTable, formatId, formatDate, formatStatus, formatDuration, json, divider, progress, progressEnd, } from "../lib/output.js";
|
|
6
|
+
import { pollUntil } from "../lib/watch.js";
|
|
7
|
+
import { confirmPrompt } from "../lib/confirm-prompt.js";
|
|
8
|
+
import { parseStatusFilter } from "../lib/object-status-filter.js";
|
|
9
|
+
import { formatAccessRuleForDisplay } from "../lib/access-rule-display.js";
|
|
10
|
+
import { requireTestCaseInputVariables } from "../lib/test-case-variables.js";
|
|
11
|
+
import { resolvePromptSelector } from "../lib/block-selector.js";
|
|
12
|
+
import { normalizeCliListEnvelope, wholeListEnvelope, } from "../lib/paginate.js";
|
|
13
|
+
import { pageCursorOption, pageLimitOption, parsePageLimit, printEmptyPage, printPageHint, } from "../lib/list-options.js";
|
|
14
|
+
/**
|
|
15
|
+
* The one-line token accounting an execution prints.
|
|
16
|
+
*
|
|
17
|
+
* #3358 — `reasoningTokens` is appended only when the provider reported some,
|
|
18
|
+
* so a prompt that does no thinking prints exactly what it always did. It is
|
|
19
|
+
* shown beside the output count rather than folded into it because the two
|
|
20
|
+
* providers disagree about whether reasoning is already inside that number,
|
|
21
|
+
* and the whole point of the metric is to say how much of the decode was
|
|
22
|
+
* deliberation rather than answer.
|
|
23
|
+
*/
|
|
24
|
+
function formatTokens(metrics) {
|
|
25
|
+
const base = `${metrics?.inputTokens || 0} in / ${metrics?.outputTokens || 0} out`;
|
|
26
|
+
const reasoning = metrics?.reasoningTokens;
|
|
27
|
+
return typeof reasoning === "number"
|
|
28
|
+
? `${base} (${reasoning} reasoning)`
|
|
29
|
+
: base;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a prompt reference — its server id, or the author-chosen key its
|
|
33
|
+
* TOML file is named for.
|
|
34
|
+
*
|
|
35
|
+
* Under TOML-only authoring the key is what an operator has: `prompts/<key>.toml`
|
|
36
|
+
* is the object, and the id is a server detail they never wrote down. Mirrors
|
|
37
|
+
* `resolveScriptId` in `scripts.ts`. The resolution itself lives in
|
|
38
|
+
* `block-selector.ts` (#3249) so the `tests` verbs and `configs get` agree on
|
|
39
|
+
* it — and drain every page of the list, where this used to read one.
|
|
40
|
+
*/
|
|
41
|
+
async function resolvePromptId(client, appId, ref) {
|
|
42
|
+
return (await resolvePromptSelector(client, appId, ref)).blockId;
|
|
43
|
+
}
|
|
9
44
|
export function registerPromptsCommands(program) {
|
|
10
45
|
const prompts = program
|
|
11
46
|
.command("prompts")
|
|
@@ -13,9 +48,19 @@ export function registerPromptsCommands(program) {
|
|
|
13
48
|
.addHelpText("after", `
|
|
14
49
|
Examples:
|
|
15
50
|
$ primitive prompts list
|
|
16
|
-
$ primitive
|
|
51
|
+
$ primitive config create prompt summarizer # scaffold prompts/summarizer.toml
|
|
52
|
+
$ primitive config push --only prompt/summarizer # apply it
|
|
17
53
|
$ primitive prompts execute 01HXY... --vars '{"text":"Hello world"}'
|
|
18
|
-
$ primitive prompts configs
|
|
54
|
+
$ primitive prompts configs list summarizer # what the server runs
|
|
55
|
+
`);
|
|
56
|
+
// Server-side configuration is authored in TOML and applied with `config push`
|
|
57
|
+
// (issue #2645) — this group no longer carries config-setting flags.
|
|
58
|
+
prompts.addHelpText("after", `
|
|
59
|
+
Configuration lives in prompts/<key>.toml:
|
|
60
|
+
$ primitive config fields prompt # the file's keys, types and defaults
|
|
61
|
+
$ primitive config create prompt <key> # scaffold one locally
|
|
62
|
+
$ primitive config set prompt/<key> <path>=<value>
|
|
63
|
+
$ primitive config push --only prompt/<key>
|
|
19
64
|
`);
|
|
20
65
|
// List prompts
|
|
21
66
|
prompts
|
|
@@ -23,21 +68,35 @@ Examples:
|
|
|
23
68
|
.description("List prompts")
|
|
24
69
|
.argument("[app-id]", "App ID (uses current app if not specified)")
|
|
25
70
|
.option("--app <app-id>", "App ID")
|
|
26
|
-
.option("--status <status>", "Filter by status:
|
|
71
|
+
.option("--status <status>", "Filter by status: active, inactive, archived")
|
|
72
|
+
.addOption(pageLimitOption())
|
|
73
|
+
.addOption(pageCursorOption())
|
|
27
74
|
.option("--json", "Output as JSON")
|
|
28
75
|
.action(async (appId, options) => {
|
|
76
|
+
const limit = parsePageLimit(options.limit);
|
|
29
77
|
const resolvedAppId = resolveAppId(appId, options);
|
|
30
78
|
const client = new ApiClient();
|
|
31
79
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
80
|
+
// #2803 — `draft` is gone; refuse it by name rather than returning the
|
|
81
|
+
// empty list a canonical filter would produce. #2887 added `archived`:
|
|
82
|
+
// the delete flow writes one now, so asking for the retired rows is a
|
|
83
|
+
// question with an answer rather than a 400.
|
|
84
|
+
const page = normalizeCliListEnvelope(await client.listPrompts(resolvedAppId, {
|
|
85
|
+
status: parseStatusFilter("prompts", options.status, [
|
|
86
|
+
"active",
|
|
87
|
+
"inactive",
|
|
88
|
+
"archived",
|
|
89
|
+
]),
|
|
90
|
+
limit,
|
|
91
|
+
cursor: options.cursor,
|
|
92
|
+
}));
|
|
93
|
+
const items = page.items;
|
|
35
94
|
if (options.json) {
|
|
36
|
-
json(
|
|
95
|
+
json(page);
|
|
37
96
|
return;
|
|
38
97
|
}
|
|
39
|
-
if (
|
|
40
|
-
|
|
98
|
+
if (items.length === 0) {
|
|
99
|
+
printEmptyPage(page, "prompts");
|
|
41
100
|
return;
|
|
42
101
|
}
|
|
43
102
|
console.log(formatTable(items, [
|
|
@@ -47,215 +106,161 @@ Examples:
|
|
|
47
106
|
{ header: "STATUS", key: "status", format: formatStatus },
|
|
48
107
|
{ header: "MODIFIED", key: "modifiedAt", format: formatDate },
|
|
49
108
|
]));
|
|
109
|
+
printPageHint(page);
|
|
50
110
|
}
|
|
51
111
|
catch (err) {
|
|
52
112
|
error(err.message);
|
|
53
113
|
process.exit(1);
|
|
54
114
|
}
|
|
55
115
|
});
|
|
56
|
-
//
|
|
116
|
+
// #2645 criterion 9 / #2803 — the operational verbs. They are the only
|
|
117
|
+
// writers of `status`, which is server-owned and no longer a TOML key.
|
|
57
118
|
prompts
|
|
58
|
-
.command("
|
|
59
|
-
.description("
|
|
60
|
-
.argument("
|
|
61
|
-
.option("--app <app-id>", "App ID")
|
|
62
|
-
.option("--key <key>", "Prompt key (unique identifier)")
|
|
63
|
-
.option("--name <name>", "Display name")
|
|
64
|
-
.option("--description <desc>", "Description")
|
|
65
|
-
.option("--provider <provider>", "LLM provider: openrouter, gemini", "openrouter")
|
|
66
|
-
.option("--model <model>", "Model name")
|
|
67
|
-
.option("--system-prompt <prompt>", "System prompt")
|
|
68
|
-
.option("--user-template <template>", "User prompt template")
|
|
69
|
-
.option("--temperature <temp>", "Temperature (0-1)")
|
|
70
|
-
.option("--max-tokens <n>", "Max output tokens")
|
|
71
|
-
.option("--output-format <format>", "Output format: text, json")
|
|
72
|
-
.option("--input-schema <json>", "Input schema as JSON")
|
|
73
|
-
.option("--output-schema <json>", "Output schema as JSON")
|
|
74
|
-
.option("--from-file <path>", "Load prompt from TOML file")
|
|
119
|
+
.command("disable")
|
|
120
|
+
.description("Take prompt out of service now. Operational, not configuration: `status` is server-owned and 'config push' can neither set nor clear it.")
|
|
121
|
+
.argument("<prompt-id>", "Prompt ID")
|
|
122
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
75
123
|
.option("--json", "Output as JSON")
|
|
76
|
-
.action(async (
|
|
77
|
-
const resolvedAppId = resolveAppId(
|
|
124
|
+
.action(async (id, options) => {
|
|
125
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
78
126
|
const client = new ApiClient();
|
|
79
|
-
let payload;
|
|
80
|
-
if (options.fromFile) {
|
|
81
|
-
try {
|
|
82
|
-
const content = readFileSync(options.fromFile, "utf-8");
|
|
83
|
-
const tomlData = TOML.parse(content);
|
|
84
|
-
const prompt = tomlData.prompt || tomlData;
|
|
85
|
-
const config = tomlData.configs?.[0] || {};
|
|
86
|
-
payload = {
|
|
87
|
-
promptKey: prompt.key || prompt.promptKey,
|
|
88
|
-
displayName: prompt.displayName || prompt.name,
|
|
89
|
-
description: prompt.description,
|
|
90
|
-
provider: config.provider || prompt.provider || "openrouter",
|
|
91
|
-
model: config.model || prompt.model,
|
|
92
|
-
systemPrompt: config.systemPrompt || prompt.systemPrompt,
|
|
93
|
-
userPromptTemplate: config.userPromptTemplate || prompt.userPromptTemplate,
|
|
94
|
-
temperature: config.temperature || prompt.temperature,
|
|
95
|
-
maxTokens: config.maxTokens || prompt.maxTokens,
|
|
96
|
-
outputFormat: config.outputFormat || prompt.outputFormat,
|
|
97
|
-
inputSchema: prompt.inputSchema,
|
|
98
|
-
outputSchema: prompt.outputSchema,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
error(`Failed to read TOML file: ${err.message}`);
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
if (!options.key || !options.name || !options.model || !options.userTemplate) {
|
|
108
|
-
error("Required: --key, --name, --model, --user-template (or use --from-file)");
|
|
109
|
-
process.exit(1);
|
|
110
|
-
}
|
|
111
|
-
payload = {
|
|
112
|
-
promptKey: options.key,
|
|
113
|
-
displayName: options.name,
|
|
114
|
-
description: options.description,
|
|
115
|
-
provider: options.provider,
|
|
116
|
-
model: options.model,
|
|
117
|
-
systemPrompt: options.systemPrompt,
|
|
118
|
-
userPromptTemplate: options.userTemplate,
|
|
119
|
-
temperature: options.temperature,
|
|
120
|
-
maxTokens: options.maxTokens ? parseInt(options.maxTokens) : undefined,
|
|
121
|
-
outputFormat: options.outputFormat,
|
|
122
|
-
inputSchema: options.inputSchema,
|
|
123
|
-
outputSchema: options.outputSchema,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
127
|
try {
|
|
127
|
-
const result = await client.
|
|
128
|
+
const result = await client.setPromptAvailability(resolvedAppId, id, "disable");
|
|
128
129
|
if (options.json) {
|
|
129
130
|
json(result);
|
|
130
131
|
return;
|
|
131
132
|
}
|
|
132
|
-
success(`Prompt
|
|
133
|
-
keyValue("Prompt ID", result.promptId);
|
|
134
|
-
keyValue("Key", result.promptKey);
|
|
133
|
+
success(`Prompt disabled: ${result.promptKey || id}`);
|
|
135
134
|
}
|
|
136
135
|
catch (err) {
|
|
137
136
|
error(err.message);
|
|
138
137
|
process.exit(1);
|
|
139
138
|
}
|
|
140
139
|
});
|
|
141
|
-
// Get prompt
|
|
142
140
|
prompts
|
|
143
|
-
.command("
|
|
144
|
-
.description("
|
|
141
|
+
.command("enable")
|
|
142
|
+
.description("Put prompt back in service.")
|
|
145
143
|
.argument("<prompt-id>", "Prompt ID")
|
|
146
144
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
147
145
|
.option("--json", "Output as JSON")
|
|
148
|
-
.action(async (
|
|
146
|
+
.action(async (id, options) => {
|
|
149
147
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
150
148
|
const client = new ApiClient();
|
|
151
149
|
try {
|
|
152
|
-
const result = await client.
|
|
150
|
+
const result = await client.setPromptAvailability(resolvedAppId, id, "enable");
|
|
153
151
|
if (options.json) {
|
|
154
152
|
json(result);
|
|
155
153
|
return;
|
|
156
154
|
}
|
|
157
|
-
|
|
158
|
-
keyValue("Key", result.promptKey);
|
|
159
|
-
keyValue("Name", result.displayName);
|
|
160
|
-
keyValue("Description", result.description);
|
|
161
|
-
keyValue("Status", formatStatus(result.status));
|
|
162
|
-
keyValue("Active Config", result.activeConfigId || "-");
|
|
163
|
-
if (result.configs && result.configs.length > 0) {
|
|
164
|
-
divider();
|
|
165
|
-
info(`Configs (${result.configs.length}):`);
|
|
166
|
-
console.log(formatTable(result.configs, [
|
|
167
|
-
{ header: "ID", key: "configId", format: formatId },
|
|
168
|
-
{ header: "NAME", key: "configName" },
|
|
169
|
-
{ header: "MODEL", key: "model" },
|
|
170
|
-
{ header: "STATUS", key: "status", format: formatStatus },
|
|
171
|
-
]));
|
|
172
|
-
}
|
|
155
|
+
success(`Prompt enabled: ${result.promptKey || id}`);
|
|
173
156
|
}
|
|
174
157
|
catch (err) {
|
|
175
158
|
error(err.message);
|
|
176
159
|
process.exit(1);
|
|
177
160
|
}
|
|
178
161
|
});
|
|
179
|
-
//
|
|
162
|
+
// #2907 — the delete flow's CLI spelling. Not a second writer of `status`:
|
|
163
|
+
// this is the plain `DELETE` #2887 made retire-with-history, which until now
|
|
164
|
+
// only the admin API and the console could reach. Named `archive`, not
|
|
165
|
+
// `delete`, because a configuration noun gets no `delete` verb
|
|
166
|
+
// (docs/cli-design.md) and prune-by-push stays the only CLI hard-delete path.
|
|
180
167
|
prompts
|
|
181
|
-
.command("
|
|
182
|
-
.description("
|
|
168
|
+
.command("archive")
|
|
169
|
+
.description("Retire a prompt (soft delete). Delete-lifecycle, not `disable`: the row and its configs are kept so executions still resolve, it goes on holding its key, and there is no un-archive.")
|
|
183
170
|
.argument("<prompt-id>", "Prompt ID")
|
|
184
171
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
185
|
-
.option("--
|
|
186
|
-
.option("--description <desc>", "Description")
|
|
187
|
-
.option("--status <status>", "Status: draft, active, archived")
|
|
188
|
-
.option("--input-schema <json>", "Input schema as JSON")
|
|
189
|
-
.option("--output-schema <json>", "Output schema as JSON")
|
|
172
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
190
173
|
.option("--json", "Output as JSON")
|
|
191
|
-
.
|
|
174
|
+
.addHelpText("after", `
|
|
175
|
+
What archiving a prompt costs (retire-with-history):
|
|
176
|
+
The member endpoint refuses it, and so does admin execution — the diagnostic
|
|
177
|
+
runs an INACTIVE prompt on purpose, but not an archived one.
|
|
178
|
+
Its configs, executions and analytics keep resolving what they point at.
|
|
179
|
+
The archived row still holds its promptKey.
|
|
180
|
+
\`primitive prompts enable\` refuses an archived prompt; there is no
|
|
181
|
+
un-archive.
|
|
182
|
+
The per-version \`[[configs]] status\` is a different key and is untouched.
|
|
183
|
+
|
|
184
|
+
Reclaiming the key means hard-deleting the row:
|
|
185
|
+
$ rm prompts/<key>.toml
|
|
186
|
+
$ primitive config push --prune # confirmed; hard-deletes the row
|
|
187
|
+
# or hard-delete it in the console, then re-add the file and push.
|
|
188
|
+
`)
|
|
189
|
+
.action(async (id, options) => {
|
|
192
190
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
191
|
+
if (!options.yes) {
|
|
192
|
+
let confirmed;
|
|
193
|
+
try {
|
|
194
|
+
confirmed = await confirmPrompt(`Archive prompt ${id}? It is retired, its configs and executions are ` +
|
|
195
|
+
`kept, it goes on holding its key, and there is no un-archive.`);
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
error(err.message);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
if (!confirmed) {
|
|
202
|
+
info("Cancelled.");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
207
205
|
}
|
|
208
206
|
const client = new ApiClient();
|
|
209
207
|
try {
|
|
210
|
-
|
|
208
|
+
// The soft delete: no `hard`, so the configs and stored prompt bodies
|
|
209
|
+
// survive and every execution row keeps resolving.
|
|
210
|
+
const result = await client.deletePrompt(resolvedAppId, id);
|
|
211
211
|
if (options.json) {
|
|
212
212
|
json(result);
|
|
213
213
|
return;
|
|
214
214
|
}
|
|
215
|
-
success(
|
|
215
|
+
success(`Prompt archived: ${id}`);
|
|
216
|
+
info("Its configs and executions keep resolving. The row still holds its key — reclaim it by removing its file and running a confirmed `primitive config push --prune`.");
|
|
216
217
|
}
|
|
217
218
|
catch (err) {
|
|
218
219
|
error(err.message);
|
|
219
220
|
process.exit(1);
|
|
220
221
|
}
|
|
221
222
|
});
|
|
222
|
-
// Delete prompt
|
|
223
223
|
prompts
|
|
224
|
-
.command("
|
|
225
|
-
.description("
|
|
224
|
+
.command("get")
|
|
225
|
+
.description("Get prompt details")
|
|
226
226
|
.argument("<prompt-id>", "Prompt ID")
|
|
227
227
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
228
|
-
.option("--
|
|
229
|
-
.option("-y, --yes", "Skip confirmation prompt")
|
|
228
|
+
.option("--json", "Output as JSON")
|
|
230
229
|
.action(async (promptId, options) => {
|
|
231
230
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
232
|
-
if (!options.yes) {
|
|
233
|
-
const action = options.hard ? "permanently delete" : "archive";
|
|
234
|
-
const inquirer = await import("inquirer");
|
|
235
|
-
const { confirm } = await inquirer.default.prompt([
|
|
236
|
-
{
|
|
237
|
-
type: "confirm",
|
|
238
|
-
name: "confirm",
|
|
239
|
-
message: `Are you sure you want to ${action} prompt ${promptId}?`,
|
|
240
|
-
default: false,
|
|
241
|
-
},
|
|
242
|
-
]);
|
|
243
|
-
if (!confirm) {
|
|
244
|
-
info("Cancelled.");
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
231
|
const client = new ApiClient();
|
|
249
232
|
try {
|
|
250
|
-
await client.
|
|
251
|
-
|
|
233
|
+
const result = await client.getPrompt(resolvedAppId, promptId);
|
|
234
|
+
if (options.json) {
|
|
235
|
+
json(result);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
printResult("Prompt ID", result.promptId);
|
|
239
|
+
printResult("Key", result.promptKey);
|
|
240
|
+
printResult("Name", result.displayName);
|
|
241
|
+
printResult("Description", result.description);
|
|
242
|
+
printResult("Status", formatStatus(result.status));
|
|
243
|
+
// #2652: no rule means every non-admin execution is denied. Print that
|
|
244
|
+
// in words — a blank or "-" would read as "unrestricted" and hide a
|
|
245
|
+
// prompt that was never given a rule.
|
|
246
|
+
printResult("Access Rule", formatAccessRuleForDisplay(result.accessRule, "prompt"));
|
|
247
|
+
printResult("Active Config", result.activeConfigId || "-");
|
|
248
|
+
if (result.configs && result.configs.length > 0) {
|
|
249
|
+
divider();
|
|
250
|
+
info(`Configs (${result.configs.length}):`);
|
|
251
|
+
console.log(formatTable(result.configs, [
|
|
252
|
+
{ header: "ID", key: "configId", format: formatId },
|
|
253
|
+
{ header: "NAME", key: "configName" },
|
|
254
|
+
{ header: "MODEL", key: "model" },
|
|
255
|
+
{ header: "STATUS", key: "status", format: formatStatus },
|
|
256
|
+
]));
|
|
257
|
+
}
|
|
252
258
|
}
|
|
253
259
|
catch (err) {
|
|
254
260
|
error(err.message);
|
|
255
261
|
process.exit(1);
|
|
256
262
|
}
|
|
257
263
|
});
|
|
258
|
-
// Execute prompt
|
|
259
264
|
prompts
|
|
260
265
|
.command("execute")
|
|
261
266
|
.description("Execute a prompt with variables")
|
|
@@ -289,7 +294,7 @@ Examples:
|
|
|
289
294
|
if (result.success) {
|
|
290
295
|
success("Execution successful");
|
|
291
296
|
keyValue("Duration", formatDuration(result.metrics?.durationMs));
|
|
292
|
-
keyValue("Tokens",
|
|
297
|
+
keyValue("Tokens", formatTokens(result.metrics));
|
|
293
298
|
console.log("\nOutput:");
|
|
294
299
|
console.log(result.output);
|
|
295
300
|
}
|
|
@@ -397,8 +402,25 @@ Examples:
|
|
|
397
402
|
process.exit(1);
|
|
398
403
|
}
|
|
399
404
|
});
|
|
400
|
-
// Configs subcommand
|
|
401
|
-
|
|
405
|
+
// Configs subcommand — reads only. A prompt's configs are `[[configs]]`
|
|
406
|
+
// entries in its own TOML file (#2645), so the verbs that used to write them
|
|
407
|
+
// were a second writer for state the file already owns.
|
|
408
|
+
const configs = prompts
|
|
409
|
+
.command("configs")
|
|
410
|
+
.description("Inspect a prompt's configs (authored in prompts/<key>.toml)")
|
|
411
|
+
.addHelpText("after", `
|
|
412
|
+
A prompt's configs are the \`[[configs]]\` entries of prompts/<key>.toml. Add,
|
|
413
|
+
edit, retire (\`status = "archived"\`) or mark one live (\`active = true\`) in that
|
|
414
|
+
file, then apply it:
|
|
415
|
+
|
|
416
|
+
$ primitive config push --only prompt/<key>
|
|
417
|
+
|
|
418
|
+
That per-config \`status\` says which VERSION is retired and is yours to author.
|
|
419
|
+
The PROMPT's own availability is server-owned and has no TOML key: use
|
|
420
|
+
'primitive prompts disable' / 'enable'.
|
|
421
|
+
|
|
422
|
+
Duplicating a config is copying its \`[[configs]]\` block and renaming it.
|
|
423
|
+
`);
|
|
402
424
|
// List configs
|
|
403
425
|
configs
|
|
404
426
|
.command("list")
|
|
@@ -412,7 +434,7 @@ Examples:
|
|
|
412
434
|
try {
|
|
413
435
|
const { items } = await client.listPromptConfigs(resolvedAppId, promptId);
|
|
414
436
|
if (options.json) {
|
|
415
|
-
json(items);
|
|
437
|
+
json(wholeListEnvelope(items));
|
|
416
438
|
return;
|
|
417
439
|
}
|
|
418
440
|
if (!items || items.length === 0) {
|
|
@@ -432,154 +454,51 @@ Examples:
|
|
|
432
454
|
process.exit(1);
|
|
433
455
|
}
|
|
434
456
|
});
|
|
435
|
-
//
|
|
457
|
+
// Get one config. `list` says which configs exist; this says what one of
|
|
458
|
+
// them actually runs — the body, provider and model the server would use.
|
|
459
|
+
// With `prompts configs create/update` gone, this is where that content is
|
|
460
|
+
// read back from.
|
|
436
461
|
configs
|
|
437
|
-
.command("
|
|
438
|
-
.description("
|
|
439
|
-
.argument("<prompt
|
|
462
|
+
.command("get")
|
|
463
|
+
.description("Show one config of a prompt as the server has it")
|
|
464
|
+
.argument("<prompt>", "Prompt ID or key")
|
|
465
|
+
.argument("<config>", "Config ID or name")
|
|
440
466
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
441
|
-
.option("--name <name>", "Config name")
|
|
442
|
-
.option("--description <desc>", "Description")
|
|
443
|
-
.option("--provider <provider>", "LLM provider: openrouter, gemini", "openrouter")
|
|
444
|
-
.option("--model <model>", "Model name")
|
|
445
|
-
.option("--system-prompt <prompt>", "System prompt")
|
|
446
|
-
.option("--user-template <template>", "User prompt template")
|
|
447
|
-
.option("--temperature <temp>", "Temperature (0-1)")
|
|
448
|
-
.option("--max-tokens <n>", "Max output tokens")
|
|
449
|
-
.option("--output-format <format>", "Output format: text, json")
|
|
450
467
|
.option("--json", "Output as JSON")
|
|
451
|
-
.action(async (
|
|
468
|
+
.action(async (prompt, config, options) => {
|
|
452
469
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
453
|
-
if (!options.name || !options.model || !options.userTemplate) {
|
|
454
|
-
error("Required: --name, --model, --user-template");
|
|
455
|
-
process.exit(1);
|
|
456
|
-
}
|
|
457
|
-
const payload = {
|
|
458
|
-
configName: options.name,
|
|
459
|
-
description: options.description,
|
|
460
|
-
provider: options.provider,
|
|
461
|
-
model: options.model,
|
|
462
|
-
systemPrompt: options.systemPrompt,
|
|
463
|
-
userPromptTemplate: options.userTemplate,
|
|
464
|
-
temperature: options.temperature,
|
|
465
|
-
maxTokens: options.maxTokens ? parseInt(options.maxTokens) : undefined,
|
|
466
|
-
outputFormat: options.outputFormat,
|
|
467
|
-
};
|
|
468
470
|
const client = new ApiClient();
|
|
469
471
|
try {
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
472
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
473
|
+
const { items } = await client.listPromptConfigs(resolvedAppId, promptId);
|
|
474
|
+
const summary = (items ?? []).find((c) => c.configId === config || c.configName === config);
|
|
475
|
+
if (!summary) {
|
|
476
|
+
error(`Prompt '${prompt}' has no config '${config}'. Run 'primitive prompts configs list ${prompt}' to see them.`);
|
|
477
|
+
process.exit(1);
|
|
474
478
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
catch (err) {
|
|
479
|
-
error(err.message);
|
|
480
|
-
process.exit(1);
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
// Activate config
|
|
484
|
-
configs
|
|
485
|
-
.command("activate")
|
|
486
|
-
.description("Set a config as the active config for a prompt")
|
|
487
|
-
.argument("<prompt-id>", "Prompt ID")
|
|
488
|
-
.argument("<config-id>", "Config ID")
|
|
489
|
-
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
490
|
-
.action(async (promptId, configId, options) => {
|
|
491
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
492
|
-
const client = new ApiClient();
|
|
493
|
-
try {
|
|
494
|
-
await client.activatePromptConfig(resolvedAppId, promptId, configId);
|
|
495
|
-
success(`Config ${configId} activated.`);
|
|
496
|
-
}
|
|
497
|
-
catch (err) {
|
|
498
|
-
error(err.message);
|
|
499
|
-
process.exit(1);
|
|
500
|
-
}
|
|
501
|
-
});
|
|
502
|
-
// Duplicate config
|
|
503
|
-
configs
|
|
504
|
-
.command("duplicate")
|
|
505
|
-
.description("Duplicate a config")
|
|
506
|
-
.argument("<prompt-id>", "Prompt ID")
|
|
507
|
-
.argument("<config-id>", "Config ID to duplicate")
|
|
508
|
-
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
509
|
-
.option("--name <name>", "New config name")
|
|
510
|
-
.option("--json", "Output as JSON")
|
|
511
|
-
.action(async (promptId, configId, options) => {
|
|
512
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
513
|
-
const client = new ApiClient();
|
|
514
|
-
try {
|
|
515
|
-
const result = await client.duplicatePromptConfig(resolvedAppId, promptId, configId, {
|
|
516
|
-
configName: options.name,
|
|
517
|
-
});
|
|
479
|
+
const full = (await client.getPromptConfig(resolvedAppId, promptId, summary.configId)) ??
|
|
480
|
+
summary;
|
|
518
481
|
if (options.json) {
|
|
519
|
-
json(
|
|
482
|
+
json(full);
|
|
520
483
|
return;
|
|
521
484
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
.option("--description <desc>", "Description")
|
|
539
|
-
.option("--provider <provider>", "LLM provider: openrouter, gemini")
|
|
540
|
-
.option("--model <model>", "Model name")
|
|
541
|
-
.option("--system-prompt <prompt>", "System prompt")
|
|
542
|
-
.option("--user-template <template>", "User prompt template")
|
|
543
|
-
.option("--temperature <temp>", "Temperature (0-1)")
|
|
544
|
-
.option("--max-tokens <n>", "Max output tokens")
|
|
545
|
-
.option("--output-format <format>", "Output format: text, json")
|
|
546
|
-
.option("--status <status>", "Status: active, archived")
|
|
547
|
-
.option("--json", "Output as JSON")
|
|
548
|
-
.action(async (promptId, configId, options) => {
|
|
549
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
550
|
-
const payload = {};
|
|
551
|
-
if (options.name)
|
|
552
|
-
payload.configName = options.name;
|
|
553
|
-
if (options.description)
|
|
554
|
-
payload.description = options.description;
|
|
555
|
-
if (options.provider)
|
|
556
|
-
payload.provider = options.provider;
|
|
557
|
-
if (options.model)
|
|
558
|
-
payload.model = options.model;
|
|
559
|
-
if (options.systemPrompt)
|
|
560
|
-
payload.systemPrompt = options.systemPrompt;
|
|
561
|
-
if (options.userTemplate)
|
|
562
|
-
payload.userPromptTemplate = options.userTemplate;
|
|
563
|
-
if (options.temperature)
|
|
564
|
-
payload.temperature = options.temperature;
|
|
565
|
-
if (options.maxTokens)
|
|
566
|
-
payload.maxTokens = parseInt(options.maxTokens);
|
|
567
|
-
if (options.outputFormat)
|
|
568
|
-
payload.outputFormat = options.outputFormat;
|
|
569
|
-
if (options.status)
|
|
570
|
-
payload.status = options.status;
|
|
571
|
-
if (Object.keys(payload).length === 0) {
|
|
572
|
-
error("No update options specified.");
|
|
573
|
-
process.exit(1);
|
|
574
|
-
}
|
|
575
|
-
const client = new ApiClient();
|
|
576
|
-
try {
|
|
577
|
-
const result = await client.updatePromptConfig(resolvedAppId, promptId, configId, payload);
|
|
578
|
-
if (options.json) {
|
|
579
|
-
json(result);
|
|
580
|
-
return;
|
|
485
|
+
printResult("Config ID", full.configId);
|
|
486
|
+
printResult("Name", full.configName);
|
|
487
|
+
printResult("Status", formatStatus(full.status));
|
|
488
|
+
printResult("Provider", full.provider || "-");
|
|
489
|
+
printResult("Model", full.model || "-");
|
|
490
|
+
if (full.description)
|
|
491
|
+
printResult("Description", full.description);
|
|
492
|
+
if (full.systemPrompt) {
|
|
493
|
+
divider();
|
|
494
|
+
info("System prompt:");
|
|
495
|
+
console.log(full.systemPrompt);
|
|
496
|
+
}
|
|
497
|
+
if (full.userPrompt) {
|
|
498
|
+
divider();
|
|
499
|
+
info("User prompt:");
|
|
500
|
+
console.log(full.userPrompt);
|
|
581
501
|
}
|
|
582
|
-
success("Config updated.");
|
|
583
502
|
}
|
|
584
503
|
catch (err) {
|
|
585
504
|
error(err.message);
|
|
@@ -591,33 +510,51 @@ Examples:
|
|
|
591
510
|
// ============================================
|
|
592
511
|
const tests = prompts
|
|
593
512
|
.command("tests")
|
|
594
|
-
.description("
|
|
513
|
+
.description("Run and inspect prompt test cases")
|
|
595
514
|
.addHelpText("after", `
|
|
515
|
+
Test cases are authored in TOML at prompts/<key>.tests/<name>.toml and applied
|
|
516
|
+
with \`primitive config push\` (issue #2769); \`config fields prompt\` lists the
|
|
517
|
+
fields. Deleting a case is removing its file and running \`config push --prune\`.
|
|
518
|
+
|
|
519
|
+
Every verb here takes the prompt's KEY or its ID (#3249). An identifier that
|
|
520
|
+
names no prompt in the app fails; "No test cases found." only ever means the
|
|
521
|
+
prompt exists and has no cases.
|
|
522
|
+
|
|
596
523
|
Examples:
|
|
597
|
-
$ primitive prompts tests list <prompt
|
|
598
|
-
$ primitive prompts tests
|
|
599
|
-
$ primitive prompts tests run <prompt
|
|
600
|
-
$ primitive prompts tests run-all <prompt-
|
|
601
|
-
$ primitive prompts tests run-all <prompt-id> --test-cases 01ABC,01DEF,01GHI
|
|
524
|
+
$ primitive prompts tests list <prompt>
|
|
525
|
+
$ primitive prompts tests run <prompt> <test-case-id>
|
|
526
|
+
$ primitive prompts tests run-all <prompt>
|
|
527
|
+
$ primitive prompts tests run-all <prompt> --test-cases 01ABC,01DEF,01GHI
|
|
602
528
|
`);
|
|
603
529
|
// List test cases
|
|
604
530
|
tests
|
|
605
531
|
.command("list")
|
|
606
532
|
.description("List test cases for a prompt")
|
|
607
|
-
.argument("<prompt
|
|
533
|
+
.argument("<prompt>", "Prompt key or ID")
|
|
608
534
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
535
|
+
.addOption(pageLimitOption())
|
|
536
|
+
.addOption(pageCursorOption())
|
|
609
537
|
.option("--json", "Output as JSON")
|
|
610
|
-
.action(async (
|
|
538
|
+
.action(async (prompt, options) => {
|
|
539
|
+
const limit = parsePageLimit(options.limit);
|
|
611
540
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
612
541
|
const client = new ApiClient();
|
|
613
542
|
try {
|
|
614
|
-
|
|
543
|
+
// #3249 — resolve BEFORE listing: the test-case endpoint answers an
|
|
544
|
+
// unknown block with an empty page, so an unresolved identifier would
|
|
545
|
+
// print "No test cases found." and exit 0.
|
|
546
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
547
|
+
const page = normalizeCliListEnvelope(await client.listTestCases(resolvedAppId, "prompt", promptId, {
|
|
548
|
+
limit,
|
|
549
|
+
cursor: options.cursor,
|
|
550
|
+
}));
|
|
551
|
+
const items = page.items;
|
|
615
552
|
if (options.json) {
|
|
616
|
-
json(
|
|
553
|
+
json(page);
|
|
617
554
|
return;
|
|
618
555
|
}
|
|
619
|
-
if (
|
|
620
|
-
|
|
556
|
+
if (items.length === 0) {
|
|
557
|
+
printEmptyPage(page, "test cases");
|
|
621
558
|
return;
|
|
622
559
|
}
|
|
623
560
|
console.log(formatTable(items, [
|
|
@@ -626,79 +563,7 @@ Examples:
|
|
|
626
563
|
{ header: "CONFIG", key: "configId", format: (v) => v ? formatId(v) : "-" },
|
|
627
564
|
{ header: "CREATED", key: "createdAt", format: formatDate },
|
|
628
565
|
]));
|
|
629
|
-
|
|
630
|
-
catch (err) {
|
|
631
|
-
error(err.message);
|
|
632
|
-
process.exit(1);
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
// Create test case
|
|
636
|
-
tests
|
|
637
|
-
.command("create")
|
|
638
|
-
.description("Create a test case for a prompt")
|
|
639
|
-
.argument("<prompt-id>", "Prompt ID")
|
|
640
|
-
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
641
|
-
.option("--name <name>", "Test case name (required)")
|
|
642
|
-
.option("--vars <json>", "Input variables as JSON (required)")
|
|
643
|
-
.option("--pattern <regex>", "Expected output pattern (regex)")
|
|
644
|
-
.option("--contains <json>", "Expected strings to contain (JSON array)")
|
|
645
|
-
.option("--json-subset <json>", "Expected JSON subset to match")
|
|
646
|
-
.option("--config <config-id>", "Config ID to use for this test")
|
|
647
|
-
.option("--evaluator-prompt <prompt-id>", "Evaluator prompt ID")
|
|
648
|
-
.option("--evaluator-config <config-id>", "Evaluator config ID")
|
|
649
|
-
.option("--json", "Output as JSON")
|
|
650
|
-
.action(async (promptId, options) => {
|
|
651
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
652
|
-
if (!options.name || !options.vars) {
|
|
653
|
-
error("Required: --name and --vars");
|
|
654
|
-
process.exit(1);
|
|
655
|
-
}
|
|
656
|
-
let inputVariables;
|
|
657
|
-
try {
|
|
658
|
-
inputVariables = JSON.parse(options.vars);
|
|
659
|
-
}
|
|
660
|
-
catch {
|
|
661
|
-
error("Invalid JSON in --vars");
|
|
662
|
-
process.exit(1);
|
|
663
|
-
}
|
|
664
|
-
let expectedOutputContains;
|
|
665
|
-
if (options.contains) {
|
|
666
|
-
try {
|
|
667
|
-
expectedOutputContains = JSON.parse(options.contains);
|
|
668
|
-
}
|
|
669
|
-
catch {
|
|
670
|
-
error("Invalid JSON in --contains");
|
|
671
|
-
process.exit(1);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
let expectedJsonSubset;
|
|
675
|
-
if (options.jsonSubset) {
|
|
676
|
-
try {
|
|
677
|
-
expectedJsonSubset = JSON.parse(options.jsonSubset);
|
|
678
|
-
}
|
|
679
|
-
catch {
|
|
680
|
-
error("Invalid JSON in --json-subset");
|
|
681
|
-
process.exit(1);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
const client = new ApiClient();
|
|
685
|
-
try {
|
|
686
|
-
const result = await client.createTestCase(resolvedAppId, "prompt", promptId, {
|
|
687
|
-
name: options.name,
|
|
688
|
-
inputVariables,
|
|
689
|
-
expectedOutputPattern: options.pattern,
|
|
690
|
-
expectedOutputContains,
|
|
691
|
-
expectedJsonSubset,
|
|
692
|
-
configId: options.config,
|
|
693
|
-
evaluatorPromptId: options.evaluatorPrompt,
|
|
694
|
-
evaluatorConfigId: options.evaluatorConfig,
|
|
695
|
-
});
|
|
696
|
-
if (options.json) {
|
|
697
|
-
json(result);
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
success(`Test case created: ${result.name}`);
|
|
701
|
-
keyValue("Test Case ID", result.testCaseId);
|
|
566
|
+
printPageHint(page);
|
|
702
567
|
}
|
|
703
568
|
catch (err) {
|
|
704
569
|
error(err.message);
|
|
@@ -709,23 +574,24 @@ Examples:
|
|
|
709
574
|
tests
|
|
710
575
|
.command("get")
|
|
711
576
|
.description("Get test case details")
|
|
712
|
-
.argument("<prompt
|
|
577
|
+
.argument("<prompt>", "Prompt key or ID")
|
|
713
578
|
.argument("<test-case-id>", "Test Case ID")
|
|
714
579
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
715
580
|
.option("--json", "Output as JSON")
|
|
716
|
-
.action(async (
|
|
581
|
+
.action(async (prompt, testCaseId, options) => {
|
|
717
582
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
718
583
|
const client = new ApiClient();
|
|
719
584
|
try {
|
|
585
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
720
586
|
const result = await client.getTestCase(resolvedAppId, "prompt", promptId, testCaseId);
|
|
721
587
|
if (options.json) {
|
|
722
588
|
json(result);
|
|
723
589
|
return;
|
|
724
590
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
591
|
+
printResult("Test Case ID", result.testCaseId);
|
|
592
|
+
printResult("Name", result.name);
|
|
593
|
+
printResult("Config ID", result.configId || "-");
|
|
594
|
+
printResult("Created", formatDate(result.createdAt));
|
|
729
595
|
divider();
|
|
730
596
|
info("Input Variables:");
|
|
731
597
|
try {
|
|
@@ -737,7 +603,7 @@ Examples:
|
|
|
737
603
|
}
|
|
738
604
|
if (result.expectedOutputPattern) {
|
|
739
605
|
divider();
|
|
740
|
-
|
|
606
|
+
printResult("Expected Pattern", result.expectedOutputPattern);
|
|
741
607
|
}
|
|
742
608
|
if (result.expectedOutputContains) {
|
|
743
609
|
divider();
|
|
@@ -761,126 +627,10 @@ Examples:
|
|
|
761
627
|
}
|
|
762
628
|
if (result.evaluatorPromptId) {
|
|
763
629
|
divider();
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
catch (err) {
|
|
769
|
-
error(err.message);
|
|
770
|
-
process.exit(1);
|
|
771
|
-
}
|
|
772
|
-
});
|
|
773
|
-
// Update test case
|
|
774
|
-
tests
|
|
775
|
-
.command("update")
|
|
776
|
-
.description("Update a test case")
|
|
777
|
-
.argument("<prompt-id>", "Prompt ID")
|
|
778
|
-
.argument("<test-case-id>", "Test Case ID")
|
|
779
|
-
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
780
|
-
.option("--name <name>", "Test case name")
|
|
781
|
-
.option("--vars <json>", "Input variables as JSON")
|
|
782
|
-
.option("--pattern <regex>", "Expected output pattern (regex)")
|
|
783
|
-
.option("--contains <json>", "Expected strings to contain (JSON array)")
|
|
784
|
-
.option("--json-subset <json>", "Expected JSON subset to match")
|
|
785
|
-
.option("--config <config-id>", "Config ID to use for this test")
|
|
786
|
-
.option("--clear-pattern", "Clear expected pattern")
|
|
787
|
-
.option("--clear-contains", "Clear expected contains")
|
|
788
|
-
.option("--clear-json-subset", "Clear expected JSON subset")
|
|
789
|
-
.option("--json", "Output as JSON")
|
|
790
|
-
.action(async (promptId, testCaseId, options) => {
|
|
791
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
792
|
-
const payload = {};
|
|
793
|
-
if (options.name)
|
|
794
|
-
payload.name = options.name;
|
|
795
|
-
if (options.vars) {
|
|
796
|
-
try {
|
|
797
|
-
payload.inputVariables = JSON.parse(options.vars);
|
|
798
|
-
}
|
|
799
|
-
catch {
|
|
800
|
-
error("Invalid JSON in --vars");
|
|
801
|
-
process.exit(1);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
if (options.clearPattern) {
|
|
805
|
-
payload.expectedOutputPattern = null;
|
|
806
|
-
}
|
|
807
|
-
else if (options.pattern) {
|
|
808
|
-
payload.expectedOutputPattern = options.pattern;
|
|
809
|
-
}
|
|
810
|
-
if (options.clearContains) {
|
|
811
|
-
payload.expectedOutputContains = null;
|
|
812
|
-
}
|
|
813
|
-
else if (options.contains) {
|
|
814
|
-
try {
|
|
815
|
-
payload.expectedOutputContains = JSON.parse(options.contains);
|
|
816
|
-
}
|
|
817
|
-
catch {
|
|
818
|
-
error("Invalid JSON in --contains");
|
|
819
|
-
process.exit(1);
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
if (options.clearJsonSubset) {
|
|
823
|
-
payload.expectedJsonSubset = null;
|
|
824
|
-
}
|
|
825
|
-
else if (options.jsonSubset) {
|
|
826
|
-
try {
|
|
827
|
-
payload.expectedJsonSubset = JSON.parse(options.jsonSubset);
|
|
828
|
-
}
|
|
829
|
-
catch {
|
|
830
|
-
error("Invalid JSON in --json-subset");
|
|
831
|
-
process.exit(1);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
if (options.config)
|
|
835
|
-
payload.configId = options.config;
|
|
836
|
-
if (Object.keys(payload).length === 0) {
|
|
837
|
-
error("No update options specified.");
|
|
838
|
-
process.exit(1);
|
|
839
|
-
}
|
|
840
|
-
const client = new ApiClient();
|
|
841
|
-
try {
|
|
842
|
-
const result = await client.updateTestCase(resolvedAppId, "prompt", promptId, testCaseId, payload);
|
|
843
|
-
if (options.json) {
|
|
844
|
-
json(result);
|
|
845
|
-
return;
|
|
846
|
-
}
|
|
847
|
-
success("Test case updated.");
|
|
848
|
-
}
|
|
849
|
-
catch (err) {
|
|
850
|
-
error(err.message);
|
|
851
|
-
process.exit(1);
|
|
852
|
-
}
|
|
853
|
-
});
|
|
854
|
-
// Delete test case
|
|
855
|
-
tests
|
|
856
|
-
.command("delete")
|
|
857
|
-
.description("Delete a test case")
|
|
858
|
-
.argument("<prompt-id>", "Prompt ID")
|
|
859
|
-
.argument("<test-case-id>", "Test Case ID")
|
|
860
|
-
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
861
|
-
.option("-y, --yes", "Skip confirmation prompt")
|
|
862
|
-
.action(async (promptId, testCaseId, options) => {
|
|
863
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
864
|
-
if (!options.yes) {
|
|
865
|
-
const inquirer = await import("inquirer");
|
|
866
|
-
const { confirm } = await inquirer.default.prompt([
|
|
867
|
-
{
|
|
868
|
-
type: "confirm",
|
|
869
|
-
name: "confirm",
|
|
870
|
-
message: `Are you sure you want to delete test case ${testCaseId}?`,
|
|
871
|
-
default: false,
|
|
872
|
-
},
|
|
873
|
-
]);
|
|
874
|
-
if (!confirm) {
|
|
875
|
-
info("Cancelled.");
|
|
876
|
-
return;
|
|
630
|
+
printResult("Evaluator Prompt", result.evaluatorPromptId);
|
|
631
|
+
printResult("Evaluator Config", result.evaluatorConfigId || "-");
|
|
877
632
|
}
|
|
878
633
|
}
|
|
879
|
-
const client = new ApiClient();
|
|
880
|
-
try {
|
|
881
|
-
await client.deleteTestCase(resolvedAppId, "prompt", promptId, testCaseId);
|
|
882
|
-
success("Test case deleted.");
|
|
883
|
-
}
|
|
884
634
|
catch (err) {
|
|
885
635
|
error(err.message);
|
|
886
636
|
process.exit(1);
|
|
@@ -890,24 +640,22 @@ Examples:
|
|
|
890
640
|
tests
|
|
891
641
|
.command("run")
|
|
892
642
|
.description("Run a single test case")
|
|
893
|
-
.argument("<prompt
|
|
643
|
+
.argument("<prompt>", "Prompt key or ID")
|
|
894
644
|
.argument("<test-case-id>", "Test Case ID")
|
|
895
645
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
896
646
|
.option("--config <config-id>", "Override config ID")
|
|
897
647
|
.option("--json", "Output as JSON")
|
|
898
|
-
.action(async (
|
|
648
|
+
.action(async (prompt, testCaseId, options) => {
|
|
899
649
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
900
650
|
const client = new ApiClient();
|
|
901
651
|
try {
|
|
652
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
902
653
|
// Get test case to retrieve its variables
|
|
903
654
|
const testCase = await client.getTestCase(resolvedAppId, "prompt", promptId, testCaseId);
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
catch {
|
|
909
|
-
// Use empty object if parsing fails
|
|
910
|
-
}
|
|
655
|
+
// #3158 — the case's stored input IS the run's input, and the server
|
|
656
|
+
// hands it back parsed; `JSON.parse` on that object threw and the run
|
|
657
|
+
// went ahead with `{}`, disagreeing with `run-all` over the same case.
|
|
658
|
+
const variables = requireTestCaseInputVariables(testCase.inputVariables, testCaseId);
|
|
911
659
|
const result = await client.executeBlockTest(resolvedAppId, "prompt", promptId, {
|
|
912
660
|
variables,
|
|
913
661
|
testCaseId,
|
|
@@ -925,7 +673,7 @@ Examples:
|
|
|
925
673
|
error("Test FAILED");
|
|
926
674
|
}
|
|
927
675
|
keyValue("Duration", formatDuration(result.metrics?.durationMs));
|
|
928
|
-
keyValue("Tokens",
|
|
676
|
+
keyValue("Tokens", formatTokens(result.metrics));
|
|
929
677
|
if (result.verification) {
|
|
930
678
|
divider();
|
|
931
679
|
info("Verification:");
|
|
@@ -962,15 +710,18 @@ Examples:
|
|
|
962
710
|
tests
|
|
963
711
|
.command("run-all")
|
|
964
712
|
.description("Run all test cases for a prompt, or a specific subset")
|
|
965
|
-
.argument("<prompt
|
|
713
|
+
.argument("<prompt>", "Prompt key or ID")
|
|
966
714
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
967
715
|
.option("--config <config-id>", "Override config ID for all tests")
|
|
968
716
|
.option("--test-cases <ids>", "Comma-separated list of test case IDs to run (runs only these)")
|
|
969
717
|
.option("--json", "Output as JSON")
|
|
970
|
-
.action(async (
|
|
718
|
+
.action(async (prompt, options) => {
|
|
971
719
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
972
720
|
const client = new ApiClient();
|
|
973
721
|
try {
|
|
722
|
+
// #3249 — resolve first, so `Total Tests: 0` can only ever mean the
|
|
723
|
+
// prompt exists and has no cases, never that the identifier was wrong.
|
|
724
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
974
725
|
// Parse test case IDs if provided
|
|
975
726
|
let testCaseIds;
|
|
976
727
|
if (options.testCases) {
|
|
@@ -1028,15 +779,16 @@ Examples:
|
|
|
1028
779
|
tests
|
|
1029
780
|
.command("runs")
|
|
1030
781
|
.description("List recent test runs for a prompt")
|
|
1031
|
-
.argument("<prompt
|
|
782
|
+
.argument("<prompt>", "Prompt key or ID")
|
|
1032
783
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1033
784
|
.option("--limit <n>", "Maximum number of runs to show", "20")
|
|
1034
785
|
.option("--group <group>", "Filter by comparison group")
|
|
1035
786
|
.option("--json", "Output as JSON")
|
|
1036
|
-
.action(async (
|
|
787
|
+
.action(async (prompt, options) => {
|
|
1037
788
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
1038
789
|
const client = new ApiClient();
|
|
1039
790
|
try {
|
|
791
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
1040
792
|
const { items } = await client.listTestRuns(resolvedAppId, "prompt", promptId, {
|
|
1041
793
|
limit: parseInt(options.limit),
|
|
1042
794
|
comparisonGroup: options.group,
|
|
@@ -1078,24 +830,25 @@ Examples:
|
|
|
1078
830
|
.description("Run tests in parallel using workflow-based execution")
|
|
1079
831
|
.addHelpText("after", `
|
|
1080
832
|
Examples:
|
|
1081
|
-
$ primitive prompts tests batch start <prompt
|
|
1082
|
-
$ primitive prompts tests batch start <prompt
|
|
1083
|
-
$ primitive prompts tests batch status <prompt
|
|
1084
|
-
$ primitive prompts tests batch cancel <prompt
|
|
833
|
+
$ primitive prompts tests batch start <prompt>
|
|
834
|
+
$ primitive prompts tests batch start <prompt> --test-cases 01ABC,01DEF
|
|
835
|
+
$ primitive prompts tests batch status <prompt> <batch-id>
|
|
836
|
+
$ primitive prompts tests batch cancel <prompt> <batch-id>
|
|
1085
837
|
`);
|
|
1086
838
|
// Start batch tests
|
|
1087
839
|
batch
|
|
1088
840
|
.command("start")
|
|
1089
841
|
.description("Start parallel batch test execution")
|
|
1090
|
-
.argument("<prompt
|
|
842
|
+
.argument("<prompt>", "Prompt key or ID")
|
|
1091
843
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1092
844
|
.option("--config <config-id>", "Override config ID for all tests")
|
|
1093
845
|
.option("--test-cases <ids>", "Comma-separated list of test case IDs to run")
|
|
1094
846
|
.option("--json", "Output as JSON")
|
|
1095
|
-
.action(async (
|
|
847
|
+
.action(async (prompt, options) => {
|
|
1096
848
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
1097
849
|
const client = new ApiClient();
|
|
1098
850
|
try {
|
|
851
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
1099
852
|
let testCaseIds;
|
|
1100
853
|
if (options.testCases) {
|
|
1101
854
|
testCaseIds = options.testCases.split(",").map((id) => id.trim()).filter(Boolean);
|
|
@@ -1132,27 +885,37 @@ Examples:
|
|
|
1132
885
|
batch
|
|
1133
886
|
.command("status")
|
|
1134
887
|
.description("Get status of a batch test execution")
|
|
1135
|
-
.argument("<prompt
|
|
888
|
+
.argument("<prompt>", "Prompt key or ID")
|
|
1136
889
|
.argument("<batch-id>", "Batch ID")
|
|
1137
890
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1138
891
|
.option("--wait", "Wait for completion (polls every 2 seconds)")
|
|
1139
892
|
.option("--json", "Output as JSON")
|
|
1140
|
-
.action(async (
|
|
893
|
+
.action(async (prompt, batchId, options) => {
|
|
1141
894
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
1142
895
|
const client = new ApiClient();
|
|
1143
896
|
try {
|
|
897
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
1144
898
|
const fetchStatus = async () => {
|
|
1145
899
|
return client.getBatchTestStatus(resolvedAppId, "prompt", promptId, batchId);
|
|
1146
900
|
};
|
|
1147
901
|
let result = await fetchStatus();
|
|
1148
902
|
if (options.wait && result.status === "running") {
|
|
1149
903
|
info("Waiting for batch completion...");
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
904
|
+
// #1969 — shared poll primitive (delay-then-probe every 2s, honoring
|
|
905
|
+
// an AbortSignal). `onError: "throw"` preserves the prior loop's
|
|
906
|
+
// fail-fast behavior: a throwing fetchStatus (expired token, deleted
|
|
907
|
+
// batch → 401/404) propagates to the command-level catch below and
|
|
908
|
+
// exits non-zero, rather than spinning forever with a frozen progress
|
|
909
|
+
// line. (The follow/watch tail loops legitimately keep "continue".)
|
|
910
|
+
const controller = new AbortController();
|
|
911
|
+
result = await pollUntil(fetchStatus, (r) => r.status !== "running", {
|
|
912
|
+
intervalMs: 2000,
|
|
913
|
+
signal: controller.signal,
|
|
914
|
+
onError: "throw",
|
|
915
|
+
// Progress goes to stderr so it can't corrupt JSON on stdout under --json.
|
|
916
|
+
onValue: (r) => progress(` Completed: ${r.completed}/${r.results?.length || 0} `),
|
|
917
|
+
});
|
|
918
|
+
progressEnd();
|
|
1156
919
|
}
|
|
1157
920
|
if (options.json) {
|
|
1158
921
|
json(result);
|
|
@@ -1188,23 +951,22 @@ Examples:
|
|
|
1188
951
|
batch
|
|
1189
952
|
.command("cancel")
|
|
1190
953
|
.description("Cancel a running batch test execution")
|
|
1191
|
-
.argument("<prompt
|
|
954
|
+
.argument("<prompt>", "Prompt key or ID")
|
|
1192
955
|
.argument("<batch-id>", "Batch ID")
|
|
1193
956
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1194
957
|
.option("-y, --yes", "Skip confirmation prompt")
|
|
1195
958
|
.option("--json", "Output as JSON")
|
|
1196
|
-
.action(async (
|
|
959
|
+
.action(async (prompt, batchId, options) => {
|
|
1197
960
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
1198
961
|
if (!options.yes) {
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
{
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
]);
|
|
962
|
+
let confirm;
|
|
963
|
+
try {
|
|
964
|
+
confirm = await confirmPrompt(`Are you sure you want to cancel batch ${batchId}?`);
|
|
965
|
+
}
|
|
966
|
+
catch (err) {
|
|
967
|
+
error(err.message);
|
|
968
|
+
process.exit(1);
|
|
969
|
+
}
|
|
1208
970
|
if (!confirm) {
|
|
1209
971
|
info("Cancelled.");
|
|
1210
972
|
return;
|
|
@@ -1212,6 +974,7 @@ Examples:
|
|
|
1212
974
|
}
|
|
1213
975
|
const client = new ApiClient();
|
|
1214
976
|
try {
|
|
977
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
1215
978
|
// First get current status to get instance IDs
|
|
1216
979
|
const status = await client.getBatchTestStatus(resolvedAppId, "prompt", promptId, batchId);
|
|
1217
980
|
// Get instance IDs that are still running
|
|
@@ -1244,29 +1007,32 @@ Examples:
|
|
|
1244
1007
|
// ============================================
|
|
1245
1008
|
const attachments = tests
|
|
1246
1009
|
.command("attachments")
|
|
1247
|
-
.description("
|
|
1010
|
+
.description("Inspect test case file attachments")
|
|
1248
1011
|
.addHelpText("after", `
|
|
1012
|
+
Attachments are authored as files in prompts/<key>.tests/<name>/ and applied
|
|
1013
|
+
with \`primitive config push\` (#2769); removing one and running
|
|
1014
|
+
\`config push --prune\` deletes it server-side. These verbs read server state.
|
|
1015
|
+
|
|
1249
1016
|
Examples:
|
|
1250
|
-
$ primitive prompts tests attachments list <prompt
|
|
1251
|
-
$ primitive prompts tests attachments
|
|
1252
|
-
$ primitive prompts tests attachments download <prompt-id> <test-case-id> document.pdf
|
|
1253
|
-
$ primitive prompts tests attachments delete <prompt-id> <test-case-id> document.pdf
|
|
1017
|
+
$ primitive prompts tests attachments list <prompt> <test-case-id>
|
|
1018
|
+
$ primitive prompts tests attachments download <prompt> <test-case-id> document.pdf
|
|
1254
1019
|
`);
|
|
1255
1020
|
// List attachments
|
|
1256
1021
|
attachments
|
|
1257
1022
|
.command("list")
|
|
1258
1023
|
.description("List attachments for a test case")
|
|
1259
|
-
.argument("<prompt
|
|
1024
|
+
.argument("<prompt>", "Prompt key or ID")
|
|
1260
1025
|
.argument("<test-case-id>", "Test Case ID")
|
|
1261
1026
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1262
1027
|
.option("--json", "Output as JSON")
|
|
1263
|
-
.action(async (
|
|
1028
|
+
.action(async (prompt, testCaseId, options) => {
|
|
1264
1029
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
1265
1030
|
const client = new ApiClient();
|
|
1266
1031
|
try {
|
|
1032
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
1267
1033
|
const result = await client.listTestCaseAttachments(resolvedAppId, "prompt", promptId, testCaseId);
|
|
1268
1034
|
if (options.json) {
|
|
1269
|
-
json(result.attachments);
|
|
1035
|
+
json(wholeListEnvelope(result.attachments));
|
|
1270
1036
|
return;
|
|
1271
1037
|
}
|
|
1272
1038
|
if (!result.attachments || result.attachments.length === 0) {
|
|
@@ -1288,62 +1054,20 @@ Examples:
|
|
|
1288
1054
|
process.exit(1);
|
|
1289
1055
|
}
|
|
1290
1056
|
});
|
|
1291
|
-
// Upload attachment
|
|
1292
|
-
attachments
|
|
1293
|
-
.command("upload")
|
|
1294
|
-
.description("Upload a file attachment to a test case")
|
|
1295
|
-
.argument("<prompt-id>", "Prompt ID")
|
|
1296
|
-
.argument("<test-case-id>", "Test Case ID")
|
|
1297
|
-
.argument("<file-path>", "Path to the file to upload")
|
|
1298
|
-
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1299
|
-
.option("--name <name>", "Override the filename")
|
|
1300
|
-
.option("--json", "Output as JSON")
|
|
1301
|
-
.action(async (promptId, testCaseId, filePath, options) => {
|
|
1302
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
1303
|
-
const client = new ApiClient();
|
|
1304
|
-
try {
|
|
1305
|
-
// Check file exists and get stats
|
|
1306
|
-
const stats = statSync(filePath);
|
|
1307
|
-
if (!stats.isFile()) {
|
|
1308
|
-
error(`Not a file: ${filePath}`);
|
|
1309
|
-
process.exit(1);
|
|
1310
|
-
}
|
|
1311
|
-
// Check file size (10 MB limit)
|
|
1312
|
-
const TEN_MB = 10 * 1024 * 1024;
|
|
1313
|
-
if (stats.size > TEN_MB) {
|
|
1314
|
-
error(`File too large (max 10MB): ${formatFileSize(stats.size)}`);
|
|
1315
|
-
process.exit(1);
|
|
1316
|
-
}
|
|
1317
|
-
const filename = options.name || basename(filePath);
|
|
1318
|
-
const data = readFileSync(filePath);
|
|
1319
|
-
const contentType = mimeLookup(filePath) || "application/octet-stream";
|
|
1320
|
-
const result = await client.uploadTestCaseAttachment(resolvedAppId, "prompt", promptId, testCaseId, filename, data, contentType);
|
|
1321
|
-
if (options.json) {
|
|
1322
|
-
json(result);
|
|
1323
|
-
return;
|
|
1324
|
-
}
|
|
1325
|
-
success(`Uploaded: ${filename}`);
|
|
1326
|
-
keyValue("Size", formatFileSize(result.attachment.size));
|
|
1327
|
-
keyValue("Type", result.attachment.contentType);
|
|
1328
|
-
}
|
|
1329
|
-
catch (err) {
|
|
1330
|
-
error(err.message);
|
|
1331
|
-
process.exit(1);
|
|
1332
|
-
}
|
|
1333
|
-
});
|
|
1334
1057
|
// Download attachment
|
|
1335
1058
|
attachments
|
|
1336
1059
|
.command("download")
|
|
1337
1060
|
.description("Download a file attachment from a test case")
|
|
1338
|
-
.argument("<prompt
|
|
1061
|
+
.argument("<prompt>", "Prompt key or ID")
|
|
1339
1062
|
.argument("<test-case-id>", "Test Case ID")
|
|
1340
1063
|
.argument("<filename>", "Name of the file to download")
|
|
1341
1064
|
.argument("[output-path]", "Output path (defaults to current directory)")
|
|
1342
1065
|
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1343
|
-
.action(async (
|
|
1066
|
+
.action(async (prompt, testCaseId, filename, outputPath, options) => {
|
|
1344
1067
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
1345
1068
|
const client = new ApiClient();
|
|
1346
1069
|
try {
|
|
1070
|
+
const promptId = await resolvePromptId(client, resolvedAppId, prompt);
|
|
1347
1071
|
const result = await client.downloadTestCaseAttachment(resolvedAppId, "prompt", promptId, testCaseId, filename);
|
|
1348
1072
|
const finalPath = outputPath || filename;
|
|
1349
1073
|
writeFileSync(finalPath, result.data);
|
|
@@ -1356,42 +1080,6 @@ Examples:
|
|
|
1356
1080
|
process.exit(1);
|
|
1357
1081
|
}
|
|
1358
1082
|
});
|
|
1359
|
-
// Delete attachment
|
|
1360
|
-
attachments
|
|
1361
|
-
.command("delete")
|
|
1362
|
-
.description("Delete a file attachment from a test case")
|
|
1363
|
-
.argument("<prompt-id>", "Prompt ID")
|
|
1364
|
-
.argument("<test-case-id>", "Test Case ID")
|
|
1365
|
-
.argument("<filename>", "Name of the file to delete")
|
|
1366
|
-
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1367
|
-
.option("-y, --yes", "Skip confirmation prompt")
|
|
1368
|
-
.action(async (promptId, testCaseId, filename, options) => {
|
|
1369
|
-
const resolvedAppId = resolveAppId(undefined, options);
|
|
1370
|
-
if (!options.yes) {
|
|
1371
|
-
const inquirer = await import("inquirer");
|
|
1372
|
-
const { confirm } = await inquirer.default.prompt([
|
|
1373
|
-
{
|
|
1374
|
-
type: "confirm",
|
|
1375
|
-
name: "confirm",
|
|
1376
|
-
message: `Are you sure you want to delete attachment "${filename}"?`,
|
|
1377
|
-
default: false,
|
|
1378
|
-
},
|
|
1379
|
-
]);
|
|
1380
|
-
if (!confirm) {
|
|
1381
|
-
info("Cancelled.");
|
|
1382
|
-
return;
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
const client = new ApiClient();
|
|
1386
|
-
try {
|
|
1387
|
-
await client.deleteTestCaseAttachment(resolvedAppId, "prompt", promptId, testCaseId, filename);
|
|
1388
|
-
success(`Deleted: ${filename}`);
|
|
1389
|
-
}
|
|
1390
|
-
catch (err) {
|
|
1391
|
-
error(err.message);
|
|
1392
|
-
process.exit(1);
|
|
1393
|
-
}
|
|
1394
|
-
});
|
|
1395
1083
|
}
|
|
1396
1084
|
function formatFileSize(bytes) {
|
|
1397
1085
|
if (bytes < 1024)
|