@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.
- package/README.md +313 -0
- package/dist/actions/codemods/dashboard.js +136 -8
- package/dist/actions/codemods/dashboard.js.map +1 -1
- package/dist/actions/codemods/types.js.map +1 -1
- package/dist/actions/context/watchJob.js +9 -3
- package/dist/actions/context/watchJob.js.map +1 -1
- package/dist/actions/deploy/blueprintManaged.js +59 -0
- package/dist/actions/deploy/blueprintManaged.js.map +1 -0
- package/dist/actions/deploy/deployApp.js +117 -1
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployRunner.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +6 -31
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/exec/runScript.worker.js +6 -2
- package/dist/actions/exec/runScript.worker.js.map +1 -1
- package/dist/actions/init/bootstrapLocalTemplate.js +35 -11
- package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
- package/dist/actions/init/createMediaLibraryCliConfig.js +12 -0
- package/dist/actions/init/createMediaLibraryCliConfig.js.map +1 -0
- package/dist/actions/init/determineMediaLibraryTemplate.js +14 -0
- package/dist/actions/init/determineMediaLibraryTemplate.js.map +1 -0
- package/dist/actions/init/initAction.js +35 -6
- package/dist/actions/init/initAction.js.map +1 -1
- package/dist/actions/init/initApp.js +81 -6
- package/dist/actions/init/initApp.js.map +1 -1
- package/dist/actions/init/offerAspectPull.js +81 -0
- package/dist/actions/init/offerAspectPull.js.map +1 -0
- package/dist/actions/init/project/getOrCreateDataset.js +8 -5
- package/dist/actions/init/project/getOrCreateDataset.js.map +1 -1
- package/dist/actions/init/project/getOrCreateProject.js +7 -5
- package/dist/actions/init/project/getOrCreateProject.js.map +1 -1
- package/dist/actions/init/project/getProjectDetails.js +25 -2
- package/dist/actions/init/project/getProjectDetails.js.map +1 -1
- package/dist/actions/init/project/promptUserForOrganization.js +6 -4
- package/dist/actions/init/project/promptUserForOrganization.js.map +1 -1
- package/dist/actions/init/templates/index.js +3 -0
- package/dist/actions/init/templates/index.js.map +1 -1
- package/dist/actions/init/templates/mediaLibraryConfig.js +51 -0
- package/dist/actions/init/templates/mediaLibraryConfig.js.map +1 -0
- package/dist/actions/media/aspectName.js +8 -0
- package/dist/actions/media/aspectName.js.map +1 -0
- package/dist/actions/media/aspectSync/contract.js +163 -0
- package/dist/actions/media/aspectSync/contract.js.map +1 -0
- package/dist/actions/media/aspectSync/syncAspects.js +115 -0
- package/dist/actions/media/aspectSync/syncAspects.js.map +1 -0
- package/dist/actions/media/cliConfigFields.js +192 -0
- package/dist/actions/media/cliConfigFields.js.map +1 -0
- package/dist/actions/media/pullAspects.js +225 -0
- package/dist/actions/media/pullAspects.js.map +1 -0
- package/dist/actions/media/resolveMediaLibraryId.js +83 -0
- package/dist/actions/media/resolveMediaLibraryId.js.map +1 -0
- package/dist/commands/codemod.js +1 -0
- package/dist/commands/codemod.js.map +1 -1
- package/dist/commands/context/build.js +2 -2
- package/dist/commands/context/build.js.map +1 -1
- package/dist/commands/context/entries/get.js +83 -0
- package/dist/commands/context/entries/get.js.map +1 -0
- package/dist/commands/context/entries/list.js +82 -0
- package/dist/commands/context/entries/list.js.map +1 -0
- package/dist/commands/context/entries/rebuild.js +95 -0
- package/dist/commands/context/entries/rebuild.js.map +1 -0
- package/dist/commands/context/issues/apply.js +86 -0
- package/dist/commands/context/issues/apply.js.map +1 -0
- package/dist/commands/context/issues/dismiss.js +52 -0
- package/dist/commands/context/issues/dismiss.js.map +1 -0
- package/dist/commands/context/issues/get.js +105 -0
- package/dist/commands/context/issues/get.js.map +1 -0
- package/dist/commands/context/issues/list.js +128 -0
- package/dist/commands/context/issues/list.js.map +1 -0
- package/dist/commands/context/issues/reopen.js +53 -0
- package/dist/commands/context/issues/reopen.js.map +1 -0
- package/dist/commands/context/issues/resolve.js +137 -0
- package/dist/commands/context/issues/resolve.js.map +1 -0
- package/dist/commands/context/jobs/get.js +2 -2
- package/dist/commands/context/jobs/get.js.map +1 -1
- package/dist/commands/context/query.js +92 -0
- package/dist/commands/context/query.js.map +1 -0
- package/dist/commands/deploy.js +13 -0
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/media/prune-aspect-data.js +170 -0
- package/dist/commands/media/prune-aspect-data.js.map +1 -0
- package/dist/commands/media/pull-aspects.js +309 -0
- package/dist/commands/media/pull-aspects.js.map +1 -0
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +17 -0
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
- package/dist/services/context.js +96 -3
- package/dist/services/context.js.map +1 -1
- package/dist/services/mediaLibraries.js +175 -8
- package/dist/services/mediaLibraries.js.map +1 -1
- package/dist/util/searchChoices.js +52 -0
- package/dist/util/searchChoices.js.map +1 -0
- package/oclif.manifest.json +2134 -1445
- package/package.json +9 -8
- package/templates/media-library-config/README.md +52 -0
- package/templates/media-library-config/eslint.config.txt +52 -0
- package/templates/media-library-config/src/fields/caption.tsx +29 -0
- package/templates/media-library-config/src/fields/notes.tsx +54 -0
- 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":"AAAA,SAAQA,IAAI,EAAEC,KAAK,QAAO,cAAa;AAEvC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAsBC,WAAW,QAAO,iBAAgB;AAExD,SAAQC,UAAU,QAAO,+BAA8B;AAEvD,MAAMC,kBAAkBJ,SAAS;AAEjC,OAAO,MAAMK,0BAA0BN;IACrC,OAAgBO,OAAO;QACrBC,iBAAiBX,KAAKY,MAAM,CAAC;YAC3BC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,6CAA4C;IAE1E,OAAgBE,iBAAiB,KAAI;IAErC,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtBC,MAAMlB,MAAMmB,MAAM,CAAY;YAC5BP,aAAa;YACb,uFAAuF;YACvFQ,SAASC,OAAOC,IAAI,CAAC;gBACnBC,WAAW;gBACXC,UAAU;gBACVC,KAAK;gBACLC,aAAa;gBACbC,cAAc;gBACdC,aAAa;gBACbC,iBAAiB;YACnB;QACF;QACAC,OAAO9B,MAAMW,MAAM,CAAC;YAClBC,aACE;YACFmB,WAAW;YACXC,OAAO,OAAOC;gBACZ,MAAMC,UAAUD,MAAME,IAAI,GAAGC,OAAO,CAAC,QAAQ;gBAC7C,IAAIF,YAAY,IAAI;oBAClB,MAAM,IAAIG,MAAM;gBAClB;gBACA,OAAOH;YACT;QACF;QACAI,UAAUtC,MAAMmB,MAAM,CAA0C;YAC9DP,aAAa;YACbQ,SAAS;gBAAC;gBAAY;aAAa;QACrC;QACAmB,QAAQvC,MAAMmB,MAAM,CAA6B;YAC/CP,aACE;YACFQ,SAAS;gBAAC;gBAAQ;gBAAY;aAAW;QAC3C;IACF,EAAqB;IAErB,MAAaoB,MAAqB;QAChC,MAAM,EAAC9B,eAAe,EAAC,GAAG,IAAI,CAACD,IAAI;QACnC,MAAM,EAACgC,IAAI,EAAEvB,IAAI,EAAEY,KAAK,EAAEQ,QAAQ,EAAEC,MAAM,EAAC,GAAG,IAAI,CAACtB,KAAK;QAExD,IAAIyB;QACJ,IAAI;YACFA,SAAS,MAAMpC,WAAWI,iBAAiB6B;QAC7C,EAAE,OAAOI,OAAO;YACdpC,gBAAgB,wBAAwBoC;YACxC,IAAItC,YAAYsC,UAAUA,MAAMC,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACD,KAAK,CAAC,CAAC,gBAAgB,EAAEjC,gBAAgB,WAAW,CAAC,EAAE;oBAC1DmC,MAAM5C,UAAU6C,aAAa;gBAC/B;YACF;YACA,IAAI,CAACH,KAAK,CAAC,CAAC,uBAAuB,EAAEvC,gBAAgBuC,QAAQ,EAAE;gBAC7DE,MAAM5C,UAAU6C,aAAa;YAC/B;QACF;QAEA,+DAA+D;QAC/D,MAAMC,UAAUL,OAAOM,MAAM,CAC3B,CAAC,EAACC,OAAO,EAAC,GACR,AAAC/B,CAAAA,SAASgC,aAAaD,QAAQ/B,IAAI,KAAKA,IAAG,KAC1CoB,CAAAA,aAAaY,aAAaD,QAAQX,QAAQ,KAAKA,QAAO,KACtDR,CAAAA,UAAUoB,aACTD,QAAQE,SAAS,KAAKrB,SACtBmB,QAAQE,SAAS,CAACC,UAAU,CAAC,GAAGtB,MAAM,CAAC,CAAC,CAAA;QAG9C,IAAIW,MAAM;YACR,IAAI,CAACY,OAAO,CAACN;YACb;QACF;QAEA,IAAIA,QAAQO,MAAM,KAAK,GAAG;YACxB,IAAI,CAACC,GAAG,CAAC;YACT;QACF;QAEA,2FAA2F;QAC3F,MAAMC,QAAQC,KAAKC,GAAG,CAACC,QAAQC,MAAM,CAACC,OAAO,IAAI,KAAK;QACtD,MAAMC,SAASf,QAAQgB,GAAG,CAAC,CAAC,EAACC,GAAG,EAAEf,OAAO,EAAEV,QAAQ0B,WAAW,EAAC;YAC7D,MAAMC,OAAO;gBAACjB,QAAQ/B,IAAI;gBAAE+B,QAAQX,QAAQ;gBAAE2B;gBAAahB,QAAQE,SAAS;aAAC,CAACgB,IAAI,CAAC;YACnF,OAAO,GAAGH,IAAI,EAAE,EAAEE,KAAK,IAAI,EAAEE,SAASnB,QAAQoB,KAAK,EAAEb,QAAQ,IAAI;QACnE;QACA,IAAI,CAACD,GAAG,CAACO,OAAOK,IAAI,CAAC;IACvB;AACF;AAIA,SAASC,SAASE,IAAY,EAAEC,SAAiB;IAC/C,+EAA+E;IAC/E,MAAMC,QAAQ;WAAIF,KAAKG,UAAU,CAAC,QAAQ,KAAKtC,IAAI;KAAG;IACtD,OAAOqC,MAAMlB,MAAM,IAAIiB,YAAYC,MAAML,IAAI,CAAC,MAAM,GAAGK,MAAME,KAAK,CAAC,GAAGH,YAAY,GAAGJ,IAAI,CAAC,IAAI,CAAC,CAAC;AAClG"}
|
|
@@ -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 =
|
|
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 =
|
|
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
|