@hubspot/cli 7.7.32-experimental.0 → 7.7.34-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 (118) hide show
  1. package/commands/app/__tests__/install.test.js +47 -0
  2. package/commands/app/install.d.ts +8 -0
  3. package/commands/app/install.js +122 -0
  4. package/commands/app.js +6 -1
  5. package/commands/getStarted.js +7 -20
  6. package/commands/project/__tests__/add.test.js +3 -5
  7. package/commands/project/__tests__/deploy.test.js +3 -2
  8. package/commands/project/add.js +2 -4
  9. package/commands/project/deploy.js +9 -61
  10. package/commands/project/dev/index.js +1 -1
  11. package/commands/project/dev/unifiedFlow.js +4 -1
  12. package/commands/project/upload.d.ts +2 -2
  13. package/commands/project/upload.js +3 -3
  14. package/commands/project/validate.js +1 -1
  15. package/commands/project/watch.js +2 -2
  16. package/commands/testAccount/create.js +3 -0
  17. package/lang/en.d.ts +30 -4
  18. package/lang/en.js +31 -5
  19. package/lib/__tests__/hasFeature.test.js +145 -7
  20. package/lib/__tests__/importData.test.js +1 -1
  21. package/lib/app/migrate.js +9 -2
  22. package/lib/constants.d.ts +2 -0
  23. package/lib/constants.js +2 -0
  24. package/lib/errorHandlers/index.d.ts +4 -0
  25. package/lib/errorHandlers/index.js +1 -1
  26. package/lib/hasFeature.js +6 -0
  27. package/lib/importData.js +1 -1
  28. package/lib/mcp/setup.js +1 -1
  29. package/lib/projectProfiles.d.ts +1 -1
  30. package/lib/projectProfiles.js +10 -2
  31. package/lib/projects/__tests__/AppDevModeInterface.test.js +61 -44
  32. package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
  33. package/lib/projects/__tests__/deploy.test.d.ts +1 -0
  34. package/lib/projects/__tests__/deploy.test.js +164 -0
  35. package/lib/projects/__tests__/platformVersion.test.d.ts +1 -0
  36. package/lib/projects/__tests__/{buildAndDeploy.test.js → platformVersion.test.js} +2 -2
  37. package/lib/projects/add/__tests__/legacyAddComponent.test.js +49 -6
  38. package/lib/projects/add/__tests__/v3AddComponent.test.js +71 -1
  39. package/lib/projects/add/legacyAddComponent.d.ts +1 -1
  40. package/lib/projects/add/legacyAddComponent.js +5 -1
  41. package/lib/projects/add/v3AddComponent.d.ts +1 -0
  42. package/lib/projects/add/v3AddComponent.js +2 -2
  43. package/lib/projects/create/__tests__/v3.test.js +97 -9
  44. package/lib/projects/create/index.js +2 -2
  45. package/lib/projects/create/legacy.js +1 -1
  46. package/lib/projects/create/v3.d.ts +2 -2
  47. package/lib/projects/create/v3.js +35 -12
  48. package/lib/projects/deploy.d.ts +13 -0
  49. package/lib/projects/deploy.js +63 -0
  50. package/lib/projects/localDev/AppDevModeInterface.d.ts +0 -2
  51. package/lib/projects/localDev/AppDevModeInterface.js +65 -36
  52. package/lib/projects/localDev/DevServerManagerV2.js +1 -0
  53. package/lib/projects/localDev/LocalDevProcess.js +3 -1
  54. package/lib/projects/localDev/LocalDevState.d.ts +5 -2
  55. package/lib/projects/localDev/LocalDevState.js +9 -1
  56. package/lib/projects/localDev/helpers/project.d.ts +2 -2
  57. package/lib/projects/localDev/helpers/project.js +6 -7
  58. package/lib/projects/platformVersion.d.ts +1 -0
  59. package/lib/projects/platformVersion.js +10 -0
  60. package/lib/projects/{buildAndDeploy.d.ts → pollProjectBuildAndDeploy.d.ts} +0 -1
  61. package/lib/projects/{buildAndDeploy.js → pollProjectBuildAndDeploy.js} +0 -10
  62. package/lib/projects/structure.d.ts +2 -2
  63. package/lib/projects/upload.d.ts +2 -1
  64. package/lib/projects/upload.js +2 -1
  65. package/lib/projects/urls.d.ts +1 -0
  66. package/lib/projects/urls.js +3 -0
  67. package/lib/prompts/__tests__/projectAddPrompt.test.d.ts +1 -0
  68. package/lib/prompts/__tests__/projectAddPrompt.test.js +143 -0
  69. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.d.ts +1 -0
  70. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.js +160 -0
  71. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +1 -0
  72. package/lib/prompts/importDataFilePathPrompt.js +4 -2
  73. package/lib/prompts/installAppPrompt.d.ts +6 -1
  74. package/lib/prompts/installAppPrompt.js +6 -1
  75. package/lib/prompts/projectAddPrompt.js +1 -1
  76. package/lib/prompts/selectProjectTemplatePrompt.js +1 -1
  77. package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
  78. package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
  79. package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
  80. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +32 -0
  81. package/mcp-server/tools/cms/HsFunctionLogsTool.js +76 -0
  82. package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
  83. package/mcp-server/tools/cms/HsListTool.js +1 -1
  84. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -1
  85. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -1
  86. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -1
  87. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.d.ts +1 -0
  88. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +183 -0
  89. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -1
  90. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
  91. package/mcp-server/tools/index.js +2 -0
  92. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
  93. package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
  94. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  95. package/mcp-server/tools/project/CreateProjectTool.js +5 -5
  96. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  97. package/mcp-server/tools/project/DocFetchTool.js +2 -2
  98. package/mcp-server/tools/project/DocsSearchTool.js +2 -2
  99. package/mcp-server/tools/project/GetConfigValuesTool.js +4 -4
  100. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  101. package/mcp-server/tools/project/UploadProjectTools.js +2 -2
  102. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  103. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
  104. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
  105. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
  106. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
  107. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +2 -2
  108. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +1 -1
  109. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
  110. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
  111. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
  112. package/mcp-server/tools/project/constants.d.ts +1 -1
  113. package/mcp-server/tools/project/constants.js +14 -6
  114. package/package.json +3 -3
  115. package/types/LocalDev.d.ts +2 -1
  116. package/types/Projects.d.ts +1 -0
  117. package/types/Yargs.d.ts +1 -1
  118. /package/{lib/projects/__tests__/buildAndDeploy.test.d.ts → commands/app/__tests__/install.test.d.ts} +0 -0
@@ -0,0 +1,143 @@
1
+ import { Separator } from '@inquirer/prompts';
2
+ import { projectAddPromptV3 } from '../projectAddPrompt.js';
3
+ import { promptUser } from '../promptUtils.js';
4
+ vi.mock('../promptUtils');
5
+ const mockedPromptUser = vi.mocked(promptUser);
6
+ describe('lib/prompts/projectAddPrompt', () => {
7
+ const mockComponentTemplate = {
8
+ label: 'Test Module',
9
+ path: 'test-module',
10
+ type: 'module',
11
+ supportedAuthTypes: ['oauth'],
12
+ supportedDistributions: ['private'],
13
+ };
14
+ const mockComponentTemplateWithCliSelector = {
15
+ label: 'Workflow Action Tool',
16
+ path: 'workflow-action-tool',
17
+ type: 'workflow-action',
18
+ cliSelector: 'workflow-action-tool',
19
+ supportedAuthTypes: ['oauth'],
20
+ supportedDistributions: ['private'],
21
+ };
22
+ describe('projectAddPromptV3()', () => {
23
+ beforeEach(() => {
24
+ // Mock returns empty result, logic will use selectedComponents when selectedFeatures provided
25
+ mockedPromptUser.mockResolvedValue({});
26
+ });
27
+ it('should select component based on cliSelector when provided', async () => {
28
+ const templateChoice = {
29
+ name: 'Workflow Action Tool',
30
+ value: mockComponentTemplateWithCliSelector,
31
+ };
32
+ const components = [templateChoice];
33
+ const selectedFeatures = ['workflow-action-tool'];
34
+ const result = await projectAddPromptV3(components, selectedFeatures);
35
+ expect(result.componentTemplate).toEqual([
36
+ mockComponentTemplateWithCliSelector,
37
+ ]);
38
+ expect(mockedPromptUser).toHaveBeenCalledWith([
39
+ expect.objectContaining({
40
+ name: 'componentTemplate',
41
+ when: false, // selectedFeatures provided, so skip prompt
42
+ }),
43
+ ]);
44
+ });
45
+ it('should select component based on type when cliSelector not provided', async () => {
46
+ const templateChoice = {
47
+ name: 'Test Module',
48
+ value: mockComponentTemplate,
49
+ };
50
+ const components = [templateChoice];
51
+ const selectedFeatures = ['module'];
52
+ const result = await projectAddPromptV3(components, selectedFeatures);
53
+ expect(result.componentTemplate).toEqual([mockComponentTemplate]);
54
+ expect(mockedPromptUser).toHaveBeenCalledWith([
55
+ expect.objectContaining({
56
+ name: 'componentTemplate',
57
+ when: false, // selectedFeatures provided and selectedComponents found
58
+ }),
59
+ ]);
60
+ });
61
+ it('should prefer cliSelector over type when both are available', async () => {
62
+ const templateChoice = {
63
+ name: 'Workflow Action Tool',
64
+ value: mockComponentTemplateWithCliSelector,
65
+ };
66
+ const components = [templateChoice];
67
+ const selectedFeatures = ['workflow-action-tool']; // matches cliSelector
68
+ const result = await projectAddPromptV3(components, selectedFeatures);
69
+ expect(result.componentTemplate).toEqual([
70
+ mockComponentTemplateWithCliSelector,
71
+ ]);
72
+ });
73
+ it('should not select component when neither cliSelector nor type matches', async () => {
74
+ const templateChoice = {
75
+ name: 'Test Module',
76
+ value: mockComponentTemplate,
77
+ };
78
+ const components = [templateChoice];
79
+ const selectedFeatures = ['non-matching-feature'];
80
+ mockedPromptUser.mockResolvedValue({ componentTemplate: [] });
81
+ const result = await projectAddPromptV3(components, selectedFeatures);
82
+ expect(result.componentTemplate).toEqual([]);
83
+ });
84
+ it('should throw error when selected feature component is disabled', async () => {
85
+ const disabledTemplateChoice = {
86
+ name: 'Disabled Component',
87
+ value: mockComponentTemplateWithCliSelector,
88
+ disabled: 'Component is disabled for testing',
89
+ };
90
+ const components = [disabledTemplateChoice];
91
+ const selectedFeatures = ['workflow-action-tool'];
92
+ await expect(projectAddPromptV3(components, selectedFeatures)).rejects.toThrow(/Cannot.*feature.*workflow-action/);
93
+ });
94
+ it('should handle multiple components with mixed cliSelector availability', async () => {
95
+ const choice1 = {
96
+ name: 'Test Module',
97
+ value: mockComponentTemplate,
98
+ };
99
+ const choice2 = {
100
+ name: 'Workflow Action Tool',
101
+ value: mockComponentTemplateWithCliSelector,
102
+ };
103
+ const components = [choice1, choice2];
104
+ const selectedFeatures = ['module', 'workflow-action-tool'];
105
+ const result = await projectAddPromptV3(components, selectedFeatures);
106
+ expect(result.componentTemplate).toEqual([
107
+ mockComponentTemplate,
108
+ mockComponentTemplateWithCliSelector,
109
+ ]);
110
+ });
111
+ it('should skip Separator instances when processing components', async () => {
112
+ const separator = new Separator();
113
+ const templateChoice = {
114
+ name: 'Test Module',
115
+ value: mockComponentTemplate,
116
+ };
117
+ const components = [separator, templateChoice];
118
+ const selectedFeatures = ['module'];
119
+ const result = await projectAddPromptV3(components, selectedFeatures);
120
+ expect(result.componentTemplate).toEqual([mockComponentTemplate]);
121
+ });
122
+ it('should prompt user when no selectedFeatures provided', async () => {
123
+ const templateChoice = {
124
+ name: 'Test Module',
125
+ value: mockComponentTemplate,
126
+ };
127
+ const components = [templateChoice];
128
+ const selectedFeatures = undefined;
129
+ mockedPromptUser.mockResolvedValue({
130
+ componentTemplate: [mockComponentTemplate],
131
+ });
132
+ const result = await projectAddPromptV3(components, selectedFeatures);
133
+ expect(mockedPromptUser).toHaveBeenCalledWith([
134
+ expect.objectContaining({
135
+ name: 'componentTemplate',
136
+ type: 'checkbox',
137
+ choices: components,
138
+ }),
139
+ ]);
140
+ expect(result.componentTemplate).toEqual([mockComponentTemplate]);
141
+ });
142
+ });
143
+ });
@@ -0,0 +1,160 @@
1
+ import { Separator } from '@inquirer/prompts';
2
+ import { selectProjectTemplatePrompt } from '../selectProjectTemplatePrompt.js';
3
+ import { promptUser } from '../promptUtils.js';
4
+ vi.mock('../promptUtils');
5
+ const mockedPromptUser = vi.mocked(promptUser);
6
+ describe('lib/prompts/selectProjectTemplatePrompt', () => {
7
+ const mockComponentTemplate = {
8
+ label: 'Test Module',
9
+ path: 'test-module',
10
+ type: 'module',
11
+ supportedAuthTypes: ['oauth'],
12
+ supportedDistributions: ['private'],
13
+ };
14
+ const mockComponentTemplateWithCliSelector = {
15
+ label: 'Workflow Action Tool',
16
+ path: 'workflow-action-tool',
17
+ type: 'workflow-action',
18
+ cliSelector: 'workflow-action-tool',
19
+ supportedAuthTypes: ['oauth'],
20
+ supportedDistributions: ['private'],
21
+ };
22
+ beforeEach(() => {
23
+ mockedPromptUser.mockResolvedValue({});
24
+ });
25
+ describe('selectProjectTemplatePrompt with component templates', () => {
26
+ it('should select component based on cliSelector when provided', async () => {
27
+ const templateChoice = {
28
+ name: 'Workflow Action Tool',
29
+ value: mockComponentTemplateWithCliSelector,
30
+ };
31
+ const componentTemplates = [templateChoice];
32
+ const promptOptions = {
33
+ features: ['workflow-action-tool'],
34
+ };
35
+ const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
36
+ expect(result.componentTemplates).toEqual([
37
+ mockComponentTemplateWithCliSelector,
38
+ ]);
39
+ expect(mockedPromptUser).toHaveBeenCalledWith([
40
+ expect.objectContaining({ name: 'projectTemplate' }),
41
+ expect.objectContaining({ name: 'componentTemplates' }),
42
+ ]);
43
+ });
44
+ it('should select component based on type when cliSelector not provided', async () => {
45
+ const templateChoice = {
46
+ name: 'Test Module',
47
+ value: mockComponentTemplate,
48
+ };
49
+ const componentTemplates = [templateChoice];
50
+ const promptOptions = {
51
+ features: ['module'],
52
+ };
53
+ const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
54
+ expect(result.componentTemplates).toEqual([mockComponentTemplate]);
55
+ });
56
+ it('should prefer cliSelector over type when both are available', async () => {
57
+ const templateChoice = {
58
+ name: 'Workflow Action Tool',
59
+ value: mockComponentTemplateWithCliSelector,
60
+ };
61
+ const componentTemplates = [templateChoice];
62
+ const promptOptions = {
63
+ features: ['workflow-action-tool'], // matches cliSelector, not type
64
+ };
65
+ const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
66
+ expect(result.componentTemplates).toEqual([
67
+ mockComponentTemplateWithCliSelector,
68
+ ]);
69
+ });
70
+ it('should not select component when neither cliSelector nor type matches', async () => {
71
+ const templateChoice = {
72
+ name: 'Test Module',
73
+ value: mockComponentTemplate,
74
+ };
75
+ const componentTemplates = [templateChoice];
76
+ const promptOptions = {
77
+ features: ['non-matching-feature'],
78
+ };
79
+ const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
80
+ expect(result.componentTemplates).toEqual([]);
81
+ });
82
+ it('should throw error when selected feature component is disabled', async () => {
83
+ const disabledTemplateChoice = {
84
+ name: 'Disabled Component',
85
+ value: mockComponentTemplateWithCliSelector,
86
+ disabled: 'Component is disabled for testing',
87
+ };
88
+ const componentTemplates = [disabledTemplateChoice];
89
+ const promptOptions = {
90
+ features: ['workflow-action-tool'],
91
+ };
92
+ await expect(selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates)).rejects.toThrow(/Cannot create project with template.*workflow-action/);
93
+ });
94
+ it('should handle multiple components with mixed cliSelector availability', async () => {
95
+ const choice1 = {
96
+ name: 'Test Module',
97
+ value: mockComponentTemplate,
98
+ };
99
+ const choice2 = {
100
+ name: 'Workflow Action Tool',
101
+ value: mockComponentTemplateWithCliSelector,
102
+ };
103
+ const componentTemplates = [choice1, choice2];
104
+ const promptOptions = {
105
+ features: ['module', 'workflow-action-tool'],
106
+ };
107
+ const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
108
+ expect(result.componentTemplates).toEqual([
109
+ mockComponentTemplate,
110
+ mockComponentTemplateWithCliSelector,
111
+ ]);
112
+ });
113
+ it('should skip Separator instances when processing components', async () => {
114
+ const separator = new Separator();
115
+ const templateChoice = {
116
+ name: 'Test Module',
117
+ value: mockComponentTemplate,
118
+ };
119
+ const componentTemplates = [separator, templateChoice];
120
+ const promptOptions = {
121
+ features: ['module'],
122
+ };
123
+ const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
124
+ expect(result.componentTemplates).toEqual([mockComponentTemplate]);
125
+ });
126
+ it('should prompt user when no features provided', async () => {
127
+ const templateChoice = {
128
+ name: 'Test Module',
129
+ value: mockComponentTemplate,
130
+ };
131
+ const componentTemplates = [templateChoice];
132
+ const promptOptions = {};
133
+ mockedPromptUser.mockResolvedValue({
134
+ componentTemplates: [mockComponentTemplate],
135
+ });
136
+ const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
137
+ expect(mockedPromptUser).toHaveBeenCalledWith([
138
+ expect.objectContaining({ name: 'projectTemplate' }),
139
+ expect.objectContaining({
140
+ name: 'componentTemplates',
141
+ type: 'checkbox',
142
+ choices: componentTemplates,
143
+ }),
144
+ ]);
145
+ expect(result.componentTemplates).toEqual([mockComponentTemplate]);
146
+ });
147
+ it('should handle empty componentTemplates selection', async () => {
148
+ const templateChoice = {
149
+ name: 'Test Module',
150
+ value: mockComponentTemplate,
151
+ };
152
+ const componentTemplates = [templateChoice];
153
+ const promptOptions = {
154
+ features: ['non-matching-feature'],
155
+ };
156
+ const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
157
+ expect(result.componentTemplates).toEqual([]);
158
+ });
159
+ });
160
+ });
@@ -101,6 +101,7 @@ export async function createDeveloperTestAccountConfigPrompt(args = {}, supportF
101
101
  type: 'checkbox',
102
102
  pageSize: 13,
103
103
  choices: TEST_ACCOUNT_TIERS,
104
+ loop: false,
104
105
  validate: choices => {
105
106
  if (choices?.length < Object.keys(hubs).length) {
106
107
  return lib.prompts.createDeveloperTestAccountConfigPrompt.errors
@@ -1,8 +1,8 @@
1
1
  import { validateImportRequestFile } from '@hubspot/local-dev-lib/crm';
2
2
  import { promptUser } from './promptUtils.js';
3
3
  import { lib } from '../../lang/en.js';
4
- import { logError } from '../errorHandlers/index.js';
5
4
  import { uiLogger } from '../ui/logger.js';
5
+ import { isErrorWithMessageOrReason } from '../errorHandlers/index.js';
6
6
  export async function importDataFilePathPrompt() {
7
7
  uiLogger.log(lib.prompts.importDataFilePathPrompt.promptContext);
8
8
  const { filePath } = await promptUser({
@@ -15,7 +15,9 @@ export async function importDataFilePathPrompt() {
15
15
  return true;
16
16
  }
17
17
  catch (error) {
18
- logError(error);
18
+ if (isErrorWithMessageOrReason(error) && error.message) {
19
+ return error.message;
20
+ }
19
21
  return false;
20
22
  }
21
23
  },
@@ -1,2 +1,7 @@
1
- export declare function installAppBrowserPrompt(installUrl: string, isReinstall?: boolean): Promise<void>;
1
+ export declare function installAppBrowserPrompt(installUrl: string, isReinstall?: boolean, staticAuthInstallOptions?: {
2
+ testingAccountId: number;
3
+ projectAccountId: number;
4
+ projectName: string;
5
+ appUid: string;
6
+ }): Promise<void>;
2
7
  export declare function installAppAutoPrompt(): Promise<boolean>;
@@ -3,7 +3,7 @@ import { promptUser } from './promptUtils.js';
3
3
  import { EXIT_CODES } from '../enums/exitCodes.js';
4
4
  import { lib } from '../../lang/en.js';
5
5
  import { uiLogger } from '../ui/logger.js';
6
- export async function installAppBrowserPrompt(installUrl, isReinstall = false) {
6
+ export async function installAppBrowserPrompt(installUrl, isReinstall = false, staticAuthInstallOptions) {
7
7
  uiLogger.log('');
8
8
  if (isReinstall) {
9
9
  uiLogger.log(lib.prompts.installAppPrompt.reinstallExplanation);
@@ -11,6 +11,11 @@ export async function installAppBrowserPrompt(installUrl, isReinstall = false) {
11
11
  else {
12
12
  uiLogger.log(lib.prompts.installAppPrompt.explanation);
13
13
  }
14
+ if (staticAuthInstallOptions) {
15
+ const { testingAccountId, projectAccountId, projectName, appUid } = staticAuthInstallOptions;
16
+ uiLogger.log(lib.prompts.installAppPrompt.staticAuthExplanation(projectAccountId, testingAccountId, projectName, appUid));
17
+ uiLogger.log('');
18
+ }
14
19
  const { shouldOpenBrowser } = await promptUser({
15
20
  name: 'shouldOpenBrowser',
16
21
  type: 'confirm',
@@ -51,7 +51,7 @@ export async function projectAddPromptV3(components, selectedFeatures) {
51
51
  if (template instanceof Separator || !template.value) {
52
52
  return;
53
53
  }
54
- if (selectedFeatures?.includes(template.value.type)) {
54
+ if (selectedFeatures?.includes(template.value.cliSelector || template.value.type)) {
55
55
  if (template.disabled) {
56
56
  throw new Error(lib.prompts.projectAddPrompt.errors.cannotAddFeature(template.value.type, template.disabled));
57
57
  }
@@ -13,7 +13,7 @@ export async function selectProjectTemplatePrompt(promptOptions, projectTemplate
13
13
  if (template instanceof Separator || !template.value) {
14
14
  return;
15
15
  }
16
- if (promptOptions.features?.includes(template.value.type)) {
16
+ if (promptOptions.features?.includes(template.value.cliSelector || template.value.type)) {
17
17
  if (template.disabled) {
18
18
  throw new Error(`Cannot create project with template '${template.value.type}'. Reasons: ${template.disabled}`);
19
19
  }
@@ -31,7 +31,7 @@ const inputSchema = {
31
31
  };
32
32
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
33
  const inputSchemaZodObject = z.object({ ...inputSchema });
34
- const toolName = 'create-hubspot-cms-function';
34
+ const toolName = 'create-cms-function';
35
35
  export class HsCreateFunctionTool extends Tool {
36
36
  constructor(mcpServer) {
37
37
  super(mcpServer);
@@ -47,7 +47,7 @@ const inputSchema = {
47
47
  };
48
48
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
49
49
  const inputSchemaZodObject = z.object({ ...inputSchema });
50
- const toolName = 'create-hubspot-cms-module';
50
+ const toolName = 'create-cms-module';
51
51
  export class HsCreateModuleTool extends Tool {
52
52
  constructor(mcpServer) {
53
53
  super(mcpServer);
@@ -23,7 +23,7 @@ const inputSchema = {
23
23
  };
24
24
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
25
  const inputSchemaZodObject = z.object({ ...inputSchema });
26
- const toolName = 'create-hubspot-cms-template';
26
+ const toolName = 'create-cms-template';
27
27
  export class HsCreateTemplateTool extends Tool {
28
28
  constructor(mcpServer) {
29
29
  super(mcpServer);
@@ -0,0 +1,32 @@
1
+ import { TextContentResponse, Tool } from '../../types.js';
2
+ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { z } from 'zod';
4
+ declare const inputSchemaZodObject: z.ZodObject<{
5
+ absoluteCurrentWorkingDirectory: z.ZodString;
6
+ endpoint: z.ZodString;
7
+ account: z.ZodOptional<z.ZodString>;
8
+ latest: z.ZodOptional<z.ZodBoolean>;
9
+ compact: z.ZodOptional<z.ZodBoolean>;
10
+ limit: z.ZodOptional<z.ZodNumber>;
11
+ }, "strip", z.ZodTypeAny, {
12
+ endpoint: string;
13
+ absoluteCurrentWorkingDirectory: string;
14
+ account?: string | undefined;
15
+ limit?: number | undefined;
16
+ compact?: boolean | undefined;
17
+ latest?: boolean | undefined;
18
+ }, {
19
+ endpoint: string;
20
+ absoluteCurrentWorkingDirectory: string;
21
+ account?: string | undefined;
22
+ limit?: number | undefined;
23
+ compact?: boolean | undefined;
24
+ latest?: boolean | undefined;
25
+ }>;
26
+ export type HsFunctionLogsInputSchema = z.infer<typeof inputSchemaZodObject>;
27
+ export declare class HsFunctionLogsTool extends Tool<HsFunctionLogsInputSchema> {
28
+ constructor(mcpServer: McpServer);
29
+ handler({ endpoint, account, latest, compact, limit, absoluteCurrentWorkingDirectory, }: HsFunctionLogsInputSchema): Promise<TextContentResponse>;
30
+ register(): RegisteredTool;
31
+ }
32
+ export {};
@@ -0,0 +1,76 @@
1
+ import { Tool } from '../../types.js';
2
+ import { z } from 'zod';
3
+ import { addFlag } from '../../utils/command.js';
4
+ import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
5
+ import { runCommandInDir } from '../../utils/project.js';
6
+ import { formatTextContents } from '../../utils/content.js';
7
+ import { trackToolUsage } from '../../utils/toolUsageTracking.js';
8
+ const inputSchema = {
9
+ absoluteCurrentWorkingDirectory,
10
+ endpoint: z
11
+ .string()
12
+ .describe('The function endpoint/path to get logs for. Required. Example: "my-function" or "api/my-endpoint" (leading slash will be automatically removed)'),
13
+ account: z
14
+ .string()
15
+ .describe('The HubSpot account id or name from the HubSpot config file to use for the operation.')
16
+ .optional(),
17
+ latest: z
18
+ .boolean()
19
+ .describe('Get only the latest log entry for the function.')
20
+ .optional(),
21
+ compact: z.boolean().describe('Display logs in compact format.').optional(),
22
+ limit: z
23
+ .number()
24
+ .describe('Maximum number of log entries to retrieve.')
25
+ .optional(),
26
+ };
27
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
28
+ const inputSchemaZodObject = z.object({ ...inputSchema });
29
+ const toolName = 'get-cms-serverless-function-logs';
30
+ export class HsFunctionLogsTool extends Tool {
31
+ constructor(mcpServer) {
32
+ super(mcpServer);
33
+ }
34
+ async handler({ endpoint, account, latest, compact, limit, absoluteCurrentWorkingDirectory, }) {
35
+ await trackToolUsage(toolName);
36
+ // Ensure endpoint doesn't start with '/'
37
+ const normalizedEndpoint = endpoint.startsWith('/')
38
+ ? endpoint.slice(1)
39
+ : endpoint;
40
+ let command = `hs logs ${normalizedEndpoint}`;
41
+ if (latest) {
42
+ command = addFlag(command, 'latest', latest);
43
+ }
44
+ if (compact) {
45
+ command = addFlag(command, 'compact', compact);
46
+ }
47
+ if (limit) {
48
+ command = addFlag(command, 'limit', limit);
49
+ }
50
+ if (account) {
51
+ command = addFlag(command, 'account', account);
52
+ }
53
+ try {
54
+ const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
55
+ return formatTextContents(stdout, stderr);
56
+ }
57
+ catch (error) {
58
+ const errorMessage = error instanceof Error ? error.message : String(error);
59
+ return {
60
+ content: [
61
+ {
62
+ type: 'text',
63
+ text: `Error executing hs logs command: ${errorMessage}`,
64
+ },
65
+ ],
66
+ };
67
+ }
68
+ }
69
+ register() {
70
+ return this.mcpServer.registerTool(toolName, {
71
+ title: 'Get HubSpot CMS serverless function logs for an endpoint',
72
+ description: 'Retrieve logs for HubSpot CMS serverless functions. Use this tool to help debug issues with serverless functions by reading the production logs. Supports various options like latest, compact, and limiting results. Use after listing functions with list-cms-serverless-functions to get the endpoint path.',
73
+ inputSchema,
74
+ }, this.handler);
75
+ }
76
+ }
@@ -18,7 +18,7 @@ const inputSchema = {
18
18
  };
19
19
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
20
  const inputSchemaZodObject = z.object({ ...inputSchema });
21
- const toolName = 'list-hubspot-cms-serverless-functions';
21
+ const toolName = 'list-cms-serverless-functions';
22
22
  export class HsListFunctionsTool extends Tool {
23
23
  constructor(mcpServer) {
24
24
  super(mcpServer);
@@ -18,7 +18,7 @@ const inputSchema = {
18
18
  };
19
19
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
20
  const inputSchemaZodObject = z.object({ ...inputSchema });
21
- const toolName = 'list-hubspot-cms-remote-contents';
21
+ const toolName = 'list-cms-remote-contents';
22
22
  export class HsListTool extends Tool {
23
23
  constructor(mcpServer) {
24
24
  super(mcpServer);
@@ -28,7 +28,7 @@ describe('HsCreateFunctionTool', () => {
28
28
  describe('register', () => {
29
29
  it('should register the tool with the MCP server', () => {
30
30
  const result = tool.register();
31
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-hubspot-cms-function', {
31
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-cms-function', {
32
32
  title: 'Create HubSpot CMS Serverless Function',
33
33
  description: `Creates a new HubSpot CMS serverless function using the hs create function command. Functions can be created non-interactively by specifying functionsFolder, filename, and endpointPath. Supports all HTTP methods (${HTTP_METHODS.join(', ')}).`,
34
34
  inputSchema: expect.any(Object),
@@ -27,7 +27,7 @@ describe('HsCreateModuleTool', () => {
27
27
  describe('register', () => {
28
28
  it('should register the tool with the MCP server', () => {
29
29
  const result = tool.register();
30
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-hubspot-cms-module', {
30
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-cms-module', {
31
31
  title: 'Create HubSpot CMS Module',
32
32
  description: 'Creates a new HubSpot CMS module using the hs create module command. Modules can be created non-interactively by specifying moduleLabel and other module options. You can create either HubL or React modules by setting the reactType parameter.',
33
33
  inputSchema: expect.any(Object),
@@ -28,7 +28,7 @@ describe('HsCreateTemplateTool', () => {
28
28
  describe('register', () => {
29
29
  it('should register the tool with the MCP server', () => {
30
30
  const result = tool.register();
31
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-hubspot-cms-template', {
31
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-cms-template', {
32
32
  title: 'Create HubSpot CMS Template',
33
33
  description: `Creates a new HubSpot CMS template using the hs create template command. Templates can be created non-interactively by specifying templateType. Supports all template types including: ${TEMPLATE_TYPES.join(', ')}.`,
34
34
  inputSchema: expect.any(Object),