atmn 1.1.25 → 2.0.1
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 +120 -0
- package/dist/bin.js +23318 -0
- package/dist/index.js +2946 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +28 -74
- package/src/actions/api/callApi.ts +280 -0
- package/src/actions/api/registerApiCommands.ts +121 -0
- package/src/actions/env/fetchOrgInfo.ts +32 -0
- package/src/actions/env/types/orgInfo.ts +18 -0
- package/src/actions/env.ts +94 -0
- package/src/actions/init/runInit.ts +401 -0
- package/src/actions/login/keyless.ts +135 -0
- package/src/actions/login.ts +144 -0
- package/src/actions/pull/appendPlanVersionFixture.ts +373 -0
- package/src/actions/pull/applyPreview.ts +553 -0
- package/src/actions/pull/applySettingsPreview.ts +114 -0
- package/src/actions/pull/changedFixtureKeys.ts +88 -0
- package/src/actions/pull/listSourceFiles.ts +29 -0
- package/src/actions/pull/locateFixture.ts +78 -0
- package/src/actions/pull/resolveCollectionTarget.ts +198 -0
- package/src/actions/pull/rewriteConfig.ts +57 -0
- package/src/actions/pull/scaffoldConfig.ts +118 -0
- package/src/actions/pull.ts +399 -0
- package/src/actions/push/backfillInternalIds.ts +394 -0
- package/src/actions/push/deprecatedFields.ts +55 -0
- package/src/actions/push.ts +294 -0
- package/src/actions/reset/runReset.ts +58 -0
- package/src/actions/sandbox/createSandbox.ts +83 -0
- package/src/actions/sandbox/deleteSandbox.ts +91 -0
- package/src/actions/sandbox/listSandboxes.ts +28 -0
- package/src/actions/sandbox/types/sandboxClient.ts +16 -0
- package/src/actions/sandbox/useSandbox.ts +157 -0
- package/src/actions/sandbox/withSandboxScopeHint.ts +27 -0
- package/src/actions/skills/skills.ts +246 -0
- package/src/auth/announceAuthorizationUrl.ts +26 -0
- package/src/auth/browser/openSystemBrowser.ts +7 -0
- package/src/auth/browser/tryOpenBrowser.ts +17 -0
- package/src/auth/browser/watchLauncher.ts +46 -0
- package/src/auth/buildAuthorizationUrl.ts +36 -0
- package/src/auth/callbackPages.ts +126 -0
- package/src/auth/createOrgApiKeys.ts +46 -0
- package/src/auth/keyless.ts +119 -0
- package/src/auth/oauthConfig.ts +63 -0
- package/src/auth/runOAuthFlow.ts +230 -0
- package/src/auth/types/browserOpener.ts +5 -0
- package/src/auth/types/impersonationTokens.ts +23 -0
- package/src/auth/types/oauthTokens.ts +13 -0
- package/src/auth/types/orgApiKeys.ts +6 -0
- package/src/bin.ts +9 -0
- package/src/cli.ts +648 -0
- package/src/config/configPackageName.ts +9 -0
- package/src/config/legacyConfig.ts +25 -0
- package/src/config/loadConfig.ts +231 -0
- package/src/env/assertSandboxTarget.ts +20 -0
- package/src/env/loadEnv.ts +184 -0
- package/src/env/resolveTarget.ts +134 -0
- package/src/env/sandboxKeyName.ts +18 -0
- package/src/generated/apiRoutes.ts +3937 -0
- package/src/generated/client.ts +52579 -0
- package/src/generated/emit.ts +1188 -0
- package/src/generated/emitRuntime.ts +522 -0
- package/src/generated/features.ts +146 -0
- package/src/generated/labels.ts +29 -0
- package/src/generated/licenses.ts +292 -0
- package/src/generated/lintRules.ts +2247 -0
- package/src/generated/lintRuntime.ts +865 -0
- package/src/generated/plans.ts +1653 -0
- package/src/generated/referralPrograms.ts +22 -0
- package/src/generated/rewards.ts +58 -0
- package/src/generated/settings.ts +21 -0
- package/src/generated/skills.ts +305 -0
- package/src/generated/variants.ts +949 -0
- package/src/generated/wire.ts +334 -0
- package/src/http/autumnFetch.ts +30 -0
- package/src/index.ts +20 -0
- package/src/project/chooseConfigDir.ts +41 -0
- package/src/project/resolveProject.ts +115 -0
- package/src/project/rootMarker.ts +40 -0
- package/src/prompt/prompt.ts +186 -0
- package/src/prompt/select.ts +162 -0
- package/src/render/renderEnv.ts +77 -0
- package/src/render/renderPreview.ts +945 -0
- package/src/render/renderSandboxes.ts +92 -0
- package/src/render/stripTerminalControls.ts +19 -0
- package/src/repo/findRepoRoot.ts +79 -0
- package/src/surgery/appendPropertyEdit.ts +67 -0
- package/src/surgery/appendToArray.ts +87 -0
- package/src/surgery/appendToBinding.ts +19 -0
- package/src/surgery/appendToCollection.ts +40 -0
- package/src/surgery/appendToFixtureArray.ts +71 -0
- package/src/surgery/arrayBinding.ts +30 -0
- package/src/surgery/deleteFixtureLiteral.ts +81 -0
- package/src/surgery/deleteReference.ts +48 -0
- package/src/surgery/ensureBuilderImport.ts +65 -0
- package/src/surgery/findFixture.ts +238 -0
- package/src/surgery/fixtureEdit.ts +156 -0
- package/src/surgery/fixtureLocation.ts +32 -0
- package/src/surgery/insertCollection.ts +86 -0
- package/src/surgery/insertFirstProperty.ts +73 -0
- package/src/surgery/patchFixtureProperty.ts +152 -0
- package/src/surgery/patchSingletonProperty.ts +221 -0
- package/src/surgery/replaceFixture.ts +28 -0
- package/src/surgery/setFixtureProperty.ts +55 -0
- package/src/surgery/staticFixtureRule.ts +48 -0
- package/src/version.ts +5 -0
- package/dist/cli.js +0 -146296
- package/dist/compose/index.js +0 -122
- package/dist/src/compose/builders/builderFunctions.d.ts +0 -84
- package/dist/src/compose/builders/rewardFunctions.d.ts +0 -5
- package/dist/src/compose/builders/variantFunctions.d.ts +0 -2
- package/dist/src/compose/index.d.ts +0 -19
- package/dist/src/compose/models/featureModels.d.ts +0 -262
- package/dist/src/compose/models/index.d.ts +0 -3
- package/dist/src/compose/models/planModels.d.ts +0 -562
- package/dist/src/compose/models/rewardModels.d.ts +0 -52
- package/dist/src/compose/models/variantModels.d.ts +0 -34
- package/readme.md +0 -186
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"7.0.0-dev.20260511.1","root":["../src/cli.tsx","../src/constants.ts","../src/commands/auth/command.ts","../src/commands/auth/constants.ts","../src/commands/auth/oauth.ts","../src/commands/config/command.ts","../src/commands/customers/command.tsx","../src/commands/customers/headless.ts","../src/commands/customers/index.ts","../src/commands/events/command.tsx","../src/commands/events/headless.ts","../src/commands/events/index.ts","../src/commands/events-aggregate-test/command.ts","../src/commands/events-aggregate-test/index.ts","../src/commands/features/command.tsx","../src/commands/features/headless.ts","../src/commands/features/index.ts","../src/commands/nuke/backup.ts","../src/commands/nuke/deletions.ts","../src/commands/nuke/legacyNuke.ts","../src/commands/nuke/types.ts","../src/commands/nuke/validation.ts","../src/commands/preview/index.ts","../src/commands/preview/previewPlan.ts","../src/commands/products/command.tsx","../src/commands/products/headless.ts","../src/commands/products/index.ts","../src/commands/pull/index.ts","../src/commands/pull/mergeEnvironments.ts","../src/commands/pull/pull.ts","../src/commands/pull/pullFromEnvironment.ts","../src/commands/pull/sdkTypes.ts","../src/commands/pull/types.ts","../src/commands/pull/writeConfig.ts","../src/commands/push/headless.ts","../src/commands/push/index.ts","../src/commands/push/prompts.ts","../src/commands/push/push.ts","../src/commands/push/types.ts","../src/commands/push/validate.ts","../src/commands/push/variantPropagation.ts","../src/commands/test-diff/command.ts","../src/commands/test-diff/index.ts","../src/commands/test-template/command.tsx","../src/compose/index.ts","../src/compose/builders/builderFunctions.ts","../src/compose/builders/rewardFunctions.ts","../src/compose/builders/variantFunctions.ts","../src/compose/models/featureModels.ts","../src/compose/models/index.ts","../src/compose/models/planModels.ts","../src/compose/models/rewardModels.ts","../src/compose/models/variantModels.ts","../src/lib/utils.ts","../src/lib/version.ts","../src/lib/writeEmptyConfig.ts","../src/lib/animation/explosion.ts","../src/lib/api/client.ts","../src/lib/api/endpoints/catalog.ts","../src/lib/api/endpoints/customers.ts","../src/lib/api/endpoints/events.ts","../src/lib/api/endpoints/features.ts","../src/lib/api/endpoints/index.ts","../src/lib/api/endpoints/organization.ts","../src/lib/api/endpoints/plans.ts","../src/lib/api/endpoints/rewards.ts","../src/lib/api/types/feature.ts","../src/lib/api/types/index.ts","../src/lib/api/types/organization.ts","../src/lib/api/types/plan.ts","../src/lib/api/types/planItem.ts","../src/lib/auth/headlessAuthRecovery.ts","../src/lib/config/loadConfig.ts","../src/lib/constants/templates.ts","../src/lib/constants/templates/index.ts","../src/lib/constants/templates/linear.config.ts","../src/lib/constants/templates/openai.config.ts","../src/lib/constants/templates/railway.config.ts","../src/lib/constants/templates/t3chat.config.ts","../src/lib/env/backendUrl.ts","../src/lib/env/cliContext.ts","../src/lib/env/detect.ts","../src/lib/env/dotenv.ts","../src/lib/env/index.ts","../src/lib/env/keys.ts","../src/lib/headless/CustomersController.ts","../src/lib/headless/FeaturesController.ts","../src/lib/headless/PlansController.ts","../src/lib/headless/index.ts","../src/lib/headless/types.ts","../src/lib/hooks/index.ts","../src/lib/hooks/useAgentSetup.ts","../src/lib/hooks/useAuthRecovery.ts","../src/lib/hooks/useClipboard.ts","../src/lib/hooks/useConfigCounts.ts","../src/lib/hooks/useCreateGuides.ts","../src/lib/hooks/useCreateSkills.ts","../src/lib/hooks/useCustomerExpanded.ts","../src/lib/hooks/useCustomerNavigation.ts","../src/lib/hooks/useCustomers.ts","../src/lib/hooks/useEnvironmentStore.ts","../src/lib/hooks/useEvents.ts","../src/lib/hooks/useEventsAggregate.ts","../src/lib/hooks/useEventsAggregateApi.ts","../src/lib/hooks/useEventsFilter.ts","../src/lib/hooks/useFeatures.ts","../src/lib/hooks/useHasCustomers.ts","../src/lib/hooks/useHeadlessAuth.ts","../src/lib/hooks/useListNavigation.ts","../src/lib/hooks/useLogin.ts","../src/lib/hooks/useNuke.ts","../src/lib/hooks/useNukeData.ts","../src/lib/hooks/useOrganization.ts","../src/lib/hooks/usePlans.ts","../src/lib/hooks/usePull.ts","../src/lib/hooks/usePush.ts","../src/lib/hooks/useTerminalSize.ts","../src/lib/hooks/useVisibleRowCount.ts","../src/lib/hooks/useWriteTemplateConfig.ts","../src/lib/transforms/index.ts","../src/lib/transforms/multiCurrency.test.ts","../src/lib/transforms/apiToSdk/Transformer.test.ts","../src/lib/transforms/apiToSdk/Transformer.ts","../src/lib/transforms/apiToSdk/feature.ts","../src/lib/transforms/apiToSdk/helpers.ts","../src/lib/transforms/apiToSdk/index.ts","../src/lib/transforms/apiToSdk/plan.ts","../src/lib/transforms/apiToSdk/planItem.ts","../src/lib/transforms/apiToSdk/reward.ts","../src/lib/transforms/inPlaceUpdate/index.ts","../src/lib/transforms/inPlaceUpdate/parseConfig.ts","../src/lib/transforms/inPlaceUpdate/updateConfig.ts","../src/lib/transforms/sdkToApi/feature.ts","../src/lib/transforms/sdkToApi/index.ts","../src/lib/transforms/sdkToApi/plan.ts","../src/lib/transforms/sdkToApi/reward.ts","../src/lib/transforms/sdkToCode/configFile.ts","../src/lib/transforms/sdkToCode/feature.ts","../src/lib/transforms/sdkToCode/helpers.test.ts","../src/lib/transforms/sdkToCode/helpers.ts","../src/lib/transforms/sdkToCode/imports.ts","../src/lib/transforms/sdkToCode/index.ts","../src/lib/transforms/sdkToCode/plan.ts","../src/lib/transforms/sdkToCode/planItem.ts","../src/lib/transforms/sdkToCode/reward.ts","../src/lib/transforms/sdkToCode/variant.ts","../src/lib/utils/index.ts","../src/lib/utils/monorepo.ts","../src/prompts/skills/autumn-billing-page.ts","../src/prompts/skills/autumn-gating.ts","../src/prompts/skills/autumn-modelling-pricing-plans.ts","../src/prompts/skills/autumn-setup.ts","../src/prompts/skills/index.ts","../src/views/App.tsx","../src/views/html/oauth-callback.ts","../src/views/react/test-agent.tsx","../src/views/react/components/AuthRecoveryBoundary.tsx","../src/views/react/components/Card.tsx","../src/views/react/components/KeyValue.tsx","../src/views/react/components/LoadingText.tsx","../src/views/react/components/MultiSelect.tsx","../src/views/react/components/ProgressBar.tsx","../src/views/react/components/PromptCard.tsx","../src/views/react/components/SelectMenu.tsx","../src/views/react/components/StatusLine.tsx","../src/views/react/components/StatusRow.tsx","../src/views/react/components/StepHeader.tsx","../src/views/react/components/index.ts","../src/views/react/components/providers/CardWidthContext.tsx","../src/views/react/components/providers/QueryProvider.tsx","../src/views/react/components/providers/index.ts","../src/views/react/customers/CustomersView.tsx","../src/views/react/customers/types.ts","../src/views/react/customers/components/CustomerRow.tsx","../src/views/react/customers/components/CustomerSheet.tsx","../src/views/react/customers/components/CustomersTable.tsx","../src/views/react/customers/components/EmptyState.tsx","../src/views/react/customers/components/ErrorState.tsx","../src/views/react/customers/components/KeybindHints.tsx","../src/views/react/customers/components/LoadingState.tsx","../src/views/react/customers/components/SearchInput.tsx","../src/views/react/customers/components/TitleBar.tsx","../src/views/react/customers/components/index.ts","../src/views/react/customers/components/sections/BalancesSection.tsx","../src/views/react/customers/components/sections/EntitiesSection.tsx","../src/views/react/customers/components/sections/InvoicesSection.tsx","../src/views/react/customers/components/sections/ReferralsSection.tsx","../src/views/react/customers/components/sections/RewardsSection.tsx","../src/views/react/customers/components/sections/SubscriptionsSection.tsx","../src/views/react/customers/components/sections/index.ts","../src/views/react/events/EventsView.tsx","../src/views/react/events/index.ts","../src/views/react/events/components/EventSheet.tsx","../src/views/react/events/components/EventsAggregateView.tsx","../src/views/react/events/components/EventsFilterSheet.tsx","../src/views/react/events/components/index.ts","../src/views/react/features/FeaturesView.tsx","../src/views/react/features/index.ts","../src/views/react/features/components/FeatureSheet.tsx","../src/views/react/features/components/index.ts","../src/views/react/init/HeadlessInitFlow.tsx","../src/views/react/init/InitFlow.tsx","../src/views/react/init/steps/AgentStep.tsx","../src/views/react/init/steps/AuthStep.tsx","../src/views/react/init/steps/ConfigStep.tsx","../src/views/react/init/steps/HandoffStep.tsx","../src/views/react/init/steps/PathInputStep.tsx","../src/views/react/init/steps/StripeStep.tsx","../src/views/react/login/LoginView.tsx","../src/views/react/nuke/InlineNukeFlow.tsx","../src/views/react/nuke/NukeAnimation.tsx","../src/views/react/nuke/NukeView.tsx","../src/views/react/nuke/components/BackupPrompt.tsx","../src/views/react/nuke/components/ConfirmScreen.tsx","../src/views/react/nuke/components/DeletionProgress.tsx","../src/views/react/nuke/components/FinalSummary.tsx","../src/views/react/nuke/components/SuccessScreen.tsx","../src/views/react/nuke/components/WarningScreen.tsx","../src/views/react/preview/PlanPreviewCard.tsx","../src/views/react/preview/PreviewView.tsx","../src/views/react/preview/index.ts","../src/views/react/primitives/index.ts","../src/views/react/primitives/input/SearchInput.tsx","../src/views/react/primitives/input/index.ts","../src/views/react/primitives/layout/BottomBar.tsx","../src/views/react/primitives/layout/ListViewLayout.tsx","../src/views/react/primitives/layout/SplitPane.tsx","../src/views/react/primitives/layout/TitleBar.tsx","../src/views/react/primitives/layout/index.ts","../src/views/react/primitives/sheet/DetailSheet.tsx","../src/views/react/primitives/sheet/SheetSection.tsx","../src/views/react/primitives/sheet/index.ts","../src/views/react/primitives/states/EmptyState.tsx","../src/views/react/primitives/states/ErrorState.tsx","../src/views/react/primitives/states/LoadingState.tsx","../src/views/react/primitives/states/index.ts","../src/views/react/primitives/table/DataTable.tsx","../src/views/react/primitives/table/TableRow.tsx","../src/views/react/primitives/table/index.ts","../src/views/react/primitives/utils/formatDate.ts","../src/views/react/primitives/utils/index.ts","../src/views/react/primitives/utils/pagination.ts","../src/views/react/primitives/utils/truncate.ts","../src/views/react/products/ProductsView.tsx","../src/views/react/products/index.ts","../src/views/react/products/components/ProductSheet.tsx","../src/views/react/products/components/index.ts","../src/views/react/pull/Pull.tsx","../src/views/react/pull/components/FeatureRow.tsx","../src/views/react/pull/components/FileRow.tsx","../src/views/react/pull/components/PlanRow.tsx","../src/views/react/pull/components/index.ts","../src/views/react/push/Push.tsx","../src/views/react/push/index.ts","../src/views/react/push/components/CompletionMessage.tsx","../src/views/react/push/components/ConfigResourcesPromptCard.tsx","../src/views/react/push/components/ErrorCard.tsx","../src/views/react/push/components/FeaturesCard.tsx","../src/views/react/push/components/NoChangesCard.tsx","../src/views/react/push/components/OrgCard.tsx","../src/views/react/push/components/OverviewCard.tsx","../src/views/react/push/components/PlansCard.tsx","../src/views/react/push/components/PushPromptCard.tsx","../src/views/react/push/components/index.ts","../src/views/react/template/TemplateSelector.tsx","../src/views/react/template2/Badge.tsx","../src/views/react/template2/PlanCard.tsx","../src/views/react/template2/TemplateRow.tsx","../src/views/react/template2/TemplateSelector2.tsx","../src/views/react/template2/data.ts","../src/views/react/template2/index.ts","../test/types/reward.ts"]}
|
|
1
|
+
{"version":"7.0.0-dev.20260511.1","root":["../src/bin.ts","../src/cli.ts","../src/index.ts","../src/version.ts","../src/actions/env.ts","../src/actions/login.ts","../src/actions/pull.ts","../src/actions/push.ts","../src/actions/api/callApi.ts","../src/actions/api/registerApiCommands.ts","../src/actions/env/fetchOrgInfo.ts","../src/actions/env/types/orgInfo.ts","../src/actions/init/runInit.ts","../src/actions/login/keyless.ts","../src/actions/pull/appendPlanVersionFixture.ts","../src/actions/pull/applyPreview.ts","../src/actions/pull/applySettingsPreview.ts","../src/actions/pull/changedFixtureKeys.ts","../src/actions/pull/listSourceFiles.ts","../src/actions/pull/locateFixture.ts","../src/actions/pull/resolveCollectionTarget.ts","../src/actions/pull/rewriteConfig.ts","../src/actions/pull/scaffoldConfig.ts","../src/actions/push/backfillInternalIds.ts","../src/actions/push/deprecatedFields.ts","../src/actions/reset/runReset.ts","../src/actions/sandbox/createSandbox.ts","../src/actions/sandbox/deleteSandbox.ts","../src/actions/sandbox/listSandboxes.ts","../src/actions/sandbox/useSandbox.ts","../src/actions/sandbox/withSandboxScopeHint.ts","../src/actions/sandbox/types/sandboxClient.ts","../src/actions/skills/skills.ts","../src/auth/announceAuthorizationUrl.ts","../src/auth/buildAuthorizationUrl.ts","../src/auth/callbackPages.ts","../src/auth/createOrgApiKeys.ts","../src/auth/keyless.ts","../src/auth/oauthConfig.ts","../src/auth/runOAuthFlow.ts","../src/auth/browser/openSystemBrowser.ts","../src/auth/browser/tryOpenBrowser.ts","../src/auth/browser/watchLauncher.ts","../src/auth/types/browserOpener.ts","../src/auth/types/impersonationTokens.ts","../src/auth/types/oauthTokens.ts","../src/auth/types/orgApiKeys.ts","../src/config/configPackageName.ts","../src/config/legacyConfig.ts","../src/config/loadConfig.ts","../src/env/assertSandboxTarget.ts","../src/env/loadEnv.ts","../src/env/resolveTarget.ts","../src/env/sandboxKeyName.ts","../src/generated/apiRoutes.ts","../src/generated/client.ts","../src/generated/emit.ts","../src/generated/emitRuntime.ts","../src/generated/features.ts","../src/generated/labels.ts","../src/generated/licenses.ts","../src/generated/lintRules.ts","../src/generated/lintRuntime.ts","../src/generated/plans.ts","../src/generated/referralPrograms.ts","../src/generated/rewards.ts","../src/generated/settings.ts","../src/generated/skills.ts","../src/generated/variants.ts","../src/generated/wire.ts","../src/http/autumnFetch.ts","../src/project/chooseConfigDir.ts","../src/project/resolveProject.ts","../src/project/rootMarker.ts","../src/prompt/prompt.ts","../src/prompt/select.ts","../src/render/renderEnv.ts","../src/render/renderPreview.ts","../src/render/renderSandboxes.ts","../src/render/stripTerminalControls.ts","../src/repo/findRepoRoot.ts","../src/surgery/appendPropertyEdit.ts","../src/surgery/appendToArray.ts","../src/surgery/appendToBinding.ts","../src/surgery/appendToCollection.ts","../src/surgery/appendToFixtureArray.ts","../src/surgery/arrayBinding.ts","../src/surgery/deleteFixtureLiteral.ts","../src/surgery/deleteReference.ts","../src/surgery/ensureBuilderImport.ts","../src/surgery/findFixture.ts","../src/surgery/fixtureEdit.ts","../src/surgery/fixtureLocation.ts","../src/surgery/insertCollection.ts","../src/surgery/insertFirstProperty.ts","../src/surgery/patchFixtureProperty.ts","../src/surgery/patchSingletonProperty.ts","../src/surgery/replaceFixture.ts","../src/surgery/setFixtureProperty.ts","../src/surgery/staticFixtureRule.ts","../test/apiCommand.test.ts","../test/authorizationUrl.test.ts","../test/autumnFetch.test.ts","../test/backfill.test.ts","../test/backfillInlineVariants.test.ts","../test/backfillSlug.test.ts","../test/backfillVariants.test.ts","../test/chooseConfigDir.test.ts","../test/cliTargetFlags.test.ts","../test/client.test.ts","../test/configPackageName.test.ts","../test/emitFixture.test.ts","../test/env.test.ts","../test/envWriter.test.ts","../test/init.test.ts","../test/keyless.test.ts","../test/legacyConfig.test.ts","../test/lintDocument.test.ts","../test/loadConfigNode.test.ts","../test/loadConfigV2.test.ts","../test/login.test.ts","../test/nodeNextTypes.test.ts","../test/plans.test.ts","../test/previewIsEmpty.test.ts","../test/prompt.test.ts","../test/pull.test.ts","../test/pullByReference.test.ts","../test/pullFieldLevel.test.ts","../test/pullOmitDefaults.test.ts","../test/pullPlans.test.ts","../test/pullStatesSlugs.test.ts","../test/pullTwoCollections.test.ts","../test/renameHint.test.ts","../test/renderPreview.test.ts","../test/renderSandboxes.test.ts","../test/renderSettings.test.ts","../test/reset.test.ts","../test/resolveCollectionTarget.test.ts","../test/resolveProject.test.ts","../test/resolveTarget.test.ts","../test/sandboxActions.test.ts","../test/sandboxKeyName.test.ts","../test/sandboxUse.test.ts","../test/settingsActions.test.ts","../test/settingsWire.test.ts","../test/skills.test.ts","../test/watchLauncher.test.ts","../test/wireDocument.test.ts","../test/helpers/targetFor.ts","../test/surgery/appendToCollection.test.ts","../test/surgery/deleteFixtureLiteral.test.ts","../test/surgery/deleteReference.test.ts","../test/surgery/findFixture.test.ts","../test/surgery/fixtureLocation.test.ts","../test/surgery/insertCollection.test.ts","../test/surgery/insertFirstProperty.test.ts","../test/surgery/patchFixtureProperty.test.ts","../test/surgery/patchSingletonProperty.test.ts","../test/surgery/replaceFixture.test.ts","../test/surgery/staticFixtureRule.test.ts"]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atmn",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"description": "The CLI for Autumn — define your pricing in code, sync it to Autumn, and keep everything in version control.",
|
|
5
6
|
"bin": {
|
|
6
|
-
"atmn": "dist/
|
|
7
|
+
"atmn": "dist/bin.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "src/index.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./src/index.ts",
|
|
14
|
+
"bun": "./src/index.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
7
18
|
},
|
|
8
19
|
"repository": {
|
|
9
20
|
"type": "git",
|
|
@@ -11,89 +22,32 @@
|
|
|
11
22
|
"directory": "packages/atmn"
|
|
12
23
|
},
|
|
13
24
|
"homepage": "https://docs.useautumn.com/api-reference/cli/getting-started",
|
|
14
|
-
"main": "dist/compose/index.js",
|
|
15
|
-
"types": "dist/src/compose/index.d.ts",
|
|
16
|
-
"exports": {
|
|
17
|
-
".": {
|
|
18
|
-
"types": "./dist/src/compose/index.d.ts",
|
|
19
|
-
"import": "./dist/compose/index.js",
|
|
20
|
-
"default": "./dist/compose/index.js"
|
|
21
|
-
},
|
|
22
|
-
"./skills": {
|
|
23
|
-
"types": "./src/prompts/skills/index.ts",
|
|
24
|
-
"import": "./src/prompts/skills/index.ts",
|
|
25
|
-
"default": "./src/prompts/skills/index.ts"
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
25
|
"type": "module",
|
|
29
26
|
"engines": {
|
|
30
|
-
"node": ">=
|
|
27
|
+
"node": ">=20"
|
|
31
28
|
},
|
|
32
29
|
"scripts": {
|
|
33
30
|
"ts": "bunx tsgo --build --noEmit",
|
|
34
31
|
"build": "bun run bun.config.ts",
|
|
35
|
-
"dev": "
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"test:integration": "echo 'deprecated: v1 atmn integration — see packages/atmn/test/integration/DEPRECATED.md' && exit 0",
|
|
39
|
-
"test:integration:cli": "echo 'deprecated: v1 atmn integration — see packages/atmn/test/integration/DEPRECATED.md' && exit 0",
|
|
40
|
-
"test:unit": "bun test test/codegen",
|
|
41
|
-
"test:watch": "bun test --watch",
|
|
42
|
-
"test2": "prettier --check . && xo && ava",
|
|
43
|
-
"test-cli": "node --trace-deprecation -- ./dist/cli.js"
|
|
32
|
+
"dev": "bun scripts/watchBuild.ts",
|
|
33
|
+
"test": "bun test test",
|
|
34
|
+
"prepack": "bun run build"
|
|
44
35
|
},
|
|
45
36
|
"files": [
|
|
46
37
|
"dist",
|
|
38
|
+
"src",
|
|
47
39
|
"package.json",
|
|
48
40
|
"README.md"
|
|
49
41
|
],
|
|
50
42
|
"dependencies": {
|
|
51
|
-
"@
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"dotenv": "^17.2.0",
|
|
62
|
-
"ervy": "^1.0.7",
|
|
63
|
-
"ink": "^6.6.0",
|
|
64
|
-
"ink-big-text": "^2.0.0",
|
|
65
|
-
"ink-chart": "^0.1.1",
|
|
66
|
-
"ink-confirm-input": "^2.0.0",
|
|
67
|
-
"ink-scroll-list": "^0.4.1",
|
|
68
|
-
"ink-scroll-view": "^0.3.5",
|
|
69
|
-
"ink-select-input": "^6.2.0",
|
|
70
|
-
"ink-spinner": "^5.0.0",
|
|
71
|
-
"ink-table": "^3.1.0",
|
|
72
|
-
"ink-text-input": "^6.0.0",
|
|
73
|
-
"inquirer": "^12.7.0",
|
|
74
|
-
"jiti": "^2.4.2",
|
|
75
|
-
"open": "^10.1.2",
|
|
76
|
-
"prettier": "^3.6.2",
|
|
77
|
-
"react": "^19.2.3",
|
|
78
|
-
"yocto-spinner": "^1.0.0",
|
|
79
|
-
"zod": "^4.0.0",
|
|
80
|
-
"conf": "^13.0.1"
|
|
81
|
-
},
|
|
82
|
-
"ava": {
|
|
83
|
-
"extensions": {
|
|
84
|
-
"ts": "module",
|
|
85
|
-
"tsx": "module"
|
|
86
|
-
},
|
|
87
|
-
"nodeArguments": [
|
|
88
|
-
"--loader=ts-node/esm"
|
|
89
|
-
]
|
|
90
|
-
},
|
|
91
|
-
"xo": {
|
|
92
|
-
"extends": "xo-react",
|
|
93
|
-
"prettier": true,
|
|
94
|
-
"rules": {
|
|
95
|
-
"react/prop-types": "off"
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
"prettier": "@vdemedes/prettier-config"
|
|
43
|
+
"@ast-grep/napi": "0.45.3",
|
|
44
|
+
"arctic": "3.7.0",
|
|
45
|
+
"chalk": "5.6.2",
|
|
46
|
+
"commander": "14.0.3",
|
|
47
|
+
"dotenv": "16.6.1",
|
|
48
|
+
"jiti": "2.7.0",
|
|
49
|
+
"open": "10.2.0",
|
|
50
|
+
"yaml": "2.9.0"
|
|
51
|
+
},
|
|
52
|
+
"gitHead": "5a548945bb2a7bec37404428767108467915449d"
|
|
99
53
|
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import type { ApiField, ApiRoute } from "../../generated/apiRoutes";
|
|
2
|
+
import { API_ROUTES, API_VERSION } from "../../generated/apiRoutes";
|
|
3
|
+
import { AutumnApiError } from "../../generated/client";
|
|
4
|
+
import { autumnFetch } from "../../http/autumnFetch";
|
|
5
|
+
|
|
6
|
+
export type ApiCallOptions = {
|
|
7
|
+
route: ApiRoute;
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
secretKey: string;
|
|
10
|
+
/** `--body`: a JSON document, or `-` for stdin. */
|
|
11
|
+
body?: string;
|
|
12
|
+
/** `key=value` arguments, keyed by wire name; a JSON-typed field arrives as text. */
|
|
13
|
+
fields?: Record<string, string>;
|
|
14
|
+
/** `-H "name: value"`, repeatable; a stated header wins over the default. */
|
|
15
|
+
headers?: string[];
|
|
16
|
+
readStdin?: () => Promise<string>;
|
|
17
|
+
fetch?: typeof globalThis.fetch;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const findApiRoute = ({
|
|
21
|
+
group,
|
|
22
|
+
method,
|
|
23
|
+
}: {
|
|
24
|
+
group: string;
|
|
25
|
+
method: string;
|
|
26
|
+
}): ApiRoute | undefined =>
|
|
27
|
+
API_ROUTES.find((route) => route.group === group && route.method === method);
|
|
28
|
+
|
|
29
|
+
export const apiGroups = (): string[] => [
|
|
30
|
+
...new Set(API_ROUTES.map((route) => route.group)),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* `customer_id=cus_1`: fields ride as positional pairs rather than flags, so a
|
|
35
|
+
* field named `version` or `config` can never collide with the CLI's own.
|
|
36
|
+
*/
|
|
37
|
+
export const parseFieldArgs = ({
|
|
38
|
+
args,
|
|
39
|
+
}: {
|
|
40
|
+
args: string[];
|
|
41
|
+
}): Record<string, string> => {
|
|
42
|
+
const fields: Record<string, string> = {};
|
|
43
|
+
for (const arg of args) {
|
|
44
|
+
const at = arg.indexOf("=");
|
|
45
|
+
if (at <= 0)
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Expected key=value, got ${JSON.stringify(arg)}. Pass the whole body with --body instead.`,
|
|
48
|
+
);
|
|
49
|
+
fields[arg.slice(0, at)] = arg.slice(at + 1);
|
|
50
|
+
}
|
|
51
|
+
return fields;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const parseJson = ({ text, what }: { text: string; what: string }): unknown => {
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(text);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
59
|
+
throw new Error(`${what} is not valid JSON: ${detail}`);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const coerce = ({
|
|
64
|
+
field,
|
|
65
|
+
value,
|
|
66
|
+
}: {
|
|
67
|
+
field: ApiField;
|
|
68
|
+
value: string;
|
|
69
|
+
}): unknown => {
|
|
70
|
+
switch (field.type) {
|
|
71
|
+
case "boolean": {
|
|
72
|
+
if (value === "true") return true;
|
|
73
|
+
if (value === "false") return false;
|
|
74
|
+
throw new Error(`${field.name} takes true or false.`);
|
|
75
|
+
}
|
|
76
|
+
case "number": {
|
|
77
|
+
const parsed = Number(value);
|
|
78
|
+
if (value === "" || !Number.isFinite(parsed))
|
|
79
|
+
throw new Error(`${field.name} takes a number.`);
|
|
80
|
+
return parsed;
|
|
81
|
+
}
|
|
82
|
+
case "string":
|
|
83
|
+
return value;
|
|
84
|
+
case "json":
|
|
85
|
+
return parseJson({ text: value, what: field.name });
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* `--body` is the whole document; field flags layer on top of it, so a saved
|
|
91
|
+
* JSON body can still be overridden for one call.
|
|
92
|
+
*/
|
|
93
|
+
export const buildApiBody = async ({
|
|
94
|
+
route,
|
|
95
|
+
body,
|
|
96
|
+
fields = {},
|
|
97
|
+
readStdin,
|
|
98
|
+
}: {
|
|
99
|
+
route: ApiRoute;
|
|
100
|
+
body?: string;
|
|
101
|
+
fields?: Record<string, string>;
|
|
102
|
+
readStdin?: () => Promise<string>;
|
|
103
|
+
}): Promise<unknown> => {
|
|
104
|
+
const stated = Object.entries(fields);
|
|
105
|
+
let document: unknown = route.body === "array" ? undefined : {};
|
|
106
|
+
if (body !== undefined) {
|
|
107
|
+
const text = body === "-" ? await (readStdin ?? defaultReadStdin)() : body;
|
|
108
|
+
document = parseJson({ text, what: "--body" });
|
|
109
|
+
}
|
|
110
|
+
if (stated.length === 0) return document;
|
|
111
|
+
if (route.body === "array")
|
|
112
|
+
throw new Error(
|
|
113
|
+
`${route.path} takes an array body: pass it with --body, not key=value.`,
|
|
114
|
+
);
|
|
115
|
+
if (
|
|
116
|
+
typeof document !== "object" ||
|
|
117
|
+
document === null ||
|
|
118
|
+
Array.isArray(document)
|
|
119
|
+
)
|
|
120
|
+
throw new Error(
|
|
121
|
+
"--body must be a JSON object when key=value pairs are given.",
|
|
122
|
+
);
|
|
123
|
+
const byName = new Map(route.fields.map((field) => [field.name, field]));
|
|
124
|
+
const merged: Record<string, unknown> = { ...document };
|
|
125
|
+
for (const [name, value] of stated) {
|
|
126
|
+
const field = byName.get(name);
|
|
127
|
+
if (field === undefined)
|
|
128
|
+
throw new Error(
|
|
129
|
+
`${route.path} has no field ${name}. Fields: ${route.fields.map((known) => known.name).join(", ")}.`,
|
|
130
|
+
);
|
|
131
|
+
merged[name] = coerce({ field, value });
|
|
132
|
+
}
|
|
133
|
+
return merged;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const defaultReadStdin = async (): Promise<string> => {
|
|
137
|
+
const chunks: Buffer[] = [];
|
|
138
|
+
for await (const chunk of process.stdin) chunks.push(chunk as Buffer);
|
|
139
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/** `-H "x-api-version: 2.3.0"` → `["x-api-version", "2.3.0"]`; the name is lowercased like fetch does. */
|
|
143
|
+
export const parseHeaderArgs = ({
|
|
144
|
+
headers,
|
|
145
|
+
}: {
|
|
146
|
+
headers: string[];
|
|
147
|
+
}): Record<string, string> => {
|
|
148
|
+
const parsed: Record<string, string> = {};
|
|
149
|
+
for (const header of headers) {
|
|
150
|
+
const at = header.indexOf(":");
|
|
151
|
+
const name = at > 0 ? header.slice(0, at).trim().toLowerCase() : "";
|
|
152
|
+
if (name === "")
|
|
153
|
+
throw new Error(
|
|
154
|
+
`Expected -H "name: value", got ${JSON.stringify(header)}.`,
|
|
155
|
+
);
|
|
156
|
+
parsed[name] = header.slice(at + 1).trim();
|
|
157
|
+
}
|
|
158
|
+
return parsed;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export type ApiRequest = {
|
|
162
|
+
url: string;
|
|
163
|
+
headers: Record<string, string>;
|
|
164
|
+
body: string;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export const buildApiRequest = async ({
|
|
168
|
+
route,
|
|
169
|
+
baseUrl,
|
|
170
|
+
secretKey,
|
|
171
|
+
body,
|
|
172
|
+
fields,
|
|
173
|
+
headers = [],
|
|
174
|
+
readStdin,
|
|
175
|
+
}: Omit<ApiCallOptions, "fetch">): Promise<ApiRequest> => ({
|
|
176
|
+
url: `${baseUrl}${route.path}`,
|
|
177
|
+
headers: {
|
|
178
|
+
authorization: `Bearer ${secretKey}`,
|
|
179
|
+
"content-type": "application/json",
|
|
180
|
+
"x-api-version": API_VERSION,
|
|
181
|
+
...parseHeaderArgs({ headers }),
|
|
182
|
+
},
|
|
183
|
+
body: JSON.stringify(
|
|
184
|
+
(await buildApiBody({ route, body, fields, readStdin })) ?? {},
|
|
185
|
+
),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const shellQuote = (text: string): string => `'${text.replace(/'/g, "'\\''")}'`;
|
|
189
|
+
|
|
190
|
+
/** The same request as a curl line, for pasting into a doc or a support
|
|
191
|
+
* thread. The key is shown as the literal name of its env var, on purpose:
|
|
192
|
+
* nothing in the output is a secret, and the reader fills it in. */
|
|
193
|
+
export const renderCurl = ({
|
|
194
|
+
request,
|
|
195
|
+
secretKey,
|
|
196
|
+
secretKeyName,
|
|
197
|
+
}: {
|
|
198
|
+
request: ApiRequest;
|
|
199
|
+
secretKey: string;
|
|
200
|
+
/** The env var the key came from, printed in the key's place. */
|
|
201
|
+
secretKeyName: string;
|
|
202
|
+
}): string =>
|
|
203
|
+
[
|
|
204
|
+
`curl -X POST ${shellQuote(request.url)}`,
|
|
205
|
+
...Object.entries(request.headers).map(([name, value]) => {
|
|
206
|
+
// An authorization the caller overrode with -H is theirs to show.
|
|
207
|
+
const shown =
|
|
208
|
+
name === "authorization" && value === `Bearer ${secretKey}`
|
|
209
|
+
? `Bearer $${secretKeyName}`
|
|
210
|
+
: value;
|
|
211
|
+
return ` -H ${shellQuote(`${name}: ${shown}`)}`;
|
|
212
|
+
}),
|
|
213
|
+
` -d ${shellQuote(request.body)}`,
|
|
214
|
+
].join(" \\\n");
|
|
215
|
+
|
|
216
|
+
const tryParseJson = ({ text }: { text: string }): unknown => {
|
|
217
|
+
try {
|
|
218
|
+
return JSON.parse(text);
|
|
219
|
+
} catch {
|
|
220
|
+
return text;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/** A non-2xx reply, whole: the command prints it rather than a one-line summary. */
|
|
225
|
+
export class ApiResponseError extends AutumnApiError {
|
|
226
|
+
readonly statusText: string;
|
|
227
|
+
|
|
228
|
+
constructor({
|
|
229
|
+
status,
|
|
230
|
+
statusText,
|
|
231
|
+
body,
|
|
232
|
+
path,
|
|
233
|
+
}: {
|
|
234
|
+
status: number;
|
|
235
|
+
statusText: string;
|
|
236
|
+
body: unknown;
|
|
237
|
+
path: string;
|
|
238
|
+
}) {
|
|
239
|
+
super({ status, body, path });
|
|
240
|
+
this.name = "ApiResponseError";
|
|
241
|
+
this.statusText = statusText;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** `HTTP 404 Not Found`, and the body as the server sent it, pretty-printed when it is JSON. */
|
|
246
|
+
export const renderApiResponseError = ({
|
|
247
|
+
error,
|
|
248
|
+
}: {
|
|
249
|
+
error: ApiResponseError;
|
|
250
|
+
}): { statusLine: string; body: string } => ({
|
|
251
|
+
statusLine: `HTTP ${error.status} ${error.statusText}`,
|
|
252
|
+
body:
|
|
253
|
+
typeof error.body === "string"
|
|
254
|
+
? error.body
|
|
255
|
+
: JSON.stringify(error.body, null, 2),
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
/** One POST, the way the generated client does it, plus the version header the spec pins. */
|
|
259
|
+
export const callApi = async ({
|
|
260
|
+
fetch = autumnFetch,
|
|
261
|
+
...options
|
|
262
|
+
}: ApiCallOptions): Promise<unknown> => {
|
|
263
|
+
const request = await buildApiRequest(options);
|
|
264
|
+
const response = await fetch(request.url, {
|
|
265
|
+
method: "POST",
|
|
266
|
+
headers: request.headers,
|
|
267
|
+
body: request.body,
|
|
268
|
+
});
|
|
269
|
+
const text = await response.text();
|
|
270
|
+
// A proxy's HTML error page is not JSON; the status and route still matter.
|
|
271
|
+
const parsed: unknown = text ? tryParseJson({ text }) : null;
|
|
272
|
+
if (!response.ok)
|
|
273
|
+
throw new ApiResponseError({
|
|
274
|
+
status: response.status,
|
|
275
|
+
statusText: response.statusText,
|
|
276
|
+
body: parsed,
|
|
277
|
+
path: options.route.path,
|
|
278
|
+
});
|
|
279
|
+
return parsed;
|
|
280
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import {
|
|
3
|
+
requireSecretKey,
|
|
4
|
+
type Target,
|
|
5
|
+
targetBaseUrl,
|
|
6
|
+
} from "../../env/resolveTarget";
|
|
7
|
+
import { API_ROUTES, type ApiRoute } from "../../generated/apiRoutes";
|
|
8
|
+
import {
|
|
9
|
+
ApiResponseError,
|
|
10
|
+
buildApiRequest,
|
|
11
|
+
callApi,
|
|
12
|
+
parseFieldArgs,
|
|
13
|
+
renderApiResponseError,
|
|
14
|
+
renderCurl,
|
|
15
|
+
} from "./callApi";
|
|
16
|
+
|
|
17
|
+
const SUMMARY_MAX = 80;
|
|
18
|
+
|
|
19
|
+
/** The first sentence, clipped: the group listing is a table, the method's own --help carries the rest. */
|
|
20
|
+
export const summarize = ({ text }: { text: string }): string => {
|
|
21
|
+
const sentence = /^.*?[.!?](?=\s|$)/.exec(text)?.[0] ?? text;
|
|
22
|
+
return sentence.length <= SUMMARY_MAX
|
|
23
|
+
? sentence
|
|
24
|
+
: `${sentence.slice(0, SUMMARY_MAX - 1).trimEnd()}…`;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const fieldHelp = ({ route }: { route: ApiRoute }): string => {
|
|
28
|
+
if (route.body === "none") return "\nTakes no body.";
|
|
29
|
+
if (route.body === "array")
|
|
30
|
+
return "\nTakes a JSON array: pass it with --body '[...]' or --body - (stdin).";
|
|
31
|
+
const width = Math.max(0, ...route.fields.map((field) => field.name.length));
|
|
32
|
+
return [
|
|
33
|
+
"\nFields (key=value; json values are parsed):",
|
|
34
|
+
...route.fields.map((field) => {
|
|
35
|
+
const type = `${field.type}${field.required ? ", required" : ""}`;
|
|
36
|
+
return ` ${field.name.padEnd(width)} ${type.padEnd(18)}${field.description ?? ""}`.trimEnd();
|
|
37
|
+
}),
|
|
38
|
+
].join("\n");
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* One subcommand per public RPC route, straight off the spec: `atmn api
|
|
43
|
+
* balances check customer_id=cus_1 feature_id=messages`. Nothing here knows
|
|
44
|
+
* what any route does; the spec's own description is the help.
|
|
45
|
+
*/
|
|
46
|
+
export const registerApiCommands = ({
|
|
47
|
+
program,
|
|
48
|
+
targetOf,
|
|
49
|
+
}: {
|
|
50
|
+
program: Command;
|
|
51
|
+
targetOf: (command: Command) => Target;
|
|
52
|
+
}): void => {
|
|
53
|
+
const api = program
|
|
54
|
+
.command("api")
|
|
55
|
+
.description(
|
|
56
|
+
"call any public API route: atmn api <group> <method> [key=value...]",
|
|
57
|
+
);
|
|
58
|
+
const groups = new Map<string, Command>();
|
|
59
|
+
for (const route of API_ROUTES) {
|
|
60
|
+
let group = groups.get(route.group);
|
|
61
|
+
if (group === undefined) {
|
|
62
|
+
group = api.command(route.group).description(`${route.group}.* routes`);
|
|
63
|
+
groups.set(route.group, group);
|
|
64
|
+
}
|
|
65
|
+
group
|
|
66
|
+
.command(route.method)
|
|
67
|
+
.summary(summarize({ text: route.description ?? route.path }))
|
|
68
|
+
.description(route.description ?? route.path)
|
|
69
|
+
.argument("[fields...]", "body fields as key=value")
|
|
70
|
+
.option("--body <json>", "the whole JSON body; - reads stdin")
|
|
71
|
+
.option(
|
|
72
|
+
"-H, --header <header>",
|
|
73
|
+
'an extra request header, "name: value"; repeatable',
|
|
74
|
+
(value: string, previous: string[] = []) => [...previous, value],
|
|
75
|
+
)
|
|
76
|
+
.option(
|
|
77
|
+
"--curl",
|
|
78
|
+
"print the request as a curl command instead of sending it",
|
|
79
|
+
)
|
|
80
|
+
.addHelpText("after", fieldHelp({ route }))
|
|
81
|
+
.action(
|
|
82
|
+
async (
|
|
83
|
+
args: string[],
|
|
84
|
+
options: { body?: string; header?: string[]; curl?: boolean },
|
|
85
|
+
command: Command,
|
|
86
|
+
) => {
|
|
87
|
+
const target = targetOf(command);
|
|
88
|
+
const call = {
|
|
89
|
+
route,
|
|
90
|
+
baseUrl: targetBaseUrl({ target }),
|
|
91
|
+
secretKey: requireSecretKey({ target }),
|
|
92
|
+
...(options.body === undefined ? {} : { body: options.body }),
|
|
93
|
+
fields: parseFieldArgs({ args }),
|
|
94
|
+
headers: options.header ?? [],
|
|
95
|
+
};
|
|
96
|
+
if (options.curl === true) {
|
|
97
|
+
process.stdout.write(
|
|
98
|
+
`${renderCurl({
|
|
99
|
+
request: await buildApiRequest(call),
|
|
100
|
+
secretKey: call.secretKey,
|
|
101
|
+
secretKeyName: target.secretKeyName,
|
|
102
|
+
})}\n`,
|
|
103
|
+
);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const response = await callApi(call);
|
|
108
|
+
process.stdout.write(`${JSON.stringify(response, null, 2)}\n`);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
// The server's reply is the answer, so it is shown whole: the
|
|
111
|
+
// status line on stderr, the body on stdout like a success.
|
|
112
|
+
if (!(error instanceof ApiResponseError)) throw error;
|
|
113
|
+
const { statusLine, body } = renderApiResponseError({ error });
|
|
114
|
+
process.stderr.write(`${statusLine}\n`);
|
|
115
|
+
process.stdout.write(`${body}\n`);
|
|
116
|
+
process.exitCode = 1;
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AutumnApiError } from "../../generated/client";
|
|
2
|
+
import { autumnFetch } from "../../http/autumnFetch";
|
|
3
|
+
import type { OrgInfo } from "./types/orgInfo";
|
|
4
|
+
|
|
5
|
+
const ORG_INFO_PATH = "/v1/organization/me";
|
|
6
|
+
|
|
7
|
+
/** Not in the spec, so hand-written like the key-minting call rather than generated. */
|
|
8
|
+
export const fetchOrgInfo = async ({
|
|
9
|
+
baseUrl,
|
|
10
|
+
secretKey,
|
|
11
|
+
fetch = autumnFetch,
|
|
12
|
+
}: {
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
secretKey: string;
|
|
15
|
+
fetch?: typeof globalThis.fetch;
|
|
16
|
+
}): Promise<OrgInfo> => {
|
|
17
|
+
const response = await fetch(`${baseUrl}${ORG_INFO_PATH}`, {
|
|
18
|
+
method: "GET",
|
|
19
|
+
headers: { authorization: `Bearer ${secretKey}` },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const text = await response.text();
|
|
23
|
+
const body: unknown = text ? JSON.parse(text) : null;
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
throw new AutumnApiError({
|
|
26
|
+
status: response.status,
|
|
27
|
+
body,
|
|
28
|
+
path: ORG_INFO_PATH,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return body as OrgInfo;
|
|
32
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** What the server says about the key it was given: the org and the env it unlocks. */
|
|
2
|
+
export type OrgInfo = {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
slug: string;
|
|
6
|
+
env: string;
|
|
7
|
+
/** True when the key belongs to a sandbox sub-org rather than the main one. */
|
|
8
|
+
is_sandbox?: boolean;
|
|
9
|
+
/** The main organization's id, for a sandbox. */
|
|
10
|
+
created_by?: string | null;
|
|
11
|
+
/** `pending` while a keyless org has no owner; null for an ordinary org. */
|
|
12
|
+
claim_state?: "pending" | "claimed" | "expiring" | null;
|
|
13
|
+
/** When a pending claim window closes, ISO 8601. */
|
|
14
|
+
claim_expires_at?: string | null;
|
|
15
|
+
user?: { id: string; email: string; name: string };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type FetchOrgInfo = () => Promise<OrgInfo>;
|