@sanity/cli 8.21.0 → 8.22.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 +106 -47
- package/codemods/dashboard.js +111 -0
- package/dist/actions/build/buildApp.js +1 -1
- package/dist/actions/build/buildApp.js.map +1 -1
- package/dist/actions/build/buildStudio.js +1 -1
- package/dist/actions/build/buildStudio.js.map +1 -1
- package/dist/actions/codemods/dashboard.js +113 -0
- package/dist/actions/codemods/dashboard.js.map +1 -0
- package/dist/actions/codemods/index.js +2 -0
- package/dist/actions/codemods/index.js.map +1 -1
- package/dist/actions/codemods/types.js.map +1 -1
- package/dist/actions/deploy/deployApp.js +13 -6
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployChecks.js +12 -7
- package/dist/actions/deploy/deployChecks.js.map +1 -1
- package/dist/actions/deploy/deployRunner.js +14 -8
- package/dist/actions/deploy/deployRunner.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +39 -4
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/resolveDeployTarget.js +7 -6
- package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
- package/dist/actions/init/project/promptForAppTemplateSetup.js +22 -0
- package/dist/actions/init/project/promptForAppTemplateSetup.js.map +1 -1
- package/dist/actions/schema/listSchemas.js +2 -3
- package/dist/actions/schema/listSchemas.js.map +1 -1
- package/dist/actions/typegen/generateResourceTypes.js +11 -8
- package/dist/actions/typegen/generateResourceTypes.js.map +1 -1
- package/dist/actions/typegen/watchResourceTypes.js +208 -0
- package/dist/actions/typegen/watchResourceTypes.js.map +1 -0
- package/dist/actions/undeploy/runUndeploy.js +14 -8
- package/dist/actions/undeploy/runUndeploy.js.map +1 -1
- package/dist/commands/codemod.js +15 -4
- package/dist/commands/codemod.js.map +1 -1
- package/dist/commands/context/get.js +3 -8
- package/dist/commands/context/get.js.map +1 -1
- package/dist/commands/context/imports/download.js +3 -8
- package/dist/commands/context/imports/download.js.map +1 -1
- package/dist/commands/context/imports/get.js +3 -8
- package/dist/commands/context/imports/get.js.map +1 -1
- package/dist/commands/context/imports/list.js +3 -8
- package/dist/commands/context/imports/list.js.map +1 -1
- package/dist/commands/context/jobs/get.js +8 -8
- package/dist/commands/context/jobs/get.js.map +1 -1
- package/dist/commands/context/list.js +2 -6
- package/dist/commands/context/list.js.map +1 -1
- package/dist/commands/deploy.js +1 -0
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/doctor.js +2 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/openapi/list.js +2 -4
- package/dist/commands/openapi/list.js.map +1 -1
- package/dist/commands/projects/create.js +3 -6
- package/dist/commands/projects/create.js.map +1 -1
- package/dist/commands/schemas/list.js +1 -3
- package/dist/commands/schemas/list.js.map +1 -1
- package/dist/commands/tokens/create.js +2 -5
- package/dist/commands/tokens/create.js.map +1 -1
- package/dist/commands/tokens/list.js +2 -6
- package/dist/commands/tokens/list.js.map +1 -1
- package/dist/commands/tokens/rotate.js +2 -5
- package/dist/commands/tokens/rotate.js.map +1 -1
- package/dist/commands/typegen/generate.js +83 -45
- package/dist/commands/typegen/generate.js.map +1 -1
- package/dist/commands/undeploy.js +1 -0
- package/dist/commands/undeploy.js.map +1 -1
- package/dist/commands/whoami.js +5 -11
- package/dist/commands/whoami.js.map +1 -1
- package/dist/exports/invokeSanityCli/index.js +26 -6
- package/dist/exports/invokeSanityCli/index.js.map +1 -1
- package/dist/generated/apiRoutes.js +11 -26
- package/dist/generated/apiRoutes.js.map +1 -1
- package/dist/telemetry/init.telemetry.js.map +1 -1
- package/oclif.manifest.json +1323 -1294
- package/package.json +6 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/projects/create.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes} from '@sanity/cli-core'\nimport {subdebug} from '@sanity/cli-core/debug'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {confirm, spinner} from '@sanity/cli-core/ux'\nimport {type DatasetResponse} from '@sanity/client'\n\nimport {createDataset} from '../../actions/dataset/create.js'\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {getOrganization} from '../../actions/organizations/getOrganization.js'\nimport {getManageUrl} from '../../actions/projects/getManageUrl.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForDefaultConfig} from '../../prompts/promptForDefaultConfig.js'\nimport {promptForProjectName} from '../../prompts/promptForProjectName.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectFeatures} from '../../services/getProjectFeatures.js'\nimport {\n type OrganizationCreateResponse,\n type ProjectOrganization,\n} from '../../services/organizations.js'\nimport {createProject, type CreateProjectResult} from '../../services/projects.js'\nimport {getCliUser} from '../../services/user.js'\n\nconst debug = subdebug('projects:create')\n\nexport class CreateProjectCommand extends SanityCommand<typeof CreateProjectCommand> {\n static override args = {\n projectName: Args.string({\n description: 'Name of the project to create',\n required: false,\n }),\n }\n\n static override description = 'Create a new Sanity project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively create a project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My New Project\"',\n description: 'Create a project named \"My New Project\"',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My Project\" --organization=my-org',\n description: 'Create a project in a specific organization',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \"My Project\" --dataset=staging --dataset-visibility=private',\n description: 'Create a project with a private dataset named \"staging\"',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"CI Project\" --yes --json',\n description: 'Create a project non-interactively with JSON output',\n },\n ]\n\n static override flags = {\n dataset: Flags.string({\n description: 'Create a dataset. Prompts for visibility unless specified or --yes used',\n parse: async (input) => {\n const datasetNameError = validateDatasetName(input)\n if (datasetNameError) {\n throw new CLIError(datasetNameError, {exit: exitCodes.USAGE_ERROR})\n }\n\n return input\n },\n }),\n 'dataset-visibility': Flags.string({\n description: 'Dataset visibility: public or private',\n options: ['private', 'public'],\n }),\n json: Flags.boolean({\n default: false,\n description: 'Output in JSON format',\n }),\n organization: Flags.string({\n description: 'Organization to create the project in',\n helpValue: '<slug|id>',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description:\n 'Skip prompts and use defaults (project: \"My Sanity Project\", dataset: production, visibility: public)',\n }),\n }\n\n static override hiddenAliases: string[] = ['project:create']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CreateProjectCommand)\n const {projectName} = args\n const {dataset, 'dataset-visibility': datasetVisibility, organization, yes} = flags\n const user = await getCliUser()\n\n const finalProjectName =\n projectName ||\n (yes || this.isUnattended() ? 'My Sanity Project' : await promptForProjectName())\n\n debug('Creating project with options: %O', {\n dataset,\n datasetVisibility,\n organizationId: organization,\n projectName,\n unattended: yes,\n })\n\n let chosenOrganization: OrganizationCreateResponse | ProjectOrganization | undefined\n try {\n chosenOrganization = await getOrganization({\n isUnattended: this.isUnattended(),\n output: this.output,\n requestedId: organization,\n user,\n })\n } catch (error) {\n const errorText = organization\n ? `Failed to retrieve organization ${organization}`\n : 'Failed to retrieve an organization'\n return this.output.error(`${errorText}: ${error}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const spin = spinner('Creating project').start()\n let newProject\n try {\n newProject = await createProject({\n displayName: finalProjectName,\n metadata: {\n integration: 'cli',\n },\n organizationId: chosenOrganization?.id,\n })\n spin.succeed('Project created successfully')\n } catch (error) {\n spin.fail()\n debug(`Failed to create project: ${error}`)\n return this.output.error(`Failed to create project: ${error}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n let newDataset: DatasetResponse | undefined\n // If dataset name specified, or in attended mode (to prompt for dataset name), create a dataset.\n if (dataset || !this.isUnattended()) {\n newDataset = await this.handleDatasetCreation(\n newProject.projectId,\n dataset,\n datasetVisibility,\n )\n }\n\n this.printProjectCreationSuccess(chosenOrganization, newProject, newDataset)\n }\n\n private async handleDatasetCreation(\n projectId: string,\n datasetFromFlag?: string,\n datasetVisibility?: string,\n ): Promise<DatasetResponse | undefined> {\n try {\n let datasetName: string | undefined = datasetFromFlag\n const existingDatasets = await listDatasets(projectId)\n const existingDatasetNames = existingDatasets.map((ds) => ds.name)\n\n // Prompt for dataset in interactive mode if not provided\n if (!datasetName && !this.isUnattended()) {\n const wantsDataset = await confirm({\n default: true,\n message: 'Would you like to create a dataset?',\n })\n\n if (wantsDataset) {\n const defaultConfig = await promptForDefaultConfig()\n\n datasetName = defaultConfig\n ? 'production'\n : await promptForDatasetName({}, existingDatasetNames)\n }\n }\n\n // Create dataset if we have a name\n if (datasetName) {\n const projectFeatures = await getProjectFeatures(projectId)\n return await createDataset({\n datasetName,\n isUnattended: this.isUnattended(),\n output: this.output,\n projectFeatures,\n projectId,\n silent: this.flags.json,\n visibility: datasetVisibility,\n })\n }\n\n return\n } catch (error) {\n debug(`Error creating dataset: ${error}`)\n this.output.warn(`Project created but dataset creation failed: ${error}`)\n return\n }\n }\n\n private async printProjectCreationSuccess(\n organization: OrganizationCreateResponse | ProjectOrganization | undefined,\n project: CreateProjectResult,\n dataset: DatasetResponse | undefined,\n ) {\n if (this.flags.json) {\n this.output.log(JSON.stringify(project, null, 2))\n return\n }\n\n this.output.log(`Project created successfully!`)\n this.output.log(`ID: ${project.projectId}`)\n this.output.log(`Name: ${project.displayName}`)\n this.output.log(`Organization: ${organization?.name || 'Personal'}`)\n\n if (dataset) {\n this.output.log(`Dataset: ${dataset.datasetName} (${dataset.aclMode})`)\n }\n\n this.output.log(``)\n this.output.log(`Manage your project: ${getManageUrl(project.projectId)}`)\n }\n}\n"],"names":["Args","Flags","CLIError","exitCodes","subdebug","SanityCommand","confirm","spinner","createDataset","validateDatasetName","getOrganization","getManageUrl","promptForDatasetName","promptForDefaultConfig","promptForProjectName","listDatasets","getProjectFeatures","createProject","getCliUser","debug","CreateProjectCommand","args","projectName","string","description","required","examples","command","flags","dataset","parse","input","datasetNameError","exit","USAGE_ERROR","options","json","boolean","default","organization","helpValue","yes","char","hiddenAliases","run","datasetVisibility","user","finalProjectName","isUnattended","organizationId","unattended","chosenOrganization","output","requestedId","error","errorText","RUNTIME_ERROR","spin","start","newProject","displayName","metadata","integration","id","succeed","fail","newDataset","handleDatasetCreation","projectId","printProjectCreationSuccess","datasetFromFlag","datasetName","existingDatasets","existingDatasetNames","map","ds","name","wantsDataset","message","defaultConfig","projectFeatures","silent","visibility","warn","project","log","JSON","stringify","aclMode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/projects/create.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes} from '@sanity/cli-core'\nimport {subdebug} from '@sanity/cli-core/debug'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {confirm, spinner} from '@sanity/cli-core/ux'\nimport {type DatasetResponse} from '@sanity/client'\n\nimport {createDataset} from '../../actions/dataset/create.js'\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {getOrganization} from '../../actions/organizations/getOrganization.js'\nimport {getManageUrl} from '../../actions/projects/getManageUrl.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForDefaultConfig} from '../../prompts/promptForDefaultConfig.js'\nimport {promptForProjectName} from '../../prompts/promptForProjectName.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectFeatures} from '../../services/getProjectFeatures.js'\nimport {\n type OrganizationCreateResponse,\n type ProjectOrganization,\n} from '../../services/organizations.js'\nimport {createProject, type CreateProjectResult} from '../../services/projects.js'\nimport {getCliUser} from '../../services/user.js'\n\nconst debug = subdebug('projects:create')\n\nexport class CreateProjectCommand extends SanityCommand<typeof CreateProjectCommand> {\n static override args = {\n projectName: Args.string({\n description: 'Name of the project to create',\n required: false,\n }),\n }\n\n static override description = 'Create a new Sanity project'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively create a project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My New Project\"',\n description: 'Create a project named \"My New Project\"',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My Project\" --organization=my-org',\n description: 'Create a project in a specific organization',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \"My Project\" --dataset=staging --dataset-visibility=private',\n description: 'Create a project with a private dataset named \"staging\"',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"CI Project\" --yes --json',\n description: 'Create a project non-interactively with JSON output',\n },\n ]\n\n static override flags = {\n dataset: Flags.string({\n description: 'Create a dataset. Prompts for visibility unless specified or --yes used',\n parse: async (input) => {\n const datasetNameError = validateDatasetName(input)\n if (datasetNameError) {\n throw new CLIError(datasetNameError, {exit: exitCodes.USAGE_ERROR})\n }\n\n return input\n },\n }),\n 'dataset-visibility': Flags.string({\n description: 'Dataset visibility: public or private',\n options: ['private', 'public'],\n }),\n organization: Flags.string({\n description: 'Organization to create the project in',\n helpValue: '<slug|id>',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description:\n 'Skip prompts and use defaults (project: \"My Sanity Project\", dataset: production, visibility: public)',\n }),\n }\n\n static override hiddenAliases: string[] = ['project:create']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CreateProjectCommand)\n const {projectName} = args\n const {dataset, 'dataset-visibility': datasetVisibility, organization, yes} = flags\n const user = await getCliUser()\n\n const finalProjectName =\n projectName ||\n (yes || this.isUnattended() ? 'My Sanity Project' : await promptForProjectName())\n\n debug('Creating project with options: %O', {\n dataset,\n datasetVisibility,\n organizationId: organization,\n projectName,\n unattended: yes,\n })\n\n let chosenOrganization: OrganizationCreateResponse | ProjectOrganization | undefined\n try {\n chosenOrganization = await getOrganization({\n isUnattended: this.isUnattended(),\n output: this.output,\n requestedId: organization,\n user,\n })\n } catch (error) {\n const errorText = organization\n ? `Failed to retrieve organization ${organization}`\n : 'Failed to retrieve an organization'\n return this.output.error(`${errorText}: ${error}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const spin = spinner('Creating project').start()\n let newProject\n try {\n newProject = await createProject({\n displayName: finalProjectName,\n metadata: {\n integration: 'cli',\n },\n organizationId: chosenOrganization?.id,\n })\n spin.succeed('Project created successfully')\n } catch (error) {\n spin.fail()\n debug(`Failed to create project: ${error}`)\n return this.output.error(`Failed to create project: ${error}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n let newDataset: DatasetResponse | undefined\n // If dataset name specified, or in attended mode (to prompt for dataset name), create a dataset.\n if (dataset || !this.isUnattended()) {\n newDataset = await this.handleDatasetCreation(\n newProject.projectId,\n dataset,\n datasetVisibility,\n )\n }\n\n this.printProjectCreationSuccess(chosenOrganization, newProject, newDataset)\n }\n\n private async handleDatasetCreation(\n projectId: string,\n datasetFromFlag?: string,\n datasetVisibility?: string,\n ): Promise<DatasetResponse | undefined> {\n try {\n let datasetName: string | undefined = datasetFromFlag\n const existingDatasets = await listDatasets(projectId)\n const existingDatasetNames = existingDatasets.map((ds) => ds.name)\n\n // Prompt for dataset in interactive mode if not provided\n if (!datasetName && !this.isUnattended()) {\n const wantsDataset = await confirm({\n default: true,\n message: 'Would you like to create a dataset?',\n })\n\n if (wantsDataset) {\n const defaultConfig = await promptForDefaultConfig()\n\n datasetName = defaultConfig\n ? 'production'\n : await promptForDatasetName({}, existingDatasetNames)\n }\n }\n\n // Create dataset if we have a name\n if (datasetName) {\n const projectFeatures = await getProjectFeatures(projectId)\n return await createDataset({\n datasetName,\n isUnattended: this.isUnattended(),\n output: this.output,\n projectFeatures,\n projectId,\n silent: this.flags.json,\n visibility: datasetVisibility,\n })\n }\n\n return\n } catch (error) {\n debug(`Error creating dataset: ${error}`)\n this.output.logDiagnostic(`Warning: Project created but dataset creation failed: ${error}`)\n return\n }\n }\n\n private async printProjectCreationSuccess(\n organization: OrganizationCreateResponse | ProjectOrganization | undefined,\n project: CreateProjectResult,\n dataset: DatasetResponse | undefined,\n ) {\n if (this.flags.json) {\n this.logJson(project)\n return\n }\n\n this.output.log(`Project created successfully!`)\n this.output.log(`ID: ${project.projectId}`)\n this.output.log(`Name: ${project.displayName}`)\n this.output.log(`Organization: ${organization?.name || 'Personal'}`)\n\n if (dataset) {\n this.output.log(`Dataset: ${dataset.datasetName} (${dataset.aclMode})`)\n }\n\n this.output.log(``)\n this.output.log(`Manage your project: ${getManageUrl(project.projectId)}`)\n }\n}\n"],"names":["Args","Flags","CLIError","exitCodes","subdebug","SanityCommand","confirm","spinner","createDataset","validateDatasetName","getOrganization","getManageUrl","promptForDatasetName","promptForDefaultConfig","promptForProjectName","listDatasets","getProjectFeatures","createProject","getCliUser","debug","CreateProjectCommand","args","projectName","string","description","required","enableJsonFlag","examples","command","flags","dataset","parse","input","datasetNameError","exit","USAGE_ERROR","options","organization","helpValue","yes","boolean","char","default","hiddenAliases","run","datasetVisibility","user","finalProjectName","isUnattended","organizationId","unattended","chosenOrganization","output","requestedId","error","errorText","RUNTIME_ERROR","spin","start","newProject","displayName","metadata","integration","id","succeed","fail","newDataset","handleDatasetCreation","projectId","printProjectCreationSuccess","datasetFromFlag","datasetName","existingDatasets","existingDatasetNames","map","ds","name","wantsDataset","message","defaultConfig","projectFeatures","silent","json","visibility","logDiagnostic","project","logJson","log","aclMode"],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ Regenerates a manifest file by default. To reuse an existing manifest, use --no-
|
|
|
12
12
|
`.trim();
|
|
13
13
|
export class ListSchemaCommand extends SanityCommand {
|
|
14
14
|
static description = description;
|
|
15
|
+
static enableJsonFlag = true;
|
|
15
16
|
static examples = [
|
|
16
17
|
{
|
|
17
18
|
command: '<%= config.bin %> <%= command.id %>',
|
|
@@ -41,9 +42,6 @@ export class ListSchemaCommand extends SanityCommand {
|
|
|
41
42
|
description: 'Fetch a single schema by id',
|
|
42
43
|
helpValue: '<schema_id>'
|
|
43
44
|
}),
|
|
44
|
-
json: Flags.boolean({
|
|
45
|
-
description: 'Get schema as json'
|
|
46
|
-
}),
|
|
47
45
|
'manifest-dir': Flags.directory({
|
|
48
46
|
default: './dist/static',
|
|
49
47
|
description: 'Directory containing manifest file',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/schemas/list.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\n\nimport {listSchemas} from '../../actions/schema/listSchemas.js'\nimport {schemasListDebug} from '../../actions/schema/utils/debug.js'\nimport {parseWorkspaceSchemaId} from '../../actions/schema/utils/schemaStoreValidation.js'\n\nconst description = `\nList all schemas in the current dataset.\n\nNote: This command is experimental and subject to change.\n\nRegenerates a manifest file by default. To reuse an existing manifest, use --no-extract-manifest.\n`.trim()\n\nexport class ListSchemaCommand extends SanityCommand<typeof ListSchemaCommand> {\n static override description = description\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List all schemas found in any workspace dataset in a table',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --id _.schemas.workspaceName',\n description: 'Get a schema for a given id',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'Get stored schemas as pretty-printed json-array',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json --id _.schemas.workspaceName',\n description: 'Get singular stored schema as pretty-printed json-object',\n },\n ]\n\n static override flags = {\n 'extract-manifest': Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Regenerate manifest before listing (use --no-extract-manifest to skip)',\n hidden: true,\n }),\n id: Flags.string({\n description: 'Fetch a single schema by id',\n helpValue: '<schema_id>',\n }),\n
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/schemas/list.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\n\nimport {listSchemas} from '../../actions/schema/listSchemas.js'\nimport {schemasListDebug} from '../../actions/schema/utils/debug.js'\nimport {parseWorkspaceSchemaId} from '../../actions/schema/utils/schemaStoreValidation.js'\n\nconst description = `\nList all schemas in the current dataset.\n\nNote: This command is experimental and subject to change.\n\nRegenerates a manifest file by default. To reuse an existing manifest, use --no-extract-manifest.\n`.trim()\n\nexport class ListSchemaCommand extends SanityCommand<typeof ListSchemaCommand> {\n static override description = description\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List all schemas found in any workspace dataset in a table',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --id _.schemas.workspaceName',\n description: 'Get a schema for a given id',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'Get stored schemas as pretty-printed json-array',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json --id _.schemas.workspaceName',\n description: 'Get singular stored schema as pretty-printed json-object',\n },\n ]\n\n static override flags = {\n 'extract-manifest': Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Regenerate manifest before listing (use --no-extract-manifest to skip)',\n hidden: true,\n }),\n id: Flags.string({\n description: 'Fetch a single schema by id',\n helpValue: '<schema_id>',\n }),\n 'manifest-dir': Flags.directory({\n default: './dist/static',\n description: 'Directory containing manifest file',\n helpValue: '<directory>',\n hidden: true,\n }),\n }\n\n static override hiddenAliases: string[] = ['schema:list']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(ListSchemaCommand)\n const errors: string[] = []\n const id = parseWorkspaceSchemaId(errors, flags.id)?.schemaId\n if (errors.length > 0) {\n this.error(`Invalid arguments:\\n${errors.map((error) => ` - ${error}`).join('\\n')}`, {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n try {\n const projectRoot = await this.getProjectRoot()\n\n await listSchemas({\n configPath: projectRoot.path,\n id,\n json: !!flags.json,\n output: this.output,\n workDir: projectRoot.directory,\n })\n } catch (error) {\n schemasListDebug('Failed to list schemas', error)\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n this.error(`Failed to list schemas:\\n${errorMessage}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","listSchemas","schemasListDebug","parseWorkspaceSchemaId","description","trim","ListSchemaCommand","enableJsonFlag","examples","command","flags","boolean","allowNo","default","hidden","id","string","helpValue","directory","hiddenAliases","run","parse","errors","schemaId","length","error","map","join","exit","USAGE_ERROR","projectRoot","getProjectRoot","configPath","path","json","output","workDir","errorMessage","Error","message","String","RUNTIME_ERROR"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,SAAS,EAAEC,aAAa,QAAO,mBAAkB;AAEzD,SAAQC,WAAW,QAAO,sCAAqC;AAC/D,SAAQC,gBAAgB,QAAO,sCAAqC;AACpE,SAAQC,sBAAsB,QAAO,sDAAqD;AAE1F,MAAMC,cAAc,CAAC;;;;;;AAMrB,CAAC,CAACC,IAAI;AAEN,OAAO,MAAMC,0BAA0BN;IACrC,OAAgBI,cAAcA,YAAW;IAEzC,OAAgBG,iBAAiB,KAAI;IAErC,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTL,aAAa;QACf;QACA;YACEK,SAAS;YACTL,aAAa;QACf;QACA;YACEK,SAAS;YACTL,aAAa;QACf;QACA;YACEK,SAAS;YACTL,aAAa;QACf;KACD,CAAA;IAED,OAAgBM,QAAQ;QACtB,oBAAoBZ,MAAMa,OAAO,CAAC;YAChCC,SAAS;YACTC,SAAS;YACTT,aAAa;YACbU,QAAQ;QACV;QACAC,IAAIjB,MAAMkB,MAAM,CAAC;YACfZ,aAAa;YACba,WAAW;QACb;QACA,gBAAgBnB,MAAMoB,SAAS,CAAC;YAC9BL,SAAS;YACTT,aAAa;YACba,WAAW;YACXH,QAAQ;QACV;IACF,EAAC;IAED,OAAgBK,gBAA0B;QAAC;KAAc,CAAA;IAEzD,MAAaC,MAAqB;QAChC,MAAM,EAACV,KAAK,EAAC,GAAG,MAAM,IAAI,CAACW,KAAK,CAACf;QACjC,MAAMgB,SAAmB,EAAE;QAC3B,MAAMP,KAAKZ,uBAAuBmB,QAAQZ,MAAMK,EAAE,GAAGQ;QACrD,IAAID,OAAOE,MAAM,GAAG,GAAG;YACrB,IAAI,CAACC,KAAK,CAAC,CAAC,oBAAoB,EAAEH,OAAOI,GAAG,CAAC,CAACD,QAAU,CAAC,IAAI,EAAEA,OAAO,EAAEE,IAAI,CAAC,OAAO,EAAE;gBACpFC,MAAM7B,UAAU8B,WAAW;YAC7B;QACF;QAEA,IAAI;YACF,MAAMC,cAAc,MAAM,IAAI,CAACC,cAAc;YAE7C,MAAM9B,YAAY;gBAChB+B,YAAYF,YAAYG,IAAI;gBAC5BlB;gBACAmB,MAAM,CAAC,CAACxB,MAAMwB,IAAI;gBAClBC,QAAQ,IAAI,CAACA,MAAM;gBACnBC,SAASN,YAAYZ,SAAS;YAChC;QACF,EAAE,OAAOO,OAAO;YACdvB,iBAAiB,0BAA0BuB;YAE3C,MAAMY,eAAeZ,iBAAiBa,QAAQb,MAAMc,OAAO,GAAGC,OAAOf;YACrE,IAAI,CAACA,KAAK,CAAC,CAAC,yBAAyB,EAAEY,cAAc,EAAE;gBAACT,MAAM7B,UAAU0C,aAAa;YAAA;QACvF;IACF;AACF"}
|
|
@@ -29,6 +29,7 @@ export class CreateTokenCommand extends SanityCommand {
|
|
|
29
29
|
})
|
|
30
30
|
};
|
|
31
31
|
static description = 'Create a new API token with access to an organization and its projects';
|
|
32
|
+
static enableJsonFlag = true;
|
|
32
33
|
static examples = [
|
|
33
34
|
{
|
|
34
35
|
command: '<%= config.bin %> <%= command.id %> "My API Token"',
|
|
@@ -73,10 +74,6 @@ export class CreateTokenCommand extends SanityCommand {
|
|
|
73
74
|
helpValue: 'context=editor',
|
|
74
75
|
multiple: true
|
|
75
76
|
}),
|
|
76
|
-
json: Flags.boolean({
|
|
77
|
-
default: false,
|
|
78
|
-
description: 'Output as JSON'
|
|
79
|
-
}),
|
|
80
77
|
role: Flags.string({
|
|
81
78
|
description: 'Role to assign on the project (defaults to viewer in unattended mode)',
|
|
82
79
|
helpValue: 'viewer'
|
|
@@ -130,7 +127,7 @@ export class CreateTokenCommand extends SanityCommand {
|
|
|
130
127
|
});
|
|
131
128
|
}
|
|
132
129
|
if (flags.json) {
|
|
133
|
-
this.
|
|
130
|
+
this.logJson(token);
|
|
134
131
|
return;
|
|
135
132
|
}
|
|
136
133
|
this.log('API token created');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/tokens/create.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage, isProjectRootNotFoundError} from '@sanity/cli-core/errors'\nimport {input, select} from '@sanity/cli-core/ux'\nimport {ClientError} from '@sanity/client'\n\nimport {\n MissingOrganizationError,\n resolveOrganizationId,\n} from '../../actions/context/resolveOrganizationId.js'\nimport {expiryInDays, parseExpiryDate} from '../../actions/tokens/expiry.js'\nimport {\n DEFAULT_ROLE,\n findGrantConflict,\n formatGrant,\n getProjectTokenMembership,\n type Grant,\n parseGrant,\n projectGrantKey,\n toMemberships,\n} from '../../actions/tokens/grants.js'\nimport {type RobotWithToken} from '../../actions/tokens/types.js'\nimport {validateRole} from '../../actions/tokens/validateRole.js'\nimport {promptForGrants} from '../../prompts/promptForGrants.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listProjects} from '../../services/projects.js'\nimport {createOrganizationToken, createToken} from '../../services/tokens.js'\nimport {getOrganizationFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst tokensCreateDebug = subdebug('tokens:create')\n\ntype Access = {\n expiresAt: string | undefined\n grants: Grant[]\n\n organizationId?: string\n roleTitle?: string\n}\n\n// Mirrors the expiry presets offered by sanity.io/manage\nconst EXPIRY_PRESET_DAYS = [30, 60, 90]\n\nconst ORGANIZATION_FORBIDDEN_MESSAGE =\n 'You need permission to create organization tokens. Grant a single project to create a project token instead.'\n\nexport class CreateTokenCommand extends SanityCommand<typeof CreateTokenCommand> {\n static override args = {\n label: Args.string({\n description: 'Label for the new token',\n required: false,\n }),\n }\n\n static override description =\n 'Create a new API token with access to an organization and its projects'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> \"My API Token\"',\n description: 'Create a token, choosing its access interactively',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \"CI Token\" --grant context=developer --grant blueprints --grant project:abc123=editor',\n description: 'Create an organization token with Context, Blueprints and project access',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My Token\" --project-id abc123 --role=editor',\n description: 'Create a project token with editor role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"CI Token\" --role=editor --yes',\n description: 'Create a project token in unattended mode',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"Build Token\" --expires-at 2027-01-01',\n description: 'Create a token that expires on a given date',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"API Token\" --json',\n description: 'Output token information as JSON',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to create token in',\n semantics: 'override',\n }),\n ...getOrganizationFlag({\n description: 'Organization to create an organization token in',\n semantics: 'override',\n }),\n 'expires-at': Flags.string({\n description: 'Date or timestamp the token expires (ISO 8601; tokens never expire by default)',\n helpValue: '2027-01-01',\n }),\n grant: Flags.string({\n description:\n 'Access to grant, as <permission>[=<role>] or project:<projectId>=<role>[,<role>]. Permissions: context, blueprints, media-library, canvas, sdk-apps, access, deploy-studios. Repeatable',\n helpValue: 'context=editor',\n multiple: true,\n }),\n json: Flags.boolean({\n default: false,\n description: 'Output as JSON',\n }),\n role: Flags.string({\n description: 'Role to assign on the project (defaults to viewer in unattended mode)',\n helpValue: 'viewer',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: 'Skip prompts and use defaults (unattended mode)',\n }),\n }\n\n static override hiddenAliases: string[] = ['tokens:add', 'token:add', 'token:create']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CreateTokenCommand)\n const {label: givenLabel} = args\n\n const label = givenLabel === undefined ? await this.promptForLabel() : givenLabel.trim()\n if (!label) {\n this.error('Token label cannot be empty. Pass a non-empty value as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const flagExpiresAt = this.parseExpiryFlag(flags['expires-at'])\n const getExpiry = async () =>\n flags['expires-at'] === undefined ? this.promptForExpiry() : flagExpiresAt\n const flagGrants = (flags.grant ?? []).map((value) => this.parseGrantFlag(value))\n const grantConflict = findGrantConflict(flagGrants)\n if (grantConflict) {\n this.error(grantConflict, {exit: exitCodes.USAGE_ERROR})\n }\n\n const {expiresAt, grants, organizationId, roleTitle}: Access =\n this.isUnattended() || flagGrants.length > 0 || flags.role !== undefined\n ? {...(await this.getFlagGrants(flagGrants)), expiresAt: await getExpiry()}\n : await this.promptForAccess(getExpiry)\n\n let token: RobotWithToken\n try {\n token = await this.createTokenForGrants({\n expiresAt,\n grants,\n label,\n organizationId,\n })\n } catch (error) {\n if (error instanceof CLIError) throw error\n tokensCreateDebug('Error creating token', error)\n this.error(`Token creation failed:\\n${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (flags.json) {\n this.log(JSON.stringify(token, null, 2))\n return\n }\n\n this.log('API token created')\n this.log(`Label: ${token.label}`)\n this.log(`ID: ${token.id}`)\n if (roleTitle) {\n this.log(`Role: ${roleTitle}`)\n }\n if (token.expiresAt) {\n this.log(`Expires: ${token.expiresAt}`)\n }\n this.log(`Token: ${token.token}`)\n this.log('')\n this.log(\"Copy the token now. It won't be shown again.\")\n\n if (!this.isUnattended()) {\n this.log('')\n this.log('To create the same token without prompts:')\n this.log(` ${formatCreateCommand(this.config.bin, {expiresAt, grants, label, token})}`)\n }\n }\n\n private async createTokenForGrants(options: {\n expiresAt: string | undefined\n grants: Grant[]\n label: string\n organizationId: string | undefined\n }): Promise<RobotWithToken> {\n const {expiresAt, grants, label} = options\n const projectMembership = getProjectTokenMembership(grants)\n if (projectMembership) {\n const {resourceId: projectId, roleNames} = projectMembership\n return createToken({expiresAt, label, projectId, roleNames})\n }\n\n const organizationId = options.organizationId ?? (await this.resolveOrganization())\n try {\n return await createOrganizationToken({\n expiresAt,\n label,\n memberships: toMemberships(grants, organizationId),\n organizationId,\n })\n } catch (error) {\n if (error instanceof ClientError && error.statusCode === 403) {\n throw new Error(ORGANIZATION_FORBIDDEN_MESSAGE, {cause: error})\n }\n throw error\n }\n }\n\n private async getFlagGrants(grants: Grant[]): Promise<{grants: Grant[]; roleTitle?: string}> {\n const {'project-id': projectIdFlag, role} = this.flags\n if (grants.length > 0 && !projectIdFlag && !role) {\n return {grants}\n }\n\n const projectId = await this.resolveProjectId()\n const projectKey = projectGrantKey(projectId)\n if (grants.some((grant) => grant.key === projectKey)) {\n if (role) {\n this.error(\n `Set the role for project ${projectId} with either --role or --grant ${projectKey}=<role>, not both.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n return {grants}\n }\n\n const projectRole = role ? await validateRole(role, projectId, this.output) : DEFAULT_ROLE\n\n return {\n grants: [...grants, {key: projectKey, roles: [projectRole.name]}],\n roleTitle: grants.length === 0 ? projectRole.title : undefined,\n }\n }\n\n private parseExpiryFlag(value: string | undefined): string | undefined {\n if (value === undefined) {\n return undefined\n }\n\n const parsed = parseExpiryDate(value)\n if ('error' in parsed) {\n this.error(parsed.error, {exit: exitCodes.USAGE_ERROR})\n }\n\n return parsed.expiresAt\n }\n\n private parseGrantFlag(value: string): Grant {\n const parsed = parseGrant(value.trim())\n if ('error' in parsed) {\n this.error(parsed.error, {exit: exitCodes.USAGE_ERROR})\n }\n\n return parsed.grant\n }\n\n private async promptForAccess(getExpiry: () => Promise<string | undefined>): Promise<Access> {\n const requestedProjectId = this.flags['project-id']\n const configProjectId = (await this.tryGetCliConfig()).api?.projectId\n const projects = await listProjects({onlyExplicitMembership: false})\n const organizationOf = (projectId: string | undefined) =>\n projects.find((project) => project.id === projectId)?.organizationId ?? undefined\n const organizationId = await this.resolveOrganization({\n fallback: organizationOf(configProjectId),\n preferred: organizationOf(requestedProjectId),\n })\n const organizationProjects = projects.filter(\n (project) => project.organizationId === organizationId,\n )\n if (requestedProjectId && !organizationProjects.some(({id}) => id === requestedProjectId)) {\n this.error(\n `Project ${requestedProjectId} isn't in organization ${organizationId}, or you don't have access to it.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const expiresAt = await getExpiry()\n const grants = await promptForGrants({\n preselectedProjectId: requestedProjectId,\n projects: organizationProjects,\n })\n return {expiresAt, grants, organizationId}\n }\n\n private async promptForExpiry(): Promise<string | undefined> {\n if (this.isUnattended()) {\n return undefined // Tokens never expire by default\n }\n\n const choice = await select({\n choices: [\n {name: 'Never', value: 'never'},\n ...EXPIRY_PRESET_DAYS.map((days) => ({\n name: `${days} days (${expiryInDays(days).slice(0, 10)})`,\n value: String(days),\n })),\n {name: 'Custom date', value: 'custom'},\n ],\n default: 'never',\n message: 'Token expiry:',\n })\n\n if (choice === 'never') {\n return undefined\n }\n\n if (choice === 'custom') {\n const value = await input({\n message: 'Expiry date (ISO 8601, e.g. 2027-01-01):',\n validate: (input) => {\n const parsed = parseExpiryDate(input.trim())\n return 'error' in parsed ? parsed.error : true\n },\n })\n const parsed = parseExpiryDate(value.trim())\n return 'error' in parsed ? undefined : parsed.expiresAt\n }\n\n return expiryInDays(Number(choice))\n }\n\n private async promptForLabel(): Promise<string> {\n if (this.isUnattended()) {\n this.error('Token label is required. Pass it as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const label = await input({\n message: 'Token label:',\n validate: (value) => {\n if (!value || !value.trim()) {\n return 'Label cannot be empty'\n }\n return true\n },\n })\n\n return label.trim()\n }\n\n private async resolveOrganization(\n projectOrganizations: {fallback?: string; preferred?: string} = {},\n ): Promise<string> {\n const {fallback, preferred} = projectOrganizations\n try {\n return await resolveOrganizationId({\n configuredOrganizationId:\n preferred ?? (await this.tryGetCliConfig()).app?.organizationId ?? fallback,\n flagOrganizationId: this.flags.organization,\n unattended: this.isUnattended(),\n })\n } catch (error) {\n tokensCreateDebug('Error resolving organization', error)\n this.error(getErrorMessage(error), {\n exit:\n error instanceof MissingOrganizationError\n ? exitCodes.USAGE_ERROR\n : exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async resolveProjectId(): Promise<string> {\n if (!this.isUnattended()) {\n return this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.roles'},\n {grant: 'create', permission: 'sanity.project.tokens'},\n ],\n }),\n })\n }\n\n try {\n return await this.getProjectId()\n } catch (error) {\n if (!isProjectRootNotFoundError(error)) throw error\n this.error(\n 'Token access is required. Pass at least one `--grant`, or `--project-id` to create a project token.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n }\n}\n\nfunction quoteShellArg(value: string): string {\n return /^[\\w./:@=,-]+$/.test(value) ? value : `'${value.replaceAll(\"'\", String.raw`'\\''`)}'`\n}\n\nfunction formatCreateCommand(\n bin: string,\n {\n expiresAt,\n grants,\n label,\n token,\n }: {\n expiresAt: string | undefined\n grants: Grant[]\n label: string\n token: RobotWithToken\n },\n): string {\n const organizationId =\n token.managedBy?.resourceType === 'organization' ? token.managedBy.resourceId : undefined\n return [\n `${bin} tokens create`,\n quoteShellArg(label),\n ...(organizationId ? [`--organization ${organizationId}`] : []),\n ...(expiresAt ? [`--expires-at ${expiresAt}`] : []),\n ...grants.map((grant) => `--grant ${formatGrant(grant)}`),\n '--yes',\n ].join(' ')\n}\n"],"names":["Args","Flags","CLIError","exitCodes","SanityCommand","subdebug","getErrorMessage","isProjectRootNotFoundError","input","select","ClientError","MissingOrganizationError","resolveOrganizationId","expiryInDays","parseExpiryDate","DEFAULT_ROLE","findGrantConflict","formatGrant","getProjectTokenMembership","parseGrant","projectGrantKey","toMemberships","validateRole","promptForGrants","promptForProject","listProjects","createOrganizationToken","createToken","getOrganizationFlag","getProjectIdFlag","tokensCreateDebug","EXPIRY_PRESET_DAYS","ORGANIZATION_FORBIDDEN_MESSAGE","CreateTokenCommand","args","label","string","description","required","examples","command","flags","semantics","helpValue","grant","multiple","json","boolean","default","role","yes","char","hiddenAliases","run","parse","givenLabel","undefined","promptForLabel","trim","error","exit","USAGE_ERROR","flagExpiresAt","parseExpiryFlag","getExpiry","promptForExpiry","flagGrants","map","value","parseGrantFlag","grantConflict","expiresAt","grants","organizationId","roleTitle","isUnattended","length","getFlagGrants","promptForAccess","token","createTokenForGrants","RUNTIME_ERROR","log","JSON","stringify","id","formatCreateCommand","config","bin","options","projectMembership","resourceId","projectId","roleNames","resolveOrganization","memberships","statusCode","Error","cause","projectIdFlag","resolveProjectId","projectKey","some","key","projectRole","output","roles","name","title","parsed","requestedProjectId","configProjectId","tryGetCliConfig","api","projects","onlyExplicitMembership","organizationOf","find","project","fallback","preferred","organizationProjects","filter","preselectedProjectId","choice","choices","days","slice","String","message","validate","Number","projectOrganizations","configuredOrganizationId","app","flagOrganizationId","organization","unattended","getProjectId","requiredPermissions","permission","quoteShellArg","test","replaceAll","raw","managedBy","resourceType","join"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/tokens/create.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage, isProjectRootNotFoundError} from '@sanity/cli-core/errors'\nimport {input, select} from '@sanity/cli-core/ux'\nimport {ClientError} from '@sanity/client'\n\nimport {\n MissingOrganizationError,\n resolveOrganizationId,\n} from '../../actions/context/resolveOrganizationId.js'\nimport {expiryInDays, parseExpiryDate} from '../../actions/tokens/expiry.js'\nimport {\n DEFAULT_ROLE,\n findGrantConflict,\n formatGrant,\n getProjectTokenMembership,\n type Grant,\n parseGrant,\n projectGrantKey,\n toMemberships,\n} from '../../actions/tokens/grants.js'\nimport {type RobotWithToken} from '../../actions/tokens/types.js'\nimport {validateRole} from '../../actions/tokens/validateRole.js'\nimport {promptForGrants} from '../../prompts/promptForGrants.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listProjects} from '../../services/projects.js'\nimport {createOrganizationToken, createToken} from '../../services/tokens.js'\nimport {getOrganizationFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst tokensCreateDebug = subdebug('tokens:create')\n\ntype Access = {\n expiresAt: string | undefined\n grants: Grant[]\n\n organizationId?: string\n roleTitle?: string\n}\n\n// Mirrors the expiry presets offered by sanity.io/manage\nconst EXPIRY_PRESET_DAYS = [30, 60, 90]\n\nconst ORGANIZATION_FORBIDDEN_MESSAGE =\n 'You need permission to create organization tokens. Grant a single project to create a project token instead.'\n\nexport class CreateTokenCommand extends SanityCommand<typeof CreateTokenCommand> {\n static override args = {\n label: Args.string({\n description: 'Label for the new token',\n required: false,\n }),\n }\n\n static override description =\n 'Create a new API token with access to an organization and its projects'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> \"My API Token\"',\n description: 'Create a token, choosing its access interactively',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> \"CI Token\" --grant context=developer --grant blueprints --grant project:abc123=editor',\n description: 'Create an organization token with Context, Blueprints and project access',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My Token\" --project-id abc123 --role=editor',\n description: 'Create a project token with editor role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"CI Token\" --role=editor --yes',\n description: 'Create a project token in unattended mode',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"Build Token\" --expires-at 2027-01-01',\n description: 'Create a token that expires on a given date',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"API Token\" --json',\n description: 'Output token information as JSON',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to create token in',\n semantics: 'override',\n }),\n ...getOrganizationFlag({\n description: 'Organization to create an organization token in',\n semantics: 'override',\n }),\n 'expires-at': Flags.string({\n description: 'Date or timestamp the token expires (ISO 8601; tokens never expire by default)',\n helpValue: '2027-01-01',\n }),\n grant: Flags.string({\n description:\n 'Access to grant, as <permission>[=<role>] or project:<projectId>=<role>[,<role>]. Permissions: context, blueprints, media-library, canvas, sdk-apps, access, deploy-studios. Repeatable',\n helpValue: 'context=editor',\n multiple: true,\n }),\n role: Flags.string({\n description: 'Role to assign on the project (defaults to viewer in unattended mode)',\n helpValue: 'viewer',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: 'Skip prompts and use defaults (unattended mode)',\n }),\n }\n\n static override hiddenAliases: string[] = ['tokens:add', 'token:add', 'token:create']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CreateTokenCommand)\n const {label: givenLabel} = args\n\n const label = givenLabel === undefined ? await this.promptForLabel() : givenLabel.trim()\n if (!label) {\n this.error('Token label cannot be empty. Pass a non-empty value as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const flagExpiresAt = this.parseExpiryFlag(flags['expires-at'])\n const getExpiry = async () =>\n flags['expires-at'] === undefined ? this.promptForExpiry() : flagExpiresAt\n const flagGrants = (flags.grant ?? []).map((value) => this.parseGrantFlag(value))\n const grantConflict = findGrantConflict(flagGrants)\n if (grantConflict) {\n this.error(grantConflict, {exit: exitCodes.USAGE_ERROR})\n }\n\n const {expiresAt, grants, organizationId, roleTitle}: Access =\n this.isUnattended() || flagGrants.length > 0 || flags.role !== undefined\n ? {...(await this.getFlagGrants(flagGrants)), expiresAt: await getExpiry()}\n : await this.promptForAccess(getExpiry)\n\n let token: RobotWithToken\n try {\n token = await this.createTokenForGrants({\n expiresAt,\n grants,\n label,\n organizationId,\n })\n } catch (error) {\n if (error instanceof CLIError) throw error\n tokensCreateDebug('Error creating token', error)\n this.error(`Token creation failed:\\n${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (flags.json) {\n this.logJson(token)\n return\n }\n\n this.log('API token created')\n this.log(`Label: ${token.label}`)\n this.log(`ID: ${token.id}`)\n if (roleTitle) {\n this.log(`Role: ${roleTitle}`)\n }\n if (token.expiresAt) {\n this.log(`Expires: ${token.expiresAt}`)\n }\n this.log(`Token: ${token.token}`)\n this.log('')\n this.log(\"Copy the token now. It won't be shown again.\")\n\n if (!this.isUnattended()) {\n this.log('')\n this.log('To create the same token without prompts:')\n this.log(` ${formatCreateCommand(this.config.bin, {expiresAt, grants, label, token})}`)\n }\n }\n\n private async createTokenForGrants(options: {\n expiresAt: string | undefined\n grants: Grant[]\n label: string\n organizationId: string | undefined\n }): Promise<RobotWithToken> {\n const {expiresAt, grants, label} = options\n const projectMembership = getProjectTokenMembership(grants)\n if (projectMembership) {\n const {resourceId: projectId, roleNames} = projectMembership\n return createToken({expiresAt, label, projectId, roleNames})\n }\n\n const organizationId = options.organizationId ?? (await this.resolveOrganization())\n try {\n return await createOrganizationToken({\n expiresAt,\n label,\n memberships: toMemberships(grants, organizationId),\n organizationId,\n })\n } catch (error) {\n if (error instanceof ClientError && error.statusCode === 403) {\n throw new Error(ORGANIZATION_FORBIDDEN_MESSAGE, {cause: error})\n }\n throw error\n }\n }\n\n private async getFlagGrants(grants: Grant[]): Promise<{grants: Grant[]; roleTitle?: string}> {\n const {'project-id': projectIdFlag, role} = this.flags\n if (grants.length > 0 && !projectIdFlag && !role) {\n return {grants}\n }\n\n const projectId = await this.resolveProjectId()\n const projectKey = projectGrantKey(projectId)\n if (grants.some((grant) => grant.key === projectKey)) {\n if (role) {\n this.error(\n `Set the role for project ${projectId} with either --role or --grant ${projectKey}=<role>, not both.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n return {grants}\n }\n\n const projectRole = role ? await validateRole(role, projectId, this.output) : DEFAULT_ROLE\n\n return {\n grants: [...grants, {key: projectKey, roles: [projectRole.name]}],\n roleTitle: grants.length === 0 ? projectRole.title : undefined,\n }\n }\n\n private parseExpiryFlag(value: string | undefined): string | undefined {\n if (value === undefined) {\n return undefined\n }\n\n const parsed = parseExpiryDate(value)\n if ('error' in parsed) {\n this.error(parsed.error, {exit: exitCodes.USAGE_ERROR})\n }\n\n return parsed.expiresAt\n }\n\n private parseGrantFlag(value: string): Grant {\n const parsed = parseGrant(value.trim())\n if ('error' in parsed) {\n this.error(parsed.error, {exit: exitCodes.USAGE_ERROR})\n }\n\n return parsed.grant\n }\n\n private async promptForAccess(getExpiry: () => Promise<string | undefined>): Promise<Access> {\n const requestedProjectId = this.flags['project-id']\n const configProjectId = (await this.tryGetCliConfig()).api?.projectId\n const projects = await listProjects({onlyExplicitMembership: false})\n const organizationOf = (projectId: string | undefined) =>\n projects.find((project) => project.id === projectId)?.organizationId ?? undefined\n const organizationId = await this.resolveOrganization({\n fallback: organizationOf(configProjectId),\n preferred: organizationOf(requestedProjectId),\n })\n const organizationProjects = projects.filter(\n (project) => project.organizationId === organizationId,\n )\n if (requestedProjectId && !organizationProjects.some(({id}) => id === requestedProjectId)) {\n this.error(\n `Project ${requestedProjectId} isn't in organization ${organizationId}, or you don't have access to it.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const expiresAt = await getExpiry()\n const grants = await promptForGrants({\n preselectedProjectId: requestedProjectId,\n projects: organizationProjects,\n })\n return {expiresAt, grants, organizationId}\n }\n\n private async promptForExpiry(): Promise<string | undefined> {\n if (this.isUnattended()) {\n return undefined // Tokens never expire by default\n }\n\n const choice = await select({\n choices: [\n {name: 'Never', value: 'never'},\n ...EXPIRY_PRESET_DAYS.map((days) => ({\n name: `${days} days (${expiryInDays(days).slice(0, 10)})`,\n value: String(days),\n })),\n {name: 'Custom date', value: 'custom'},\n ],\n default: 'never',\n message: 'Token expiry:',\n })\n\n if (choice === 'never') {\n return undefined\n }\n\n if (choice === 'custom') {\n const value = await input({\n message: 'Expiry date (ISO 8601, e.g. 2027-01-01):',\n validate: (input) => {\n const parsed = parseExpiryDate(input.trim())\n return 'error' in parsed ? parsed.error : true\n },\n })\n const parsed = parseExpiryDate(value.trim())\n return 'error' in parsed ? undefined : parsed.expiresAt\n }\n\n return expiryInDays(Number(choice))\n }\n\n private async promptForLabel(): Promise<string> {\n if (this.isUnattended()) {\n this.error('Token label is required. Pass it as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const label = await input({\n message: 'Token label:',\n validate: (value) => {\n if (!value || !value.trim()) {\n return 'Label cannot be empty'\n }\n return true\n },\n })\n\n return label.trim()\n }\n\n private async resolveOrganization(\n projectOrganizations: {fallback?: string; preferred?: string} = {},\n ): Promise<string> {\n const {fallback, preferred} = projectOrganizations\n try {\n return await resolveOrganizationId({\n configuredOrganizationId:\n preferred ?? (await this.tryGetCliConfig()).app?.organizationId ?? fallback,\n flagOrganizationId: this.flags.organization,\n unattended: this.isUnattended(),\n })\n } catch (error) {\n tokensCreateDebug('Error resolving organization', error)\n this.error(getErrorMessage(error), {\n exit:\n error instanceof MissingOrganizationError\n ? exitCodes.USAGE_ERROR\n : exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async resolveProjectId(): Promise<string> {\n if (!this.isUnattended()) {\n return this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.roles'},\n {grant: 'create', permission: 'sanity.project.tokens'},\n ],\n }),\n })\n }\n\n try {\n return await this.getProjectId()\n } catch (error) {\n if (!isProjectRootNotFoundError(error)) throw error\n this.error(\n 'Token access is required. Pass at least one `--grant`, or `--project-id` to create a project token.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n }\n}\n\nfunction quoteShellArg(value: string): string {\n return /^[\\w./:@=,-]+$/.test(value) ? value : `'${value.replaceAll(\"'\", String.raw`'\\''`)}'`\n}\n\nfunction formatCreateCommand(\n bin: string,\n {\n expiresAt,\n grants,\n label,\n token,\n }: {\n expiresAt: string | undefined\n grants: Grant[]\n label: string\n token: RobotWithToken\n },\n): string {\n const organizationId =\n token.managedBy?.resourceType === 'organization' ? token.managedBy.resourceId : undefined\n return [\n `${bin} tokens create`,\n quoteShellArg(label),\n ...(organizationId ? [`--organization ${organizationId}`] : []),\n ...(expiresAt ? [`--expires-at ${expiresAt}`] : []),\n ...grants.map((grant) => `--grant ${formatGrant(grant)}`),\n '--yes',\n ].join(' ')\n}\n"],"names":["Args","Flags","CLIError","exitCodes","SanityCommand","subdebug","getErrorMessage","isProjectRootNotFoundError","input","select","ClientError","MissingOrganizationError","resolveOrganizationId","expiryInDays","parseExpiryDate","DEFAULT_ROLE","findGrantConflict","formatGrant","getProjectTokenMembership","parseGrant","projectGrantKey","toMemberships","validateRole","promptForGrants","promptForProject","listProjects","createOrganizationToken","createToken","getOrganizationFlag","getProjectIdFlag","tokensCreateDebug","EXPIRY_PRESET_DAYS","ORGANIZATION_FORBIDDEN_MESSAGE","CreateTokenCommand","args","label","string","description","required","enableJsonFlag","examples","command","flags","semantics","helpValue","grant","multiple","role","yes","boolean","char","default","hiddenAliases","run","parse","givenLabel","undefined","promptForLabel","trim","error","exit","USAGE_ERROR","flagExpiresAt","parseExpiryFlag","getExpiry","promptForExpiry","flagGrants","map","value","parseGrantFlag","grantConflict","expiresAt","grants","organizationId","roleTitle","isUnattended","length","getFlagGrants","promptForAccess","token","createTokenForGrants","RUNTIME_ERROR","json","logJson","log","id","formatCreateCommand","config","bin","options","projectMembership","resourceId","projectId","roleNames","resolveOrganization","memberships","statusCode","Error","cause","projectIdFlag","resolveProjectId","projectKey","some","key","projectRole","output","roles","name","title","parsed","requestedProjectId","configProjectId","tryGetCliConfig","api","projects","onlyExplicitMembership","organizationOf","find","project","fallback","preferred","organizationProjects","filter","preselectedProjectId","choice","choices","days","slice","String","message","validate","Number","projectOrganizations","configuredOrganizationId","app","flagOrganizationId","organization","unattended","getProjectId","requiredPermissions","permission","quoteShellArg","test","replaceAll","raw","managedBy","resourceType","join"],"mappings":"AAAA,SAAQA,IAAI,EAAEC,KAAK,QAAO,cAAa;AACvC,SAAQC,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,EAAEC,0BAA0B,QAAO,0BAAyB;AACnF,SAAQC,KAAK,EAAEC,MAAM,QAAO,sBAAqB;AACjD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SACEC,wBAAwB,EACxBC,qBAAqB,QAChB,iDAAgD;AACvD,SAAQC,YAAY,EAAEC,eAAe,QAAO,iCAAgC;AAC5E,SACEC,YAAY,EACZC,iBAAiB,EACjBC,WAAW,EACXC,yBAAyB,EAEzBC,UAAU,EACVC,eAAe,EACfC,aAAa,QACR,iCAAgC;AAEvC,SAAQC,YAAY,QAAO,uCAAsC;AACjE,SAAQC,eAAe,QAAO,mCAAkC;AAChE,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,YAAY,QAAO,6BAA4B;AACvD,SAAQC,uBAAuB,EAAEC,WAAW,QAAO,2BAA0B;AAC7E,SAAQC,mBAAmB,EAAEC,gBAAgB,QAAO,4BAA2B;AAE/E,MAAMC,oBAAoBzB,SAAS;AAUnC,yDAAyD;AACzD,MAAM0B,qBAAqB;IAAC;IAAI;IAAI;CAAG;AAEvC,MAAMC,iCACJ;AAEF,OAAO,MAAMC,2BAA2B7B;IACtC,OAAgB8B,OAAO;QACrBC,OAAOnC,KAAKoC,MAAM,CAAC;YACjBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cACd,yEAAwE;IAE1E,OAAgBE,iBAAiB,KAAI;IAErC,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SACE;YACFJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtB,GAAGb,iBAAiB;YAClBQ,aAAa;YACbM,WAAW;QACb,EAAE;QACF,GAAGf,oBAAoB;YACrBS,aAAa;YACbM,WAAW;QACb,EAAE;QACF,cAAc1C,MAAMmC,MAAM,CAAC;YACzBC,aAAa;YACbO,WAAW;QACb;QACAC,OAAO5C,MAAMmC,MAAM,CAAC;YAClBC,aACE;YACFO,WAAW;YACXE,UAAU;QACZ;QACAC,MAAM9C,MAAMmC,MAAM,CAAC;YACjBC,aAAa;YACbO,WAAW;QACb;QACAI,KAAK/C,MAAMgD,OAAO,CAAC;YACjBC,MAAM;YACNC,SAAS;YACTd,aAAa;QACf;IACF,EAAC;IAED,OAAgBe,gBAA0B;QAAC;QAAc;QAAa;KAAe,CAAA;IAErF,MAAaC,MAAqB;QAChC,MAAM,EAACnB,IAAI,EAAEQ,KAAK,EAAC,GAAG,MAAM,IAAI,CAACY,KAAK,CAACrB;QACvC,MAAM,EAACE,OAAOoB,UAAU,EAAC,GAAGrB;QAE5B,MAAMC,QAAQoB,eAAeC,YAAY,MAAM,IAAI,CAACC,cAAc,KAAKF,WAAWG,IAAI;QACtF,IAAI,CAACvB,OAAO;YACV,IAAI,CAACwB,KAAK,CAAC,kFAAkF;gBAC3FC,MAAMzD,UAAU0D,WAAW;YAC7B;QACF;QAEA,MAAMC,gBAAgB,IAAI,CAACC,eAAe,CAACrB,KAAK,CAAC,aAAa;QAC9D,MAAMsB,YAAY,UAChBtB,KAAK,CAAC,aAAa,KAAKc,YAAY,IAAI,CAACS,eAAe,KAAKH;QAC/D,MAAMI,aAAa,AAACxB,CAAAA,MAAMG,KAAK,IAAI,EAAE,AAAD,EAAGsB,GAAG,CAAC,CAACC,QAAU,IAAI,CAACC,cAAc,CAACD;QAC1E,MAAME,gBAAgBtD,kBAAkBkD;QACxC,IAAII,eAAe;YACjB,IAAI,CAACX,KAAK,CAACW,eAAe;gBAACV,MAAMzD,UAAU0D,WAAW;YAAA;QACxD;QAEA,MAAM,EAACU,SAAS,EAAEC,MAAM,EAAEC,cAAc,EAAEC,SAAS,EAAC,GAClD,IAAI,CAACC,YAAY,MAAMT,WAAWU,MAAM,GAAG,KAAKlC,MAAMK,IAAI,KAAKS,YAC3D;YAAC,GAAI,MAAM,IAAI,CAACqB,aAAa,CAACX,WAAW;YAAGK,WAAW,MAAMP;QAAW,IACxE,MAAM,IAAI,CAACc,eAAe,CAACd;QAEjC,IAAIe;QACJ,IAAI;YACFA,QAAQ,MAAM,IAAI,CAACC,oBAAoB,CAAC;gBACtCT;gBACAC;gBACArC;gBACAsC;YACF;QACF,EAAE,OAAOd,OAAO;YACd,IAAIA,iBAAiBzD,UAAU,MAAMyD;YACrC7B,kBAAkB,wBAAwB6B;YAC1C,IAAI,CAACA,KAAK,CAAC,CAAC,wBAAwB,EAAErD,gBAAgBqD,QAAQ,EAAE;gBAC9DC,MAAMzD,UAAU8E,aAAa;YAC/B;QACF;QAEA,IAAIvC,MAAMwC,IAAI,EAAE;YACd,IAAI,CAACC,OAAO,CAACJ;YACb;QACF;QAEA,IAAI,CAACK,GAAG,CAAC;QACT,IAAI,CAACA,GAAG,CAAC,CAAC,OAAO,EAAEL,MAAM5C,KAAK,EAAE;QAChC,IAAI,CAACiD,GAAG,CAAC,CAAC,IAAI,EAAEL,MAAMM,EAAE,EAAE;QAC1B,IAAIX,WAAW;YACb,IAAI,CAACU,GAAG,CAAC,CAAC,MAAM,EAAEV,WAAW;QAC/B;QACA,IAAIK,MAAMR,SAAS,EAAE;YACnB,IAAI,CAACa,GAAG,CAAC,CAAC,SAAS,EAAEL,MAAMR,SAAS,EAAE;QACxC;QACA,IAAI,CAACa,GAAG,CAAC,CAAC,OAAO,EAAEL,MAAMA,KAAK,EAAE;QAChC,IAAI,CAACK,GAAG,CAAC;QACT,IAAI,CAACA,GAAG,CAAC;QAET,IAAI,CAAC,IAAI,CAACT,YAAY,IAAI;YACxB,IAAI,CAACS,GAAG,CAAC;YACT,IAAI,CAACA,GAAG,CAAC;YACT,IAAI,CAACA,GAAG,CAAC,CAAC,EAAE,EAAEE,oBAAoB,IAAI,CAACC,MAAM,CAACC,GAAG,EAAE;gBAACjB;gBAAWC;gBAAQrC;gBAAO4C;YAAK,IAAI;QACzF;IACF;IAEA,MAAcC,qBAAqBS,OAKlC,EAA2B;QAC1B,MAAM,EAAClB,SAAS,EAAEC,MAAM,EAAErC,KAAK,EAAC,GAAGsD;QACnC,MAAMC,oBAAoBxE,0BAA0BsD;QACpD,IAAIkB,mBAAmB;YACrB,MAAM,EAACC,YAAYC,SAAS,EAAEC,SAAS,EAAC,GAAGH;YAC3C,OAAO/D,YAAY;gBAAC4C;gBAAWpC;gBAAOyD;gBAAWC;YAAS;QAC5D;QAEA,MAAMpB,iBAAiBgB,QAAQhB,cAAc,IAAK,MAAM,IAAI,CAACqB,mBAAmB;QAChF,IAAI;YACF,OAAO,MAAMpE,wBAAwB;gBACnC6C;gBACApC;gBACA4D,aAAa1E,cAAcmD,QAAQC;gBACnCA;YACF;QACF,EAAE,OAAOd,OAAO;YACd,IAAIA,iBAAiBjD,eAAeiD,MAAMqC,UAAU,KAAK,KAAK;gBAC5D,MAAM,IAAIC,MAAMjE,gCAAgC;oBAACkE,OAAOvC;gBAAK;YAC/D;YACA,MAAMA;QACR;IACF;IAEA,MAAckB,cAAcL,MAAe,EAAkD;QAC3F,MAAM,EAAC,cAAc2B,aAAa,EAAEpD,IAAI,EAAC,GAAG,IAAI,CAACL,KAAK;QACtD,IAAI8B,OAAOI,MAAM,GAAG,KAAK,CAACuB,iBAAiB,CAACpD,MAAM;YAChD,OAAO;gBAACyB;YAAM;QAChB;QAEA,MAAMoB,YAAY,MAAM,IAAI,CAACQ,gBAAgB;QAC7C,MAAMC,aAAajF,gBAAgBwE;QACnC,IAAIpB,OAAO8B,IAAI,CAAC,CAACzD,QAAUA,MAAM0D,GAAG,KAAKF,aAAa;YACpD,IAAItD,MAAM;gBACR,IAAI,CAACY,KAAK,CACR,CAAC,yBAAyB,EAAEiC,UAAU,+BAA+B,EAAES,WAAW,kBAAkB,CAAC,EACrG;oBAACzC,MAAMzD,UAAU0D,WAAW;gBAAA;YAEhC;YACA,OAAO;gBAACW;YAAM;QAChB;QAEA,MAAMgC,cAAczD,OAAO,MAAMzB,aAAayB,MAAM6C,WAAW,IAAI,CAACa,MAAM,IAAI1F;QAE9E,OAAO;YACLyD,QAAQ;mBAAIA;gBAAQ;oBAAC+B,KAAKF;oBAAYK,OAAO;wBAACF,YAAYG,IAAI;qBAAC;gBAAA;aAAE;YACjEjC,WAAWF,OAAOI,MAAM,KAAK,IAAI4B,YAAYI,KAAK,GAAGpD;QACvD;IACF;IAEQO,gBAAgBK,KAAyB,EAAsB;QACrE,IAAIA,UAAUZ,WAAW;YACvB,OAAOA;QACT;QAEA,MAAMqD,SAAS/F,gBAAgBsD;QAC/B,IAAI,WAAWyC,QAAQ;YACrB,IAAI,CAAClD,KAAK,CAACkD,OAAOlD,KAAK,EAAE;gBAACC,MAAMzD,UAAU0D,WAAW;YAAA;QACvD;QAEA,OAAOgD,OAAOtC,SAAS;IACzB;IAEQF,eAAeD,KAAa,EAAS;QAC3C,MAAMyC,SAAS1F,WAAWiD,MAAMV,IAAI;QACpC,IAAI,WAAWmD,QAAQ;YACrB,IAAI,CAAClD,KAAK,CAACkD,OAAOlD,KAAK,EAAE;gBAACC,MAAMzD,UAAU0D,WAAW;YAAA;QACvD;QAEA,OAAOgD,OAAOhE,KAAK;IACrB;IAEA,MAAciC,gBAAgBd,SAA4C,EAAmB;QAC3F,MAAM8C,qBAAqB,IAAI,CAACpE,KAAK,CAAC,aAAa;QACnD,MAAMqE,kBAAkB,AAAC,CAAA,MAAM,IAAI,CAACC,eAAe,EAAC,EAAGC,GAAG,EAAErB;QAC5D,MAAMsB,WAAW,MAAMzF,aAAa;YAAC0F,wBAAwB;QAAK;QAClE,MAAMC,iBAAiB,CAACxB,YACtBsB,SAASG,IAAI,CAAC,CAACC,UAAYA,QAAQjC,EAAE,KAAKO,YAAYnB,kBAAkBjB;QAC1E,MAAMiB,iBAAiB,MAAM,IAAI,CAACqB,mBAAmB,CAAC;YACpDyB,UAAUH,eAAeL;YACzBS,WAAWJ,eAAeN;QAC5B;QACA,MAAMW,uBAAuBP,SAASQ,MAAM,CAC1C,CAACJ,UAAYA,QAAQ7C,cAAc,KAAKA;QAE1C,IAAIqC,sBAAsB,CAACW,qBAAqBnB,IAAI,CAAC,CAAC,EAACjB,EAAE,EAAC,GAAKA,OAAOyB,qBAAqB;YACzF,IAAI,CAACnD,KAAK,CACR,CAAC,QAAQ,EAAEmD,mBAAmB,uBAAuB,EAAErC,eAAe,iCAAiC,CAAC,EACxG;gBAACb,MAAMzD,UAAU0D,WAAW;YAAA;QAEhC;QAEA,MAAMU,YAAY,MAAMP;QACxB,MAAMQ,SAAS,MAAMjD,gBAAgB;YACnCoG,sBAAsBb;YACtBI,UAAUO;QACZ;QACA,OAAO;YAAClD;YAAWC;YAAQC;QAAc;IAC3C;IAEA,MAAcR,kBAA+C;QAC3D,IAAI,IAAI,CAACU,YAAY,IAAI;YACvB,OAAOnB,UAAU,iCAAiC;;QACpD;QAEA,MAAMoE,SAAS,MAAMnH,OAAO;YAC1BoH,SAAS;gBACP;oBAAClB,MAAM;oBAASvC,OAAO;gBAAO;mBAC3BrC,mBAAmBoC,GAAG,CAAC,CAAC2D,OAAU,CAAA;wBACnCnB,MAAM,GAAGmB,KAAK,OAAO,EAAEjH,aAAaiH,MAAMC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;wBACzD3D,OAAO4D,OAAOF;oBAChB,CAAA;gBACA;oBAACnB,MAAM;oBAAevC,OAAO;gBAAQ;aACtC;YACDjB,SAAS;YACT8E,SAAS;QACX;QAEA,IAAIL,WAAW,SAAS;YACtB,OAAOpE;QACT;QAEA,IAAIoE,WAAW,UAAU;YACvB,MAAMxD,QAAQ,MAAM5D,MAAM;gBACxByH,SAAS;gBACTC,UAAU,CAAC1H;oBACT,MAAMqG,SAAS/F,gBAAgBN,MAAMkD,IAAI;oBACzC,OAAO,WAAWmD,SAASA,OAAOlD,KAAK,GAAG;gBAC5C;YACF;YACA,MAAMkD,SAAS/F,gBAAgBsD,MAAMV,IAAI;YACzC,OAAO,WAAWmD,SAASrD,YAAYqD,OAAOtC,SAAS;QACzD;QAEA,OAAO1D,aAAasH,OAAOP;IAC7B;IAEA,MAAcnE,iBAAkC;QAC9C,IAAI,IAAI,CAACkB,YAAY,IAAI;YACvB,IAAI,CAAChB,KAAK,CAAC,+DAA+D;gBACxEC,MAAMzD,UAAU0D,WAAW;YAC7B;QACF;QAEA,MAAM1B,QAAQ,MAAM3B,MAAM;YACxByH,SAAS;YACTC,UAAU,CAAC9D;gBACT,IAAI,CAACA,SAAS,CAACA,MAAMV,IAAI,IAAI;oBAC3B,OAAO;gBACT;gBACA,OAAO;YACT;QACF;QAEA,OAAOvB,MAAMuB,IAAI;IACnB;IAEA,MAAcoC,oBACZsC,uBAAgE,CAAC,CAAC,EACjD;QACjB,MAAM,EAACb,QAAQ,EAAEC,SAAS,EAAC,GAAGY;QAC9B,IAAI;YACF,OAAO,MAAMxH,sBAAsB;gBACjCyH,0BACEb,aAAa,AAAC,CAAA,MAAM,IAAI,CAACR,eAAe,EAAC,EAAGsB,GAAG,EAAE7D,kBAAkB8C;gBACrEgB,oBAAoB,IAAI,CAAC7F,KAAK,CAAC8F,YAAY;gBAC3CC,YAAY,IAAI,CAAC9D,YAAY;YAC/B;QACF,EAAE,OAAOhB,OAAO;YACd7B,kBAAkB,gCAAgC6B;YAClD,IAAI,CAACA,KAAK,CAACrD,gBAAgBqD,QAAQ;gBACjCC,MACED,iBAAiBhD,2BACbR,UAAU0D,WAAW,GACrB1D,UAAU8E,aAAa;YAC/B;QACF;IACF;IAEA,MAAcmB,mBAAoC;QAChD,IAAI,CAAC,IAAI,CAACzB,YAAY,IAAI;YACxB,OAAO,IAAI,CAAC+D,YAAY,CAAC;gBACvBnB,UAAU,IACR/F,iBAAiB;wBACfmH,qBAAqB;4BACnB;gCAAC9F,OAAO;gCAAQ+F,YAAY;4BAAsB;4BAClD;gCAAC/F,OAAO;gCAAU+F,YAAY;4BAAuB;yBACtD;oBACH;YACJ;QACF;QAEA,IAAI;YACF,OAAO,MAAM,IAAI,CAACF,YAAY;QAChC,EAAE,OAAO/E,OAAO;YACd,IAAI,CAACpD,2BAA2BoD,QAAQ,MAAMA;YAC9C,IAAI,CAACA,KAAK,CACR,uGACA;gBAACC,MAAMzD,UAAU0D,WAAW;YAAA;QAEhC;IACF;AACF;AAEA,SAASgF,cAAczE,KAAa;IAClC,OAAO,iBAAiB0E,IAAI,CAAC1E,SAASA,QAAQ,CAAC,CAAC,EAAEA,MAAM2E,UAAU,CAAC,KAAKf,OAAOgB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC9F;AAEA,SAAS1D,oBACPE,GAAW,EACX,EACEjB,SAAS,EACTC,MAAM,EACNrC,KAAK,EACL4C,KAAK,EAMN;IAED,MAAMN,iBACJM,MAAMkE,SAAS,EAAEC,iBAAiB,iBAAiBnE,MAAMkE,SAAS,CAACtD,UAAU,GAAGnC;IAClF,OAAO;QACL,GAAGgC,IAAI,cAAc,CAAC;QACtBqD,cAAc1G;WACVsC,iBAAiB;YAAC,CAAC,eAAe,EAAEA,gBAAgB;SAAC,GAAG,EAAE;WAC1DF,YAAY;YAAC,CAAC,aAAa,EAAEA,WAAW;SAAC,GAAG,EAAE;WAC/CC,OAAOL,GAAG,CAAC,CAACtB,QAAU,CAAC,QAAQ,EAAE5B,YAAY4B,QAAQ;QACxD;KACD,CAACsG,IAAI,CAAC;AACT"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Flags } from '@oclif/core';
|
|
2
1
|
import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
|
|
3
2
|
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
4
3
|
import { formatTokenAccess } from '../../actions/tokens/formatTokenAccess.js';
|
|
@@ -9,6 +8,7 @@ import { getOrganizationFlag, getProjectIdFlag } from '../../util/sharedFlags.js
|
|
|
9
8
|
const listTokenDebug = subdebug('tokens:list');
|
|
10
9
|
export class TokensListCommand extends SanityCommand {
|
|
11
10
|
static description = 'List API tokens for a project or organization';
|
|
11
|
+
static enableJsonFlag = true;
|
|
12
12
|
static examples = [
|
|
13
13
|
{
|
|
14
14
|
command: '<%= config.bin %> <%= command.id %> --organization org123',
|
|
@@ -38,10 +38,6 @@ export class TokensListCommand extends SanityCommand {
|
|
|
38
38
|
...getProjectIdFlag({
|
|
39
39
|
description: 'Project ID to list tokens for',
|
|
40
40
|
semantics: 'override'
|
|
41
|
-
}),
|
|
42
|
-
json: Flags.boolean({
|
|
43
|
-
default: false,
|
|
44
|
-
description: 'Output tokens in JSON format'
|
|
45
41
|
})
|
|
46
42
|
};
|
|
47
43
|
static hiddenAliases = [
|
|
@@ -67,7 +63,7 @@ export class TokensListCommand extends SanityCommand {
|
|
|
67
63
|
});
|
|
68
64
|
}
|
|
69
65
|
if (outputJson) {
|
|
70
|
-
this.
|
|
66
|
+
this.logJson(tokens);
|
|
71
67
|
return;
|
|
72
68
|
}
|
|
73
69
|
if (tokens.length === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/tokens/list.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/tokens/list.ts"],"sourcesContent":["import {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\n\nimport {formatTokenAccess} from '../../actions/tokens/formatTokenAccess.js'\nimport {type Robot} from '../../actions/tokens/types.js'\nimport {getTokens} from '../../services/tokens.js'\nimport {resolveTokenScope} from '../../util/resolveTokenScope.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getOrganizationFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listTokenDebug = subdebug('tokens:list')\n\nexport class TokensListCommand extends SanityCommand<typeof TokensListCommand> {\n static override description = 'List API tokens for a project or organization'\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> --organization org123',\n description: 'List tokens managed by an organization',\n },\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List tokens for the project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'List tokens in JSON format',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'List tokens for a specific project',\n },\n ]\n static override flags = {\n ...getOrganizationFlag({\n description: 'List tokens managed by this organization',\n exclusive: ['project-id'],\n semantics: 'specify',\n }),\n ...getProjectIdFlag({\n description: 'Project ID to list tokens for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['token:list']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(TokensListCommand)\n const {json} = flags\n const outputJson = json ?? false\n\n const scope = await resolveTokenScope({\n getProjectId: this.getProjectId.bind(this),\n grant: 'read',\n organization: flags.organization,\n })\n\n let tokens: Robot[]\n try {\n tokens = await getTokens(scope)\n } catch (error) {\n const message = getErrorMessage(error)\n listTokenDebug(`Error fetching tokens for ${scope.resourceType} ${scope.resourceId}`, error)\n this.error(`Token list retrieval failed:\\n${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (outputJson) {\n this.logJson(tokens)\n return\n }\n\n if (tokens.length === 0) {\n this.log(`No API tokens found for this ${scope.resourceType}.`)\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'label', title: 'Label'},\n {alignment: 'left', name: 'id', title: 'ID'},\n {\n alignment: 'left',\n name: 'roles',\n title: scope.resourceType === 'organization' ? 'Access' : 'Roles',\n },\n {alignment: 'left', name: 'expires', title: 'Expires'},\n ],\n title: `Found ${tokens.length} API tokens`,\n })\n\n for (const token of tokens) {\n const roles = formatTokenAccess(token, scope)\n\n table.addRow({\n expires: token.expiresAt ? token.expiresAt.slice(0, 10) : 'Never',\n id: token.id,\n label: token.label,\n roles,\n })\n }\n\n this.log(table.render())\n }\n}\n"],"names":["exitCodes","SanityCommand","subdebug","getErrorMessage","formatTokenAccess","getTokens","resolveTokenScope","Table","getOrganizationFlag","getProjectIdFlag","listTokenDebug","TokensListCommand","description","enableJsonFlag","examples","command","flags","exclusive","semantics","hiddenAliases","run","parse","json","outputJson","scope","getProjectId","bind","grant","organization","tokens","error","message","resourceType","resourceId","exit","RUNTIME_ERROR","logJson","length","log","table","columns","alignment","name","title","token","roles","addRow","expires","expiresAt","slice","id","label","render"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AAEvD,SAAQC,iBAAiB,QAAO,4CAA2C;AAE3E,SAAQC,SAAS,QAAO,2BAA0B;AAClD,SAAQC,iBAAiB,QAAO,kCAAiC;AACjE,SAAQC,KAAK,QAAO,gCAA+B;AACnD,SAAQC,mBAAmB,EAAEC,gBAAgB,QAAO,4BAA2B;AAE/E,MAAMC,iBAAiBR,SAAS;AAEhC,OAAO,MAAMS,0BAA0BV;IACrC,OAAgBW,cAAc,gDAA+C;IAC7E,OAAgBC,iBAAiB,KAAI;IAErC,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IACD,OAAgBI,QAAQ;QACtB,GAAGR,oBAAoB;YACrBI,aAAa;YACbK,WAAW;gBAAC;aAAa;YACzBC,WAAW;QACb,EAAE;QACF,GAAGT,iBAAiB;YAClBG,aAAa;YACbM,WAAW;QACb,EAAE;IACJ,EAAC;IAED,OAAgBC,gBAA0B;QAAC;KAAa,CAAA;IAExD,MAAaC,MAAqB;QAChC,MAAM,EAACJ,KAAK,EAAC,GAAG,MAAM,IAAI,CAACK,KAAK,CAACV;QACjC,MAAM,EAACW,IAAI,EAAC,GAAGN;QACf,MAAMO,aAAaD,QAAQ;QAE3B,MAAME,QAAQ,MAAMlB,kBAAkB;YACpCmB,cAAc,IAAI,CAACA,YAAY,CAACC,IAAI,CAAC,IAAI;YACzCC,OAAO;YACPC,cAAcZ,MAAMY,YAAY;QAClC;QAEA,IAAIC;QACJ,IAAI;YACFA,SAAS,MAAMxB,UAAUmB;QAC3B,EAAE,OAAOM,OAAO;YACd,MAAMC,UAAU5B,gBAAgB2B;YAChCpB,eAAe,CAAC,0BAA0B,EAAEc,MAAMQ,YAAY,CAAC,CAAC,EAAER,MAAMS,UAAU,EAAE,EAAEH;YACtF,IAAI,CAACA,KAAK,CAAC,CAAC,8BAA8B,EAAEC,SAAS,EAAE;gBAACG,MAAMlC,UAAUmC,aAAa;YAAA;QACvF;QAEA,IAAIZ,YAAY;YACd,IAAI,CAACa,OAAO,CAACP;YACb;QACF;QAEA,IAAIA,OAAOQ,MAAM,KAAK,GAAG;YACvB,IAAI,CAACC,GAAG,CAAC,CAAC,6BAA6B,EAAEd,MAAMQ,YAAY,CAAC,CAAC,CAAC;YAC9D;QACF;QAEA,MAAMO,QAAQ,IAAIhC,MAAM;YACtBiC,SAAS;gBACP;oBAACC,WAAW;oBAAQC,MAAM;oBAASC,OAAO;gBAAO;gBACjD;oBAACF,WAAW;oBAAQC,MAAM;oBAAMC,OAAO;gBAAI;gBAC3C;oBACEF,WAAW;oBACXC,MAAM;oBACNC,OAAOnB,MAAMQ,YAAY,KAAK,iBAAiB,WAAW;gBAC5D;gBACA;oBAACS,WAAW;oBAAQC,MAAM;oBAAWC,OAAO;gBAAS;aACtD;YACDA,OAAO,CAAC,MAAM,EAAEd,OAAOQ,MAAM,CAAC,WAAW,CAAC;QAC5C;QAEA,KAAK,MAAMO,SAASf,OAAQ;YAC1B,MAAMgB,QAAQzC,kBAAkBwC,OAAOpB;YAEvCe,MAAMO,MAAM,CAAC;gBACXC,SAASH,MAAMI,SAAS,GAAGJ,MAAMI,SAAS,CAACC,KAAK,CAAC,GAAG,MAAM;gBAC1DC,IAAIN,MAAMM,EAAE;gBACZC,OAAOP,MAAMO,KAAK;gBAClBN;YACF;QACF;QAEA,IAAI,CAACP,GAAG,CAACC,MAAMa,MAAM;IACvB;AACF"}
|
|
@@ -8,6 +8,7 @@ const tokensRotateDebug = subdebug('tokens:rotate');
|
|
|
8
8
|
const TOKEN_USAGE_HINT = 'Token is required. Pipe it on standard input (`echo "$SANITY_TOKEN" | sanity tokens rotate`) to keep it out of shell history, or pass it with `--token`.';
|
|
9
9
|
export class RotateTokenCommand extends SanityCommand {
|
|
10
10
|
static description = 'Rotate an API token, replacing its secret with a new one';
|
|
11
|
+
static enableJsonFlag = true;
|
|
11
12
|
static examples = [
|
|
12
13
|
{
|
|
13
14
|
command: 'echo "$SANITY_TOKEN" | <%= config.bin %> <%= command.id %>',
|
|
@@ -23,10 +24,6 @@ export class RotateTokenCommand extends SanityCommand {
|
|
|
23
24
|
}
|
|
24
25
|
];
|
|
25
26
|
static flags = {
|
|
26
|
-
json: Flags.boolean({
|
|
27
|
-
default: false,
|
|
28
|
-
description: 'Output as JSON'
|
|
29
|
-
}),
|
|
30
27
|
token: Flags.string({
|
|
31
28
|
char: 't',
|
|
32
29
|
description: 'Token to rotate (prefer standard input to keep it out of shell history)',
|
|
@@ -44,7 +41,7 @@ export class RotateTokenCommand extends SanityCommand {
|
|
|
44
41
|
tokensRotateDebug('Rotating API token');
|
|
45
42
|
const robot = await rotateToken(token);
|
|
46
43
|
if (json) {
|
|
47
|
-
this.
|
|
44
|
+
this.logJson(robot);
|
|
48
45
|
return;
|
|
49
46
|
}
|
|
50
47
|
this.log('API token rotated');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/tokens/rotate.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {rotateToken} from '../../services/tokens.js'\nimport {readTokenFromStdin} from '../../util/readTokenFromStdin.js'\n\nconst tokensRotateDebug = subdebug('tokens:rotate')\n\nconst TOKEN_USAGE_HINT =\n 'Token is required. Pipe it on standard input (`echo \"$SANITY_TOKEN\" | sanity tokens rotate`) to keep it out of shell history, or pass it with `--token`.'\n\nexport class RotateTokenCommand extends SanityCommand<typeof RotateTokenCommand> {\n static override description = 'Rotate an API token, replacing its secret with a new one'\n\n static override examples = [\n {\n command: 'echo \"$SANITY_TOKEN\" | <%= config.bin %> <%= command.id %>',\n description: 'Rotate the token piped on standard input',\n },\n {\n command: '<%= config.bin %> <%= command.id %> < token.txt',\n description: 'Rotate a token read from a file',\n },\n {\n command: 'echo \"$SANITY_TOKEN\" | <%= config.bin %> <%= command.id %> --json',\n description: 'Output the rotated token as JSON',\n },\n ]\n\n static override flags = {\n
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/tokens/rotate.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {rotateToken} from '../../services/tokens.js'\nimport {readTokenFromStdin} from '../../util/readTokenFromStdin.js'\n\nconst tokensRotateDebug = subdebug('tokens:rotate')\n\nconst TOKEN_USAGE_HINT =\n 'Token is required. Pipe it on standard input (`echo \"$SANITY_TOKEN\" | sanity tokens rotate`) to keep it out of shell history, or pass it with `--token`.'\n\nexport class RotateTokenCommand extends SanityCommand<typeof RotateTokenCommand> {\n static override description = 'Rotate an API token, replacing its secret with a new one'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: 'echo \"$SANITY_TOKEN\" | <%= config.bin %> <%= command.id %>',\n description: 'Rotate the token piped on standard input',\n },\n {\n command: '<%= config.bin %> <%= command.id %> < token.txt',\n description: 'Rotate a token read from a file',\n },\n {\n command: 'echo \"$SANITY_TOKEN\" | <%= config.bin %> <%= command.id %> --json',\n description: 'Output the rotated token as JSON',\n },\n ]\n\n static override flags = {\n token: Flags.string({\n char: 't',\n description: 'Token to rotate (prefer standard input to keep it out of shell history)',\n helpValue: '<token>',\n }),\n }\n\n static override hiddenAliases: string[] = ['token:rotate']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(RotateTokenCommand)\n const {json} = flags\n\n const token = flags.token?.trim() || (await this.readTokenArg())\n\n try {\n tokensRotateDebug('Rotating API token')\n const robot = await rotateToken(token)\n\n if (json) {\n this.logJson(robot)\n return\n }\n\n this.log('API token rotated')\n this.log(`Label: ${robot.label}`)\n this.log(`ID: ${robot.id}`)\n if (robot.expiresAt) {\n this.log(`Expires: ${robot.expiresAt}`)\n }\n this.log(`Token: ${robot.token}`)\n this.log('')\n this.log(\"Copy the token now. It won't be shown again.\")\n this.log('The previous token was revoked and no longer works.')\n } catch (error) {\n tokensRotateDebug('Error rotating token', error)\n\n if (isHttpError(error) && error.statusCode === 401) {\n this.error(\n 'Token is invalid, expired, or was already rotated or revoked. Check that you are passing the current token.',\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n if (isHttpError(error) && error.statusCode === 403) {\n this.error(\n 'The token is not an API token and cannot be rotated. Personal tokens are renewed by logging in again.',\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n this.error(`Token rotation failed:\\n${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async readTokenArg(): Promise<string> {\n let token: string\n try {\n token = await readTokenFromStdin(TOKEN_USAGE_HINT)\n } catch (error) {\n return this.error(getErrorMessage(error), {exit: exitCodes.USAGE_ERROR})\n }\n\n if (!token) {\n this.error(TOKEN_USAGE_HINT, {exit: exitCodes.USAGE_ERROR})\n }\n\n return token\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","isHttpError","rotateToken","readTokenFromStdin","tokensRotateDebug","TOKEN_USAGE_HINT","RotateTokenCommand","description","enableJsonFlag","examples","command","flags","token","string","char","helpValue","hiddenAliases","run","parse","json","trim","readTokenArg","robot","logJson","log","label","id","expiresAt","error","statusCode","exit","RUNTIME_ERROR","USAGE_ERROR"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,WAAW,QAAO,2BAA0B;AACpD,SAAQC,kBAAkB,QAAO,mCAAkC;AAEnE,MAAMC,oBAAoBL,SAAS;AAEnC,MAAMM,mBACJ;AAEF,OAAO,MAAMC,2BAA2BR;IACtC,OAAgBS,cAAc,2DAA0D;IAExF,OAAgBC,iBAAiB,KAAI;IAErC,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtBC,OAAOhB,MAAMiB,MAAM,CAAC;YAClBC,MAAM;YACNP,aAAa;YACbQ,WAAW;QACb;IACF,EAAC;IAED,OAAgBC,gBAA0B;QAAC;KAAe,CAAA;IAE1D,MAAaC,MAAqB;QAChC,MAAM,EAACN,KAAK,EAAC,GAAG,MAAM,IAAI,CAACO,KAAK,CAACZ;QACjC,MAAM,EAACa,IAAI,EAAC,GAAGR;QAEf,MAAMC,QAAQD,MAAMC,KAAK,EAAEQ,UAAW,MAAM,IAAI,CAACC,YAAY;QAE7D,IAAI;YACFjB,kBAAkB;YAClB,MAAMkB,QAAQ,MAAMpB,YAAYU;YAEhC,IAAIO,MAAM;gBACR,IAAI,CAACI,OAAO,CAACD;gBACb;YACF;YAEA,IAAI,CAACE,GAAG,CAAC;YACT,IAAI,CAACA,GAAG,CAAC,CAAC,OAAO,EAAEF,MAAMG,KAAK,EAAE;YAChC,IAAI,CAACD,GAAG,CAAC,CAAC,IAAI,EAAEF,MAAMI,EAAE,EAAE;YAC1B,IAAIJ,MAAMK,SAAS,EAAE;gBACnB,IAAI,CAACH,GAAG,CAAC,CAAC,SAAS,EAAEF,MAAMK,SAAS,EAAE;YACxC;YACA,IAAI,CAACH,GAAG,CAAC,CAAC,OAAO,EAAEF,MAAMV,KAAK,EAAE;YAChC,IAAI,CAACY,GAAG,CAAC;YACT,IAAI,CAACA,GAAG,CAAC;YACT,IAAI,CAACA,GAAG,CAAC;QACX,EAAE,OAAOI,OAAO;YACdxB,kBAAkB,wBAAwBwB;YAE1C,IAAI3B,YAAY2B,UAAUA,MAAMC,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACD,KAAK,CACR,+GACA;oBAACE,MAAMjC,UAAUkC,aAAa;gBAAA;YAElC;YACA,IAAI9B,YAAY2B,UAAUA,MAAMC,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACD,KAAK,CACR,yGACA;oBAACE,MAAMjC,UAAUkC,aAAa;gBAAA;YAElC;YAEA,IAAI,CAACH,KAAK,CAAC,CAAC,wBAAwB,EAAE5B,gBAAgB4B,QAAQ,EAAE;gBAC9DE,MAAMjC,UAAUkC,aAAa;YAC/B;QACF;IACF;IAEA,MAAcV,eAAgC;QAC5C,IAAIT;QACJ,IAAI;YACFA,QAAQ,MAAMT,mBAAmBE;QACnC,EAAE,OAAOuB,OAAO;YACd,OAAO,IAAI,CAACA,KAAK,CAAC5B,gBAAgB4B,QAAQ;gBAACE,MAAMjC,UAAUmC,WAAW;YAAA;QACxE;QAEA,IAAI,CAACpB,OAAO;YACV,IAAI,CAACgB,KAAK,CAACvB,kBAAkB;gBAACyB,MAAMjC,UAAUmC,WAAW;YAAA;QAC3D;QAEA,OAAOpB;IACT;AACF"}
|
|
@@ -12,6 +12,7 @@ import { generateResourceTypes, resolveResourceSchemas } from '../../actions/typ
|
|
|
12
12
|
import { parseTypegenConfig } from '../../actions/typegen/parseTypegenConfig.js';
|
|
13
13
|
import { parseTypegenResources } from '../../actions/typegen/parseTypegenResources.js';
|
|
14
14
|
import { createTypegenProgressRenderer } from '../../actions/typegen/renderTypegenProgress.js';
|
|
15
|
+
import { watchResourceTypes } from '../../actions/typegen/watchResourceTypes.js';
|
|
15
16
|
const description = `Sanity TypeGen
|
|
16
17
|
|
|
17
18
|
${styleText('bold', 'Configuration:')}
|
|
@@ -30,7 +31,7 @@ The default configuration values listed above are used if not overridden in your
|
|
|
30
31
|
|
|
31
32
|
${styleText('bold', 'Note:')}
|
|
32
33
|
- The \`sanity schema extract\` command is a prerequisite for extracting your Sanity Studio schema into a \`schema.json\` file, which is then used by the \`sanity typegen generate\` command to generate type definitions.
|
|
33
|
-
- To generate types for several datasets, list them in \`typegen.resources\` in \`sanity.cli.ts\` (beta). Each resource uses its own \`schema\` file, or the schema bound to its dataset when \`schema\` is omitted.`.trim();
|
|
34
|
+
- To generate types for several datasets, list them in \`typegen.resources\` in \`sanity.cli.ts\` (beta). Each resource uses its own \`schema\` file, or the schema bound to its dataset when \`schema\` is omitted. With \`--watch\`, a schema bound to a dataset is fetched once, when the watcher starts. Restart it after deploying a schema.`.trim();
|
|
34
35
|
export class TypegenGenerateCommand extends SanityCommand {
|
|
35
36
|
static description = description;
|
|
36
37
|
static examples = [
|
|
@@ -131,6 +132,25 @@ export class TypegenGenerateCommand extends SanityCommand {
|
|
|
131
132
|
});
|
|
132
133
|
}
|
|
133
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Prints the resources' configuration warnings, then locates every resource's schema, reporting
|
|
137
|
+
* each one on the spinner.
|
|
138
|
+
*/ async resolveResources(resources, spin, workDir, { checkSchemaFiles }) {
|
|
139
|
+
for (const warning of resources.warnings){
|
|
140
|
+
this.output.warn(warning);
|
|
141
|
+
}
|
|
142
|
+
return resolveResourceSchemas({
|
|
143
|
+
checkSchemaFiles,
|
|
144
|
+
onResolved: (entry)=>{
|
|
145
|
+
spin.succeed('schemaPath' in entry ? `Using ${formatPath(entry.schemaPath)} for ${entry.key}` : `Fetched the schema bound to ${entry.key} (${entry.schemaVersion})`);
|
|
146
|
+
},
|
|
147
|
+
onResolving: (resource, key)=>{
|
|
148
|
+
spin.start(resource.schema === undefined ? `Fetching the schema bound to ${key}…` : `Checking the schema file for ${key}…`);
|
|
149
|
+
},
|
|
150
|
+
resources: resources.resources,
|
|
151
|
+
workDir
|
|
152
|
+
});
|
|
153
|
+
}
|
|
134
154
|
async runSingle() {
|
|
135
155
|
const trace = this.telemetry.trace(TypesGeneratedTrace);
|
|
136
156
|
let spin;
|
|
@@ -138,31 +158,14 @@ export class TypegenGenerateCommand extends SanityCommand {
|
|
|
138
158
|
const { config: typegenConfig, resources, type: typegenConfigMethod, workDir } = await this.getConfig();
|
|
139
159
|
trace.start();
|
|
140
160
|
if (resources) {
|
|
141
|
-
for (const warning of resources.warnings){
|
|
142
|
-
this.output.warn(warning);
|
|
143
|
-
}
|
|
144
161
|
const resourceSpin = spinner({});
|
|
145
162
|
spin = resourceSpin;
|
|
146
|
-
const resolved = await
|
|
147
|
-
|
|
148
|
-
resourceSpin.succeed('schemaPath' in entry ? `Using ${formatPath(entry.schemaPath)} for ${entry.key}` : `Fetched the schema bound to ${entry.key} (${entry.schemaVersion})`);
|
|
149
|
-
},
|
|
150
|
-
onResolving: (resource, key)=>{
|
|
151
|
-
resourceSpin.start(resource.schema === undefined ? `Fetching the schema bound to ${key}…` : `Checking the schema file for ${key}…`);
|
|
152
|
-
},
|
|
153
|
-
resources: resources.resources,
|
|
154
|
-
workDir
|
|
163
|
+
const resolved = await this.resolveResources(resources, resourceSpin, workDir, {
|
|
164
|
+
checkSchemaFiles: true
|
|
155
165
|
});
|
|
156
166
|
const results = await generateResourceTypes({
|
|
157
167
|
config: typegenConfig,
|
|
158
|
-
onProgress: (
|
|
159
|
-
resourceSpin.start(`Generating types for ${entry.key}…`);
|
|
160
|
-
return createTypegenProgressRenderer(resourceSpin, {
|
|
161
|
-
formatGeneratedCode: typegenConfig.formatGeneratedCode,
|
|
162
|
-
generates: entry.resource.generates,
|
|
163
|
-
schema: 'schemaPath' in entry ? entry.schemaPath : entry.key
|
|
164
|
-
});
|
|
165
|
-
},
|
|
168
|
+
onProgress: createResourceProgressHandler(resourceSpin, typegenConfig),
|
|
166
169
|
resolved,
|
|
167
170
|
workDir
|
|
168
171
|
});
|
|
@@ -208,36 +211,14 @@ export class TypegenGenerateCommand extends SanityCommand {
|
|
|
208
211
|
let spin;
|
|
209
212
|
try {
|
|
210
213
|
const { config: typegenConfig, resources, workDir } = await this.getConfig();
|
|
211
|
-
if (resources) {
|
|
212
|
-
this.error('Watch mode does not support typegen.resources yet. Run "sanity typegen generate" without --watch.', {
|
|
213
|
-
exit: exitCodes.USAGE_ERROR
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
214
|
trace.start();
|
|
217
215
|
let resolve = ()=>{};
|
|
218
216
|
const promise = new Promise((res)=>{
|
|
219
217
|
resolve = res;
|
|
220
218
|
});
|
|
221
|
-
const watchSpin = spinner({})
|
|
219
|
+
const watchSpin = spinner({});
|
|
222
220
|
spin = watchSpin;
|
|
223
|
-
const
|
|
224
|
-
formatGeneratedCode: typegenConfig.formatGeneratedCode,
|
|
225
|
-
generates: typegenConfig.generates,
|
|
226
|
-
schema: typegenConfig.schema
|
|
227
|
-
};
|
|
228
|
-
let renderProgress = createTypegenProgressRenderer(watchSpin, rendererContext);
|
|
229
|
-
const typegenWatcher = runTypegenWatcher({
|
|
230
|
-
config: typegenConfig,
|
|
231
|
-
onProgress: (event)=>{
|
|
232
|
-
// 'schemaLoaded' is the first event of every generation cycle; the
|
|
233
|
-
// renderer accumulates per-run state, so start each cycle fresh.
|
|
234
|
-
if (event.type === 'schemaLoaded') {
|
|
235
|
-
renderProgress = createTypegenProgressRenderer(watchSpin, rendererContext);
|
|
236
|
-
}
|
|
237
|
-
renderProgress(event);
|
|
238
|
-
},
|
|
239
|
-
workDir
|
|
240
|
-
});
|
|
221
|
+
const typegenWatcher = resources ? await this.startResourceWatcher(typegenConfig, resources, watchSpin, workDir) : this.startSchemaWatcher(typegenConfig, watchSpin, workDir);
|
|
241
222
|
const stop = once(async ()=>{
|
|
242
223
|
process.off('SIGINT', stop);
|
|
243
224
|
process.off('SIGTERM', stop);
|
|
@@ -272,6 +253,63 @@ export class TypegenGenerateCommand extends SanityCommand {
|
|
|
272
253
|
});
|
|
273
254
|
}
|
|
274
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Locates every resource's schema once, then generates on each query or local schema change.
|
|
258
|
+
* A schema bound to a dataset is fetched only here, so a schema deployed while the watcher runs
|
|
259
|
+
* is used after a restart.
|
|
260
|
+
*/ async startResourceWatcher(config, resources, spin, workDir) {
|
|
261
|
+
// A missing local schema file fails that resource's generation until the file appears,
|
|
262
|
+
// instead of ending the watcher.
|
|
263
|
+
const resolved = await this.resolveResources(resources, spin, workDir, {
|
|
264
|
+
checkSchemaFiles: false
|
|
265
|
+
});
|
|
266
|
+
return watchResourceTypes({
|
|
267
|
+
config,
|
|
268
|
+
onChange: (event, relativePath)=>{
|
|
269
|
+
this.output.log(`[${new Date().toLocaleTimeString()}] ${event}: ${relativePath}`);
|
|
270
|
+
},
|
|
271
|
+
onError: (error)=>{
|
|
272
|
+
spin.fail(error instanceof Error ? error.message : String(error));
|
|
273
|
+
},
|
|
274
|
+
onProgress: createResourceProgressHandler(spin, config),
|
|
275
|
+
resolved,
|
|
276
|
+
workDir
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
startSchemaWatcher(config, spin, workDir) {
|
|
280
|
+
spin.start('Loading schema…');
|
|
281
|
+
const rendererContext = {
|
|
282
|
+
formatGeneratedCode: config.formatGeneratedCode,
|
|
283
|
+
generates: config.generates,
|
|
284
|
+
schema: config.schema
|
|
285
|
+
};
|
|
286
|
+
let renderProgress = createTypegenProgressRenderer(spin, rendererContext);
|
|
287
|
+
return runTypegenWatcher({
|
|
288
|
+
config,
|
|
289
|
+
onProgress: (event)=>{
|
|
290
|
+
// 'schemaLoaded' is the first event of every generation cycle; the
|
|
291
|
+
// renderer accumulates per-run state, so start each cycle fresh.
|
|
292
|
+
if (event.type === 'schemaLoaded') {
|
|
293
|
+
renderProgress = createTypegenProgressRenderer(spin, rendererContext);
|
|
294
|
+
}
|
|
295
|
+
renderProgress(event);
|
|
296
|
+
},
|
|
297
|
+
workDir
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Returns the progress handler for one resource's generation, which starts with a line naming
|
|
303
|
+
* the resource. The renderer keeps per-run state, so each generation gets a new one.
|
|
304
|
+
*/ function createResourceProgressHandler(spin, config) {
|
|
305
|
+
return (entry)=>{
|
|
306
|
+
spin.start(`Generating types for ${entry.key}…`);
|
|
307
|
+
return createTypegenProgressRenderer(spin, {
|
|
308
|
+
formatGeneratedCode: config.formatGeneratedCode,
|
|
309
|
+
generates: entry.resource.generates,
|
|
310
|
+
schema: 'schemaPath' in entry ? entry.schemaPath : entry.key
|
|
311
|
+
});
|
|
312
|
+
};
|
|
275
313
|
}
|
|
276
314
|
|
|
277
315
|
//# sourceMappingURL=generate.js.map
|