@sanity/runtime-cli 11.2.0 → 12.0.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 (47) hide show
  1. package/README.md +36 -38
  2. package/dist/actions/blueprints/blueprint.d.ts +3 -0
  3. package/dist/actions/blueprints/blueprint.js +26 -14
  4. package/dist/actions/blueprints/config.d.ts +34 -3
  5. package/dist/actions/blueprints/config.js +67 -14
  6. package/dist/actions/blueprints/stacks.d.ts +1 -2
  7. package/dist/actions/blueprints/stacks.js +2 -3
  8. package/dist/commands/blueprints/config.d.ts +0 -1
  9. package/dist/commands/blueprints/config.js +4 -12
  10. package/dist/commands/blueprints/deploy.js +1 -1
  11. package/dist/commands/blueprints/destroy.js +3 -3
  12. package/dist/commands/blueprints/info.js +2 -2
  13. package/dist/commands/blueprints/init.d.ts +1 -0
  14. package/dist/commands/blueprints/init.js +4 -0
  15. package/dist/commands/blueprints/logs.js +2 -2
  16. package/dist/commands/blueprints/plan.js +1 -0
  17. package/dist/config.d.ts +2 -1
  18. package/dist/config.js +8 -1
  19. package/dist/cores/blueprints/config.d.ts +1 -1
  20. package/dist/cores/blueprints/config.js +92 -78
  21. package/dist/cores/blueprints/deploy.js +8 -8
  22. package/dist/cores/blueprints/destroy.d.ts +1 -0
  23. package/dist/cores/blueprints/destroy.js +22 -26
  24. package/dist/cores/blueprints/doctor.js +24 -72
  25. package/dist/cores/blueprints/info.d.ts +1 -0
  26. package/dist/cores/blueprints/info.js +5 -4
  27. package/dist/cores/blueprints/init.d.ts +1 -1
  28. package/dist/cores/blueprints/init.js +50 -78
  29. package/dist/cores/blueprints/logs.d.ts +1 -0
  30. package/dist/cores/blueprints/logs.js +7 -7
  31. package/dist/cores/blueprints/plan.d.ts +3 -0
  32. package/dist/cores/blueprints/plan.js +5 -4
  33. package/dist/cores/blueprints/stacks.d.ts +1 -0
  34. package/dist/cores/blueprints/stacks.js +1 -2
  35. package/dist/cores/functions/add.js +58 -70
  36. package/dist/cores/functions/logs.js +2 -4
  37. package/dist/cores/index.js +3 -3
  38. package/dist/server/static/vendor/vendor.bundle.js +515 -234
  39. package/dist/utils/display/blueprints-formatting.js +8 -3
  40. package/dist/utils/display/errors.js +2 -2
  41. package/dist/utils/display/presenters.d.ts +2 -0
  42. package/dist/utils/display/presenters.js +7 -0
  43. package/dist/utils/display/prompt.d.ts +14 -2
  44. package/dist/utils/display/prompt.js +60 -41
  45. package/dist/utils/types.d.ts +0 -1
  46. package/oclif.manifest.json +19 -26
  47. package/package.json +6 -5
@@ -2,14 +2,14 @@ import { Flags } from '@oclif/core';
2
2
  import { BlueprintCommand } from '../../baseCommands.js';
3
3
  import { blueprintDestroyCore } from '../../cores/blueprints/destroy.js';
4
4
  export default class DestroyCommand extends BlueprintCommand {
5
- static description = 'Destroy a Blueprint deployment (will not delete local files)';
5
+ static description = 'Destroy a Blueprint Stack deployment and its resources (will not delete local files)';
6
6
  static examples = [
7
7
  '<%= config.bin %> <%= command.id %>',
8
8
  '<%= config.bin %> <%= command.id %> --stack-id <stackId> --project-id <projectId> --force --no-wait',
9
9
  ];
10
10
  static flags = {
11
11
  force: Flags.boolean({
12
- description: 'Force destroy (skip confirmation)',
12
+ description: 'Force Stack destruction (skip confirmation)',
13
13
  aliases: ['f'],
14
14
  default: false,
15
15
  }),
@@ -28,7 +28,7 @@ export default class DestroyCommand extends BlueprintCommand {
28
28
  aliases: ['stackId', 'stack'],
29
29
  }),
30
30
  'no-wait': Flags.boolean({
31
- description: 'Do not wait for destruction to complete',
31
+ description: 'Do not wait for Stack destruction to complete',
32
32
  default: false,
33
33
  }),
34
34
  };
@@ -2,14 +2,14 @@ import { Flags } from '@oclif/core';
2
2
  import { DeployedBlueprintCommand } from '../../baseCommands.js';
3
3
  import { blueprintInfoCore } from '../../cores/blueprints/info.js';
4
4
  export default class InfoCommand extends DeployedBlueprintCommand {
5
- static description = 'Show information about a Blueprint deployment';
5
+ static description = 'Show information about a Blueprint Stack deployment';
6
6
  static examples = [
7
7
  '<%= config.bin %> <%= command.id %>',
8
8
  '<%= config.bin %> <%= command.id %> --stack-id <stackId>',
9
9
  ];
10
10
  static flags = {
11
11
  id: Flags.string({
12
- description: 'Stack ID to show info for (defaults to current stack)',
12
+ description: 'Stack ID to show info for (defaults to current Stack)',
13
13
  }),
14
14
  };
15
15
  async run() {
@@ -13,6 +13,7 @@ export default class InitCommand extends Command {
13
13
  'organization-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
14
  'stack-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
15
  'stack-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
17
  };
17
18
  run(): Promise<void>;
18
19
  }
@@ -48,6 +48,10 @@ export default class InitCommand extends Command {
48
48
  aliases: ['name'],
49
49
  exclusive: ['stack-id'],
50
50
  }),
51
+ verbose: Flags.boolean({
52
+ description: 'Verbose output',
53
+ default: false,
54
+ }),
51
55
  };
52
56
  async run() {
53
57
  const { args, flags } = await this.parse(InitCommand);
@@ -2,7 +2,7 @@ import { Flags } from '@oclif/core';
2
2
  import { DeployedBlueprintCommand } from '../../baseCommands.js';
3
3
  import { blueprintLogsCore } from '../../cores/blueprints/logs.js';
4
4
  export default class LogsCommand extends DeployedBlueprintCommand {
5
- static description = 'Display logs for a Blueprint deployment';
5
+ static description = 'Display logs for a Blueprint Stack deployment';
6
6
  static examples = [
7
7
  '<%= config.bin %> <%= command.id %>',
8
8
  '<%= config.bin %> <%= command.id %> --watch',
@@ -10,7 +10,7 @@ export default class LogsCommand extends DeployedBlueprintCommand {
10
10
  static flags = {
11
11
  watch: Flags.boolean({
12
12
  char: 'w',
13
- description: 'Watch for new logs (streaming mode)',
13
+ description: 'Watch for new Stack logs (streaming mode)',
14
14
  aliases: ['follow'],
15
15
  }),
16
16
  };
@@ -9,6 +9,7 @@ export default class PlanCommand extends BlueprintCommand {
9
9
  log: (message) => this.log(message),
10
10
  token: this.sanityToken,
11
11
  blueprint: this.blueprint,
12
+ flags: this.flags,
12
13
  });
13
14
  if (!success)
14
15
  this.error(error);
package/dist/config.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export declare const BLUEPRINT_CONFIG_VERSION = "v2025-05-08";
2
- export declare const BLUEPRINT_DIR = ".sanity";
2
+ export declare const BLUEPRINT_CONFIG_DIR = ".sanity";
3
3
  export declare const BLUEPRINT_CONFIG_FILE = "blueprint.config.json";
4
+ export declare let RUNTIME_CLI_VERSION: string | undefined;
4
5
  declare const _default: {
5
6
  isTest: boolean;
6
7
  apiUrl: string;
package/dist/config.js CHANGED
@@ -1,8 +1,15 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
1
3
  import { env } from 'node:process';
2
4
  import getToken from './utils/get-token.js';
3
5
  export const BLUEPRINT_CONFIG_VERSION = 'v2025-05-08';
4
- export const BLUEPRINT_DIR = '.sanity';
6
+ export const BLUEPRINT_CONFIG_DIR = '.sanity';
5
7
  export const BLUEPRINT_CONFIG_FILE = 'blueprint.config.json';
8
+ export let RUNTIME_CLI_VERSION;
9
+ try {
10
+ RUNTIME_CLI_VERSION = JSON.parse(readFileSync(join(dirname(import.meta.url), '..', 'package.json'), 'utf8')).version;
11
+ }
12
+ catch { }
6
13
  const nodeEnv = env.NODE_ENV?.toLowerCase() ?? 'production';
7
14
  const isTest = nodeEnv === 'test';
8
15
  const sanityEnv = env.SANITY_INTERNAL_ENV?.toLowerCase() ?? 'production';
@@ -4,11 +4,11 @@ export interface BlueprintConfigOptions extends CoreConfig {
4
4
  token: string;
5
5
  blueprint: ReadBlueprintResult;
6
6
  flags: {
7
- 'test-config'?: boolean;
8
7
  edit?: boolean;
9
8
  'project-id'?: string;
10
9
  'organization-id'?: string;
11
10
  'stack-id'?: string;
11
+ verbose?: boolean;
12
12
  };
13
13
  }
14
14
  export declare function blueprintConfigCore(options: BlueprintConfigOptions): Promise<CoreResult>;
@@ -1,89 +1,103 @@
1
1
  import { highlight } from 'cardinal';
2
2
  import chalk from 'chalk';
3
- import { writeConfigFile } from '../../actions/blueprints/config.js';
4
- import { BLUEPRINT_CONFIG_FILE, BLUEPRINT_DIR } from '../../config.js';
5
- import { capitalize, niceId, warn } from '../../utils/display/presenters.js';
6
- import { promptForProject } from '../../utils/display/prompt.js';
3
+ import { patchConfigFile, writeConfigFile, } from '../../actions/blueprints/config.js';
4
+ import { BLUEPRINT_CONFIG_DIR, BLUEPRINT_CONFIG_FILE } from '../../config.js';
5
+ import { capitalize, filePathRelativeToCwd, niceId, warn } from '../../utils/display/presenters.js';
6
+ import { promptForProject, promptForStack } from '../../utils/display/prompt.js';
7
7
  export async function blueprintConfigCore(options) {
8
- const { bin = 'sanity', log, token, flags } = options;
9
- const { edit: editConfig = false, 'test-config': testConfig = false, 'project-id': flagProjectId, 'organization-id': flagOrganizationId, 'stack-id': flagStackId, } = flags;
10
- const { blueprint } = options;
11
- const { stackId: configStackId, scopeType: configScopeType, scopeId: configScopeId } = blueprint;
12
- try {
13
- if (!configStackId && !configScopeType && !configScopeId) {
14
- log(warn('Incomplete configuration.'));
15
- if (!editConfig) {
16
- // blueprint.json exists but no config JSON
17
- log(`Run \`${bin} blueprints doctor\` for diagnostics.`);
18
- return { success: true }; // not necessarily fatal
19
- }
8
+ const { bin = 'sanity', blueprint, log, token, flags } = options;
9
+ const { edit: editConfig = false, 'project-id': flagProjectId, 'organization-id': flagOrganizationId, 'stack-id': flagStackId, verbose: v = false, } = flags;
10
+ const { stackId: configStackId, scopeType: configScopeType, scopeId: configScopeId, blueprintConfig, fileInfo, } = blueprint;
11
+ const blueprintFilePath = fileInfo.blueprintFilePath;
12
+ const configPath = blueprintConfig?.configPath;
13
+ if (!configStackId && !configScopeType && !configScopeId) {
14
+ log(warn('Incomplete configuration.'));
15
+ if (!editConfig) {
16
+ // blueprint.json exists but no config JSON
17
+ log(`Run \`${bin} blueprints doctor\` for diagnostics.`);
18
+ return { success: true }; // not necessarily fatal
20
19
  }
21
- log(chalk.bold('Current configuration:'));
22
- log(` Blueprint scoped to: ${chalk.blue(capitalize(configScopeType || 'unknown'))} ${niceId(configScopeId || 'unknown')}`);
23
- log(` Deployment ID: ${niceId(configStackId || 'unknown')}`);
24
- // passing new config without --edit flag is not allowed
25
- if ((flagProjectId || flagOrganizationId || flagStackId) && !editConfig) {
26
- log('To update the configuration, use the --edit flag.');
20
+ }
21
+ log(chalk.bold('Current Blueprint configuration:'));
22
+ if (configPath) {
23
+ if (v)
24
+ log(` File: ${filePathRelativeToCwd(configPath)}`);
25
+ }
26
+ log(` Deployment: ${chalk.blue('Stack')} ${niceId(configStackId || 'unknown')}`);
27
+ log(` Scoped to: ${chalk.blue(capitalize(configScopeType || 'unknown'))} ${niceId(configScopeId || 'unknown')}`);
28
+ if (blueprintConfig?.updatedAt) {
29
+ if (v)
30
+ log(` Updated: ${new Date(blueprintConfig.updatedAt).toLocaleString()}`);
31
+ }
32
+ // passing new config without --edit flag is not allowed
33
+ if ((flagProjectId || flagOrganizationId || flagStackId) && !editConfig) {
34
+ log('To update the configuration, use the --edit flag.');
35
+ return { success: true };
36
+ }
37
+ if (!editConfig) {
38
+ // no edit; return success early
39
+ return { success: true };
40
+ }
41
+ else {
42
+ // if a config property flag was passed, set the value and return success
43
+ // do not try to validate correctness of combined flags; this should not be interactive
44
+ const providedConfigFlag = [flagProjectId, flagStackId, flagOrganizationId].some(Boolean);
45
+ if (providedConfigFlag) {
46
+ const configUpdate = {};
47
+ if (flagProjectId)
48
+ configUpdate.projectId = flagProjectId;
49
+ if (flagStackId)
50
+ configUpdate.stackId = flagStackId;
51
+ if (flagOrganizationId)
52
+ configUpdate.organizationId = flagOrganizationId;
53
+ try {
54
+ patchConfigFile(blueprintFilePath, configUpdate);
55
+ }
56
+ catch {
57
+ return { success: false, error: 'Unable to update configuration.' };
58
+ }
59
+ log('\nConfiguration updated.');
27
60
  return { success: true };
28
61
  }
29
- // no edit or test: return success
30
- if (!editConfig && !testConfig)
62
+ // organization-based Blueprints are not yet supported for interactive editing
63
+ if (configScopeType === 'organization') {
64
+ return { success: false, error: 'Only project-based Blueprints are supported.' };
65
+ }
66
+ // otherwise, prompt for values interactively
67
+ let updatedProjectId = flagProjectId;
68
+ if (!updatedProjectId) {
69
+ const pickedProject = await promptForProject({
70
+ token,
71
+ knownProjectId: configScopeId,
72
+ });
73
+ updatedProjectId = pickedProject.projectId;
74
+ }
75
+ if (!updatedProjectId)
76
+ return { success: false, error: 'Project ID is required.' };
77
+ let updatedStackId = flagStackId;
78
+ if (!updatedStackId) {
79
+ const pickedStack = await promptForStack({ projectId: updatedProjectId, token });
80
+ updatedStackId = pickedStack.stackId;
81
+ }
82
+ if (!updatedStackId)
83
+ return { success: false, error: 'Stack is required.' };
84
+ try {
85
+ // update or create config JSON
86
+ writeConfigFile({ blueprintFilePath, projectId: updatedProjectId, stackId: updatedStackId });
87
+ log(`\n${chalk.bold('New configuration:')}`);
88
+ log(` Deployment: ${chalk.blue('Stack')} ${niceId(updatedStackId)}`);
89
+ log(` Scoped to: ${chalk.blue('Project')} ${niceId(updatedProjectId)}`);
90
+ log('');
91
+ log('Configuration updated.');
31
92
  return { success: true };
32
- // warn about deprecated test-config flag
33
- if (testConfig) {
34
- log(warn(`The "test" flag is deprecated. Use "${bin} blueprints doctor" instead.`));
35
93
  }
36
- // editing...
37
- if (editConfig) {
38
- if (configScopeType === 'project') {
39
- // maintain original project-based flow
40
- const updatedProjectId = flagProjectId ||
41
- (await promptForProject({
42
- token,
43
- knownProjectId: configScopeId,
44
- })).projectId;
45
- if (!updatedProjectId) {
46
- return {
47
- success: false,
48
- error: 'Project ID is required.',
49
- };
50
- }
51
- // LAUNCH LIMIT: 1 Stack per Project - configStackId is always inferred from projectId if not set in config JSON
52
- let updatedStackId = flagStackId ?? // flag first
53
- configStackId ?? // existing config second
54
- `ST-${updatedProjectId}`; //?? LAUNCH LIMIT: 1 Stack per Project - project-based third
55
- // (await promptForStackId({projectId: updatedProjectId, knownStackId: configStackId})) // prompt for stackId
56
- const isProjectBasedId = updatedStackId === `ST-${updatedProjectId}`;
57
- log(`\n${chalk.bold('New configuration:')}`);
58
- log(` Blueprint scoped to: ${chalk.blue(capitalize(configScopeType))} ${niceId(updatedProjectId)}`);
59
- log(` Deployment ID: ${niceId(updatedStackId)}`);
60
- // LAUNCH LIMIT: 1 Stack per Project - do not set Stack ID if it's project-based
61
- if (isProjectBasedId)
62
- updatedStackId = undefined;
63
- try {
64
- // update or create config JSON
65
- writeConfigFile({ projectId: updatedProjectId, stackId: updatedStackId });
66
- log('Configuration updated successfully.');
67
- return { success: true };
68
- }
69
- catch {
70
- log(`Unable to update config. These values should be set in ${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`);
71
- log(highlight(JSON.stringify({ metadata: { projectId: updatedProjectId, stackId: updatedStackId } }, null, 2)));
72
- return {
73
- success: false,
74
- error: `Be sure to update your ${BLUEPRINT_DIR}/${BLUEPRINT_CONFIG_FILE}`,
75
- };
76
- }
77
- }
78
- else {
79
- log(warn('Only project-based Blueprints are currently supported.'));
80
- return { success: false, error: 'Only project-based Blueprints are supported.' };
81
- }
94
+ catch {
95
+ log(`Unable to update config. These values should be set in ${BLUEPRINT_CONFIG_DIR}/${BLUEPRINT_CONFIG_FILE}`);
96
+ log(highlight(JSON.stringify({ metadata: { projectId: updatedProjectId, stackId: updatedStackId } }, null, 2)));
97
+ return {
98
+ success: false,
99
+ error: `Be sure to update your ${BLUEPRINT_CONFIG_DIR}/${BLUEPRINT_CONFIG_FILE}`,
100
+ };
82
101
  }
83
- // Default return (shouldn't reach here with proper flow control)
84
- return { success: true };
85
- }
86
- catch {
87
- return { success: false, error: 'Unknown error' };
88
102
  }
89
103
  }
@@ -64,16 +64,16 @@ export async function blueprintDeployCore(options) {
64
64
  auth,
65
65
  });
66
66
  if (!deployOk) {
67
- spinner.fail(`${chalk.red('Failed')} to update deployment`);
68
- return { success: false, error: deployError || 'Failed to update deployment' };
67
+ spinner.fail(`${chalk.red('Failed')} to update Stack deployment`);
68
+ return { success: false, error: deployError || 'Failed to update Stack deployment' };
69
69
  }
70
70
  spinner.stop().clear();
71
71
  if (noWait) {
72
- log(chalk.bold.green('Deployment started!'));
72
+ log(chalk.bold.green('Stack deployment started!'));
73
73
  log(`Use \`${bin} blueprints info\` to check status`);
74
74
  return { success: true, data: { resources } };
75
75
  }
76
- log(chalk.dim('Deployment progress:'));
76
+ log(chalk.dim('Stack deployment progress:'));
77
77
  let logStreamCleanup = null;
78
78
  try {
79
79
  logStreamCleanup = await setupLogStreaming({
@@ -87,23 +87,23 @@ export async function blueprintDeployCore(options) {
87
87
  if (!ok) {
88
88
  if (logStreamCleanup)
89
89
  logStreamCleanup();
90
- return { success: false, error: 'Failed to check deployment status' };
90
+ return { success: false, error: 'Failed to check Stack deployment status' };
91
91
  }
92
92
  const operation = currentStack.recentOperation;
93
93
  if (!operation) {
94
94
  if (logStreamCleanup)
95
95
  logStreamCleanup();
96
- return { success: false, error: 'No deployment operation found' };
96
+ return { success: false, error: 'No Stack deployment operation found' };
97
97
  }
98
98
  if (operation.status === 'FAILED') {
99
99
  if (logStreamCleanup)
100
100
  logStreamCleanup();
101
- return { success: false, error: 'Deployment failed' };
101
+ return { success: false, error: 'Stack deployment failed' };
102
102
  }
103
103
  if (operation.status === 'COMPLETED') {
104
104
  if (logStreamCleanup)
105
105
  logStreamCleanup();
106
- log(chalk.bold.green('Deployment completed!'));
106
+ log(chalk.bold.green('Stack deployment completed!'));
107
107
  return { success: true, data: { resources } };
108
108
  }
109
109
  await setTimeout(1500);
@@ -9,6 +9,7 @@ export interface BlueprintDestroyOptions extends CoreConfig {
9
9
  'organization-id'?: string;
10
10
  'stack-id'?: string;
11
11
  'no-wait'?: boolean;
12
+ verbose?: boolean;
12
13
  };
13
14
  }
14
15
  export declare function blueprintDestroyCore(options: BlueprintDestroyOptions): Promise<CoreResult>;
@@ -1,13 +1,13 @@
1
1
  import { setTimeout } from 'node:timers/promises';
2
+ import { confirm } from '@inquirer/prompts';
2
3
  import chalk from 'chalk';
3
- import inquirer from 'inquirer';
4
4
  import ora from 'ora';
5
5
  import { setupLogStreaming } from '../../actions/blueprints/logs-streaming.js';
6
6
  import { destroyStack, getStack } from '../../actions/blueprints/stacks.js';
7
7
  import { niceId } from '../../utils/display/presenters.js';
8
8
  export async function blueprintDestroyCore(options) {
9
9
  const { log, token, blueprint, flags } = options;
10
- const { force = false, 'project-id': flagProjectId, 'organization-id': flagOrganizationId, 'stack-id': flagStackId, 'no-wait': noWait = false, } = flags;
10
+ const { force = false, 'project-id': flagProjectId, 'organization-id': flagOrganizationId, 'stack-id': flagStackId, 'no-wait': noWait = false, verbose: _verbose = false, } = flags;
11
11
  // 3-flag combo: just destroy it
12
12
  if ((flagProjectId || flagOrganizationId) && flagStackId && force) {
13
13
  let scopeType;
@@ -28,8 +28,8 @@ export async function blueprintDestroyCore(options) {
28
28
  auth: { token, scopeType, scopeId },
29
29
  });
30
30
  if (!ok)
31
- return { success: false, error: error || 'Failed to destroy deployment' };
32
- log(`Deployment "${stack.name}" ${niceId(stack.id)} destroyed`);
31
+ return { success: false, error: error || 'Failed to destroy Stack deployment' };
32
+ log(`Stack deployment "${stack.name}" ${niceId(stack.id)} destroyed`);
33
33
  return { success: true };
34
34
  }
35
35
  const { scopeType, scopeId, stackId } = blueprint;
@@ -41,42 +41,38 @@ export async function blueprintDestroyCore(options) {
41
41
  if (flagStackId) {
42
42
  const flagStack = await getStack({ stackId: flagStackId, auth });
43
43
  if (!flagStack.ok)
44
- return { success: false, error: flagStack.error || 'Failed to get stack' };
44
+ return { success: false, error: flagStack.error || 'Failed to get Stack' };
45
45
  stack = flagStack.stack;
46
46
  }
47
47
  else if (stackId) {
48
48
  const blueprintStack = await getStack({ stackId, auth });
49
49
  if (!blueprintStack.ok)
50
- return { success: false, error: blueprintStack.error || 'Failed to get stack' };
50
+ return { success: false, error: blueprintStack.error || 'Failed to get Stack' };
51
51
  stack = blueprintStack.stack;
52
52
  }
53
53
  if (!stack)
54
- return { success: false, error: 'Deployment not found' };
54
+ return { success: false, error: 'Stack deployment not found' };
55
55
  const destroySpinner = ora({
56
- text: `Destroying ${chalk.bold(stack.name)} ${niceId(stack.id)}...`,
56
+ text: `Destroying Stack deployment "${chalk.bold(stack.name)}" ${niceId(stack.id)}...`,
57
57
  color: 'red',
58
58
  });
59
59
  if (!force) {
60
- const { confirm } = await inquirer.prompt([
61
- {
62
- type: 'confirm',
63
- name: 'confirm',
64
- message: `Are you sure you want to destroy stack "${stack.name}" ${niceId(stack.id)}?`,
65
- default: false,
66
- },
67
- ]);
68
- if (!confirm) {
69
- log('Deployment destruction cancelled');
60
+ const confirmed = await confirm({
61
+ message: `Are you sure you want to destroy stack "${stack.name}" ${niceId(stack.id)}?`,
62
+ default: false,
63
+ });
64
+ if (!confirmed) {
65
+ log('Stack deployment destruction cancelled');
70
66
  return { success: true };
71
67
  }
72
68
  destroySpinner.start();
73
69
  // 5 second countdown
74
70
  let i = 5;
75
71
  while (i >= 0) {
76
- destroySpinner.text = `Destroying deployment in ${chalk.bold((i--).toString())} seconds...`;
72
+ destroySpinner.text = `Destroying Stack deployment in ${chalk.bold((i--).toString())} seconds...`;
77
73
  await setTimeout(1000);
78
74
  }
79
- destroySpinner.text = 'Destroying deployment 💥';
75
+ destroySpinner.text = 'Destroying Stack deployment 💥';
80
76
  await setTimeout(500);
81
77
  }
82
78
  else {
@@ -85,15 +81,15 @@ export async function blueprintDestroyCore(options) {
85
81
  const isoNow = new Date().toISOString();
86
82
  const { ok, error } = await destroyStack({ stackId: stack.id, auth });
87
83
  if (!ok) {
88
- destroySpinner.fail('Failed to destroy deployment');
89
- return { success: false, error: error || 'Failed to destroy deployment' };
84
+ destroySpinner.fail('Failed to destroy Stack deployment');
85
+ return { success: false, error: error || 'Failed to destroy Stack deployment' };
90
86
  }
91
87
  destroySpinner.stop().clear();
92
88
  if (noWait) {
93
- log(chalk.bold.magenta('Destruction started!'));
89
+ log(chalk.bold.magenta('Stack destruction started!'));
94
90
  return { success: true };
95
91
  }
96
- log(chalk.dim('Destruction progress:'));
92
+ log(chalk.dim('Stack destruction progress:'));
97
93
  let logStreamCleanup = null;
98
94
  try {
99
95
  logStreamCleanup = await setupLogStreaming({
@@ -110,13 +106,13 @@ export async function blueprintDestroyCore(options) {
110
106
  // it's possible that the operation is "gone" or available and "COMPLETED"
111
107
  if (logStreamCleanup)
112
108
  logStreamCleanup();
113
- log(chalk.bold.magenta('Destruction completed!'));
109
+ log(chalk.bold.magenta('Stack destruction completed!'));
114
110
  return { success: true };
115
111
  }
116
112
  if (operation.status === 'FAILED') {
117
113
  if (logStreamCleanup)
118
114
  logStreamCleanup();
119
- return { success: false, error: 'Destruction failed' };
115
+ return { success: false, error: 'Stack destruction failed' };
120
116
  }
121
117
  await setTimeout(1500);
122
118
  }