@hubspot/cli 8.0.11-experimental.2 → 8.0.12-experimental.1

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 (75) hide show
  1. package/bin/cli.js +4 -3
  2. package/commands/account/clean.js +2 -0
  3. package/commands/account/createOverride.js +3 -0
  4. package/commands/account/info.js +34 -16
  5. package/commands/account/link.d.ts +4 -0
  6. package/commands/account/link.js +89 -0
  7. package/commands/account/list.js +29 -71
  8. package/commands/account/remove.js +2 -0
  9. package/commands/account/removeOverride.js +3 -0
  10. package/commands/account/unlink.d.ts +4 -0
  11. package/commands/account/unlink.js +70 -0
  12. package/commands/account/use.js +71 -1
  13. package/commands/account.js +4 -0
  14. package/commands/project/appInstallStatus.d.ts +4 -0
  15. package/commands/project/appInstallStatus.js +132 -0
  16. package/commands/project/create.js +8 -0
  17. package/commands/project/dev/deprecatedFlow.js +20 -2
  18. package/commands/project/dev/index.js +6 -0
  19. package/commands/project/dev/unifiedFlow.js +20 -3
  20. package/commands/project/lint.js +20 -2
  21. package/commands/project/upload.d.ts +2 -0
  22. package/commands/project/upload.js +47 -3
  23. package/commands/project.js +2 -0
  24. package/lang/en.d.ts +122 -0
  25. package/lang/en.js +136 -8
  26. package/lib/app/migrate.js +2 -1
  27. package/lib/constants.d.ts +2 -0
  28. package/lib/constants.js +4 -0
  29. package/lib/doctor/Doctor.js +5 -5
  30. package/lib/link/accountTableUtils.d.ts +10 -0
  31. package/lib/link/accountTableUtils.js +39 -0
  32. package/lib/link/index.d.ts +18 -0
  33. package/lib/link/index.js +185 -0
  34. package/lib/link/linkUtils.d.ts +5 -0
  35. package/lib/link/linkUtils.js +49 -0
  36. package/lib/link/prompts.d.ts +7 -0
  37. package/lib/link/prompts.js +126 -0
  38. package/lib/link/renderLinkedAccountsTable.d.ts +2 -0
  39. package/lib/link/renderLinkedAccountsTable.js +14 -0
  40. package/lib/link/warnIfLinkedDirectory.d.ts +1 -0
  41. package/lib/link/warnIfLinkedDirectory.js +9 -0
  42. package/lib/projects/ProjectLogsManager.js +4 -1
  43. package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +2 -1
  44. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
  45. package/lib/projects/localDev/LocalDevManager_DEPRECATED.d.ts +2 -0
  46. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -0
  47. package/lib/projects/preview.d.ts +7 -0
  48. package/lib/projects/preview.js +58 -0
  49. package/lib/projects/uieLinting.d.ts +17 -3
  50. package/lib/projects/uieLinting.js +93 -28
  51. package/lib/projects/upload.d.ts +1 -0
  52. package/lib/projects/upload.js +4 -3
  53. package/lib/prompts/projectsLogsPrompt.js +3 -0
  54. package/lib/prompts/promptUtils.js +1 -0
  55. package/lib/ui/accountTable.d.ts +8 -0
  56. package/lib/ui/accountTable.js +67 -0
  57. package/lib/yargs/parseYargsOrExit.d.ts +4 -0
  58. package/lib/yargs/parseYargsOrExit.js +25 -0
  59. package/mcp-server/server.js +39 -1
  60. package/mcp-server/tools/index.js +2 -0
  61. package/mcp-server/tools/project/AddFeatureToProjectTool.js +1 -1
  62. package/mcp-server/tools/project/CreateTestAccountTool.js +1 -1
  63. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  64. package/mcp-server/tools/project/FindProjectsTool.d.ts +15 -0
  65. package/mcp-server/tools/project/FindProjectsTool.js +60 -0
  66. package/mcp-server/tools/project/GetBuildLogsTool.js +1 -1
  67. package/mcp-server/tools/project/GetBuildStatusTool.js +1 -1
  68. package/mcp-server/tools/project/UploadProjectTools.js +1 -1
  69. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  70. package/package.json +7 -7
  71. package/types/Link.d.ts +32 -0
  72. package/types/Link.js +5 -0
  73. package/types/PackageJson.d.ts +1 -0
  74. package/types/Prompts.d.ts +1 -0
  75. package/types/Yargs.d.ts +1 -0
package/lang/en.d.ts CHANGED
@@ -129,8 +129,12 @@ export declare const commands: {
129
129
  };
130
130
  list: {
131
131
  accounts: string;
132
+ allAccounts: string;
133
+ linkedAccounts: string;
132
134
  defaultAccountTitle: string;
135
+ linkedDefaultTitle: string;
133
136
  currentResolvedDefaultAccount: (accountId: number) => string;
137
+ directory: (dir: string) => string;
134
138
  describe: string;
135
139
  configPath: (configPath: string) => string;
136
140
  overrideFilePathTitle: string;
@@ -177,6 +181,70 @@ export declare const commands: {
177
181
  success: {
178
182
  defaultAccountUpdated: (accountName: string) => string;
179
183
  };
184
+ linked: {
185
+ editingLinkedDefault: (dir: string) => string;
186
+ alreadyDefault: (accountId: number) => string;
187
+ setLinkedDefault: (account: string) => string;
188
+ accountNotLinked: (account: string) => string;
189
+ promptToLink: (account: string) => string;
190
+ settingGlobalDefault: string;
191
+ nonInteractiveNotLinked: (account: string) => string;
192
+ };
193
+ };
194
+ link: {
195
+ describe: string;
196
+ verboseDescribe: string;
197
+ shared: {
198
+ noLinkedAccounts: string;
199
+ globalAccountsAvailable: (count: number) => string;
200
+ configurePrompt: string;
201
+ deprecatedConfigNotSupported: (command: string) => string;
202
+ writeSettingsFailed: (path: string, err: unknown) => string;
203
+ savedToSettings: (path: string) => string;
204
+ usingLinkedAccounts: (settingsPath: string) => string;
205
+ accountAutoLinked: (accountId: number) => string;
206
+ accountAutoLinkFailed: (accountId: number) => string;
207
+ };
208
+ linkingDirectory: (dir: string) => string;
209
+ managingLinkedAccounts: (dir: string) => string;
210
+ settingsInfo: (path: string) => string;
211
+ success: {
212
+ created: (path: string) => string;
213
+ };
214
+ errors: {
215
+ authFailed: string;
216
+ };
217
+ events: {
218
+ accountsLinked: (count: number) => string;
219
+ accountsUnlinked: (count: number) => string;
220
+ overrideAccountDetected: (accountId: number) => string;
221
+ defaultAccountSet: (accountId: number) => string;
222
+ defaultAccountRemoved: (isSelectionRequired: boolean) => string;
223
+ defaultAccountRemains: (accountId: number) => string;
224
+ updatedLinkedAccounts: string;
225
+ noAccountsLinked: string;
226
+ overrideFileRemoved: string;
227
+ invalidDefaultAccount: (accountId: number) => string;
228
+ };
229
+ prompts: {
230
+ howToProceed: string;
231
+ whatToDo: string;
232
+ linkExisting: string;
233
+ authenticateNew: string;
234
+ cancel: string;
235
+ selectDefault: string;
236
+ selectToLink: string;
237
+ selectToUnlink: string;
238
+ alreadyLinked: string;
239
+ fromHsAccount: string;
240
+ newlyAuthenticated: string;
241
+ mustSelectOne: string;
242
+ keepAsDefault: string;
243
+ };
244
+ };
245
+ unlink: {
246
+ describe: string;
247
+ verboseDescribe: string;
180
248
  };
181
249
  remove: {
182
250
  describe: string;
@@ -245,6 +313,9 @@ export declare const commands: {
245
313
  };
246
314
  name: (name: string) => string;
247
315
  scopeGroups: string;
316
+ linkedDefaultTitle: string;
317
+ settingsPath: (path: string) => string;
318
+ linkedDefault: (account: string) => string;
248
319
  };
249
320
  clean: {
250
321
  describe: string;
@@ -1448,6 +1519,7 @@ export declare const commands: {
1448
1519
  projectAccount: string;
1449
1520
  testingAccount: string;
1450
1521
  account: string;
1522
+ port: string;
1451
1523
  };
1452
1524
  };
1453
1525
  create: {
@@ -1458,6 +1530,9 @@ export declare const commands: {
1458
1530
  failedToFetchProjectList: string;
1459
1531
  cannotNestProjects: (projectDir: string) => string;
1460
1532
  };
1533
+ warnings: {
1534
+ betaPlatformVersion: (platformVersion: string) => string;
1535
+ };
1461
1536
  logs: {
1462
1537
  success: (projectName: string, projectDest: string) => string;
1463
1538
  };
@@ -1684,6 +1759,7 @@ export declare const commands: {
1684
1759
  noFunctionsLinkText: string;
1685
1760
  noFunctionsInProject: string;
1686
1761
  noFunctionWithName: (name: string) => string;
1762
+ functionNameRequired: string;
1687
1763
  functionNotDeployed: (name: string) => string;
1688
1764
  projectLogsManagerNotInitialized: string;
1689
1765
  noDeployedBuild: string;
@@ -1729,6 +1805,7 @@ export declare const commands: {
1729
1805
  examples: {
1730
1806
  default: string;
1731
1807
  withProfile: string;
1808
+ withPreview: string;
1732
1809
  };
1733
1810
  logs: {
1734
1811
  buildSucceeded: (buildId: number) => string;
@@ -1739,6 +1816,8 @@ export declare const commands: {
1739
1816
  errors: {
1740
1817
  noProjectConfig: string;
1741
1818
  projectLockedError: string;
1819
+ previewRequiresTarget: string;
1820
+ targetRequiresPreview: string;
1742
1821
  };
1743
1822
  options: {
1744
1823
  forceCreate: {
@@ -1750,6 +1829,12 @@ export declare const commands: {
1750
1829
  profile: {
1751
1830
  describe: string;
1752
1831
  };
1832
+ preview: {
1833
+ describe: string;
1834
+ };
1835
+ target: {
1836
+ describe: string;
1837
+ };
1753
1838
  };
1754
1839
  };
1755
1840
  watch: {
@@ -1864,6 +1949,7 @@ export declare const commands: {
1864
1949
  loading: {
1865
1950
  checking: string;
1866
1951
  creatingConfig: string;
1952
+ addingLintScripts: string;
1867
1953
  linting: string;
1868
1954
  };
1869
1955
  noProjectConfig: string;
@@ -1876,8 +1962,12 @@ export declare const commands: {
1876
1962
  }[]) => string;
1877
1963
  createEslintConfigPrompt: (directories: string[]) => string;
1878
1964
  eslintConfigCreated: (configPath: string) => string;
1965
+ createEslintConfigRequiresV2Platform: (platformVersion?: string | null) => string;
1966
+ failedToFetchRemoteEslintConfig: (platformVersion: string) => string;
1879
1967
  failedToCreateEslintConfig: (configPath: string) => string;
1880
1968
  eslintConfigRequired: string;
1969
+ lintScriptsAdded: (scriptNames: string[], packageJsonPath: string) => string;
1970
+ failedToAddLintScripts: (packageJsonPath: string) => string;
1881
1971
  };
1882
1972
  updateDeps: {
1883
1973
  help: {
@@ -2009,6 +2099,25 @@ export declare const commands: {
2009
2099
  force: string;
2010
2100
  };
2011
2101
  };
2102
+ installStatus: {
2103
+ describe: string;
2104
+ examples: {
2105
+ default: string;
2106
+ json: string;
2107
+ };
2108
+ errors: {
2109
+ noProjectConfig: string;
2110
+ unsupportedPlatformVersion: (platformVersion: string) => string;
2111
+ failedToParseProject: string;
2112
+ noAppInProject: string;
2113
+ unsupportedAuthType: (authType: string) => string;
2114
+ };
2115
+ success: {
2116
+ installed: (appName: string, accountId: number) => string;
2117
+ installedWithOutdatedScopes: (appName: string, accountId: number) => string;
2118
+ };
2119
+ notInstalled: (appName: string, accountId: number) => string;
2120
+ };
2012
2121
  };
2013
2122
  sandbox: {
2014
2123
  describe: string;
@@ -2977,6 +3086,9 @@ export declare const commands: {
2977
3086
  };
2978
3087
  };
2979
3088
  export declare const lib: {
3089
+ linkedDirectory: {
3090
+ warning: (action: string, settingsPath: string) => string;
3091
+ };
2980
3092
  parsing: {
2981
3093
  unableToParseStringToNumber: string;
2982
3094
  };
@@ -3290,6 +3402,16 @@ export declare const lib: {
3290
3402
  updatedFileDependency: (packageName: string, relativePath: string) => string;
3291
3403
  };
3292
3404
  };
3405
+ projectPreview: {
3406
+ triggeringPreview: (buildId: number, targetPortalId: number) => string;
3407
+ pollingStatus: (releaseTag: string, targetPortalId: number) => string;
3408
+ succeeded: (releaseTag: string, targetPortalId: number) => string;
3409
+ triggerFailed: string;
3410
+ pollFailed: string;
3411
+ timeout: string;
3412
+ warning: string;
3413
+ missingProjectId: string;
3414
+ };
3293
3415
  importData: {
3294
3416
  errors: {
3295
3417
  incorrectAccountType: (derivedAccountId: number) => string;
package/lang/en.js CHANGED
@@ -2,11 +2,12 @@ import chalk from 'chalk';
2
2
  import { mapToUserFriendlyName } from '@hubspot/project-parsing-lib/transform';
3
3
  import { PLATFORM_VERSIONS } from '@hubspot/project-parsing-lib/constants';
4
4
  import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constants/auth';
5
- import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME, } from '@hubspot/local-dev-lib/constants/config';
6
- import { uiAccountDescription, uiBetaTag, uiCommandReference, uiLink, UI_COLORS, uiAuthCommandReference, } from '../lib/ui/index.js';
7
- import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, } from '../lib/projects/urls.js';
5
+ import { LOCAL_DEV_DEFAULT_PORT } from '../lib/constants.js';
6
+ import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, } from '@hubspot/local-dev-lib/constants/config';
7
+ import { indent, UI_COLORS, uiAccountDescription, uiAuthCommandReference, uiBetaTag, uiCommandReference, uiLink, } from '../lib/ui/index.js';
8
+ import { getLocalDevUiUrl, getProjectDetailUrl, getProjectSettingsUrl, } from '../lib/projects/urls.js';
8
9
  import { getProductUpdatesUrl } from '../lib/links.js';
9
- import { APP_DISTRIBUTION_TYPES, APP_AUTH_TYPES, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, LEGACY_PUBLIC_APP_FILE, } from '../lib/constants.js';
10
+ import { APP_AUTH_TYPES, APP_DISTRIBUTION_TYPES, LEGACY_PUBLIC_APP_FILE, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, } from '../lib/constants.js';
10
11
  export const commands = {
11
12
  generalErrors: {
12
13
  srcIsProject: (src, command) => `"${src}" is in a project folder. Did you mean "hs project ${command}"?`,
@@ -137,8 +138,12 @@ export const commands = {
137
138
  },
138
139
  list: {
139
140
  accounts: `${chalk.bold('Accounts')}:`,
141
+ allAccounts: `${chalk.bold('All Accounts')}:`,
142
+ linkedAccounts: `${chalk.bold('Linked Accounts')}:`,
140
143
  defaultAccountTitle: `${chalk.bold('Default Account')}`,
144
+ linkedDefaultTitle: `${chalk.bold('Linked Default Account')}`,
141
145
  currentResolvedDefaultAccount: (accountId) => `Account: ${uiAccountDescription(accountId)}`,
146
+ directory: (dir) => `Directory: ${dir}`,
142
147
  describe: 'List names of accounts defined in config.',
143
148
  configPath: (configPath) => `Source: ${configPath}`,
144
149
  overrideFilePathTitle: `${chalk.bold('Default Account Override')}`,
@@ -185,6 +190,70 @@ export const commands = {
185
190
  success: {
186
191
  defaultAccountUpdated: (accountName) => `Default account updated to "${accountName}"`,
187
192
  },
193
+ linked: {
194
+ editingLinkedDefault: (dir) => `Editing the default linked account for this directory (not the global default):\n${indent(1)}${dir}`,
195
+ alreadyDefault: (accountId) => `${uiAccountDescription(accountId)} is the only linked account and is already the default.`,
196
+ setLinkedDefault: (account) => `Linked default set to ${chalk.cyan(account)}`,
197
+ accountNotLinked: (account) => `${account} is not linked to this directory.`,
198
+ promptToLink: (account) => `Would you like to link ${account} to this directory?`,
199
+ settingGlobalDefault: 'Setting global default instead.',
200
+ nonInteractiveNotLinked: (account) => `${account} is not linked to this directory. Setting global default instead.`,
201
+ },
202
+ },
203
+ link: {
204
+ describe: 'Link authenticated HubSpot accounts to the current directory',
205
+ verboseDescribe: `Link one or more authenticated HubSpot accounts to the current directory. Linked accounts are saved in a local ${chalk.bold('.hs/settings.json')} file (added to .gitignore automatically).\n\nThis lets the CLI know which account to use when you run commands from this directory, without changing the global default. Run ${uiCommandReference('hs account current')} to see the active configuration.`,
206
+ shared: {
207
+ noLinkedAccounts: 'No HubSpot accounts are linked in this directory.',
208
+ globalAccountsAvailable: (count) => `You have ${chalk.cyan(count.toString())} ${count === 1 ? 'account' : 'accounts'} in your global config.`,
209
+ configurePrompt: `To configure this directory, run:\n${indent(1)}${uiCommandReference('hs account link')}`,
210
+ deprecatedConfigNotSupported: (command) => `${uiCommandReference(command)} does not support deprecated config. Run ${uiCommandReference('hs config migrate')} to migrate to global config. Then re-run ${uiCommandReference(command)}`,
211
+ writeSettingsFailed: (path, err) => `Failed to write to ${path}: ${err}`,
212
+ savedToSettings: (path) => `Saved to ${path}`,
213
+ usingLinkedAccounts: (settingsPath) => `This directory has linked accounts via ${settingsPath}. Only linked accounts will be available for this command. To manage linked accounts, run ${uiCommandReference('hs account link')}.`,
214
+ accountAutoLinked: (accountId) => `Automatically linked ${uiAccountDescription(accountId)} to this directory.`,
215
+ accountAutoLinkFailed: (accountId) => `Could not automatically link ${uiAccountDescription(accountId)} to this directory. Run ${uiCommandReference('hs account link')} to link it manually.`,
216
+ },
217
+ linkingDirectory: (dir) => `Linking HubSpot account(s) for directory:\n${indent(1)}${dir}`,
218
+ managingLinkedAccounts: (dir) => `Managing linked accounts for:\n${indent(1)}${dir}`,
219
+ settingsInfo: (path) => `\u2139 Accounts linked here are saved in ${path}`,
220
+ success: {
221
+ created: (path) => `Linked to ${path} (created .hs and added to .gitignore).\n${indent(1)}Now when you work within this directory, the CLI will use the linked account(s).`,
222
+ },
223
+ errors: {
224
+ authFailed: `Authentication failed to complete`,
225
+ },
226
+ events: {
227
+ accountsLinked: (count) => `Linked ${count} account${count !== 1 ? 's' : ''}`,
228
+ accountsUnlinked: (count) => `Unlinked ${count} account${count !== 1 ? 's' : ''}`,
229
+ overrideAccountDetected: (accountId) => `\nCurrent default account (from .hsaccount):\n${indent(1)}${uiAccountDescription(accountId)}`,
230
+ defaultAccountSet: (accountId) => `Linked default account set to ${chalk.cyan(uiAccountDescription(accountId))}`,
231
+ defaultAccountRemoved: (isSelectionRequired) => `The linked default account was removed. ${isSelectionRequired ? 'A linked default account is required.\n' : ''}`,
232
+ defaultAccountRemains: (accountId) => `Linked default account remains ${chalk.cyan(uiAccountDescription(accountId))}`,
233
+ updatedLinkedAccounts: 'Updated linked accounts',
234
+ noAccountsLinked: `All accounts have been unlinked. Your global config accounts will be used.\n${indent(1)}The ${chalk.bold('.hs')} directory is no longer needed and can be safely deleted.`,
235
+ overrideFileRemoved: '.hsaccount file removed',
236
+ invalidDefaultAccount: (accountId) => `Default account ${uiAccountDescription(accountId)} is not in the linked accounts list and has been reset. Please select a new default.`,
237
+ },
238
+ prompts: {
239
+ howToProceed: 'How would you like to link an account?',
240
+ whatToDo: 'Which action would you like to perform?',
241
+ linkExisting: 'Link existing authenticated account(s)',
242
+ authenticateNew: 'Authenticate and link a new account',
243
+ cancel: 'Cancel',
244
+ selectDefault: 'Select a linked default account',
245
+ selectToLink: 'Select authenticated account(s) to link:',
246
+ selectToUnlink: 'Select account(s) to unlink',
247
+ alreadyLinked: '- Already linked',
248
+ fromHsAccount: '(from .hsaccount)',
249
+ newlyAuthenticated: '(just authenticated)',
250
+ mustSelectOne: 'You must select at least one account to link',
251
+ keepAsDefault: 'Keep this as the default?',
252
+ },
253
+ },
254
+ unlink: {
255
+ describe: 'Unlink HubSpot account(s) from the current directory',
256
+ verboseDescribe: `Remove one or more linked accounts from this directory's ${chalk.bold('.hs/settings.json')}. If the default account is removed, you will be prompted to select a new one.\n\nThis does not delete the account from the global config - it only removes the local association.`,
188
257
  },
189
258
  remove: {
190
259
  describe: 'Remove an account from the config.',
@@ -253,6 +322,9 @@ export const commands = {
253
322
  },
254
323
  name: (name) => `${chalk.bold('Account name')}: ${name}`,
255
324
  scopeGroups: `${chalk.bold('Scopes available')}:`,
325
+ linkedDefaultTitle: `${chalk.bold('Linked Default Account')}`,
326
+ settingsPath: (path) => `Source: ${path}`,
327
+ linkedDefault: (account) => `Account: ${account}`,
256
328
  },
257
329
  clean: {
258
330
  describe: 'Check for inactive accounts and removes them from the CLI config.',
@@ -1464,6 +1536,7 @@ export const commands = {
1464
1536
  projectAccount: 'The id of the account to upload your project to. Must be used with --testing-account. Supported on platform versions 2025.2 and newer.',
1465
1537
  testingAccount: 'The id of the account to install apps and test on. Must be used with --project-account. Supported on platform versions 2025.2 and newer.',
1466
1538
  account: 'The id of the account to upload your project to. Unsupported on platform versions 2025.2 and newer.',
1539
+ port: `The port for the local dev server. Defaults to ${LOCAL_DEV_DEFAULT_PORT}.`,
1467
1540
  },
1468
1541
  },
1469
1542
  create: {
@@ -1474,6 +1547,9 @@ export const commands = {
1474
1547
  failedToFetchProjectList: 'Failed to fetch the list of available project templates. Please try again later.',
1475
1548
  cannotNestProjects: (projectDir) => `A project already exists at ${projectDir}. Projects cannot be nested within other projects. Please choose a different destination and try again.`,
1476
1549
  },
1550
+ warnings: {
1551
+ betaPlatformVersion: (platformVersion) => `Your portal must be enrolled in the beta to use platform version ${platformVersion}. If you are not enrolled in the beta, your upload will fail. ${uiLink('Enroll in the beta', getProductUpdatesUrl('286886'))}`,
1552
+ },
1477
1553
  logs: {
1478
1554
  success: (projectName, projectDest) => `Project ${chalk.bold(projectName)} was successfully created in ${projectDest}`,
1479
1555
  },
@@ -1700,6 +1776,7 @@ export const commands = {
1700
1776
  noFunctionsLinkText: 'Visit developer docs',
1701
1777
  noFunctionsInProject: `There aren't any functions in this project\n\t- Run ${uiCommandReference('hs project logs --help')} to learn more about logs\n\t- ${uiLink('Visit developer docs', 'https://developers.hubspot.com/docs/platform/serverless-functions')} to learn more about serverless functions`,
1702
1778
  noFunctionWithName: (name) => `No function with name "${name}"`,
1779
+ functionNameRequired: `A function name is required. Pass ${uiCommandReference('--function=<name>')} or run without it to select one interactively.`,
1703
1780
  functionNotDeployed: (name) => `The function with name "${name}" is not deployed`,
1704
1781
  projectLogsManagerNotInitialized: 'Function called on ProjectLogsManager before initialization',
1705
1782
  noDeployedBuild: 'This project has not been deployed yet. Deploy the project first, then try again.',
@@ -1745,6 +1822,7 @@ export const commands = {
1745
1822
  examples: {
1746
1823
  default: 'Upload a project into your HubSpot account',
1747
1824
  withProfile: 'Upload a project into your HubSpot account when using profiles',
1825
+ withPreview: 'Upload and preview the build on a target portal',
1748
1826
  },
1749
1827
  logs: {
1750
1828
  buildSucceeded: (buildId) => `Build #${buildId} succeeded\n`,
@@ -1755,6 +1833,8 @@ export const commands = {
1755
1833
  errors: {
1756
1834
  noProjectConfig: 'No project detected. Run this command from a project directory.',
1757
1835
  projectLockedError: `Your project is locked. This may mean that another user is running the ${uiCommandReference('hs project dev')} command for this project. If this is you, unlock the project in Projects UI.`,
1836
+ previewRequiresTarget: `${uiCommandReference('--preview')} requires ${uiCommandReference('--target=<portalId>')} to specify the portal to preview on.`,
1837
+ targetRequiresPreview: `${uiCommandReference('--target')} can only be used with ${uiCommandReference('--preview')}.`,
1758
1838
  },
1759
1839
  options: {
1760
1840
  forceCreate: {
@@ -1766,6 +1846,12 @@ export const commands = {
1766
1846
  profile: {
1767
1847
  describe: 'Profile to target for this upload',
1768
1848
  },
1849
+ preview: {
1850
+ describe: 'Preview the build on a target portal after a successful upload',
1851
+ },
1852
+ target: {
1853
+ describe: 'Portal ID to preview the build on',
1854
+ },
1769
1855
  },
1770
1856
  },
1771
1857
  watch: {
@@ -1880,6 +1966,7 @@ export const commands = {
1880
1966
  loading: {
1881
1967
  checking: 'Checking lint packages and configuration…',
1882
1968
  creatingConfig: 'Creating ESLint configuration files…',
1969
+ addingLintScripts: 'Adding lint scripts to package.json files…',
1883
1970
  linting: 'Linting…',
1884
1971
  },
1885
1972
  noProjectConfig: 'No project detected. Run this command from a project directory.',
@@ -1899,8 +1986,17 @@ export const commands = {
1899
1986
  },
1900
1987
  createEslintConfigPrompt: (directories) => `ESLint configuration file not found in the following ${directories.length === 1 ? 'directory' : 'directories'}:\n${directories.map(d => ` - ${d}`).join('\n')}\n\nWould you like to set up the required ESLint configuration?`,
1901
1988
  eslintConfigCreated: (configPath) => `ESLint configuration created at ${configPath}`,
1989
+ createEslintConfigRequiresV2Platform: (platformVersion) => {
1990
+ if (!platformVersion) {
1991
+ return 'Automatic ESLint configuration requires a Developer Platform project (2025.2 or later) with platformVersion set in hsproject.json. Add an eslint.config.js file manually, or set platformVersion and try again.';
1992
+ }
1993
+ return `Automatic ESLint configuration is not available for platform version ${platformVersion}. Use Developer Platform 2025.2 or later, or add eslint.config.js manually.`;
1994
+ },
1995
+ failedToFetchRemoteEslintConfig: (platformVersion) => `Could not download the ESLint config from HubSpot project components for platform version ${platformVersion}. Check your network connection and try again, or add eslint.config.js manually.`,
1902
1996
  failedToCreateEslintConfig: (configPath) => `Failed to create ESLint configuration at ${configPath}`,
1903
1997
  eslintConfigRequired: 'ESLint configuration is required to run the lint command. Run the command again to create the configuration.',
1998
+ lintScriptsAdded: (scriptNames, packageJsonPath) => `Added ${scriptNames.map(s => `"${s}"`).join(' and ')} to ${packageJsonPath}`,
1999
+ failedToAddLintScripts: (packageJsonPath) => `Failed to add lint scripts to ${packageJsonPath}`,
1904
2000
  },
1905
2001
  updateDeps: {
1906
2002
  help: {
@@ -2029,6 +2125,25 @@ export const commands = {
2029
2125
  force: 'skip confirmation prompt',
2030
2126
  },
2031
2127
  },
2128
+ installStatus: {
2129
+ describe: 'Check whether a static auth app in the current project is installed in the target account. This command must be run from within a HubSpot project directory.',
2130
+ examples: {
2131
+ default: 'Check install status for the static auth app in the current project',
2132
+ json: 'Output install status as JSON',
2133
+ },
2134
+ errors: {
2135
+ noProjectConfig: `No project config found. Run this command from within a HubSpot project directory, or use ${uiCommandReference('hs project create')} to create a new one.`,
2136
+ unsupportedPlatformVersion: (platformVersion) => `This command is only supported for projects on platform version 2025.2 or later (detected: ${platformVersion}).`,
2137
+ failedToParseProject: 'Failed to parse the project. Check your project configuration is valid and try again.',
2138
+ noAppInProject: 'No app was found in the local project. Install status is only available for projects that contain an app.',
2139
+ unsupportedAuthType: (authType) => `This command only supports static auth apps. Detected auth type: ${authType}.`,
2140
+ },
2141
+ success: {
2142
+ installed: (appName, accountId) => `${chalk.bold(appName)} is installed in ${uiAccountDescription(accountId)}.`,
2143
+ installedWithOutdatedScopes: (appName, accountId) => `${chalk.bold(appName)} is installed in ${uiAccountDescription(accountId)} with outdated scopes. Reinstall the app to grant the latest scopes.`,
2144
+ },
2145
+ notInstalled: (appName, accountId) => `${chalk.bold(appName)} is not installed in ${uiAccountDescription(accountId)}.`,
2146
+ },
2032
2147
  },
2033
2148
  sandbox: {
2034
2149
  describe: 'Commands for managing sandboxes.',
@@ -2344,7 +2459,7 @@ export const commands = {
2344
2459
  opsLevel: 'Operations Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2345
2460
  serviceLevel: 'Service Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2346
2461
  salesLevel: 'Sales Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2347
- contentLevel: 'CMS Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2462
+ contentLevel: 'Content Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2348
2463
  commerceLevel: 'Commerce Hub tier. Options: FREE, PROFESSIONAL, ENTERPRISE',
2349
2464
  },
2350
2465
  example: (configPath) => `Create a test account from the config file at ${configPath}`,
@@ -2997,6 +3112,9 @@ export const commands = {
2997
3112
  },
2998
3113
  };
2999
3114
  export const lib = {
3115
+ linkedDirectory: {
3116
+ warning: (action, settingsPath) => `This directory has linked accounts via ${settingsPath}. ${uiCommandReference(action)} modifies your global config, not the linked directory settings. Use ${uiCommandReference('hs account link')} to manage linked accounts.\n`,
3117
+ },
3000
3118
  parsing: {
3001
3119
  unableToParseStringToNumber: 'Unable to parse string to number',
3002
3120
  },
@@ -3310,6 +3428,16 @@ export const lib = {
3310
3428
  updatedFileDependency: (packageName, relativePath) => ` Updated dependencies.${packageName}: file:${relativePath}`,
3311
3429
  },
3312
3430
  },
3431
+ projectPreview: {
3432
+ triggeringPreview: (buildId, targetPortalId) => `Previewing build #${buildId} on portal ${targetPortalId}`,
3433
+ pollingStatus: (releaseTag, targetPortalId) => `Previewing ${releaseTag} on portal ${targetPortalId}`,
3434
+ succeeded: (releaseTag, targetPortalId) => `Previewed ${releaseTag} on portal ${targetPortalId}`,
3435
+ triggerFailed: 'Failed to trigger preview',
3436
+ pollFailed: 'Failed to poll preview status',
3437
+ timeout: 'Preview timed out after 5 minutes',
3438
+ warning: 'The build succeeded but the preview failed. You can manually preview this build from the project UI.',
3439
+ missingProjectId: 'Unable to preview: could not resolve the project ID.',
3440
+ },
3313
3441
  importData: {
3314
3442
  errors: {
3315
3443
  incorrectAccountType: (derivedAccountId) => `The account ${uiAccountDescription(derivedAccountId)} is not a standard account, developer test account, or app developer account.`,
@@ -3963,9 +4091,9 @@ export const lib = {
3963
4091
  validJson: 'JSON files valid',
3964
4092
  },
3965
4093
  port: {
3966
- inUse: (port) => `Port ${port} is in use`,
3967
- inUseSecondary: `Make sure it is available before running ${uiCommandReference('hs project dev')}`,
3968
- available: (port) => `Port ${port} available for local development`,
4094
+ inUse: (port) => `Default port ${port} is in use`,
4095
+ inUseSecondary: `Make sure it is available before running ${uiCommandReference('hs project dev')}, or use ${uiCommandReference('--port')} to specify a different port`,
4096
+ available: (port) => `Default port ${port} available for local development`,
3969
4097
  },
3970
4098
  projectValidation: {
3971
4099
  valid: 'Project configuration and structure is valid',
@@ -127,7 +127,8 @@ export async function selectAppToMigrate(allApps, derivedAccountId, appId) {
127
127
  let hasLegacyCrmCards = false;
128
128
  selectedApp?.migrationComponents.forEach(component => {
129
129
  const userFacingComponentType = mapToUserFacingType(component.componentType);
130
- if (component.componentType === 'LEGACY_CRM_CARD' && !component.isSupported) {
130
+ if (component.componentType === 'LEGACY_CRM_CARD' &&
131
+ !component.isSupported) {
131
132
  hasLegacyCrmCards = true;
132
133
  }
133
134
  const shouldDisplayComponent = !AUTO_GENERATED_COMPONENT_TYPES.includes(userFacingComponentType);
@@ -4,6 +4,7 @@ export declare const FEEDBACK_INTERVAL: 10;
4
4
  export declare const HUBSPOT_FOLDER: "@hubspot";
5
5
  export declare const MARKETPLACE_FOLDER: "@marketplace";
6
6
  export declare const DEFAULT_POLLING_DELAY = 2000;
7
+ export declare const PREVIEW_POLL_TIMEOUT: number;
7
8
  export declare const PROJECT_CONFIG_FILE: "hsproject.json";
8
9
  export declare const PROJECT_BUILD_STATES: {
9
10
  readonly BUILDING: "BUILDING";
@@ -142,3 +143,4 @@ export declare const ACCOUNT_LEVELS: {
142
143
  };
143
144
  export declare const ACCOUNT_LEVEL_CHOICES: readonly ["FREE", "STARTER", "PROFESSIONAL", "ENTERPRISE"];
144
145
  export declare const FEEDBACK_URL = "https://developers.hubspot.com/feedback";
146
+ export declare const LOCAL_DEV_DEFAULT_PORT = 4828;
package/lib/constants.js CHANGED
@@ -4,6 +4,7 @@ export const FEEDBACK_INTERVAL = 10;
4
4
  export const HUBSPOT_FOLDER = '@hubspot';
5
5
  export const MARKETPLACE_FOLDER = '@marketplace';
6
6
  export const DEFAULT_POLLING_DELAY = 2000;
7
+ export const PREVIEW_POLL_TIMEOUT = 5 * 60 * 1000;
7
8
  export const PROJECT_CONFIG_FILE = 'hsproject.json';
8
9
  export const PROJECT_BUILD_STATES = {
9
10
  BUILDING: 'BUILDING',
@@ -143,3 +144,6 @@ export const ACCOUNT_LEVEL_CHOICES = [
143
144
  ACCOUNT_LEVELS.ENTERPRISE,
144
145
  ];
145
146
  export const FEEDBACK_URL = 'https://developers.hubspot.com/feedback';
147
+ // TODO: remove this constant and use PORT_MANAGER_SERVER_PORT from
148
+ // @hubspot/local-dev-lib once LDL changes its default to 4828.
149
+ export const LOCAL_DEV_DEFAULT_PORT = 4828;
@@ -10,8 +10,8 @@ import fs from 'fs';
10
10
  import path from 'path';
11
11
  import { Diagnosis } from './Diagnosis.js';
12
12
  import { DiagnosticInfoBuilder, } from './DiagnosticInfoBuilder.js';
13
- import { isPortManagerPortAvailable } from '@hubspot/local-dev-lib/portManager';
14
- import { PORT_MANAGER_SERVER_PORT } from '@hubspot/local-dev-lib/constants/ports';
13
+ import { isPortAvailable } from '@hubspot/local-dev-lib/portManager';
14
+ import { LOCAL_DEV_DEFAULT_PORT } from '../constants.js';
15
15
  import { accessTokenForPersonalAccessKey,
16
16
  // scopesOnAccessToken,
17
17
  } from '@hubspot/local-dev-lib/personalAccessKey';
@@ -322,16 +322,16 @@ export class Doctor {
322
322
  }
323
323
  }
324
324
  async checkIfPortsAreAvailable() {
325
- if (await isPortManagerPortAvailable()) {
325
+ if (await isPortAvailable(LOCAL_DEV_DEFAULT_PORT)) {
326
326
  this.diagnosis?.addProjectSection({
327
327
  type: 'success',
328
- message: lib.doctor.port.available(PORT_MANAGER_SERVER_PORT),
328
+ message: lib.doctor.port.available(LOCAL_DEV_DEFAULT_PORT),
329
329
  });
330
330
  return;
331
331
  }
332
332
  this.diagnosis?.addProjectSection({
333
333
  type: 'warning',
334
- message: lib.doctor.port.inUse(PORT_MANAGER_SERVER_PORT),
334
+ message: lib.doctor.port.inUse(LOCAL_DEV_DEFAULT_PORT),
335
335
  secondaryMessaging: lib.doctor.port.inUseSecondary,
336
336
  });
337
337
  }
@@ -0,0 +1,10 @@
1
+ export interface AccountRow {
2
+ name: string;
3
+ accountId: string;
4
+ }
5
+ export declare function buildAccountRow(accountId: number, isDefault: boolean): AccountRow;
6
+ export declare function buildAccountHeader(nameColumnWidth: number): string;
7
+ export declare function getNameColumnWidth(rows: AccountRow[]): number;
8
+ export declare function sortDefaultFirst<T extends number | {
9
+ accountId: number;
10
+ }>(items: T[], defaultAccount: number | undefined): T[];
@@ -0,0 +1,39 @@
1
+ import { HUBSPOT_ACCOUNT_TYPE_STRINGS } from '@hubspot/local-dev-lib/constants/config';
2
+ import { getConfigAccountIfExists } from '@hubspot/local-dev-lib/config';
3
+ import chalk from 'chalk';
4
+ import { commands } from '../../lang/en.js';
5
+ import { indent } from '../ui/index.js';
6
+ import { INK_COLORS } from '../../ui/styles.js';
7
+ export function buildAccountRow(accountId, isDefault) {
8
+ const account = getConfigAccountIfExists(accountId);
9
+ let name = String(accountId);
10
+ if (account && account.accountType) {
11
+ const typeStr = HUBSPOT_ACCOUNT_TYPE_STRINGS[account.accountType];
12
+ name = `${account.name} [${typeStr}]`;
13
+ }
14
+ if (isDefault) {
15
+ name = `${name} (default)`;
16
+ }
17
+ return { name, accountId: String(accountId) };
18
+ }
19
+ export function buildAccountHeader(nameColumnWidth) {
20
+ const labels = commands.account.subcommands.list.labels;
21
+ const paddedName = chalk.bold(chalk.hex(INK_COLORS.INFO_BLUE)(labels.name.padEnd(nameColumnWidth)));
22
+ const accountId = chalk.bold(chalk.hex(INK_COLORS.INFO_BLUE)(labels.accountId));
23
+ return `${indent(1)}${paddedName} ${accountId}`;
24
+ }
25
+ export function getNameColumnWidth(rows) {
26
+ const labels = commands.account.subcommands.list.labels;
27
+ return Math.max(labels.name.length, ...rows.map(r => r.name.length));
28
+ }
29
+ export function sortDefaultFirst(items, defaultAccount) {
30
+ return [...items].sort((a, b) => {
31
+ const aId = typeof a === 'number' ? a : a.accountId;
32
+ const bId = typeof b === 'number' ? b : b.accountId;
33
+ if (aId === defaultAccount)
34
+ return -1;
35
+ if (bId === defaultAccount)
36
+ return 1;
37
+ return 0;
38
+ });
39
+ }
@@ -0,0 +1,18 @@
1
+ import { HsSettingsFile } from '@hubspot/local-dev-lib/types/HsSettings';
2
+ import { ActionHandlerParams, ActionResult, LinkArgs } from '../../types/Link.js';
3
+ import { ArgumentsCamelCase } from 'yargs';
4
+ export declare class ActionHandlers {
5
+ static link({ state, context, }: ActionHandlerParams): Promise<ActionResult>;
6
+ static unlink({ state }: ActionHandlerParams): Promise<ActionResult>;
7
+ static authenticate({ state, context, args, }: ActionHandlerParams): Promise<ActionResult>;
8
+ static cancel(): Promise<ActionResult>;
9
+ }
10
+ export declare function handleLinkFlow({ settings, accountOverrideId, args, }: {
11
+ settings: HsSettingsFile;
12
+ accountOverrideId: number | null;
13
+ args: ArgumentsCamelCase<LinkArgs>;
14
+ }): Promise<ActionResult>;
15
+ export declare function handleLinkedUseAction({ state, targetAccountId, }: {
16
+ state: HsSettingsFile;
17
+ targetAccountId?: number;
18
+ }): Promise<ActionResult>;