@sanity/cli 7.7.1 → 7.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +17 -2
  2. package/dist/actions/auth/ensureAuthenticated.js +1 -1
  3. package/dist/actions/auth/ensureAuthenticated.js.map +1 -1
  4. package/dist/actions/auth/login/validateToken.js +1 -1
  5. package/dist/actions/auth/login/validateToken.js.map +1 -1
  6. package/dist/actions/build/buildApp.js +2 -3
  7. package/dist/actions/build/buildApp.js.map +1 -1
  8. package/dist/actions/build/buildStudio.js +1 -2
  9. package/dist/actions/build/buildStudio.js.map +1 -1
  10. package/dist/actions/deploy/createUserApplication.js +8 -8
  11. package/dist/actions/deploy/createUserApplication.js.map +1 -1
  12. package/dist/actions/deploy/deployApp.js +121 -24
  13. package/dist/actions/deploy/deployApp.js.map +1 -1
  14. package/dist/actions/deploy/deployChecks.js +90 -66
  15. package/dist/actions/deploy/deployChecks.js.map +1 -1
  16. package/dist/actions/deploy/deployRunner.js +8 -8
  17. package/dist/actions/deploy/deployRunner.js.map +1 -1
  18. package/dist/actions/deploy/deployStudio.js +126 -23
  19. package/dist/actions/deploy/deployStudio.js.map +1 -1
  20. package/dist/actions/deploy/deploymentPlan.js +29 -30
  21. package/dist/actions/deploy/deploymentPlan.js.map +1 -1
  22. package/dist/actions/deploy/findUserApplication.js +39 -14
  23. package/dist/actions/deploy/findUserApplication.js.map +1 -1
  24. package/dist/actions/deploy/resolveDeployTarget.js +43 -0
  25. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  26. package/dist/actions/dev/servers/getDevServerConfig.js +1 -1
  27. package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
  28. package/dist/actions/dev/servers/startStudioDevServer.js +1 -2
  29. package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
  30. package/dist/actions/graphql/SchemaError.js.map +1 -1
  31. package/dist/actions/graphql/extractGraphQLAPIs.js +3 -1
  32. package/dist/actions/graphql/extractGraphQLAPIs.js.map +1 -1
  33. package/dist/actions/manifest/extractCoreAppManifest.js +1 -1
  34. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  35. package/dist/actions/organizations/getOrganization.js +1 -1
  36. package/dist/actions/organizations/getOrganization.js.map +1 -1
  37. package/dist/actions/schema/validateAction.js +1 -1
  38. package/dist/actions/schema/validateAction.js.map +1 -1
  39. package/dist/actions/skills/setupSkills.js +1 -1
  40. package/dist/actions/skills/setupSkills.js.map +1 -1
  41. package/dist/actions/undeploy/adapters.js +101 -0
  42. package/dist/actions/undeploy/adapters.js.map +1 -0
  43. package/dist/actions/undeploy/runUndeploy.js +165 -0
  44. package/dist/actions/undeploy/runUndeploy.js.map +1 -0
  45. package/dist/actions/undeploy/undeployPlan.js +108 -0
  46. package/dist/actions/undeploy/undeployPlan.js.map +1 -0
  47. package/dist/commands/datasets/copy.js +2 -1
  48. package/dist/commands/datasets/copy.js.map +1 -1
  49. package/dist/commands/mcp/configure.js +1 -1
  50. package/dist/commands/mcp/configure.js.map +1 -1
  51. package/dist/commands/projects/create.js +18 -13
  52. package/dist/commands/projects/create.js.map +1 -1
  53. package/dist/commands/projects/list.js +2 -1
  54. package/dist/commands/projects/list.js.map +1 -1
  55. package/dist/commands/schemas/validate.js +3 -2
  56. package/dist/commands/schemas/validate.js.map +1 -1
  57. package/dist/commands/skills/install.js +1 -1
  58. package/dist/commands/skills/install.js.map +1 -1
  59. package/dist/commands/tokens/list.js +1 -1
  60. package/dist/commands/tokens/list.js.map +1 -1
  61. package/dist/commands/undeploy.js +36 -74
  62. package/dist/commands/undeploy.js.map +1 -1
  63. package/dist/util/checks.js +83 -0
  64. package/dist/util/checks.js.map +1 -0
  65. package/dist/util/toStderrOutput.js +13 -0
  66. package/dist/util/toStderrOutput.js.map +1 -0
  67. package/oclif.manifest.json +1007 -980
  68. package/package.json +7 -8
  69. package/dist/actions/undeploy/getStudioOrAppUserApplication.js +0 -36
  70. package/dist/actions/undeploy/getStudioOrAppUserApplication.js.map +0 -1
  71. package/dist/util/compareDependencyVersions.js +0 -110
  72. package/dist/util/compareDependencyVersions.js.map +0 -1
  73. package/dist/util/getErrorMessage.js +0 -28
  74. package/dist/util/getErrorMessage.js.map +0 -1
@@ -0,0 +1,101 @@
1
+ import { deleteUserApplication, getUserApplication } from '../../services/userApplications.js';
2
+ import { getAppId } from '../../util/appId.js';
3
+ import { NO_PROJECT_ID } from '../../util/errorMessages.js';
4
+ import { getCoreAppUrl } from '../deploy/urlUtils.js';
5
+ export function createAppUndeployAdapter(cliConfig) {
6
+ return {
7
+ async resolveTarget () {
8
+ const appId = getAppId(cliConfig);
9
+ if (!appId) {
10
+ return {
11
+ message: 'No `deployment.appId` configured',
12
+ solution: 'Add `deployment.appId` to sanity.cli.ts',
13
+ type: 'none'
14
+ };
15
+ }
16
+ const application = await getUserApplication({
17
+ appId,
18
+ isSdkApp: true
19
+ });
20
+ if (!application) {
21
+ return {
22
+ message: 'Application with the given ID does not exist',
23
+ type: 'none'
24
+ };
25
+ }
26
+ return {
27
+ target: toUndeployTarget(application, 'coreApp'),
28
+ type: 'found'
29
+ };
30
+ },
31
+ type: 'coreApp',
32
+ undeploy: ({ id })=>deleteUserApplication({
33
+ applicationId: id,
34
+ appType: 'coreApp'
35
+ })
36
+ };
37
+ }
38
+ export function createStudioUndeployAdapter(cliConfig) {
39
+ return {
40
+ async resolveTarget () {
41
+ const appId = cliConfig.deployment?.appId;
42
+ const studioHost = cliConfig.studioHost;
43
+ if (!appId && !studioHost) {
44
+ return {
45
+ message: 'No studio hostname configured',
46
+ solution: 'Set `studioHost` in sanity.cli.ts',
47
+ type: 'none'
48
+ };
49
+ }
50
+ const projectId = cliConfig.api?.projectId;
51
+ if (!projectId) throw new Error(NO_PROJECT_ID);
52
+ const application = await getUserApplication({
53
+ appHost: studioHost,
54
+ appId,
55
+ isSdkApp: false,
56
+ projectId
57
+ });
58
+ if (!application) {
59
+ return {
60
+ message: 'Your project has not been assigned an app ID or a studio hostname, or the `appId` or `studioHost` provided does not exist',
61
+ type: 'none'
62
+ };
63
+ }
64
+ return {
65
+ target: toUndeployTarget(application, 'studio'),
66
+ type: 'found'
67
+ };
68
+ },
69
+ type: 'studio',
70
+ undeploy: ({ id })=>deleteUserApplication({
71
+ applicationId: id,
72
+ appType: 'studio'
73
+ })
74
+ };
75
+ }
76
+ function toUndeployTarget(application, type) {
77
+ return {
78
+ activeDeployment: application.activeDeployment ? {
79
+ deployedAt: application.activeDeployment.deployedAt,
80
+ deployedBy: application.activeDeployment.deployedBy,
81
+ version: application.activeDeployment.version
82
+ } : null,
83
+ appHost: application.appHost ?? null,
84
+ createdAt: application.createdAt ?? null,
85
+ id: application.id,
86
+ organizationId: application.organizationId ?? null,
87
+ projectId: application.projectId ?? null,
88
+ title: application.title ?? null,
89
+ type,
90
+ url: resolveTargetUrl(application, type)
91
+ };
92
+ }
93
+ function resolveTargetUrl(application, type) {
94
+ if (type === 'coreApp') {
95
+ return application.organizationId ? getCoreAppUrl(application.organizationId, application.id) : null;
96
+ }
97
+ if (!application.appHost) return null;
98
+ return application.urlType === 'external' ? application.appHost : `https://${application.appHost}.sanity.studio`;
99
+ }
100
+
101
+ //# sourceMappingURL=adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/undeploy/adapters.ts"],"sourcesContent":["import {type CliConfig} from '@sanity/cli-core'\n\nimport {\n deleteUserApplication,\n getUserApplication,\n type UserApplication,\n} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {NO_PROJECT_ID} from '../../util/errorMessages.js'\nimport {getCoreAppUrl} from '../deploy/urlUtils.js'\nimport {type UndeployAdapter} from './runUndeploy.js'\nimport {type UndeployTarget} from './undeployPlan.js'\n\nexport function createAppUndeployAdapter(cliConfig: CliConfig): UndeployAdapter {\n return {\n async resolveTarget() {\n const appId = getAppId(cliConfig)\n if (!appId) {\n return {\n message: 'No `deployment.appId` configured',\n solution: 'Add `deployment.appId` to sanity.cli.ts',\n type: 'none',\n }\n }\n\n const application = await getUserApplication({appId, isSdkApp: true})\n if (!application) {\n return {message: 'Application with the given ID does not exist', type: 'none'}\n }\n\n return {target: toUndeployTarget(application, 'coreApp'), type: 'found'}\n },\n type: 'coreApp',\n undeploy: ({id}) => deleteUserApplication({applicationId: id, appType: 'coreApp'}),\n }\n}\n\nexport function createStudioUndeployAdapter(cliConfig: CliConfig): UndeployAdapter {\n return {\n async resolveTarget() {\n const appId = cliConfig.deployment?.appId\n const studioHost = cliConfig.studioHost\n if (!appId && !studioHost) {\n return {\n message: 'No studio hostname configured',\n solution: 'Set `studioHost` in sanity.cli.ts',\n type: 'none',\n }\n }\n\n const projectId = cliConfig.api?.projectId\n if (!projectId) throw new Error(NO_PROJECT_ID)\n\n const application = await getUserApplication({\n appHost: studioHost,\n appId,\n isSdkApp: false,\n projectId,\n })\n if (!application) {\n return {\n message:\n 'Your project has not been assigned an app ID or a studio hostname, or the `appId` or `studioHost` provided does not exist',\n type: 'none',\n }\n }\n\n return {target: toUndeployTarget(application, 'studio'), type: 'found'}\n },\n type: 'studio',\n undeploy: ({id}) => deleteUserApplication({applicationId: id, appType: 'studio'}),\n }\n}\n\nfunction toUndeployTarget(\n application: UserApplication,\n type: UndeployTarget['type'],\n): UndeployTarget {\n return {\n activeDeployment: application.activeDeployment\n ? {\n deployedAt: application.activeDeployment.deployedAt,\n deployedBy: application.activeDeployment.deployedBy,\n version: application.activeDeployment.version,\n }\n : null,\n appHost: application.appHost ?? null,\n createdAt: application.createdAt ?? null,\n id: application.id,\n organizationId: application.organizationId ?? null,\n projectId: application.projectId ?? null,\n title: application.title ?? null,\n type,\n url: resolveTargetUrl(application, type),\n }\n}\n\nfunction resolveTargetUrl(\n application: UserApplication,\n type: UndeployTarget['type'],\n): string | null {\n if (type === 'coreApp') {\n return application.organizationId\n ? getCoreAppUrl(application.organizationId, application.id)\n : null\n }\n if (!application.appHost) return null\n return application.urlType === 'external'\n ? application.appHost\n : `https://${application.appHost}.sanity.studio`\n}\n"],"names":["deleteUserApplication","getUserApplication","getAppId","NO_PROJECT_ID","getCoreAppUrl","createAppUndeployAdapter","cliConfig","resolveTarget","appId","message","solution","type","application","isSdkApp","target","toUndeployTarget","undeploy","id","applicationId","appType","createStudioUndeployAdapter","deployment","studioHost","projectId","api","Error","appHost","activeDeployment","deployedAt","deployedBy","version","createdAt","organizationId","title","url","resolveTargetUrl","urlType"],"mappings":"AAEA,SACEA,qBAAqB,EACrBC,kBAAkB,QAEb,qCAAoC;AAC3C,SAAQC,QAAQ,QAAO,sBAAqB;AAC5C,SAAQC,aAAa,QAAO,8BAA6B;AACzD,SAAQC,aAAa,QAAO,wBAAuB;AAInD,OAAO,SAASC,yBAAyBC,SAAoB;IAC3D,OAAO;QACL,MAAMC;YACJ,MAAMC,QAAQN,SAASI;YACvB,IAAI,CAACE,OAAO;gBACV,OAAO;oBACLC,SAAS;oBACTC,UAAU;oBACVC,MAAM;gBACR;YACF;YAEA,MAAMC,cAAc,MAAMX,mBAAmB;gBAACO;gBAAOK,UAAU;YAAI;YACnE,IAAI,CAACD,aAAa;gBAChB,OAAO;oBAACH,SAAS;oBAAgDE,MAAM;gBAAM;YAC/E;YAEA,OAAO;gBAACG,QAAQC,iBAAiBH,aAAa;gBAAYD,MAAM;YAAO;QACzE;QACAA,MAAM;QACNK,UAAU,CAAC,EAACC,EAAE,EAAC,GAAKjB,sBAAsB;gBAACkB,eAAeD;gBAAIE,SAAS;YAAS;IAClF;AACF;AAEA,OAAO,SAASC,4BAA4Bd,SAAoB;IAC9D,OAAO;QACL,MAAMC;YACJ,MAAMC,QAAQF,UAAUe,UAAU,EAAEb;YACpC,MAAMc,aAAahB,UAAUgB,UAAU;YACvC,IAAI,CAACd,SAAS,CAACc,YAAY;gBACzB,OAAO;oBACLb,SAAS;oBACTC,UAAU;oBACVC,MAAM;gBACR;YACF;YAEA,MAAMY,YAAYjB,UAAUkB,GAAG,EAAED;YACjC,IAAI,CAACA,WAAW,MAAM,IAAIE,MAAMtB;YAEhC,MAAMS,cAAc,MAAMX,mBAAmB;gBAC3CyB,SAASJ;gBACTd;gBACAK,UAAU;gBACVU;YACF;YACA,IAAI,CAACX,aAAa;gBAChB,OAAO;oBACLH,SACE;oBACFE,MAAM;gBACR;YACF;YAEA,OAAO;gBAACG,QAAQC,iBAAiBH,aAAa;gBAAWD,MAAM;YAAO;QACxE;QACAA,MAAM;QACNK,UAAU,CAAC,EAACC,EAAE,EAAC,GAAKjB,sBAAsB;gBAACkB,eAAeD;gBAAIE,SAAS;YAAQ;IACjF;AACF;AAEA,SAASJ,iBACPH,WAA4B,EAC5BD,IAA4B;IAE5B,OAAO;QACLgB,kBAAkBf,YAAYe,gBAAgB,GAC1C;YACEC,YAAYhB,YAAYe,gBAAgB,CAACC,UAAU;YACnDC,YAAYjB,YAAYe,gBAAgB,CAACE,UAAU;YACnDC,SAASlB,YAAYe,gBAAgB,CAACG,OAAO;QAC/C,IACA;QACJJ,SAASd,YAAYc,OAAO,IAAI;QAChCK,WAAWnB,YAAYmB,SAAS,IAAI;QACpCd,IAAIL,YAAYK,EAAE;QAClBe,gBAAgBpB,YAAYoB,cAAc,IAAI;QAC9CT,WAAWX,YAAYW,SAAS,IAAI;QACpCU,OAAOrB,YAAYqB,KAAK,IAAI;QAC5BtB;QACAuB,KAAKC,iBAAiBvB,aAAaD;IACrC;AACF;AAEA,SAASwB,iBACPvB,WAA4B,EAC5BD,IAA4B;IAE5B,IAAIA,SAAS,WAAW;QACtB,OAAOC,YAAYoB,cAAc,GAC7B5B,cAAcQ,YAAYoB,cAAc,EAAEpB,YAAYK,EAAE,IACxD;IACN;IACA,IAAI,CAACL,YAAYc,OAAO,EAAE,OAAO;IACjC,OAAOd,YAAYwB,OAAO,KAAK,aAC3BxB,YAAYc,OAAO,GACnB,CAAC,QAAQ,EAAEd,YAAYc,OAAO,CAAC,cAAc,CAAC;AACpD"}
@@ -0,0 +1,165 @@
1
+ import { styleText } from 'node:util';
2
+ import { CLIError } from '@oclif/core/errors';
3
+ import { subdebug } from '@sanity/cli-core';
4
+ import { getErrorMessage } from '@sanity/cli-core/errors';
5
+ import { confirm, spinner } from '@sanity/cli-core/ux';
6
+ import { createCollectingReporter, createFailFastReporter } from '../../util/checks.js';
7
+ import { toStderrOutput } from '../../util/toStderrOutput.js';
8
+ import { describeUndeployTarget, renderUndeployPlan, undeployPlanToJson } from './undeployPlan.js';
9
+ const undeployDebug = subdebug('undeploy');
10
+ /**
11
+ * Runs an undeploy in the mode the flags select: a real undeploy fails fast,
12
+ * confirms, and deletes; `--dry-run` drives the same target resolution
13
+ * read-only and renders a plan instead. `--json` emits the same information as
14
+ * machine-readable JSON.
15
+ */ export async function runUndeploy(options, adapter) {
16
+ const { flags, output } = options;
17
+ const json = !!flags.json;
18
+ const emitJson = (payload)=>output.log(JSON.stringify(payload, null, 2));
19
+ // The JSON payload owns stdout, so the run's progress logs go to stderr; only
20
+ // the final JSON.stringify writes to stdout.
21
+ const runOptions = json ? {
22
+ ...options,
23
+ output: toStderrOutput(output)
24
+ } : options;
25
+ try {
26
+ if (flags['dry-run']) {
27
+ const reporter = createCollectingReporter();
28
+ const resolution = await resolveTarget(adapter, reporter);
29
+ const plan = {
30
+ checks: reporter.results,
31
+ reason: resolution?.type === 'none' ? resolution.message : null,
32
+ target: resolution?.type === 'found' ? resolution.target : null,
33
+ type: adapter.type
34
+ };
35
+ if (json) emitJson(undeployPlanToJson(plan));
36
+ else renderUndeployPlan(plan, output);
37
+ // A blocked plan exits like a real (fail-fast) undeploy would.
38
+ const failed = plan.checks.find((check)=>check.status === 'fail');
39
+ if (failed) output.error('Undeploy blocked by failing checks.', {
40
+ exit: failed.exitCode ?? 1
41
+ });
42
+ return;
43
+ }
44
+ const result = await undeployApp(runOptions, adapter);
45
+ if (json && result) emitJson(result);
46
+ } catch (error) {
47
+ const failure = normalizeFailure(error, adapter.type);
48
+ // A blocked dry run reaches this catch too (its exit throws) and already
49
+ // printed its plan, so only a real undeploy adds the {undeployed: false} envelope.
50
+ if (json && !flags['dry-run']) {
51
+ emitJson({
52
+ error: {
53
+ message: failure.message
54
+ },
55
+ undeployed: false
56
+ });
57
+ }
58
+ output.error(failure.message, {
59
+ exit: failure.exit
60
+ });
61
+ }
62
+ }
63
+ /** The real run: resolve the target, confirm, delete, report. */ async function undeployApp(options, adapter) {
64
+ const { flags, output } = options;
65
+ const resolution = await resolveTarget(adapter, createFailFastReporter(output));
66
+ // A resolve failure never lands here: the fail-fast reporter already exited.
67
+ if (!resolution) return undefined;
68
+ if (resolution.type === 'none') {
69
+ output.log(`${resolution.message}.`);
70
+ if (resolution.solution) output.log(`${resolution.solution}.`);
71
+ output.log('Nothing to undeploy.');
72
+ return {
73
+ reason: resolution.message,
74
+ undeployed: false
75
+ };
76
+ }
77
+ const { target } = resolution;
78
+ if (!flags.yes) {
79
+ const shouldUndeploy = await confirm({
80
+ default: false,
81
+ message: confirmUndeployMessage(target)
82
+ });
83
+ if (!shouldUndeploy) return undefined;
84
+ }
85
+ const spin = spinner(`Undeploying ${adapter.type === 'coreApp' ? 'application' : 'studio'}`).start();
86
+ try {
87
+ await adapter.undeploy(target);
88
+ } catch (error) {
89
+ spin.fail();
90
+ throw error;
91
+ }
92
+ spin.succeed();
93
+ printUndeployScheduled(target, output);
94
+ return {
95
+ application: target,
96
+ undeployed: true
97
+ };
98
+ }
99
+ /**
100
+ * Resolves the undeploy target and reports the verdict as a check, shared by
101
+ * both modes so a real run and a dry run can't diverge.
102
+ */ async function resolveTarget(adapter, reporter) {
103
+ const spin = spinner('Checking application info').start();
104
+ let resolution;
105
+ try {
106
+ resolution = await adapter.resolveTarget();
107
+ } catch (error) {
108
+ spin.fail();
109
+ undeployDebug('Failed to resolve undeploy target', error);
110
+ reporter.report({
111
+ message: `Failed to resolve undeploy target: ${getErrorMessage(error)}`,
112
+ status: 'fail'
113
+ });
114
+ return null;
115
+ }
116
+ spin[resolution.type === 'found' ? 'succeed' : 'fail']();
117
+ reporter.report(describeUndeployTarget(resolution));
118
+ return resolution;
119
+ }
120
+ function confirmUndeployMessage(target) {
121
+ if (target.type === 'coreApp') {
122
+ return `This will undeploy the following application:
123
+
124
+ Title: ${styleText('yellow', target.title || '(untitled application)')}
125
+ ID: ${styleText('yellow', target.id)}
126
+
127
+ The application will no longer be available for any of your users if you proceed.
128
+
129
+ Are you ${styleText('red', 'sure')} you want to undeploy?`;
130
+ }
131
+ return `This will undeploy ${styleText('yellow', target.url ?? target.id)} and make it unavailable for your users.
132
+ Are you ${styleText('red', 'sure')} you want to undeploy?`;
133
+ }
134
+ function printUndeployScheduled(target, output) {
135
+ if (target.type === 'coreApp') {
136
+ output.log(`\n${styleText('bold', '⏱️ Application undeploy scheduled.')} It might be a few minutes until ${target.title ? styleText('italic', `'${target.title}'`) : 'your application'} is unavailable.`);
137
+ output.log(`\n${styleText('bold', 'Remember to remove `deployment.appId` from your application configuration')} to avoid errors when redeploying.`);
138
+ return;
139
+ }
140
+ output.log(`\nStudio undeploy scheduled. It might be a few minutes until ${target.url ?? 'your studio'} is unavailable.`);
141
+ }
142
+ /** The one failure diagnosis both the stderr message and the `--json` envelope read. */ function normalizeFailure(error, type) {
143
+ // Ctrl+C on the confirmation prompt isn't a real failure
144
+ if (error instanceof Error && error.name === 'ExitPromptError') {
145
+ return {
146
+ exit: 1,
147
+ message: 'Undeploy cancelled by user'
148
+ };
149
+ }
150
+ // A failed check already carries its own message and exit code
151
+ if (error instanceof CLIError) {
152
+ return {
153
+ exit: error.oclif?.exit ?? 1,
154
+ message: error.message
155
+ };
156
+ }
157
+ const label = type === 'coreApp' ? 'application' : 'studio';
158
+ undeployDebug(`Error undeploying ${label}`, error);
159
+ return {
160
+ exit: 1,
161
+ message: `Error undeploying ${label}: ${getErrorMessage(error)}`
162
+ };
163
+ }
164
+
165
+ //# sourceMappingURL=runUndeploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/undeploy/runUndeploy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {CLIError} from '@oclif/core/errors'\nimport {type Output, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {confirm, spinner} from '@sanity/cli-core/ux'\n\nimport {type UndeployCommand} from '../../commands/undeploy.js'\nimport {\n type CheckReporter,\n createCollectingReporter,\n createFailFastReporter,\n} from '../../util/checks.js'\nimport {toStderrOutput} from '../../util/toStderrOutput.js'\nimport {\n describeUndeployTarget,\n renderUndeployPlan,\n type UndeployPlan,\n undeployPlanToJson,\n type UndeployTarget,\n type UndeployTargetResolution,\n} from './undeployPlan.js'\n\nconst undeployDebug = subdebug('undeploy')\n\ntype UndeployFlags = UndeployCommand['flags']\n\nexport interface UndeployOptions {\n flags: UndeployFlags\n output: Output\n}\n\n/**\n * The parts of an undeploy that differ per application backend. The shared\n * sequence — mode selection, target reporting, confirmation, error handling —\n * lives in `runUndeploy`; adapters only resolve and delete.\n */\nexport interface UndeployAdapter {\n /** Resolves what an undeploy would delete; read-only. */\n resolveTarget(): Promise<UndeployTargetResolution>\n type: 'coreApp' | 'studio'\n /** Deletes the target — the only mutating step, never run on a dry run. */\n undeploy(target: UndeployTarget): Promise<void>\n}\n\n/** What a real undeploy produced — the payload `--json` reports. */\ntype UndeployResult =\n | {application: UndeployTarget; undeployed: true}\n | {reason: string; undeployed: false}\n\n/**\n * Runs an undeploy in the mode the flags select: a real undeploy fails fast,\n * confirms, and deletes; `--dry-run` drives the same target resolution\n * read-only and renders a plan instead. `--json` emits the same information as\n * machine-readable JSON.\n */\nexport async function runUndeploy(\n options: UndeployOptions,\n adapter: UndeployAdapter,\n): Promise<void> {\n const {flags, output} = options\n const json = !!flags.json\n const emitJson = (payload: unknown) => output.log(JSON.stringify(payload, null, 2))\n\n // The JSON payload owns stdout, so the run's progress logs go to stderr; only\n // the final JSON.stringify writes to stdout.\n const runOptions = json ? {...options, output: toStderrOutput(output)} : options\n\n try {\n if (flags['dry-run']) {\n const reporter = createCollectingReporter()\n const resolution = await resolveTarget(adapter, reporter)\n const plan: UndeployPlan = {\n checks: reporter.results,\n reason: resolution?.type === 'none' ? resolution.message : null,\n target: resolution?.type === 'found' ? resolution.target : null,\n type: adapter.type,\n }\n if (json) emitJson(undeployPlanToJson(plan))\n else renderUndeployPlan(plan, output)\n // A blocked plan exits like a real (fail-fast) undeploy would.\n const failed = plan.checks.find((check) => check.status === 'fail')\n if (failed) output.error('Undeploy blocked by failing checks.', {exit: failed.exitCode ?? 1})\n return\n }\n\n const result = await undeployApp(runOptions, adapter)\n if (json && result) emitJson(result)\n } catch (error) {\n const failure = normalizeFailure(error, adapter.type)\n // A blocked dry run reaches this catch too (its exit throws) and already\n // printed its plan, so only a real undeploy adds the {undeployed: false} envelope.\n if (json && !flags['dry-run']) {\n emitJson({error: {message: failure.message}, undeployed: false})\n }\n output.error(failure.message, {exit: failure.exit})\n }\n}\n\n/** The real run: resolve the target, confirm, delete, report. */\nasync function undeployApp(\n options: UndeployOptions,\n adapter: UndeployAdapter,\n): Promise<UndeployResult | undefined> {\n const {flags, output} = options\n\n const resolution = await resolveTarget(adapter, createFailFastReporter(output))\n // A resolve failure never lands here: the fail-fast reporter already exited.\n if (!resolution) return undefined\n if (resolution.type === 'none') {\n output.log(`${resolution.message}.`)\n if (resolution.solution) output.log(`${resolution.solution}.`)\n output.log('Nothing to undeploy.')\n return {reason: resolution.message, undeployed: false}\n }\n\n const {target} = resolution\n if (!flags.yes) {\n const shouldUndeploy = await confirm({\n default: false,\n message: confirmUndeployMessage(target),\n })\n if (!shouldUndeploy) return undefined\n }\n\n const spin = spinner(\n `Undeploying ${adapter.type === 'coreApp' ? 'application' : 'studio'}`,\n ).start()\n try {\n await adapter.undeploy(target)\n } catch (error) {\n spin.fail()\n throw error\n }\n spin.succeed()\n\n printUndeployScheduled(target, output)\n return {application: target, undeployed: true}\n}\n\n/**\n * Resolves the undeploy target and reports the verdict as a check, shared by\n * both modes so a real run and a dry run can't diverge.\n */\nasync function resolveTarget(\n adapter: UndeployAdapter,\n reporter: CheckReporter,\n): Promise<UndeployTargetResolution | null> {\n const spin = spinner('Checking application info').start()\n\n let resolution: UndeployTargetResolution\n try {\n resolution = await adapter.resolveTarget()\n } catch (error) {\n spin.fail()\n undeployDebug('Failed to resolve undeploy target', error)\n reporter.report({\n message: `Failed to resolve undeploy target: ${getErrorMessage(error)}`,\n status: 'fail',\n })\n return null\n }\n\n spin[resolution.type === 'found' ? 'succeed' : 'fail']()\n reporter.report(describeUndeployTarget(resolution))\n return resolution\n}\n\nfunction confirmUndeployMessage(target: UndeployTarget): string {\n if (target.type === 'coreApp') {\n return `This will undeploy the following application:\n\n Title: ${styleText('yellow', target.title || '(untitled application)')}\n ID: ${styleText('yellow', target.id)}\n\nThe application will no longer be available for any of your users if you proceed.\n\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n }\n\n return `This will undeploy ${styleText('yellow', target.url ?? target.id)} and make it unavailable for your users.\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n}\n\nfunction printUndeployScheduled(target: UndeployTarget, output: Output): void {\n if (target.type === 'coreApp') {\n output.log(\n `\\n${styleText('bold', '⏱️ Application undeploy scheduled.')} It might be a few minutes until ${\n target.title ? styleText('italic', `'${target.title}'`) : 'your application'\n } is unavailable.`,\n )\n output.log(\n `\\n${styleText('bold', 'Remember to remove `deployment.appId` from your application configuration')} to avoid errors when redeploying.`,\n )\n return\n }\n\n output.log(\n `\\nStudio undeploy scheduled. It might be a few minutes until ${target.url ?? 'your studio'} is unavailable.`,\n )\n}\n\n/** The one failure diagnosis both the stderr message and the `--json` envelope read. */\nfunction normalizeFailure(\n error: unknown,\n type: UndeployAdapter['type'],\n): {exit: number; message: string} {\n // Ctrl+C on the confirmation prompt isn't a real failure\n if (error instanceof Error && error.name === 'ExitPromptError') {\n return {exit: 1, message: 'Undeploy cancelled by user'}\n }\n // A failed check already carries its own message and exit code\n if (error instanceof CLIError) {\n return {exit: error.oclif?.exit ?? 1, message: error.message}\n }\n const label = type === 'coreApp' ? 'application' : 'studio'\n undeployDebug(`Error undeploying ${label}`, error)\n return {exit: 1, message: `Error undeploying ${label}: ${getErrorMessage(error)}`}\n}\n"],"names":["styleText","CLIError","subdebug","getErrorMessage","confirm","spinner","createCollectingReporter","createFailFastReporter","toStderrOutput","describeUndeployTarget","renderUndeployPlan","undeployPlanToJson","undeployDebug","runUndeploy","options","adapter","flags","output","json","emitJson","payload","log","JSON","stringify","runOptions","reporter","resolution","resolveTarget","plan","checks","results","reason","type","message","target","failed","find","check","status","error","exit","exitCode","result","undeployApp","failure","normalizeFailure","undeployed","undefined","solution","yes","shouldUndeploy","default","confirmUndeployMessage","spin","start","undeploy","fail","succeed","printUndeployScheduled","application","report","title","id","url","Error","name","oclif","label"],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ import { styleText } from 'node:util';
2
+ import { checkStatusIcon, renderIssues } from '../../util/checks.js';
3
+ export function canUndeploy(plan) {
4
+ return plan.target !== null && plan.checks.every((check)=>check.status !== 'fail');
5
+ }
6
+ /**
7
+ * A machine-readable projection of the plan: blocking problems mapped to their
8
+ * fix, warnings as messages. Derived from the same checks and target the human
9
+ * report renders, so the two can't drift.
10
+ */ export function undeployPlanToJson(plan) {
11
+ const errors = {};
12
+ const warnings = [];
13
+ for (const check of plan.checks){
14
+ if (check.status === 'fail') errors[check.message] = check.solution ?? null;
15
+ else if (check.status === 'warn') warnings.push(check.message);
16
+ }
17
+ return {
18
+ application: plan.target,
19
+ canUndeploy: canUndeploy(plan),
20
+ errors,
21
+ reason: plan.reason,
22
+ warnings
23
+ };
24
+ }
25
+ /**
26
+ * The single diagnosis for each undeploy-target verdict, shared by the dry-run
27
+ * report and the real run so message and fix can't drift between the two.
28
+ */ export function describeUndeployTarget(resolution) {
29
+ if (resolution.type === 'none') {
30
+ return {
31
+ message: resolution.message,
32
+ solution: resolution.solution,
33
+ status: 'skip'
34
+ };
35
+ }
36
+ const { target } = resolution;
37
+ if (target.type === 'studio') {
38
+ return {
39
+ message: `Undeploys studio ${target.url ?? target.id}`,
40
+ status: 'pass'
41
+ };
42
+ }
43
+ const name = target.title ? `"${target.title}" (${target.id})` : target.id;
44
+ return {
45
+ message: `Undeploys application ${name}`,
46
+ status: 'pass'
47
+ };
48
+ }
49
+ export function renderUndeployPlan(plan, output) {
50
+ const problems = plan.checks.filter((check)=>check.status === 'fail');
51
+ const warnings = plan.checks.filter((check)=>check.status === 'warn');
52
+ output.log('\nDry run — no changes made.\n');
53
+ // Only pass/skip here; problems and warnings render below with their fixes.
54
+ // A passing target check already says what gets undeployed, so an
55
+ // undeployable plan needs no extra verdict line.
56
+ for (const check of plan.checks){
57
+ if (check.status === 'pass' || check.status === 'skip') {
58
+ const fix = check.solution ? `: ${check.solution}` : '';
59
+ output.log(` ${checkStatusIcon(check.status)} ${check.message}${fix}`);
60
+ }
61
+ }
62
+ if (plan.target) renderTarget(plan.target, output);
63
+ if (!canUndeploy(plan)) {
64
+ if (problems.length > 0) {
65
+ const label = plan.type === 'coreApp' ? 'Application' : 'Studio';
66
+ output.log(styleText('red', `\n${checkStatusIcon('fail')} ${label} can not be undeployed.`));
67
+ } else {
68
+ output.log('\nNothing to undeploy.');
69
+ }
70
+ }
71
+ renderIssues(output, 'Problems to fix:', problems);
72
+ renderIssues(output, 'Warnings:', warnings);
73
+ }
74
+ function renderTarget(target, output) {
75
+ const rows = [
76
+ [
77
+ 'Title',
78
+ target.title
79
+ ],
80
+ [
81
+ 'ID',
82
+ target.id
83
+ ],
84
+ [
85
+ 'URL',
86
+ target.url
87
+ ],
88
+ [
89
+ 'Deployed',
90
+ formatDeployment(target.activeDeployment)
91
+ ]
92
+ ];
93
+ output.log('');
94
+ for (const [key, value] of rows){
95
+ if (value) output.log(` ${key.padEnd(8)} ${styleText('yellow', value)}`);
96
+ }
97
+ }
98
+ function formatDeployment(deployment) {
99
+ if (!deployment) return null;
100
+ const parts = [
101
+ deployment.version ? `version ${deployment.version}` : null,
102
+ deployment.deployedAt ? `at ${deployment.deployedAt}` : null,
103
+ deployment.deployedBy ? `by ${deployment.deployedBy}` : null
104
+ ].filter(Boolean);
105
+ return parts.length > 0 ? parts.join(' ') : null;
106
+ }
107
+
108
+ //# sourceMappingURL=undeployPlan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/undeploy/undeployPlan.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core'\n\nimport {type Check, checkStatusIcon, renderIssues} from '../../util/checks.js'\n\n/**\n * What an undeploy deletes, resolved once and read by every report — the\n * dry-run plan, the `--json` payloads, and the real run's confirmation prompt —\n * so the human and machine outputs can't drift.\n */\nexport interface UndeployTarget {\n /** Details of the deployment currently being served; `null` when none is live. */\n activeDeployment: {deployedAt: string; deployedBy: string; version: string} | null\n /** Hostname the application is served from. */\n appHost: string | null\n createdAt: string | null\n /** The application an undeploy deletes, along with all its deployments. */\n id: string\n organizationId: string | null\n projectId: string | null\n title: string | null\n type: 'coreApp' | 'studio'\n /** Where the deployed studio/app is currently reachable. */\n url: string | null\n}\n\nexport type UndeployTargetResolution =\n | {message: string; solution?: string; type: 'none'}\n | {target: UndeployTarget; type: 'found'}\n\n/** What a `--dry-run` undeploy would do: the real undeploy sequence with the deletion gated off. */\nexport interface UndeployPlan {\n checks: Check[]\n /** Why there is nothing to undeploy; `null` when a target resolved. */\n reason: string | null\n /** What would be deleted; `null` when there is nothing to undeploy. */\n target: UndeployTarget | null\n type: 'coreApp' | 'studio'\n}\n\nexport function canUndeploy(plan: UndeployPlan): boolean {\n return plan.target !== null && plan.checks.every((check) => check.status !== 'fail')\n}\n\n/**\n * A machine-readable projection of the plan: blocking problems mapped to their\n * fix, warnings as messages. Derived from the same checks and target the human\n * report renders, so the two can't drift.\n */\nexport function undeployPlanToJson(plan: UndeployPlan): {\n application: UndeployTarget | null\n canUndeploy: boolean\n errors: Record<string, string | null>\n reason: string | null\n warnings: string[]\n} {\n const errors: Record<string, string | null> = {}\n const warnings: string[] = []\n for (const check of plan.checks) {\n if (check.status === 'fail') errors[check.message] = check.solution ?? null\n else if (check.status === 'warn') warnings.push(check.message)\n }\n\n return {\n application: plan.target,\n canUndeploy: canUndeploy(plan),\n errors,\n reason: plan.reason,\n warnings,\n }\n}\n\n/**\n * The single diagnosis for each undeploy-target verdict, shared by the dry-run\n * report and the real run so message and fix can't drift between the two.\n */\nexport function describeUndeployTarget(resolution: UndeployTargetResolution): Check {\n if (resolution.type === 'none') {\n return {message: resolution.message, solution: resolution.solution, status: 'skip'}\n }\n\n const {target} = resolution\n if (target.type === 'studio') {\n return {message: `Undeploys studio ${target.url ?? target.id}`, status: 'pass'}\n }\n const name = target.title ? `\"${target.title}\" (${target.id})` : target.id\n return {message: `Undeploys application ${name}`, status: 'pass'}\n}\n\nexport function renderUndeployPlan(plan: UndeployPlan, output: Output): void {\n const problems = plan.checks.filter((check) => check.status === 'fail')\n const warnings = plan.checks.filter((check) => check.status === 'warn')\n\n output.log('\\nDry run — no changes made.\\n')\n\n // Only pass/skip here; problems and warnings render below with their fixes.\n // A passing target check already says what gets undeployed, so an\n // undeployable plan needs no extra verdict line.\n for (const check of plan.checks) {\n if (check.status === 'pass' || check.status === 'skip') {\n const fix = check.solution ? `: ${check.solution}` : ''\n output.log(` ${checkStatusIcon(check.status)} ${check.message}${fix}`)\n }\n }\n\n if (plan.target) renderTarget(plan.target, output)\n\n if (!canUndeploy(plan)) {\n if (problems.length > 0) {\n const label = plan.type === 'coreApp' ? 'Application' : 'Studio'\n output.log(styleText('red', `\\n${checkStatusIcon('fail')} ${label} can not be undeployed.`))\n } else {\n output.log('\\nNothing to undeploy.')\n }\n }\n\n renderIssues(output, 'Problems to fix:', problems)\n renderIssues(output, 'Warnings:', warnings)\n}\n\nfunction renderTarget(target: UndeployTarget, output: Output): void {\n const rows: [string, string | null][] = [\n ['Title', target.title],\n ['ID', target.id],\n ['URL', target.url],\n ['Deployed', formatDeployment(target.activeDeployment)],\n ]\n\n output.log('')\n for (const [key, value] of rows) {\n if (value) output.log(` ${key.padEnd(8)} ${styleText('yellow', value)}`)\n }\n}\n\nfunction formatDeployment(deployment: UndeployTarget['activeDeployment']): string | null {\n if (!deployment) return null\n const parts = [\n deployment.version ? `version ${deployment.version}` : null,\n deployment.deployedAt ? `at ${deployment.deployedAt}` : null,\n deployment.deployedBy ? `by ${deployment.deployedBy}` : null,\n ].filter(Boolean)\n return parts.length > 0 ? parts.join(' ') : null\n}\n"],"names":["styleText","checkStatusIcon","renderIssues","canUndeploy","plan","target","checks","every","check","status","undeployPlanToJson","errors","warnings","message","solution","push","application","reason","describeUndeployTarget","resolution","type","url","id","name","title","renderUndeployPlan","output","problems","filter","log","fix","renderTarget","length","label","rows","formatDeployment","activeDeployment","key","value","padEnd","deployment","parts","version","deployedAt","deployedBy","Boolean","join"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { styleText } from 'node:util';
2
2
  import { Args, Flags } from '@oclif/core';
3
3
  import { exit } from '@oclif/core/errors';
4
- import { SanityCommand, subdebug } from '@sanity/cli-core';
4
+ import { subdebug } from '@sanity/cli-core/debug';
5
+ import { SanityCommand } from '@sanity/cli-core/SanityCommand';
5
6
  import { spinner } from '@sanity/cli-core/ux';
6
7
  import { Table } from 'console-table-printer';
7
8
  import { formatDistance } from 'date-fns/formatDistance';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/datasets/copy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exit} from '@oclif/core/errors'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {Table} from 'console-table-printer'\nimport {formatDistance} from 'date-fns/formatDistance'\nimport {formatDistanceToNow} from 'date-fns/formatDistanceToNow'\nimport {parseISO} from 'date-fns/parseISO'\n\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {\n copyDataset,\n type CopyJobProgressEvent,\n type DatasetCopyJob,\n followCopyJobProgress,\n listDatasetCopyJobs,\n listDatasets,\n} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst copyDatasetDebug = subdebug('dataset:copy')\n\nexport class CopyDatasetCommand extends SanityCommand<typeof CopyDatasetCommand> {\n static override args = {\n source: Args.string({\n description: 'Name of the dataset to copy from',\n required: false,\n }),\n target: Args.string({\n description: 'Name of the dataset to copy to',\n required: false,\n }),\n }\n\n static override description = 'Copy a dataset or manage copy jobs'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively copy a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset',\n description: 'Copy from source-dataset (prompts for target)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset target-dataset',\n description: 'Copy from source-dataset to target-dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-history source target',\n description: 'Copy without preserving document history (faster for large datasets)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-content-releases source target',\n description: 'Copy without content release documents',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --detach source target',\n description: 'Start copy job without waiting for completion',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --attach <job-id>',\n description: 'Attach to a running copy job to follow progress',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list',\n description: 'List all dataset copy jobs',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list --offset 2 --limit 10',\n description: 'List copy jobs with pagination',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to copy dataset in',\n semantics: 'override',\n }),\n attach: Flags.string({\n description: 'Attach to the running copy process to show progress',\n exclusive: ['list', 'detach', 'skip-history'],\n required: false,\n }),\n detach: Flags.boolean({\n description: 'Start the copy without waiting for it to finish',\n exclusive: ['list', 'attach'],\n required: false,\n }),\n limit: Flags.integer({\n dependsOn: ['list'],\n description: 'Maximum number of jobs returned (default 10, max 1000)',\n max: 1000,\n required: false,\n }),\n list: Flags.boolean({\n description: 'Lists all dataset copy jobs',\n exclusive: ['attach', 'detach', 'skip-history'],\n required: false,\n }),\n offset: Flags.integer({\n dependsOn: ['list'],\n description: 'Start position in the list of jobs (default 0)',\n required: false,\n }),\n 'skip-content-releases': Flags.boolean({\n description: \"Don't copy content release documents to the target dataset\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n 'skip-history': Flags.boolean({\n description: \"Don't preserve document history on copy\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['dataset:copy']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CopyDatasetCommand)\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'create', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n // Route to appropriate mode\n if (flags.list) {\n return this.handleListMode(projectId, flags)\n }\n\n if (flags.attach) {\n return this.handleAttachMode(projectId, flags.attach)\n }\n\n return this.handleCopyMode(projectId, args, flags)\n }\n\n private displayCopyJobsTable(jobs: DatasetCopyJob[]): void {\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'Job ID'},\n {alignment: 'left', name: 'sourceDataset', title: 'Source Dataset'},\n {alignment: 'left', name: 'targetDataset', title: 'Target Dataset'},\n {alignment: 'left', name: 'state', title: 'State'},\n {alignment: 'left', name: 'withHistory', title: 'With history'},\n {alignment: 'left', name: 'timeStarted', title: 'Time started'},\n {alignment: 'left', name: 'timeTaken', title: 'Time taken'},\n ],\n title: 'Dataset copy jobs for this project in descending order',\n })\n\n for (const job of jobs) {\n const {createdAt, id, sourceDataset, state, targetDataset, updatedAt, withHistory} = job\n\n let timeStarted = ''\n if (createdAt !== '') {\n timeStarted = formatDistanceToNow(parseISO(createdAt))\n }\n\n let timeTaken = ''\n if (updatedAt !== '') {\n timeTaken = formatDistance(parseISO(updatedAt), parseISO(createdAt))\n }\n\n let color: '' | 'green' | 'red' | 'yellow'\n switch (state) {\n case 'completed': {\n color = 'green'\n break\n }\n case 'failed': {\n color = 'red'\n break\n }\n case 'pending': {\n color = 'yellow'\n break\n }\n default: {\n color = ''\n }\n }\n\n table.addRow(\n {\n id,\n sourceDataset,\n state,\n targetDataset,\n timeStarted: `${timeStarted} ago`,\n timeTaken,\n withHistory,\n },\n {color},\n )\n }\n\n this.output.log(table.render())\n }\n\n private async handleAttachMode(projectId: string, jobId: string): Promise<void> {\n copyDatasetDebug('Attaching to copy job %s', jobId)\n\n if (jobId.trim() === '') {\n return this.output.error('Please supply a valid jobId', {exit: 1})\n }\n\n try {\n await this.subscribeToProgress(projectId, jobId)\n this.output.log(`Job ${styleText('green', jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to attach to copy job: %s', message, error)\n return this.output.error(`Failed to attach to copy job: ${message}`, {exit: 1})\n }\n }\n\n private async handleCopyMode(\n projectId: string,\n args: {source?: string; target?: string},\n flags: {detach?: boolean; 'skip-content-releases'?: boolean; 'skip-history'?: boolean},\n ): Promise<void> {\n copyDatasetDebug('Starting copy mode')\n\n const skipHistory = Boolean(flags['skip-history'])\n const skipContentReleases = Boolean(flags['skip-content-releases'])\n\n // Get and validate source dataset\n let sourceDataset = args.source\n if (sourceDataset) {\n const nameError = validateDatasetName(sourceDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: 1})\n }\n }\n\n let datasetsResponse\n try {\n datasetsResponse = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to fetch datasets: %s', message, error)\n return this.output.error(`Failed to fetch datasets: ${message}`, {exit: 1})\n }\n\n const datasetNames = new Set(datasetsResponse.map((ds) => ds.name))\n\n // Prompt for source if not provided\n if (!sourceDataset) {\n sourceDataset = await promptForDataset({\n datasets: datasetsResponse,\n })\n }\n\n if (!datasetNames.has(sourceDataset)) {\n return this.output.error(`Source dataset \"${sourceDataset}\" doesn't exist`, {exit: 1})\n }\n\n // Get and validate target dataset\n let targetDataset = args.target\n if (targetDataset) {\n const nameError = validateDatasetName(targetDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: 1})\n }\n } else {\n targetDataset = await promptForDatasetName({\n message: 'Target dataset name:',\n })\n }\n\n if (datasetNames.has(targetDataset)) {\n return this.output.error(`Target dataset \"${targetDataset}\" already exists`, {exit: 1})\n }\n\n // Start the copy job\n try {\n this.output.log(\n `Copying dataset ${styleText('green', sourceDataset)} to ${styleText('green', targetDataset)}...`,\n )\n\n if (!skipHistory) {\n this.output.log(\n `Note: You can run this command with flag '--skip-history'. The flag will reduce copy time in larger datasets.`,\n )\n }\n\n const response = await copyDataset({\n projectId,\n skipContentReleases,\n skipHistory,\n sourceDataset,\n targetDataset,\n })\n\n this.output.log(`Job ${styleText('green', response.jobId)} started`)\n\n if (flags.detach) {\n return\n }\n\n await this.subscribeToProgress(projectId, response.jobId)\n this.output.log(`Job ${styleText('green', response.jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Dataset copying failed: %s', message, error)\n return this.output.error(`Dataset copying failed: ${message}`, {exit: 1})\n }\n }\n\n private async handleListMode(\n projectId: string,\n flags: {limit?: number; offset?: number},\n ): Promise<void> {\n copyDatasetDebug('Listing dataset copy jobs')\n\n try {\n const jobs = await listDatasetCopyJobs({\n limit: flags.limit,\n offset: flags.offset,\n projectId,\n })\n\n if (jobs.length === 0) {\n this.output.log(\"This project doesn't have any dataset copy jobs\")\n return\n }\n\n this.displayCopyJobsTable(jobs)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to list dataset copy jobs: %s', message, error)\n return this.output.error(`Failed to list dataset copy jobs: ${message}`, {exit: 1})\n }\n }\n\n private async subscribeToProgress(projectId: string, jobId: string): Promise<void> {\n const spin = spinner('').start()\n\n return new Promise<void>((resolve, reject) => {\n const sigintHandler = () => {\n subscription.unsubscribe()\n spin.fail('Copy interrupted.')\n exit(130)\n }\n\n const subscription = followCopyJobProgress({jobId, projectId}).subscribe({\n complete: () => {\n process.off('SIGINT', sigintHandler)\n spin.succeed('Copy finished.')\n resolve()\n },\n error: (err) => {\n process.off('SIGINT', sigintHandler)\n spin.fail('Copy failed.')\n reject(err)\n },\n next: (event: CopyJobProgressEvent) => {\n if (typeof event.progress === 'number') {\n spin.text = `Copy in progress: ${event.progress}%`\n }\n },\n })\n\n process.once('SIGINT', sigintHandler)\n })\n }\n}\n"],"names":["styleText","Args","Flags","exit","SanityCommand","subdebug","spinner","Table","formatDistance","formatDistanceToNow","parseISO","validateDatasetName","promptForDataset","promptForDatasetName","promptForProject","copyDataset","followCopyJobProgress","listDatasetCopyJobs","listDatasets","getProjectIdFlag","copyDatasetDebug","CopyDatasetCommand","args","source","string","description","required","target","examples","command","flags","semantics","attach","exclusive","detach","boolean","limit","integer","dependsOn","max","list","offset","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","handleListMode","handleAttachMode","handleCopyMode","displayCopyJobsTable","jobs","table","columns","alignment","name","title","job","createdAt","id","sourceDataset","state","targetDataset","updatedAt","withHistory","timeStarted","timeTaken","color","addRow","output","log","render","jobId","trim","error","subscribeToProgress","message","Error","String","skipHistory","Boolean","skipContentReleases","nameError","datasetsResponse","datasetNames","Set","map","ds","datasets","has","response","length","spin","start","Promise","resolve","reject","sigintHandler","subscription","unsubscribe","fail","subscribe","complete","process","off","succeed","err","next","event","progress","text","once"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/datasets/copy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exit} from '@oclif/core/errors'\nimport {subdebug} from '@sanity/cli-core/debug'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {Table} from 'console-table-printer'\nimport {formatDistance} from 'date-fns/formatDistance'\nimport {formatDistanceToNow} from 'date-fns/formatDistanceToNow'\nimport {parseISO} from 'date-fns/parseISO'\n\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {\n copyDataset,\n type CopyJobProgressEvent,\n type DatasetCopyJob,\n followCopyJobProgress,\n listDatasetCopyJobs,\n listDatasets,\n} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst copyDatasetDebug = subdebug('dataset:copy')\n\nexport class CopyDatasetCommand extends SanityCommand<typeof CopyDatasetCommand> {\n static override args = {\n source: Args.string({\n description: 'Name of the dataset to copy from',\n required: false,\n }),\n target: Args.string({\n description: 'Name of the dataset to copy to',\n required: false,\n }),\n }\n\n static override description = 'Copy a dataset or manage copy jobs'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively copy a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset',\n description: 'Copy from source-dataset (prompts for target)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset target-dataset',\n description: 'Copy from source-dataset to target-dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-history source target',\n description: 'Copy without preserving document history (faster for large datasets)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-content-releases source target',\n description: 'Copy without content release documents',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --detach source target',\n description: 'Start copy job without waiting for completion',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --attach <job-id>',\n description: 'Attach to a running copy job to follow progress',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list',\n description: 'List all dataset copy jobs',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list --offset 2 --limit 10',\n description: 'List copy jobs with pagination',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to copy dataset in',\n semantics: 'override',\n }),\n attach: Flags.string({\n description: 'Attach to the running copy process to show progress',\n exclusive: ['list', 'detach', 'skip-history'],\n required: false,\n }),\n detach: Flags.boolean({\n description: 'Start the copy without waiting for it to finish',\n exclusive: ['list', 'attach'],\n required: false,\n }),\n limit: Flags.integer({\n dependsOn: ['list'],\n description: 'Maximum number of jobs returned (default 10, max 1000)',\n max: 1000,\n required: false,\n }),\n list: Flags.boolean({\n description: 'Lists all dataset copy jobs',\n exclusive: ['attach', 'detach', 'skip-history'],\n required: false,\n }),\n offset: Flags.integer({\n dependsOn: ['list'],\n description: 'Start position in the list of jobs (default 0)',\n required: false,\n }),\n 'skip-content-releases': Flags.boolean({\n description: \"Don't copy content release documents to the target dataset\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n 'skip-history': Flags.boolean({\n description: \"Don't preserve document history on copy\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['dataset:copy']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CopyDatasetCommand)\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'create', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n // Route to appropriate mode\n if (flags.list) {\n return this.handleListMode(projectId, flags)\n }\n\n if (flags.attach) {\n return this.handleAttachMode(projectId, flags.attach)\n }\n\n return this.handleCopyMode(projectId, args, flags)\n }\n\n private displayCopyJobsTable(jobs: DatasetCopyJob[]): void {\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'Job ID'},\n {alignment: 'left', name: 'sourceDataset', title: 'Source Dataset'},\n {alignment: 'left', name: 'targetDataset', title: 'Target Dataset'},\n {alignment: 'left', name: 'state', title: 'State'},\n {alignment: 'left', name: 'withHistory', title: 'With history'},\n {alignment: 'left', name: 'timeStarted', title: 'Time started'},\n {alignment: 'left', name: 'timeTaken', title: 'Time taken'},\n ],\n title: 'Dataset copy jobs for this project in descending order',\n })\n\n for (const job of jobs) {\n const {createdAt, id, sourceDataset, state, targetDataset, updatedAt, withHistory} = job\n\n let timeStarted = ''\n if (createdAt !== '') {\n timeStarted = formatDistanceToNow(parseISO(createdAt))\n }\n\n let timeTaken = ''\n if (updatedAt !== '') {\n timeTaken = formatDistance(parseISO(updatedAt), parseISO(createdAt))\n }\n\n let color: '' | 'green' | 'red' | 'yellow'\n switch (state) {\n case 'completed': {\n color = 'green'\n break\n }\n case 'failed': {\n color = 'red'\n break\n }\n case 'pending': {\n color = 'yellow'\n break\n }\n default: {\n color = ''\n }\n }\n\n table.addRow(\n {\n id,\n sourceDataset,\n state,\n targetDataset,\n timeStarted: `${timeStarted} ago`,\n timeTaken,\n withHistory,\n },\n {color},\n )\n }\n\n this.output.log(table.render())\n }\n\n private async handleAttachMode(projectId: string, jobId: string): Promise<void> {\n copyDatasetDebug('Attaching to copy job %s', jobId)\n\n if (jobId.trim() === '') {\n return this.output.error('Please supply a valid jobId', {exit: 1})\n }\n\n try {\n await this.subscribeToProgress(projectId, jobId)\n this.output.log(`Job ${styleText('green', jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to attach to copy job: %s', message, error)\n return this.output.error(`Failed to attach to copy job: ${message}`, {exit: 1})\n }\n }\n\n private async handleCopyMode(\n projectId: string,\n args: {source?: string; target?: string},\n flags: {detach?: boolean; 'skip-content-releases'?: boolean; 'skip-history'?: boolean},\n ): Promise<void> {\n copyDatasetDebug('Starting copy mode')\n\n const skipHistory = Boolean(flags['skip-history'])\n const skipContentReleases = Boolean(flags['skip-content-releases'])\n\n // Get and validate source dataset\n let sourceDataset = args.source\n if (sourceDataset) {\n const nameError = validateDatasetName(sourceDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: 1})\n }\n }\n\n let datasetsResponse\n try {\n datasetsResponse = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to fetch datasets: %s', message, error)\n return this.output.error(`Failed to fetch datasets: ${message}`, {exit: 1})\n }\n\n const datasetNames = new Set(datasetsResponse.map((ds) => ds.name))\n\n // Prompt for source if not provided\n if (!sourceDataset) {\n sourceDataset = await promptForDataset({\n datasets: datasetsResponse,\n })\n }\n\n if (!datasetNames.has(sourceDataset)) {\n return this.output.error(`Source dataset \"${sourceDataset}\" doesn't exist`, {exit: 1})\n }\n\n // Get and validate target dataset\n let targetDataset = args.target\n if (targetDataset) {\n const nameError = validateDatasetName(targetDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: 1})\n }\n } else {\n targetDataset = await promptForDatasetName({\n message: 'Target dataset name:',\n })\n }\n\n if (datasetNames.has(targetDataset)) {\n return this.output.error(`Target dataset \"${targetDataset}\" already exists`, {exit: 1})\n }\n\n // Start the copy job\n try {\n this.output.log(\n `Copying dataset ${styleText('green', sourceDataset)} to ${styleText('green', targetDataset)}...`,\n )\n\n if (!skipHistory) {\n this.output.log(\n `Note: You can run this command with flag '--skip-history'. The flag will reduce copy time in larger datasets.`,\n )\n }\n\n const response = await copyDataset({\n projectId,\n skipContentReleases,\n skipHistory,\n sourceDataset,\n targetDataset,\n })\n\n this.output.log(`Job ${styleText('green', response.jobId)} started`)\n\n if (flags.detach) {\n return\n }\n\n await this.subscribeToProgress(projectId, response.jobId)\n this.output.log(`Job ${styleText('green', response.jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Dataset copying failed: %s', message, error)\n return this.output.error(`Dataset copying failed: ${message}`, {exit: 1})\n }\n }\n\n private async handleListMode(\n projectId: string,\n flags: {limit?: number; offset?: number},\n ): Promise<void> {\n copyDatasetDebug('Listing dataset copy jobs')\n\n try {\n const jobs = await listDatasetCopyJobs({\n limit: flags.limit,\n offset: flags.offset,\n projectId,\n })\n\n if (jobs.length === 0) {\n this.output.log(\"This project doesn't have any dataset copy jobs\")\n return\n }\n\n this.displayCopyJobsTable(jobs)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to list dataset copy jobs: %s', message, error)\n return this.output.error(`Failed to list dataset copy jobs: ${message}`, {exit: 1})\n }\n }\n\n private async subscribeToProgress(projectId: string, jobId: string): Promise<void> {\n const spin = spinner('').start()\n\n return new Promise<void>((resolve, reject) => {\n const sigintHandler = () => {\n subscription.unsubscribe()\n spin.fail('Copy interrupted.')\n exit(130)\n }\n\n const subscription = followCopyJobProgress({jobId, projectId}).subscribe({\n complete: () => {\n process.off('SIGINT', sigintHandler)\n spin.succeed('Copy finished.')\n resolve()\n },\n error: (err) => {\n process.off('SIGINT', sigintHandler)\n spin.fail('Copy failed.')\n reject(err)\n },\n next: (event: CopyJobProgressEvent) => {\n if (typeof event.progress === 'number') {\n spin.text = `Copy in progress: ${event.progress}%`\n }\n },\n })\n\n process.once('SIGINT', sigintHandler)\n })\n }\n}\n"],"names":["styleText","Args","Flags","exit","subdebug","SanityCommand","spinner","Table","formatDistance","formatDistanceToNow","parseISO","validateDatasetName","promptForDataset","promptForDatasetName","promptForProject","copyDataset","followCopyJobProgress","listDatasetCopyJobs","listDatasets","getProjectIdFlag","copyDatasetDebug","CopyDatasetCommand","args","source","string","description","required","target","examples","command","flags","semantics","attach","exclusive","detach","boolean","limit","integer","dependsOn","max","list","offset","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","handleListMode","handleAttachMode","handleCopyMode","displayCopyJobsTable","jobs","table","columns","alignment","name","title","job","createdAt","id","sourceDataset","state","targetDataset","updatedAt","withHistory","timeStarted","timeTaken","color","addRow","output","log","render","jobId","trim","error","subscribeToProgress","message","Error","String","skipHistory","Boolean","skipContentReleases","nameError","datasetsResponse","datasetNames","Set","map","ds","datasets","has","response","length","spin","start","Promise","resolve","reject","sigintHandler","subscription","unsubscribe","fail","subscribe","complete","process","off","succeed","err","next","event","progress","text","once"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { isInteractive, SanityCommand, subdebug } from '@sanity/cli-core';
2
+ import { getErrorMessage, toError } from '@sanity/cli-core/errors';
2
3
  import { ensureAuthenticated } from '../../actions/auth/ensureAuthenticated.js';
3
4
  import { setupMCP } from '../../actions/mcp/setupMCP.js';
4
5
  import { LoginError } from '../../errors/LoginError.js';
5
6
  import { MCPConfigureTrace } from '../../telemetry/mcp.telemetry.js';
6
- import { getErrorMessage, toError } from '../../util/getErrorMessage.js';
7
7
  const debug = subdebug('mcp:configure');
8
8
  export class ConfigureMcpCommand extends SanityCommand {
9
9
  static description = 'Configure Sanity MCP server for AI editors (Antigravity, Claude Code, Cline, Cline CLI, Codex CLI, Cursor, Gemini CLI, GitHub Copilot CLI, MCPorter, OpenCode, VS Code, VS Code Insiders, Zed)';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/mcp/configure.ts"],"sourcesContent":["import {isInteractive, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {ensureAuthenticated} from '../../actions/auth/ensureAuthenticated.js'\nimport {setupMCP} from '../../actions/mcp/setupMCP.js'\nimport {LoginError} from '../../errors/LoginError.js'\nimport {MCPConfigureTrace} from '../../telemetry/mcp.telemetry.js'\nimport {getErrorMessage, toError} from '../../util/getErrorMessage.js'\n\nconst debug = subdebug('mcp:configure')\n\nexport class ConfigureMcpCommand extends SanityCommand<typeof ConfigureMcpCommand> {\n static override description =\n 'Configure Sanity MCP server for AI editors (Antigravity, Claude Code, Cline, Cline CLI, Codex CLI, Cursor, Gemini CLI, GitHub Copilot CLI, MCPorter, OpenCode, VS Code, VS Code Insiders, Zed)'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Configure Sanity MCP server for detected AI editors',\n },\n ]\n\n public async run(): Promise<void> {\n const trace = this.telemetry.trace(MCPConfigureTrace)\n trace.start()\n\n try {\n await ensureAuthenticated({output: this.output, telemetry: this.telemetry})\n } catch (error) {\n debug('Authentication check failed: %O', error)\n trace.error(toError(error))\n\n if (error instanceof LoginError) {\n this.error(\n `Failed to verify authentication credentials: ${error.message}. Try running \\`sanity login\\`.`,\n {exit: 1},\n )\n }\n\n this.error(`Failed to check authentication: ${getErrorMessage(error)}`, {exit: 1})\n }\n\n try {\n const mcpResult = await setupMCP({\n explicit: true,\n mode: isInteractive() ? 'prompt' : 'auto',\n output: this.output,\n skillsMode: 'skip',\n })\n\n trace.log({\n configuredEditors: mcpResult.configuredEditors,\n detectedEditors: mcpResult.detectedEditors,\n })\n\n if (mcpResult.error) {\n trace.error(mcpResult.error)\n } else {\n trace.complete()\n }\n } catch (error) {\n trace.error(toError(error))\n this.error(getErrorMessage(error), {exit: 1})\n }\n }\n}\n"],"names":["isInteractive","SanityCommand","subdebug","ensureAuthenticated","setupMCP","LoginError","MCPConfigureTrace","getErrorMessage","toError","debug","ConfigureMcpCommand","description","examples","command","run","trace","telemetry","start","output","error","message","exit","mcpResult","explicit","mode","skillsMode","log","configuredEditors","detectedEditors","complete"],"mappings":"AAAA,SAAQA,aAAa,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AAEvE,SAAQC,mBAAmB,QAAO,4CAA2C;AAC7E,SAAQC,QAAQ,QAAO,gCAA+B;AACtD,SAAQC,UAAU,QAAO,6BAA4B;AACrD,SAAQC,iBAAiB,QAAO,mCAAkC;AAClE,SAAQC,eAAe,EAAEC,OAAO,QAAO,gCAA+B;AAEtE,MAAMC,QAAQP,SAAS;AAEvB,OAAO,MAAMQ,4BAA4BT;IACvC,OAAgBU,cACd,iMAAgM;IAElM,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,MAAaG,MAAqB;QAChC,MAAMC,QAAQ,IAAI,CAACC,SAAS,CAACD,KAAK,CAACT;QACnCS,MAAME,KAAK;QAEX,IAAI;YACF,MAAMd,oBAAoB;gBAACe,QAAQ,IAAI,CAACA,MAAM;gBAAEF,WAAW,IAAI,CAACA,SAAS;YAAA;QAC3E,EAAE,OAAOG,OAAO;YACdV,MAAM,mCAAmCU;YACzCJ,MAAMI,KAAK,CAACX,QAAQW;YAEpB,IAAIA,iBAAiBd,YAAY;gBAC/B,IAAI,CAACc,KAAK,CACR,CAAC,6CAA6C,EAAEA,MAAMC,OAAO,CAAC,+BAA+B,CAAC,EAC9F;oBAACC,MAAM;gBAAC;YAEZ;YAEA,IAAI,CAACF,KAAK,CAAC,CAAC,gCAAgC,EAAEZ,gBAAgBY,QAAQ,EAAE;gBAACE,MAAM;YAAC;QAClF;QAEA,IAAI;YACF,MAAMC,YAAY,MAAMlB,SAAS;gBAC/BmB,UAAU;gBACVC,MAAMxB,kBAAkB,WAAW;gBACnCkB,QAAQ,IAAI,CAACA,MAAM;gBACnBO,YAAY;YACd;YAEAV,MAAMW,GAAG,CAAC;gBACRC,mBAAmBL,UAAUK,iBAAiB;gBAC9CC,iBAAiBN,UAAUM,eAAe;YAC5C;YAEA,IAAIN,UAAUH,KAAK,EAAE;gBACnBJ,MAAMI,KAAK,CAACG,UAAUH,KAAK;YAC7B,OAAO;gBACLJ,MAAMc,QAAQ;YAChB;QACF,EAAE,OAAOV,OAAO;YACdJ,MAAMI,KAAK,CAACX,QAAQW;YACpB,IAAI,CAACA,KAAK,CAACZ,gBAAgBY,QAAQ;gBAACE,MAAM;YAAC;QAC7C;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/commands/mcp/configure.ts"],"sourcesContent":["import {isInteractive, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage, toError} from '@sanity/cli-core/errors'\n\nimport {ensureAuthenticated} from '../../actions/auth/ensureAuthenticated.js'\nimport {setupMCP} from '../../actions/mcp/setupMCP.js'\nimport {LoginError} from '../../errors/LoginError.js'\nimport {MCPConfigureTrace} from '../../telemetry/mcp.telemetry.js'\n\nconst debug = subdebug('mcp:configure')\n\nexport class ConfigureMcpCommand extends SanityCommand<typeof ConfigureMcpCommand> {\n static override description =\n 'Configure Sanity MCP server for AI editors (Antigravity, Claude Code, Cline, Cline CLI, Codex CLI, Cursor, Gemini CLI, GitHub Copilot CLI, MCPorter, OpenCode, VS Code, VS Code Insiders, Zed)'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Configure Sanity MCP server for detected AI editors',\n },\n ]\n\n public async run(): Promise<void> {\n const trace = this.telemetry.trace(MCPConfigureTrace)\n trace.start()\n\n try {\n await ensureAuthenticated({output: this.output, telemetry: this.telemetry})\n } catch (error) {\n debug('Authentication check failed: %O', error)\n trace.error(toError(error))\n\n if (error instanceof LoginError) {\n this.error(\n `Failed to verify authentication credentials: ${error.message}. Try running \\`sanity login\\`.`,\n {exit: 1},\n )\n }\n\n this.error(`Failed to check authentication: ${getErrorMessage(error)}`, {exit: 1})\n }\n\n try {\n const mcpResult = await setupMCP({\n explicit: true,\n mode: isInteractive() ? 'prompt' : 'auto',\n output: this.output,\n skillsMode: 'skip',\n })\n\n trace.log({\n configuredEditors: mcpResult.configuredEditors,\n detectedEditors: mcpResult.detectedEditors,\n })\n\n if (mcpResult.error) {\n trace.error(mcpResult.error)\n } else {\n trace.complete()\n }\n } catch (error) {\n trace.error(toError(error))\n this.error(getErrorMessage(error), {exit: 1})\n }\n }\n}\n"],"names":["isInteractive","SanityCommand","subdebug","getErrorMessage","toError","ensureAuthenticated","setupMCP","LoginError","MCPConfigureTrace","debug","ConfigureMcpCommand","description","examples","command","run","trace","telemetry","start","output","error","message","exit","mcpResult","explicit","mode","skillsMode","log","configuredEditors","detectedEditors","complete"],"mappings":"AAAA,SAAQA,aAAa,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACvE,SAAQC,eAAe,EAAEC,OAAO,QAAO,0BAAyB;AAEhE,SAAQC,mBAAmB,QAAO,4CAA2C;AAC7E,SAAQC,QAAQ,QAAO,gCAA+B;AACtD,SAAQC,UAAU,QAAO,6BAA4B;AACrD,SAAQC,iBAAiB,QAAO,mCAAkC;AAElE,MAAMC,QAAQP,SAAS;AAEvB,OAAO,MAAMQ,4BAA4BT;IACvC,OAAgBU,cACd,iMAAgM;IAElM,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,MAAaG,MAAqB;QAChC,MAAMC,QAAQ,IAAI,CAACC,SAAS,CAACD,KAAK,CAACP;QACnCO,MAAME,KAAK;QAEX,IAAI;YACF,MAAMZ,oBAAoB;gBAACa,QAAQ,IAAI,CAACA,MAAM;gBAAEF,WAAW,IAAI,CAACA,SAAS;YAAA;QAC3E,EAAE,OAAOG,OAAO;YACdV,MAAM,mCAAmCU;YACzCJ,MAAMI,KAAK,CAACf,QAAQe;YAEpB,IAAIA,iBAAiBZ,YAAY;gBAC/B,IAAI,CAACY,KAAK,CACR,CAAC,6CAA6C,EAAEA,MAAMC,OAAO,CAAC,+BAA+B,CAAC,EAC9F;oBAACC,MAAM;gBAAC;YAEZ;YAEA,IAAI,CAACF,KAAK,CAAC,CAAC,gCAAgC,EAAEhB,gBAAgBgB,QAAQ,EAAE;gBAACE,MAAM;YAAC;QAClF;QAEA,IAAI;YACF,MAAMC,YAAY,MAAMhB,SAAS;gBAC/BiB,UAAU;gBACVC,MAAMxB,kBAAkB,WAAW;gBACnCkB,QAAQ,IAAI,CAACA,MAAM;gBACnBO,YAAY;YACd;YAEAV,MAAMW,GAAG,CAAC;gBACRC,mBAAmBL,UAAUK,iBAAiB;gBAC9CC,iBAAiBN,UAAUM,eAAe;YAC5C;YAEA,IAAIN,UAAUH,KAAK,EAAE;gBACnBJ,MAAMI,KAAK,CAACG,UAAUH,KAAK;YAC7B,OAAO;gBACLJ,MAAMc,QAAQ;YAChB;QACF,EAAE,OAAOV,OAAO;YACdJ,MAAMI,KAAK,CAACf,QAAQe;YACpB,IAAI,CAACA,KAAK,CAAChB,gBAAgBgB,QAAQ;gBAACE,MAAM;YAAC;QAC7C;IACF;AACF"}