@sanity/cli 8.22.1 → 8.24.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 (98) hide show
  1. package/README.md +313 -0
  2. package/dist/actions/codemods/dashboard.js +136 -8
  3. package/dist/actions/codemods/dashboard.js.map +1 -1
  4. package/dist/actions/codemods/types.js.map +1 -1
  5. package/dist/actions/context/watchJob.js +9 -3
  6. package/dist/actions/context/watchJob.js.map +1 -1
  7. package/dist/actions/deploy/blueprintManaged.js +59 -0
  8. package/dist/actions/deploy/blueprintManaged.js.map +1 -0
  9. package/dist/actions/deploy/deployApp.js +117 -1
  10. package/dist/actions/deploy/deployApp.js.map +1 -1
  11. package/dist/actions/deploy/deployRunner.js.map +1 -1
  12. package/dist/actions/deploy/deployStudio.js +6 -31
  13. package/dist/actions/deploy/deployStudio.js.map +1 -1
  14. package/dist/actions/exec/runScript.worker.js +6 -2
  15. package/dist/actions/exec/runScript.worker.js.map +1 -1
  16. package/dist/actions/init/bootstrapLocalTemplate.js +35 -11
  17. package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
  18. package/dist/actions/init/createMediaLibraryCliConfig.js +12 -0
  19. package/dist/actions/init/createMediaLibraryCliConfig.js.map +1 -0
  20. package/dist/actions/init/determineMediaLibraryTemplate.js +14 -0
  21. package/dist/actions/init/determineMediaLibraryTemplate.js.map +1 -0
  22. package/dist/actions/init/initAction.js +35 -6
  23. package/dist/actions/init/initAction.js.map +1 -1
  24. package/dist/actions/init/initApp.js +81 -6
  25. package/dist/actions/init/initApp.js.map +1 -1
  26. package/dist/actions/init/offerAspectPull.js +81 -0
  27. package/dist/actions/init/offerAspectPull.js.map +1 -0
  28. package/dist/actions/init/project/getOrCreateDataset.js +8 -5
  29. package/dist/actions/init/project/getOrCreateDataset.js.map +1 -1
  30. package/dist/actions/init/project/getOrCreateProject.js +7 -5
  31. package/dist/actions/init/project/getOrCreateProject.js.map +1 -1
  32. package/dist/actions/init/project/getProjectDetails.js +25 -2
  33. package/dist/actions/init/project/getProjectDetails.js.map +1 -1
  34. package/dist/actions/init/project/promptUserForOrganization.js +6 -4
  35. package/dist/actions/init/project/promptUserForOrganization.js.map +1 -1
  36. package/dist/actions/init/templates/index.js +3 -0
  37. package/dist/actions/init/templates/index.js.map +1 -1
  38. package/dist/actions/init/templates/mediaLibraryConfig.js +51 -0
  39. package/dist/actions/init/templates/mediaLibraryConfig.js.map +1 -0
  40. package/dist/actions/media/aspectName.js +8 -0
  41. package/dist/actions/media/aspectName.js.map +1 -0
  42. package/dist/actions/media/aspectSync/contract.js +163 -0
  43. package/dist/actions/media/aspectSync/contract.js.map +1 -0
  44. package/dist/actions/media/aspectSync/syncAspects.js +115 -0
  45. package/dist/actions/media/aspectSync/syncAspects.js.map +1 -0
  46. package/dist/actions/media/cliConfigFields.js +192 -0
  47. package/dist/actions/media/cliConfigFields.js.map +1 -0
  48. package/dist/actions/media/pullAspects.js +225 -0
  49. package/dist/actions/media/pullAspects.js.map +1 -0
  50. package/dist/actions/media/resolveMediaLibraryId.js +83 -0
  51. package/dist/actions/media/resolveMediaLibraryId.js.map +1 -0
  52. package/dist/commands/codemod.js +1 -0
  53. package/dist/commands/codemod.js.map +1 -1
  54. package/dist/commands/context/build.js +2 -2
  55. package/dist/commands/context/build.js.map +1 -1
  56. package/dist/commands/context/entries/get.js +83 -0
  57. package/dist/commands/context/entries/get.js.map +1 -0
  58. package/dist/commands/context/entries/list.js +82 -0
  59. package/dist/commands/context/entries/list.js.map +1 -0
  60. package/dist/commands/context/entries/rebuild.js +95 -0
  61. package/dist/commands/context/entries/rebuild.js.map +1 -0
  62. package/dist/commands/context/issues/apply.js +86 -0
  63. package/dist/commands/context/issues/apply.js.map +1 -0
  64. package/dist/commands/context/issues/dismiss.js +52 -0
  65. package/dist/commands/context/issues/dismiss.js.map +1 -0
  66. package/dist/commands/context/issues/get.js +105 -0
  67. package/dist/commands/context/issues/get.js.map +1 -0
  68. package/dist/commands/context/issues/list.js +128 -0
  69. package/dist/commands/context/issues/list.js.map +1 -0
  70. package/dist/commands/context/issues/reopen.js +53 -0
  71. package/dist/commands/context/issues/reopen.js.map +1 -0
  72. package/dist/commands/context/issues/resolve.js +137 -0
  73. package/dist/commands/context/issues/resolve.js.map +1 -0
  74. package/dist/commands/context/jobs/get.js +2 -2
  75. package/dist/commands/context/jobs/get.js.map +1 -1
  76. package/dist/commands/context/query.js +92 -0
  77. package/dist/commands/context/query.js.map +1 -0
  78. package/dist/commands/deploy.js +13 -0
  79. package/dist/commands/deploy.js.map +1 -1
  80. package/dist/commands/media/prune-aspect-data.js +170 -0
  81. package/dist/commands/media/prune-aspect-data.js.map +1 -0
  82. package/dist/commands/media/pull-aspects.js +309 -0
  83. package/dist/commands/media/pull-aspects.js.map +1 -0
  84. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +17 -0
  85. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  86. package/dist/services/context.js +96 -3
  87. package/dist/services/context.js.map +1 -1
  88. package/dist/services/mediaLibraries.js +175 -8
  89. package/dist/services/mediaLibraries.js.map +1 -1
  90. package/dist/util/searchChoices.js +52 -0
  91. package/dist/util/searchChoices.js.map +1 -0
  92. package/oclif.manifest.json +2134 -1445
  93. package/package.json +9 -8
  94. package/templates/media-library-config/README.md +52 -0
  95. package/templates/media-library-config/eslint.config.txt +52 -0
  96. package/templates/media-library-config/src/fields/caption.tsx +29 -0
  97. package/templates/media-library-config/src/fields/notes.tsx +54 -0
  98. package/templates/shopify/.gitignore +0 -26
@@ -0,0 +1,105 @@
1
+ import { Args } 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 { getIssue } from '../../../services/context.js';
7
+ const getIssueDebug = subdebug('context:issues:get');
8
+ export class GetIssueCommand 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
+ issueId: Args.string({
16
+ description: 'Issue ID',
17
+ required: true
18
+ })
19
+ };
20
+ static description = 'Get details of an issue';
21
+ static enableJsonFlag = true;
22
+ static examples = [
23
+ {
24
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456',
25
+ description: 'Get details of a specific issue'
26
+ },
27
+ {
28
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456 --json',
29
+ description: 'Output the issue as JSON'
30
+ }
31
+ ];
32
+ async run() {
33
+ const { issueId, knowledgeBaseId } = this.args;
34
+ const { json } = this.flags;
35
+ let issue;
36
+ try {
37
+ issue = await getIssue(knowledgeBaseId, issueId);
38
+ } catch (error) {
39
+ getIssueDebug('Error getting issue', 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 issue: ${getErrorMessage(error)}`, {
46
+ exit: exitCodes.RUNTIME_ERROR
47
+ });
48
+ }
49
+ if (!issue) {
50
+ this.error(`Issue "${issueId}" not found on knowledge base "${knowledgeBaseId}"`, {
51
+ exit: exitCodes.RUNTIME_ERROR
52
+ });
53
+ }
54
+ if (json) {
55
+ this.logJson(issue);
56
+ return;
57
+ }
58
+ const { content } = issue;
59
+ const padTo = 13 // "Suggested fix" is the longest key
60
+ ;
61
+ this.log(formatKeyValue('ID', issue._id, {
62
+ padTo
63
+ }));
64
+ this.log(formatKeyValue('Kind', content.kind, {
65
+ padTo
66
+ }));
67
+ this.log(formatKeyValue('Severity', content.severity, {
68
+ padTo
69
+ }));
70
+ this.log(formatKeyValue('Status', issue.status, {
71
+ padTo
72
+ }));
73
+ this.log(formatKeyValue('Scope', content.scopePath, {
74
+ padTo
75
+ }));
76
+ this.log(formatKeyValue('Issue', content.issue, {
77
+ padTo
78
+ }));
79
+ this.log(formatKeyValue('Suggested fix', content.suggestedFix, {
80
+ padTo
81
+ }));
82
+ // Sides are labeled with the 0-based index that `issues resolve --side` takes
83
+ if (content.kind === 'conflict') {
84
+ for (const [index, side] of content.sides.entries()){
85
+ const entryPaths = side.entryPaths?.length ? ` (${side.entryPaths.join(', ')})` : '';
86
+ this.log(formatKeyValue(`Side [${index}]`, `${side.claim}${entryPaths}`, {
87
+ padTo
88
+ }));
89
+ }
90
+ if (content.suggested !== undefined) {
91
+ this.log(formatKeyValue('Suggested', `[${content.suggested}]`, {
92
+ padTo
93
+ }));
94
+ }
95
+ }
96
+ this.log(formatKeyValue('Resolution', issue.resolution === null ? '-' : `[${issue.resolution}]`, {
97
+ padTo
98
+ }));
99
+ this.log(formatKeyValue('Created', issue._createdAt, {
100
+ padTo
101
+ }));
102
+ }
103
+ }
104
+
105
+ //# sourceMappingURL=get.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/issues/get.ts"],"sourcesContent":["import {Args} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {formatKeyValue} from '../../../actions/debug/output.js'\nimport {getIssue} from '../../../services/context.js'\n\nconst getIssueDebug = subdebug('context:issues:get')\n\nexport class GetIssueCommand extends SanityCommand<typeof GetIssueCommand> {\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 issueId: Args.string({\n description: 'Issue ID',\n required: true,\n }),\n }\n\n static override description = 'Get details of an issue'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456',\n description: 'Get details of a specific issue',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456 --json',\n description: 'Output the issue as JSON',\n },\n ]\n\n public async run(): Promise<void> {\n const {issueId, knowledgeBaseId} = this.args\n const {json} = this.flags\n\n let issue\n try {\n issue = await getIssue(knowledgeBaseId, issueId)\n } catch (error) {\n getIssueDebug('Error getting issue', 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 issue: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (!issue) {\n this.error(`Issue \"${issueId}\" not found on knowledge base \"${knowledgeBaseId}\"`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (json) {\n this.logJson(issue)\n return\n }\n\n const {content} = issue\n const padTo = 13 // \"Suggested fix\" is the longest key\n this.log(formatKeyValue('ID', issue._id, {padTo}))\n this.log(formatKeyValue('Kind', content.kind, {padTo}))\n this.log(formatKeyValue('Severity', content.severity, {padTo}))\n this.log(formatKeyValue('Status', issue.status, {padTo}))\n this.log(formatKeyValue('Scope', content.scopePath, {padTo}))\n this.log(formatKeyValue('Issue', content.issue, {padTo}))\n this.log(formatKeyValue('Suggested fix', content.suggestedFix, {padTo}))\n // Sides are labeled with the 0-based index that `issues resolve --side` takes\n if (content.kind === 'conflict') {\n for (const [index, side] of content.sides.entries()) {\n const entryPaths = side.entryPaths?.length ? ` (${side.entryPaths.join(', ')})` : ''\n this.log(formatKeyValue(`Side [${index}]`, `${side.claim}${entryPaths}`, {padTo}))\n }\n if (content.suggested !== undefined) {\n this.log(formatKeyValue('Suggested', `[${content.suggested}]`, {padTo}))\n }\n }\n this.log(\n formatKeyValue('Resolution', issue.resolution === null ? '-' : `[${issue.resolution}]`, {\n padTo,\n }),\n )\n this.log(formatKeyValue('Created', issue._createdAt, {padTo}))\n }\n}\n"],"names":["Args","exitCodes","SanityCommand","subdebug","getErrorMessage","isHttpError","formatKeyValue","getIssue","getIssueDebug","GetIssueCommand","args","knowledgeBaseId","string","description","required","issueId","enableJsonFlag","examples","command","run","json","flags","issue","error","statusCode","exit","RUNTIME_ERROR","logJson","content","padTo","log","_id","kind","severity","status","scopePath","suggestedFix","index","side","sides","entries","entryPaths","length","join","claim","suggested","undefined","resolution","_createdAt"],"mappings":"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"}
@@ -0,0 +1,128 @@
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 { listIssues } from '../../../services/context.js';
6
+ const listIssuesDebug = subdebug('context:issues:list');
7
+ export class ListIssuesCommand extends SanityCommand {
8
+ static args = {
9
+ knowledgeBaseId: Args.string({
10
+ description: 'Knowledge base ID',
11
+ required: true
12
+ })
13
+ };
14
+ static description = 'List issues found by knowledge base builds';
15
+ static enableJsonFlag = true;
16
+ static examples = [
17
+ {
18
+ command: '<%= config.bin %> <%= command.id %> kb-abc123',
19
+ description: 'List all issues for a knowledge base'
20
+ },
21
+ {
22
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 --status open',
23
+ description: 'List issues awaiting triage'
24
+ },
25
+ {
26
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 --kind conflict --severity critical',
27
+ description: 'List critical conflicts'
28
+ },
29
+ {
30
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 --scope billing',
31
+ description: 'List issues about entries under billing/'
32
+ },
33
+ {
34
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 --json',
35
+ description: 'Output issues as JSON'
36
+ }
37
+ ];
38
+ static flags = {
39
+ kind: Flags.custom({
40
+ description: 'Only list issues of this kind',
41
+ // A Record forces every kind the client knows to be listed, so the options can't drift
42
+ options: Object.keys({
43
+ add_entry: true,
44
+ conflict: true,
45
+ gap: true,
46
+ merge_entry: true,
47
+ remove_entry: true,
48
+ split_entry: true,
49
+ update_required: true
50
+ })
51
+ })(),
52
+ scope: Flags.string({
53
+ description: 'Only list issues about this entry path or anything under it (`*` for knowledge-base-wide issues)',
54
+ helpValue: '<path>',
55
+ parse: async (input)=>{
56
+ const trimmed = input.trim().replace(/\/+$/, '');
57
+ if (trimmed === '') {
58
+ throw new Error('`--scope` cannot be empty if provided');
59
+ }
60
+ return trimmed;
61
+ }
62
+ }),
63
+ severity: Flags.custom({
64
+ description: 'Only list issues of this severity',
65
+ options: [
66
+ 'critical',
67
+ 'suggestion'
68
+ ]
69
+ })(),
70
+ status: Flags.custom({
71
+ description: 'Only list issues with this status (rejected = dismissed, accepted = resolved or applied)',
72
+ options: [
73
+ 'open',
74
+ 'accepted',
75
+ 'rejected'
76
+ ]
77
+ })()
78
+ };
79
+ async run() {
80
+ const { knowledgeBaseId } = this.args;
81
+ const { json, kind, scope, severity, status } = this.flags;
82
+ let issues;
83
+ try {
84
+ issues = await listIssues(knowledgeBaseId, status);
85
+ } catch (error) {
86
+ listIssuesDebug('Error listing issues', error);
87
+ if (isHttpError(error) && error.statusCode === 404) {
88
+ this.error(`Knowledge base "${knowledgeBaseId}" not found`, {
89
+ exit: exitCodes.RUNTIME_ERROR
90
+ });
91
+ }
92
+ this.error(`Failed to list issues: ${getErrorMessage(error)}`, {
93
+ exit: exitCodes.RUNTIME_ERROR
94
+ });
95
+ }
96
+ // The client only filters on status; the rest are applied here
97
+ const matches = issues.filter(({ content })=>(kind === undefined || content.kind === kind) && (severity === undefined || content.severity === severity) && (scope === undefined || content.scopePath === scope || content.scopePath.startsWith(`${scope}/`)));
98
+ if (json) {
99
+ this.logJson(matches);
100
+ return;
101
+ }
102
+ if (matches.length === 0) {
103
+ this.log('No issues found');
104
+ return;
105
+ }
106
+ // Two lines per issue so long IDs never wrap: ID and metadata, then the summary cut to fit
107
+ const width = Math.max(process.stdout.columns ?? 100, 40);
108
+ const blocks = matches.map(({ _id, content, status: issueStatus })=>{
109
+ const meta = [
110
+ content.kind,
111
+ content.severity,
112
+ issueStatus,
113
+ content.scopePath
114
+ ].join(' · ');
115
+ return `${_id} ${meta}\n ${truncate(content.issue, width - 2)}`;
116
+ });
117
+ this.log(blocks.join('\n\n'));
118
+ }
119
+ }
120
+ function truncate(text, maxLength) {
121
+ // Count characters, not UTF-16 units, so an emoji at the cut point isn't split
122
+ const chars = [
123
+ ...text.replaceAll(/\s+/g, ' ').trim()
124
+ ];
125
+ return chars.length <= maxLength ? chars.join('') : `${chars.slice(0, maxLength - 1).join('')}…`;
126
+ }
127
+
128
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/issues/list.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 {type Context, isHttpError} from '@sanity/client'\n\nimport {listIssues} from '../../../services/context.js'\n\nconst listIssuesDebug = subdebug('context:issues:list')\n\nexport class ListIssuesCommand extends SanityCommand<typeof ListIssuesCommand> {\n static override args = {\n knowledgeBaseId: Args.string({\n description: 'Knowledge base ID',\n required: true,\n }),\n }\n\n static override description = 'List issues found by knowledge base builds'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123',\n description: 'List all issues for a knowledge base',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --status open',\n description: 'List issues awaiting triage',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --kind conflict --severity critical',\n description: 'List critical conflicts',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --scope billing',\n description: 'List issues about entries under billing/',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 --json',\n description: 'Output issues as JSON',\n },\n ]\n\n static override flags = {\n kind: Flags.custom<IssueKind>({\n description: 'Only list issues of this kind',\n // A Record forces every kind the client knows to be listed, so the options can't drift\n options: Object.keys({\n add_entry: true,\n conflict: true,\n gap: true,\n merge_entry: true,\n remove_entry: true,\n split_entry: true,\n update_required: true,\n } satisfies Record<IssueKind, true>),\n })(),\n scope: Flags.string({\n description:\n 'Only list issues about this entry path or anything under it (`*` for knowledge-base-wide issues)',\n helpValue: '<path>',\n parse: async (input: string) => {\n const trimmed = input.trim().replace(/\\/+$/, '')\n if (trimmed === '') {\n throw new Error('`--scope` cannot be empty if provided')\n }\n return trimmed\n },\n }),\n severity: Flags.custom<Context.IssueDoc['content']['severity']>({\n description: 'Only list issues of this severity',\n options: ['critical', 'suggestion'],\n })(),\n status: Flags.custom<Context.IssueDoc['status']>({\n description:\n 'Only list issues with this status (rejected = dismissed, accepted = resolved or applied)',\n options: ['open', 'accepted', 'rejected'],\n })(),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {knowledgeBaseId} = this.args\n const {json, kind, scope, severity, status} = this.flags\n\n let issues\n try {\n issues = await listIssues(knowledgeBaseId, status)\n } catch (error) {\n listIssuesDebug('Error listing issues', 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 list issues: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // The client only filters on status; the rest are applied here\n const matches = issues.filter(\n ({content}) =>\n (kind === undefined || content.kind === kind) &&\n (severity === undefined || content.severity === severity) &&\n (scope === undefined ||\n content.scopePath === scope ||\n content.scopePath.startsWith(`${scope}/`)),\n )\n\n if (json) {\n this.logJson(matches)\n return\n }\n\n if (matches.length === 0) {\n this.log('No issues found')\n return\n }\n\n // Two lines per issue so long IDs never wrap: ID and metadata, then the summary cut to fit\n const width = Math.max(process.stdout.columns ?? 100, 40)\n const blocks = matches.map(({_id, content, status: issueStatus}) => {\n const meta = [content.kind, content.severity, issueStatus, content.scopePath].join(' · ')\n return `${_id} ${meta}\\n ${truncate(content.issue, width - 2)}`\n })\n this.log(blocks.join('\\n\\n'))\n }\n}\n\ntype IssueKind = Context.IssueDoc['content']['kind']\n\nfunction truncate(text: string, maxLength: number): string {\n // Count characters, not UTF-16 units, so an emoji at the cut point isn't split\n const chars = [...text.replaceAll(/\\s+/g, ' ').trim()]\n return chars.length <= maxLength ? chars.join('') : `${chars.slice(0, maxLength - 1).join('')}…`\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","isHttpError","listIssues","listIssuesDebug","ListIssuesCommand","args","knowledgeBaseId","string","description","required","enableJsonFlag","examples","command","flags","kind","custom","options","Object","keys","add_entry","conflict","gap","merge_entry","remove_entry","split_entry","update_required","scope","helpValue","parse","input","trimmed","trim","replace","Error","severity","status","run","json","issues","error","statusCode","exit","RUNTIME_ERROR","matches","filter","content","undefined","scopePath","startsWith","logJson","length","log","width","Math","max","process","stdout","columns","blocks","map","_id","issueStatus","meta","join","truncate","issue","text","maxLength","chars","replaceAll","slice"],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { Args } 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 { reopenIssue } from '../../../services/context.js';
6
+ const reopenIssueDebug = subdebug('context:issues:reopen');
7
+ export class ReopenIssueCommand extends SanityCommand {
8
+ static args = {
9
+ knowledgeBaseId: Args.string({
10
+ description: 'Knowledge base ID',
11
+ required: true
12
+ }),
13
+ // eslint-disable-next-line perfectionist/sort-objects -- positional order: knowledge base first
14
+ issueId: Args.string({
15
+ description: 'Issue ID',
16
+ required: true
17
+ })
18
+ };
19
+ static description = 'Reopen a resolved conflict issue';
20
+ static examples = [
21
+ {
22
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456',
23
+ description: 'Return a resolved conflict issue to triage'
24
+ }
25
+ ];
26
+ async run() {
27
+ const { issueId, knowledgeBaseId } = this.args;
28
+ let reopened;
29
+ try {
30
+ reopened = await reopenIssue(knowledgeBaseId, issueId);
31
+ } catch (error) {
32
+ reopenIssueDebug('Error reopening issue', error);
33
+ if (isHttpError(error) && error.statusCode === 404) {
34
+ this.error(`Issue "${issueId}" not found on knowledge base "${knowledgeBaseId}"`, {
35
+ exit: exitCodes.RUNTIME_ERROR
36
+ });
37
+ }
38
+ this.error(`Failed to reopen issue: ${getErrorMessage(error)}`, {
39
+ exit: exitCodes.RUNTIME_ERROR
40
+ });
41
+ }
42
+ // The API returns the issue unchanged when it cannot be reopened
43
+ if (reopened.status !== 'open') {
44
+ const status = reopened.status === 'rejected' ? 'rejected (dismissed)' : 'accepted (resolved or applied)';
45
+ this.error(`Issue "${issueId}" is ${status} and cannot be reopened.\nOnly resolved conflict issues can be reopened.`, {
46
+ exit: exitCodes.RUNTIME_ERROR
47
+ });
48
+ }
49
+ this.log('Issue reopened');
50
+ }
51
+ }
52
+
53
+ //# sourceMappingURL=reopen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/issues/reopen.ts"],"sourcesContent":["import {Args} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {reopenIssue} from '../../../services/context.js'\n\nconst reopenIssueDebug = subdebug('context:issues:reopen')\n\nexport class ReopenIssueCommand extends SanityCommand<typeof ReopenIssueCommand> {\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 issueId: Args.string({\n description: 'Issue ID',\n required: true,\n }),\n }\n\n static override description = 'Reopen a resolved conflict issue'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456',\n description: 'Return a resolved conflict issue to triage',\n },\n ]\n\n public async run(): Promise<void> {\n const {issueId, knowledgeBaseId} = this.args\n\n let reopened\n try {\n reopened = await reopenIssue(knowledgeBaseId, issueId)\n } catch (error) {\n reopenIssueDebug('Error reopening issue', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`Issue \"${issueId}\" not found on knowledge base \"${knowledgeBaseId}\"`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n this.error(`Failed to reopen issue: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // The API returns the issue unchanged when it cannot be reopened\n if (reopened.status !== 'open') {\n const status =\n reopened.status === 'rejected' ? 'rejected (dismissed)' : 'accepted (resolved or applied)'\n this.error(\n `Issue \"${issueId}\" is ${status} and cannot be reopened.\\nOnly resolved conflict issues can be reopened.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n this.log('Issue reopened')\n }\n}\n"],"names":["Args","exitCodes","SanityCommand","subdebug","getErrorMessage","isHttpError","reopenIssue","reopenIssueDebug","ReopenIssueCommand","args","knowledgeBaseId","string","description","required","issueId","examples","command","run","reopened","error","statusCode","exit","RUNTIME_ERROR","status","log"],"mappings":"AAAA,SAAQA,IAAI,QAAO,cAAa;AAChC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,WAAW,QAAO,+BAA8B;AAExD,MAAMC,mBAAmBJ,SAAS;AAElC,OAAO,MAAMK,2BAA2BN;IACtC,OAAgBO,OAAO;QACrBC,iBAAiBV,KAAKW,MAAM,CAAC;YAC3BC,aAAa;YACbC,UAAU;QACZ;QACA,gGAAgG;QAChGC,SAASd,KAAKW,MAAM,CAAC;YACnBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,mCAAkC;IAEhE,OAAgBG,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,MAAaK,MAAqB;QAChC,MAAM,EAACH,OAAO,EAAEJ,eAAe,EAAC,GAAG,IAAI,CAACD,IAAI;QAE5C,IAAIS;QACJ,IAAI;YACFA,WAAW,MAAMZ,YAAYI,iBAAiBI;QAChD,EAAE,OAAOK,OAAO;YACdZ,iBAAiB,yBAAyBY;YAC1C,IAAId,YAAYc,UAAUA,MAAMC,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACD,KAAK,CAAC,CAAC,OAAO,EAAEL,QAAQ,+BAA+B,EAAEJ,gBAAgB,CAAC,CAAC,EAAE;oBAChFW,MAAMpB,UAAUqB,aAAa;gBAC/B;YACF;YACA,IAAI,CAACH,KAAK,CAAC,CAAC,wBAAwB,EAAEf,gBAAgBe,QAAQ,EAAE;gBAC9DE,MAAMpB,UAAUqB,aAAa;YAC/B;QACF;QAEA,iEAAiE;QACjE,IAAIJ,SAASK,MAAM,KAAK,QAAQ;YAC9B,MAAMA,SACJL,SAASK,MAAM,KAAK,aAAa,yBAAyB;YAC5D,IAAI,CAACJ,KAAK,CACR,CAAC,OAAO,EAAEL,QAAQ,KAAK,EAAES,OAAO,wEAAwE,CAAC,EACzG;gBAACF,MAAMpB,UAAUqB,aAAa;YAAA;QAElC;QAEA,IAAI,CAACE,GAAG,CAAC;IACX;AACF"}
@@ -0,0 +1,137 @@
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 { requiredWhenUnattended } from '@sanity/cli-core/flags';
5
+ import { select } from '@sanity/cli-core/ux';
6
+ import { isHttpError } from '@sanity/client';
7
+ import { formatJobFailure, watchJob } from '../../../actions/context/watchJob.js';
8
+ import { formatKeyValue } from '../../../actions/debug/output.js';
9
+ import { getIssue, resolveIssue } from '../../../services/context.js';
10
+ const resolveIssueDebug = subdebug('context:issues:resolve');
11
+ export class ResolveIssueCommand extends SanityCommand {
12
+ static args = {
13
+ knowledgeBaseId: Args.string({
14
+ description: 'Knowledge base ID',
15
+ required: true
16
+ }),
17
+ // eslint-disable-next-line perfectionist/sort-objects -- positional order: knowledge base first
18
+ issueId: Args.string({
19
+ description: 'Issue ID',
20
+ required: true
21
+ })
22
+ };
23
+ static description = 'Resolve a conflict issue by choosing the correct side\n\nResolving also creates a standing instruction that future builds follow. `sanity context issues reopen` removes it.';
24
+ static examples = [
25
+ {
26
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456',
27
+ description: 'Interactively choose the correct side of a conflict'
28
+ },
29
+ {
30
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456 --side 1 --watch',
31
+ description: 'Choose side 1 and wait for the entry to be rewritten'
32
+ },
33
+ {
34
+ command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456 --side 0',
35
+ description: 'Keep the entry as it is (for a conflict on a single entry)'
36
+ }
37
+ ];
38
+ static flags = {
39
+ side: requiredWhenUnattended(Flags.integer({
40
+ description: 'Index of the correct side, as listed by `sanity context issues get`',
41
+ min: 0
42
+ })),
43
+ watch: Flags.boolean({
44
+ default: false,
45
+ description: 'Wait for the entry rewrite to finish, exiting non-zero if it fails'
46
+ })
47
+ };
48
+ async run() {
49
+ const { issueId, knowledgeBaseId } = this.args;
50
+ const { side: sideFlag, watch } = this.flags;
51
+ const side = sideFlag ?? await this.promptForSide(knowledgeBaseId, issueId);
52
+ let resolved;
53
+ try {
54
+ resolved = await resolveIssue(knowledgeBaseId, issueId, side);
55
+ } catch (error) {
56
+ resolveIssueDebug('Error resolving issue', error);
57
+ if (isHttpError(error) && error.statusCode === 404) {
58
+ this.error(`Issue "${issueId}" not found on knowledge base "${knowledgeBaseId}"`, {
59
+ exit: exitCodes.RUNTIME_ERROR
60
+ });
61
+ }
62
+ this.error(`Failed to resolve issue: ${getErrorMessage(error)}`, {
63
+ exit: exitCodes.RUNTIME_ERROR
64
+ });
65
+ }
66
+ this.log('Issue resolved');
67
+ const { jobId } = resolved;
68
+ if (jobId === null) {
69
+ return;
70
+ }
71
+ this.log(formatKeyValue('Job ID', jobId));
72
+ this.log(`Track it with: sanity context jobs get ${knowledgeBaseId} ${jobId}`);
73
+ if (!watch) {
74
+ return;
75
+ }
76
+ let job;
77
+ try {
78
+ job = await watchJob(knowledgeBaseId, jobId, {
79
+ label: 'Rewriting entry',
80
+ successText: 'Entry rewritten'
81
+ });
82
+ } catch (error) {
83
+ resolveIssueDebug('Error watching rewrite job', error);
84
+ this.error(`Failed to watch rewrite job: ${getErrorMessage(error)}`, {
85
+ exit: exitCodes.RUNTIME_ERROR
86
+ });
87
+ }
88
+ if (job.status !== 'succeeded') {
89
+ this.error(formatJobFailure('Rewrite', job), {
90
+ exit: exitCodes.RUNTIME_ERROR
91
+ });
92
+ }
93
+ }
94
+ async promptForSide(knowledgeBaseId, issueId) {
95
+ let issue;
96
+ try {
97
+ issue = await getIssue(knowledgeBaseId, issueId);
98
+ } catch (error) {
99
+ resolveIssueDebug('Error getting issue', error);
100
+ if (isHttpError(error) && error.statusCode === 404) {
101
+ this.error(`Knowledge base "${knowledgeBaseId}" not found`, {
102
+ exit: exitCodes.RUNTIME_ERROR
103
+ });
104
+ }
105
+ this.error(`Failed to get issue: ${getErrorMessage(error)}`, {
106
+ exit: exitCodes.RUNTIME_ERROR
107
+ });
108
+ }
109
+ if (!issue) {
110
+ this.error(`Issue "${issueId}" not found on knowledge base "${knowledgeBaseId}"`, {
111
+ exit: exitCodes.RUNTIME_ERROR
112
+ });
113
+ }
114
+ if (issue.status === 'rejected') {
115
+ this.error(`Issue "${issueId}" is rejected (dismissed) and cannot be resolved`, {
116
+ exit: exitCodes.RUNTIME_ERROR
117
+ });
118
+ }
119
+ const { content } = issue;
120
+ if (content.kind !== 'conflict') {
121
+ this.error(`Only conflict issues can be resolved, "${issueId}" is of kind ${content.kind}`, {
122
+ exit: exitCodes.RUNTIME_ERROR
123
+ });
124
+ }
125
+ return select({
126
+ choices: content.sides.map((side, index)=>({
127
+ name: `[${index}] ${side.claim}`,
128
+ value: index
129
+ })),
130
+ // Re-resolving keeps the current choice preselected, so Enter can't flip it
131
+ default: issue.resolution ?? content.suggested,
132
+ message: 'Which side is correct?'
133
+ });
134
+ }
135
+ }
136
+
137
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/context/issues/resolve.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 {requiredWhenUnattended} from '@sanity/cli-core/flags'\nimport {select} from '@sanity/cli-core/ux'\nimport {isHttpError} from '@sanity/client'\n\nimport {formatJobFailure, watchJob} from '../../../actions/context/watchJob.js'\nimport {formatKeyValue} from '../../../actions/debug/output.js'\nimport {getIssue, resolveIssue} from '../../../services/context.js'\n\nconst resolveIssueDebug = subdebug('context:issues:resolve')\n\nexport class ResolveIssueCommand extends SanityCommand<typeof ResolveIssueCommand> {\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 issueId: Args.string({\n description: 'Issue ID',\n required: true,\n }),\n }\n\n static override description =\n 'Resolve a conflict issue by choosing the correct side\\n\\nResolving also creates a standing instruction that future builds follow. `sanity context issues reopen` removes it.'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456',\n description: 'Interactively choose the correct side of a conflict',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456 --side 1 --watch',\n description: 'Choose side 1 and wait for the entry to be rewritten',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 issue.def456 --side 0',\n description: 'Keep the entry as it is (for a conflict on a single entry)',\n },\n ]\n\n static override flags = {\n side: requiredWhenUnattended(\n Flags.integer({\n description: 'Index of the correct side, as listed by `sanity context issues get`',\n min: 0,\n }),\n ),\n watch: Flags.boolean({\n default: false,\n description: 'Wait for the entry rewrite to finish, exiting non-zero if it fails',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {issueId, knowledgeBaseId} = this.args\n const {side: sideFlag, watch} = this.flags\n\n const side = sideFlag ?? (await this.promptForSide(knowledgeBaseId, issueId))\n\n let resolved\n try {\n resolved = await resolveIssue(knowledgeBaseId, issueId, side)\n } catch (error) {\n resolveIssueDebug('Error resolving issue', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`Issue \"${issueId}\" not found on knowledge base \"${knowledgeBaseId}\"`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n this.error(`Failed to resolve issue: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n this.log('Issue resolved')\n\n const {jobId} = resolved\n if (jobId === null) {\n return\n }\n\n this.log(formatKeyValue('Job ID', jobId))\n this.log(`Track it with: sanity context jobs get ${knowledgeBaseId} ${jobId}`)\n\n if (!watch) {\n return\n }\n\n let job\n try {\n job = await watchJob(knowledgeBaseId, jobId, {\n label: 'Rewriting entry',\n successText: 'Entry rewritten',\n })\n } catch (error) {\n resolveIssueDebug('Error watching rewrite job', error)\n this.error(`Failed to watch rewrite job: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (job.status !== 'succeeded') {\n this.error(formatJobFailure('Rewrite', job), {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async promptForSide(knowledgeBaseId: string, issueId: string): Promise<number> {\n let issue\n try {\n issue = await getIssue(knowledgeBaseId, issueId)\n } catch (error) {\n resolveIssueDebug('Error getting issue', 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 issue: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (!issue) {\n this.error(`Issue \"${issueId}\" not found on knowledge base \"${knowledgeBaseId}\"`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n if (issue.status === 'rejected') {\n this.error(`Issue \"${issueId}\" is rejected (dismissed) and cannot be resolved`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const {content} = issue\n if (content.kind !== 'conflict') {\n this.error(`Only conflict issues can be resolved, \"${issueId}\" is of kind ${content.kind}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n return select({\n choices: content.sides.map((side, index) => ({\n name: `[${index}] ${side.claim}`,\n value: index,\n })),\n // Re-resolving keeps the current choice preselected, so Enter can't flip it\n default: issue.resolution ?? content.suggested,\n message: 'Which side is correct?',\n })\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","requiredWhenUnattended","select","isHttpError","formatJobFailure","watchJob","formatKeyValue","getIssue","resolveIssue","resolveIssueDebug","ResolveIssueCommand","args","knowledgeBaseId","string","description","required","issueId","examples","command","flags","side","integer","min","watch","boolean","default","run","sideFlag","promptForSide","resolved","error","statusCode","exit","RUNTIME_ERROR","log","jobId","job","label","successText","status","issue","content","kind","choices","sides","map","index","name","claim","value","resolution","suggested","message"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { Args, Flags } from '@oclif/core';
2
2
  import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
3
  import { getErrorMessage, JsonOutputError } from '@sanity/cli-core/errors';
4
4
  import { isHttpError } from '@sanity/client';
5
- import { watchJob } from '../../../actions/context/watchJob.js';
5
+ import { formatJobFailure, watchJob } from '../../../actions/context/watchJob.js';
6
6
  import { formatKeyValue } from '../../../actions/debug/output.js';
7
7
  import { getJob } from '../../../services/context.js';
8
8
  const getJobDebug = subdebug('context:jobs:get');
@@ -56,7 +56,7 @@ export class GetJobCommand extends SanityCommand {
56
56
  });
57
57
  }
58
58
  const jobFailed = watch && job.status !== 'succeeded';
59
- const failureMessage = `Job ${job.status}${job.error ? `: ${job.error}` : ''}`;
59
+ const failureMessage = formatJobFailure('Job', job);
60
60
  if (json) {
61
61
  if (jobFailed) throw new JsonOutputError(failureMessage, job, exitCodes.RUNTIME_ERROR);
62
62
  this.logJson(job);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/commands/context/jobs/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, JsonOutputError} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {watchJob} from '../../../actions/context/watchJob.js'\nimport {formatKeyValue} from '../../../actions/debug/output.js'\nimport {getJob} from '../../../services/context.js'\n\nconst getJobDebug = subdebug('context:jobs:get')\n\nexport class GetJobCommand extends SanityCommand<typeof GetJobCommand> {\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 jobId: Args.string({\n description: 'Job ID',\n required: true,\n }),\n }\n\n static override description = 'Get the status of a knowledge base job'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 job-def456',\n description: 'Get the current status of a job',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 job-def456 --watch',\n description: 'Wait for the job to finish (non-zero exit if it does not succeed)',\n },\n ]\n\n static override flags = {\n watch: Flags.boolean({\n default: false,\n description: 'Poll until the job reaches a terminal state',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {jobId, knowledgeBaseId} = this.args\n const {json, watch} = this.flags\n\n let job\n try {\n job = watch\n ? await watchJob(knowledgeBaseId, jobId, {label: 'Waiting for job'})\n : await getJob(knowledgeBaseId, jobId)\n } catch (error) {\n getJobDebug('Error getting job', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`Job \"${jobId}\" not found on knowledge base \"${knowledgeBaseId}\"`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n this.error(`Failed to get job: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const jobFailed = watch && job.status !== 'succeeded'\n const failureMessage = `Job ${job.status}${job.error ? `: ${job.error}` : ''}`\n if (json) {\n if (jobFailed) throw new JsonOutputError(failureMessage, job, exitCodes.RUNTIME_ERROR)\n this.logJson(job)\n } else {\n const padTo = 9 // \"Completed\" is the longest key\n this.log(formatKeyValue('ID', job.id, {padTo}))\n this.log(formatKeyValue('Status', job.status, {padTo}))\n this.log(formatKeyValue('Started', job.startedAt ?? '-', {padTo}))\n this.log(formatKeyValue('Completed', job.completedAt ?? '-', {padTo}))\n this.log(formatKeyValue('Error', job.error ?? '-', {padTo}))\n }\n\n if (jobFailed) {\n this.error(failureMessage, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","JsonOutputError","isHttpError","watchJob","formatKeyValue","getJob","getJobDebug","GetJobCommand","args","knowledgeBaseId","string","description","required","jobId","enableJsonFlag","examples","command","flags","watch","boolean","default","run","json","job","label","error","statusCode","exit","RUNTIME_ERROR","jobFailed","status","failureMessage","logJson","padTo","log","id","startedAt","completedAt"],"mappings":"AAAA,SAAQA,IAAI,EAAEC,KAAK,QAAO,cAAa;AAEvC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,EAAEC,eAAe,QAAO,0BAAyB;AACxE,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,QAAQ,QAAO,uCAAsC;AAC7D,SAAQC,cAAc,QAAO,mCAAkC;AAC/D,SAAQC,MAAM,QAAO,+BAA8B;AAEnD,MAAMC,cAAcP,SAAS;AAE7B,OAAO,MAAMQ,sBAAsBT;IACjC,OAAgBU,OAAO;QACrBC,iBAAiBd,KAAKe,MAAM,CAAC;YAC3BC,aAAa;YACbC,UAAU;QACZ;QACA,gGAAgG;QAChGC,OAAOlB,KAAKe,MAAM,CAAC;YACjBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,yCAAwC;IAEtE,OAAgBG,iBAAiB,KAAI;IAErC,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTL,aAAa;QACf;QACA;YACEK,SAAS;YACTL,aAAa;QACf;KACD,CAAA;IAED,OAAgBM,QAAQ;QACtBC,OAAOtB,MAAMuB,OAAO,CAAC;YACnBC,SAAS;YACTT,aAAa;QACf;IACF,EAAqB;IAErB,MAAaU,MAAqB;QAChC,MAAM,EAACR,KAAK,EAAEJ,eAAe,EAAC,GAAG,IAAI,CAACD,IAAI;QAC1C,MAAM,EAACc,IAAI,EAAEJ,KAAK,EAAC,GAAG,IAAI,CAACD,KAAK;QAEhC,IAAIM;QACJ,IAAI;YACFA,MAAML,QACF,MAAMf,SAASM,iBAAiBI,OAAO;gBAACW,OAAO;YAAiB,KAChE,MAAMnB,OAAOI,iBAAiBI;QACpC,EAAE,OAAOY,OAAO;YACdnB,YAAY,qBAAqBmB;YACjC,IAAIvB,YAAYuB,UAAUA,MAAMC,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACD,KAAK,CAAC,CAAC,KAAK,EAAEZ,MAAM,+BAA+B,EAAEJ,gBAAgB,CAAC,CAAC,EAAE;oBAC5EkB,MAAM9B,UAAU+B,aAAa;gBAC/B;YACF;YACA,IAAI,CAACH,KAAK,CAAC,CAAC,mBAAmB,EAAEzB,gBAAgByB,QAAQ,EAAE;gBACzDE,MAAM9B,UAAU+B,aAAa;YAC/B;QACF;QAEA,MAAMC,YAAYX,SAASK,IAAIO,MAAM,KAAK;QAC1C,MAAMC,iBAAiB,CAAC,IAAI,EAAER,IAAIO,MAAM,GAAGP,IAAIE,KAAK,GAAG,CAAC,EAAE,EAAEF,IAAIE,KAAK,EAAE,GAAG,IAAI;QAC9E,IAAIH,MAAM;YACR,IAAIO,WAAW,MAAM,IAAI5B,gBAAgB8B,gBAAgBR,KAAK1B,UAAU+B,aAAa;YACrF,IAAI,CAACI,OAAO,CAACT;QACf,OAAO;YACL,MAAMU,QAAQ,EAAE,iCAAiC;;YACjD,IAAI,CAACC,GAAG,CAAC9B,eAAe,MAAMmB,IAAIY,EAAE,EAAE;gBAACF;YAAK;YAC5C,IAAI,CAACC,GAAG,CAAC9B,eAAe,UAAUmB,IAAIO,MAAM,EAAE;gBAACG;YAAK;YACpD,IAAI,CAACC,GAAG,CAAC9B,eAAe,WAAWmB,IAAIa,SAAS,IAAI,KAAK;gBAACH;YAAK;YAC/D,IAAI,CAACC,GAAG,CAAC9B,eAAe,aAAamB,IAAIc,WAAW,IAAI,KAAK;gBAACJ;YAAK;YACnE,IAAI,CAACC,GAAG,CAAC9B,eAAe,SAASmB,IAAIE,KAAK,IAAI,KAAK;gBAACQ;YAAK;QAC3D;QAEA,IAAIJ,WAAW;YACb,IAAI,CAACJ,KAAK,CAACM,gBAAgB;gBACzBJ,MAAM9B,UAAU+B,aAAa;YAC/B;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../../../src/commands/context/jobs/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, JsonOutputError} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {formatJobFailure, watchJob} from '../../../actions/context/watchJob.js'\nimport {formatKeyValue} from '../../../actions/debug/output.js'\nimport {getJob} from '../../../services/context.js'\n\nconst getJobDebug = subdebug('context:jobs:get')\n\nexport class GetJobCommand extends SanityCommand<typeof GetJobCommand> {\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 jobId: Args.string({\n description: 'Job ID',\n required: true,\n }),\n }\n\n static override description = 'Get the status of a knowledge base job'\n\n static override enableJsonFlag = true\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 job-def456',\n description: 'Get the current status of a job',\n },\n {\n command: '<%= config.bin %> <%= command.id %> kb-abc123 job-def456 --watch',\n description: 'Wait for the job to finish (non-zero exit if it does not succeed)',\n },\n ]\n\n static override flags = {\n watch: Flags.boolean({\n default: false,\n description: 'Poll until the job reaches a terminal state',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {jobId, knowledgeBaseId} = this.args\n const {json, watch} = this.flags\n\n let job\n try {\n job = watch\n ? await watchJob(knowledgeBaseId, jobId, {label: 'Waiting for job'})\n : await getJob(knowledgeBaseId, jobId)\n } catch (error) {\n getJobDebug('Error getting job', error)\n if (isHttpError(error) && error.statusCode === 404) {\n this.error(`Job \"${jobId}\" not found on knowledge base \"${knowledgeBaseId}\"`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n this.error(`Failed to get job: ${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const jobFailed = watch && job.status !== 'succeeded'\n const failureMessage = formatJobFailure('Job', job)\n if (json) {\n if (jobFailed) throw new JsonOutputError(failureMessage, job, exitCodes.RUNTIME_ERROR)\n this.logJson(job)\n } else {\n const padTo = 9 // \"Completed\" is the longest key\n this.log(formatKeyValue('ID', job.id, {padTo}))\n this.log(formatKeyValue('Status', job.status, {padTo}))\n this.log(formatKeyValue('Started', job.startedAt ?? '-', {padTo}))\n this.log(formatKeyValue('Completed', job.completedAt ?? '-', {padTo}))\n this.log(formatKeyValue('Error', job.error ?? '-', {padTo}))\n }\n\n if (jobFailed) {\n this.error(failureMessage, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","JsonOutputError","isHttpError","formatJobFailure","watchJob","formatKeyValue","getJob","getJobDebug","GetJobCommand","args","knowledgeBaseId","string","description","required","jobId","enableJsonFlag","examples","command","flags","watch","boolean","default","run","json","job","label","error","statusCode","exit","RUNTIME_ERROR","jobFailed","status","failureMessage","logJson","padTo","log","id","startedAt","completedAt"],"mappings":"AAAA,SAAQA,IAAI,EAAEC,KAAK,QAAO,cAAa;AAEvC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,EAAEC,eAAe,QAAO,0BAAyB;AACxE,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,gBAAgB,EAAEC,QAAQ,QAAO,uCAAsC;AAC/E,SAAQC,cAAc,QAAO,mCAAkC;AAC/D,SAAQC,MAAM,QAAO,+BAA8B;AAEnD,MAAMC,cAAcR,SAAS;AAE7B,OAAO,MAAMS,sBAAsBV;IACjC,OAAgBW,OAAO;QACrBC,iBAAiBf,KAAKgB,MAAM,CAAC;YAC3BC,aAAa;YACbC,UAAU;QACZ;QACA,gGAAgG;QAChGC,OAAOnB,KAAKgB,MAAM,CAAC;YACjBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,yCAAwC;IAEtE,OAAgBG,iBAAiB,KAAI;IAErC,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTL,aAAa;QACf;QACA;YACEK,SAAS;YACTL,aAAa;QACf;KACD,CAAA;IAED,OAAgBM,QAAQ;QACtBC,OAAOvB,MAAMwB,OAAO,CAAC;YACnBC,SAAS;YACTT,aAAa;QACf;IACF,EAAqB;IAErB,MAAaU,MAAqB;QAChC,MAAM,EAACR,KAAK,EAAEJ,eAAe,EAAC,GAAG,IAAI,CAACD,IAAI;QAC1C,MAAM,EAACc,IAAI,EAAEJ,KAAK,EAAC,GAAG,IAAI,CAACD,KAAK;QAEhC,IAAIM;QACJ,IAAI;YACFA,MAAML,QACF,MAAMf,SAASM,iBAAiBI,OAAO;gBAACW,OAAO;YAAiB,KAChE,MAAMnB,OAAOI,iBAAiBI;QACpC,EAAE,OAAOY,OAAO;YACdnB,YAAY,qBAAqBmB;YACjC,IAAIxB,YAAYwB,UAAUA,MAAMC,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACD,KAAK,CAAC,CAAC,KAAK,EAAEZ,MAAM,+BAA+B,EAAEJ,gBAAgB,CAAC,CAAC,EAAE;oBAC5EkB,MAAM/B,UAAUgC,aAAa;gBAC/B;YACF;YACA,IAAI,CAACH,KAAK,CAAC,CAAC,mBAAmB,EAAE1B,gBAAgB0B,QAAQ,EAAE;gBACzDE,MAAM/B,UAAUgC,aAAa;YAC/B;QACF;QAEA,MAAMC,YAAYX,SAASK,IAAIO,MAAM,KAAK;QAC1C,MAAMC,iBAAiB7B,iBAAiB,OAAOqB;QAC/C,IAAID,MAAM;YACR,IAAIO,WAAW,MAAM,IAAI7B,gBAAgB+B,gBAAgBR,KAAK3B,UAAUgC,aAAa;YACrF,IAAI,CAACI,OAAO,CAACT;QACf,OAAO;YACL,MAAMU,QAAQ,EAAE,iCAAiC;;YACjD,IAAI,CAACC,GAAG,CAAC9B,eAAe,MAAMmB,IAAIY,EAAE,EAAE;gBAACF;YAAK;YAC5C,IAAI,CAACC,GAAG,CAAC9B,eAAe,UAAUmB,IAAIO,MAAM,EAAE;gBAACG;YAAK;YACpD,IAAI,CAACC,GAAG,CAAC9B,eAAe,WAAWmB,IAAIa,SAAS,IAAI,KAAK;gBAACH;YAAK;YAC/D,IAAI,CAACC,GAAG,CAAC9B,eAAe,aAAamB,IAAIc,WAAW,IAAI,KAAK;gBAACJ;YAAK;YACnE,IAAI,CAACC,GAAG,CAAC9B,eAAe,SAASmB,IAAIE,KAAK,IAAI,KAAK;gBAACQ;YAAK;QAC3D;QAEA,IAAIJ,WAAW;YACb,IAAI,CAACJ,KAAK,CAACM,gBAAgB;gBACzBJ,MAAM/B,UAAUgC,aAAa;YAC/B;QACF;IACF;AACF"}
@@ -0,0 +1,92 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { colorizeJson, exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
+ import { getErrorMessage } from '@sanity/cli-core/errors';
4
+ import { isRecord } from '@sanity/cli-core/util';
5
+ import { MissingOrganizationError, resolveOrganizationId } from '../../actions/context/resolveOrganizationId.js';
6
+ import { queryContext } from '../../services/context.js';
7
+ import { getOrganizationFlag } from '../../util/sharedFlags.js';
8
+ import { defineCommandTelemetry } from '../../util/telemetry/commandTelemetry.js';
9
+ const queryContextDebug = subdebug('context:query');
10
+ const flags = {
11
+ ...getOrganizationFlag({
12
+ description: 'Organization to query',
13
+ semantics: 'override'
14
+ }),
15
+ params: Flags.custom({
16
+ description: 'Query parameters as a JSON object',
17
+ helpValue: '<json>',
18
+ parse: async (input)=>{
19
+ let parsed;
20
+ try {
21
+ parsed = JSON.parse(input);
22
+ } catch {
23
+ throw new Error('`--params` must be valid JSON');
24
+ }
25
+ if (!isRecord(parsed)) {
26
+ throw new Error('`--params` must be a JSON object, like \'{"type": "issue"}\'');
27
+ }
28
+ return parsed;
29
+ }
30
+ })()
31
+ };
32
+ export class QueryContextCommand extends SanityCommand {
33
+ static args = {
34
+ query: Args.string({
35
+ description: "GROQ query to run against the organization's Context documents",
36
+ required: true
37
+ })
38
+ };
39
+ static description = "Query an organization's Context documents with GROQ";
40
+ static enableJsonFlag = true;
41
+ static examples = [
42
+ {
43
+ command: '<%= config.bin %> <%= command.id %> \'*[_type == "sanity.context.issue" && status == "open"]{_id, knowledgeBaseId}\'',
44
+ description: 'List open issues across all knowledge bases'
45
+ },
46
+ {
47
+ command: '<%= config.bin %> <%= command.id %> \'*[_type == "sanity.context.entry" && knowledgeBaseId == $kb].path\' --params \'{"kb": "kb-abc123"}\' --json',
48
+ description: 'List the entry paths of one knowledge base as plain JSON'
49
+ }
50
+ ];
51
+ static flags = flags;
52
+ static telemetry = defineCommandTelemetry(flags, {
53
+ redact: [
54
+ 'params'
55
+ ]
56
+ });
57
+ async run() {
58
+ const { query } = this.args;
59
+ const { json, organization, params } = this.flags;
60
+ let organizationId;
61
+ try {
62
+ organizationId = await resolveOrganizationId({
63
+ configuredOrganizationId: (await this.tryGetCliConfig()).app?.organizationId,
64
+ flagOrganizationId: organization,
65
+ unattended: this.isUnattended()
66
+ });
67
+ } catch (error) {
68
+ queryContextDebug('Error resolving organization', error);
69
+ this.error(getErrorMessage(error), {
70
+ exit: error instanceof MissingOrganizationError ? exitCodes.USAGE_ERROR : exitCodes.RUNTIME_ERROR
71
+ });
72
+ }
73
+ let result;
74
+ try {
75
+ result = await queryContext(organizationId, query, params);
76
+ } catch (error) {
77
+ queryContextDebug('Error running query', error);
78
+ this.error(`Failed to run query: ${getErrorMessage(error)}`, {
79
+ exit: exitCodes.RUNTIME_ERROR
80
+ });
81
+ }
82
+ // GROQ returns null for no match; guard undefined, which colorizeJson cannot tokenize
83
+ const output = result ?? null;
84
+ if (json) {
85
+ this.logJson(output);
86
+ return;
87
+ }
88
+ this.log(colorizeJson(output));
89
+ }
90
+ }
91
+
92
+ //# sourceMappingURL=query.js.map