@hubspot/cli 8.0.2-experimental.0 → 8.0.3-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 (69) hide show
  1. package/commands/__tests__/getStarted.test.js +2 -2
  2. package/commands/__tests__/project.test.js +30 -0
  3. package/commands/account/auth.js +8 -97
  4. package/commands/account/use.js +19 -4
  5. package/commands/cms/module/marketplace-validate.js +23 -5
  6. package/commands/cms/theme/marketplace-validate.js +25 -6
  7. package/commands/mcp/setup.js +1 -2
  8. package/commands/mcp.js +1 -2
  9. package/commands/project.js +22 -1
  10. package/lang/en.d.ts +22 -1
  11. package/lang/en.js +26 -5
  12. package/lib/__tests__/accountAuth.test.d.ts +1 -0
  13. package/lib/__tests__/accountAuth.test.js +258 -0
  14. package/lib/accountAuth.d.ts +10 -0
  15. package/lib/accountAuth.js +105 -0
  16. package/lib/app/urls.d.ts +1 -0
  17. package/lib/app/urls.js +4 -0
  18. package/lib/errors/ProjectErrors.d.ts +15 -0
  19. package/lib/errors/ProjectErrors.js +30 -0
  20. package/lib/getStarted/getStartedV2.js +3 -41
  21. package/lib/getStartedV2Actions.d.ts +29 -0
  22. package/lib/getStartedV2Actions.js +104 -9
  23. package/lib/marketplaceValidate.d.ts +1 -1
  24. package/lib/marketplaceValidate.js +23 -41
  25. package/lib/projects/ProjectLogsManager.d.ts +12 -3
  26. package/lib/projects/ProjectLogsManager.js +70 -12
  27. package/lib/projects/__tests__/ProjectLogsManager.test.js +131 -18
  28. package/lib/projects/__tests__/platformVersion.test.js +37 -1
  29. package/lib/projects/__tests__/projects.test.js +6 -2
  30. package/lib/projects/components.d.ts +6 -0
  31. package/lib/projects/components.js +1 -1
  32. package/lib/projects/config.js +9 -2
  33. package/lib/projects/localDev/helpers/project.d.ts +4 -1
  34. package/lib/projects/localDev/helpers/project.js +13 -8
  35. package/lib/projects/platformVersion.d.ts +8 -0
  36. package/lib/projects/platformVersion.js +31 -2
  37. package/lib/prompts/accountsPrompt.d.ts +2 -1
  38. package/lib/prompts/accountsPrompt.js +10 -2
  39. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +20 -3
  40. package/mcp-server/tools/project/AddFeatureToProjectTool.js +6 -10
  41. package/mcp-server/tools/project/CreateProjectTool.d.ts +24 -4
  42. package/mcp-server/tools/project/CreateProjectTool.js +5 -10
  43. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +5 -8
  44. package/mcp-server/tools/project/GetBuildLogsTool.d.ts +2 -2
  45. package/mcp-server/tools/project/GetBuildLogsTool.js +3 -4
  46. package/mcp-server/tools/project/GetBuildStatusTool.d.ts +1 -1
  47. package/mcp-server/tools/project/GetBuildStatusTool.js +3 -4
  48. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +6 -1
  49. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -6
  50. package/mcp-server/tools/project/constants.d.ts +12 -1
  51. package/mcp-server/tools/project/constants.js +12 -16
  52. package/package.json +2 -2
  53. package/ui/components/ActionSection.d.ts +1 -1
  54. package/ui/components/BoxWithTitle.js +1 -1
  55. package/ui/components/getStarted/GetStartedFlow.d.ts +8 -0
  56. package/ui/components/getStarted/GetStartedFlow.js +136 -0
  57. package/ui/components/getStarted/reducer.d.ts +59 -0
  58. package/ui/components/getStarted/reducer.js +72 -0
  59. package/ui/components/getStarted/screens/ProjectSetupScreen.d.ts +16 -0
  60. package/ui/components/getStarted/screens/ProjectSetupScreen.js +39 -0
  61. package/ui/components/getStarted/screens/UploadScreen.d.ts +7 -0
  62. package/ui/components/getStarted/screens/UploadScreen.js +43 -0
  63. package/ui/components/getStarted/selectors.d.ts +2 -0
  64. package/ui/components/getStarted/selectors.js +1 -0
  65. package/ui/lib/constants.d.ts +16 -0
  66. package/ui/lib/constants.js +16 -0
  67. package/ui/render.js +6 -0
  68. package/ui/components/GetStartedFlow.d.ts +0 -24
  69. package/ui/components/GetStartedFlow.js +0 -128
@@ -10,7 +10,7 @@ import { GET_STARTED_OPTIONS } from '../../lib/constants.js';
10
10
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
11
11
  import open from 'open';
12
12
  import { renderInteractive } from '../../ui/render.js';
13
- import { getGetStartedFlow } from '../../ui/components/GetStartedFlow.js';
13
+ import { getGetStartedFlow } from '../../ui/components/getStarted/GetStartedFlow.js';
14
14
  vi.mock('../../lib/prompts/promptUtils');
15
15
  vi.mock('../../lib/prompts/projectNameAndDestPrompt');
16
16
  vi.mock('../../lib/projects/config');
@@ -19,7 +19,7 @@ vi.mock('@hubspot/local-dev-lib/github');
19
19
  vi.mock('../../lib/dependencyManagement');
20
20
  vi.mock('@hubspot/local-dev-lib/config');
21
21
  vi.mock('../../ui/render');
22
- vi.mock('../../ui/components/GetStartedFlow');
22
+ vi.mock('../../ui/components/getStarted/GetStartedFlow');
23
23
  vi.mock('open');
24
24
  vi.mock('fs-extra', () => ({
25
25
  default: {
@@ -17,6 +17,9 @@ import validate from '../project/validate.js';
17
17
  import profileCommands from '../project/profile.js';
18
18
  import list from '../project/list.js';
19
19
  import projectCommand from '../project.js';
20
+ import * as projectConfigLib from '../../lib/projects/config.js';
21
+ import * as platformVersionLib from '../../lib/projects/platformVersion.js';
22
+ import { uiLogger } from '../../lib/ui/logger.js';
20
23
  vi.mock('../project/deploy');
21
24
  vi.mock('../project/create');
22
25
  vi.mock('../project/upload');
@@ -40,13 +43,24 @@ vi.mock('../project/installDeps');
40
43
  vi.mock('../project/lint');
41
44
  vi.mock('../project/profile');
42
45
  vi.mock('../../lib/commonOpts');
46
+ vi.mock('../../lib/projects/config.js');
47
+ vi.mock('../../lib/projects/platformVersion.js');
43
48
  const commandSpy = vi
44
49
  .spyOn(yargs, 'command')
45
50
  .mockReturnValue(yargs);
46
51
  const demandCommandSpy = vi
47
52
  .spyOn(yargs, 'demandCommand')
48
53
  .mockReturnValue(yargs);
54
+ const getProjectConfigSpy = vi.spyOn(projectConfigLib, 'getProjectConfig');
55
+ const isUnsupportedPlatformVersionSpy = vi.spyOn(platformVersionLib, 'isUnsupportedPlatformVersion');
56
+ const processExitSpy = vi.spyOn(process, 'exit');
57
+ const uiLoggerErrorSpy = vi.spyOn(uiLogger, 'error');
49
58
  describe('commands/project', () => {
59
+ beforeEach(() => {
60
+ vi.clearAllMocks();
61
+ // @ts-expect-error Mock implementation
62
+ processExitSpy.mockImplementation(() => { });
63
+ });
50
64
  describe('command', () => {
51
65
  it('should have the correct command structure', () => {
52
66
  expect(projectCommand.command).toEqual(['project', 'projects']);
@@ -92,4 +106,20 @@ describe('commands/project', () => {
92
106
  expect(commandSpy).toHaveBeenCalledWith(module);
93
107
  });
94
108
  });
109
+ describe('middleware - validatePlatformVersion', () => {
110
+ it('should have platform version validation functions available', () => {
111
+ // Verify the necessary functions are available for middleware
112
+ expect(getProjectConfigSpy).toBeDefined();
113
+ expect(isUnsupportedPlatformVersionSpy).toBeDefined();
114
+ expect(uiLoggerErrorSpy).toBeDefined();
115
+ expect(processExitSpy).toBeDefined();
116
+ });
117
+ it('should register middleware when building', async () => {
118
+ // Verify middleware is registered during builder execution
119
+ await projectCommand.builder(yargs);
120
+ // The middleware should be registered (we can't easily test async middleware execution)
121
+ // but we verify the builder completes successfully
122
+ expect(projectCommand.builder).toBeDefined();
123
+ });
124
+ });
95
125
  });
@@ -1,96 +1,21 @@
1
- import { updateConfigAccount, createEmptyConfigFile, deleteConfigFileIfEmpty, getConfigFilePath, localConfigFileExists, globalConfigFileExists, } from '@hubspot/local-dev-lib/config';
2
- import { getAccessToken, updateConfigWithAccessToken, } from '@hubspot/local-dev-lib/personalAccessKey';
3
1
  import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
4
- import { toKebabCase } from '@hubspot/local-dev-lib/text';
5
2
  import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constants/auth';
6
- import { handleMerge, handleMigration } from '../../lib/configMigrate.js';
3
+ import { deleteConfigFileIfEmpty } from '@hubspot/local-dev-lib/config';
7
4
  import { handleExit } from '../../lib/process.js';
8
- import { debugError } from '../../lib/errorHandlers/index.js';
9
5
  import { trackCommandUsage, trackAuthAction } from '../../lib/usageTracking.js';
10
- import { personalAccessKeyPrompt } from '../../lib/prompts/personalAccessKeyPrompt.js';
11
- import { cliAccountNamePrompt } from '../../lib/prompts/accountNamePrompt.js';
12
- import { setAsDefaultAccountPrompt } from '../../lib/prompts/setAsDefaultAccountPrompt.js';
13
- import { logError } from '../../lib/errorHandlers/index.js';
14
6
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
15
7
  import { uiFeatureHighlight } from '../../lib/ui/index.js';
16
8
  import { parseStringToNumber } from '../../lib/parsing.js';
17
9
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
18
10
  import { commands } from '../../lang/en.js';
19
11
  import { uiLogger } from '../../lib/ui/logger.js';
12
+ import { authenticateNewAccount } from '../../lib/accountAuth.js';
20
13
  const TRACKING_STATUS = {
21
14
  STARTED: 'started',
22
15
  ERROR: 'error',
23
16
  COMPLETE: 'complete',
24
17
  };
25
18
  const authType = PERSONAL_ACCESS_KEY_AUTH_METHOD.value;
26
- async function updateConfigWithNewAccount(env, configAlreadyExists, providedPersonalAccessKey, accountId) {
27
- try {
28
- const { personalAccessKey } = providedPersonalAccessKey
29
- ? { personalAccessKey: providedPersonalAccessKey }
30
- : await personalAccessKeyPrompt({
31
- env,
32
- account: accountId,
33
- });
34
- const token = await getAccessToken(personalAccessKey, env);
35
- const defaultAccountName = token.hubName
36
- ? toKebabCase(token.hubName)
37
- : undefined;
38
- const accountName = configAlreadyExists
39
- ? undefined
40
- : (await cliAccountNamePrompt(defaultAccountName)).name;
41
- const updatedConfig = await updateConfigWithAccessToken(token, personalAccessKey, env, accountName, !configAlreadyExists);
42
- if (!updatedConfig)
43
- return null;
44
- // Can happen if the user is re-authenticating an account with no name
45
- if (configAlreadyExists && !updatedConfig.name) {
46
- updatedConfig.name = (await cliAccountNamePrompt(defaultAccountName)).name;
47
- updateConfigAccount({
48
- ...updatedConfig,
49
- });
50
- }
51
- return updatedConfig;
52
- }
53
- catch (e) {
54
- debugError(e);
55
- return null;
56
- }
57
- }
58
- async function handleConfigMigration() {
59
- const deprecatedConfigExists = localConfigFileExists();
60
- const globalConfigExists = globalConfigFileExists();
61
- // No deprecated config exists, so no migration is needed
62
- if (!deprecatedConfigExists) {
63
- return true;
64
- }
65
- // Global config exists, so we need to merge the deprecated config with the global config
66
- if (globalConfigExists) {
67
- try {
68
- const mergeConfirmed = await handleMerge();
69
- if (!mergeConfirmed) {
70
- uiLogger.log('');
71
- uiLogger.log(commands.account.subcommands.auth.errors.mergeNotConfirmed);
72
- }
73
- return mergeConfirmed;
74
- }
75
- catch (error) {
76
- logError(error);
77
- return false;
78
- }
79
- }
80
- // Global config does not exist, so we only need to migrate the deprecated config
81
- try {
82
- const migrationConfirmed = await handleMigration();
83
- if (!migrationConfirmed) {
84
- uiLogger.log('');
85
- uiLogger.log(commands.account.subcommands.auth.errors.migrationNotConfirmed);
86
- }
87
- return migrationConfirmed;
88
- }
89
- catch (error) {
90
- logError(error);
91
- return false;
92
- }
93
- }
94
19
  const describe = commands.account.subcommands.auth.describe;
95
20
  const command = 'auth';
96
21
  async function handler(args) {
@@ -109,33 +34,19 @@ async function handler(args) {
109
34
  trackCommandUsage('account-auth', {}, parsedUserProvidedAccountId);
110
35
  await trackAuthAction('account-auth', authType, TRACKING_STATUS.STARTED);
111
36
  }
112
- const configMigrationSuccess = await handleConfigMigration();
113
- if (!configMigrationSuccess) {
114
- await trackAuthAction('account-auth', authType, TRACKING_STATUS.ERROR);
115
- process.exit(EXIT_CODES.ERROR);
116
- }
117
- const configAlreadyExists = globalConfigFileExists();
118
- if (!configAlreadyExists) {
119
- createEmptyConfigFile(true);
120
- }
121
37
  handleExit(deleteConfigFileIfEmpty);
122
- const updatedConfig = await updateConfigWithNewAccount(args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD, configAlreadyExists, providedPersonalAccessKey, parsedUserProvidedAccountId);
38
+ const updatedConfig = await authenticateNewAccount({
39
+ env: args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD,
40
+ providedPersonalAccessKey,
41
+ accountId: parsedUserProvidedAccountId,
42
+ });
123
43
  if (!updatedConfig) {
124
44
  if (!disableTracking) {
125
45
  await trackAuthAction('account-auth', authType, TRACKING_STATUS.ERROR);
126
46
  }
127
- uiLogger.error(commands.account.subcommands.auth.errors.failedToUpdateConfig);
128
47
  return process.exit(EXIT_CODES.ERROR);
129
48
  }
130
- const { accountId, name } = updatedConfig;
131
- if (!configAlreadyExists) {
132
- uiLogger.log('');
133
- uiLogger.success(commands.account.subcommands.auth.success.configFileCreated(getConfigFilePath()));
134
- uiLogger.success(commands.account.subcommands.auth.success.configFileUpdated(accountId));
135
- }
136
- else {
137
- await setAsDefaultAccountPrompt(name);
138
- }
49
+ const { accountId } = updatedConfig;
139
50
  uiFeatureHighlight([
140
51
  'getStartedCommand',
141
52
  'helpCommand',
@@ -1,24 +1,39 @@
1
- import { getConfigFilePath, setConfigAccountAsDefault, getConfigAccountIfExists, getConfigAccountByName, getConfigAccountById, getAllConfigAccounts, } from '@hubspot/local-dev-lib/config';
1
+ import { getConfigFilePath, setConfigAccountAsDefault, getConfigAccountIfExists, getConfigAccountByName, getConfigAccountById, globalConfigFileExists, getAllConfigAccounts, } from '@hubspot/local-dev-lib/config';
2
2
  import { getDefaultAccountOverrideAccountId, getDefaultAccountOverrideFilePath, } from '@hubspot/local-dev-lib/config/defaultAccountOverride';
3
+ import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
3
4
  import { trackCommandUsage } from '../../lib/usageTracking.js';
4
5
  import { commands } from '../../lang/en.js';
5
6
  import { uiLogger } from '../../lib/ui/logger.js';
6
- import { selectAccountFromConfig } from '../../lib/prompts/accountsPrompt.js';
7
+ import { selectAccountFromConfig, AUTHENTICATE_NEW_ACCOUNT_VALUE, } from '../../lib/prompts/accountsPrompt.js';
7
8
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
9
+ import { authenticateNewAccount } from '../../lib/accountAuth.js';
10
+ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
8
11
  const command = 'use [account]';
9
12
  const describe = commands.account.subcommands.use.describe;
10
13
  async function handler(args) {
11
14
  let newDefaultAccount = args.account;
15
+ const usingGlobalConfig = globalConfigFileExists();
12
16
  if (!newDefaultAccount) {
13
- newDefaultAccount = await selectAccountFromConfig();
17
+ newDefaultAccount = await selectAccountFromConfig('', usingGlobalConfig);
14
18
  }
15
19
  else {
16
20
  const account = getConfigAccountIfExists(newDefaultAccount);
17
21
  if (!account) {
18
22
  uiLogger.error(commands.account.subcommands.use.errors.accountNotFound(newDefaultAccount, getConfigFilePath()));
19
- newDefaultAccount = await selectAccountFromConfig();
23
+ newDefaultAccount = await selectAccountFromConfig('', usingGlobalConfig);
20
24
  }
21
25
  }
26
+ if (newDefaultAccount === AUTHENTICATE_NEW_ACCOUNT_VALUE) {
27
+ const updatedConfig = await authenticateNewAccount({
28
+ env: args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD,
29
+ setAsDefaultAccount: true,
30
+ });
31
+ if (!updatedConfig) {
32
+ process.exit(EXIT_CODES.ERROR);
33
+ }
34
+ trackCommandUsage('accounts-use', undefined, updatedConfig.accountId);
35
+ return;
36
+ }
22
37
  let account;
23
38
  if (!isNaN(Number(newDefaultAccount))) {
24
39
  account = getConfigAccountById(Number(newDefaultAccount));
@@ -1,9 +1,10 @@
1
1
  import SpinniesManager from '../../../lib/ui/SpinniesManager.js';
2
2
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
3
- import { kickOffValidation, pollForValidationFinish, fetchValidationResults, processValidationErrors, displayValidationResults, } from '../../../lib/marketplaceValidate.js';
3
+ import { kickOffValidation, pollForValidationFinish, fetchValidationResults, hasProcessValidationErrors, displayValidationResults, } from '../../../lib/marketplaceValidate.js';
4
4
  import { commands } from '../../../lang/en.js';
5
5
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
6
6
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
7
+ import { logError } from '../../../lib/errorHandlers/index.js';
7
8
  const command = 'marketplace-validate <src>';
8
9
  const describe = commands.cms.subcommands.module.subcommands.marketplaceValidate.describe;
9
10
  async function handler(args) {
@@ -13,12 +14,29 @@ async function handler(args) {
13
14
  text: commands.cms.subcommands.module.subcommands.marketplaceValidate.logs.validatingModule(src),
14
15
  });
15
16
  const assetType = 'MODULE';
16
- const validationId = await kickOffValidation(derivedAccountId, assetType, src);
17
- await pollForValidationFinish(derivedAccountId, validationId);
17
+ let validationId;
18
+ try {
19
+ validationId = await kickOffValidation(derivedAccountId, assetType, src);
20
+ await pollForValidationFinish(derivedAccountId, validationId);
21
+ }
22
+ catch (e) {
23
+ logError(e);
24
+ process.exit(EXIT_CODES.ERROR);
25
+ }
18
26
  SpinniesManager.remove('marketplaceValidation');
19
- const validationResults = await fetchValidationResults(derivedAccountId, validationId);
20
- processValidationErrors(commands.cms.subcommands.module.subcommands.marketplaceValidate.errors
27
+ let validationResults;
28
+ try {
29
+ validationResults = await fetchValidationResults(derivedAccountId, validationId);
30
+ }
31
+ catch (e) {
32
+ logError(e);
33
+ process.exit(EXIT_CODES.ERROR);
34
+ }
35
+ const hasErrors = hasProcessValidationErrors(commands.cms.subcommands.module.subcommands.marketplaceValidate.errors
21
36
  .invalidPath, validationResults);
37
+ if (hasErrors) {
38
+ process.exit(EXIT_CODES.ERROR);
39
+ }
22
40
  displayValidationResults(commands.cms.subcommands.module.subcommands.marketplaceValidate.results, validationResults);
23
41
  process.exit(EXIT_CODES.SUCCESS);
24
42
  }
@@ -1,8 +1,10 @@
1
1
  import SpinniesManager from '../../../lib/ui/SpinniesManager.js';
2
2
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
3
- import { kickOffValidation, pollForValidationFinish, fetchValidationResults, processValidationErrors, displayValidationResults, } from '../../../lib/marketplaceValidate.js';
3
+ import { kickOffValidation, pollForValidationFinish, fetchValidationResults, hasProcessValidationErrors, displayValidationResults, } from '../../../lib/marketplaceValidate.js';
4
4
  import { commands } from '../../../lang/en.js';
5
5
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
6
+ import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
7
+ import { logError } from '../../../lib/errorHandlers/index.js';
6
8
  const command = 'marketplace-validate <path>';
7
9
  const describe = commands.cms.subcommands.theme.subcommands.marketplaceValidate.describe;
8
10
  async function handler(args) {
@@ -12,14 +14,31 @@ async function handler(args) {
12
14
  text: commands.cms.subcommands.theme.subcommands.marketplaceValidate.logs.validatingTheme(path),
13
15
  });
14
16
  const assetType = 'THEME';
15
- const validationId = await kickOffValidation(derivedAccountId, assetType, path);
16
- await pollForValidationFinish(derivedAccountId, validationId);
17
+ let validationId;
18
+ try {
19
+ validationId = await kickOffValidation(derivedAccountId, assetType, path);
20
+ await pollForValidationFinish(derivedAccountId, validationId);
21
+ }
22
+ catch (e) {
23
+ logError(e);
24
+ process.exit(EXIT_CODES.ERROR);
25
+ }
17
26
  SpinniesManager.remove('marketplaceValidation');
18
- const validationResults = await fetchValidationResults(derivedAccountId, validationId);
19
- processValidationErrors(commands.cms.subcommands.theme.subcommands.marketplaceValidate.errors
27
+ let validationResults;
28
+ try {
29
+ validationResults = await fetchValidationResults(derivedAccountId, validationId);
30
+ }
31
+ catch (e) {
32
+ logError(e);
33
+ process.exit(EXIT_CODES.ERROR);
34
+ }
35
+ const hasErrors = hasProcessValidationErrors(commands.cms.subcommands.theme.subcommands.marketplaceValidate.errors
20
36
  .invalidPath, validationResults);
37
+ if (hasErrors) {
38
+ process.exit(EXIT_CODES.ERROR);
39
+ }
21
40
  displayValidationResults(commands.cms.subcommands.theme.subcommands.marketplaceValidate.results, validationResults);
22
- process.exit();
41
+ process.exit(EXIT_CODES.SUCCESS);
23
42
  }
24
43
  function themeValidateBuilder(yargs) {
25
44
  yargs.positional('path', {
@@ -6,9 +6,8 @@ import { addMcpServerToConfig, supportedTools } from '../../lib/mcp/setup.js';
6
6
  import { trackCommandUsage } from '../../lib/usageTracking.js';
7
7
  import { hasFeature } from '../../lib/hasFeature.js';
8
8
  import { FEATURES } from '../../lib/constants.js';
9
- import { uiBetaTag } from '../../lib/ui/index.js';
10
9
  const command = ['setup'];
11
- const describe = uiBetaTag(commands.mcp.setup.describe, false);
10
+ const describe = commands.mcp.setup.describe;
12
11
  async function handler(args) {
13
12
  const { derivedAccountId } = args;
14
13
  const hasMcpAccess = await hasFeature(derivedAccountId, FEATURES.MCP_ACCESS);
package/commands/mcp.js CHANGED
@@ -2,9 +2,8 @@ import startCommand from './mcp/start.js';
2
2
  import setupCommand from './mcp/setup.js';
3
3
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
4
4
  import { commands } from '../lang/en.js';
5
- import { uiBetaTag } from '../lib/ui/index.js';
6
5
  const command = 'mcp';
7
- const describe = uiBetaTag(commands.mcp.describe, false);
6
+ const describe = commands.mcp.describe;
8
7
  function mcpBuilder(yargs) {
9
8
  yargs.command(startCommand).command(setupCommand).demandCommand(1, '');
10
9
  return yargs;
@@ -1,4 +1,5 @@
1
- import { commands } from '../lang/en.js';
1
+ import { pkg } from '../lib/jsonLoader.js';
2
+ import { commands, lib } from '../lang/en.js';
2
3
  import deploy from './project/deploy.js';
3
4
  import create from './project/create.js';
4
5
  import upload from './project/upload.js';
@@ -17,9 +18,29 @@ import profile from './project/profile.js';
17
18
  import projectValidate from './project/validate.js';
18
19
  import list from './project/list.js';
19
20
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
21
+ import { getProjectConfig } from '../lib/projects/config.js';
22
+ import { isUnsupportedPlatformVersion, LATEST_SUPPORTED_PLATFORM_VERSION, } from '../lib/projects/platformVersion.js';
23
+ import { uiLogger } from '../lib/ui/logger.js';
24
+ import { debugError } from '../lib/errorHandlers/index.js';
20
25
  const command = ['project', 'projects'];
21
26
  const describe = commands.project.describe;
27
+ // Warn users when they are interacting with a version of projects that this version of
28
+ // the CLI is not officially compatible with
29
+ async function validatePlatformVersion() {
30
+ try {
31
+ const { projectConfig } = await getProjectConfig();
32
+ if (isUnsupportedPlatformVersion(projectConfig?.platformVersion)) {
33
+ uiLogger.warn(lib.projects.platformVersion.unsupported(pkg.version, LATEST_SUPPORTED_PLATFORM_VERSION, projectConfig?.platformVersion));
34
+ uiLogger.log('');
35
+ }
36
+ }
37
+ catch (error) {
38
+ // Silently fail. We don't want this to interrupt command execution
39
+ debugError(error);
40
+ }
41
+ }
22
42
  function projectBuilder(yargs) {
43
+ yargs.middleware([validatePlatformVersion]);
23
44
  yargs
24
45
  .command(create)
25
46
  .command(add)
package/lang/en.d.ts CHANGED
@@ -34,6 +34,15 @@ export declare const commands: {
34
34
  installingDependenciesIn: (installPath: string) => string;
35
35
  createdProjectSuccess: (projectName: string, projectDest: string) => string;
36
36
  pressEnterToContinueDeploy: (accountName: string) => string;
37
+ uploadingProject: string;
38
+ uploadSuccess: string;
39
+ appDeployedReady: string;
40
+ appConfigDetails: string;
41
+ distribution: string;
42
+ authType: string;
43
+ checkOutConfig: (configPath: string) => string;
44
+ pressEnterToInstall: (accountName: string) => string;
45
+ pressKeyToExit: string;
37
46
  prompts: {
38
47
  selectOptionV2: string;
39
48
  options: {
@@ -75,10 +84,15 @@ export declare const commands: {
75
84
  uploadingProject: string;
76
85
  uploadSuccess: string;
77
86
  developerOverviewLink: string;
87
+ initialUploadMessage: string;
78
88
  };
79
89
  errors: {
80
90
  uploadFailed: string;
81
91
  configFileNotFound: string;
92
+ noAppsFound: string;
93
+ uploadActionFailed: string;
94
+ buildOrDeployFailed: string;
95
+ failedToUploadAndDeploy: string;
82
96
  };
83
97
  };
84
98
  completion: {
@@ -154,6 +168,7 @@ export declare const commands: {
154
168
  };
155
169
  };
156
170
  promptMessage: string;
171
+ authenticateNewAccount: string;
157
172
  success: {
158
173
  defaultAccountUpdated: (accountName: string) => string;
159
174
  };
@@ -1255,6 +1270,7 @@ export declare const commands: {
1255
1270
  codexNotFound: string;
1256
1271
  codexInstallFailed: string;
1257
1272
  configuringCursor: string;
1273
+ cursorNotFound: string;
1258
1274
  failedToConfigureCursor: string;
1259
1275
  configuredCursor: string;
1260
1276
  configuringGemini: string;
@@ -1263,6 +1279,7 @@ export declare const commands: {
1263
1279
  geminiInstallFailed: string;
1264
1280
  alreadyInstalled: string;
1265
1281
  configuringWindsurf: string;
1282
+ windsurfNotFound: string;
1266
1283
  failedToConfigureWindsurf: string;
1267
1284
  configuredWindsurf: string;
1268
1285
  configuringVsCode: string;
@@ -1642,6 +1659,7 @@ export declare const commands: {
1642
1659
  noFunctionWithName: (name: string) => string;
1643
1660
  functionNotDeployed: (name: string) => string;
1644
1661
  projectLogsManagerNotInitialized: string;
1662
+ noDeployedBuild: string;
1645
1663
  generic: string;
1646
1664
  };
1647
1665
  logs: {
@@ -3093,13 +3111,16 @@ export declare const lib: {
3093
3111
  };
3094
3112
  validateProjectConfig: {
3095
3113
  configNotFound: string;
3096
- configMissingFields: string;
3114
+ configMissingFields: (missingFields: string[]) => string;
3097
3115
  srcDirNotFound: (srcDir: string, projectDir: string) => string;
3098
3116
  srcOutsideProjectDir: (projectConfig: string, srcDir: string) => string;
3099
3117
  };
3100
3118
  getProjectConfig: {
3101
3119
  error: string;
3102
3120
  };
3121
+ platformVersion: {
3122
+ unsupported: (currentCliVersion: string, latestSupported: string, platformVersion?: string) => string;
3123
+ };
3103
3124
  ensureProjectExists: {
3104
3125
  createPrompt: (projectName: string, accountIdentifier: string) => string;
3105
3126
  createPromptUpload: (projectName: string, accountIdentifier: string) => string;
package/lang/en.js CHANGED
@@ -42,6 +42,15 @@ export const commands = {
42
42
  installingDependenciesIn: (installPath) => `Installing dependencies in ${installPath}`,
43
43
  createdProjectSuccess: (projectName, projectDest) => `[SUCCESS] Project ${chalk.bold(projectName)} was successfully created in ${projectDest}`,
44
44
  pressEnterToContinueDeploy: (accountName) => `Press ${chalk.bold('<enter>')} to deploy this project to ${chalk.bold(accountName)} ...`,
45
+ uploadingProject: `Running \`${uiCommandReference('hs project upload', false)}\` to upload and deploy your project...`,
46
+ uploadSuccess: '✓ Upload and deploy complete!',
47
+ appDeployedReady: '🚀 Your new app is now deployed on HubSpot and ready to install!',
48
+ appConfigDetails: 'App configuration details:',
49
+ distribution: 'distribution',
50
+ authType: 'auth type',
51
+ checkOutConfig: (configPath) => `Check out ${chalk.cyan(configPath)} for the full configuration.`,
52
+ pressEnterToInstall: (accountName) => `? Press ${chalk.bold('<enter>')} to continue installing and previewing this app in ${chalk.bold(accountName)}`,
53
+ pressKeyToExit: `Press any key to exit...`,
45
54
  prompts: {
46
55
  selectOptionV2: 'Choose a component type to get started',
47
56
  options: {
@@ -83,10 +92,15 @@ export const commands = {
83
92
  uploadingProject: 'Uploading your project to HubSpot...',
84
93
  uploadSuccess: 'Project uploaded successfully!',
85
94
  developerOverviewLink: 'Open this link to navigate to your HubSpot developer portal',
95
+ initialUploadMessage: 'Initial upload from get-started command',
86
96
  },
87
97
  errors: {
88
98
  uploadFailed: 'Failed to upload project to HubSpot.',
89
99
  configFileNotFound: 'Could not find project configuration for upload.',
100
+ noAppsFound: 'No apps found after deployment.',
101
+ uploadActionFailed: 'Upload failed',
102
+ buildOrDeployFailed: 'Build or deploy failed',
103
+ failedToUploadAndDeploy: 'Failed to upload and deploy project',
90
104
  },
91
105
  },
92
106
  completion: {
@@ -162,6 +176,7 @@ export const commands = {
162
176
  },
163
177
  },
164
178
  promptMessage: 'Select an account to use as the default',
179
+ authenticateNewAccount: '<Authenticate a new account>',
165
180
  success: {
166
181
  defaultAccountUpdated: (accountName) => `Default account updated to "${accountName}"`,
167
182
  },
@@ -1259,32 +1274,34 @@ export const commands = {
1259
1274
  // Claude
1260
1275
  configuringClaudeCode: 'Configuring Claude Code...',
1261
1276
  configuredClaudeCode: 'Configured Claude Code',
1262
- claudeCodeNotFound: 'Claude Code not found - skipping configuration',
1277
+ claudeCodeNotFound: "Claude Code is not installed (missing 'claude' command). Install it and re-run hs mcp setup.",
1263
1278
  claudeCodeInstallFailed: 'Claude Code CLI not working - skipping configuration',
1264
1279
  // Codex
1265
1280
  configuringCodex: 'Configuring Codex...',
1266
1281
  configuredCodex: 'Configured Codex',
1267
- codexNotFound: 'Codex command not found - skipping configuration',
1282
+ codexNotFound: "Codex CLI is not installed (missing 'codex' command). Install it and re-run hs mcp setup.",
1268
1283
  codexInstallFailed: 'Failed to configure Codex',
1269
1284
  // Cursor
1270
1285
  configuringCursor: 'Configuring Cursor...',
1286
+ cursorNotFound: 'Cursor is not installed. Install it and re-run hs mcp setup.',
1271
1287
  failedToConfigureCursor: 'Failed to configure Cursor',
1272
1288
  configuredCursor: 'Configured Cursor',
1273
1289
  // Gemini
1274
1290
  configuringGemini: 'Configuring Gemini CLI...',
1275
1291
  configuredGemini: 'Configured Gemini CLI',
1276
- geminiNotFound: 'Gemini CLI not found - skipping configuration',
1292
+ geminiNotFound: "Gemini CLI is not installed (missing 'gemini' command). Install it and re-run hs mcp setup.",
1277
1293
  geminiInstallFailed: 'Failed to configure Gemini CLI',
1278
1294
  alreadyInstalled: 'HubSpot CLI mcp server already installed, reinstalling',
1279
1295
  // Windsurf
1280
1296
  configuringWindsurf: 'Configuring Windsurf...',
1297
+ windsurfNotFound: 'Windsurf is not installed. Install it and re-run hs mcp setup.',
1281
1298
  failedToConfigureWindsurf: 'Failed to configure Windsurf',
1282
1299
  configuredWindsurf: 'Configured Windsurf',
1283
1300
  // VS Code
1284
1301
  configuringVsCode: 'Configuring VSCode...',
1285
1302
  failedToConfigureVsCode: 'Failed to configure VSCode',
1286
1303
  configuredVsCode: 'Configured VSCode',
1287
- vsCodeNotFound: 'VSCode not found - skipping configuration',
1304
+ vsCodeNotFound: "VSCode CLI is not installed (missing 'code' command). Install it and re-run hs mcp setup.",
1288
1305
  },
1289
1306
  prompts: {
1290
1307
  targets: '[--client] Which tools would you like to add the HubSpot CLI MCP server to?',
@@ -1658,6 +1675,7 @@ export const commands = {
1658
1675
  noFunctionWithName: (name) => `No function with name "${name}"`,
1659
1676
  functionNotDeployed: (name) => `The function with name "${name}" is not deployed`,
1660
1677
  projectLogsManagerNotInitialized: 'Function called on ProjectLogsManager before initialization',
1678
+ noDeployedBuild: 'This project has not been deployed yet. Deploy the project first, then try again.',
1661
1679
  generic: 'Error fetching logs',
1662
1680
  },
1663
1681
  logs: {
@@ -3116,13 +3134,16 @@ export const lib = {
3116
3134
  },
3117
3135
  validateProjectConfig: {
3118
3136
  configNotFound: `Unable to locate a project configuration file. Try running again from a project directory, or run ${uiCommandReference('hs project create')} to create a new project.`,
3119
- configMissingFields: 'The project configuration file is missing required fields.',
3137
+ configMissingFields: (missingFields) => `The project configuration file is missing required field${missingFields.length > 1 ? 's' : ''}: ${missingFields.map(f => chalk.bold(f)).join(', ')}`,
3120
3138
  srcDirNotFound: (srcDir, projectDir) => `Project source directory ${chalk.bold(srcDir)} could not be found in ${chalk.bold(projectDir)}.`,
3121
3139
  srcOutsideProjectDir: (projectConfig, srcDir) => `Invalid value for 'srcDir' in ${projectConfig}: ${chalk.bold(`srcDir: "${srcDir}"`)}\n\t'srcDir' must be a relative path to a folder under the project root, such as "." or "./src"`,
3122
3140
  },
3123
3141
  getProjectConfig: {
3124
3142
  error: 'Could not read from project config',
3125
3143
  },
3144
+ platformVersion: {
3145
+ unsupported: (currentCliVersion, latestSupported, platformVersion) => `Platform version${platformVersion ? ' ' + chalk.bold(platformVersion) : ''} is not officially supported by this version of the CLI (${chalk.bold(currentCliVersion)}).\n\nUpdate your CLI to the latest version with ${uiCommandReference('hs upgrade')} or use a compatible platform version (${chalk.bold(latestSupported)} or earlier).`,
3146
+ },
3126
3147
  ensureProjectExists: {
3127
3148
  createPrompt: (projectName, accountIdentifier) => `The project ${projectName} does not exist in ${accountIdentifier}. Would you like to create it?`,
3128
3149
  createPromptUpload: (projectName, accountIdentifier) => `[--forceCreate] The project ${projectName} does not exist in ${accountIdentifier}. Would you like to create it?`,
@@ -0,0 +1 @@
1
+ export {};