@sanity/cli 8.7.0 → 8.9.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.
Files changed (102) hide show
  1. package/README.md +396 -0
  2. package/dist/actions/api/distillApiRoutes.js +1 -1
  3. package/dist/actions/api/distillApiRoutes.js.map +1 -1
  4. package/dist/actions/api/resolveEndpoint.js +1 -1
  5. package/dist/actions/api/resolveEndpoint.js.map +1 -1
  6. package/dist/actions/backup/archiveDir.js +7 -3
  7. package/dist/actions/backup/archiveDir.js.map +1 -1
  8. package/dist/actions/context/resolveOrganizationId.js +17 -0
  9. package/dist/actions/context/resolveOrganizationId.js.map +1 -0
  10. package/dist/actions/context/waitForJob.js +24 -0
  11. package/dist/actions/context/waitForJob.js.map +1 -0
  12. package/dist/actions/context/watchJob.js +32 -0
  13. package/dist/actions/context/watchJob.js.map +1 -0
  14. package/dist/actions/deploy/deployApp.js +7 -6
  15. package/dist/actions/deploy/deployApp.js.map +1 -1
  16. package/dist/actions/deploy/deployStudio.js +7 -6
  17. package/dist/actions/deploy/deployStudio.js.map +1 -1
  18. package/dist/actions/exec/execScript.js +6 -18
  19. package/dist/actions/exec/execScript.js.map +1 -1
  20. package/dist/actions/exec/runScript.worker.js +21 -0
  21. package/dist/actions/exec/runScript.worker.js.map +1 -0
  22. package/dist/actions/init/initAction.js +1 -1
  23. package/dist/actions/init/initAction.js.map +1 -1
  24. package/dist/actions/media/importMedia.js +4 -2
  25. package/dist/actions/media/importMedia.js.map +1 -1
  26. package/dist/actions/typegen/parseTypegenConfig.js +21 -0
  27. package/dist/actions/typegen/parseTypegenConfig.js.map +1 -0
  28. package/dist/commands/backups/download.js +8 -17
  29. package/dist/commands/backups/download.js.map +1 -1
  30. package/dist/commands/context/build.js +111 -0
  31. package/dist/commands/context/build.js.map +1 -0
  32. package/dist/commands/context/create.js +106 -0
  33. package/dist/commands/context/create.js.map +1 -0
  34. package/dist/commands/context/delete.js +68 -0
  35. package/dist/commands/context/delete.js.map +1 -0
  36. package/dist/commands/context/get.js +95 -0
  37. package/dist/commands/context/get.js.map +1 -0
  38. package/dist/commands/context/imports/create.js +270 -0
  39. package/dist/commands/context/imports/create.js.map +1 -0
  40. package/dist/commands/context/imports/delete.js +73 -0
  41. package/dist/commands/context/imports/delete.js.map +1 -0
  42. package/dist/commands/context/imports/download.js +65 -0
  43. package/dist/commands/context/imports/download.js.map +1 -0
  44. package/dist/commands/context/imports/get.js +93 -0
  45. package/dist/commands/context/imports/get.js.map +1 -0
  46. package/dist/commands/context/imports/list.js +105 -0
  47. package/dist/commands/context/imports/list.js.map +1 -0
  48. package/dist/commands/context/jobs/get.js +90 -0
  49. package/dist/commands/context/jobs/get.js.map +1 -0
  50. package/dist/commands/context/list.js +109 -0
  51. package/dist/commands/context/list.js.map +1 -0
  52. package/dist/commands/context/refresh.js +46 -0
  53. package/dist/commands/context/refresh.js.map +1 -0
  54. package/dist/commands/context/update.js +127 -0
  55. package/dist/commands/context/update.js.map +1 -0
  56. package/dist/commands/datasets/alias/delete.js +4 -9
  57. package/dist/commands/datasets/alias/delete.js.map +1 -1
  58. package/dist/commands/datasets/alias/unlink.js +4 -9
  59. package/dist/commands/datasets/alias/unlink.js.map +1 -1
  60. package/dist/commands/datasets/delete.js +4 -9
  61. package/dist/commands/datasets/delete.js.map +1 -1
  62. package/dist/commands/graphql/undeploy.js +4 -9
  63. package/dist/commands/graphql/undeploy.js.map +1 -1
  64. package/dist/commands/init.js +1 -1
  65. package/dist/commands/init.js.map +1 -1
  66. package/dist/commands/media/create-aspect.js +5 -6
  67. package/dist/commands/media/create-aspect.js.map +1 -1
  68. package/dist/commands/media/delete-aspect.js +7 -23
  69. package/dist/commands/media/delete-aspect.js.map +1 -1
  70. package/dist/commands/media/deploy-aspect.js +4 -9
  71. package/dist/commands/media/deploy-aspect.js.map +1 -1
  72. package/dist/commands/media/export.js +3 -7
  73. package/dist/commands/media/export.js.map +1 -1
  74. package/dist/commands/media/import.js +3 -7
  75. package/dist/commands/media/import.js.map +1 -1
  76. package/dist/commands/organizations/create.js +4 -9
  77. package/dist/commands/organizations/create.js.map +1 -1
  78. package/dist/commands/schemas/delete.js +3 -8
  79. package/dist/commands/schemas/delete.js.map +1 -1
  80. package/dist/commands/tokens/delete.js +4 -7
  81. package/dist/commands/tokens/delete.js.map +1 -1
  82. package/dist/commands/typegen/generate.js +4 -3
  83. package/dist/commands/typegen/generate.js.map +1 -1
  84. package/dist/commands/users/invite.js +8 -18
  85. package/dist/commands/users/invite.js.map +1 -1
  86. package/dist/exports/index.js +1 -1
  87. package/dist/exports/index.js.map +1 -1
  88. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +18 -0
  89. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  90. package/dist/generated/apiRoutes.js +11 -9
  91. package/dist/generated/apiRoutes.js.map +1 -1
  92. package/dist/prompts/promptForOrganization.js +19 -0
  93. package/dist/prompts/promptForOrganization.js.map +1 -0
  94. package/dist/server/vite/plugin-typegen.js +3 -2
  95. package/dist/server/vite/plugin-typegen.js.map +1 -1
  96. package/dist/services/context.js +132 -0
  97. package/dist/services/context.js.map +1 -0
  98. package/dist/util/sharedFlags.js +24 -0
  99. package/dist/util/sharedFlags.js.map +1 -1
  100. package/oclif.config.js +1 -0
  101. package/oclif.manifest.json +1516 -718
  102. package/package.json +18 -23
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/commands/context/get.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 {isHttpError} from '@sanity/client'\n\nimport {formatKeyValue} from '../../actions/debug/output.js'\nimport {getKnowledgeBase} from '../../services/context.js'\n\nconst getContextDebug = subdebug('context:get')\n\nexport class GetKnowledgeBaseCommand extends SanityCommand<typeof GetKnowledgeBaseCommand> {\n static override args = {\n knowledgeBaseId: Args.string({\n description: 'Knowledge base ID',\n required: true,\n }),\n }\n\n static override description = 'Get details of a knowledge base'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123',\n description: 'Get details of a specific knowledge base',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --json',\n description: 'Output the knowledge base as JSON',\n },\n ]\n\n static override flags = {\n json: Flags.boolean({\n default: false,\n description: 'Output the knowledge base in JSON format',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {knowledgeBaseId} = this.args\n const {json} = this.flags\n\n let knowledgeBase\n try {\n knowledgeBase = await getKnowledgeBase(knowledgeBaseId)\n } catch (error) {\n getContextDebug('Error getting 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 get knowledge base: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (json) {\n this.log(JSON.stringify(knowledgeBase, null, 2))\n return\n }\n\n const pending = knowledgeBase.pendingChanges\n const padTo = 15 // \"Pending changes\" is the longest key\n this.log(formatKeyValue('ID', knowledgeBase.publicId, {padTo}))\n this.log(formatKeyValue('Title', knowledgeBase.title, {padTo}))\n this.log(formatKeyValue('Description', knowledgeBase.description, {padTo}))\n this.log(formatKeyValue('Organization', knowledgeBase.organizationId, {padTo}))\n this.log(formatKeyValue('State', knowledgeBase.state, {padTo}))\n this.log(formatKeyValue('Active job', knowledgeBase.activeJobId ?? '-', {padTo}))\n this.log(\n formatKeyValue(\n 'Pending changes',\n pending\n ? `${pending.added} added, ${pending.changed} changed, ${pending.removed} removed`\n : '-',\n {padTo},\n ),\n )\n this.log(\n formatKeyValue(\n 'Refresh',\n knowledgeBase.refreshEnabled ? `enabled (${knowledgeBase.refreshFrequency})` : 'disabled',\n {padTo},\n ),\n )\n this.log(formatKeyValue('Open issues', knowledgeBase.openIssueCount, {padTo}))\n this.log(formatKeyValue('Instructions', knowledgeBase.instructionCount, {padTo}))\n this.log(formatKeyValue('Created', knowledgeBase.createdAt, {padTo}))\n this.log(formatKeyValue('Updated', knowledgeBase.updatedAt, {padTo}))\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","isHttpError","formatKeyValue","getKnowledgeBase","getContextDebug","GetKnowledgeBaseCommand","args","knowledgeBaseId","string","description","required","examples","command","flags","json","boolean","default","run","knowledgeBase","error","statusCode","exit","RUNTIME_ERROR","log","JSON","stringify","pending","pendingChanges","padTo","publicId","title","organizationId","state","activeJobId","added","changed","removed","refreshEnabled","refreshFrequency","openIssueCount","instructionCount","createdAt","updatedAt"],"mappings":"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"}
@@ -0,0 +1,270 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { basename, extname } from 'node:path';
3
+ import { Args, Flags } from '@oclif/core';
4
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
5
+ import { getErrorMessage } from '@sanity/cli-core/errors';
6
+ import { spinner } from '@sanity/cli-core/ux';
7
+ import { isHttpError } from '@sanity/client';
8
+ import { formatKeyValue } from '../../../actions/debug/output.js';
9
+ import { createImport } from '../../../services/context.js';
10
+ import { defineCommandTelemetry } from '../../../util/telemetry/commandTelemetry.js';
11
+ const createImportDebug = subdebug('context:imports:create');
12
+ const TEXT_CONTENT_TYPES = [
13
+ 'text/markdown',
14
+ 'text/plain'
15
+ ];
16
+ function isTextContentType(value) {
17
+ const textContentTypes = TEXT_CONTENT_TYPES;
18
+ return textContentTypes.includes(value);
19
+ }
20
+ const FILE_CONTENT_TYPES = {
21
+ '.csv': 'text/csv',
22
+ '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
23
+ '.htm': 'text/html',
24
+ '.html': 'text/html',
25
+ '.json': 'application/json',
26
+ '.markdown': 'text/markdown',
27
+ '.md': 'text/markdown',
28
+ '.pdf': 'application/pdf',
29
+ '.txt': 'text/plain'
30
+ };
31
+ const flags = {
32
+ 'content-type': Flags.string({
33
+ description: 'Content type of the import (--text: text/markdown or text/plain; --file: any MIME type, inferred from the file extension when omitted)',
34
+ helpValue: '<mime>',
35
+ parse: async (input)=>{
36
+ const trimmed = input.trim();
37
+ if (trimmed === '') {
38
+ throw new Error('`--content-type` cannot be empty if provided');
39
+ }
40
+ return trimmed;
41
+ },
42
+ required: false
43
+ }),
44
+ file: Flags.string({
45
+ description: 'Path to a local file to import',
46
+ exclusive: [
47
+ 'text',
48
+ 'url',
49
+ 'query'
50
+ ],
51
+ helpValue: '<path>',
52
+ required: false
53
+ }),
54
+ query: Flags.string({
55
+ description: 'GROQ query binding a Sanity dataset as a source',
56
+ exclusive: [
57
+ 'text',
58
+ 'url',
59
+ 'file'
60
+ ],
61
+ required: false
62
+ }),
63
+ 'sanity-dataset': Flags.string({
64
+ dependsOn: [
65
+ 'query'
66
+ ],
67
+ description: 'Sanity dataset for a dataset import',
68
+ helpValue: '<name>',
69
+ required: false
70
+ }),
71
+ 'sanity-project': Flags.string({
72
+ dependsOn: [
73
+ 'query'
74
+ ],
75
+ description: 'Sanity project ID for a dataset import',
76
+ helpValue: '<id>',
77
+ required: false
78
+ }),
79
+ text: Flags.string({
80
+ description: 'Inline text content to import (requires --title)',
81
+ exclusive: [
82
+ 'file',
83
+ 'url',
84
+ 'query'
85
+ ],
86
+ required: false
87
+ }),
88
+ title: Flags.string({
89
+ dependsOn: [
90
+ 'text'
91
+ ],
92
+ description: 'Title for an inline text import',
93
+ required: false
94
+ }),
95
+ url: Flags.string({
96
+ description: 'Website URL to crawl',
97
+ exclusive: [
98
+ 'text',
99
+ 'file',
100
+ 'query'
101
+ ],
102
+ helpValue: '<url>',
103
+ required: false
104
+ })
105
+ };
106
+ export class CreateImportCommand extends SanityCommand {
107
+ static args = {
108
+ knowledgeBaseId: Args.string({
109
+ description: 'Knowledge base ID',
110
+ required: true
111
+ })
112
+ };
113
+ static description = 'Import content into a knowledge base';
114
+ static examples = [
115
+ {
116
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 --text "Refunds are processed within 5 days" --title "Refund policy"',
117
+ description: 'Import inline text'
118
+ },
119
+ {
120
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 --file ./handbook.pdf',
121
+ description: 'Upload and import a local file'
122
+ },
123
+ {
124
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 --url https://example.com/docs',
125
+ description: 'Crawl a website'
126
+ },
127
+ {
128
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 --query \'*[_type == "article"]\' --sanity-project abc123 --sanity-dataset production',
129
+ description: 'Bind a Sanity dataset as a source'
130
+ }
131
+ ];
132
+ static flags = flags;
133
+ static telemetry = defineCommandTelemetry(flags, {
134
+ redact: [
135
+ 'text',
136
+ 'file',
137
+ 'url',
138
+ 'title',
139
+ 'query'
140
+ ]
141
+ });
142
+ async run() {
143
+ const { knowledgeBaseId } = this.args;
144
+ const params = await this.buildImportParams();
145
+ const spin = spinner('Creating import').start();
146
+ try {
147
+ const { jobId } = await createImport(knowledgeBaseId, params);
148
+ spin.succeed('Import created');
149
+ this.log(formatKeyValue('Job ID', jobId));
150
+ this.log(`Track it with: sanity context jobs get ${knowledgeBaseId} ${jobId}`);
151
+ } catch (error) {
152
+ spin.fail();
153
+ createImportDebug('Error creating import', error);
154
+ if (isHttpError(error) && error.statusCode === 404) {
155
+ this.error(`Knowledge base "${knowledgeBaseId}" not found`, {
156
+ exit: exitCodes.RUNTIME_ERROR
157
+ });
158
+ }
159
+ this.error(`Failed to create import: ${getErrorMessage(error)}`, {
160
+ exit: exitCodes.RUNTIME_ERROR
161
+ });
162
+ }
163
+ }
164
+ async buildFileImportParams(filePath) {
165
+ const { 'content-type': contentType } = this.flags;
166
+ let fileContent;
167
+ try {
168
+ fileContent = await readFile(filePath);
169
+ } catch (error) {
170
+ createImportDebug('Error reading import file', error);
171
+ this.error(`Failed to read file "${filePath}": ${getErrorMessage(error)}`, {
172
+ exit: exitCodes.RUNTIME_ERROR
173
+ });
174
+ }
175
+ // The Context API signs the upload URL for a content-type header, so one
176
+ // must always be sent or the upload fails (API quirk found in live testing).
177
+ const inferredContentType = FILE_CONTENT_TYPES[extname(filePath).toLowerCase()] ?? 'application/octet-stream';
178
+ return {
179
+ contentType: contentType ?? inferredContentType,
180
+ file: fileContent,
181
+ filename: basename(filePath),
182
+ type: 'file'
183
+ };
184
+ }
185
+ async buildImportParams() {
186
+ const { 'content-type': contentType, file, query, 'sanity-dataset': sanityDataset, 'sanity-project': sanityProject, text, url } = this.flags;
187
+ const sources = [
188
+ [
189
+ '--text',
190
+ text
191
+ ],
192
+ [
193
+ '--file',
194
+ file
195
+ ],
196
+ [
197
+ '--url',
198
+ url
199
+ ],
200
+ [
201
+ '--query',
202
+ query
203
+ ]
204
+ ];
205
+ const provided = sources.filter(([, value])=>value !== undefined);
206
+ if (provided.length !== 1) {
207
+ this.error('Provide exactly one of --text, --file, --url or --query.', {
208
+ exit: exitCodes.USAGE_ERROR
209
+ });
210
+ }
211
+ const [sourceFlag, sourceValue] = provided[0];
212
+ if (!sourceValue?.trim()) {
213
+ this.error(`${sourceFlag} cannot be empty`, {
214
+ exit: exitCodes.USAGE_ERROR
215
+ });
216
+ }
217
+ if (sourceFlag === '--text') {
218
+ return this.buildTextImportParams(sourceValue);
219
+ }
220
+ if (sourceFlag === '--file') {
221
+ return this.buildFileImportParams(sourceValue);
222
+ }
223
+ if (contentType !== undefined) {
224
+ this.error('The --content-type flag only applies to --text and --file imports.', {
225
+ exit: exitCodes.USAGE_ERROR
226
+ });
227
+ }
228
+ if (sourceFlag === '--url') {
229
+ return {
230
+ type: 'crawl',
231
+ url: sourceValue
232
+ };
233
+ }
234
+ // Only --query remains; the checks above narrow `sourceFlag` to '--query'.
235
+ if (!sanityProject || !sanityDataset) {
236
+ this.error('Dataset imports require --sanity-project and --sanity-dataset alongside --query.', {
237
+ exit: exitCodes.USAGE_ERROR
238
+ });
239
+ }
240
+ return {
241
+ query: sourceValue,
242
+ sanityDatasetId: sanityDataset,
243
+ sanityProjectId: sanityProject,
244
+ type: 'dataset'
245
+ };
246
+ }
247
+ buildTextImportParams(text) {
248
+ const { 'content-type': contentType, title } = this.flags;
249
+ if (!title?.trim()) {
250
+ this.error('Title is required for text imports. Provide it with the --title flag.', {
251
+ exit: exitCodes.USAGE_ERROR
252
+ });
253
+ }
254
+ if (contentType !== undefined && !isTextContentType(contentType)) {
255
+ this.error(`Text imports support content types ${TEXT_CONTENT_TYPES.join(' and ')}, got "${contentType}".`, {
256
+ exit: exitCodes.USAGE_ERROR
257
+ });
258
+ }
259
+ return {
260
+ content: text,
261
+ title: title.trim(),
262
+ type: 'text',
263
+ ...contentType === undefined ? {} : {
264
+ contentType
265
+ }
266
+ };
267
+ }
268
+ }
269
+
270
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/imports/create.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport {basename, extname} from 'node:path'\n\nimport {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 {type Context, isHttpError} from '@sanity/client'\n\nimport {formatKeyValue} from '../../../actions/debug/output.js'\nimport {createImport} from '../../../services/context.js'\nimport {defineCommandTelemetry} from '../../../util/telemetry/commandTelemetry.js'\n\nconst createImportDebug = subdebug('context:imports:create')\n\nconst TEXT_CONTENT_TYPES = ['text/markdown', 'text/plain'] as const\n\nfunction isTextContentType(value: string): value is (typeof TEXT_CONTENT_TYPES)[number] {\n const textContentTypes: readonly string[] = TEXT_CONTENT_TYPES\n return textContentTypes.includes(value)\n}\n\nconst FILE_CONTENT_TYPES: Record<string, string> = {\n '.csv': 'text/csv',\n '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.htm': 'text/html',\n '.html': 'text/html',\n '.json': 'application/json',\n '.markdown': 'text/markdown',\n '.md': 'text/markdown',\n '.pdf': 'application/pdf',\n '.txt': 'text/plain',\n}\n\nconst flags = {\n 'content-type': Flags.string({\n description:\n 'Content type of the import (--text: text/markdown or text/plain; --file: any MIME type, inferred from the file extension when omitted)',\n helpValue: '<mime>',\n parse: async (input: string) => {\n const trimmed = input.trim()\n if (trimmed === '') {\n throw new Error('`--content-type` cannot be empty if provided')\n }\n return trimmed\n },\n required: false,\n }),\n file: Flags.string({\n description: 'Path to a local file to import',\n exclusive: ['text', 'url', 'query'],\n helpValue: '<path>',\n required: false,\n }),\n query: Flags.string({\n description: 'GROQ query binding a Sanity dataset as a source',\n exclusive: ['text', 'url', 'file'],\n required: false,\n }),\n 'sanity-dataset': Flags.string({\n dependsOn: ['query'],\n description: 'Sanity dataset for a dataset import',\n helpValue: '<name>',\n required: false,\n }),\n 'sanity-project': Flags.string({\n dependsOn: ['query'],\n description: 'Sanity project ID for a dataset import',\n helpValue: '<id>',\n required: false,\n }),\n text: Flags.string({\n description: 'Inline text content to import (requires --title)',\n exclusive: ['file', 'url', 'query'],\n required: false,\n }),\n title: Flags.string({\n dependsOn: ['text'],\n description: 'Title for an inline text import',\n required: false,\n }),\n url: Flags.string({\n description: 'Website URL to crawl',\n exclusive: ['text', 'file', 'query'],\n helpValue: '<url>',\n required: false,\n }),\n} satisfies FlagInput\n\nexport class CreateImportCommand extends SanityCommand<typeof CreateImportCommand> {\n static override args = {\n knowledgeBaseId: Args.string({\n description: 'Knowledge base ID',\n required: true,\n }),\n }\n\n static override description = 'Import content into a knowledge base'\n\n static override examples = [\n {\n command:\n '<%= config.bin %> <%= command.id %> kb-abc123 --text \"Refunds are processed within 5 days\" --title \"Refund policy\"',\n description: 'Import inline text',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --file ./handbook.pdf',\n description: 'Upload and import a local file',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --url https://example.com/docs',\n description: 'Crawl a website',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> kb-abc123 --query \\'*[_type == \"article\"]\\' --sanity-project abc123 --sanity-dataset production',\n description: 'Bind a Sanity dataset as a source',\n },\n ]\n\n static override flags = flags\n\n static telemetry = defineCommandTelemetry(flags, {\n redact: ['text', 'file', 'url', 'title', 'query'],\n })\n\n public async run(): Promise<void> {\n const {knowledgeBaseId} = this.args\n\n const params = await this.buildImportParams()\n\n const spin = spinner('Creating import').start()\n try {\n const {jobId} = await createImport(knowledgeBaseId, params)\n spin.succeed('Import created')\n this.log(formatKeyValue('Job ID', jobId))\n this.log(`Track it with: sanity context jobs get ${knowledgeBaseId} ${jobId}`)\n } catch (error) {\n spin.fail()\n createImportDebug('Error creating import', 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 create import: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async buildFileImportParams(filePath: string): Promise<Context.CreateFileImportParams> {\n const {'content-type': contentType} = this.flags\n\n let fileContent: Buffer\n try {\n fileContent = await readFile(filePath)\n } catch (error) {\n createImportDebug('Error reading import file', error)\n this.error(`Failed to read file \"${filePath}\": ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // The Context API signs the upload URL for a content-type header, so one\n // must always be sent or the upload fails (API quirk found in live testing).\n const inferredContentType =\n FILE_CONTENT_TYPES[extname(filePath).toLowerCase()] ?? 'application/octet-stream'\n\n return {\n contentType: contentType ?? inferredContentType,\n file: fileContent,\n filename: basename(filePath),\n type: 'file',\n }\n }\n\n private async buildImportParams(): Promise<\n Context.CreateFileImportParams | Context.CreateImportParams\n > {\n const {\n 'content-type': contentType,\n file,\n query,\n 'sanity-dataset': sanityDataset,\n 'sanity-project': sanityProject,\n text,\n url,\n } = this.flags\n\n const sources: [flag: '--file' | '--query' | '--text' | '--url', value: string | undefined][] =\n [\n ['--text', text],\n ['--file', file],\n ['--url', url],\n ['--query', query],\n ]\n const provided = sources.filter(([, value]) => value !== undefined)\n if (provided.length !== 1) {\n this.error('Provide exactly one of --text, --file, --url or --query.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const [sourceFlag, sourceValue] = provided[0]\n if (!sourceValue?.trim()) {\n this.error(`${sourceFlag} cannot be empty`, {exit: exitCodes.USAGE_ERROR})\n }\n\n if (sourceFlag === '--text') {\n return this.buildTextImportParams(sourceValue)\n }\n\n if (sourceFlag === '--file') {\n return this.buildFileImportParams(sourceValue)\n }\n\n if (contentType !== undefined) {\n this.error('The --content-type flag only applies to --text and --file imports.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n if (sourceFlag === '--url') {\n return {type: 'crawl', url: sourceValue}\n }\n\n // Only --query remains; the checks above narrow `sourceFlag` to '--query'.\n if (!sanityProject || !sanityDataset) {\n this.error(\n 'Dataset imports require --sanity-project and --sanity-dataset alongside --query.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n return {\n query: sourceValue,\n sanityDatasetId: sanityDataset,\n sanityProjectId: sanityProject,\n type: 'dataset',\n }\n }\n\n private buildTextImportParams(text: string): Context.CreateImportParams {\n const {'content-type': contentType, title} = this.flags\n\n if (!title?.trim()) {\n this.error('Title is required for text imports. Provide it with the --title flag.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n if (contentType !== undefined && !isTextContentType(contentType)) {\n this.error(\n `Text imports support content types ${TEXT_CONTENT_TYPES.join(' and ')}, got \"${contentType}\".`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n return {\n content: text,\n title: title.trim(),\n type: 'text',\n ...(contentType === undefined ? {} : {contentType}),\n }\n }\n}\n"],"names":["readFile","basename","extname","Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","spinner","isHttpError","formatKeyValue","createImport","defineCommandTelemetry","createImportDebug","TEXT_CONTENT_TYPES","isTextContentType","value","textContentTypes","includes","FILE_CONTENT_TYPES","flags","string","description","helpValue","parse","input","trimmed","trim","Error","required","file","exclusive","query","dependsOn","text","title","url","CreateImportCommand","args","knowledgeBaseId","examples","command","telemetry","redact","run","params","buildImportParams","spin","start","jobId","succeed","log","error","fail","statusCode","exit","RUNTIME_ERROR","buildFileImportParams","filePath","contentType","fileContent","inferredContentType","toLowerCase","filename","type","sanityDataset","sanityProject","sources","provided","filter","undefined","length","USAGE_ERROR","sourceFlag","sourceValue","buildTextImportParams","sanityDatasetId","sanityProjectId","join","content"],"mappings":"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"}
@@ -0,0 +1,73 @@
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 { deleteImport } from '../../../services/context.js';
7
+ const deleteImportDebug = subdebug('context:imports:delete');
8
+ export class DeleteImportCommand extends SanityCommand {
9
+ static args = {
10
+ knowledgeBaseId: Args.string({
11
+ description: 'Knowledge base ID',
12
+ required: true
13
+ }),
14
+ // eslint-disable-next-line perfectionist/sort-objects -- positional order: knowledge base first
15
+ importId: Args.string({
16
+ description: 'Import ID',
17
+ required: true
18
+ })
19
+ };
20
+ static description = 'Delete an import from a knowledge base';
21
+ static examples = [
22
+ {
23
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 import-def456',
24
+ description: 'Delete an import after confirmation'
25
+ },
26
+ {
27
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 import-def456 --yes',
28
+ description: 'Delete an import without confirmation prompt'
29
+ }
30
+ ];
31
+ static flags = {
32
+ yes: Flags.boolean({
33
+ char: 'y',
34
+ default: false,
35
+ description: 'Skip confirmation prompt (unattended mode)'
36
+ })
37
+ };
38
+ async run() {
39
+ const { importId, knowledgeBaseId } = this.args;
40
+ const { yes: skipConfirmation } = this.flags;
41
+ if (this.isUnattended() && !skipConfirmation) {
42
+ this.error('Deletion requires confirmation. Pass `--yes` to delete the import.', {
43
+ exit: exitCodes.USAGE_ERROR
44
+ });
45
+ }
46
+ if (!skipConfirmation) {
47
+ const confirmed = await confirm({
48
+ default: false,
49
+ message: `Delete import "${importId}"?`
50
+ });
51
+ if (!confirmed) {
52
+ this.log('Import not deleted');
53
+ this.exit(exitCodes.USER_ABORT);
54
+ }
55
+ }
56
+ try {
57
+ await deleteImport(knowledgeBaseId, importId);
58
+ this.log('Import deleted');
59
+ } catch (error) {
60
+ deleteImportDebug('Error deleting import', error);
61
+ if (isHttpError(error) && error.statusCode === 404) {
62
+ this.error(`Import "${importId}" not found on knowledge base "${knowledgeBaseId}"`, {
63
+ exit: exitCodes.RUNTIME_ERROR
64
+ });
65
+ }
66
+ this.error(`Failed to delete import: ${getErrorMessage(error)}`, {
67
+ exit: exitCodes.RUNTIME_ERROR
68
+ });
69
+ }
70
+ }
71
+ }
72
+
73
+ //# sourceMappingURL=delete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/imports/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 {deleteImport} from '../../../services/context.js'\n\nconst deleteImportDebug = subdebug('context:imports:delete')\n\nexport class DeleteImportCommand extends SanityCommand<typeof DeleteImportCommand> {\n static override args = {\n knowledgeBaseId: Args.string({\n description: 'Knowledge base ID',\n required: true,\n }),\n // eslint-disable-next-line perfectionist/sort-objects -- positional order: knowledge base first\n importId: Args.string({\n description: 'Import ID',\n required: true,\n }),\n }\n\n static override description = 'Delete an import from a knowledge base'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 import-def456',\n description: 'Delete an import after confirmation',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 import-def456 --yes',\n description: 'Delete an import 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 {importId, 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 import.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n if (!skipConfirmation) {\n const confirmed = await confirm({\n default: false,\n message: `Delete import \"${importId}\"?`,\n })\n\n if (!confirmed) {\n this.log('Import not deleted')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n try {\n await deleteImport(knowledgeBaseId, importId)\n this.log('Import deleted')\n } catch (error) {\n deleteImportDebug('Error deleting import', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`Import \"${importId}\" not found on knowledge base \"${knowledgeBaseId}\"`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n this.error(`Failed to delete import: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","confirm","isHttpError","deleteImport","deleteImportDebug","DeleteImportCommand","args","knowledgeBaseId","string","description","required","importId","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,YAAY,QAAO,+BAA8B;AAEzD,MAAMC,oBAAoBL,SAAS;AAEnC,OAAO,MAAMM,4BAA4BP;IACvC,OAAgBQ,OAAO;QACrBC,iBAAiBZ,KAAKa,MAAM,CAAC;YAC3BC,aAAa;YACbC,UAAU;QACZ;QACA,gGAAgG;QAChGC,UAAUhB,KAAKa,MAAM,CAAC;YACpBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,yCAAwC;IAEtE,OAAgBG,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtBC,KAAKnB,MAAMoB,OAAO,CAAC;YACjBC,MAAM;YACNC,SAAS;YACTT,aAAa;QACf;IACF,EAAqB;IAErB,MAAaU,MAAqB;QAChC,MAAM,EAACR,QAAQ,EAAEJ,eAAe,EAAC,GAAG,IAAI,CAACD,IAAI;QAC7C,MAAM,EAACS,KAAKK,gBAAgB,EAAC,GAAG,IAAI,CAACN,KAAK;QAE1C,IAAI,IAAI,CAACO,YAAY,MAAM,CAACD,kBAAkB;YAC5C,IAAI,CAACE,KAAK,CAAC,sEAAsE;gBAC/EC,MAAM1B,UAAU2B,WAAW;YAC7B;QACF;QAEA,IAAI,CAACJ,kBAAkB;YACrB,MAAMK,YAAY,MAAMxB,QAAQ;gBAC9BiB,SAAS;gBACTQ,SAAS,CAAC,eAAe,EAAEf,SAAS,EAAE,CAAC;YACzC;YAEA,IAAI,CAACc,WAAW;gBACd,IAAI,CAACE,GAAG,CAAC;gBACT,IAAI,CAACJ,IAAI,CAAC1B,UAAU+B,UAAU;YAChC;QACF;QAEA,IAAI;YACF,MAAMzB,aAAaI,iBAAiBI;YACpC,IAAI,CAACgB,GAAG,CAAC;QACX,EAAE,OAAOL,OAAO;YACdlB,kBAAkB,yBAAyBkB;YAC3C,IAAIpB,YAAYoB,UAAUA,MAAMO,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACP,KAAK,CAAC,CAAC,QAAQ,EAAEX,SAAS,+BAA+B,EAAEJ,gBAAgB,CAAC,CAAC,EAAE;oBAClFgB,MAAM1B,UAAUiC,aAAa;gBAC/B;YACF;YACA,IAAI,CAACR,KAAK,CAAC,CAAC,yBAAyB,EAAEtB,gBAAgBsB,QAAQ,EAAE;gBAC/DC,MAAM1B,UAAUiC,aAAa;YAC/B;QACF;IACF;AACF"}
@@ -0,0 +1,65 @@
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 { downloadImport } from '../../../services/context.js';
7
+ const downloadImportDebug = subdebug('context:imports:download');
8
+ export class DownloadImportCommand extends SanityCommand {
9
+ static args = {
10
+ knowledgeBaseId: Args.string({
11
+ description: 'Knowledge base ID',
12
+ required: true
13
+ }),
14
+ // eslint-disable-next-line perfectionist/sort-objects -- positional order: knowledge base first
15
+ importId: Args.string({
16
+ description: 'Import ID',
17
+ required: true
18
+ })
19
+ };
20
+ static description = 'Get a short-lived signed URL for the original uploaded bytes of a file import';
21
+ static examples = [
22
+ {
23
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 import-def456',
24
+ description: 'Get a download URL for an uploaded file'
25
+ }
26
+ ];
27
+ static flags = {
28
+ json: Flags.boolean({
29
+ default: false,
30
+ description: 'Output the download URL in JSON format'
31
+ })
32
+ };
33
+ async run() {
34
+ const { importId, knowledgeBaseId } = this.args;
35
+ const { json } = this.flags;
36
+ let download;
37
+ try {
38
+ download = await downloadImport(knowledgeBaseId, importId);
39
+ } catch (error) {
40
+ downloadImportDebug('Error getting import download URL', error);
41
+ if (isHttpError(error) && error.statusCode === 404) {
42
+ this.error(`Import "${importId}" not found on knowledge base "${knowledgeBaseId}"`, {
43
+ exit: exitCodes.RUNTIME_ERROR
44
+ });
45
+ }
46
+ this.error(`Failed to get import download URL: ${getErrorMessage(error)}`, {
47
+ exit: exitCodes.RUNTIME_ERROR
48
+ });
49
+ }
50
+ if (json) {
51
+ this.log(JSON.stringify(download, null, 2));
52
+ return;
53
+ }
54
+ const padTo = 7 // "Expires" is the longest key
55
+ ;
56
+ this.log(formatKeyValue('URL', download.url, {
57
+ padTo
58
+ }));
59
+ this.log(formatKeyValue('Expires', download.expiresAt, {
60
+ padTo
61
+ }));
62
+ }
63
+ }
64
+
65
+ //# sourceMappingURL=download.js.map