@hubspot/cli 7.9.0 → 7.9.1-experimental.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 (214) hide show
  1. package/bin/cli.js +5 -4
  2. package/commands/__tests__/getStarted.test.js +10 -0
  3. package/commands/__tests__/project.test.js +2 -0
  4. package/commands/account/__tests__/rename.test.js +42 -0
  5. package/commands/account/auth.js +10 -14
  6. package/commands/account/clean.js +11 -19
  7. package/commands/account/createOverride.js +17 -23
  8. package/commands/account/info.js +8 -5
  9. package/commands/account/list.js +13 -18
  10. package/commands/account/remove.js +23 -22
  11. package/commands/account/removeOverride.js +8 -16
  12. package/commands/account/rename.d.ts +1 -1
  13. package/commands/account/rename.js +6 -3
  14. package/commands/account/use.js +19 -8
  15. package/commands/app/__tests__/migrate.test.js +8 -4
  16. package/commands/app/migrate.js +2 -2
  17. package/commands/auth.js +18 -14
  18. package/commands/cms/theme/preview.js +1 -4
  19. package/commands/config/migrate.js +5 -5
  20. package/commands/config/set.js +1 -2
  21. package/commands/customObject/createSchema.js +2 -3
  22. package/commands/customObject/updateSchema.js +2 -3
  23. package/commands/getStarted.js +15 -22
  24. package/commands/hubdb/__tests__/list.test.js +1 -0
  25. package/commands/hubdb/list.js +2 -2
  26. package/commands/hubdb.d.ts +1 -1
  27. package/commands/init.js +36 -32
  28. package/commands/project/__tests__/deploy.test.js +10 -5
  29. package/commands/project/__tests__/devUnifiedFlow.test.js +6 -4
  30. package/commands/project/__tests__/logs.test.js +4 -0
  31. package/commands/project/__tests__/updateDeps.test.js +142 -0
  32. package/commands/project/__tests__/validate.test.js +2 -2
  33. package/commands/project/cloneApp.js +2 -2
  34. package/commands/project/create.js +0 -1
  35. package/commands/project/deploy.js +2 -2
  36. package/commands/project/dev/deprecatedFlow.js +4 -5
  37. package/commands/project/dev/index.js +14 -4
  38. package/commands/project/dev/unifiedFlow.js +4 -5
  39. package/commands/project/listBuilds.js +7 -1
  40. package/commands/project/logs.js +2 -3
  41. package/commands/project/profile/add.js +6 -7
  42. package/commands/project/profile/delete.js +2 -2
  43. package/commands/project/updateDeps.d.ts +6 -0
  44. package/commands/project/updateDeps.js +80 -0
  45. package/commands/project/upload.js +9 -3
  46. package/commands/project/validate.js +9 -3
  47. package/commands/project/watch.js +7 -2
  48. package/commands/project.js +2 -0
  49. package/commands/sandbox/__tests__/create.test.js +14 -5
  50. package/commands/sandbox/create.js +4 -5
  51. package/commands/sandbox/delete.js +23 -20
  52. package/commands/testAccount/__tests__/create.test.js +68 -0
  53. package/commands/testAccount/create.d.ts +8 -0
  54. package/commands/testAccount/create.js +135 -45
  55. package/commands/testAccount/delete.js +9 -8
  56. package/commands/testAccount/importData.d.ts +1 -1
  57. package/lang/en.d.ts +3199 -3185
  58. package/lang/en.js +52 -14
  59. package/lib/__tests__/buildAccount.test.js +22 -30
  60. package/lib/__tests__/commonOpts.test.js +9 -13
  61. package/lib/__tests__/dependencyManagement.test.js +273 -1
  62. package/lib/__tests__/developerTestAccounts.test.js +29 -17
  63. package/lib/__tests__/importData.test.js +20 -10
  64. package/lib/__tests__/oauth.test.js +19 -8
  65. package/lib/__tests__/sandboxSync.test.js +33 -11
  66. package/lib/__tests__/sandboxes.test.js +30 -19
  67. package/lib/__tests__/usageTracking.test.js +10 -10
  68. package/lib/__tests__/validation.test.js +32 -32
  69. package/lib/accountTypes.d.ts +9 -9
  70. package/lib/accountTypes.js +2 -4
  71. package/lib/app/__tests__/migrate.test.js +15 -0
  72. package/lib/app/__tests__/migrate_legacy.test.js +9 -0
  73. package/lib/app/migrate_legacy.d.ts +2 -2
  74. package/lib/buildAccount.d.ts +4 -4
  75. package/lib/buildAccount.js +7 -14
  76. package/lib/commonOpts.js +5 -8
  77. package/lib/configMigrate.d.ts +2 -2
  78. package/lib/configMigrate.js +42 -18
  79. package/lib/configOptions.js +3 -2
  80. package/lib/constants.d.ts +1 -0
  81. package/lib/constants.js +6 -0
  82. package/lib/dependencyManagement.d.ts +8 -2
  83. package/lib/dependencyManagement.js +75 -12
  84. package/lib/developerTestAccounts.d.ts +3 -3
  85. package/lib/developerTestAccounts.js +4 -7
  86. package/lib/doctor/DiagnosticInfoBuilder.d.ts +1 -1
  87. package/lib/doctor/DiagnosticInfoBuilder.js +9 -6
  88. package/lib/doctor/Doctor.js +4 -3
  89. package/lib/doctor/__tests__/Diagnosis.test.js +4 -3
  90. package/lib/doctor/__tests__/DiagnosticInfoBuilder.test.js +17 -9
  91. package/lib/doctor/__tests__/Doctor.test.js +14 -0
  92. package/lib/importData.js +8 -7
  93. package/lib/links.js +5 -5
  94. package/lib/mcp/__tests__/setup.test.d.ts +1 -0
  95. package/lib/mcp/__tests__/setup.test.js +127 -0
  96. package/lib/mcp/setup.d.ts +4 -12
  97. package/lib/mcp/setup.js +34 -1
  98. package/lib/middleware/__test__/commandTargetingUtils.test.js +3 -3
  99. package/lib/middleware/__test__/configMiddleware.test.js +23 -22
  100. package/lib/middleware/__test__/gitMiddleware.test.js +9 -7
  101. package/lib/middleware/autoUpdateMiddleware.d.ts +3 -1
  102. package/lib/middleware/autoUpdateMiddleware.js +10 -2
  103. package/lib/middleware/commandTargetingUtils.js +2 -2
  104. package/lib/middleware/configMiddleware.d.ts +6 -1
  105. package/lib/middleware/configMiddleware.js +36 -15
  106. package/lib/middleware/gitMiddleware.js +8 -4
  107. package/lib/npm.d.ts +3 -0
  108. package/lib/npm.js +6 -0
  109. package/lib/oauth.d.ts +2 -2
  110. package/lib/oauth.js +8 -10
  111. package/lib/projects/__tests__/AppDevModeInterface.test.js +17 -6
  112. package/lib/projects/__tests__/DevServerManager.test.js +1 -0
  113. package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
  114. package/lib/projects/__tests__/components.test.js +148 -24
  115. package/lib/projects/__tests__/deploy.test.js +1 -0
  116. package/lib/projects/__tests__/platformVersion.test.js +5 -1
  117. package/lib/projects/__tests__/projects.test.js +13 -42
  118. package/lib/projects/components.js +76 -20
  119. package/lib/projects/config.js +5 -9
  120. package/lib/projects/create/__tests__/v2.test.js +11 -0
  121. package/lib/projects/localDev/AppDevModeInterface.js +2 -2
  122. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
  123. package/lib/projects/localDev/LocalDevLogger.js +4 -4
  124. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -3
  125. package/lib/projects/localDev/helpers/account.d.ts +10 -10
  126. package/lib/projects/localDev/helpers/account.js +6 -11
  127. package/lib/projects/platformVersion.js +1 -1
  128. package/lib/projects/urls.js +5 -6
  129. package/lib/prompts/__tests__/createDeveloperTestAccountConfigPrompt.test.d.ts +1 -0
  130. package/lib/prompts/__tests__/createDeveloperTestAccountConfigPrompt.test.js +153 -0
  131. package/lib/prompts/__tests__/downloadProjectPrompt.test.js +7 -5
  132. package/lib/prompts/accountNamePrompt.js +3 -3
  133. package/lib/prompts/accountsPrompt.d.ts +1 -1
  134. package/lib/prompts/accountsPrompt.js +6 -7
  135. package/lib/prompts/confirmImportDataPrompt.js +2 -2
  136. package/lib/prompts/createDeveloperTestAccountConfigPrompt.d.ts +5 -0
  137. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +76 -66
  138. package/lib/prompts/downloadProjectPrompt.d.ts +1 -0
  139. package/lib/prompts/downloadProjectPrompt.js +5 -2
  140. package/lib/prompts/importDataTestAccountSelectPrompt.js +4 -5
  141. package/lib/prompts/personalAccessKeyPrompt.js +2 -2
  142. package/lib/prompts/projectDevTargetAccountPrompt.d.ts +3 -3
  143. package/lib/prompts/projectDevTargetAccountPrompt.js +5 -7
  144. package/lib/prompts/sandboxesPrompt.js +7 -8
  145. package/lib/prompts/setAsDefaultAccountPrompt.js +7 -6
  146. package/lib/sandboxSync.d.ts +2 -2
  147. package/lib/sandboxSync.js +3 -9
  148. package/lib/sandboxes.d.ts +4 -4
  149. package/lib/sandboxes.js +6 -11
  150. package/lib/serverlessLogs.js +2 -2
  151. package/lib/theme/__tests__/migrate.test.js +15 -0
  152. package/lib/ui/index.js +6 -3
  153. package/lib/usageTracking.js +15 -8
  154. package/lib/validation.js +13 -11
  155. package/mcp-server/tools/cms/HsCreateFunctionTool.js +8 -2
  156. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +4 -4
  157. package/mcp-server/tools/cms/HsCreateModuleTool.js +8 -2
  158. package/mcp-server/tools/cms/HsCreateTemplateTool.js +8 -2
  159. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +4 -4
  160. package/mcp-server/tools/cms/HsFunctionLogsTool.js +6 -2
  161. package/mcp-server/tools/cms/HsListFunctionsTool.js +5 -1
  162. package/mcp-server/tools/cms/HsListTool.js +5 -1
  163. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -0
  164. package/mcp-server/tools/index.js +4 -0
  165. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +1 -1
  166. package/mcp-server/tools/project/AddFeatureToProjectTool.js +9 -3
  167. package/mcp-server/tools/project/CreateProjectTool.js +8 -2
  168. package/mcp-server/tools/project/CreateTestAccountTool.d.ts +41 -0
  169. package/mcp-server/tools/project/CreateTestAccountTool.js +150 -0
  170. package/mcp-server/tools/project/DeployProjectTool.d.ts +1 -1
  171. package/mcp-server/tools/project/DeployProjectTool.js +8 -2
  172. package/mcp-server/tools/project/DocFetchTool.d.ts +1 -1
  173. package/mcp-server/tools/project/DocFetchTool.js +9 -5
  174. package/mcp-server/tools/project/DocsSearchTool.d.ts +1 -1
  175. package/mcp-server/tools/project/DocsSearchTool.js +12 -8
  176. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +1 -1
  177. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +11 -7
  178. package/mcp-server/tools/project/GetApplicationInfoTool.d.ts +1 -1
  179. package/mcp-server/tools/project/GetApplicationInfoTool.js +11 -7
  180. package/mcp-server/tools/project/GetBuildStatusTool.d.ts +26 -0
  181. package/mcp-server/tools/project/GetBuildStatusTool.js +164 -0
  182. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +1 -1
  183. package/mcp-server/tools/project/GetConfigValuesTool.js +11 -7
  184. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +1 -1
  185. package/mcp-server/tools/project/GuidedWalkthroughTool.js +7 -3
  186. package/mcp-server/tools/project/UploadProjectTools.d.ts +9 -3
  187. package/mcp-server/tools/project/UploadProjectTools.js +51 -5
  188. package/mcp-server/tools/project/ValidateProjectTool.d.ts +1 -1
  189. package/mcp-server/tools/project/ValidateProjectTool.js +7 -3
  190. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.d.ts +1 -0
  191. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +454 -0
  192. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +5 -1
  193. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +25 -13
  194. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +7 -5
  195. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +7 -5
  196. package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.d.ts +1 -0
  197. package/mcp-server/tools/project/__tests__/GetBuildStatusTool.test.js +240 -0
  198. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +8 -6
  199. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +56 -4
  200. package/mcp-server/utils/__tests__/content.test.js +21 -20
  201. package/mcp-server/utils/__tests__/feedbackTracking.test.js +33 -28
  202. package/mcp-server/utils/content.d.ts +1 -1
  203. package/mcp-server/utils/content.js +2 -2
  204. package/mcp-server/utils/feedbackTracking.d.ts +1 -1
  205. package/mcp-server/utils/feedbackTracking.js +3 -3
  206. package/mcp-server/utils/toolUsageTracking.js +4 -3
  207. package/package.json +8 -7
  208. package/lang/en.lyaml +0 -1508
  209. package/lib/lang.d.ts +0 -8
  210. package/lib/lang.js +0 -72
  211. package/mcp-server/utils/__tests__/cliConfig.test.js +0 -110
  212. package/mcp-server/utils/cliConfig.d.ts +0 -1
  213. package/mcp-server/utils/cliConfig.js +0 -12
  214. /package/{mcp-server/utils/__tests__/cliConfig.test.d.ts → commands/project/__tests__/updateDeps.test.d.ts} +0 -0
@@ -14,6 +14,7 @@ import { uiLogger } from '../../../lib/ui/logger.js';
14
14
  import * as sandboxesLib from '../../../lib/sandboxes.js';
15
15
  import * as sandboxSync from '../../../lib/sandboxSync.js';
16
16
  import { vi } from 'vitest';
17
+ import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
17
18
  vi.mock('../../../lib/ui/logger.js');
18
19
  vi.mock('@hubspot/local-dev-lib/config');
19
20
  vi.mock('../../../lib/commonOpts');
@@ -26,12 +27,13 @@ vi.mock('../../../lib/usageTracking');
26
27
  vi.mock('../../../lib/buildAccount');
27
28
  vi.mock('../../../lib/sandboxes');
28
29
  vi.mock('../../../lib/commonOpts');
29
- const getAccountConfigSpy = vi.spyOn(configUtils, 'getAccountConfig');
30
+ const getConfigAccountByIdSpy = vi.spyOn(configUtils, 'getConfigAccountById');
30
31
  const promptUserSpy = vi.spyOn(promptUtils, 'promptUser');
31
32
  const sandboxTypePromptSpy = vi.spyOn(sandboxPrompts, 'sandboxTypePrompt');
32
33
  const processExitSpy = vi.spyOn(process, 'exit');
33
34
  const buildSandboxSpy = vi.spyOn(buildAccount, 'buildSandbox');
34
35
  const buildV2SandboxSpy = vi.spyOn(buildAccount, 'buildV2Sandbox');
36
+ const getConfigAccountEnvironmentSpy = vi.spyOn(configUtils, 'getConfigAccountEnvironment');
35
37
  const getAvailableSyncTypesSpy = vi.spyOn(sandboxesLib, 'getAvailableSyncTypes');
36
38
  const syncSandboxSpy = vi.spyOn(sandboxSync, 'syncSandbox');
37
39
  const validateSandboxUsageLimitsSpy = vi.spyOn(sandboxesLib, 'validateSandboxUsageLimits');
@@ -88,7 +90,8 @@ describe('commands/sandbox/create', () => {
88
90
  args = {
89
91
  derivedAccountId: 1234567890,
90
92
  };
91
- getAccountConfigSpy.mockReturnValue({
93
+ getConfigAccountByIdSpy.mockReturnValue({
94
+ accountId: 1234567890,
92
95
  accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
93
96
  env: 'prod',
94
97
  });
@@ -104,6 +107,7 @@ describe('commands/sandbox/create', () => {
104
107
  validateSandboxUsageLimitsSpy.mockResolvedValue(undefined);
105
108
  mockedHasFeatureV2Sandboxes.mockResolvedValue(false);
106
109
  mockedHasFeatureV2Cli.mockResolvedValue(false);
110
+ getConfigAccountEnvironmentSpy.mockReturnValue(ENVIRONMENTS.PROD);
107
111
  buildSandboxSpy.mockResolvedValue({
108
112
  sandbox: mockSandbox,
109
113
  personalAccessKey: 'mock-personal-access-key',
@@ -123,8 +127,8 @@ describe('commands/sandbox/create', () => {
123
127
  });
124
128
  it('should load the account config for the correct account id', async () => {
125
129
  await sandboxCreateCommand.handler(args);
126
- expect(getAccountConfigSpy).toHaveBeenCalledTimes(2); // 1st is for parent account, 2nd is for sandbox account
127
- expect(getAccountConfigSpy).toHaveBeenCalledWith(args.derivedAccountId);
130
+ expect(getConfigAccountByIdSpy).toHaveBeenCalledTimes(2); // 1st is for parent account, 2nd is for sandbox account
131
+ expect(getConfigAccountByIdSpy).toHaveBeenCalledWith(args.derivedAccountId);
128
132
  });
129
133
  it('should track the command usage', async () => {
130
134
  await sandboxCreateCommand.handler(args);
@@ -135,6 +139,7 @@ describe('commands/sandbox/create', () => {
135
139
  await sandboxCreateCommand.handler(args);
136
140
  expect(validateSandboxUsageLimitsSpy).toHaveBeenCalledTimes(1);
137
141
  expect(validateSandboxUsageLimitsSpy).toHaveBeenCalledWith({
142
+ accountId: 1234567890,
138
143
  accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
139
144
  env: 'prod',
140
145
  }, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'prod');
@@ -168,6 +173,7 @@ describe('commands/sandbox/create', () => {
168
173
  await sandboxCreateCommand.handler(args);
169
174
  expect(buildSandboxSpy).toHaveBeenCalledTimes(1);
170
175
  expect(buildSandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
176
+ accountId: 1234567890,
171
177
  accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
172
178
  env: 'prod',
173
179
  }, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'prod', undefined // force
@@ -181,6 +187,7 @@ describe('commands/sandbox/create', () => {
181
187
  await sandboxCreateCommand.handler(args);
182
188
  expect(buildSandboxSpy).toHaveBeenCalledTimes(1);
183
189
  expect(buildSandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
190
+ accountId: 1234567890,
184
191
  accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
185
192
  env: 'prod',
186
193
  }, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'prod', undefined // force
@@ -194,6 +201,7 @@ describe('commands/sandbox/create', () => {
194
201
  await sandboxCreateCommand.handler(args);
195
202
  expect(buildV2SandboxSpy).toHaveBeenCalledTimes(1);
196
203
  expect(buildV2SandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
204
+ accountId: 1234567890,
197
205
  accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
198
206
  env: 'prod',
199
207
  }, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, false, // syncObjectRecords
@@ -222,7 +230,8 @@ describe('commands/sandbox/create', () => {
222
230
  expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
223
231
  });
224
232
  it('should error out if the default account type is not standard', async () => {
225
- getAccountConfigSpy.mockReturnValue({
233
+ getConfigAccountByIdSpy.mockReturnValue({
234
+ accountId: 1234567890,
226
235
  accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
227
236
  env: 'prod',
228
237
  });
@@ -1,9 +1,8 @@
1
- import { getAccountConfig, getEnv } from '@hubspot/local-dev-lib/config';
1
+ import { getConfigAccountById, getConfigAccountEnvironment, } from '@hubspot/local-dev-lib/config';
2
2
  import { uiLogger } from '../../lib/ui/logger.js';
3
3
  import { isMissingScopeError } from '@hubspot/local-dev-lib/errors/index';
4
4
  import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
5
5
  import { HUBSPOT_ACCOUNT_TYPES, HUBSPOT_ACCOUNT_TYPE_STRINGS, } from '@hubspot/local-dev-lib/constants/config';
6
- import { getValidEnv } from '@hubspot/local-dev-lib/environment';
7
6
  import { commands, lib } from '../../lang/en.js';
8
7
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
9
8
  import { uiFeatureHighlight, uiBetaTag } from '../../lib/ui/index.js';
@@ -22,8 +21,8 @@ const command = 'create';
22
21
  const describe = uiBetaTag(commands.sandbox.subcommands.create.describe, false);
23
22
  async function handler(args) {
24
23
  const { name, type, force, derivedAccountId } = args;
25
- const accountConfig = getAccountConfig(derivedAccountId);
26
- const env = getValidEnv(getEnv(derivedAccountId));
24
+ const accountConfig = getConfigAccountById(derivedAccountId);
25
+ const env = getConfigAccountEnvironment(derivedAccountId);
27
26
  trackCommandUsage('sandbox-create', {}, derivedAccountId);
28
27
  // Check if account config exists
29
28
  if (!accountConfig) {
@@ -111,7 +110,7 @@ async function handler(args) {
111
110
  else {
112
111
  result = await buildSandbox(sandboxName, accountConfig, sandboxType, env, force);
113
112
  }
114
- const sandboxAccountConfig = getAccountConfig(result.sandbox.sandboxHubId);
113
+ const sandboxAccountConfig = getConfigAccountById(result.sandbox.sandboxHubId);
115
114
  // Check if sandbox account config exists
116
115
  if (!sandboxAccountConfig) {
117
116
  uiLogger.error(commands.sandbox.subcommands.create.failure.noSandboxAccountConfig(result.sandbox.sandboxHubId));
@@ -1,8 +1,7 @@
1
1
  import { uiLogger } from '../../lib/ui/logger.js';
2
2
  import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
3
3
  import { deleteSandbox } from '@hubspot/local-dev-lib/api/sandboxHubs';
4
- import { getEnv, removeSandboxAccountFromConfig, updateDefaultAccount, getAccountId, getConfigAccounts, } from '@hubspot/local-dev-lib/config';
5
- import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
4
+ import { getConfigAccountEnvironment, removeAccountFromConfig, setConfigAccountAsDefault, getAllConfigAccounts, getConfigAccountIfExists, getConfigDefaultAccountIfExists, } from '@hubspot/local-dev-lib/config';
6
5
  import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
7
6
  import { getValidEnv } from '@hubspot/local-dev-lib/environment';
8
7
  import { trackCommandUsage } from '../../lib/usageTracking.js';
@@ -36,17 +35,20 @@ async function handler(args) {
36
35
  process.exit(EXIT_CODES.ERROR);
37
36
  }
38
37
  }
39
- const sandboxAccountId = getAccountId(userProvidedAccount || accountPrompt.account);
40
- if (!sandboxAccountId) {
38
+ const accountIdentifier = userProvidedAccount || accountPrompt.account;
39
+ const sandboxAccount = getConfigAccountIfExists(accountIdentifier);
40
+ if (!sandboxAccount) {
41
41
  uiLogger.error(commands.sandbox.subcommands.delete.failure.noSandboxAccountId);
42
42
  process.exit(EXIT_CODES.ERROR);
43
43
  }
44
- const isDefaultAccount = sandboxAccountId === getAccountId();
45
- const baseUrl = getHubSpotWebsiteOrigin(getValidEnv(getEnv(sandboxAccountId)));
44
+ const sandboxAccountId = sandboxAccount.accountId;
45
+ const defaultAccount = getConfigDefaultAccountIfExists();
46
+ const isDefaultAccount = sandboxAccountId === defaultAccount?.accountId;
47
+ const baseUrl = getHubSpotWebsiteOrigin(getValidEnv(getConfigAccountEnvironment(sandboxAccountId)));
46
48
  let parentAccountId;
47
- const accountsList = getConfigAccounts() || [];
49
+ const accountsList = getAllConfigAccounts() || [];
48
50
  for (const portal of accountsList) {
49
- if (getAccountIdentifier(portal) === sandboxAccountId) {
51
+ if (portal.accountId === sandboxAccountId) {
50
52
  if (portal.parentAccountId) {
51
53
  parentAccountId = portal.parentAccountId;
52
54
  }
@@ -56,7 +58,8 @@ async function handler(args) {
56
58
  uiLogger.error(commands.sandbox.subcommands.delete.failure.noParentAccount);
57
59
  process.exit(EXIT_CODES.ERROR);
58
60
  }
59
- parentAccountId = getAccountId(parentAccountPrompt.account);
61
+ const parentAccount = getConfigAccountIfExists(parentAccountPrompt.account);
62
+ parentAccountId = parentAccount?.accountId;
60
63
  }
61
64
  else {
62
65
  uiLogger.error(commands.sandbox.subcommands.delete.failure.noParentAccount);
@@ -67,9 +70,9 @@ async function handler(args) {
67
70
  const url = `${baseUrl}/sandboxes/${parentAccountId}`;
68
71
  const command = uiAuthCommandReference({
69
72
  accountId: parentAccountId || undefined,
70
- qa: getEnv(sandboxAccountId) === 'qa',
73
+ qa: getConfigAccountEnvironment(sandboxAccountId) === 'qa',
71
74
  });
72
- if (parentAccountId && !getAccountId(parentAccountId)) {
75
+ if (parentAccountId && !getConfigAccountIfExists(parentAccountId)) {
73
76
  uiLogger.log('');
74
77
  uiLogger.error(commands.sandbox.subcommands.delete.failure.noParentPortalAvailable(command, url));
75
78
  uiLogger.log('');
@@ -99,14 +102,14 @@ async function handler(args) {
99
102
  ? commands.sandbox.subcommands.delete.success.deleteDefault(userProvidedAccount || accountPrompt.account, sandboxAccountId)
100
103
  : commands.sandbox.subcommands.delete.success.delete(userProvidedAccount || accountPrompt.account, sandboxAccountId));
101
104
  uiLogger.log('');
102
- const promptDefaultAccount = removeSandboxAccountFromConfig(sandboxAccountId);
103
- if (promptDefaultAccount && !force) {
105
+ removeAccountFromConfig(sandboxAccountId);
106
+ if (isDefaultAccount && !force) {
104
107
  const newDefaultAccount = await selectAccountFromConfig();
105
- updateDefaultAccount(newDefaultAccount);
108
+ setConfigAccountAsDefault(newDefaultAccount);
106
109
  }
107
110
  else if (isDefaultAccount && force) {
108
111
  // If force is specified, skip prompt and set the parent account id as the default account
109
- updateDefaultAccount(parentAccountId);
112
+ setConfigAccountAsDefault(parentAccountId);
110
113
  }
111
114
  process.exit(EXIT_CODES.SUCCESS);
112
115
  }
@@ -135,14 +138,14 @@ async function handler(args) {
135
138
  uiLogger.log('');
136
139
  uiLogger.warn(commands.sandbox.subcommands.delete.failure.objectNotFound(sandboxAccountId));
137
140
  uiLogger.log('');
138
- const promptDefaultAccount = removeSandboxAccountFromConfig(sandboxAccountId);
139
- if (promptDefaultAccount && !force) {
141
+ removeAccountFromConfig(sandboxAccountId);
142
+ if (isDefaultAccount && !force) {
140
143
  const newDefaultAccount = await selectAccountFromConfig();
141
- updateDefaultAccount(newDefaultAccount);
144
+ setConfigAccountAsDefault(newDefaultAccount);
142
145
  }
143
- else {
146
+ else if (isDefaultAccount) {
144
147
  // If force is specified, skip prompt and set the parent account id as the default account
145
- updateDefaultAccount(parentAccountId);
148
+ setConfigAccountAsDefault(parentAccountId);
146
149
  }
147
150
  process.exit(EXIT_CODES.SUCCESS);
148
151
  }
@@ -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;
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import { getValidEnv } from '@hubspot/local-dev-lib/environment';
4
- import { getEnv } from '@hubspot/local-dev-lib/config';
4
+ import { getConfigAccountEnvironment } from '@hubspot/local-dev-lib/config';
5
5
  import { getCwd } from '@hubspot/local-dev-lib/path';
6
6
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
7
7
  import { promptUser, listPrompt } from '../../lib/prompts/promptUtils.js';
@@ -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(getConfigAccountEnvironment(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
- '$0 create --config-path ./test-account-config.json',
111
- commands.testAccount.create.example('./test-account-config.json'),
200
+ '$0 test-account create --config-path ./test-account-config.json',
201
+ 'Create from config file (mutually exclusive with other flags)',
112
202
  ],
113
203
  ]);
114
204
  return yargs;
@@ -4,7 +4,7 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
4
4
  import { uiLogger } from '../../lib/ui/logger.js';
5
5
  import { trackCommandUsage } from '../../lib/usageTracking.js';
6
6
  import { commands } from '../../lang/en.js';
7
- import { deleteAccount, getAccountConfig, getAccountId, getConfigPath, loadConfig, updateDefaultAccount, } from '@hubspot/local-dev-lib/config';
7
+ import { removeAccountFromConfig, getConfigAccountById, getConfigAccountIfExists, setConfigAccountAsDefault, getConfigDefaultAccountIfExists, } from '@hubspot/local-dev-lib/config';
8
8
  import { promptUser } from '../../lib/prompts/promptUtils.js';
9
9
  import { debugError } from '../../lib/errorHandlers/index.js';
10
10
  const command = 'delete [test-account]';
@@ -64,12 +64,12 @@ async function deleteTestAccountFromConfig(testAccountId, parentAccountName, acc
64
64
  // If the account isn't in the local config then it wasn't auth'd on the local machine
65
65
  if (account && account.name && account.accountType) {
66
66
  // If the deleted test account was the default account, replace the default account with the parent account
67
- loadConfig(getConfigPath()); // Get updated version of the config
68
- const defaultAccountId = getAccountId(); // We need to get the current default accountId before delete the test account
69
- await deleteAccount(account.name);
67
+ const defaultAccount = getConfigDefaultAccountIfExists();
68
+ const defaultAccountId = defaultAccount?.accountId;
69
+ removeAccountFromConfig(account.accountId);
70
70
  uiLogger.success(commands.testAccount.delete.success.testAccountDeletedFromConfig(testAccountId));
71
71
  if (testAccountId === defaultAccountId) {
72
- updateDefaultAccount(parentAccountName);
72
+ setConfigAccountAsDefault(parentAccountName);
73
73
  uiLogger.info(commands.testAccount.delete.info.replaceDefaultAccount(testAccountId, parentAccountName));
74
74
  }
75
75
  }
@@ -84,12 +84,12 @@ async function validateTestAccountConfigs(testAccountId) {
84
84
  uiLogger.error(commands.testAccount.delete.errors.testAccountNotFound(testAccountId));
85
85
  process.exit(EXIT_CODES.ERROR);
86
86
  }
87
- const testAccountConfig = getAccountConfig(testAccountId);
87
+ const testAccountConfig = getConfigAccountById(testAccountId);
88
88
  if (!testAccountConfig) {
89
89
  uiLogger.error(commands.testAccount.delete.errors.testAccountNotFound(testAccountId));
90
90
  process.exit(EXIT_CODES.ERROR);
91
91
  }
92
- const parentAccountConfig = getAccountConfig(testAccountConfig.parentAccountId);
92
+ const parentAccountConfig = getConfigAccountById(testAccountConfig.parentAccountId);
93
93
  if (!parentAccountConfig) {
94
94
  uiLogger.error(commands.testAccount.delete.errors.parentAccountNotFound(testAccountId));
95
95
  process.exit(EXIT_CODES.ERROR);
@@ -103,7 +103,8 @@ async function handler(args) {
103
103
  let testAccountIdToDelete = 0;
104
104
  // See if the account exists
105
105
  if (testAccount) {
106
- const accountId = getAccountId(testAccount);
106
+ const account = getConfigAccountIfExists(testAccount);
107
+ const accountId = account?.accountId || null;
107
108
  await validateTestAccountConfigs(accountId);
108
109
  if (accountId) {
109
110
  testAccountIdToDelete = accountId;
@@ -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;