@hubspot/cli 7.10.0-beta.0 → 7.10.0-beta.2

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 (59) hide show
  1. package/commands/account/__tests__/rename.test.js +35 -0
  2. package/commands/account/rename.d.ts +1 -1
  3. package/commands/account/rename.js +5 -2
  4. package/commands/config/set.js +1 -2
  5. package/commands/getStarted.js +8 -2
  6. package/commands/hubdb.d.ts +1 -1
  7. package/commands/project/dev/index.js +8 -1
  8. package/commands/project/listBuilds.js +7 -1
  9. package/commands/project/upload.js +7 -1
  10. package/commands/project/validate.js +7 -1
  11. package/commands/project/watch.js +7 -2
  12. package/commands/testAccount/__tests__/create.test.js +68 -0
  13. package/commands/testAccount/create.d.ts +8 -0
  14. package/commands/testAccount/create.js +133 -43
  15. package/commands/testAccount/importData.d.ts +1 -1
  16. package/lang/en.d.ts +3199 -3204
  17. package/lang/en.js +24 -3
  18. package/lib/constants.d.ts +1 -0
  19. package/lib/constants.js +6 -0
  20. package/lib/mcp/__tests__/setup.test.d.ts +1 -0
  21. package/lib/mcp/__tests__/setup.test.js +127 -0
  22. package/lib/mcp/setup.d.ts +4 -12
  23. package/lib/mcp/setup.js +34 -1
  24. package/lib/middleware/autoUpdateMiddleware.d.ts +3 -1
  25. package/lib/middleware/autoUpdateMiddleware.js +1 -0
  26. package/lib/projects/__tests__/components.test.js +148 -24
  27. package/lib/projects/__tests__/projects.test.js +13 -42
  28. package/lib/projects/components.js +76 -20
  29. package/lib/projects/config.js +5 -9
  30. package/lib/prompts/__tests__/createDeveloperTestAccountConfigPrompt.test.d.ts +1 -0
  31. package/lib/prompts/__tests__/createDeveloperTestAccountConfigPrompt.test.js +153 -0
  32. package/lib/prompts/createDeveloperTestAccountConfigPrompt.d.ts +5 -0
  33. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +76 -66
  34. package/mcp-server/tools/cms/HsCreateFunctionTool.js +6 -0
  35. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +4 -4
  36. package/mcp-server/tools/cms/HsCreateModuleTool.js +6 -0
  37. package/mcp-server/tools/cms/HsCreateTemplateTool.js +6 -0
  38. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +4 -4
  39. package/mcp-server/tools/cms/HsFunctionLogsTool.js +4 -0
  40. package/mcp-server/tools/cms/HsListFunctionsTool.js +4 -0
  41. package/mcp-server/tools/cms/HsListTool.js +4 -0
  42. package/mcp-server/tools/index.js +2 -0
  43. package/mcp-server/tools/project/AddFeatureToProjectTool.js +6 -0
  44. package/mcp-server/tools/project/CreateProjectTool.js +6 -0
  45. package/mcp-server/tools/project/CreateTestAccountTool.d.ts +41 -0
  46. package/mcp-server/tools/project/CreateTestAccountTool.js +150 -0
  47. package/mcp-server/tools/project/DeployProjectTool.js +6 -0
  48. package/mcp-server/tools/project/DocFetchTool.js +4 -0
  49. package/mcp-server/tools/project/DocsSearchTool.js +4 -0
  50. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +4 -0
  51. package/mcp-server/tools/project/GetApplicationInfoTool.js +4 -0
  52. package/mcp-server/tools/project/GetConfigValuesTool.js +4 -0
  53. package/mcp-server/tools/project/GuidedWalkthroughTool.js +4 -0
  54. package/mcp-server/tools/project/UploadProjectTools.js +6 -0
  55. package/mcp-server/tools/project/ValidateProjectTool.js +4 -0
  56. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.d.ts +1 -0
  57. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +444 -0
  58. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +2 -2
  59. package/package.json +1 -1
@@ -1,13 +1,20 @@
1
1
  import yargs from 'yargs';
2
2
  import { addConfigOptions, addAccountOptions, } from '../../../lib/commonOpts.js';
3
3
  import accountRenameCommand from '../rename.js';
4
+ import * as config from '@hubspot/local-dev-lib/config';
5
+ import { logError } from '../../../lib/errorHandlers/index.js';
6
+ import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
4
7
  vi.mock('../../../lib/commonOpts');
8
+ vi.mock('@hubspot/local-dev-lib/config');
9
+ vi.mock('../../../lib/errorHandlers/index.js');
5
10
  const positionalSpy = vi
6
11
  .spyOn(yargs, 'positional')
7
12
  .mockReturnValue(yargs);
8
13
  const exampleSpy = vi
9
14
  .spyOn(yargs, 'example')
10
15
  .mockReturnValue(yargs);
16
+ const renameAccountSpy = vi.spyOn(config, 'renameAccount');
17
+ const processExitSpy = vi.spyOn(process, 'exit');
11
18
  describe('commands/account/rename', () => {
12
19
  const yargsMock = yargs;
13
20
  describe('command', () => {
@@ -20,6 +27,34 @@ describe('commands/account/rename', () => {
20
27
  expect(accountRenameCommand.describe).toBeDefined();
21
28
  });
22
29
  });
30
+ describe('handler', () => {
31
+ let args;
32
+ beforeEach(() => {
33
+ vi.clearAllMocks();
34
+ renameAccountSpy.mockResolvedValue(undefined);
35
+ processExitSpy.mockImplementation(() => {
36
+ throw new Error('process.exit called');
37
+ });
38
+ args = {
39
+ accountName: 'myExistingAccountName',
40
+ newName: 'myNewAccountName',
41
+ };
42
+ });
43
+ it('should rename the account', async () => {
44
+ await expect(accountRenameCommand.handler(args)).rejects.toThrow('process.exit called');
45
+ expect(renameAccountSpy).toHaveBeenCalledWith('myExistingAccountName', 'my-new-account-name');
46
+ expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.SUCCESS);
47
+ });
48
+ it('should handle errors when renameAccount throws', async () => {
49
+ const error = new Error('Failed to rename account');
50
+ renameAccountSpy.mockRejectedValue(error);
51
+ await expect(accountRenameCommand.handler(args)).rejects.toThrow('process.exit called');
52
+ expect(renameAccountSpy).toHaveBeenCalledWith('myExistingAccountName', 'my-new-account-name');
53
+ expect(logError).toHaveBeenCalledTimes(1);
54
+ expect(logError).toHaveBeenCalledWith(error);
55
+ expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
56
+ });
57
+ });
23
58
  describe('builder', () => {
24
59
  it('should support the correct options', () => {
25
60
  accountRenameCommand.builder(yargsMock);
@@ -1,5 +1,5 @@
1
1
  import { CommonArgs, ConfigArgs, YargsCommandModule } from '../../types/Yargs.js';
2
- type AccountRenameArgs = CommonArgs & ConfigArgs & {
2
+ export type AccountRenameArgs = CommonArgs & ConfigArgs & {
3
3
  accountName: string;
4
4
  newName: string;
5
5
  };
@@ -5,19 +5,22 @@ import { uiLogger } from '../../lib/ui/logger.js';
5
5
  import { logError } from '../../lib/errorHandlers/index.js';
6
6
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
7
7
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
8
+ import { toKebabCase } from '@hubspot/local-dev-lib/text';
8
9
  const command = 'rename <account-name> <new-name>';
9
10
  const describe = commands.account.subcommands.rename.describe;
10
11
  async function handler(args) {
11
12
  const { accountName, newName, derivedAccountId } = args;
12
13
  trackCommandUsage('accounts-rename', undefined, derivedAccountId);
14
+ const newNameKebabCase = toKebabCase(newName);
15
+ const nameWasSanitized = newNameKebabCase !== newName;
13
16
  try {
14
- await renameAccount(accountName, newName);
17
+ await renameAccount(accountName, newNameKebabCase);
15
18
  }
16
19
  catch (error) {
17
20
  logError(error);
18
21
  process.exit(EXIT_CODES.ERROR);
19
22
  }
20
- uiLogger.log(commands.account.subcommands.rename.success.renamed(accountName, newName));
23
+ uiLogger.success(commands.account.subcommands.rename.success.renamed(accountName, newNameKebabCase, nameWasSanitized));
21
24
  process.exit(EXIT_CODES.SUCCESS);
22
25
  }
23
26
  function accountRenameBuilder(yargs) {
@@ -22,7 +22,7 @@ async function selectOptions() {
22
22
  },
23
23
  { name: 'Allow usage tracking', value: { allowUsageTracking: '' } },
24
24
  { name: 'HTTP timeout', value: { httpTimeout: '' } },
25
- // TODO enable when we unhide this option { name: 'Allow auto updates', value: { allowAutoUpdates: '' } },
25
+ { name: 'Allow auto updates', value: { allowAutoUpdates: '' } },
26
26
  ],
27
27
  },
28
28
  ]);
@@ -94,7 +94,6 @@ function configSetBuilder(yargs) {
94
94
  'allow-auto-updates': {
95
95
  describe: commands.config.subcommands.set.options.allowAutoUpdates.describe,
96
96
  type: 'boolean',
97
- hidden: true,
98
97
  },
99
98
  'auto-open-browser': {
100
99
  describe: commands.config.subcommands.set.options.autoOpenBrowser.describe,
@@ -11,7 +11,7 @@ import { promptUser } from '../lib/prompts/promptUtils.js';
11
11
  import { projectNameAndDestPrompt } from '../lib/prompts/projectNameAndDestPrompt.js';
12
12
  import { uiAccountDescription, uiFeatureHighlight, uiInfoSection, } from '../lib/ui/index.js';
13
13
  import { uiLogger } from '../lib/ui/logger.js';
14
- import { debugError } from '../lib/errorHandlers/index.js';
14
+ import { debugError, logError } from '../lib/errorHandlers/index.js';
15
15
  import { handleProjectUpload } from '../lib/projects/upload.js';
16
16
  import { PROJECT_CONFIG_FILE, GET_STARTED_OPTIONS, HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, } from '../lib/constants.js';
17
17
  import { writeProjectConfig, getProjectConfig, validateProjectConfig, } from '../lib/projects/config.js';
@@ -162,7 +162,13 @@ async function handler(args) {
162
162
  uiLogger.error(commands.getStarted.errors.configFileNotFound);
163
163
  process.exit(EXIT_CODES.ERROR);
164
164
  }
165
- validateProjectConfig(newProjectConfig, newProjectDir);
165
+ try {
166
+ validateProjectConfig(newProjectConfig, newProjectDir);
167
+ }
168
+ catch (error) {
169
+ logError(error);
170
+ process.exit(EXIT_CODES.ERROR);
171
+ }
166
172
  uiLogger.log(' ');
167
173
  uiLogger.log(commands.getStarted.logs.uploadingProject);
168
174
  uiLogger.log(' ');
@@ -1,5 +1,5 @@
1
1
  import { YargsCommandModuleBucket } from '../types/Yargs.js';
2
2
  export declare const command = "hubdb";
3
- export declare const describe: "Commands for managing HubDB tables.";
3
+ export declare const describe: string;
4
4
  declare const hubdbCommand: YargsCommandModuleBucket;
5
5
  export default hubdbCommand;
@@ -10,6 +10,7 @@ import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
10
10
  import { loadProfile, exitIfUsingProfiles, } from '../../../lib/projectProfiles.js';
11
11
  import { commands } from '../../../lang/en.js';
12
12
  import { uiLogger } from '../../../lib/ui/logger.js';
13
+ import { logError } from '../../../lib/errorHandlers/index.js';
13
14
  const command = 'dev';
14
15
  const describe = commands.project.dev.describe;
15
16
  function validateAccountFlags(testingAccount, projectAccount, userProvidedAccount, useV2) {
@@ -26,7 +27,13 @@ function validateAccountFlags(testingAccount, projectAccount, userProvidedAccoun
26
27
  async function handler(args) {
27
28
  const { derivedAccountId, userProvidedAccount, testingAccount, projectAccount, } = args;
28
29
  const { projectConfig, projectDir } = await getProjectConfig();
29
- validateProjectConfig(projectConfig, projectDir);
30
+ try {
31
+ validateProjectConfig(projectConfig, projectDir);
32
+ }
33
+ catch (error) {
34
+ logError(error);
35
+ process.exit(EXIT_CODES.ERROR);
36
+ }
30
37
  const useV2Projects = isV2Project(projectConfig.platformVersion);
31
38
  if (!projectDir) {
32
39
  uiLogger.error(commands.project.dev.errors.noProjectConfig);
@@ -68,7 +68,13 @@ async function handler(args) {
68
68
  let projectName = projectFlagValue;
69
69
  if (!projectName) {
70
70
  const { projectConfig, projectDir } = await getProjectConfig();
71
- validateProjectConfig(projectConfig, projectDir);
71
+ try {
72
+ validateProjectConfig(projectConfig, projectDir);
73
+ }
74
+ catch (error) {
75
+ logError(error);
76
+ process.exit(EXIT_CODES.ERROR);
77
+ }
72
78
  projectName = projectConfig.name;
73
79
  }
74
80
  try {
@@ -20,7 +20,13 @@ async function handler(args) {
20
20
  const { forceCreate, message, derivedAccountId, skipValidation, formatOutputAsJson, profile, } = args;
21
21
  const jsonOutput = {};
22
22
  const { projectConfig, projectDir } = await getProjectConfig();
23
- validateProjectConfig(projectConfig, projectDir);
23
+ try {
24
+ validateProjectConfig(projectConfig, projectDir);
25
+ }
26
+ catch (error) {
27
+ logError(error);
28
+ process.exit(EXIT_CODES.ERROR);
29
+ }
24
30
  let targetAccountId;
25
31
  if (isV2Project(projectConfig.platformVersion)) {
26
32
  targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
@@ -23,7 +23,13 @@ async function handler(args) {
23
23
  uiLogger.error(commands.project.validate.badVersion);
24
24
  process.exit(EXIT_CODES.ERROR);
25
25
  }
26
- validateProjectConfig(projectConfig, projectDir);
26
+ try {
27
+ validateProjectConfig(projectConfig, projectDir);
28
+ }
29
+ catch (error) {
30
+ logError(error);
31
+ process.exit(EXIT_CODES.ERROR);
32
+ }
27
33
  let targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
28
34
  targetAccountId = targetAccountId || derivedAccountId;
29
35
  const accountConfig = getAccountConfig(targetAccountId);
@@ -58,7 +58,13 @@ async function handler(args) {
58
58
  const { initialUpload, derivedAccountId } = args;
59
59
  trackCommandUsage('project-watch', undefined, derivedAccountId);
60
60
  const { projectConfig, projectDir } = await getProjectConfig();
61
- validateProjectConfig(projectConfig, projectDir);
61
+ try {
62
+ validateProjectConfig(projectConfig, projectDir);
63
+ }
64
+ catch (error) {
65
+ logError(error);
66
+ process.exit(EXIT_CODES.ERROR);
67
+ }
62
68
  if (!projectConfig || !projectDir) {
63
69
  uiLogger.error(commands.project.watch.errors.projectConfigNotFound);
64
70
  return process.exit(EXIT_CODES.ERROR);
@@ -67,7 +73,6 @@ async function handler(args) {
67
73
  uiLogger.error(projectConfig.platformVersion);
68
74
  return process.exit(EXIT_CODES.ERROR);
69
75
  }
70
- validateProjectConfig(projectConfig, projectDir);
71
76
  try {
72
77
  const { data: { results: builds }, } = await fetchProjectBuilds(derivedAccountId, projectConfig.name);
73
78
  const hasNoBuilds = !builds || !builds.length;
@@ -1,6 +1,7 @@
1
1
  import yargs from 'yargs';
2
2
  import { addAccountOptions, addConfigOptions, addUseEnvironmentOptions, addTestingOptions, addJSONOutputOptions, } from '../../../lib/commonOpts.js';
3
3
  import testAccountCreateCommand from '../create.js';
4
+ import { ACCOUNT_LEVEL_CHOICES } from '../../../lib/constants.js';
4
5
  vi.mock('../../../lib/commonOpts');
5
6
  describe('commands/testAccount/create', () => {
6
7
  const yargsMock = yargs;
@@ -29,5 +30,72 @@ describe('commands/testAccount/create', () => {
29
30
  expect(addJSONOutputOptions).toHaveBeenCalledTimes(1);
30
31
  expect(addJSONOutputOptions).toHaveBeenCalledWith(yargsMock);
31
32
  });
33
+ it('should add account-name option', () => {
34
+ testAccountCreateCommand.builder(yargsMock);
35
+ expect(yargsMock.option).toHaveBeenCalledWith('name', {
36
+ type: 'string',
37
+ description: 'Name for the test account',
38
+ });
39
+ });
40
+ it('should add description option', () => {
41
+ testAccountCreateCommand.builder(yargsMock);
42
+ expect(yargsMock.option).toHaveBeenCalledWith('description', {
43
+ type: 'string',
44
+ description: 'Description for the test account',
45
+ });
46
+ });
47
+ it('should add hub level options', () => {
48
+ testAccountCreateCommand.builder(yargsMock);
49
+ expect(yargsMock.option).toHaveBeenCalledWith('marketing-level', {
50
+ type: 'string',
51
+ description: 'Marketing Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
52
+ choices: ACCOUNT_LEVEL_CHOICES,
53
+ });
54
+ expect(yargsMock.option).toHaveBeenCalledWith('ops-level', {
55
+ type: 'string',
56
+ description: 'Operations Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
57
+ choices: ACCOUNT_LEVEL_CHOICES,
58
+ });
59
+ expect(yargsMock.option).toHaveBeenCalledWith('service-level', {
60
+ type: 'string',
61
+ description: 'Service Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
62
+ choices: ACCOUNT_LEVEL_CHOICES,
63
+ });
64
+ expect(yargsMock.option).toHaveBeenCalledWith('sales-level', {
65
+ type: 'string',
66
+ description: 'Sales Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
67
+ choices: ACCOUNT_LEVEL_CHOICES,
68
+ });
69
+ expect(yargsMock.option).toHaveBeenCalledWith('content-level', {
70
+ type: 'string',
71
+ description: 'CMS Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
72
+ choices: ACCOUNT_LEVEL_CHOICES,
73
+ });
74
+ });
75
+ it('should add examples for all usage scenarios', () => {
76
+ testAccountCreateCommand.builder(yargsMock);
77
+ expect(yargsMock.example).toHaveBeenCalledWith([
78
+ [
79
+ '$0 test-account create',
80
+ 'Interactive mode - prompts for all options',
81
+ ],
82
+ [
83
+ '$0 test-account create --name "MyTestAccount"',
84
+ 'Provide name via flag, prompt for description and tier selection',
85
+ ],
86
+ [
87
+ '$0 test-account create --name "MyTestAccount" --description "Test account"',
88
+ 'Provide name and description, prompt for tier selection',
89
+ ],
90
+ [
91
+ '$0 test-account create --name "MyTestAccount" --marketing-level PROFESSIONAL',
92
+ 'Specify marketing tier, other tiers default to ENTERPRISE',
93
+ ],
94
+ [
95
+ '$0 test-account create --config-path ./test-account-config.json',
96
+ 'Create from config file (mutually exclusive with other flags)',
97
+ ],
98
+ ]);
99
+ });
32
100
  });
33
101
  });
@@ -1,6 +1,14 @@
1
+ import { AccountLevel } from '@hubspot/local-dev-lib/types/developerTestAccounts.js';
1
2
  import { CommonArgs, ConfigArgs, AccountArgs, TestingArgs, YargsCommandModule, EnvironmentArgs, JSONOutputArgs } from '../../types/Yargs.js';
2
3
  type CreateTestAccountArgs = CommonArgs & AccountArgs & ConfigArgs & TestingArgs & EnvironmentArgs & JSONOutputArgs & {
3
4
  configPath?: string;
5
+ name?: string;
6
+ description?: string;
7
+ marketingLevel?: AccountLevel;
8
+ opsLevel?: AccountLevel;
9
+ serviceLevel?: AccountLevel;
10
+ salesLevel?: AccountLevel;
11
+ contentLevel?: AccountLevel;
4
12
  };
5
13
  declare const createTestAccountCommand: YargsCommandModule<unknown, CreateTestAccountArgs>;
6
14
  export default createTestAccountCommand;
@@ -14,53 +14,83 @@ import { createDeveloperTestAccountConfigPrompt } from '../../lib/prompts/create
14
14
  import { debugError, logError } from '../../lib/errorHandlers/index.js';
15
15
  import SpinniesManager from '../../lib/ui/SpinniesManager.js';
16
16
  import { createDeveloperTestAccountV2, saveAccountToConfig, } from '../../lib/buildAccount.js';
17
+ import { ACCOUNT_LEVEL_CHOICES } from '../../lib/constants.js';
17
18
  const command = 'create';
18
19
  const describe = commands.testAccount.create.describe;
19
- async function handler(args) {
20
- const { derivedAccountId, configPath, formatOutputAsJson } = args;
21
- trackCommandUsage('test-account-create', {}, derivedAccountId);
22
- const env = getValidEnv(getEnv(derivedAccountId));
23
- let accountConfigPath = configPath;
24
- let testAccountConfig;
25
- if (!accountConfigPath) {
26
- const createTestAccountFromConfig = await listPrompt(commands.testAccount.create.createTestAccountFromConfigPrompt, {
27
- choices: [
28
- {
29
- name: commands.testAccount.create.createFromConfigOption,
30
- value: true,
31
- },
32
- {
33
- name: commands.testAccount.create.createFromScratchOption,
34
- value: false,
35
- },
36
- ],
20
+ function hasAnyFlags(args) {
21
+ const { name, description, marketingLevel, opsLevel, serviceLevel, salesLevel, contentLevel, } = args;
22
+ return !!(name ||
23
+ description ||
24
+ marketingLevel ||
25
+ opsLevel ||
26
+ serviceLevel ||
27
+ salesLevel ||
28
+ contentLevel);
29
+ }
30
+ async function readConfigFile(configPath) {
31
+ const absoluteConfigPath = path.resolve(getCwd(), configPath);
32
+ if (!fileExists(absoluteConfigPath)) {
33
+ uiLogger.error(commands.testAccount.create.errors.configFileNotFound(absoluteConfigPath));
34
+ process.exit(EXIT_CODES.ERROR);
35
+ }
36
+ try {
37
+ return JSON.parse(fs.readFileSync(absoluteConfigPath, 'utf8'));
38
+ }
39
+ catch (err) {
40
+ uiLogger.error(commands.testAccount.create.errors.configFileParseFailed(absoluteConfigPath));
41
+ process.exit(EXIT_CODES.ERROR);
42
+ }
43
+ }
44
+ async function promptForConfigPath() {
45
+ const createTestAccountFromConfig = await listPrompt(commands.testAccount.create.createTestAccountFromConfigPrompt, {
46
+ choices: [
47
+ {
48
+ name: commands.testAccount.create.createFromConfigOption,
49
+ value: true,
50
+ },
51
+ {
52
+ name: commands.testAccount.create.createFromScratchOption,
53
+ value: false,
54
+ },
55
+ ],
56
+ });
57
+ if (createTestAccountFromConfig) {
58
+ const configPathPromptResult = await promptUser({
59
+ name: 'configPath',
60
+ message: commands.testAccount.create.configPathPrompt,
61
+ type: 'input',
37
62
  });
38
- if (createTestAccountFromConfig) {
39
- const configPathPromptResult = await promptUser({
40
- name: 'configPath',
41
- message: commands.testAccount.create.configPathPrompt,
42
- type: 'input',
43
- });
44
- accountConfigPath = configPathPromptResult.configPath;
45
- }
63
+ return configPathPromptResult.configPath;
46
64
  }
47
- if (accountConfigPath) {
48
- const absoluteConfigPath = path.resolve(getCwd(), accountConfigPath);
49
- if (!fileExists(absoluteConfigPath)) {
50
- uiLogger.error(commands.testAccount.create.errors.configFileNotFound(absoluteConfigPath));
51
- process.exit(EXIT_CODES.ERROR);
52
- }
53
- try {
54
- testAccountConfig = JSON.parse(fs.readFileSync(absoluteConfigPath, 'utf8'));
55
- }
56
- catch (err) {
57
- uiLogger.error(commands.testAccount.create.errors.configFileParseFailed(absoluteConfigPath));
58
- process.exit(EXIT_CODES.ERROR);
59
- }
65
+ return undefined;
66
+ }
67
+ async function buildTestAccountConfig(args) {
68
+ const { configPath, name, description, marketingLevel, opsLevel, serviceLevel, salesLevel, contentLevel, } = args;
69
+ if (configPath) {
70
+ return readConfigFile(configPath);
60
71
  }
61
- else {
62
- testAccountConfig = await createDeveloperTestAccountConfigPrompt();
72
+ let accountConfigPath;
73
+ if (!hasAnyFlags(args)) {
74
+ accountConfigPath = await promptForConfigPath();
75
+ }
76
+ if (accountConfigPath) {
77
+ return readConfigFile(accountConfigPath);
63
78
  }
79
+ return createDeveloperTestAccountConfigPrompt({
80
+ name,
81
+ description,
82
+ marketingLevel,
83
+ opsLevel,
84
+ serviceLevel,
85
+ salesLevel,
86
+ contentLevel,
87
+ });
88
+ }
89
+ async function handler(args) {
90
+ const { derivedAccountId, formatOutputAsJson } = args;
91
+ trackCommandUsage('test-account-create', {}, derivedAccountId);
92
+ const env = getValidEnv(getEnv(derivedAccountId));
93
+ const testAccountConfig = await buildTestAccountConfig(args);
64
94
  const resultJson = {};
65
95
  SpinniesManager.init({
66
96
  succeedColor: 'white',
@@ -90,7 +120,12 @@ async function handler(args) {
90
120
  else {
91
121
  // Only save to config if not using json output
92
122
  try {
93
- await saveAccountToConfig(resultJson.accountId, testAccountConfig.accountName, env, resultJson.personalAccessKey);
123
+ const savedAccountName = await saveAccountToConfig(resultJson.accountId, testAccountConfig.accountName, env, resultJson.personalAccessKey);
124
+ // Inform user if the account name was normalized
125
+ if (savedAccountName !== testAccountConfig.accountName) {
126
+ uiLogger.log('');
127
+ uiLogger.info(commands.testAccount.create.savedAccountNameDiffers(testAccountConfig.accountName, savedAccountName));
128
+ }
94
129
  }
95
130
  catch (e) {
96
131
  debugError(e);
@@ -105,10 +140,65 @@ function createTestAccountBuilder(yargs) {
105
140
  type: 'string',
106
141
  description: commands.testAccount.create.options.configPath,
107
142
  });
143
+ yargs.option('name', {
144
+ type: 'string',
145
+ description: commands.testAccount.create.options.accountName,
146
+ });
147
+ yargs.option('description', {
148
+ type: 'string',
149
+ description: commands.testAccount.create.options.description,
150
+ });
151
+ yargs.option('marketing-level', {
152
+ type: 'string',
153
+ description: commands.testAccount.create.options.marketingLevel,
154
+ choices: ACCOUNT_LEVEL_CHOICES,
155
+ });
156
+ yargs.option('ops-level', {
157
+ type: 'string',
158
+ description: commands.testAccount.create.options.opsLevel,
159
+ choices: ACCOUNT_LEVEL_CHOICES,
160
+ });
161
+ yargs.option('service-level', {
162
+ type: 'string',
163
+ description: commands.testAccount.create.options.serviceLevel,
164
+ choices: ACCOUNT_LEVEL_CHOICES,
165
+ });
166
+ yargs.option('sales-level', {
167
+ type: 'string',
168
+ description: commands.testAccount.create.options.salesLevel,
169
+ choices: ACCOUNT_LEVEL_CHOICES,
170
+ });
171
+ yargs.option('content-level', {
172
+ type: 'string',
173
+ description: commands.testAccount.create.options.contentLevel,
174
+ choices: ACCOUNT_LEVEL_CHOICES,
175
+ });
176
+ yargs.conflicts('config-path', [
177
+ 'name',
178
+ 'description',
179
+ 'marketing-level',
180
+ 'ops-level',
181
+ 'service-level',
182
+ 'sales-level',
183
+ 'content-level',
184
+ ]);
108
185
  yargs.example([
186
+ ['$0 test-account create', 'Interactive mode - prompts for all options'],
187
+ [
188
+ '$0 test-account create --name "MyTestAccount"',
189
+ 'Provide name via flag, prompt for description and tier selection',
190
+ ],
191
+ [
192
+ '$0 test-account create --name "MyTestAccount" --description "Test account"',
193
+ 'Provide name and description, prompt for tier selection',
194
+ ],
195
+ [
196
+ '$0 test-account create --name "MyTestAccount" --marketing-level PROFESSIONAL',
197
+ 'Specify marketing tier, other tiers default to ENTERPRISE',
198
+ ],
109
199
  [
110
200
  '$0 test-account create --config-path ./test-account-config.json',
111
- commands.testAccount.create.example('./test-account-config.json'),
201
+ 'Create from config file (mutually exclusive with other flags)',
112
202
  ],
113
203
  ]);
114
204
  return yargs;
@@ -1,6 +1,6 @@
1
1
  import { AccountArgs, CommonArgs, ConfigArgs, EnvironmentArgs, YargsCommandModule } from '../../types/Yargs.js';
2
2
  export declare const command = "import-data";
3
- export declare const describe: "Import data into the CRM";
3
+ export declare const describe: string;
4
4
  type CrmImportDataArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & {
5
5
  filePath: string | undefined;
6
6
  skipConfirm: boolean | undefined;