@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.
- package/README.md +396 -0
- 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 +7 -6
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +7 -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/actions/typegen/parseTypegenConfig.js +21 -0
- package/dist/actions/typegen/parseTypegenConfig.js.map +1 -0
- package/dist/commands/backups/download.js +8 -17
- package/dist/commands/backups/download.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/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/typegen/generate.js +4 -3
- package/dist/commands/typegen/generate.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/server/vite/plugin-typegen.js +3 -2
- package/dist/server/vite/plugin-typegen.js.map +1 -1
- package/dist/services/context.js +132 -0
- package/dist/services/context.js.map +1 -0
- 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 +1516 -718
- 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":"AAAA,SAAQA,IAAI,EAAEC,KAAK,QAAO,cAAa;AAEvC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,cAAc,QAAO,gCAA+B;AAC5D,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,kBAAkBL,SAAS;AAEjC,OAAO,MAAMM,gCAAgCP;IAC3C,OAAgBQ,OAAO;QACrBC,iBAAiBZ,KAAKa,MAAM,CAAC;YAC3BC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,kCAAiC;IAE/D,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtBC,MAAMlB,MAAMmB,OAAO,CAAC;YAClBC,SAAS;YACTP,aAAa;QACf;IACF,EAAqB;IAErB,MAAaQ,MAAqB;QAChC,MAAM,EAACV,eAAe,EAAC,GAAG,IAAI,CAACD,IAAI;QACnC,MAAM,EAACQ,IAAI,EAAC,GAAG,IAAI,CAACD,KAAK;QAEzB,IAAIK;QACJ,IAAI;YACFA,gBAAgB,MAAMf,iBAAiBI;QACzC,EAAE,OAAOY,OAAO;YACdf,gBAAgB,gCAAgCe;YAChD,IAAIlB,YAAYkB,UAAUA,MAAMC,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACD,KAAK,CAAC,CAAC,gBAAgB,EAAEZ,gBAAgB,WAAW,CAAC,EAAE;oBAC1Dc,MAAMxB,UAAUyB,aAAa;gBAC/B;YACF;YACA,IAAI,CAACH,KAAK,CAAC,CAAC,8BAA8B,EAAEnB,gBAAgBmB,QAAQ,EAAE;gBACpEE,MAAMxB,UAAUyB,aAAa;YAC/B;QACF;QAEA,IAAIR,MAAM;YACR,IAAI,CAACS,GAAG,CAACC,KAAKC,SAAS,CAACP,eAAe,MAAM;YAC7C;QACF;QAEA,MAAMQ,UAAUR,cAAcS,cAAc;QAC5C,MAAMC,QAAQ,GAAG,uCAAuC;;QACxD,IAAI,CAACL,GAAG,CAACrB,eAAe,MAAMgB,cAAcW,QAAQ,EAAE;YAACD;QAAK;QAC5D,IAAI,CAACL,GAAG,CAACrB,eAAe,SAASgB,cAAcY,KAAK,EAAE;YAACF;QAAK;QAC5D,IAAI,CAACL,GAAG,CAACrB,eAAe,eAAegB,cAAcT,WAAW,EAAE;YAACmB;QAAK;QACxE,IAAI,CAACL,GAAG,CAACrB,eAAe,gBAAgBgB,cAAca,cAAc,EAAE;YAACH;QAAK;QAC5E,IAAI,CAACL,GAAG,CAACrB,eAAe,SAASgB,cAAcc,KAAK,EAAE;YAACJ;QAAK;QAC5D,IAAI,CAACL,GAAG,CAACrB,eAAe,cAAcgB,cAAce,WAAW,IAAI,KAAK;YAACL;QAAK;QAC9E,IAAI,CAACL,GAAG,CACNrB,eACE,mBACAwB,UACI,GAAGA,QAAQQ,KAAK,CAAC,QAAQ,EAAER,QAAQS,OAAO,CAAC,UAAU,EAAET,QAAQU,OAAO,CAAC,QAAQ,CAAC,GAChF,KACJ;YAACR;QAAK;QAGV,IAAI,CAACL,GAAG,CACNrB,eACE,WACAgB,cAAcmB,cAAc,GAAG,CAAC,SAAS,EAAEnB,cAAcoB,gBAAgB,CAAC,CAAC,CAAC,GAAG,YAC/E;YAACV;QAAK;QAGV,IAAI,CAACL,GAAG,CAACrB,eAAe,eAAegB,cAAcqB,cAAc,EAAE;YAACX;QAAK;QAC3E,IAAI,CAACL,GAAG,CAACrB,eAAe,gBAAgBgB,cAAcsB,gBAAgB,EAAE;YAACZ;QAAK;QAC9E,IAAI,CAACL,GAAG,CAACrB,eAAe,WAAWgB,cAAcuB,SAAS,EAAE;YAACb;QAAK;QAClE,IAAI,CAACL,GAAG,CAACrB,eAAe,WAAWgB,cAAcwB,SAAS,EAAE;YAACd;QAAK;IACpE;AACF"}
|
|
@@ -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
|