@sanity/cli 8.6.0 → 8.8.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 +918 -40
- package/dist/actions/api/distillApiRoutes.js +1 -1
- package/dist/actions/api/distillApiRoutes.js.map +1 -1
- package/dist/actions/api/resolveEndpoint.js +1 -1
- package/dist/actions/api/resolveEndpoint.js.map +1 -1
- package/dist/actions/backup/archiveDir.js +7 -3
- package/dist/actions/backup/archiveDir.js.map +1 -1
- package/dist/actions/context/resolveOrganizationId.js +17 -0
- package/dist/actions/context/resolveOrganizationId.js.map +1 -0
- package/dist/actions/context/waitForJob.js +24 -0
- package/dist/actions/context/waitForJob.js.map +1 -0
- package/dist/actions/context/watchJob.js +32 -0
- package/dist/actions/context/watchJob.js.map +1 -0
- package/dist/actions/deploy/deployApp.js +6 -6
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +6 -6
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/exec/execScript.js +6 -18
- package/dist/actions/exec/execScript.js.map +1 -1
- package/dist/actions/exec/runScript.worker.js +21 -0
- package/dist/actions/exec/runScript.worker.js.map +1 -0
- package/dist/actions/init/initAction.js +1 -1
- package/dist/actions/init/initAction.js.map +1 -1
- package/dist/actions/media/importMedia.js +4 -2
- package/dist/actions/media/importMedia.js.map +1 -1
- package/dist/commands/backups/download.js +8 -17
- package/dist/commands/backups/download.js.map +1 -1
- package/dist/commands/backups/list.js +1 -1
- package/dist/commands/backups/list.js.map +1 -1
- package/dist/commands/context/build.js +111 -0
- package/dist/commands/context/build.js.map +1 -0
- package/dist/commands/context/create.js +106 -0
- package/dist/commands/context/create.js.map +1 -0
- package/dist/commands/context/delete.js +68 -0
- package/dist/commands/context/delete.js.map +1 -0
- package/dist/commands/context/get.js +95 -0
- package/dist/commands/context/get.js.map +1 -0
- package/dist/commands/context/imports/create.js +270 -0
- package/dist/commands/context/imports/create.js.map +1 -0
- package/dist/commands/context/imports/delete.js +73 -0
- package/dist/commands/context/imports/delete.js.map +1 -0
- package/dist/commands/context/imports/download.js +65 -0
- package/dist/commands/context/imports/download.js.map +1 -0
- package/dist/commands/context/imports/get.js +93 -0
- package/dist/commands/context/imports/get.js.map +1 -0
- package/dist/commands/context/imports/list.js +105 -0
- package/dist/commands/context/imports/list.js.map +1 -0
- package/dist/commands/context/jobs/get.js +90 -0
- package/dist/commands/context/jobs/get.js.map +1 -0
- package/dist/commands/context/list.js +109 -0
- package/dist/commands/context/list.js.map +1 -0
- package/dist/commands/context/refresh.js +46 -0
- package/dist/commands/context/refresh.js.map +1 -0
- package/dist/commands/context/update.js +127 -0
- package/dist/commands/context/update.js.map +1 -0
- package/dist/commands/datasets/alias/delete.js +4 -9
- package/dist/commands/datasets/alias/delete.js.map +1 -1
- package/dist/commands/datasets/alias/unlink.js +4 -9
- package/dist/commands/datasets/alias/unlink.js.map +1 -1
- package/dist/commands/datasets/copy.js +1 -1
- package/dist/commands/datasets/copy.js.map +1 -1
- package/dist/commands/datasets/delete.js +4 -9
- package/dist/commands/datasets/delete.js.map +1 -1
- package/dist/commands/graphql/undeploy.js +4 -9
- package/dist/commands/graphql/undeploy.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/media/create-aspect.js +5 -6
- package/dist/commands/media/create-aspect.js.map +1 -1
- package/dist/commands/media/delete-aspect.js +7 -23
- package/dist/commands/media/delete-aspect.js.map +1 -1
- package/dist/commands/media/deploy-aspect.js +4 -9
- package/dist/commands/media/deploy-aspect.js.map +1 -1
- package/dist/commands/media/export.js +3 -7
- package/dist/commands/media/export.js.map +1 -1
- package/dist/commands/media/import.js +3 -7
- package/dist/commands/media/import.js.map +1 -1
- package/dist/commands/organizations/create.js +4 -9
- package/dist/commands/organizations/create.js.map +1 -1
- package/dist/commands/schemas/delete.js +3 -8
- package/dist/commands/schemas/delete.js.map +1 -1
- package/dist/commands/tokens/delete.js +4 -7
- package/dist/commands/tokens/delete.js.map +1 -1
- package/dist/commands/users/invite.js +8 -18
- package/dist/commands/users/invite.js.map +1 -1
- package/dist/exports/index.js +1 -1
- package/dist/exports/index.js.map +1 -1
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +18 -0
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
- package/dist/generated/apiRoutes.js +11 -9
- package/dist/generated/apiRoutes.js.map +1 -1
- package/dist/prompts/promptForOrganization.js +19 -0
- package/dist/prompts/promptForOrganization.js.map +1 -0
- package/dist/services/context.js +132 -0
- package/dist/services/context.js.map +1 -0
- package/dist/util/extractDocumentsFromNdjsonOrTarball.js.map +1 -1
- package/dist/util/sharedFlags.js +24 -0
- package/dist/util/sharedFlags.js.map +1 -1
- package/oclif.config.js +1 -0
- package/oclif.manifest.json +1931 -1133
- package/package.json +20 -26
- package/dist/util/dates.js +0 -125
- package/dist/util/dates.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/backups/download.ts"],"sourcesContent":["import {createWriteStream} from 'node:fs'\nimport {access, mkdir, mkdtemp} from 'node:fs/promises'\nimport {tmpdir} from 'node:os'\nimport path from 'node:path'\nimport {finished} from 'node:stream/promises'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\nimport {boxen, confirm, input, select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport pMap from 'p-map'\nimport prettyMs from 'pretty-ms'\n\nimport {archiveDir} from '../../actions/backup/archiveDir.js'\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {backupDownloadDebug} from '../../actions/backup/backupDownloadDebug.js'\nimport {cleanupTmpDir} from '../../actions/backup/cleanupTmpDir.js'\nimport {downloadAsset} from '../../actions/backup/downloadAsset.js'\nimport {downloadDocument} from '../../actions/backup/downloadDocument.js'\nimport {type File, PaginatedGetBackupStream} from '../../actions/backup/fetchNextBackupPage.js'\nimport {newProgress} from '../../actions/backup/progressSpinner.js'\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type BackupItem, listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {humanFileSize} from '../../util/humanFileSize.js'\nimport {isPathDirName} from '../../util/isPathDirName.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst DEFAULT_DOWNLOAD_CONCURRENCY = 10\nconst MAX_DOWNLOAD_CONCURRENCY = 24\n\ninterface DownloadBackupOptions {\n backupId: string\n concurrency: number\n datasetName: string\n outDir: string\n outFileName: string\n overwrite: boolean\n projectId: string\n}\n\nexport class DownloadBackupCommand extends SanityCommand<typeof DownloadBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to download backup from',\n required: false,\n }),\n }\n\n static override description = 'Download a dataset backup to a local file'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively download a backup',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-1',\n description: 'Download a specific backup for the production dataset',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-2 --out /path/to/file',\n description: 'Download backup to a specific file',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-3 --out /path/to/file --overwrite',\n description: 'Download backup and overwrite existing file',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to download backup from',\n semantics: 'override',\n }),\n 'backup-id': Flags.string({\n description: 'The backup ID to download',\n }),\n concurrency: Flags.integer({\n default: DEFAULT_DOWNLOAD_CONCURRENCY,\n description: `Concurrent number of backup item downloads (max: ${MAX_DOWNLOAD_CONCURRENCY})`,\n }),\n out: Flags.string({\n description: 'The file or directory path the backup should download to',\n }),\n overwrite: Flags.boolean({\n default: false,\n description: 'Allows overwriting of existing backup file',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:download']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(DownloadBackupCommand)\n let {dataset} = args\n\n if (this.isUnattended()) {\n const errors: string[] = []\n\n if (!dataset) {\n errors.push('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.')\n }\n if (!flags['backup-id']) {\n errors.push('Backup ID is required in unattended mode. Pass it with `--backup-id <id>`.')\n }\n\n if (errors.length > 0) {\n this.error(formatCliErrorMessages(errors), {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else {\n dataset = await promptForDataset({allowCreation: false, datasets})\n }\n\n const opts = await this.prepareBackupOptions(projectId, dataset)\n const outFilePath = path.join(opts.outDir, opts.outFileName)\n\n this.log(\n boxen(\n `Downloading backup for:\n\n${styleText('bold', 'projectId')}: ${styleText('cyan', opts.projectId)}\n${styleText('bold', 'dataset')}: ${styleText('cyan', opts.datasetName)}\n${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`,\n {\n borderColor: 'cyan',\n borderStyle: 'round',\n padding: 1,\n },\n ),\n )\n this.log('')\n this.log(`Downloading backup to \"${styleText('cyan', outFilePath)}\"`)\n\n const start = Date.now()\n const progressSpinner = newProgress('Setting up backup environment...')\n\n // Create a unique temporary directory to store files before bundling them into the archive at outputPath.\n // Temporary directories are normally deleted at the end of backup process, any unexpected exit may leave them\n // behind, hence it is important to create a unique directory for each attempt.\n const tmpOutDir = await mkdtemp(path.join(tmpdir(), `sanity-backup-`))\n\n // Create required directories if they don't exist.\n for (const dir of [\n opts.outDir,\n path.join(tmpOutDir, 'images'),\n path.join(tmpOutDir, 'files'),\n ]) {\n await mkdir(dir, {recursive: true})\n }\n\n backupDownloadDebug('Writing to temporary directory %s', tmpOutDir)\n const tmpOutDocumentsFile = path.join(tmpOutDir, 'data.ndjson')\n\n const docOutStream = createWriteStream(tmpOutDocumentsFile)\n\n try {\n const backupFileStream = new PaginatedGetBackupStream(\n opts.projectId,\n opts.datasetName,\n opts.backupId,\n )\n\n const files: File[] = []\n let i = 0\n for await (const file of backupFileStream) {\n files.push(file)\n i++\n progressSpinner.set({\n current: i,\n step: `Reading backup files...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n }\n\n let totalItemsDownloaded = 0\n await pMap(\n files,\n async (file: File) => {\n if (file.type === 'file' || file.type === 'image') {\n await downloadAsset(file.url, file.name, file.type, tmpOutDir)\n } else {\n const doc = await downloadDocument(file.url)\n docOutStream.write(`${doc}\\n`)\n }\n\n totalItemsDownloaded += 1\n progressSpinner.set({\n current: totalItemsDownloaded,\n step: `Downloading documents and assets...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n },\n {concurrency: opts.concurrency},\n )\n } catch (error) {\n progressSpinner.fail()\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Downloading dataset backup failed: ${message}`, error)\n this.error(`Downloading dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n docOutStream.end()\n await finished(docOutStream)\n\n progressSpinner.set({step: `Archiving files into a tarball...`, update: true})\n try {\n await archiveDir(tmpOutDir, outFilePath, (processedBytes: number) => {\n progressSpinner.update({\n step: `Archiving files into a tarball, ${humanFileSize(processedBytes)} bytes written...`,\n })\n })\n } catch (err) {\n progressSpinner.fail()\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Archiving backup failed: ${message}`, err)\n this.error(`Archiving backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n progressSpinner.set({\n step: `Cleaning up temporary files at ${styleText('cyan', `${tmpOutDir}`)}`,\n })\n await cleanupTmpDir(tmpOutDir)\n\n progressSpinner.set({\n step: `Backup download complete [${prettyMs(Date.now() - start)}]`,\n })\n progressSpinner.succeed()\n }\n\n private async getOutputPath(defaultOutFileName: string): Promise<string> {\n if (this.flags.out !== undefined) {\n // Rewrite the output path to an absolute path, if it is not already.\n return path.resolve(this.flags.out)\n }\n\n const workDir = process.cwd()\n const defaultPath = path.join(workDir, defaultOutFileName)\n if (this.isUnattended()) {\n return defaultPath\n }\n\n const inputResult = await input({\n default: defaultPath,\n message: 'Output path:',\n })\n return path.resolve(inputResult)\n }\n\n private async prepareBackupOptions(\n projectId: string,\n datasetName: string,\n ): Promise<DownloadBackupOptions> {\n const err = validateDatasetName(datasetName)\n if (err) {\n this.error(err, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const backupId = String(\n this.flags['backup-id'] || (await this.promptForBackupId(projectId, datasetName)),\n )\n\n if (\n 'concurrency' in this.flags &&\n (this.flags.concurrency < 1 || this.flags.concurrency > MAX_DOWNLOAD_CONCURRENCY)\n ) {\n this.error(`\\`--concurrency\\` must be between 1 and ${MAX_DOWNLOAD_CONCURRENCY}.`, {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const defaultOutFileName = `${datasetName}-backup-${backupId}.tar.gz`\n let out = await this.getOutputPath(defaultOutFileName)\n\n // If path is a directory name, then add a default file name to the path.\n if (isPathDirName(out)) {\n out = path.join(out, defaultOutFileName)\n }\n\n const exists = await access(out).then(\n () => true,\n () => false,\n )\n // If the file already exists, ask for confirmation if it should be overwritten.\n if (!this.flags.overwrite && exists) {\n if (this.isUnattended()) {\n this.error(`File \"${out}\" already exists. Pass \\`--overwrite\\` to replace it.`, {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const shouldOverwrite = await confirm({\n default: false,\n message: `File \"${out}\" already exists, would you like to overwrite it?`,\n })\n\n // If the user does not want to overwrite the file, cancel the operation.\n if (!shouldOverwrite) {\n this.error('Operation cancelled.', {exit: exitCodes.USER_ABORT})\n }\n }\n\n return {\n backupId,\n concurrency: this.flags.concurrency || DEFAULT_DOWNLOAD_CONCURRENCY,\n datasetName,\n outDir: path.dirname(out),\n outFileName: path.basename(out),\n overwrite: this.flags.overwrite,\n projectId,\n }\n }\n\n private async promptForBackupId(projectId: string, datasetName: string): Promise<string> {\n const maxBackupIdsShown = 100\n\n try {\n const response = await listBackups({\n datasetName,\n limit: maxBackupIdsShown,\n projectId,\n })\n\n if (!response?.backups?.length) {\n this.error('No backups found', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const backupIdChoices = response.backups.map((backup: BackupItem) => ({\n name: backup.id,\n value: backup.id,\n }))\n\n const hint =\n backupIdChoices.length === maxBackupIdsShown\n ? ` (only last ${maxBackupIdsShown} shown)`\n : ''\n\n return select({\n choices: backupIdChoices,\n message: `Select backup ID to use${hint}`,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Failed to fetch backups for dataset ${datasetName}: ${message}`, err)\n this.error(`Failed to fetch backups for dataset ${datasetName}: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["createWriteStream","access","mkdir","mkdtemp","tmpdir","path","finished","styleText","Args","Flags","exitCodes","SanityCommand","boxen","confirm","input","select","pMap","prettyMs","archiveDir","assertDatasetExists","backupDownloadDebug","cleanupTmpDir","downloadAsset","downloadDocument","PaginatedGetBackupStream","newProgress","validateDatasetName","promptForDataset","promptForProject","listBackups","listDatasets","formatCliErrorMessages","humanFileSize","isPathDirName","getProjectIdFlag","DEFAULT_DOWNLOAD_CONCURRENCY","MAX_DOWNLOAD_CONCURRENCY","DownloadBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","concurrency","integer","default","out","overwrite","boolean","hiddenAliases","run","parse","isUnattended","errors","push","length","error","exit","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","message","Error","String","RUNTIME_ERROR","output","allowCreation","opts","prepareBackupOptions","outFilePath","join","outDir","outFileName","log","datasetName","backupId","borderColor","borderStyle","padding","start","Date","now","progressSpinner","tmpOutDir","dir","recursive","tmpOutDocumentsFile","docOutStream","backupFileStream","files","i","file","set","current","step","total","totalFiles","update","totalItemsDownloaded","type","url","name","doc","write","fail","end","processedBytes","err","succeed","getOutputPath","defaultOutFileName","undefined","resolve","workDir","process","cwd","defaultPath","inputResult","promptForBackupId","exists","then","shouldOverwrite","USER_ABORT","dirname","basename","maxBackupIdsShown","response","limit","backups","backupIdChoices","map","backup","id","value","hint","choices"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backups/download.ts"],"sourcesContent":["import {createWriteStream} from 'node:fs'\nimport {access, mkdir, mkdtemp} from 'node:fs/promises'\nimport {tmpdir} from 'node:os'\nimport path from 'node:path'\nimport {finished} from 'node:stream/promises'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\nimport {requiredWhenUnattended} from '@sanity/cli-core/flags'\nimport {boxen, confirm, input, select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport pMap from 'p-map'\nimport prettyMs from 'pretty-ms'\n\nimport {archiveDir} from '../../actions/backup/archiveDir.js'\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {backupDownloadDebug} from '../../actions/backup/backupDownloadDebug.js'\nimport {cleanupTmpDir} from '../../actions/backup/cleanupTmpDir.js'\nimport {downloadAsset} from '../../actions/backup/downloadAsset.js'\nimport {downloadDocument} from '../../actions/backup/downloadDocument.js'\nimport {type File, PaginatedGetBackupStream} from '../../actions/backup/fetchNextBackupPage.js'\nimport {newProgress} from '../../actions/backup/progressSpinner.js'\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type BackupItem, listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {humanFileSize} from '../../util/humanFileSize.js'\nimport {isPathDirName} from '../../util/isPathDirName.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst DEFAULT_DOWNLOAD_CONCURRENCY = 10\nconst MAX_DOWNLOAD_CONCURRENCY = 24\n\ninterface DownloadBackupOptions {\n backupId: string\n concurrency: number\n datasetName: string\n outDir: string\n outFileName: string\n overwrite: boolean\n projectId: string\n}\n\nexport class DownloadBackupCommand extends SanityCommand<typeof DownloadBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to download backup from',\n required: false,\n }),\n }\n\n static override description = 'Download a dataset backup to a local file'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively download a backup',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-1',\n description: 'Download a specific backup for the production dataset',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-2 --out /path/to/file',\n description: 'Download backup to a specific file',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-3 --out /path/to/file --overwrite',\n description: 'Download backup and overwrite existing file',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to download backup from',\n semantics: 'override',\n }),\n 'backup-id': requiredWhenUnattended(\n Flags.string({\n description: 'The backup ID to download',\n }),\n ),\n concurrency: Flags.integer({\n default: DEFAULT_DOWNLOAD_CONCURRENCY,\n description: `Concurrent number of backup item downloads (max: ${MAX_DOWNLOAD_CONCURRENCY})`,\n }),\n out: Flags.string({\n description: 'The file or directory path the backup should download to',\n }),\n overwrite: Flags.boolean({\n default: false,\n description: 'Allows overwriting of existing backup file',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:download']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(DownloadBackupCommand)\n let {dataset} = args\n\n if (this.isUnattended() && !dataset) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else {\n dataset = await promptForDataset({allowCreation: false, datasets})\n }\n\n const opts = await this.prepareBackupOptions(projectId, dataset)\n const outFilePath = path.join(opts.outDir, opts.outFileName)\n\n this.log(\n boxen(\n `Downloading backup for:\n\n${styleText('bold', 'projectId')}: ${styleText('cyan', opts.projectId)}\n${styleText('bold', 'dataset')}: ${styleText('cyan', opts.datasetName)}\n${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`,\n {\n borderColor: 'cyan',\n borderStyle: 'round',\n padding: 1,\n },\n ),\n )\n this.log('')\n this.log(`Downloading backup to \"${styleText('cyan', outFilePath)}\"`)\n\n const start = Date.now()\n const progressSpinner = newProgress('Setting up backup environment...')\n\n // Create a unique temporary directory to store files before bundling them into the archive at outputPath.\n // Temporary directories are normally deleted at the end of backup process, any unexpected exit may leave them\n // behind, hence it is important to create a unique directory for each attempt.\n const tmpOutDir = await mkdtemp(path.join(tmpdir(), `sanity-backup-`))\n\n // Create required directories if they don't exist.\n for (const dir of [\n opts.outDir,\n path.join(tmpOutDir, 'images'),\n path.join(tmpOutDir, 'files'),\n ]) {\n await mkdir(dir, {recursive: true})\n }\n\n backupDownloadDebug('Writing to temporary directory %s', tmpOutDir)\n const tmpOutDocumentsFile = path.join(tmpOutDir, 'data.ndjson')\n\n const docOutStream = createWriteStream(tmpOutDocumentsFile)\n\n try {\n const backupFileStream = new PaginatedGetBackupStream(\n opts.projectId,\n opts.datasetName,\n opts.backupId,\n )\n\n const files: File[] = []\n let i = 0\n for await (const file of backupFileStream) {\n files.push(file)\n i++\n progressSpinner.set({\n current: i,\n step: `Reading backup files...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n }\n\n let totalItemsDownloaded = 0\n await pMap(\n files,\n async (file: File) => {\n if (file.type === 'file' || file.type === 'image') {\n await downloadAsset(file.url, file.name, file.type, tmpOutDir)\n } else {\n const doc = await downloadDocument(file.url)\n docOutStream.write(`${doc}\\n`)\n }\n\n totalItemsDownloaded += 1\n progressSpinner.set({\n current: totalItemsDownloaded,\n step: `Downloading documents and assets...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n },\n {concurrency: opts.concurrency},\n )\n } catch (error) {\n progressSpinner.fail()\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Downloading dataset backup failed: ${message}`, error)\n this.error(`Downloading dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n docOutStream.end()\n await finished(docOutStream)\n\n progressSpinner.set({step: `Archiving files into a tarball...`, update: true})\n try {\n await archiveDir(tmpOutDir, outFilePath, (processedBytes: number) => {\n progressSpinner.update({\n step: `Archiving files into a tarball, ${humanFileSize(processedBytes)} bytes written...`,\n })\n })\n } catch (err) {\n progressSpinner.fail()\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Archiving backup failed: ${message}`, err)\n this.error(`Archiving backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n progressSpinner.set({\n step: `Cleaning up temporary files at ${styleText('cyan', `${tmpOutDir}`)}`,\n })\n await cleanupTmpDir(tmpOutDir)\n\n progressSpinner.set({\n step: `Backup download complete [${prettyMs(Date.now() - start)}]`,\n })\n progressSpinner.succeed()\n }\n\n private async getOutputPath(defaultOutFileName: string): Promise<string> {\n if (this.flags.out !== undefined) {\n // Rewrite the output path to an absolute path, if it is not already.\n return path.resolve(this.flags.out)\n }\n\n const workDir = process.cwd()\n const defaultPath = path.join(workDir, defaultOutFileName)\n if (this.isUnattended()) {\n return defaultPath\n }\n\n const inputResult = await input({\n default: defaultPath,\n message: 'Output path:',\n })\n return path.resolve(inputResult)\n }\n\n private async prepareBackupOptions(\n projectId: string,\n datasetName: string,\n ): Promise<DownloadBackupOptions> {\n const err = validateDatasetName(datasetName)\n if (err) {\n this.error(err, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const backupId = String(\n this.flags['backup-id'] || (await this.promptForBackupId(projectId, datasetName)),\n )\n\n if (\n 'concurrency' in this.flags &&\n (this.flags.concurrency < 1 || this.flags.concurrency > MAX_DOWNLOAD_CONCURRENCY)\n ) {\n this.error(`\\`--concurrency\\` must be between 1 and ${MAX_DOWNLOAD_CONCURRENCY}.`, {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const defaultOutFileName = `${datasetName}-backup-${backupId}.tar.gz`\n let out = await this.getOutputPath(defaultOutFileName)\n\n // If path is a directory name, then add a default file name to the path.\n if (isPathDirName(out)) {\n out = path.join(out, defaultOutFileName)\n }\n\n const exists = await access(out).then(\n () => true,\n () => false,\n )\n // If the file already exists, ask for confirmation if it should be overwritten.\n if (!this.flags.overwrite && exists) {\n if (this.isUnattended()) {\n this.error(`File \"${out}\" already exists. Pass \\`--overwrite\\` to replace it.`, {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const shouldOverwrite = await confirm({\n default: false,\n message: `File \"${out}\" already exists, would you like to overwrite it?`,\n })\n\n // If the user does not want to overwrite the file, cancel the operation.\n if (!shouldOverwrite) {\n this.error('Operation cancelled.', {exit: exitCodes.USER_ABORT})\n }\n }\n\n return {\n backupId,\n concurrency: this.flags.concurrency || DEFAULT_DOWNLOAD_CONCURRENCY,\n datasetName,\n outDir: path.dirname(out),\n outFileName: path.basename(out),\n overwrite: this.flags.overwrite,\n projectId,\n }\n }\n\n private async promptForBackupId(projectId: string, datasetName: string): Promise<string> {\n const maxBackupIdsShown = 100\n\n try {\n const response = await listBackups({\n datasetName,\n limit: maxBackupIdsShown,\n projectId,\n })\n\n if (!response?.backups?.length) {\n this.error('No backups found', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const backupIdChoices = response.backups.map((backup: BackupItem) => ({\n name: backup.id,\n value: backup.id,\n }))\n\n const hint =\n backupIdChoices.length === maxBackupIdsShown\n ? ` (only last ${maxBackupIdsShown} shown)`\n : ''\n\n return select({\n choices: backupIdChoices,\n message: `Select backup ID to use${hint}`,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Failed to fetch backups for dataset ${datasetName}: ${message}`, err)\n this.error(`Failed to fetch backups for dataset ${datasetName}: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["createWriteStream","access","mkdir","mkdtemp","tmpdir","path","finished","styleText","Args","Flags","exitCodes","SanityCommand","requiredWhenUnattended","boxen","confirm","input","select","pMap","prettyMs","archiveDir","assertDatasetExists","backupDownloadDebug","cleanupTmpDir","downloadAsset","downloadDocument","PaginatedGetBackupStream","newProgress","validateDatasetName","promptForDataset","promptForProject","listBackups","listDatasets","humanFileSize","isPathDirName","getProjectIdFlag","DEFAULT_DOWNLOAD_CONCURRENCY","MAX_DOWNLOAD_CONCURRENCY","DownloadBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","concurrency","integer","default","out","overwrite","boolean","hiddenAliases","run","parse","isUnattended","error","exit","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","message","Error","String","RUNTIME_ERROR","length","output","allowCreation","opts","prepareBackupOptions","outFilePath","join","outDir","outFileName","log","datasetName","backupId","borderColor","borderStyle","padding","start","Date","now","progressSpinner","tmpOutDir","dir","recursive","tmpOutDocumentsFile","docOutStream","backupFileStream","files","i","file","push","set","current","step","total","totalFiles","update","totalItemsDownloaded","type","url","name","doc","write","fail","end","processedBytes","err","succeed","getOutputPath","defaultOutFileName","undefined","resolve","workDir","process","cwd","defaultPath","inputResult","promptForBackupId","exists","then","shouldOverwrite","USER_ABORT","dirname","basename","maxBackupIdsShown","response","limit","backups","backupIdChoices","map","backup","id","value","hint","choices"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
|
|
3
|
+
import { formatDateTime, parseDateOnly } from '@sanity/cli-core/dates';
|
|
3
4
|
import { select } from '@sanity/cli-core/ux';
|
|
4
5
|
import { assertDatasetExists } from '../../actions/backup/assertDatasetExist.js';
|
|
5
6
|
import { promptForProject } from '../../prompts/promptForProject.js';
|
|
6
7
|
import { listBackups } from '../../services/backup.js';
|
|
7
8
|
import { listDatasets } from '../../services/datasets.js';
|
|
8
|
-
import { formatDateTime, parseDateOnly } from '../../util/dates.js';
|
|
9
9
|
import { Table } from '../../util/responsiveTable.js';
|
|
10
10
|
import { getProjectIdFlag } from '../../util/sharedFlags.js';
|
|
11
11
|
const listBackupDebug = subdebug('backup:list');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {formatDateTime, parseDateOnly} from '../../util/dates.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listBackupDebug = subdebug('backup:list')\n\nconst DEFAULT_LIST_BACKUP_LIMIT = 30\n\ntype ListBackupRequestQueryParams = {\n after?: string\n before?: string\n limit: string\n}\n\nexport class ListBackupCommand extends SanityCommand<typeof ListBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to list backups for',\n required: false,\n }),\n }\n\n static override description = 'List available backups for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List backups for a dataset interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'List backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --limit 50',\n description: 'List up to 50 backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --after 2024-01-31 --limit 10',\n description: 'List up to 10 backups created after 2024-01-31',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list backups for',\n semantics: 'override',\n }),\n after: Flags.string({\n description: 'Only return backups after this date (inclusive, YYYY-MM-DD format)',\n }),\n before: Flags.string({\n description: 'Only return backups before this date (exclusive, YYYY-MM-DD format)',\n }),\n limit: Flags.integer({\n char: 'l',\n default: DEFAULT_LIST_BACKUP_LIMIT,\n description: 'Maximum number of backups returned',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:list']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ListBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && parsedAfter.getTime() > parsedBefore.getTime()) {\n this.error('--after date must be before --before', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await listBackups({\n after: flags.after,\n before: flags.before,\n datasetName: dataset,\n limit: flags.limit,\n projectId,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: formatDateTime(createdAt),\n resource: 'Dataset',\n })\n }\n\n this.log(table.render())\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parseDateOnly(date)\n if (parsedDate) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","select","assertDatasetExists","promptForProject","listBackups","listDatasets","formatDateTime","parseDateOnly","Table","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","promptForDataset","parsedBefore","processDateFlag","parsedAfter","getTime","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","resource","render","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {formatDateTime, parseDateOnly} from '@sanity/cli-core/dates'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listBackupDebug = subdebug('backup:list')\n\nconst DEFAULT_LIST_BACKUP_LIMIT = 30\n\ntype ListBackupRequestQueryParams = {\n after?: string\n before?: string\n limit: string\n}\n\nexport class ListBackupCommand extends SanityCommand<typeof ListBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to list backups for',\n required: false,\n }),\n }\n\n static override description = 'List available backups for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List backups for a dataset interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'List backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --limit 50',\n description: 'List up to 50 backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --after 2024-01-31 --limit 10',\n description: 'List up to 10 backups created after 2024-01-31',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list backups for',\n semantics: 'override',\n }),\n after: Flags.string({\n description: 'Only return backups after this date (inclusive, YYYY-MM-DD format)',\n }),\n before: Flags.string({\n description: 'Only return backups before this date (exclusive, YYYY-MM-DD format)',\n }),\n limit: Flags.integer({\n char: 'l',\n default: DEFAULT_LIST_BACKUP_LIMIT,\n description: 'Maximum number of backups returned',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:list']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ListBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && parsedAfter.getTime() > parsedBefore.getTime()) {\n this.error('--after date must be before --before', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await listBackups({\n after: flags.after,\n before: flags.before,\n datasetName: dataset,\n limit: flags.limit,\n projectId,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: formatDateTime(createdAt),\n resource: 'Dataset',\n })\n }\n\n this.log(table.render())\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parseDateOnly(date)\n if (parsedDate) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","formatDateTime","parseDateOnly","select","assertDatasetExists","promptForProject","listBackups","listDatasets","Table","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","promptForDataset","parsedBefore","processDateFlag","parsedAfter","getTime","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","resource","render","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
|
|
3
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
4
|
+
import { spinner } from '@sanity/cli-core/ux';
|
|
5
|
+
import { isHttpError } from '@sanity/client';
|
|
6
|
+
import { watchJob } from '../../actions/context/watchJob.js';
|
|
7
|
+
import { formatKeyValue } from '../../actions/debug/output.js';
|
|
8
|
+
import { buildKnowledgeBase, cancelKnowledgeBaseBuild } from '../../services/context.js';
|
|
9
|
+
const buildContextDebug = subdebug('context:build');
|
|
10
|
+
export class BuildKnowledgeBaseCommand extends SanityCommand {
|
|
11
|
+
static args = {
|
|
12
|
+
knowledgeBaseId: Args.string({
|
|
13
|
+
description: 'Knowledge base ID',
|
|
14
|
+
required: true
|
|
15
|
+
})
|
|
16
|
+
};
|
|
17
|
+
static description = 'Build a knowledge base from its imported content';
|
|
18
|
+
static examples = [
|
|
19
|
+
{
|
|
20
|
+
command: '<%= config.bin %> <%= command.id %> kb-abc123',
|
|
21
|
+
description: 'Start a build and return the job ID'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
command: '<%= config.bin %> <%= command.id %> kb-abc123 --watch',
|
|
25
|
+
description: 'Start a build and wait for it to finish (non-zero exit on failure)'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
command: '<%= config.bin %> <%= command.id %> kb-abc123 --cancel',
|
|
29
|
+
description: 'Cancel the running build, if any'
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
static flags = {
|
|
33
|
+
cancel: Flags.boolean({
|
|
34
|
+
default: false,
|
|
35
|
+
description: 'Cancel the running build instead of starting one',
|
|
36
|
+
exclusive: [
|
|
37
|
+
'watch'
|
|
38
|
+
]
|
|
39
|
+
}),
|
|
40
|
+
watch: Flags.boolean({
|
|
41
|
+
default: false,
|
|
42
|
+
description: 'Wait for the build to finish, exiting non-zero if it fails'
|
|
43
|
+
})
|
|
44
|
+
};
|
|
45
|
+
async run() {
|
|
46
|
+
const { knowledgeBaseId } = this.args;
|
|
47
|
+
const { cancel, watch } = this.flags;
|
|
48
|
+
if (cancel) {
|
|
49
|
+
await this.cancelBuild(knowledgeBaseId);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
let jobId;
|
|
53
|
+
const spin = spinner('Starting build').start();
|
|
54
|
+
try {
|
|
55
|
+
const accepted = await buildKnowledgeBase(knowledgeBaseId);
|
|
56
|
+
jobId = accepted.jobId;
|
|
57
|
+
spin.succeed('Build started');
|
|
58
|
+
} catch (error) {
|
|
59
|
+
spin.fail();
|
|
60
|
+
buildContextDebug('Error starting build', error);
|
|
61
|
+
if (isHttpError(error) && error.statusCode === 404) {
|
|
62
|
+
this.error(`Knowledge base "${knowledgeBaseId}" not found`, {
|
|
63
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
this.error(`Failed to start build: ${getErrorMessage(error)}`, {
|
|
67
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
this.log(formatKeyValue('Job ID', jobId));
|
|
71
|
+
this.log(`Track it with: sanity context jobs get ${knowledgeBaseId} ${jobId}`);
|
|
72
|
+
if (!watch) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let job;
|
|
76
|
+
try {
|
|
77
|
+
job = await watchJob(knowledgeBaseId, jobId, {
|
|
78
|
+
label: 'Building knowledge base',
|
|
79
|
+
successText: 'Build succeeded'
|
|
80
|
+
});
|
|
81
|
+
} catch (error) {
|
|
82
|
+
buildContextDebug('Error watching build job', error);
|
|
83
|
+
this.error(`Failed to watch build job: ${getErrorMessage(error)}`, {
|
|
84
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (job.status !== 'succeeded') {
|
|
88
|
+
this.error(`Build ${job.status}${job.error ? `: ${job.error}` : ''}`, {
|
|
89
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async cancelBuild(knowledgeBaseId) {
|
|
94
|
+
try {
|
|
95
|
+
const { cancelled } = await cancelKnowledgeBaseBuild(knowledgeBaseId);
|
|
96
|
+
this.log(cancelled ? 'Build cancelled' : 'No running build to cancel');
|
|
97
|
+
} catch (error) {
|
|
98
|
+
buildContextDebug('Error cancelling build', error);
|
|
99
|
+
if (isHttpError(error) && error.statusCode === 404) {
|
|
100
|
+
this.error(`Knowledge base "${knowledgeBaseId}" not found`, {
|
|
101
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
this.error(`Failed to cancel build: ${getErrorMessage(error)}`, {
|
|
105
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/context/build.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {isHttpError} from '@sanity/client'\n\nimport {watchJob} from '../../actions/context/watchJob.js'\nimport {formatKeyValue} from '../../actions/debug/output.js'\nimport {buildKnowledgeBase, cancelKnowledgeBaseBuild} from '../../services/context.js'\n\nconst buildContextDebug = subdebug('context:build')\n\nexport class BuildKnowledgeBaseCommand extends SanityCommand<typeof BuildKnowledgeBaseCommand> {\n static override args = {\n knowledgeBaseId: Args.string({\n description: 'Knowledge base ID',\n required: true,\n }),\n }\n\n static override description = 'Build a knowledge base from its imported content'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123',\n description: 'Start a build and return the job ID',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --watch',\n description: 'Start a build and wait for it to finish (non-zero exit on failure)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --cancel',\n description: 'Cancel the running build, if any',\n },\n ]\n\n static override flags = {\n cancel: Flags.boolean({\n default: false,\n description: 'Cancel the running build instead of starting one',\n exclusive: ['watch'],\n }),\n watch: Flags.boolean({\n default: false,\n description: 'Wait for the build to finish, exiting non-zero if it fails',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {knowledgeBaseId} = this.args\n const {cancel, watch} = this.flags\n\n if (cancel) {\n await this.cancelBuild(knowledgeBaseId)\n return\n }\n\n let jobId: string\n const spin = spinner('Starting build').start()\n try {\n const accepted = await buildKnowledgeBase(knowledgeBaseId)\n jobId = accepted.jobId\n spin.succeed('Build started')\n } catch (error) {\n spin.fail()\n buildContextDebug('Error starting build', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`Knowledge base \"${knowledgeBaseId}\" not found`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n this.error(`Failed to start build: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n this.log(formatKeyValue('Job ID', jobId))\n this.log(`Track it with: sanity context jobs get ${knowledgeBaseId} ${jobId}`)\n\n if (!watch) {\n return\n }\n\n let job\n try {\n job = await watchJob(knowledgeBaseId, jobId, {\n label: 'Building knowledge base',\n successText: 'Build succeeded',\n })\n } catch (error) {\n buildContextDebug('Error watching build job', error)\n this.error(`Failed to watch build job: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (job.status !== 'succeeded') {\n this.error(`Build ${job.status}${job.error ? `: ${job.error}` : ''}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async cancelBuild(knowledgeBaseId: string): Promise<void> {\n try {\n const {cancelled} = await cancelKnowledgeBaseBuild(knowledgeBaseId)\n this.log(cancelled ? 'Build cancelled' : 'No running build to cancel')\n } catch (error) {\n buildContextDebug('Error cancelling build', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`Knowledge base \"${knowledgeBaseId}\" not found`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n this.error(`Failed to cancel build: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","spinner","isHttpError","watchJob","formatKeyValue","buildKnowledgeBase","cancelKnowledgeBaseBuild","buildContextDebug","BuildKnowledgeBaseCommand","args","knowledgeBaseId","string","description","required","examples","command","flags","cancel","boolean","default","exclusive","watch","run","cancelBuild","jobId","spin","start","accepted","succeed","error","fail","statusCode","exit","RUNTIME_ERROR","log","job","label","successText","status","cancelled"],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
|
|
3
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
4
|
+
import { requiredWhenUnattended } from '@sanity/cli-core/flags';
|
|
5
|
+
import { input, spinner } from '@sanity/cli-core/ux';
|
|
6
|
+
import { MissingOrganizationError, resolveOrganizationId } from '../../actions/context/resolveOrganizationId.js';
|
|
7
|
+
import { formatKeyValue } from '../../actions/debug/output.js';
|
|
8
|
+
import { createKnowledgeBase } from '../../services/context.js';
|
|
9
|
+
import { getOrganizationFlag } from '../../util/sharedFlags.js';
|
|
10
|
+
import { defineCommandTelemetry } from '../../util/telemetry/commandTelemetry.js';
|
|
11
|
+
const createContextDebug = subdebug('context:create');
|
|
12
|
+
const flags = {
|
|
13
|
+
...getOrganizationFlag({
|
|
14
|
+
description: 'Organization to create the knowledge base in',
|
|
15
|
+
semantics: 'override'
|
|
16
|
+
}),
|
|
17
|
+
description: requiredWhenUnattended(Flags.string({
|
|
18
|
+
description: 'Knowledge base description'
|
|
19
|
+
})),
|
|
20
|
+
title: requiredWhenUnattended(Flags.string({
|
|
21
|
+
description: 'Knowledge base title'
|
|
22
|
+
}))
|
|
23
|
+
};
|
|
24
|
+
export class CreateKnowledgeBaseCommand extends SanityCommand {
|
|
25
|
+
static description = 'Create a knowledge base';
|
|
26
|
+
static examples = [
|
|
27
|
+
{
|
|
28
|
+
command: '<%= config.bin %> <%= command.id %>',
|
|
29
|
+
description: 'Interactively create a knowledge base'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
command: '<%= config.bin %> <%= command.id %> --organization org-abc123 --title "Support docs" --description "Product docs and troubleshooting guides"',
|
|
33
|
+
description: 'Create a knowledge base in a specific organization'
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
static flags = flags;
|
|
37
|
+
static telemetry = defineCommandTelemetry(flags, {
|
|
38
|
+
redact: [
|
|
39
|
+
'title',
|
|
40
|
+
'description'
|
|
41
|
+
]
|
|
42
|
+
});
|
|
43
|
+
async run() {
|
|
44
|
+
const { description: descriptionFlag, organization, title: titleFlag } = this.flags;
|
|
45
|
+
if (titleFlag !== undefined && titleFlag.trim() === '') {
|
|
46
|
+
this.error('Title cannot be empty', {
|
|
47
|
+
exit: exitCodes.USAGE_ERROR
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (descriptionFlag !== undefined && descriptionFlag.trim() === '') {
|
|
51
|
+
this.error('Description cannot be empty', {
|
|
52
|
+
exit: exitCodes.USAGE_ERROR
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
let organizationId;
|
|
56
|
+
try {
|
|
57
|
+
organizationId = await resolveOrganizationId({
|
|
58
|
+
configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,
|
|
59
|
+
flagOrganizationId: organization,
|
|
60
|
+
unattended: this.isUnattended()
|
|
61
|
+
});
|
|
62
|
+
} catch (error) {
|
|
63
|
+
createContextDebug('Error resolving organization', error);
|
|
64
|
+
this.error(getErrorMessage(error), {
|
|
65
|
+
exit: error instanceof MissingOrganizationError ? exitCodes.USAGE_ERROR : exitCodes.RUNTIME_ERROR
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const title = titleFlag?.trim() || await promptForText('Knowledge base title:');
|
|
69
|
+
const description = descriptionFlag?.trim() || await promptForText('Knowledge base description:');
|
|
70
|
+
const spin = spinner('Creating knowledge base').start();
|
|
71
|
+
try {
|
|
72
|
+
const knowledgeBase = await createKnowledgeBase({
|
|
73
|
+
description,
|
|
74
|
+
organizationId,
|
|
75
|
+
title
|
|
76
|
+
});
|
|
77
|
+
spin.succeed('Knowledge base created');
|
|
78
|
+
const padTo = 12 // "Organization" is the longest key
|
|
79
|
+
;
|
|
80
|
+
this.log(formatKeyValue('ID', knowledgeBase.publicId, {
|
|
81
|
+
padTo
|
|
82
|
+
}));
|
|
83
|
+
this.log(formatKeyValue('Title', knowledgeBase.title, {
|
|
84
|
+
padTo
|
|
85
|
+
}));
|
|
86
|
+
this.log(formatKeyValue('Organization', knowledgeBase.organizationId, {
|
|
87
|
+
padTo
|
|
88
|
+
}));
|
|
89
|
+
} catch (error) {
|
|
90
|
+
spin.fail();
|
|
91
|
+
createContextDebug('Error creating knowledge base', error);
|
|
92
|
+
this.error(`Failed to create knowledge base: ${getErrorMessage(error)}`, {
|
|
93
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function promptForText(message) {
|
|
99
|
+
const value = await input({
|
|
100
|
+
message,
|
|
101
|
+
validate: (input_)=>input_.trim().length > 0 || 'Value cannot be empty'
|
|
102
|
+
});
|
|
103
|
+
return value.trim();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/context/create.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {requiredWhenUnattended} from '@sanity/cli-core/flags'\nimport {input, spinner} from '@sanity/cli-core/ux'\n\nimport {\n MissingOrganizationError,\n resolveOrganizationId,\n} from '../../actions/context/resolveOrganizationId.js'\nimport {formatKeyValue} from '../../actions/debug/output.js'\nimport {createKnowledgeBase} from '../../services/context.js'\nimport {getOrganizationFlag} from '../../util/sharedFlags.js'\nimport {defineCommandTelemetry} from '../../util/telemetry/commandTelemetry.js'\n\nconst createContextDebug = subdebug('context:create')\n\nconst flags = {\n ...getOrganizationFlag({\n description: 'Organization to create the knowledge base in',\n semantics: 'override',\n }),\n description: requiredWhenUnattended(\n Flags.string({\n description: 'Knowledge base description',\n }),\n ),\n title: requiredWhenUnattended(\n Flags.string({\n description: 'Knowledge base title',\n }),\n ),\n} satisfies FlagInput\n\nexport class CreateKnowledgeBaseCommand extends SanityCommand<typeof CreateKnowledgeBaseCommand> {\n static override description = 'Create a knowledge base'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively create a knowledge base',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --organization org-abc123 --title \"Support docs\" --description \"Product docs and troubleshooting guides\"',\n description: 'Create a knowledge base in a specific organization',\n },\n ]\n\n static override flags = flags\n\n static telemetry = defineCommandTelemetry(flags, {redact: ['title', 'description']})\n\n public async run(): Promise<void> {\n const {description: descriptionFlag, organization, title: titleFlag} = this.flags\n\n if (titleFlag !== undefined && titleFlag.trim() === '') {\n this.error('Title cannot be empty', {exit: exitCodes.USAGE_ERROR})\n }\n if (descriptionFlag !== undefined && descriptionFlag.trim() === '') {\n this.error('Description cannot be empty', {exit: exitCodes.USAGE_ERROR})\n }\n\n let organizationId: string\n try {\n organizationId = await resolveOrganizationId({\n configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,\n flagOrganizationId: organization,\n unattended: this.isUnattended(),\n })\n } catch (error) {\n createContextDebug('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 const title = titleFlag?.trim() || (await promptForText('Knowledge base title:'))\n const description =\n descriptionFlag?.trim() || (await promptForText('Knowledge base description:'))\n\n const spin = spinner('Creating knowledge base').start()\n try {\n const knowledgeBase = await createKnowledgeBase({description, organizationId, title})\n spin.succeed('Knowledge base created')\n const padTo = 12 // \"Organization\" is the longest key\n this.log(formatKeyValue('ID', knowledgeBase.publicId, {padTo}))\n this.log(formatKeyValue('Title', knowledgeBase.title, {padTo}))\n this.log(formatKeyValue('Organization', knowledgeBase.organizationId, {padTo}))\n } catch (error) {\n spin.fail()\n createContextDebug('Error creating knowledge base', error)\n this.error(`Failed to create knowledge base: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n\nasync function promptForText(message: string): Promise<string> {\n const value = await input({\n message,\n validate: (input_) => input_.trim().length > 0 || 'Value cannot be empty',\n })\n return value.trim()\n}\n"],"names":["Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","requiredWhenUnattended","input","spinner","MissingOrganizationError","resolveOrganizationId","formatKeyValue","createKnowledgeBase","getOrganizationFlag","defineCommandTelemetry","createContextDebug","flags","description","semantics","string","title","CreateKnowledgeBaseCommand","examples","command","telemetry","redact","run","descriptionFlag","organization","titleFlag","undefined","trim","error","exit","USAGE_ERROR","organizationId","configuredOrganizationId","tryGetCliConfig","app","flagOrganizationId","unattended","isUnattended","RUNTIME_ERROR","promptForText","spin","start","knowledgeBase","succeed","padTo","log","publicId","fail","message","value","validate","input_","length"],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
|
|
3
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
4
|
+
import { confirm } from '@sanity/cli-core/ux';
|
|
5
|
+
import { isHttpError } from '@sanity/client';
|
|
6
|
+
import { deleteKnowledgeBase } from '../../services/context.js';
|
|
7
|
+
const deleteContextDebug = subdebug('context:delete');
|
|
8
|
+
export class DeleteKnowledgeBaseCommand extends SanityCommand {
|
|
9
|
+
static args = {
|
|
10
|
+
knowledgeBaseId: Args.string({
|
|
11
|
+
description: 'Knowledge base ID',
|
|
12
|
+
required: true
|
|
13
|
+
})
|
|
14
|
+
};
|
|
15
|
+
static description = 'Delete a knowledge base and its generated content';
|
|
16
|
+
static examples = [
|
|
17
|
+
{
|
|
18
|
+
command: '<%= config.bin %> <%= command.id %> kb-abc123',
|
|
19
|
+
description: 'Delete a knowledge base after confirmation'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
command: '<%= config.bin %> <%= command.id %> kb-abc123 --yes',
|
|
23
|
+
description: 'Delete a knowledge base without confirmation prompt'
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
static flags = {
|
|
27
|
+
yes: Flags.boolean({
|
|
28
|
+
char: 'y',
|
|
29
|
+
default: false,
|
|
30
|
+
description: 'Skip confirmation prompt (unattended mode)'
|
|
31
|
+
})
|
|
32
|
+
};
|
|
33
|
+
async run() {
|
|
34
|
+
const { knowledgeBaseId } = this.args;
|
|
35
|
+
const { yes: skipConfirmation } = this.flags;
|
|
36
|
+
if (this.isUnattended() && !skipConfirmation) {
|
|
37
|
+
this.error('Deletion requires confirmation. Pass `--yes` to delete the knowledge base.', {
|
|
38
|
+
exit: exitCodes.USAGE_ERROR
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (!skipConfirmation) {
|
|
42
|
+
const confirmed = await confirm({
|
|
43
|
+
default: false,
|
|
44
|
+
message: `Delete knowledge base "${knowledgeBaseId}"? This permanently deletes its generated content.`
|
|
45
|
+
});
|
|
46
|
+
if (!confirmed) {
|
|
47
|
+
this.log('Knowledge base not deleted');
|
|
48
|
+
this.exit(exitCodes.USER_ABORT);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
await deleteKnowledgeBase(knowledgeBaseId);
|
|
53
|
+
this.log('Knowledge base deleted');
|
|
54
|
+
} catch (error) {
|
|
55
|
+
deleteContextDebug('Error deleting knowledge base', error);
|
|
56
|
+
if (isHttpError(error) && error.statusCode === 404) {
|
|
57
|
+
this.error(`Knowledge base "${knowledgeBaseId}" not found`, {
|
|
58
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
this.error(`Failed to delete knowledge base: ${getErrorMessage(error)}`, {
|
|
62
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/context/delete.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {confirm} from '@sanity/cli-core/ux'\nimport {isHttpError} from '@sanity/client'\n\nimport {deleteKnowledgeBase} from '../../services/context.js'\n\nconst deleteContextDebug = subdebug('context:delete')\n\nexport class DeleteKnowledgeBaseCommand extends SanityCommand<typeof DeleteKnowledgeBaseCommand> {\n static override args = {\n knowledgeBaseId: Args.string({\n description: 'Knowledge base ID',\n required: true,\n }),\n }\n\n static override description = 'Delete a knowledge base and its generated content'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123',\n description: 'Delete a knowledge base after confirmation',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --yes',\n description: 'Delete a knowledge base without confirmation prompt',\n },\n ]\n\n static override flags = {\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: 'Skip confirmation prompt (unattended mode)',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {knowledgeBaseId} = this.args\n const {yes: skipConfirmation} = this.flags\n\n if (this.isUnattended() && !skipConfirmation) {\n this.error('Deletion requires confirmation. Pass `--yes` to delete the knowledge base.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n if (!skipConfirmation) {\n const confirmed = await confirm({\n default: false,\n message: `Delete knowledge base \"${knowledgeBaseId}\"? This permanently deletes its generated content.`,\n })\n\n if (!confirmed) {\n this.log('Knowledge base not deleted')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n try {\n await deleteKnowledgeBase(knowledgeBaseId)\n this.log('Knowledge base deleted')\n } catch (error) {\n deleteContextDebug('Error deleting knowledge base', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`Knowledge base \"${knowledgeBaseId}\" not found`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n this.error(`Failed to delete knowledge base: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","confirm","isHttpError","deleteKnowledgeBase","deleteContextDebug","DeleteKnowledgeBaseCommand","args","knowledgeBaseId","string","description","required","examples","command","flags","yes","boolean","char","default","run","skipConfirmation","isUnattended","error","exit","USAGE_ERROR","confirmed","message","log","USER_ABORT","statusCode","RUNTIME_ERROR"],"mappings":"AAAA,SAAQA,IAAI,EAAEC,KAAK,QAAO,cAAa;AAEvC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,mBAAmB,QAAO,4BAA2B;AAE7D,MAAMC,qBAAqBL,SAAS;AAEpC,OAAO,MAAMM,mCAAmCP;IAC9C,OAAgBQ,OAAO;QACrBC,iBAAiBZ,KAAKa,MAAM,CAAC;YAC3BC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,oDAAmD;IAEjF,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtBC,KAAKlB,MAAMmB,OAAO,CAAC;YACjBC,MAAM;YACNC,SAAS;YACTR,aAAa;QACf;IACF,EAAqB;IAErB,MAAaS,MAAqB;QAChC,MAAM,EAACX,eAAe,EAAC,GAAG,IAAI,CAACD,IAAI;QACnC,MAAM,EAACQ,KAAKK,gBAAgB,EAAC,GAAG,IAAI,CAACN,KAAK;QAE1C,IAAI,IAAI,CAACO,YAAY,MAAM,CAACD,kBAAkB;YAC5C,IAAI,CAACE,KAAK,CAAC,8EAA8E;gBACvFC,MAAMzB,UAAU0B,WAAW;YAC7B;QACF;QAEA,IAAI,CAACJ,kBAAkB;YACrB,MAAMK,YAAY,MAAMvB,QAAQ;gBAC9BgB,SAAS;gBACTQ,SAAS,CAAC,uBAAuB,EAAElB,gBAAgB,kDAAkD,CAAC;YACxG;YAEA,IAAI,CAACiB,WAAW;gBACd,IAAI,CAACE,GAAG,CAAC;gBACT,IAAI,CAACJ,IAAI,CAACzB,UAAU8B,UAAU;YAChC;QACF;QAEA,IAAI;YACF,MAAMxB,oBAAoBI;YAC1B,IAAI,CAACmB,GAAG,CAAC;QACX,EAAE,OAAOL,OAAO;YACdjB,mBAAmB,iCAAiCiB;YACpD,IAAInB,YAAYmB,UAAUA,MAAMO,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACP,KAAK,CAAC,CAAC,gBAAgB,EAAEd,gBAAgB,WAAW,CAAC,EAAE;oBAC1De,MAAMzB,UAAUgC,aAAa;gBAC/B;YACF;YACA,IAAI,CAACR,KAAK,CAAC,CAAC,iCAAiC,EAAErB,gBAAgBqB,QAAQ,EAAE;gBACvEC,MAAMzB,UAAUgC,aAAa;YAC/B;QACF;IACF;AACF"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
|
|
3
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
4
|
+
import { isHttpError } from '@sanity/client';
|
|
5
|
+
import { formatKeyValue } from '../../actions/debug/output.js';
|
|
6
|
+
import { getKnowledgeBase } from '../../services/context.js';
|
|
7
|
+
const getContextDebug = subdebug('context:get');
|
|
8
|
+
export class GetKnowledgeBaseCommand extends SanityCommand {
|
|
9
|
+
static args = {
|
|
10
|
+
knowledgeBaseId: Args.string({
|
|
11
|
+
description: 'Knowledge base ID',
|
|
12
|
+
required: true
|
|
13
|
+
})
|
|
14
|
+
};
|
|
15
|
+
static description = 'Get details of a knowledge base';
|
|
16
|
+
static examples = [
|
|
17
|
+
{
|
|
18
|
+
command: '<%= config.bin %> <%= command.id %> kb-abc123',
|
|
19
|
+
description: 'Get details of a specific knowledge base'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
command: '<%= config.bin %> <%= command.id %> kb-abc123 --json',
|
|
23
|
+
description: 'Output the knowledge base as JSON'
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
static flags = {
|
|
27
|
+
json: Flags.boolean({
|
|
28
|
+
default: false,
|
|
29
|
+
description: 'Output the knowledge base in JSON format'
|
|
30
|
+
})
|
|
31
|
+
};
|
|
32
|
+
async run() {
|
|
33
|
+
const { knowledgeBaseId } = this.args;
|
|
34
|
+
const { json } = this.flags;
|
|
35
|
+
let knowledgeBase;
|
|
36
|
+
try {
|
|
37
|
+
knowledgeBase = await getKnowledgeBase(knowledgeBaseId);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
getContextDebug('Error getting knowledge base', error);
|
|
40
|
+
if (isHttpError(error) && error.statusCode === 404) {
|
|
41
|
+
this.error(`Knowledge base "${knowledgeBaseId}" not found`, {
|
|
42
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
this.error(`Failed to get knowledge base: ${getErrorMessage(error)}`, {
|
|
46
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (json) {
|
|
50
|
+
this.log(JSON.stringify(knowledgeBase, null, 2));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const pending = knowledgeBase.pendingChanges;
|
|
54
|
+
const padTo = 15 // "Pending changes" is the longest key
|
|
55
|
+
;
|
|
56
|
+
this.log(formatKeyValue('ID', knowledgeBase.publicId, {
|
|
57
|
+
padTo
|
|
58
|
+
}));
|
|
59
|
+
this.log(formatKeyValue('Title', knowledgeBase.title, {
|
|
60
|
+
padTo
|
|
61
|
+
}));
|
|
62
|
+
this.log(formatKeyValue('Description', knowledgeBase.description, {
|
|
63
|
+
padTo
|
|
64
|
+
}));
|
|
65
|
+
this.log(formatKeyValue('Organization', knowledgeBase.organizationId, {
|
|
66
|
+
padTo
|
|
67
|
+
}));
|
|
68
|
+
this.log(formatKeyValue('State', knowledgeBase.state, {
|
|
69
|
+
padTo
|
|
70
|
+
}));
|
|
71
|
+
this.log(formatKeyValue('Active job', knowledgeBase.activeJobId ?? '-', {
|
|
72
|
+
padTo
|
|
73
|
+
}));
|
|
74
|
+
this.log(formatKeyValue('Pending changes', pending ? `${pending.added} added, ${pending.changed} changed, ${pending.removed} removed` : '-', {
|
|
75
|
+
padTo
|
|
76
|
+
}));
|
|
77
|
+
this.log(formatKeyValue('Refresh', knowledgeBase.refreshEnabled ? `enabled (${knowledgeBase.refreshFrequency})` : 'disabled', {
|
|
78
|
+
padTo
|
|
79
|
+
}));
|
|
80
|
+
this.log(formatKeyValue('Open issues', knowledgeBase.openIssueCount, {
|
|
81
|
+
padTo
|
|
82
|
+
}));
|
|
83
|
+
this.log(formatKeyValue('Instructions', knowledgeBase.instructionCount, {
|
|
84
|
+
padTo
|
|
85
|
+
}));
|
|
86
|
+
this.log(formatKeyValue('Created', knowledgeBase.createdAt, {
|
|
87
|
+
padTo
|
|
88
|
+
}));
|
|
89
|
+
this.log(formatKeyValue('Updated', knowledgeBase.updatedAt, {
|
|
90
|
+
padTo
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=get.js.map
|