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
|
@@ -0,0 +1,1630 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `primitive functions` — server functions' read and operational verbs
|
|
3
|
+
* (#3179, project `server-functions`).
|
|
4
|
+
*
|
|
5
|
+
* A server function is the SIXTH status-carrying type (`docs/cli-design.md`,
|
|
6
|
+
* §Object availability): it exposes a public endpoint, so it carries
|
|
7
|
+
* `active | inactive | archived`, the `enable`/`disable` pair, and the
|
|
8
|
+
* `archive` spelling of the delete flow. It carries no `create`, `update` or
|
|
9
|
+
* `delete` verb — the function is authored in `functions/<key>.toml` beside the
|
|
10
|
+
* TypeScript its `entry` names, applied with `config push`, and prune-by-push
|
|
11
|
+
* stays the only CLI hard-delete path.
|
|
12
|
+
*
|
|
13
|
+
* The rule worth stating loudly here, because a function IS code on a public
|
|
14
|
+
* endpoint: a `config push` to a DISABLED function updates the code and leaves
|
|
15
|
+
* it out of service. `status` has one writer pair and push is not one of them
|
|
16
|
+
* (#2803's resurrection guard) — otherwise shipping a fix would silently put
|
|
17
|
+
* an endpoint an operator had taken down back in service.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, readdirSync } from "fs";
|
|
20
|
+
import { join, relative as relativePath } from "path";
|
|
21
|
+
import { ApiClient } from "../lib/api-client.js";
|
|
22
|
+
import { resolveAppId } from "../lib/config.js";
|
|
23
|
+
import { resolveSyncDir } from "../lib/sync-paths.js";
|
|
24
|
+
import { FunctionSchemaCodegenError, functionSchemaSources, } from "../lib/function-schema-codegen.js";
|
|
25
|
+
import { functionTypeWiringProblems, staleFunctionTypeArtifacts, writeFunctionTypeArtifacts, } from "../lib/function-db-types.js";
|
|
26
|
+
import { AGENT_CLIENT_FILE_SUFFIX, promptSchemaSources, treeDeclaresAgentPrompt, } from "../lib/prompt-schema-codegen.js";
|
|
27
|
+
import { generateFunctionSwiftTypes } from "../lib/swift-codegen/functionGenerator.js";
|
|
28
|
+
import { AGENT_SWIFT_FILE_SUFFIX } from "../lib/swift-codegen/agentGenerator.js";
|
|
29
|
+
import { SwiftCodegenError } from "../lib/swift-codegen/schemaToSwift.js";
|
|
30
|
+
import { success, error, info, warn, keyValue, result as printResult, formatTable, formatDate, formatStatus, json, jsonLine, divider, } from "../lib/output.js";
|
|
31
|
+
import { confirmPrompt } from "../lib/confirm-prompt.js";
|
|
32
|
+
import { FUNCTION_LOG_COLUMNS, functionLogCells, functionLogLine, } from "../lib/function-log-row.js";
|
|
33
|
+
import { activatedLine, activationOutput, activationPrompt, activationRefusalLine, functionVersionRows, newerVersionLine, versionLine, } from "../lib/function-versions.js";
|
|
34
|
+
import { parseStatusFilter } from "../lib/object-status-filter.js";
|
|
35
|
+
import { renderStepRunTable } from "../lib/step-run-table.js";
|
|
36
|
+
import { triggerKindsCell, triggerTableRows, } from "../lib/function-trigger-listing.js";
|
|
37
|
+
import { renderRunLogs } from "../lib/function-log-lines.js";
|
|
38
|
+
import { buildWorkflowStepEnvelope } from "../lib/log-inspection.js";
|
|
39
|
+
import { followLoop } from "../lib/watch.js";
|
|
40
|
+
import { buildLogFollowSource } from "../lib/function-log-tail.js";
|
|
41
|
+
import { toFunctionLogInspectionRow } from "../lib/log-inspection.js";
|
|
42
|
+
import { decidePageContinuation, describeIdentity, exitCodeForInvokeStatus, exitCodeForRunStatus, invokeIdLines, logsFlagConflict, refusalLines, renderIdLines, resolveRunFlags, runRuntimeCell, runSliceCells, runtimeForExecutionMode, resumeCommand, startIdLines, userTokenName, waitDelayMs, withMintedToken, EXIT_INTERRUPTED, EXIT_TIMED_OUT, USER_TOKEN_TTL, WAIT_DEFAULT_TIMEOUT_SECONDS, } from "../lib/function-run.js";
|
|
43
|
+
import { wholeListEnvelope } from "../lib/paginate.js";
|
|
44
|
+
/**
|
|
45
|
+
* How far back one `--follow` tick will page to meet its high-water mark.
|
|
46
|
+
*
|
|
47
|
+
* A bound on one CALL rather than "until you get there": the listing is
|
|
48
|
+
* newest-first, so an unbounded walk on a busy function would page through a
|
|
49
|
+
* whole window of history in one go. Ten pages at the default limit is 250
|
|
50
|
+
* invocations between polls — comfortably past what two seconds of an admitted
|
|
51
|
+
* caller can record. A tail that does fall behind carries its walk into the
|
|
52
|
+
* next call rather than skipping the gap (CR3287-017): the rows between the
|
|
53
|
+
* mark and now are all printed, in order, a bounded number of pages at a time.
|
|
54
|
+
*/
|
|
55
|
+
const FOLLOW_MAX_PAGES_PER_TICK = 10;
|
|
56
|
+
/**
|
|
57
|
+
* Which cell each shared invocation column reads, and how it is rendered.
|
|
58
|
+
*
|
|
59
|
+
* The ORDER lives in `FUNCTION_LOG_COLUMNS` (#3462); this is only the mapping
|
|
60
|
+
* from a header to the cell `functionLogCells` builds for it, so adding a
|
|
61
|
+
* column is one edit in each place rather than three copies of the order.
|
|
62
|
+
*/
|
|
63
|
+
const LOG_COLUMN_KEYS = {
|
|
64
|
+
TIME: "startedAt",
|
|
65
|
+
STATUS: "status",
|
|
66
|
+
TRIGGER: "trigger",
|
|
67
|
+
RUNTIME: "runtime",
|
|
68
|
+
VERSION: "configId",
|
|
69
|
+
"RUN/INVOCATION ID": "runOrInvocation",
|
|
70
|
+
CODE: "errorCode",
|
|
71
|
+
ERROR: "firstError",
|
|
72
|
+
};
|
|
73
|
+
const LOG_COLUMN_FORMATS = {
|
|
74
|
+
TIME: formatDate,
|
|
75
|
+
STATUS: formatStatus,
|
|
76
|
+
};
|
|
77
|
+
export function registerFunctionsCommands(program) {
|
|
78
|
+
const functions = program
|
|
79
|
+
.command("functions")
|
|
80
|
+
.description("Inspect and operate server functions")
|
|
81
|
+
.addHelpText("after", `
|
|
82
|
+
Examples:
|
|
83
|
+
$ primitive config create function greet # then edit functions/greet.toml and: primitive config push
|
|
84
|
+
$ primitive functions list
|
|
85
|
+
$ primitive functions get 01HXY...
|
|
86
|
+
$ primitive functions disable 01HXY... # out of service; pushes still land
|
|
87
|
+
$ primitive functions archive 01HXY... # retire it (soft delete)
|
|
88
|
+
`);
|
|
89
|
+
// Configuration is authored in TOML and applied with `config push` (#2645):
|
|
90
|
+
// this group carries no config-setting flags, and the code the function runs
|
|
91
|
+
// is the TypeScript beside its file rather than anything a verb can send.
|
|
92
|
+
functions.addHelpText("after", `
|
|
93
|
+
Configuration lives in functions/<key>.toml, and the code beside it:
|
|
94
|
+
$ primitive config fields function # the file's keys, types and defaults
|
|
95
|
+
$ primitive config create function <key> # scaffold one locally
|
|
96
|
+
$ primitive config set function/<key> <path>=<value>
|
|
97
|
+
$ primitive config push --only function/<key>
|
|
98
|
+
`);
|
|
99
|
+
functions
|
|
100
|
+
.command("list")
|
|
101
|
+
.description("List server functions and their triggers")
|
|
102
|
+
.argument("[app-id]", "App ID (uses current app if not specified)")
|
|
103
|
+
.option("--app <app-id>", "App ID")
|
|
104
|
+
.option("--status <status>", "Filter by status: active, inactive, archived")
|
|
105
|
+
.option("--json", "Output as JSON")
|
|
106
|
+
.action(async (appId, options) => {
|
|
107
|
+
const resolvedAppId = resolveAppId(appId, options);
|
|
108
|
+
const client = new ApiClient();
|
|
109
|
+
try {
|
|
110
|
+
const status = parseStatusFilter("functions", options.status, [
|
|
111
|
+
"active",
|
|
112
|
+
"inactive",
|
|
113
|
+
"archived",
|
|
114
|
+
]);
|
|
115
|
+
// The admin listing drains `functionsByApp` server-side, so there is no
|
|
116
|
+
// cursor to follow and the filter is applied here.
|
|
117
|
+
const { items } = await client.listFunctions(resolvedAppId);
|
|
118
|
+
const filtered = status
|
|
119
|
+
? (items || []).filter((f) => String(f.status ?? "active") === status)
|
|
120
|
+
: items || [];
|
|
121
|
+
if (options.json) {
|
|
122
|
+
json(wholeListEnvelope(filtered));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (filtered.length === 0) {
|
|
126
|
+
info("No functions found.");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
// #3690 — which functions a webhook or a schedule reaches, in one call.
|
|
130
|
+
// The standalone `webhooks list` and `cron-triggers list` hide the
|
|
131
|
+
// rows a function owns (#3181), so without this the only way to find
|
|
132
|
+
// an app's triggers was `functions get`, one function at a time.
|
|
133
|
+
console.log(formatTable(filtered.map((f) => ({
|
|
134
|
+
...f,
|
|
135
|
+
triggersCell: triggerKindsCell(f.triggers),
|
|
136
|
+
})), [
|
|
137
|
+
{ header: "KEY", key: "functionKey" },
|
|
138
|
+
{ header: "ID", key: "functionId" },
|
|
139
|
+
{ header: "STATUS", key: "status", format: formatStatus },
|
|
140
|
+
{ header: "TRIGGERS", key: "triggersCell" },
|
|
141
|
+
{ header: "MODIFIED", key: "modifiedAt", format: formatDate },
|
|
142
|
+
]));
|
|
143
|
+
const triggerRows = triggerTableRows(filtered);
|
|
144
|
+
if (triggerRows.length > 0) {
|
|
145
|
+
console.log("");
|
|
146
|
+
console.log(formatTable(triggerRows, [
|
|
147
|
+
{ header: "FUNCTION", key: "functionKey" },
|
|
148
|
+
{ header: "KIND", key: "kind" },
|
|
149
|
+
{ header: "NAME", key: "name" },
|
|
150
|
+
{ header: "SCHEDULE", key: "schedule" },
|
|
151
|
+
{ header: "STATUS", key: "status", format: formatStatus },
|
|
152
|
+
{ header: "NEXT FIRE", key: "nextFireAt", format: formatDate },
|
|
153
|
+
{
|
|
154
|
+
header: "LAST FIRE",
|
|
155
|
+
key: "lastFireAt",
|
|
156
|
+
format: (value) => (value ? formatDate(value) : "never"),
|
|
157
|
+
},
|
|
158
|
+
]));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
error(err.message);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
functions
|
|
167
|
+
.command("get")
|
|
168
|
+
.description("Get a server function, and the version it currently runs")
|
|
169
|
+
.argument("<function-id>", "Function ID")
|
|
170
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
171
|
+
.option("--json", "Output as JSON")
|
|
172
|
+
.action(async (functionId, options) => {
|
|
173
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
174
|
+
const client = new ApiClient();
|
|
175
|
+
try {
|
|
176
|
+
const record = await client.getFunction(resolvedAppId, functionId);
|
|
177
|
+
// The versions are append-only and nameless; `activeConfigId` is the
|
|
178
|
+
// only thing that says which one runs, so a listing without it would
|
|
179
|
+
// report the newest rather than the live one.
|
|
180
|
+
//
|
|
181
|
+
// #3462 — a POINT READ, not a drained listing scanned for the id. Past
|
|
182
|
+
// 25 versions the listing is a page and the active version may not be
|
|
183
|
+
// on it at all; one read by id is both cheaper and correct.
|
|
184
|
+
let active = null;
|
|
185
|
+
if (record?.activeConfigId) {
|
|
186
|
+
// #3455 — this single read is also what carries
|
|
187
|
+
// `documentSchemaModels`: the models this version can seed are
|
|
188
|
+
// derived from the schema INSIDE the envelope rather than stored on
|
|
189
|
+
// the row, so the LISTING (one row per version, no R2 read) cannot
|
|
190
|
+
// carry them and only the point read can. Best-effort — an older
|
|
191
|
+
// server sends no such field, and the line then reads exactly as it
|
|
192
|
+
// does for a version pushed without a schema.
|
|
193
|
+
try {
|
|
194
|
+
active = await client.getFunctionConfig(resolvedAppId, functionId, String(record.activeConfigId));
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
active = null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// #3462 — one row of the listing answers "is the active version the
|
|
201
|
+
// newest", which is what tells an operator this function is rolled back.
|
|
202
|
+
let newest = null;
|
|
203
|
+
try {
|
|
204
|
+
const page = await client.listFunctionConfigs(resolvedAppId, functionId, {
|
|
205
|
+
limit: 1,
|
|
206
|
+
});
|
|
207
|
+
newest = (page.items || [])[0] ?? null;
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
newest = null;
|
|
211
|
+
}
|
|
212
|
+
const newer = newerVersionLine(newest, record?.activeConfigId, formatDate);
|
|
213
|
+
if (options.json) {
|
|
214
|
+
json({
|
|
215
|
+
...record,
|
|
216
|
+
activeConfig: active,
|
|
217
|
+
...(newer ? { newerVersion: newest } : {}),
|
|
218
|
+
});
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
printResult("Function ID", record.functionId);
|
|
222
|
+
printResult("Key", record.functionKey);
|
|
223
|
+
// #3462 — which code this function is running, beside its identity. The
|
|
224
|
+
// version was only reachable by reading the section below, or by asking
|
|
225
|
+
// the API; an operator answering "what is deployed" reads this line.
|
|
226
|
+
printResult("Version", versionLine({
|
|
227
|
+
activeConfigId: record.activeConfigId,
|
|
228
|
+
contentHash: active?.contentHash,
|
|
229
|
+
}));
|
|
230
|
+
printResult("Description", record.description || "-");
|
|
231
|
+
printResult("Status", formatStatus(record.status));
|
|
232
|
+
printResult("Access", record.access || "-");
|
|
233
|
+
printResult("Input Schema", record.inputSchema || "-");
|
|
234
|
+
printResult("Output Schema", record.outputSchema || "-");
|
|
235
|
+
divider();
|
|
236
|
+
info("Active version:");
|
|
237
|
+
if (!active) {
|
|
238
|
+
printResult(" Version", "none — no code has been pushed yet");
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
printResult(" Config ID", active.configId);
|
|
242
|
+
printResult(" Entry", active.entry);
|
|
243
|
+
printResult(" Content Hash", active.contentHash || "-");
|
|
244
|
+
// #3482 — no Mode line. The config says nothing about how a
|
|
245
|
+
// function runs: `invoke` runs it inside the request, `start` runs
|
|
246
|
+
// it as a task, and every function takes both.
|
|
247
|
+
printResult(" Limits", active.limits || "-");
|
|
248
|
+
// #3182 — what this version declared: since #3279 the egress
|
|
249
|
+
// allowlist, the secrets that may enter the sandbox and the
|
|
250
|
+
// high-blast opt-ins. Shown here because `functions get` is where an
|
|
251
|
+
// operator asks what a deployed function is configured to reach.
|
|
252
|
+
const capabilities = Array.isArray(active.capabilities)
|
|
253
|
+
? active.capabilities
|
|
254
|
+
: [];
|
|
255
|
+
printResult(" Capabilities", capabilities.length > 0 ? capabilities.join(", ") : "-");
|
|
256
|
+
// #3462 — who put this version live and when, from either door (a
|
|
257
|
+
// push or `functions activate`). A header written before that change
|
|
258
|
+
// carries neither field and reads `unknown` rather than naming an
|
|
259
|
+
// actor nobody recorded.
|
|
260
|
+
printResult(" Activated", activatedLine(record, formatDate));
|
|
261
|
+
if (newer) {
|
|
262
|
+
printResult(" Newer version", newer);
|
|
263
|
+
}
|
|
264
|
+
// #3455 — the document models this version can SEED on a first
|
|
265
|
+
// write. An operator asking "why did the first write not seed
|
|
266
|
+
// `institution`" gets the answer here: either the model is in this
|
|
267
|
+
// list, or the version was pushed without a document schema at all.
|
|
268
|
+
// A server that predates the field sends nothing, which reads the
|
|
269
|
+
// same way as a version pushed without one.
|
|
270
|
+
const documentSchemaModels = Array.isArray(active.documentSchemaModels)
|
|
271
|
+
? active.documentSchemaModels
|
|
272
|
+
: null;
|
|
273
|
+
printResult(" Document schema", documentSchemaModels === null
|
|
274
|
+
? "none — this version was pushed without one"
|
|
275
|
+
: documentSchemaModels.length > 0
|
|
276
|
+
? documentSchemaModels.join(", ")
|
|
277
|
+
: "declares no models");
|
|
278
|
+
}
|
|
279
|
+
// #3181 — the ONE surface a function's triggers are visible on. The
|
|
280
|
+
// hidden `AppWebhook` and `CronTrigger` rows are omitted from
|
|
281
|
+
// `webhooks list`, `cron-triggers list` and `config pull` by design, so
|
|
282
|
+
// without this an operator could not find the receiver URL a provider
|
|
283
|
+
// must be pointed at, or the schedule the platform actually holds.
|
|
284
|
+
divider();
|
|
285
|
+
info("Triggers:");
|
|
286
|
+
const triggers = record.triggers ?? { webhook: null, cron: [] };
|
|
287
|
+
const cronEntries = Array.isArray(triggers.cron)
|
|
288
|
+
? triggers.cron
|
|
289
|
+
: [];
|
|
290
|
+
// #3483 — an OLDER server still answers a `databases` key, and this
|
|
291
|
+
// command ignores it rather than branching on it: the trigger it
|
|
292
|
+
// described was removed, so a section about it would tell an operator
|
|
293
|
+
// that a write runs this function when nothing does.
|
|
294
|
+
if (!triggers.webhook && cronEntries.length === 0) {
|
|
295
|
+
printResult(" Triggers", "none — this function is invoked over HTTP only");
|
|
296
|
+
}
|
|
297
|
+
if (triggers.webhook) {
|
|
298
|
+
printResult(" Webhook", triggers.webhook.webhookId);
|
|
299
|
+
printResult(" URL", triggers.webhook.url || "-");
|
|
300
|
+
printResult(" Scheme", triggers.webhook.verificationScheme || "-");
|
|
301
|
+
printResult(" Status", formatStatus(triggers.webhook.status));
|
|
302
|
+
printResult(" Signing Secret", triggers.webhook.signingSecret || "-");
|
|
303
|
+
printResult(" Last Triggered", triggers.webhook.lastTriggeredAt
|
|
304
|
+
? formatDate(triggers.webhook.lastTriggeredAt)
|
|
305
|
+
: "never");
|
|
306
|
+
}
|
|
307
|
+
if (cronEntries.length > 0) {
|
|
308
|
+
console.log(formatTable(cronEntries, [
|
|
309
|
+
{ header: "NAME", key: "name" },
|
|
310
|
+
{ header: "ID", key: "triggerId" },
|
|
311
|
+
{ header: "CRON", key: "cron" },
|
|
312
|
+
// #3454 — which runner this schedule's fires use. Always one of
|
|
313
|
+
// the two words: an `any` function's entry names it, and a
|
|
314
|
+
// locked one's takes its lock.
|
|
315
|
+
{ header: "TZ", key: "timezone" },
|
|
316
|
+
{ header: "STATUS", key: "status", format: formatStatus },
|
|
317
|
+
{ header: "NEXT FIRE", key: "nextFireAt", format: formatDate },
|
|
318
|
+
{ header: "FIRED", key: "firedCount" },
|
|
319
|
+
{ header: "LAST RUN", key: "lastTriggeredRunId" },
|
|
320
|
+
]));
|
|
321
|
+
}
|
|
322
|
+
// #3187 / #3279 — the ACTIVE version's manifest: what it registered
|
|
323
|
+
// and what it TOUCHES, from the push-time collection. Documentation,
|
|
324
|
+
// never authorization — function code acts as the system, and this
|
|
325
|
+
// is what lets an operator see what a version reaches without opening
|
|
326
|
+
// the bundle (principle 8). A server that predates the manifest block
|
|
327
|
+
// sends only `queries`.
|
|
328
|
+
divider();
|
|
329
|
+
info("Manifest (documentation, not authorization):");
|
|
330
|
+
const manifest = record.manifest && typeof record.manifest === "object"
|
|
331
|
+
? record.manifest
|
|
332
|
+
: { queries: Array.isArray(record.queries) ? record.queries : [] };
|
|
333
|
+
const queries = Array.isArray(manifest.queries) ? manifest.queries : [];
|
|
334
|
+
const models = Array.isArray(manifest.models) ? manifest.models : [];
|
|
335
|
+
const families = Array.isArray(manifest.families)
|
|
336
|
+
? manifest.families
|
|
337
|
+
: [];
|
|
338
|
+
if (!record.manifest) {
|
|
339
|
+
printResult(" Manifest", "none recorded — this version was pushed without a manifest");
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
printResult(" Models", (models.length > 0 ? models.join(", ") : "none named") +
|
|
343
|
+
// The marker is the difference between "names none" and "names
|
|
344
|
+
// these, and possibly more" (D3279-006).
|
|
345
|
+
(manifest.dynamicModels === true
|
|
346
|
+
? " (+ a model name resolved at run time)"
|
|
347
|
+
: ""));
|
|
348
|
+
printResult(" Families", families.length > 0 ? families.join(", ") : "none reached");
|
|
349
|
+
}
|
|
350
|
+
if (queries.length === 0) {
|
|
351
|
+
if (record.manifest)
|
|
352
|
+
printResult(" Queries", "none registered");
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
console.log(formatTable(queries, [
|
|
356
|
+
{ header: "NAME", key: "name" },
|
|
357
|
+
{ header: "KIND", key: "kind" },
|
|
358
|
+
{
|
|
359
|
+
header: "MODELS",
|
|
360
|
+
key: "models",
|
|
361
|
+
format: (value) => Array.isArray(value) && value.length > 0 ? value.join(", ") : "-",
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
header: "SCOPED",
|
|
365
|
+
key: "callerScoped",
|
|
366
|
+
format: (value) => (value ? "$caller" : "no"),
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
header: "CACHE",
|
|
370
|
+
key: "cache",
|
|
371
|
+
format: (value) => (value ? `${value.ttlMs}ms` : "off"),
|
|
372
|
+
},
|
|
373
|
+
]));
|
|
374
|
+
}
|
|
375
|
+
divider();
|
|
376
|
+
printResult("Created", formatDate(record.createdAt));
|
|
377
|
+
printResult("Modified", formatDate(record.modifiedAt));
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
error(err.message);
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
// ── #3462: a function's versions, and pointing it back at one ─────────
|
|
385
|
+
//
|
|
386
|
+
// Every changing push mints an immutable version and repoints the function at
|
|
387
|
+
// it. That was already true and already stored — and entirely invisible: an
|
|
388
|
+
// operator could not list a function's versions, and a bad push was undone by
|
|
389
|
+
// editing the tree and pushing again, with `disable` as the only immediate
|
|
390
|
+
// stop.
|
|
391
|
+
//
|
|
392
|
+
// Both are OPERATIONAL verbs on a configuration group in `docs/cli-design.md`'s
|
|
393
|
+
// sense: they author nothing. A function's versions are nameless, immutable
|
|
394
|
+
// and not in TOML — unlike a prompt's `[[configs]]` blocks, whose `active`
|
|
395
|
+
// marker is why `prompts configs activate` was retired — so there is no TOML
|
|
396
|
+
// spelling of activation to prefer to a verb.
|
|
397
|
+
functions
|
|
398
|
+
.command("configs")
|
|
399
|
+
.description("List a server function's versions, newest first")
|
|
400
|
+
.argument("<function-id>", "Function ID")
|
|
401
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
402
|
+
.option("--limit <n>", "Rows per page (default 25, max 100)")
|
|
403
|
+
.option("--cursor <cursor>", "Continue from a previous page")
|
|
404
|
+
.option("--json", "Output as JSON")
|
|
405
|
+
.action(async (functionId, options) => {
|
|
406
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
407
|
+
const client = new ApiClient();
|
|
408
|
+
try {
|
|
409
|
+
// Always a `limit`, so this pages. The server's no-parameter form
|
|
410
|
+
// drains every version and exists for a CLI installed before paging
|
|
411
|
+
// did — this one is not it.
|
|
412
|
+
const { items, nextCursor } = await client.listFunctionConfigs(resolvedAppId, functionId, {
|
|
413
|
+
limit: options.limit ? Number(options.limit) : 25,
|
|
414
|
+
...(options.cursor ? { cursor: options.cursor } : {}),
|
|
415
|
+
});
|
|
416
|
+
if (options.json) {
|
|
417
|
+
// Full hashes, not the table's twelve characters: a consumer
|
|
418
|
+
// comparing versions needs the value the server stored.
|
|
419
|
+
json({ items, hasMore: !!nextCursor, ...(nextCursor ? { nextCursor } : {}) });
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (!items || items.length === 0) {
|
|
423
|
+
info("No versions pushed yet.");
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
console.log(formatTable(functionVersionRows(items), [
|
|
427
|
+
{ header: "CONFIG ID", key: "configId" },
|
|
428
|
+
{ header: "PUSHED", key: "createdAt", format: formatDate },
|
|
429
|
+
{ header: "ACTIVE", key: "active" },
|
|
430
|
+
{ header: "CONTENT", key: "content" },
|
|
431
|
+
{ header: "ENVELOPE", key: "envelope" },
|
|
432
|
+
{ header: "TRIGGERS", key: "triggers" },
|
|
433
|
+
{ header: "BY", key: "by" },
|
|
434
|
+
]));
|
|
435
|
+
if (nextCursor) {
|
|
436
|
+
info(`More versions: --cursor ${nextCursor}`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch (err) {
|
|
440
|
+
error(err.message);
|
|
441
|
+
process.exit(1);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
functions
|
|
445
|
+
.command("activate")
|
|
446
|
+
.description("Point a server function at one of its own existing versions (roll back or forward)")
|
|
447
|
+
.argument("<function-id>", "Function ID")
|
|
448
|
+
.argument("<config-id>", "The version to activate (`functions configs`)")
|
|
449
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
450
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
451
|
+
.option("--json", "Output as JSON")
|
|
452
|
+
.addHelpText("after", `
|
|
453
|
+
What activating a version does, and does not:
|
|
454
|
+
Request calls and trigger fires use it from the NEXT call, under that
|
|
455
|
+
version's own capabilities, limits and triggers.
|
|
456
|
+
A task run already in flight finishes on the version it started on — the
|
|
457
|
+
content hash pins its code.
|
|
458
|
+
No version is created. \`config push\` of the tree is still what makes the
|
|
459
|
+
tree's code the newest version.
|
|
460
|
+
`)
|
|
461
|
+
.action(async (functionId, configId, options) => {
|
|
462
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
463
|
+
if (!options.yes) {
|
|
464
|
+
let confirmed;
|
|
465
|
+
try {
|
|
466
|
+
confirmed = await confirmPrompt(activationPrompt(String(functionId), String(configId)));
|
|
467
|
+
}
|
|
468
|
+
catch (err) {
|
|
469
|
+
error(err.message);
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
if (!confirmed) {
|
|
473
|
+
info("Cancelled.");
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const client = new ApiClient();
|
|
478
|
+
try {
|
|
479
|
+
const answer = await client.activateFunctionConfig(resolvedAppId, functionId, configId);
|
|
480
|
+
if (options.json) {
|
|
481
|
+
json(answer);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
const rendered = activationOutput(answer, formatDate);
|
|
485
|
+
if (!rendered.changed) {
|
|
486
|
+
info(rendered.headline);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
success(rendered.headline);
|
|
490
|
+
for (const [label, value] of rendered.lines)
|
|
491
|
+
printResult(label, value);
|
|
492
|
+
}
|
|
493
|
+
catch (err) {
|
|
494
|
+
// The server's own sentence and code: an archived function, an archived
|
|
495
|
+
// version, a held delete lease and a pointer another writer moved each
|
|
496
|
+
// owe the operator a different next move, and only the code says which
|
|
497
|
+
// one without reading English.
|
|
498
|
+
error(activationRefusalLine(err));
|
|
499
|
+
process.exit(1);
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
// #3181 — a trigger-fired invocation writes a run row, and a function run has
|
|
503
|
+
// no `workflowId`, so `primitive workflows runs` cannot see it. This verb is
|
|
504
|
+
// the only way to inspect what a webhook or a schedule actually did
|
|
505
|
+
// (principle 8).
|
|
506
|
+
const runs = functions
|
|
507
|
+
.command("runs")
|
|
508
|
+
.description("List a server function's runs, newest first")
|
|
509
|
+
.argument("<function-id>", "Function ID")
|
|
510
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
511
|
+
.option("--limit <n>", "Rows per page (default 25, max 100)")
|
|
512
|
+
.option("--cursor <cursor>", "Continue from a previous page")
|
|
513
|
+
.option("--json", "Output as JSON")
|
|
514
|
+
.action(async (functionId, options) => {
|
|
515
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
516
|
+
const client = new ApiClient();
|
|
517
|
+
try {
|
|
518
|
+
const { items, nextCursor } = await client.listFunctionRuns(resolvedAppId, functionId, {
|
|
519
|
+
...(options.limit ? { limit: Number(options.limit) } : {}),
|
|
520
|
+
...(options.cursor ? { cursor: options.cursor } : {}),
|
|
521
|
+
});
|
|
522
|
+
// #3482, C-6 — the runtime this run used, derived BEFORE the output is
|
|
523
|
+
// chosen so the table and `--json` say the same thing about the same
|
|
524
|
+
// row. `--json` takes the word or `null`, never the table's blank: a
|
|
525
|
+
// reader parsing this needs "no runtime" to be a value rather than an
|
|
526
|
+
// empty string that sorts beside real ones.
|
|
527
|
+
const withRuntime = (items ?? []).map((run) => ({
|
|
528
|
+
...run,
|
|
529
|
+
runtime: runtimeForExecutionMode(run?.executionMode),
|
|
530
|
+
}));
|
|
531
|
+
if (options.json) {
|
|
532
|
+
json({ items: withRuntime, nextCursor });
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
if (!items || items.length === 0) {
|
|
536
|
+
info("No runs found for this function.");
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
console.log(formatTable(
|
|
540
|
+
// #3381, #3566 — flatten the slice record's two counts onto the
|
|
541
|
+
// row for the REFRESHES and RESETS columns. One helper decides
|
|
542
|
+
// both, so a row with no record gets a blank cell in each and a
|
|
543
|
+
// record with no count gets `0`.
|
|
544
|
+
items.map((run) => ({
|
|
545
|
+
...run,
|
|
546
|
+
// #3482 — which RUNTIME this run used, from the row's own
|
|
547
|
+
// `executionMode`. One pure mapping, so the word an operator
|
|
548
|
+
// reads here is the word `ctx.runtime` gave the code.
|
|
549
|
+
runtime: runRuntimeCell(run),
|
|
550
|
+
...runSliceCells(run),
|
|
551
|
+
})), [
|
|
552
|
+
{ header: "RUN ID", key: "runId" },
|
|
553
|
+
{ header: "STATUS", key: "status", format: formatStatus },
|
|
554
|
+
{ header: "FIRED BY", key: "initiatorKind" },
|
|
555
|
+
// #3482 — the runtime this run used: `request` for a webhook
|
|
556
|
+
// delivery or a `workflow.call` leaf, `task` for everything the
|
|
557
|
+
// engine ran. Blank for a DSL row, which is not a function run.
|
|
558
|
+
{ header: "RUNTIME", key: "runtime" },
|
|
559
|
+
// #3462 — WHICH version this run executed. The row has carried
|
|
560
|
+
// `configId` since #3181; nothing showed it, so an operator
|
|
561
|
+
// reading a failed run could not tell which code failed.
|
|
562
|
+
{ header: "VERSION", key: "configId" },
|
|
563
|
+
// #3280 — FIRED BY already said `function`; this is the half that
|
|
564
|
+
// makes it actionable, so a failed page leads back to the
|
|
565
|
+
// orchestrator that started it. Blank for a root.
|
|
566
|
+
{ header: "PARENT", key: "parentFunctionKey" },
|
|
567
|
+
// #3381 — how many times a task slice's token was refreshed
|
|
568
|
+
// through the gateway. Blank for a request run.
|
|
569
|
+
{ header: "REFRESHES", key: "refreshes" },
|
|
570
|
+
// #3566 — how many of this run's slices a platform deploy (or an
|
|
571
|
+
// isolate eviction) tore down. The engine re-runs the interrupted
|
|
572
|
+
// step, so a run that reset and then COMPLETED carries no failure
|
|
573
|
+
// and no error code: this cell is the only place it shows.
|
|
574
|
+
{ header: "RESETS", key: "resets" },
|
|
575
|
+
{ header: "STARTED", key: "startedAt", format: formatDate },
|
|
576
|
+
{ header: "ENDED", key: "endedAt", format: formatDate },
|
|
577
|
+
{ header: "CODE", key: "errorCode" },
|
|
578
|
+
]));
|
|
579
|
+
if (nextCursor) {
|
|
580
|
+
info(`More runs: --cursor ${nextCursor}`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
catch (err) {
|
|
584
|
+
error(err.message);
|
|
585
|
+
process.exit(1);
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
// #3348 — the step-level view of a durable run.
|
|
589
|
+
//
|
|
590
|
+
// A durable function's handler calls named steps and the engine memoizes each
|
|
591
|
+
// completed one to replay it, but nothing exposed them: `workflows runs
|
|
592
|
+
// steps|status|step-detail` resolve their first argument to a workflow
|
|
593
|
+
// DEFINITION, which a function id and a function key alike miss, and
|
|
594
|
+
// `functions runs` above reports only the run row — status, start, end, one
|
|
595
|
+
// error string. For a run that did partial work there was no way to see which
|
|
596
|
+
// steps completed, how long each took, or where the time went.
|
|
597
|
+
//
|
|
598
|
+
// A SUBCOMMAND of `runs`, which keeps its own `<function-id>` argument form.
|
|
599
|
+
// Commander dispatches to a subcommand only when the first operand names one,
|
|
600
|
+
// and a function id is a ULID, so `functions runs <function-id>` is untouched.
|
|
601
|
+
runs
|
|
602
|
+
.command("steps")
|
|
603
|
+
.description("Show step-level details for a function run")
|
|
604
|
+
.argument("<function-id>", "Function ID")
|
|
605
|
+
.argument("<run-id>", "Run ID")
|
|
606
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
607
|
+
.option("--json", "Output as JSON")
|
|
608
|
+
.action(async (functionId, runId, _options, command) => {
|
|
609
|
+
// `optsWithGlobals`, not the action's own `options`. `runs` is a command
|
|
610
|
+
// with arguments AND subcommands, so commander parses ITS options out of
|
|
611
|
+
// the argv before it dispatches here — and `runs` declares `--app` and
|
|
612
|
+
// `--json` too, so both land on the parent and the subcommand's copies
|
|
613
|
+
// read empty. The merged view is what the flags an operator typed
|
|
614
|
+
// actually are.
|
|
615
|
+
const options = command.optsWithGlobals();
|
|
616
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
617
|
+
const client = new ApiClient();
|
|
618
|
+
try {
|
|
619
|
+
const { items } = await client.getFunctionStepRuns(resolvedAppId, functionId, runId);
|
|
620
|
+
if (options.json) {
|
|
621
|
+
// The same envelope `workflows runs steps --json` emits: one run's
|
|
622
|
+
// full trace under `items`, never a bare array and never a cursor.
|
|
623
|
+
json(buildWorkflowStepEnvelope(items));
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
if (!items || items.length === 0) {
|
|
627
|
+
info("No step runs found.");
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
console.log(renderStepRunTable(items));
|
|
631
|
+
}
|
|
632
|
+
catch (err) {
|
|
633
|
+
error(err.message);
|
|
634
|
+
process.exit(1);
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
// #3348 — end a run that will not settle.
|
|
638
|
+
//
|
|
639
|
+
// `client.functions.terminate` has existed in the SDK since #3186 and nothing
|
|
640
|
+
// reached it from an operator's side, so a run left `running` after its
|
|
641
|
+
// context document was deleted stayed `running` with nothing to do about it.
|
|
642
|
+
runs
|
|
643
|
+
.command("terminate")
|
|
644
|
+
.description("End a function run that will not settle")
|
|
645
|
+
.argument("<function-id>", "Function ID")
|
|
646
|
+
.argument("<run-id>", "Run ID")
|
|
647
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
648
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
649
|
+
.option("--json", "Output as JSON")
|
|
650
|
+
.action(async (functionId, runId, _options, command) => {
|
|
651
|
+
// See `runs steps` above: `runs` parses its own `--app`/`--json` out of
|
|
652
|
+
// the argv before dispatching here, so the merged view is the real one.
|
|
653
|
+
const options = command.optsWithGlobals();
|
|
654
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
655
|
+
if (!options.yes) {
|
|
656
|
+
let confirm;
|
|
657
|
+
try {
|
|
658
|
+
confirm = await confirmPrompt(`Terminate run ${runId}? Its work stops where it is.`);
|
|
659
|
+
}
|
|
660
|
+
catch (err) {
|
|
661
|
+
error(err.message);
|
|
662
|
+
process.exit(1);
|
|
663
|
+
}
|
|
664
|
+
if (!confirm) {
|
|
665
|
+
info("Cancelled.");
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
const client = new ApiClient();
|
|
670
|
+
try {
|
|
671
|
+
const result = await client.terminateFunctionRun(resolvedAppId, functionId, runId);
|
|
672
|
+
if (options.json) {
|
|
673
|
+
json(result);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (!result.terminated) {
|
|
677
|
+
// The run finished on its own. Reporting a kill would be a lie, and
|
|
678
|
+
// the status it settled at is the answer the operator wanted.
|
|
679
|
+
info(`Run ${result.runId} had already settled (${result.status}); nothing to terminate.`);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
success(`Run ${result.runId} terminated.`);
|
|
683
|
+
printResult("Status", formatStatus(result.status));
|
|
684
|
+
printResult("Ended", formatDate(result.endedAt));
|
|
685
|
+
}
|
|
686
|
+
catch (err) {
|
|
687
|
+
error(err.message);
|
|
688
|
+
process.exit(1);
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
// #3287 — the invocation logs. `runs` above answers what HAPPENED; this
|
|
692
|
+
// answers what the function PRINTED and what it threw, which is the thing
|
|
693
|
+
// an operator reaches for when a function is failing and the response says
|
|
694
|
+
// only that it did.
|
|
695
|
+
functions
|
|
696
|
+
.command("logs")
|
|
697
|
+
.description("List a server function's invocation logs, newest first")
|
|
698
|
+
.argument("<function-id>", "Function ID")
|
|
699
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
700
|
+
.option("--limit <n>", "Rows per page (default 25, max 100)")
|
|
701
|
+
.option("--cursor <cursor>", "Continue from a previous page")
|
|
702
|
+
.option("--follow", "Append new invocations as they are recorded (tail)")
|
|
703
|
+
.option("--interval <seconds>", "Poll interval for --follow (default 2)")
|
|
704
|
+
.option("--run <run-id>", "Only this run's records (#3448)")
|
|
705
|
+
.option("--invocation <invocation-id>", "One record, by the id an invoke answered with (#3448)")
|
|
706
|
+
.option("--json", "Output as JSON")
|
|
707
|
+
.action(async (functionId, options) => {
|
|
708
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
709
|
+
const client = new ApiClient();
|
|
710
|
+
const limit = options.limit ? Number(options.limit) : undefined;
|
|
711
|
+
// A cursor names a page in the PAST and a tail starts from now: serving
|
|
712
|
+
// both would mean guessing which the caller meant.
|
|
713
|
+
if (options.follow && options.cursor) {
|
|
714
|
+
error("--follow and --cursor cannot be combined; --follow tails from now.");
|
|
715
|
+
process.exit(1);
|
|
716
|
+
}
|
|
717
|
+
// #3448 — `--invocation` names ONE record, so every flag that shapes a
|
|
718
|
+
// listing contradicts it, and `--run` is a filter a tail cannot honour.
|
|
719
|
+
const conflict = logsFlagConflict(options);
|
|
720
|
+
if (conflict) {
|
|
721
|
+
error(conflict);
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
724
|
+
try {
|
|
725
|
+
if (options.invocation) {
|
|
726
|
+
// The point read. A record that never existed, one of ANOTHER
|
|
727
|
+
// function, and one the seven-day TTL has expired all answer the
|
|
728
|
+
// same 404 — which is the listing's own filter, said out loud.
|
|
729
|
+
let record;
|
|
730
|
+
try {
|
|
731
|
+
record = await client.getFunctionLog(resolvedAppId, functionId, String(options.invocation));
|
|
732
|
+
}
|
|
733
|
+
catch (err) {
|
|
734
|
+
// An id nothing wrote, one of ANOTHER function, and one the
|
|
735
|
+
// seven-day TTL has expired all answer the same 404 — so the
|
|
736
|
+
// message names the retention rather than implying the platform
|
|
737
|
+
// lost something (edge 37).
|
|
738
|
+
if (err?.statusCode === 404) {
|
|
739
|
+
error(`No invocation ${options.invocation} for this function. ` +
|
|
740
|
+
`Invocation records are kept for seven days.`);
|
|
741
|
+
process.exit(1);
|
|
742
|
+
}
|
|
743
|
+
throw err;
|
|
744
|
+
}
|
|
745
|
+
if (options.json) {
|
|
746
|
+
json(toFunctionLogInspectionRow(record));
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
console.log(formatTable([functionLogCells(record)], FUNCTION_LOG_COLUMNS.map((header) => ({
|
|
750
|
+
header,
|
|
751
|
+
key: LOG_COLUMN_KEYS[header],
|
|
752
|
+
...(LOG_COLUMN_FORMATS[header]
|
|
753
|
+
? { format: LOG_COLUMN_FORMATS[header] }
|
|
754
|
+
: {}),
|
|
755
|
+
}))));
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
if (options.follow) {
|
|
759
|
+
const intervalSeconds = options.interval ? Number(options.interval) : 2;
|
|
760
|
+
if (!Number.isFinite(intervalSeconds) || intervalSeconds <= 0) {
|
|
761
|
+
error("--interval must be a positive number of seconds.");
|
|
762
|
+
process.exit(1);
|
|
763
|
+
}
|
|
764
|
+
const controller = new AbortController();
|
|
765
|
+
const onSigint = () => controller.abort();
|
|
766
|
+
process.on("SIGINT", onSigint);
|
|
767
|
+
try {
|
|
768
|
+
// The high-water mark is the newest `invocationId` already seen.
|
|
769
|
+
// It is a ULID, so string order IS creation order and "newer than
|
|
770
|
+
// this" is a comparison rather than a set of ids to remember.
|
|
771
|
+
// The route answers NEWEST first, so a tick walks BACKWARD until
|
|
772
|
+
// it meets the mark rather than reading one page and hoping
|
|
773
|
+
// (CR3287-007). The walk is `buildLogFollowSource`, where it can
|
|
774
|
+
// be put to a test without a server.
|
|
775
|
+
const source = buildLogFollowSource(({ cursor }) => client.listFunctionLogs(resolvedAppId, functionId, {
|
|
776
|
+
...(limit ? { limit } : {}),
|
|
777
|
+
...(cursor ? { cursor } : {}),
|
|
778
|
+
}), {
|
|
779
|
+
idOf: (row) => String(row.invocationId),
|
|
780
|
+
maxPagesPerTick: FOLLOW_MAX_PAGES_PER_TICK,
|
|
781
|
+
});
|
|
782
|
+
if (!options.json) {
|
|
783
|
+
info("Following invocations (Ctrl-C to stop)…");
|
|
784
|
+
}
|
|
785
|
+
await followLoop(source, {
|
|
786
|
+
intervalMs: intervalSeconds * 1000,
|
|
787
|
+
signal: controller.signal,
|
|
788
|
+
render: (items) => {
|
|
789
|
+
if (options.json) {
|
|
790
|
+
// One JSON object per line — a tail has no closing bracket
|
|
791
|
+
// to wait for — each in the SHARED inspection item shape,
|
|
792
|
+
// the same items the one-shot envelope carries.
|
|
793
|
+
for (const row of items)
|
|
794
|
+
jsonLine(toFunctionLogInspectionRow(row));
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
for (const row of items)
|
|
798
|
+
console.log(functionLogLine(row));
|
|
799
|
+
},
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
finally {
|
|
803
|
+
process.off("SIGINT", onSigint);
|
|
804
|
+
}
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
// #3448 D3448-SO-006, the CLI half. A FILTERED page holding only
|
|
808
|
+
// other runs' records comes back EMPTY WITH A CURSOR, which is an
|
|
809
|
+
// intermediate page and not the end: returning on it — as this did —
|
|
810
|
+
// reports "no logs" for a run whose records are one page further down.
|
|
811
|
+
// The continuation is followed a bounded number of times, and if it is
|
|
812
|
+
// still holding nothing the empty state is printed WITH the cursor
|
|
813
|
+
// rather than as a bare "none".
|
|
814
|
+
let cursor = options.cursor;
|
|
815
|
+
let items = [];
|
|
816
|
+
let nextCursor;
|
|
817
|
+
let exhaustedCursor = null;
|
|
818
|
+
for (let followed = 0;; followed += 1) {
|
|
819
|
+
const page = await client.listFunctionLogs(resolvedAppId, functionId, {
|
|
820
|
+
...(limit ? { limit } : {}),
|
|
821
|
+
...(cursor ? { cursor } : {}),
|
|
822
|
+
...(options.run ? { runId: String(options.run) } : {}),
|
|
823
|
+
});
|
|
824
|
+
items = page.items;
|
|
825
|
+
nextCursor = page.nextCursor;
|
|
826
|
+
const decision = decidePageContinuation(page, followed);
|
|
827
|
+
if (decision.action === "continue") {
|
|
828
|
+
cursor = decision.cursor;
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
if (decision.action === "exhausted")
|
|
832
|
+
exhaustedCursor = decision.cursor;
|
|
833
|
+
break;
|
|
834
|
+
}
|
|
835
|
+
if (options.json) {
|
|
836
|
+
// The shared envelope, whose items are the shared inspection shape:
|
|
837
|
+
// a consumer classifies and pivots on `outcome`, `nativeStatus` and
|
|
838
|
+
// `correlation` without knowing which store answered (DSO3287-007).
|
|
839
|
+
json({
|
|
840
|
+
items: items.map((row) => toFunctionLogInspectionRow(row)),
|
|
841
|
+
hasMore: !!nextCursor,
|
|
842
|
+
...(nextCursor ? { nextCursor } : {}),
|
|
843
|
+
});
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
if (!items || items.length === 0) {
|
|
847
|
+
info(options.run
|
|
848
|
+
? `No invocation logs found for run ${options.run}.`
|
|
849
|
+
: "No invocation logs found for this function.");
|
|
850
|
+
// Not the end: the scan's budget was spent with matching records
|
|
851
|
+
// possibly still below. Printing the cursor is what lets an operator
|
|
852
|
+
// continue rather than concluding there is nothing there.
|
|
853
|
+
if (exhaustedCursor)
|
|
854
|
+
info(`More logs: --cursor ${exhaustedCursor}`);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
if (options.run) {
|
|
858
|
+
// #3453 — reading ONE run, which is a different question from
|
|
859
|
+
// listing a function's invocations. A table of one row per record
|
|
860
|
+
// answers "which invocations were there"; what an operator opened
|
|
861
|
+
// `--run` for is what the run PRINTED, and until now that was only
|
|
862
|
+
// reachable through `--json`. So: the step trace first, then each
|
|
863
|
+
// record oldest first with its own lines under it.
|
|
864
|
+
let trace = null;
|
|
865
|
+
let traceError = null;
|
|
866
|
+
try {
|
|
867
|
+
const read = await client.getFunctionStepRuns(resolvedAppId, functionId, String(options.run));
|
|
868
|
+
trace = read?.items ?? [];
|
|
869
|
+
}
|
|
870
|
+
catch (traceErr) {
|
|
871
|
+
traceError = traceErr?.message ?? String(traceErr);
|
|
872
|
+
}
|
|
873
|
+
const rendered = renderRunLogs({ records: items, trace, traceError });
|
|
874
|
+
if (rendered.warning)
|
|
875
|
+
warn(rendered.warning);
|
|
876
|
+
for (const line of rendered.lines)
|
|
877
|
+
console.log(line);
|
|
878
|
+
if (nextCursor) {
|
|
879
|
+
info(`More logs: --cursor ${nextCursor}`);
|
|
880
|
+
}
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
console.log(formatTable(items.map((row) => functionLogCells(row)), FUNCTION_LOG_COLUMNS.map((header) => ({
|
|
884
|
+
header,
|
|
885
|
+
key: LOG_COLUMN_KEYS[header],
|
|
886
|
+
...(LOG_COLUMN_FORMATS[header]
|
|
887
|
+
? { format: LOG_COLUMN_FORMATS[header] }
|
|
888
|
+
: {}),
|
|
889
|
+
}))));
|
|
890
|
+
if (nextCursor) {
|
|
891
|
+
info(`More logs: --cursor ${nextCursor}`);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
catch (err) {
|
|
895
|
+
error(err.message);
|
|
896
|
+
process.exit(1);
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
// #3182 — the typed handle's generated files, and the freshness question a
|
|
900
|
+
// CI job has to be able to ASK. `config push` writes them and `config diff`
|
|
901
|
+
// names the stale ones, but a diff exits 0 whatever it found, so a job that
|
|
902
|
+
// meant to fail on stale types passed and a declaration that disagreed with
|
|
903
|
+
// its schema reached the author as types that quietly lie (F3182-004). The
|
|
904
|
+
// verb and the flag are the two `databases codegen` and `workflows codegen`
|
|
905
|
+
// already use for exactly this.
|
|
906
|
+
functions
|
|
907
|
+
.command("codegen")
|
|
908
|
+
.allowExcessArguments(false)
|
|
909
|
+
.description("Write the generated files into the config tree's functions/ directory: the database-type declaration, the primitive-functions declaration, the functions declaration (<Key>Input/<Key>Output from each function's schemas), the per-function client invokers under generated/, and the tsconfig that wires the declarations into your editor. `config push` does this too; this is the standalone form, with a --check mode for CI.")
|
|
910
|
+
.option("--check", "Exit non-zero if the generated files are out of date or the tsconfig no longer loads them (CI guard); does not write.")
|
|
911
|
+
.option("-o, --out <dir>", "Directory for the per-function client invokers (default: <tree>/functions/generated/)")
|
|
912
|
+
.option("--lang <lang>", "Target language for the generated per-key invokers: 'ts' (default) or 'swift'. Swift emits one <key>.generated.swift per function (<Key>Input/<Key>Output Codable types + a <Key>Function invoker) and writes no TypeScript artifact. Every invoker carries both verb sets in either language — invoke, start, getStatus, waitFor, terminate — because the caller picks the runtime at each call.", "ts")
|
|
913
|
+
.option("--json", "Output the result summary as JSON")
|
|
914
|
+
.action(async (options) => {
|
|
915
|
+
// `--lang` is validated FIRST, before a single file is read, so an
|
|
916
|
+
// unknown value fails naming the flag rather than reporting whatever the
|
|
917
|
+
// schema parser happened to meet on the way (#3344 behavior 8).
|
|
918
|
+
const lang = String(options.lang ?? "ts").toLowerCase();
|
|
919
|
+
if (lang !== "ts" && lang !== "swift") {
|
|
920
|
+
error(`Unknown --lang "${options.lang}". Use "ts" or "swift".`);
|
|
921
|
+
process.exit(1);
|
|
922
|
+
}
|
|
923
|
+
// A tree command: what it writes is the environment's own directory,
|
|
924
|
+
// which is the only directory it can be asked to write (#3154).
|
|
925
|
+
const configDir = resolveSyncDir();
|
|
926
|
+
if (!existsSync(configDir)) {
|
|
927
|
+
error(`Config directory not found: ${configDir}`);
|
|
928
|
+
process.exit(1);
|
|
929
|
+
}
|
|
930
|
+
if (!existsSync(join(configDir, "functions")) &&
|
|
931
|
+
!treeDeclaresAgentPrompt(configDir) &&
|
|
932
|
+
!holdsAgentArtifacts(options.out ? String(options.out) : join(configDir, "functions", "generated"))) {
|
|
933
|
+
// Nothing to type: the artifacts exist to serve function sources, and
|
|
934
|
+
// a tree with none needs no compiler wiring. Not a failure — a tree
|
|
935
|
+
// may legitimately have no functions. #3798 (DSO-3798-005) — unless
|
|
936
|
+
// it declares an agent, whose client-side types are generated with or
|
|
937
|
+
// without functions, or (CSO-3798-003) a removed agent's artifacts are
|
|
938
|
+
// still there to sweep and for `--check` to report.
|
|
939
|
+
if (options.json)
|
|
940
|
+
json({ ok: true, stale: [], problems: [] });
|
|
941
|
+
else
|
|
942
|
+
info(`No functions/ directory in ${configDir}; nothing to generate.`);
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
// #3281 — the per-key client invokers go beside the declarations unless
|
|
946
|
+
// `-o` names another directory, the `workflows codegen` shape.
|
|
947
|
+
const artifactOptions = options.out ? { invokerDir: String(options.out) } : {};
|
|
948
|
+
// #3344 — the Swift half. It shares the default output directory with
|
|
949
|
+
// the TypeScript invokers (suffixes and ownership banners keep the two
|
|
950
|
+
// sweeps apart) and writes NO TypeScript artifact: a Swift app has no
|
|
951
|
+
// tsconfig to wire, so `problems` is always empty here.
|
|
952
|
+
if (lang === "swift") {
|
|
953
|
+
const outputDir = options.out
|
|
954
|
+
? String(options.out)
|
|
955
|
+
: join(configDir, "functions", "generated");
|
|
956
|
+
// Tree-relative, so the paths a CI job prints read the way the author's
|
|
957
|
+
// repository does.
|
|
958
|
+
const relative = (filePath) => relativePath(configDir, filePath);
|
|
959
|
+
let outcome;
|
|
960
|
+
try {
|
|
961
|
+
outcome = await generateFunctionSwiftTypes({
|
|
962
|
+
inputs: functionSchemaSources(configDir),
|
|
963
|
+
// #3798 — each agent prompt's Swift types, in the same run.
|
|
964
|
+
agentInputs: promptSchemaSources(configDir),
|
|
965
|
+
outputDir,
|
|
966
|
+
check: Boolean(options.check),
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
catch (err) {
|
|
970
|
+
// A malformed schema, a duplicate key, an un-typable schema or a
|
|
971
|
+
// collision with another generator's file: nothing is written, and
|
|
972
|
+
// the message names the file the author edits.
|
|
973
|
+
if (!(err instanceof FunctionSchemaCodegenError) &&
|
|
974
|
+
!(err instanceof SwiftCodegenError)) {
|
|
975
|
+
throw err;
|
|
976
|
+
}
|
|
977
|
+
if (options.json)
|
|
978
|
+
json({ ok: false, stale: [], problems: [err.message] });
|
|
979
|
+
else
|
|
980
|
+
error(err.message);
|
|
981
|
+
process.exit(1);
|
|
982
|
+
}
|
|
983
|
+
if (options.check) {
|
|
984
|
+
const stale = outcome.mismatches.map((m) => relative(m.filePath)).sort();
|
|
985
|
+
if (stale.length === 0) {
|
|
986
|
+
if (options.json)
|
|
987
|
+
json({ ok: true, stale: [], problems: [] });
|
|
988
|
+
else
|
|
989
|
+
success("Check passed: the generated Swift invokers are up to date.");
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
if (options.json) {
|
|
993
|
+
json({ ok: false, stale, problems: [] });
|
|
994
|
+
}
|
|
995
|
+
else {
|
|
996
|
+
error(`Check failed: ${stale.length} file(s) out of date.`);
|
|
997
|
+
for (const mismatch of outcome.mismatches) {
|
|
998
|
+
error(` ${mismatch.reason}: ${relative(mismatch.filePath)}`);
|
|
999
|
+
}
|
|
1000
|
+
const regenerate = ["primitive functions codegen --lang swift"];
|
|
1001
|
+
if (options.out)
|
|
1002
|
+
regenerate.push(`-o ${options.out}`);
|
|
1003
|
+
info(`Run \`${regenerate.join(" ")}\` to regenerate.`);
|
|
1004
|
+
}
|
|
1005
|
+
process.exit(1);
|
|
1006
|
+
}
|
|
1007
|
+
const writtenRelative = outcome.writtenFiles.map(relative).sort();
|
|
1008
|
+
const deletedRelative = outcome.deletedFiles.map(relative).sort();
|
|
1009
|
+
if (options.json) {
|
|
1010
|
+
json({ written: writtenRelative, deleted: deletedRelative, problems: [] });
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
success(`Wrote ${writtenRelative.length} Swift file(s) into ${outputDir}.`);
|
|
1014
|
+
for (const file of writtenRelative)
|
|
1015
|
+
keyValue(" wrote", file);
|
|
1016
|
+
for (const file of deletedRelative)
|
|
1017
|
+
keyValue(" swept", file);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
// Wiring push cannot repair is checked alongside freshness: a tsconfig
|
|
1021
|
+
// that carries comments is never rewritten, so it can be perfectly
|
|
1022
|
+
// current and still load none of the declarations (CR3182-003).
|
|
1023
|
+
let problems;
|
|
1024
|
+
let stale = [];
|
|
1025
|
+
try {
|
|
1026
|
+
problems = functionTypeWiringProblems(configDir, artifactOptions);
|
|
1027
|
+
if (options.check)
|
|
1028
|
+
stale = staleFunctionTypeArtifacts(configDir, artifactOptions);
|
|
1029
|
+
}
|
|
1030
|
+
catch (err) {
|
|
1031
|
+
// A malformed schema in a function file: nothing can be generated
|
|
1032
|
+
// from it, and the message names the file.
|
|
1033
|
+
if (!(err instanceof FunctionSchemaCodegenError))
|
|
1034
|
+
throw err;
|
|
1035
|
+
if (options.json)
|
|
1036
|
+
json({ ok: false, stale: [], problems: [err.message] });
|
|
1037
|
+
else
|
|
1038
|
+
error(err.message);
|
|
1039
|
+
process.exit(1);
|
|
1040
|
+
}
|
|
1041
|
+
if (options.check) {
|
|
1042
|
+
if (stale.length === 0 && problems.length === 0) {
|
|
1043
|
+
if (options.json)
|
|
1044
|
+
json({ ok: true, stale: [], problems: [] });
|
|
1045
|
+
else
|
|
1046
|
+
success("Check passed: the function types are up to date.");
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
if (options.json) {
|
|
1050
|
+
json({ ok: false, stale, problems });
|
|
1051
|
+
}
|
|
1052
|
+
else {
|
|
1053
|
+
error(`Check failed: ${stale.length} file(s) out of date` +
|
|
1054
|
+
(problems.length > 0
|
|
1055
|
+
? `, ${problems.length} wiring problem(s).`
|
|
1056
|
+
: "."));
|
|
1057
|
+
for (const relative of stale)
|
|
1058
|
+
error(` out of date: ${relative}`);
|
|
1059
|
+
for (const problem of problems) {
|
|
1060
|
+
error(` functions/tsconfig.json: ${problem}`);
|
|
1061
|
+
}
|
|
1062
|
+
// Name the command that fixes what was just reported, keeping the
|
|
1063
|
+
// options that decided WHERE it looked (#2911). The tree itself is
|
|
1064
|
+
// no longer one of them (#3154) — only `-o` is left.
|
|
1065
|
+
const regenerate = ["primitive functions codegen"];
|
|
1066
|
+
if (options.out)
|
|
1067
|
+
regenerate.push(`-o ${options.out}`);
|
|
1068
|
+
info(`Run \`${regenerate.join(" ")}\` to regenerate.`);
|
|
1069
|
+
}
|
|
1070
|
+
process.exit(1);
|
|
1071
|
+
}
|
|
1072
|
+
const written = writeFunctionTypeArtifacts(configDir, undefined, artifactOptions);
|
|
1073
|
+
if (options.json) {
|
|
1074
|
+
json({ written, problems });
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
success(`Wrote ${written.length} file(s) into ${configDir}/functions/.`);
|
|
1078
|
+
for (const relative of written)
|
|
1079
|
+
keyValue(" wrote", relative);
|
|
1080
|
+
for (const problem of problems) {
|
|
1081
|
+
warn(`functions/tsconfig.json: ${problem}`);
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
// #2803 — the operational verb pair, and the only writers of `status` besides
|
|
1085
|
+
// the delete flow. `config push` is deliberately not one: see the module note.
|
|
1086
|
+
functions
|
|
1087
|
+
.command("disable")
|
|
1088
|
+
.description("Take a server function out of service now. Invocations are refused; the committed configuration and code are unchanged.")
|
|
1089
|
+
.argument("<function-id>", "Function ID")
|
|
1090
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1091
|
+
.option("--json", "Output as JSON")
|
|
1092
|
+
.action(async (functionId, options) => {
|
|
1093
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
1094
|
+
const client = new ApiClient();
|
|
1095
|
+
try {
|
|
1096
|
+
const result = await client.setFunctionAvailability(resolvedAppId, functionId, "disable");
|
|
1097
|
+
if (options.json) {
|
|
1098
|
+
json(result);
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
success(`Function disabled: ${result.functionKey || functionId}`);
|
|
1102
|
+
info("Invocations are refused. `primitive config push` still updates the code and leaves it out of service — `primitive functions enable` puts it back.");
|
|
1103
|
+
}
|
|
1104
|
+
catch (err) {
|
|
1105
|
+
error(err.message);
|
|
1106
|
+
process.exit(1);
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
functions
|
|
1110
|
+
.command("enable")
|
|
1111
|
+
.description("Put a server function back in service.")
|
|
1112
|
+
.argument("<function-id>", "Function ID")
|
|
1113
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1114
|
+
.option("--json", "Output as JSON")
|
|
1115
|
+
.action(async (functionId, options) => {
|
|
1116
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
1117
|
+
const client = new ApiClient();
|
|
1118
|
+
try {
|
|
1119
|
+
const result = await client.setFunctionAvailability(resolvedAppId, functionId, "enable");
|
|
1120
|
+
if (options.json) {
|
|
1121
|
+
json(result);
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
success(`Function enabled: ${result.functionKey || functionId}`);
|
|
1125
|
+
}
|
|
1126
|
+
catch (err) {
|
|
1127
|
+
error(err.message);
|
|
1128
|
+
process.exit(1);
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
// #2907 — the delete flow's CLI spelling. The plain `DELETE` the server has
|
|
1132
|
+
// always answered by writing the tombstone; not a second writer of `status`.
|
|
1133
|
+
functions
|
|
1134
|
+
.command("archive")
|
|
1135
|
+
.description("Retire a server function (soft delete). Delete-lifecycle, not `disable`: the row is kept, goes on holding its key, and there is no un-archive.")
|
|
1136
|
+
.argument("<function-id>", "Function ID")
|
|
1137
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1138
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
1139
|
+
.option("--json", "Output as JSON")
|
|
1140
|
+
.addHelpText("after", `
|
|
1141
|
+
What archiving a function costs:
|
|
1142
|
+
Invocations are refused, as they are for a disabled function.
|
|
1143
|
+
The archived row still holds its key in the app's SINGLE key namespace — no
|
|
1144
|
+
new function, workflow, script or webhook may take that key while it stands —
|
|
1145
|
+
and pushing code to an archived function is refused.
|
|
1146
|
+
\`primitive functions enable\` refuses an archived function; there is no
|
|
1147
|
+
un-archive.
|
|
1148
|
+
|
|
1149
|
+
Reclaiming the key means hard-deleting the row:
|
|
1150
|
+
$ rm functions/<key>.toml
|
|
1151
|
+
$ primitive config push --prune # confirmed; hard-deletes the row, its
|
|
1152
|
+
# versions and their stored bundles
|
|
1153
|
+
# or hard-delete it in the console, then re-add the file and push.
|
|
1154
|
+
`)
|
|
1155
|
+
.action(async (functionId, options) => {
|
|
1156
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
1157
|
+
if (!options.yes) {
|
|
1158
|
+
let confirmed;
|
|
1159
|
+
try {
|
|
1160
|
+
confirmed = await confirmPrompt(`Archive function ${functionId}? Invocations stop, the row keeps its ` +
|
|
1161
|
+
`key in the app's single key namespace, and there is no un-archive.`);
|
|
1162
|
+
}
|
|
1163
|
+
catch (err) {
|
|
1164
|
+
error(err.message);
|
|
1165
|
+
process.exit(1);
|
|
1166
|
+
}
|
|
1167
|
+
if (!confirmed) {
|
|
1168
|
+
info("Cancelled.");
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
const client = new ApiClient();
|
|
1173
|
+
try {
|
|
1174
|
+
// The soft delete: no `hard`, so the server writes the tombstone and
|
|
1175
|
+
// keeps the reservation. Prune-by-push owns the hard path.
|
|
1176
|
+
const result = await client.deleteFunction(resolvedAppId, functionId);
|
|
1177
|
+
if (options.json) {
|
|
1178
|
+
json(result);
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
success(`Function archived: ${functionId}`);
|
|
1182
|
+
info("Invocations are refused and pushes to it are rejected. The row still holds its key — reclaim it by removing its file and running a confirmed `primitive config push --prune`.");
|
|
1183
|
+
}
|
|
1184
|
+
catch (err) {
|
|
1185
|
+
error(err.message);
|
|
1186
|
+
process.exit(1);
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
// ── #3448: running a function ───────────────────────────────────────
|
|
1190
|
+
//
|
|
1191
|
+
// `functions list|get|runs|logs` could INSPECT a function and nothing could
|
|
1192
|
+
// run one: invoking a request function, starting a task and waiting for a
|
|
1193
|
+
// run all needed a signed-in app client or a test harness, and stopping an
|
|
1194
|
+
// experimental run needed SDK code (Compound developer feedback SF3).
|
|
1195
|
+
//
|
|
1196
|
+
// Two identities were missing with it. Every HTTP invocation ran as a
|
|
1197
|
+
// caller, so a function fired by cron, a webhook or a database change —
|
|
1198
|
+
// `ctx.user` null, the system principal — could not be exercised from
|
|
1199
|
+
// outside without faking its trigger.
|
|
1200
|
+
//
|
|
1201
|
+
// Every decision these verbs make is in `cli/src/lib/function-run.ts`, where
|
|
1202
|
+
// it is pinned without a server; what is here is the I/O and the rendering.
|
|
1203
|
+
/**
|
|
1204
|
+
* Key → the function's id.
|
|
1205
|
+
*
|
|
1206
|
+
* `invoke` and `start` take the KEY, because that is the public route's
|
|
1207
|
+
* argument and what a developer wrote in their TOML — while `runs`, `logs`
|
|
1208
|
+
* and `steps` keep the function id they have always taken. So the key has to
|
|
1209
|
+
* be resolved, and the drained admin listing is what `functions list` reads.
|
|
1210
|
+
*
|
|
1211
|
+
* #3482 — the ACTIVE VERSION is no longer read. It was point-read for one
|
|
1212
|
+
* reason: the mode pre-check, which refused the wrong verb before a round
|
|
1213
|
+
* trip. There is no wrong verb any more — the caller picks the runtime and
|
|
1214
|
+
* every function takes both — so the read and the pre-check go together.
|
|
1215
|
+
*/
|
|
1216
|
+
async function resolveFunctionByKey(client, appId, functionKey) {
|
|
1217
|
+
const { items } = await client.listFunctions(appId);
|
|
1218
|
+
const wanted = functionKey.trim().toLowerCase();
|
|
1219
|
+
const match = (items || []).find((item) => String(item.functionKey ?? "").toLowerCase() === wanted);
|
|
1220
|
+
if (!match) {
|
|
1221
|
+
throw new Error(`No function with key '${functionKey}' in this app. ` +
|
|
1222
|
+
`List them with 'primitive functions list'.`);
|
|
1223
|
+
}
|
|
1224
|
+
return { functionId: String(match.functionId) };
|
|
1225
|
+
}
|
|
1226
|
+
/** The identity a call runs under, and the bearer it presents. */
|
|
1227
|
+
async function planIdentity(client, appId, identity) {
|
|
1228
|
+
if (identity.kind === "system") {
|
|
1229
|
+
const me = await client.getAppProfile(appId);
|
|
1230
|
+
return {
|
|
1231
|
+
describe: describeIdentity({ kind: "system", byUserId: me.userId }),
|
|
1232
|
+
actingUserId: null,
|
|
1233
|
+
asSystem: true,
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
if (identity.kind === "user") {
|
|
1237
|
+
return {
|
|
1238
|
+
describe: describeIdentity({ kind: "user", userId: identity.userId }),
|
|
1239
|
+
actingUserId: identity.userId,
|
|
1240
|
+
asSystem: false,
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
const me = await client.getAppProfile(appId);
|
|
1244
|
+
return {
|
|
1245
|
+
describe: describeIdentity({
|
|
1246
|
+
kind: "self",
|
|
1247
|
+
userId: me.userId,
|
|
1248
|
+
appRole: me.appRole,
|
|
1249
|
+
}),
|
|
1250
|
+
actingUserId: me.userId,
|
|
1251
|
+
asSystem: false,
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
/** Print an envelope's ids, each followed by the command that takes it. */
|
|
1255
|
+
function printIdLines(lines) {
|
|
1256
|
+
for (const line of renderIdLines(lines))
|
|
1257
|
+
console.log(line);
|
|
1258
|
+
}
|
|
1259
|
+
functions
|
|
1260
|
+
.command("invoke")
|
|
1261
|
+
.allowExcessArguments(false)
|
|
1262
|
+
.description("Run a REQUEST function over the public route and print its result — the same call `client.functions.invoke` makes")
|
|
1263
|
+
.argument("<key>", "Function key (as written in functions/<key>.toml)")
|
|
1264
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1265
|
+
.option("--input <json>", "The handler's input, as JSON (default {})")
|
|
1266
|
+
.option("--context-doc-id <id>", "Context document for the invocation")
|
|
1267
|
+
.option("--timeout <seconds>", "Wall-clock budget; the platform clamps at 30s")
|
|
1268
|
+
.option("--user <user-id>", "Run as this app user (admin/owner only)")
|
|
1269
|
+
.option("--as <principal>", "Run with no caller: --as system (admin/owner only)")
|
|
1270
|
+
.option("--json", "Output as JSON")
|
|
1271
|
+
.action(async (functionKey, options) => {
|
|
1272
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
1273
|
+
const flags = resolveRunFlags(options);
|
|
1274
|
+
if (flags.ok === false) {
|
|
1275
|
+
error(flags.error);
|
|
1276
|
+
process.exit(1);
|
|
1277
|
+
}
|
|
1278
|
+
const client = new ApiClient();
|
|
1279
|
+
try {
|
|
1280
|
+
const target = await resolveFunctionByKey(client, resolvedAppId, functionKey);
|
|
1281
|
+
const identity = await planIdentity(client, resolvedAppId, flags.identity);
|
|
1282
|
+
const body = {
|
|
1283
|
+
rootInput: flags.input,
|
|
1284
|
+
...(options.contextDocId ? { contextDocId: options.contextDocId } : {}),
|
|
1285
|
+
...(flags.timeoutSeconds
|
|
1286
|
+
? { timeoutMs: Math.round(flags.timeoutSeconds * 1000) }
|
|
1287
|
+
: {}),
|
|
1288
|
+
};
|
|
1289
|
+
const answer = await runAs(client, resolvedAppId, flags.identity, "invoke", functionKey, (bearer) => client.invokeFunction(resolvedAppId, functionKey, body, {
|
|
1290
|
+
...(bearer ? { bearer } : {}),
|
|
1291
|
+
asSystem: identity.asSystem,
|
|
1292
|
+
// #3482 — the ROUTE says which runtime this call means. The
|
|
1293
|
+
// admin-only system door is one route for both, so the body still
|
|
1294
|
+
// names it there and only there.
|
|
1295
|
+
runtime: "request",
|
|
1296
|
+
...(identity.asSystem ? {} : {}),
|
|
1297
|
+
}));
|
|
1298
|
+
const envelope = answer.body ?? {};
|
|
1299
|
+
if (options.json) {
|
|
1300
|
+
json({
|
|
1301
|
+
functionId: target.functionId,
|
|
1302
|
+
functionKey,
|
|
1303
|
+
identity: identity.describe,
|
|
1304
|
+
httpStatus: answer.httpStatus,
|
|
1305
|
+
...(answer.retryAfterSeconds !== null
|
|
1306
|
+
? { retryAfterSeconds: answer.retryAfterSeconds }
|
|
1307
|
+
: {}),
|
|
1308
|
+
...envelope,
|
|
1309
|
+
});
|
|
1310
|
+
process.exit(exitCodeForInvokeAnswer(answer));
|
|
1311
|
+
}
|
|
1312
|
+
if (answer.httpStatus >= 400) {
|
|
1313
|
+
renderRefusal(answer);
|
|
1314
|
+
// The ids, each with the command that takes it — the same rendering
|
|
1315
|
+
// a success gets, because a refusal is when an operator most needs
|
|
1316
|
+
// the next command (CR3448-004). A REFUSAL CAN CARRY AN INVOCATION
|
|
1317
|
+
// ID: the two 500s the platform records answer one (edge 35), and
|
|
1318
|
+
// printing the function id bare beside it left the record findable
|
|
1319
|
+
// only by someone who already knew the flag.
|
|
1320
|
+
printIdLines(invokeIdLines({
|
|
1321
|
+
functionId: target.functionId,
|
|
1322
|
+
invocationId: envelope.invocationId,
|
|
1323
|
+
}));
|
|
1324
|
+
process.exit(1);
|
|
1325
|
+
}
|
|
1326
|
+
printResult("Status", formatStatus(String(envelope.status ?? "")));
|
|
1327
|
+
if (envelope.output !== undefined) {
|
|
1328
|
+
console.log("Output");
|
|
1329
|
+
console.log(JSON.stringify(envelope.output, null, 2));
|
|
1330
|
+
}
|
|
1331
|
+
if (envelope.error)
|
|
1332
|
+
printResult("Error", String(envelope.error));
|
|
1333
|
+
if (envelope.errorCode)
|
|
1334
|
+
printResult("Code", String(envelope.errorCode));
|
|
1335
|
+
if (envelope.limits) {
|
|
1336
|
+
printResult("Limits", `cpuMs ${envelope.limits.cpuMs}, subRequests ${envelope.limits.subRequests}, ratePerMinute ${envelope.limits.ratePerMinute}`);
|
|
1337
|
+
}
|
|
1338
|
+
console.log(identity.describe);
|
|
1339
|
+
printIdLines(invokeIdLines({
|
|
1340
|
+
functionId: target.functionId,
|
|
1341
|
+
invocationId: envelope.invocationId,
|
|
1342
|
+
}));
|
|
1343
|
+
process.exit(exitCodeForInvokeAnswer(answer));
|
|
1344
|
+
}
|
|
1345
|
+
catch (err) {
|
|
1346
|
+
error(err.message);
|
|
1347
|
+
process.exit(1);
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
functions
|
|
1351
|
+
.command("start")
|
|
1352
|
+
.allowExcessArguments(false)
|
|
1353
|
+
.description("Start a TASK function and print its run id")
|
|
1354
|
+
.argument("<key>", "Function key (as written in functions/<key>.toml)")
|
|
1355
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1356
|
+
.option("--input <json>", "The handler's input, as JSON (default {})")
|
|
1357
|
+
.option("--context-doc-id <id>", "Context document the run is keyed under")
|
|
1358
|
+
.option("--run-key <key>", "Idempotency key; a repeat replays the existing run")
|
|
1359
|
+
.option("--user <user-id>", "Run as this app user (admin/owner only)")
|
|
1360
|
+
.option("--as <principal>", "Run with no caller: --as system (admin/owner only)")
|
|
1361
|
+
.option("--wait", "Wait for the run to settle, then print its outcome")
|
|
1362
|
+
.option("--timeout <seconds>", "Budget for --wait (default 900)")
|
|
1363
|
+
.option("--json", "Output as JSON")
|
|
1364
|
+
.action(async (functionKey, options) => {
|
|
1365
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
1366
|
+
const flags = resolveRunFlags(options);
|
|
1367
|
+
if (flags.ok === false) {
|
|
1368
|
+
error(flags.error);
|
|
1369
|
+
process.exit(1);
|
|
1370
|
+
}
|
|
1371
|
+
const client = new ApiClient();
|
|
1372
|
+
try {
|
|
1373
|
+
const target = await resolveFunctionByKey(client, resolvedAppId, functionKey);
|
|
1374
|
+
const identity = await planIdentity(client, resolvedAppId, flags.identity);
|
|
1375
|
+
// D3448-014 — the task path defaults the context to the acting user's
|
|
1376
|
+
// `AppUser.rootDocId`, and the app user provisioned for an admin has
|
|
1377
|
+
// none, so the advertised `functions start <key> --wait` would refuse
|
|
1378
|
+
// `CONTEXT_DOC_REQUIRED` on a fresh app. The route that mints one is
|
|
1379
|
+
// idempotent, so asking for it is free on the second call.
|
|
1380
|
+
//
|
|
1381
|
+
// `--as system` keeps the synthetic `fn:<functionId>` context and does
|
|
1382
|
+
// NOT call the route: there is no app user to mint a document for.
|
|
1383
|
+
let contextDocId = options.contextDocId;
|
|
1384
|
+
if (!contextDocId && !identity.asSystem && identity.actingUserId) {
|
|
1385
|
+
const root = await client.ensureUserRootDocument(resolvedAppId, identity.actingUserId);
|
|
1386
|
+
contextDocId = root.rootDocId;
|
|
1387
|
+
}
|
|
1388
|
+
const body = {
|
|
1389
|
+
rootInput: flags.input,
|
|
1390
|
+
// #3482 — `--as system` is the ONE door that is a single route for
|
|
1391
|
+
// both runtimes, so it keeps the deprecated body selector; every
|
|
1392
|
+
// other call says it with the route.
|
|
1393
|
+
...(identity.asSystem ? { mode: "task" } : {}),
|
|
1394
|
+
...(contextDocId ? { contextDocId } : {}),
|
|
1395
|
+
...(options.runKey ? { runKey: options.runKey } : {}),
|
|
1396
|
+
};
|
|
1397
|
+
const answer = await runAs(client, resolvedAppId, flags.identity, "start", functionKey, (bearer) => client.invokeFunction(resolvedAppId, functionKey, body, {
|
|
1398
|
+
...(bearer ? { bearer } : {}),
|
|
1399
|
+
asSystem: identity.asSystem,
|
|
1400
|
+
runtime: "task",
|
|
1401
|
+
}));
|
|
1402
|
+
const envelope = answer.body ?? {};
|
|
1403
|
+
if (answer.httpStatus >= 400) {
|
|
1404
|
+
if (options.json) {
|
|
1405
|
+
json({
|
|
1406
|
+
functionId: target.functionId,
|
|
1407
|
+
functionKey,
|
|
1408
|
+
identity: identity.describe,
|
|
1409
|
+
httpStatus: answer.httpStatus,
|
|
1410
|
+
...(answer.retryAfterSeconds !== null
|
|
1411
|
+
? { retryAfterSeconds: answer.retryAfterSeconds }
|
|
1412
|
+
: {}),
|
|
1413
|
+
...envelope,
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
else {
|
|
1417
|
+
renderRefusal(answer);
|
|
1418
|
+
}
|
|
1419
|
+
process.exit(1);
|
|
1420
|
+
}
|
|
1421
|
+
const runId = String(envelope.runId ?? "");
|
|
1422
|
+
if (!options.json) {
|
|
1423
|
+
if (envelope.existing) {
|
|
1424
|
+
info(`Replayed the existing run for this run key (existing).`);
|
|
1425
|
+
}
|
|
1426
|
+
console.log(identity.describe);
|
|
1427
|
+
printIdLines(startIdLines({ functionId: target.functionId, runId }));
|
|
1428
|
+
}
|
|
1429
|
+
if (!options.wait) {
|
|
1430
|
+
if (options.json) {
|
|
1431
|
+
json({
|
|
1432
|
+
functionId: target.functionId,
|
|
1433
|
+
functionKey,
|
|
1434
|
+
identity: identity.describe,
|
|
1435
|
+
httpStatus: answer.httpStatus,
|
|
1436
|
+
...envelope,
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
process.exit(0);
|
|
1440
|
+
}
|
|
1441
|
+
// The run id is printed BEFORE the wait begins, so an operator who
|
|
1442
|
+
// interrupts still holds the thing they need to resume.
|
|
1443
|
+
const outcome = await waitForFunctionRun({
|
|
1444
|
+
client,
|
|
1445
|
+
appId: resolvedAppId,
|
|
1446
|
+
functionId: target.functionId,
|
|
1447
|
+
runId,
|
|
1448
|
+
timeoutSeconds: flags.timeoutSeconds ?? WAIT_DEFAULT_TIMEOUT_SECONDS,
|
|
1449
|
+
json: !!options.json,
|
|
1450
|
+
});
|
|
1451
|
+
process.exit(outcome);
|
|
1452
|
+
}
|
|
1453
|
+
catch (err) {
|
|
1454
|
+
error(err.message);
|
|
1455
|
+
process.exit(1);
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
1458
|
+
runs
|
|
1459
|
+
.command("wait")
|
|
1460
|
+
.allowExcessArguments(false)
|
|
1461
|
+
.description("Poll a function run until it settles")
|
|
1462
|
+
.argument("<function-id>", "Function ID")
|
|
1463
|
+
.argument("<run-id>", "Run ID")
|
|
1464
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
1465
|
+
.option("--timeout <seconds>", "Give up after this many seconds (default 900)")
|
|
1466
|
+
.option("--json", "Output as JSON")
|
|
1467
|
+
.action(async (functionId, runId, _options, command) => {
|
|
1468
|
+
// `optsWithGlobals`, for the reason `runs steps` records: `runs` declares
|
|
1469
|
+
// `--app` and `--json` of its own, so both land on the parent.
|
|
1470
|
+
const options = command.optsWithGlobals();
|
|
1471
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
1472
|
+
const flags = resolveRunFlags({ timeout: options.timeout });
|
|
1473
|
+
if (flags.ok === false) {
|
|
1474
|
+
error(flags.error);
|
|
1475
|
+
process.exit(1);
|
|
1476
|
+
}
|
|
1477
|
+
const client = new ApiClient();
|
|
1478
|
+
try {
|
|
1479
|
+
const outcome = await waitForFunctionRun({
|
|
1480
|
+
client,
|
|
1481
|
+
appId: resolvedAppId,
|
|
1482
|
+
functionId,
|
|
1483
|
+
runId,
|
|
1484
|
+
timeoutSeconds: flags.timeoutSeconds ?? WAIT_DEFAULT_TIMEOUT_SECONDS,
|
|
1485
|
+
json: !!options.json,
|
|
1486
|
+
});
|
|
1487
|
+
process.exit(outcome);
|
|
1488
|
+
}
|
|
1489
|
+
catch (err) {
|
|
1490
|
+
error(err.message);
|
|
1491
|
+
process.exit(1);
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* A refusal, rendered from the envelope the server sent — #3448.
|
|
1497
|
+
*
|
|
1498
|
+
* `ApiClient.invokeFunction` hands the answer back rather than throwing for
|
|
1499
|
+
* exactly this: a 429 carries a `Retry-After` an operator needs and a 409 mode
|
|
1500
|
+
* mismatch carries the version's real mode, and re-deriving either from an
|
|
1501
|
+
* exception message would be re-deriving what the server already said.
|
|
1502
|
+
*/
|
|
1503
|
+
/**
|
|
1504
|
+
* Whether a directory still holds an agent's generated artifact (either
|
|
1505
|
+
* language) — #3798, CSO-3798-003: a tree whose last agent was removed has
|
|
1506
|
+
* nothing left to generate, and those files still need sweeping (and
|
|
1507
|
+
* reporting under `--check`).
|
|
1508
|
+
*/
|
|
1509
|
+
function holdsAgentArtifacts(dir) {
|
|
1510
|
+
if (!existsSync(dir))
|
|
1511
|
+
return false;
|
|
1512
|
+
try {
|
|
1513
|
+
return readdirSync(dir).some((file) => file.endsWith(AGENT_CLIENT_FILE_SUFFIX) || file.endsWith(AGENT_SWIFT_FILE_SUFFIX));
|
|
1514
|
+
}
|
|
1515
|
+
catch {
|
|
1516
|
+
return false;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
function renderRefusal(answer) {
|
|
1520
|
+
const lines = refusalLines(answer);
|
|
1521
|
+
error(lines.message);
|
|
1522
|
+
for (const field of lines.fields)
|
|
1523
|
+
printResult(field.label, field.value);
|
|
1524
|
+
for (const detail of lines.details)
|
|
1525
|
+
info(` ${detail}`);
|
|
1526
|
+
}
|
|
1527
|
+
/** Exit 0 only for a completed invocation; every refusal and every terminal failure is 1. */
|
|
1528
|
+
function exitCodeForInvokeAnswer(answer) {
|
|
1529
|
+
if (answer.httpStatus >= 400)
|
|
1530
|
+
return 1;
|
|
1531
|
+
return exitCodeForInvokeStatus(answer.body?.status);
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Run a call under the operator's own identity, or under a ten-minute token
|
|
1535
|
+
* minted for `--user` — D3448-009.
|
|
1536
|
+
*
|
|
1537
|
+
* The token is a REAL credential for another user, so it is revoked on every
|
|
1538
|
+
* exit path including Ctrl-C (the wrapper's `finally`), and its value never
|
|
1539
|
+
* reaches stdout, stderr or `--json`: `use` receives it and nothing else does.
|
|
1540
|
+
*/
|
|
1541
|
+
async function runAs(client, appId, identity, verb, functionKey, use) {
|
|
1542
|
+
if (identity.kind !== "user")
|
|
1543
|
+
return use(undefined);
|
|
1544
|
+
return withMintedToken({
|
|
1545
|
+
mint: async () => {
|
|
1546
|
+
const minted = await client.createToken(appId, {
|
|
1547
|
+
name: userTokenName(verb, functionKey),
|
|
1548
|
+
ttl: USER_TOKEN_TTL,
|
|
1549
|
+
userId: identity.userId,
|
|
1550
|
+
});
|
|
1551
|
+
return { token: minted.token, tokenId: minted.tokenId };
|
|
1552
|
+
},
|
|
1553
|
+
revoke: (tokenId) => client.revokeToken(appId, tokenId).then(() => undefined),
|
|
1554
|
+
warn: (message) => warn(message),
|
|
1555
|
+
}, (token) => use(token));
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Poll one run until it settles, the budget is spent, or the operator
|
|
1559
|
+
* interrupts — #3448 behavior 16.
|
|
1560
|
+
*
|
|
1561
|
+
* It polls the ADMIN single-run read, never the runs listing: that reports the
|
|
1562
|
+
* STORED status, and a durable run's row stays `running` until something asks
|
|
1563
|
+
* the engine, so a wait built on it would never settle.
|
|
1564
|
+
*
|
|
1565
|
+
* Ctrl-C exits 130 after printing the resume command, and a second one exits
|
|
1566
|
+
* at once — the failures sweep's shape, and the workflows group's convention.
|
|
1567
|
+
*/
|
|
1568
|
+
async function waitForFunctionRun(args) {
|
|
1569
|
+
const { client, appId, functionId, runId } = args;
|
|
1570
|
+
const deadline = Date.now() + args.timeoutSeconds * 1000;
|
|
1571
|
+
let interrupted = false;
|
|
1572
|
+
let interruptCount = 0;
|
|
1573
|
+
const onSigint = () => {
|
|
1574
|
+
interruptCount += 1;
|
|
1575
|
+
if (interruptCount > 1)
|
|
1576
|
+
process.exit(EXIT_INTERRUPTED);
|
|
1577
|
+
interrupted = true;
|
|
1578
|
+
};
|
|
1579
|
+
process.on("SIGINT", onSigint);
|
|
1580
|
+
try {
|
|
1581
|
+
let attempt = 0;
|
|
1582
|
+
for (;;) {
|
|
1583
|
+
const read = await client.getFunctionRun(appId, functionId, runId);
|
|
1584
|
+
const status = String(read?.status?.status ?? "");
|
|
1585
|
+
const settled = exitCodeForRunStatus(status);
|
|
1586
|
+
if (settled !== null) {
|
|
1587
|
+
if (args.json) {
|
|
1588
|
+
json({ functionId, runId, ...read });
|
|
1589
|
+
return settled;
|
|
1590
|
+
}
|
|
1591
|
+
printResult("Status", formatStatus(status));
|
|
1592
|
+
if (read?.status?.output !== undefined) {
|
|
1593
|
+
console.log("Output");
|
|
1594
|
+
console.log(JSON.stringify(read.status.output, null, 2));
|
|
1595
|
+
}
|
|
1596
|
+
const failure = read?.status?.error;
|
|
1597
|
+
if (failure) {
|
|
1598
|
+
printResult("Error", typeof failure === "string" ? failure : String(failure?.message ?? ""));
|
|
1599
|
+
}
|
|
1600
|
+
if (read?.run?.errorCode)
|
|
1601
|
+
printResult("Code", String(read.run.errorCode));
|
|
1602
|
+
return settled;
|
|
1603
|
+
}
|
|
1604
|
+
if (interrupted) {
|
|
1605
|
+
if (!args.json) {
|
|
1606
|
+
info(`Stopped waiting. The run is still going; resume with:`);
|
|
1607
|
+
info(` ${resumeCommand(functionId, runId)}`);
|
|
1608
|
+
}
|
|
1609
|
+
return EXIT_INTERRUPTED;
|
|
1610
|
+
}
|
|
1611
|
+
if (Date.now() >= deadline) {
|
|
1612
|
+
if (args.json) {
|
|
1613
|
+
json({ functionId, runId, timedOut: true, ...read });
|
|
1614
|
+
}
|
|
1615
|
+
else {
|
|
1616
|
+
warn(`Gave up after ${args.timeoutSeconds}s; the run is still ${status || "in flight"}. Resume with:`);
|
|
1617
|
+
info(` ${resumeCommand(functionId, runId)}`);
|
|
1618
|
+
}
|
|
1619
|
+
return EXIT_TIMED_OUT;
|
|
1620
|
+
}
|
|
1621
|
+
const delay = Math.min(waitDelayMs(attempt), Math.max(0, deadline - Date.now()));
|
|
1622
|
+
attempt += 1;
|
|
1623
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
finally {
|
|
1627
|
+
process.off("SIGINT", onSigint);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
//# sourceMappingURL=functions.js.map
|