@hubspot/cli 8.10.0 → 8.12.0-beta.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 (110) hide show
  1. package/commands/account/clean.js +1 -1
  2. package/commands/account/list.js +1 -1
  3. package/commands/account/remove.js +1 -1
  4. package/commands/account/rename.js +1 -1
  5. package/commands/account/use.js +1 -1
  6. package/commands/api.d.ts +4 -1
  7. package/commands/api.js +11 -14
  8. package/commands/hubdb/list.d.ts +1 -1
  9. package/commands/mcp/setup.d.ts +2 -0
  10. package/commands/mcp/setup.js +29 -1
  11. package/commands/project/appInstallStatus.d.ts +2 -1
  12. package/commands/project/appInstallStatus.js +19 -17
  13. package/commands/project/create.js +0 -1
  14. package/commands/project/deploy.d.ts +2 -1
  15. package/commands/project/deploy.js +31 -14
  16. package/commands/project/dev/index.js +18 -64
  17. package/commands/project/info.d.ts +2 -1
  18. package/commands/project/info.js +8 -6
  19. package/commands/project/installApp.d.ts +2 -1
  20. package/commands/project/installApp.js +25 -26
  21. package/commands/project/list.js +1 -1
  22. package/commands/project/profile/add.d.ts +2 -1
  23. package/commands/project/profile/add.js +22 -1
  24. package/commands/project/profile/delete.d.ts +1 -1
  25. package/commands/project/release/create.d.ts +2 -1
  26. package/commands/project/release/create.js +68 -69
  27. package/commands/project/release/info.d.ts +2 -1
  28. package/commands/project/release/info.js +7 -6
  29. package/commands/project/release/list.d.ts +2 -1
  30. package/commands/project/release/list.js +14 -6
  31. package/commands/project/upload.d.ts +2 -1
  32. package/commands/project/upload.js +23 -17
  33. package/commands/project/validate.d.ts +1 -1
  34. package/commands/project.js +2 -2
  35. package/commands/testAccount/create.d.ts +2 -1
  36. package/commands/testAccount/create.js +7 -7
  37. package/commands/testAccount/importData.d.ts +1 -1
  38. package/lang/en.d.ts +32 -28
  39. package/lang/en.js +41 -37
  40. package/lib/commonOpts.js +6 -1
  41. package/lib/constants.d.ts +1 -0
  42. package/lib/constants.js +1 -0
  43. package/lib/jsonOutput.d.ts +109 -0
  44. package/lib/jsonOutput.js +94 -0
  45. package/lib/mcp/setup.d.ts +6 -1
  46. package/lib/mcp/setup.js +29 -18
  47. package/lib/projects/builds.d.ts +2 -0
  48. package/lib/projects/builds.js +22 -0
  49. package/lib/projects/localDev/DevSessionManager.d.ts +2 -2
  50. package/lib/projects/localDev/DevSessionManager.js +4 -30
  51. package/lib/projects/localDev/helpers/account.d.ts +0 -7
  52. package/lib/projects/localDev/helpers/account.js +1 -80
  53. package/lib/projects/release.d.ts +4 -0
  54. package/lib/projects/release.js +90 -0
  55. package/lib/projects/upload.d.ts +1 -1
  56. package/lib/projects/upload.js +4 -8
  57. package/lib/yargs/makeWrappedYargsHandler.d.ts +5 -1
  58. package/lib/yargs/makeWrappedYargsHandler.js +123 -65
  59. package/mcp-server/Tool.d.ts +8 -3
  60. package/mcp-server/Tool.js +58 -32
  61. package/mcp-server/server.js +10 -6
  62. package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +2 -2
  63. package/mcp-server/tools/cms/HsCreateFunctionTool.js +10 -17
  64. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +2 -2
  65. package/mcp-server/tools/cms/HsCreateModuleTool.js +12 -18
  66. package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +2 -2
  67. package/mcp-server/tools/cms/HsCreateTemplateTool.js +8 -10
  68. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +2 -2
  69. package/mcp-server/tools/cms/HsFunctionLogsTool.js +9 -9
  70. package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +2 -2
  71. package/mcp-server/tools/cms/HsListFunctionsTool.js +7 -7
  72. package/mcp-server/tools/cms/HsListTool.d.ts +2 -2
  73. package/mcp-server/tools/cms/HsListTool.js +6 -10
  74. package/mcp-server/tools/index.js +2 -0
  75. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +2 -2
  76. package/mcp-server/tools/project/AddFeatureToProjectTool.js +8 -9
  77. package/mcp-server/tools/project/AuthAccountTool.d.ts +18 -0
  78. package/mcp-server/tools/project/AuthAccountTool.js +78 -0
  79. package/mcp-server/tools/project/CreateProjectTool.d.ts +2 -2
  80. package/mcp-server/tools/project/CreateProjectTool.js +13 -12
  81. package/mcp-server/tools/project/CreateTestAccountTool.d.ts +2 -2
  82. package/mcp-server/tools/project/CreateTestAccountTool.js +14 -15
  83. package/mcp-server/tools/project/DeployProjectTool.d.ts +2 -2
  84. package/mcp-server/tools/project/DeployProjectTool.js +10 -10
  85. package/mcp-server/tools/project/DocFetchTool.js +1 -1
  86. package/mcp-server/tools/project/DocsSearchTool.js +9 -3
  87. package/mcp-server/tools/project/FindProjectsTool.js +1 -1
  88. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +9 -4
  89. package/mcp-server/tools/project/GetApplicationInfoTool.js +9 -4
  90. package/mcp-server/tools/project/GetBuildLogsTool.js +9 -3
  91. package/mcp-server/tools/project/GetBuildStatusTool.js +9 -3
  92. package/mcp-server/tools/project/GetConfigValuesTool.js +9 -3
  93. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  94. package/mcp-server/tools/project/UploadProjectTools.d.ts +2 -2
  95. package/mcp-server/tools/project/UploadProjectTools.js +9 -8
  96. package/mcp-server/tools/project/ValidateProjectTool.d.ts +2 -2
  97. package/mcp-server/tools/project/ValidateProjectTool.js +5 -4
  98. package/mcp-server/utils/command.d.ts +25 -2
  99. package/mcp-server/utils/command.js +67 -17
  100. package/mcp-server/utils/logger.d.ts +3 -0
  101. package/mcp-server/utils/logger.js +30 -0
  102. package/mcp-server/utils/toolUsageTracking.js +8 -1
  103. package/package.json +10 -8
  104. package/types/Yargs.d.ts +3 -1
  105. package/commands/project/dev/deprecatedFlow.d.ts +0 -11
  106. package/commands/project/dev/deprecatedFlow.js +0 -180
  107. package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +0 -43
  108. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +0 -128
  109. package/lib/projects/localDev/LocalDevManager_DEPRECATED.d.ts +0 -64
  110. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +0 -394
@@ -1,6 +1,7 @@
1
1
  import { getConfigAccountIfExists } from '@hubspot/local-dev-lib/config';
2
2
  import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
3
3
  import { makeWrappedYargsHandler } from '../../lib/yargs/makeWrappedYargsHandler.js';
4
+ import { InstallAppSchema, } from '../../lib/jsonOutput.js';
4
5
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
5
6
  import { uiLogger } from '../../lib/ui/logger.js';
6
7
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
@@ -13,7 +14,7 @@ const command = 'install-app';
13
14
  const describe = commands.project.installApp.describe;
14
15
  const verboseDescribe = commands.project.installApp.verboseDescribe;
15
16
  async function handler(args) {
16
- const { derivedAccountId, formatOutputAsJson, force, profile: profileOption, useEnv: useEnvOption, exit, } = args;
17
+ const { derivedAccountId, formatOutputAsJson, force, profile: profileOption, useEnv: useEnvOption, exit, addJsonOutput, } = args;
17
18
  const { projectConfig, projectDir } = await getProjectConfig();
18
19
  if (!projectConfig || !projectDir) {
19
20
  uiLogger.error(commands.project.installApp.errors.noProjectConfig);
@@ -60,18 +61,16 @@ async function handler(args) {
60
61
  const needsReinstall = !isInstalledWithScopeGroups && previouslyAuthorizedScopeGroups.length > 0;
61
62
  const installationState = getAppInstallationState(isInstalledWithScopeGroups, previouslyAuthorizedScopeGroups);
62
63
  if (isInstalledWithScopeGroups) {
63
- if (formatOutputAsJson) {
64
- uiLogger.json({
65
- appId,
66
- appUid: appNode.uid,
67
- accountId: installAccountId,
68
- projectId,
69
- installationState,
70
- installed: true,
71
- reinstalled: false,
72
- });
73
- }
74
- else {
64
+ addJsonOutput({
65
+ appId,
66
+ appUid: appNode.uid,
67
+ accountId: installAccountId,
68
+ projectId,
69
+ installationState,
70
+ installed: true,
71
+ reinstalled: false,
72
+ });
73
+ if (!formatOutputAsJson) {
75
74
  uiLogger.success(commands.project.installApp.alreadyInstalled(appNode.config.name, installAccountId));
76
75
  }
77
76
  return exit(EXIT_CODES.SUCCESS);
@@ -117,18 +116,16 @@ async function handler(args) {
117
116
  if (!installed) {
118
117
  return exit(EXIT_CODES.ERROR);
119
118
  }
120
- if (formatOutputAsJson) {
121
- uiLogger.json({
122
- appId,
123
- appUid: appNode.uid,
124
- accountId: installAccountId,
125
- projectId,
126
- installationState: APP_INSTALLATION_STATES.INSTALLED,
127
- installed: true,
128
- reinstalled: needsReinstall,
129
- });
130
- }
131
- else {
119
+ addJsonOutput({
120
+ appId,
121
+ appUid: appNode.uid,
122
+ accountId: installAccountId,
123
+ projectId,
124
+ installationState: APP_INSTALLATION_STATES.INSTALLED,
125
+ installed: true,
126
+ reinstalled: needsReinstall,
127
+ });
128
+ if (!formatOutputAsJson) {
132
129
  uiLogger.success(commands.project.installApp.success(appNode.config.name, installAccountId));
133
130
  }
134
131
  return exit(EXIT_CODES.SUCCESS);
@@ -175,7 +172,9 @@ const builder = makeYargsBuilder(projectInstallAppBuilder, command, verboseDescr
175
172
  const projectInstallAppCommand = {
176
173
  command,
177
174
  describe,
178
- handler: makeWrappedYargsHandler('project-install-app', handler),
175
+ handler: makeWrappedYargsHandler('project-install-app', handler, {
176
+ jsonOutputSchema: InstallAppSchema,
177
+ }),
179
178
  builder,
180
179
  };
181
180
  export default projectInstallAppCommand;
@@ -57,7 +57,7 @@ const builder = makeYargsBuilder(projectListBuilder, command, describe, {
57
57
  const projectListCommand = {
58
58
  command,
59
59
  describe,
60
- handler: makeWrappedYargsHandler('projects-list', handler),
60
+ handler: makeWrappedYargsHandler('project-list', handler),
61
61
  builder,
62
62
  };
63
63
  export default projectListCommand;
@@ -1,7 +1,8 @@
1
1
  import { YargsCommandModule, CommonArgs } from '../../../types/Yargs.js';
2
- type ProjectProfileAddArgs = CommonArgs & {
2
+ export type ProjectProfileAddArgs = CommonArgs & {
3
3
  name?: string;
4
4
  targetAccount?: number;
5
+ copyFrom?: string;
5
6
  };
6
7
  declare const projectProfileAddCommand: YargsCommandModule<unknown, ProjectProfileAddArgs>;
7
8
  export default projectProfileAddCommand;
@@ -157,7 +157,20 @@ async function handler(args) {
157
157
  accountId: targetAccountId,
158
158
  variables: {},
159
159
  };
160
- const profileToCopyVariablesFrom = await selectProfileToCopyVariablesFrom(existingProfiles);
160
+ let profileToCopyVariablesFrom;
161
+ if (args.copyFrom) {
162
+ if (existingProfiles.includes(args.copyFrom)) {
163
+ profileToCopyVariablesFrom = args.copyFrom;
164
+ }
165
+ else {
166
+ uiLogger.error(commands.project.profile.add.errors.copyFromProfileNotFound(args.copyFrom));
167
+ return exit(EXIT_CODES.ERROR);
168
+ }
169
+ }
170
+ else {
171
+ profileToCopyVariablesFrom =
172
+ await selectProfileToCopyVariablesFrom(existingProfiles);
173
+ }
161
174
  if (profileToCopyVariablesFrom) {
162
175
  try {
163
176
  const profileToCopyFileContent = loadHsProfileFile(projectSourceDir, profileToCopyVariablesFrom);
@@ -190,8 +203,16 @@ function projectProfileAddBuilder(yargs) {
190
203
  describe: commands.project.profile.add.options.targetAccount,
191
204
  type: 'number',
192
205
  });
206
+ yargs.option('copy-from', {
207
+ describe: commands.project.profile.add.options.copyFrom,
208
+ type: 'string',
209
+ });
193
210
  yargs.example([
194
211
  ['$0 project profile add qa', commands.project.profile.add.example],
212
+ [
213
+ '$0 project profile add qa --target-account 123 --copy-from staging',
214
+ commands.project.profile.add.exampleCopyFrom,
215
+ ],
195
216
  ]);
196
217
  return yargs;
197
218
  }
@@ -1,5 +1,5 @@
1
1
  import { YargsCommandModule, CommonArgs } from '../../../types/Yargs.js';
2
- type ProjectProfileDeleteArgs = CommonArgs & {
2
+ export type ProjectProfileDeleteArgs = CommonArgs & {
3
3
  name?: string;
4
4
  };
5
5
  declare const projectProfileDeleteCommand: YargsCommandModule<unknown, ProjectProfileDeleteArgs>;
@@ -1,5 +1,6 @@
1
1
  import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../../types/Yargs.js';
2
- export type ProjectReleaseCreateArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
2
+ import { ReleaseJsonOutput } from '../../../lib/jsonOutput.js';
3
+ export type ProjectReleaseCreateArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs<ReleaseJsonOutput> & {
3
4
  build?: number;
4
5
  force: boolean;
5
6
  };
@@ -1,6 +1,8 @@
1
- import { fetchProject, getBuildStatus, } from '@hubspot/local-dev-lib/api/projects';
2
- import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
1
+ import { handleProjectUpload } from '../../../lib/projects/upload.js';
2
+ import { pollProjectBuildAndDeploy } from '../../../lib/projects/pollProjectBuildAndDeploy.js';
3
+ import { resolveBuildId, validateBuildForRelease, executeRelease, } from '../../../lib/projects/release.js';
3
4
  import { logError, ApiErrorContext } from '../../../lib/errorHandlers/index.js';
5
+ import { isPromptExitError } from '../../../lib/errors/PromptExitError.js';
4
6
  import { getProjectConfig, validateProjectConfig, } from '../../../lib/projects/config.js';
5
7
  import { confirmPrompt } from '../../../lib/prompts/promptUtils.js';
6
8
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
@@ -8,64 +10,23 @@ import { uiLogger } from '../../../lib/ui/logger.js';
8
10
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
9
11
  import { commands } from '../../../lang/en.js';
10
12
  import { makeWrappedYargsHandler } from '../../../lib/yargs/makeWrappedYargsHandler.js';
11
- import { createRelease } from '../../../api/releases.js';
13
+ import { ReleaseSchema, mapReleaseToJsonOutput, } from '../../../lib/jsonOutput.js';
12
14
  const command = 'create';
13
15
  // const describe = commands.project.release.create.describe;
14
16
  const describe = undefined;
15
17
  // const verboseDescribe = commands.project.release.create.verboseDescribe;
16
18
  const verboseDescribe = undefined;
17
- async function resolveBuildId(accountId, projectName, buildOption) {
18
- const { data: { deployedBuildId }, } = await fetchProject(accountId, projectName);
19
- if (buildOption) {
20
- return buildOption;
21
- }
22
- if (!deployedBuildId) {
23
- throw new Error(commands.project.release.create.errors.noDeployedBuild);
24
- }
25
- return deployedBuildId;
26
- }
27
- async function validateBuild(accountId, projectName, buildId) {
28
- try {
29
- await getBuildStatus(accountId, projectName, buildId);
30
- }
31
- catch (e) {
32
- if (isHubSpotHttpError(e) && e.status === 404) {
33
- uiLogger.error(commands.project.release.create.errors.buildNotFound(buildId, projectName));
34
- }
35
- else {
36
- logError(e, new ApiErrorContext({
37
- accountId,
38
- request: 'project release create',
39
- }));
40
- }
41
- throw e;
42
- }
43
- }
44
- async function executeRelease(accountId, projectName, buildId, formatOutputAsJson) {
45
- try {
46
- const { data: release } = await createRelease(accountId, projectName, buildId);
47
- if (formatOutputAsJson) {
48
- uiLogger.json(release);
49
- }
50
- else {
51
- uiLogger.success(commands.project.release.create.success(release.releaseTag, release.buildId));
52
- }
53
- }
54
- catch (e) {
55
- if (isHubSpotHttpError(e) && e.status === 422) {
56
- uiLogger.error(commands.project.release.create.errors.buildNotDeployed(buildId));
57
- }
58
- else {
59
- logError(e, new ApiErrorContext({
60
- accountId,
61
- request: 'project release create',
62
- }));
63
- }
64
- throw e;
19
+ function logUploadError(error, accountId) {
20
+ if (!error) {
21
+ return;
65
22
  }
23
+ logError(error, new ApiErrorContext({
24
+ accountId,
25
+ request: 'project release create',
26
+ }));
66
27
  }
67
28
  async function handler(args) {
68
- const { exit, derivedAccountId, build: buildOption, json: formatOutputAsJson, force, } = args;
29
+ const { exit, derivedAccountId, build: buildOption, json: formatOutputAsJson, force, addJsonOutput, } = args;
69
30
  const { projectConfig, projectDir } = await getProjectConfig();
70
31
  try {
71
32
  validateProjectConfig(projectConfig, projectDir);
@@ -74,34 +35,66 @@ async function handler(args) {
74
35
  logError(error);
75
36
  return exit(EXIT_CODES.ERROR);
76
37
  }
38
+ if (!projectConfig || !projectDir) {
39
+ return exit(EXIT_CODES.ERROR);
40
+ }
77
41
  const projectName = projectConfig.name;
78
42
  let buildId;
79
43
  try {
80
- buildId = await resolveBuildId(derivedAccountId, projectName, buildOption);
44
+ buildId = await resolveBuildId(derivedAccountId, projectName, buildOption, force);
81
45
  }
82
46
  catch (e) {
83
- if (isHubSpotHttpError(e) && e.status === 404) {
84
- uiLogger.error(commands.project.release.create.errors.projectNotFound(derivedAccountId, projectName));
85
- }
86
- else if (!(e instanceof Error) || !e.message) {
87
- logError(e, new ApiErrorContext({
88
- accountId: derivedAccountId,
89
- request: 'project release create',
90
- }));
91
- }
92
- else {
93
- uiLogger.error(e.message);
47
+ if (isPromptExitError(e)) {
48
+ return exit(e.exitCode);
94
49
  }
50
+ logError(e, new ApiErrorContext({
51
+ accountId: derivedAccountId,
52
+ request: 'project release create',
53
+ }));
54
+ return exit(EXIT_CODES.ERROR);
55
+ }
56
+ if (buildId === null) {
95
57
  return exit(EXIT_CODES.ERROR);
96
58
  }
97
- if (buildOption) {
59
+ if (!buildId && !force) {
60
+ let shouldUpload;
98
61
  try {
99
- await validateBuild(derivedAccountId, projectName, buildId);
62
+ shouldUpload = await confirmPrompt(commands.project.release.create.uploadPrompt(projectName));
63
+ }
64
+ catch (e) {
65
+ if (isPromptExitError(e)) {
66
+ return exit(e.exitCode);
67
+ }
68
+ throw e;
69
+ }
70
+ if (!shouldUpload) {
71
+ return exit(EXIT_CODES.SUCCESS);
72
+ }
73
+ }
74
+ if (!buildId) {
75
+ const { result: pollResult, uploadError, projectNotFound, } = await handleProjectUpload({
76
+ accountId: derivedAccountId,
77
+ projectConfig,
78
+ projectDir,
79
+ sendIR: true,
80
+ callbackFunc: (...args) => pollProjectBuildAndDeploy(...args, { skipDeploy: true }),
81
+ });
82
+ if (projectNotFound || !pollResult || !pollResult.succeeded) {
83
+ logUploadError(uploadError, derivedAccountId);
84
+ return exit(EXIT_CODES.ERROR);
100
85
  }
101
- catch {
86
+ buildId = pollResult.buildId;
87
+ }
88
+ try {
89
+ const supportsReleases = await validateBuildForRelease(derivedAccountId, projectName, buildId);
90
+ if (!supportsReleases) {
91
+ uiLogger.error(commands.project.release.create.errors.incompatibleBuildVersion);
102
92
  return exit(EXIT_CODES.ERROR);
103
93
  }
104
94
  }
95
+ catch {
96
+ return exit(EXIT_CODES.ERROR);
97
+ }
105
98
  if (!formatOutputAsJson && !force) {
106
99
  const confirmed = await confirmPrompt(commands.project.release.create.confirmPrompt(projectName, buildId));
107
100
  if (!confirmed) {
@@ -110,7 +103,11 @@ async function handler(args) {
110
103
  }
111
104
  }
112
105
  try {
113
- await executeRelease(derivedAccountId, projectName, buildId, !!formatOutputAsJson);
106
+ const release = await executeRelease(derivedAccountId, projectName, buildId);
107
+ addJsonOutput(mapReleaseToJsonOutput(release));
108
+ if (!formatOutputAsJson) {
109
+ uiLogger.success(commands.project.release.create.success(release.releaseTag, release.buildId));
110
+ }
114
111
  }
115
112
  catch {
116
113
  return exit(EXIT_CODES.ERROR);
@@ -154,6 +151,8 @@ const projectReleaseCreateCommand = {
154
151
  command,
155
152
  describe,
156
153
  builder,
157
- handler: makeWrappedYargsHandler('project-release-create', handler),
154
+ handler: makeWrappedYargsHandler('project-release-create', handler, {
155
+ jsonOutputSchema: ReleaseSchema,
156
+ }),
158
157
  };
159
158
  export default projectReleaseCreateCommand;
@@ -1,5 +1,6 @@
1
1
  import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../../types/Yargs.js';
2
- export type ProjectReleaseInfoArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
2
+ import { ReleaseJsonOutput } from '../../../lib/jsonOutput.js';
3
+ export type ProjectReleaseInfoArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs<ReleaseJsonOutput> & {
3
4
  tag?: string;
4
5
  };
5
6
  declare const projectReleaseInfoCommand: YargsCommandModule<unknown, ProjectReleaseInfoArgs>;
@@ -12,6 +12,7 @@ import { makeWrappedYargsHandler } from '../../../lib/yargs/makeWrappedYargsHand
12
12
  import { isPromptExitError } from '../../../lib/errors/PromptExitError.js';
13
13
  import { mapToUserFacingType } from '@hubspot/project-parsing-lib/transform';
14
14
  import { AUTO_GENERATED_COMPONENT_TYPES } from '@hubspot/project-parsing-lib/constants';
15
+ import { ReleaseSchema, mapReleaseToJsonOutput, } from '../../../lib/jsonOutput.js';
15
16
  const command = 'info';
16
17
  // const describe = commands.project.release.info.describe;
17
18
  const describe = undefined;
@@ -36,7 +37,7 @@ async function selectReleasePrompt(accountId, projectName) {
36
37
  });
37
38
  }
38
39
  async function handler(args) {
39
- const { derivedAccountId, tag: rawTag, json: formatOutputAsJson, exit, } = args;
40
+ const { derivedAccountId, tag: rawTag, json: formatOutputAsJson, exit, addJsonOutput, } = args;
40
41
  const { projectConfig, projectDir } = await getProjectConfig();
41
42
  try {
42
43
  validateProjectConfig(projectConfig, projectDir);
@@ -69,10 +70,8 @@ async function handler(args) {
69
70
  }
70
71
  try {
71
72
  const { data: release } = await getReleaseInfo(derivedAccountId, projectName, tag);
72
- if (formatOutputAsJson) {
73
- uiLogger.json(release);
74
- }
75
- else {
73
+ addJsonOutput(mapReleaseToJsonOutput(release));
74
+ if (!formatOutputAsJson) {
76
75
  uiLogger.log(commands.project.release.info.releaseDetails(release.releaseTag, projectName));
77
76
  renderTable(['Release', 'Build', 'Created'], [
78
77
  [
@@ -142,6 +141,8 @@ const projectReleaseInfoCommand = {
142
141
  command,
143
142
  describe,
144
143
  builder,
145
- handler: makeWrappedYargsHandler('project-release-info', handler),
144
+ handler: makeWrappedYargsHandler('project-release-info', handler, {
145
+ jsonOutputSchema: ReleaseSchema,
146
+ }),
146
147
  };
147
148
  export default projectReleaseInfoCommand;
@@ -1,5 +1,6 @@
1
1
  import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../../types/Yargs.js';
2
- export type ProjectReleaseListArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
2
+ import { ReleaseListJsonOutput } from '../../../lib/jsonOutput.js';
3
+ export type ProjectReleaseListArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs<ReleaseListJsonOutput> & {
3
4
  limit?: number;
4
5
  };
5
6
  declare const projectReleaseListCommand: YargsCommandModule<unknown, ProjectReleaseListArgs>;
@@ -10,6 +10,7 @@ import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
10
10
  import { commands } from '../../../lang/en.js';
11
11
  import { renderTable } from '../../../ui/render.js';
12
12
  import { makeWrappedYargsHandler } from '../../../lib/yargs/makeWrappedYargsHandler.js';
13
+ import { ReleaseListSchema, mapReleaseToJsonOutput, } from '../../../lib/jsonOutput.js';
13
14
  const command = 'list';
14
15
  // const describe = commands.project.release.list.describe;
15
16
  const describe = undefined;
@@ -41,14 +42,14 @@ async function fetchAndDisplayReleases(accountId, projectName, options) {
41
42
  }
42
43
  }
43
44
  async function handler(args) {
44
- const { derivedAccountId, limit, json: formatOutputAsJson } = args;
45
+ const { derivedAccountId, limit, json: formatOutputAsJson, exit, addJsonOutput, } = args;
45
46
  const { projectConfig, projectDir } = await getProjectConfig();
46
47
  try {
47
48
  validateProjectConfig(projectConfig, projectDir);
48
49
  }
49
50
  catch (error) {
50
51
  logError(error);
51
- process.exit(EXIT_CODES.ERROR);
52
+ return exit(EXIT_CODES.ERROR);
52
53
  }
53
54
  const projectName = projectConfig.name;
54
55
  try {
@@ -56,7 +57,12 @@ async function handler(args) {
56
57
  const { data } = await listReleases(derivedAccountId, projectName, {
57
58
  limit,
58
59
  });
59
- uiLogger.json(data);
60
+ addJsonOutput({
61
+ results: data.results.map(mapReleaseToJsonOutput),
62
+ paging: data.paging
63
+ ? { next: { after: data.paging.next.after } }
64
+ : undefined,
65
+ });
60
66
  }
61
67
  else {
62
68
  await fetchAndDisplayReleases(derivedAccountId, projectName, { limit });
@@ -75,9 +81,9 @@ async function handler(args) {
75
81
  request: 'project release list',
76
82
  }));
77
83
  }
78
- process.exit(EXIT_CODES.ERROR);
84
+ return exit(EXIT_CODES.ERROR);
79
85
  }
80
- process.exit(EXIT_CODES.SUCCESS);
86
+ return exit(EXIT_CODES.SUCCESS);
81
87
  }
82
88
  function projectReleaseListBuilder(yargs) {
83
89
  yargs.options({
@@ -106,6 +112,8 @@ const projectReleaseListCommand = {
106
112
  command,
107
113
  describe,
108
114
  builder,
109
- handler: makeWrappedYargsHandler('project-release-list', handler),
115
+ handler: makeWrappedYargsHandler('project-release-list', handler, {
116
+ jsonOutputSchema: ReleaseListSchema,
117
+ }),
110
118
  };
111
119
  export default projectReleaseListCommand;
@@ -1,5 +1,6 @@
1
1
  import { CommonArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
2
- export type ProjectUploadArgs = CommonArgs & JSONOutputArgs & {
2
+ import { UploadJsonOutput } from '../../lib/jsonOutput.js';
3
+ export type ProjectUploadArgs = CommonArgs & JSONOutputArgs<UploadJsonOutput> & {
3
4
  force: boolean;
4
5
  forceCreate: boolean;
5
6
  message: string;
@@ -2,7 +2,7 @@ import chalk from 'chalk';
2
2
  import { uiLogger } from '../../lib/ui/logger.js';
3
3
  import { getConfigAccountById } from '@hubspot/local-dev-lib/config';
4
4
  import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
5
- import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
5
+ import { isLegacyProject, meetsMinimumPlatformVersion, } from '@hubspot/project-parsing-lib/projects';
6
6
  import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
7
7
  import { logFeedbackMessage } from '../../lib/projects/ui.js';
8
8
  import { handleProjectUpload } from '../../lib/projects/upload.js';
@@ -14,9 +14,11 @@ import { PROJECT_ERROR_TYPES } from '../../lib/constants.js';
14
14
  import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
15
15
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
16
16
  import { makeWrappedYargsHandler } from '../../lib/yargs/makeWrappedYargsHandler.js';
17
+ import { UploadSchema, } from '../../lib/jsonOutput.js';
17
18
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
18
19
  import { projectProfilePrompt } from '../../lib/prompts/projectProfilePrompt.js';
19
20
  import { showMcpPromotionNudge } from '../../lib/mcp/promotion.js';
21
+ import { PLATFORM_VERSIONS } from '@hubspot/project-parsing-lib/constants';
20
22
  const command = 'upload';
21
23
  const describe = commands.project.upload.describe;
22
24
  async function handlePreview(accountId, projectId, buildId, targetPortalId) {
@@ -34,8 +36,7 @@ async function handlePreview(accountId, projectId, buildId, targetPortalId) {
34
36
  };
35
37
  }
36
38
  async function handler(args) {
37
- const { force = false, forceCreate = false, message, derivedAccountId, skipValidation, skipNpmAudit, skipAutoDeploy, formatOutputAsJson, profile: profileOption, useEnv: useEnvOption, preview, target: targetPortalId, exit, addUsageMetadata, } = args;
38
- const jsonOutput = {};
39
+ const { force = false, forceCreate = false, message, derivedAccountId, skipValidation, skipNpmAudit, skipAutoDeploy, formatOutputAsJson, profile: profileOption, useEnv: useEnvOption, preview, target: targetPortalId, exit, addUsageMetadata, addJsonOutput, } = args;
39
40
  const { projectConfig, projectDir } = await getProjectConfig();
40
41
  try {
41
42
  validateProjectConfig(projectConfig, projectDir);
@@ -113,27 +114,33 @@ async function handler(args) {
113
114
  (!result.buildResult.isAutoDeployEnabled || preview || skipAutoDeploy)) {
114
115
  uiLogger.log(chalk.bold(commands.project.upload.logs.buildSucceeded(result.buildId)));
115
116
  if (!preview) {
116
- uiLogger.log(commands.project.upload.logs.autoDeployDisabled(`hs project deploy --build=${result.buildId}`));
117
+ if (meetsMinimumPlatformVersion(result.buildResult.platformVersion, PLATFORM_VERSIONS.v2026_09_BETA)) {
118
+ const releaseCommand = `hs project release create --build=${result.buildId}`;
119
+ uiLogger.log(commands.project.upload.logs.releaseManagementRequired(releaseCommand));
120
+ }
121
+ else {
122
+ const deployCommand = `hs project deploy --build=${result.buildId}`;
123
+ uiLogger.log(skipAutoDeploy
124
+ ? commands.project.upload.logs.autoDeploySkipped(deployCommand)
125
+ : commands.project.upload.logs.autoDeployDisabled(deployCommand));
126
+ }
117
127
  logFeedbackMessage(result.buildId);
118
128
  }
119
129
  await displayWarnLogs(targetAccountId, projectConfig.name, result.buildId);
120
130
  }
121
131
  if (result && result.succeeded && preview && targetPortalId) {
122
132
  const previewJson = await handlePreview(targetAccountId, projectId, result.buildId, targetPortalId);
123
- if (previewJson && formatOutputAsJson) {
124
- jsonOutput.preview = previewJson;
133
+ if (previewJson) {
134
+ addJsonOutput({ preview: previewJson });
125
135
  }
126
136
  }
127
- if (result && result.succeeded && formatOutputAsJson) {
128
- jsonOutput.buildId = result.buildId;
137
+ if (result) {
138
+ addJsonOutput({ buildId: result.buildId });
129
139
  if (result.deployResult) {
130
- jsonOutput.deployId = result.deployResult.deployId;
140
+ addJsonOutput({ deployId: result.deployResult.deployId });
131
141
  }
132
142
  }
133
143
  if (result && !result.succeeded) {
134
- if (formatOutputAsJson) {
135
- uiLogger.json(jsonOutput);
136
- }
137
144
  return exit(EXIT_CODES.ERROR);
138
145
  }
139
146
  if (!result && !uploadError) {
@@ -147,10 +154,7 @@ async function handler(args) {
147
154
  }));
148
155
  return exit(EXIT_CODES.ERROR);
149
156
  }
150
- if (formatOutputAsJson) {
151
- uiLogger.json(jsonOutput);
152
- }
153
- else {
157
+ if (!formatOutputAsJson) {
154
158
  await showMcpPromotionNudge(args._.join(' '));
155
159
  }
156
160
  return exit(EXIT_CODES.SUCCESS);
@@ -242,7 +246,9 @@ const builder = makeYargsBuilder(projectUploadBuilder, command, describe, {
242
246
  const projectUploadCommand = {
243
247
  command,
244
248
  describe,
245
- handler: makeWrappedYargsHandler('project-upload', handler),
249
+ handler: makeWrappedYargsHandler('project-upload', handler, {
250
+ jsonOutputSchema: UploadSchema,
251
+ }),
246
252
  builder,
247
253
  };
248
254
  export default projectUploadCommand;
@@ -1,5 +1,5 @@
1
1
  import { CommonArgs, YargsCommandModule } from '../../types/Yargs.js';
2
- type ProjectValidateArgs = CommonArgs & {
2
+ export type ProjectValidateArgs = CommonArgs & {
3
3
  profile?: string;
4
4
  };
5
5
  declare const projectValidateCommand: YargsCommandModule<unknown, ProjectValidateArgs>;
@@ -24,7 +24,7 @@ import appInstallStatus from './project/appInstallStatus.js';
24
24
  import installApp from './project/installApp.js';
25
25
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
26
26
  import { getProjectConfig } from '../lib/projects/config.js';
27
- import { isSupportedPlatformVersion, LATEST_SUPPORTED_PLATFORM_VERSION, } from '@hubspot/project-parsing-lib/projects';
27
+ import { isKnownPlatformVersion, LATEST_SUPPORTED_PLATFORM_VERSION, } from '@hubspot/project-parsing-lib/projects';
28
28
  import { uiLogger } from '../lib/ui/logger.js';
29
29
  import { debugError } from '../lib/errorHandlers/index.js';
30
30
  const command = ['project', 'projects'];
@@ -37,7 +37,7 @@ async function validatePlatformVersion() {
37
37
  // Only warn if a platform version is explicitly set but not supported
38
38
  // Don't warn if the platform version is missing/undefined
39
39
  if (projectConfig?.platformVersion &&
40
- !isSupportedPlatformVersion(projectConfig.platformVersion)) {
40
+ !isKnownPlatformVersion(projectConfig.platformVersion)) {
41
41
  uiLogger.warn(lib.projects.platformVersion.unsupported(pkg.version, LATEST_SUPPORTED_PLATFORM_VERSION, projectConfig.platformVersion));
42
42
  uiLogger.log('');
43
43
  }
@@ -1,6 +1,7 @@
1
1
  import { AccountLevel } from '@hubspot/local-dev-lib/types/developerTestAccounts.js';
2
2
  import { CommonArgs, ConfigArgs, AccountArgs, TestingArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
3
- type CreateTestAccountArgs = CommonArgs & AccountArgs & ConfigArgs & TestingArgs & EnvironmentArgs & JSONOutputArgs & {
3
+ import { CreateTestAccountJsonOutput } from '../../lib/jsonOutput.js';
4
+ type CreateTestAccountArgs = CommonArgs & AccountArgs & ConfigArgs & TestingArgs & EnvironmentArgs & JSONOutputArgs<CreateTestAccountJsonOutput> & {
4
5
  configPath?: string;
5
6
  name?: string;
6
7
  description?: string;
@@ -4,6 +4,7 @@ import { getValidEnv } from '@hubspot/local-dev-lib/environment';
4
4
  import { getConfigAccountEnvironment } from '@hubspot/local-dev-lib/config';
5
5
  import { getCwd } from '@hubspot/local-dev-lib/path';
6
6
  import { makeWrappedYargsHandler } from '../../lib/yargs/makeWrappedYargsHandler.js';
7
+ import { CreateTestAccountSchema, } from '../../lib/jsonOutput.js';
7
8
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
8
9
  import { promptUser, listPrompt } from '../../lib/prompts/promptUtils.js';
9
10
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
@@ -88,7 +89,7 @@ async function buildTestAccountConfig(args) {
88
89
  });
89
90
  }
90
91
  async function handler(args) {
91
- const { derivedAccountId, formatOutputAsJson, exit } = args;
92
+ const { derivedAccountId, formatOutputAsJson, exit, addJsonOutput } = args;
92
93
  const env = getValidEnv(getConfigAccountEnvironment(derivedAccountId));
93
94
  let testAccountConfig;
94
95
  try {
@@ -121,11 +122,8 @@ async function handler(args) {
121
122
  SpinniesManager.succeed('createTestAccount', {
122
123
  text: commands.testAccount.create.polling.success(testAccountConfig.accountName, resultJson.accountId, derivedAccountId),
123
124
  });
124
- if (formatOutputAsJson) {
125
- uiLogger.json(resultJson);
126
- }
127
- else {
128
- // Only save to config if not using json output
125
+ addJsonOutput(resultJson);
126
+ if (!formatOutputAsJson) {
129
127
  try {
130
128
  const savedAccountName = await saveAccountToConfig(resultJson.accountId, testAccountConfig.accountName, env, resultJson.personalAccessKey);
131
129
  // Inform user if the account name was normalized
@@ -230,7 +228,9 @@ const builder = makeYargsBuilder(createTestAccountBuilder, command, describe, {
230
228
  const createTestAccountCommand = {
231
229
  command,
232
230
  describe,
233
- handler: makeWrappedYargsHandler('test-account-create', handler),
231
+ handler: makeWrappedYargsHandler('test-account-create', handler, {
232
+ jsonOutputSchema: CreateTestAccountSchema,
233
+ }),
234
234
  builder,
235
235
  };
236
236
  export default createTestAccountCommand;