@hubspot/cli 8.2.1-experimental.0 → 8.2.1-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/commands/cms/function/server.js +14 -7
  2. package/commands/cms/theme/preview.js +8 -1
  3. package/commands/getStarted.js +15 -20
  4. package/commands/project/add.js +7 -6
  5. package/commands/project/appInstallStatus.js +7 -3
  6. package/commands/project/create.js +9 -6
  7. package/commands/project/deploy.js +13 -11
  8. package/commands/project/dev/index.js +9 -7
  9. package/commands/project/dev/unifiedFlow.js +8 -0
  10. package/commands/project/download.js +3 -3
  11. package/commands/project/info.js +7 -4
  12. package/commands/project/installApp.js +8 -3
  13. package/commands/project/installDeps.js +6 -5
  14. package/commands/project/lint.js +16 -9
  15. package/commands/project/listBuilds.d.ts +3 -2
  16. package/commands/project/listBuilds.js +40 -13
  17. package/commands/project/migrate.js +14 -12
  18. package/commands/project/open.js +15 -7
  19. package/commands/project/profile/add.js +8 -4
  20. package/commands/project/profile/delete.js +8 -4
  21. package/commands/project/release/create.js +4 -6
  22. package/commands/project/release/info.js +3 -3
  23. package/commands/project/release/list.js +3 -3
  24. package/commands/project/updateDeps.js +6 -5
  25. package/commands/project/upload.js +26 -15
  26. package/commands/project/validate.js +14 -97
  27. package/commands/project/watch.js +8 -11
  28. package/commands/project.js +1 -1
  29. package/lang/en.d.ts +19 -18
  30. package/lang/en.js +23 -20
  31. package/lib/app/migrate.js +5 -7
  32. package/lib/cms/serverlessDevRuntime.d.ts +1 -0
  33. package/lib/cms/serverlessDevRuntime.js +72 -0
  34. package/lib/constants.d.ts +2 -0
  35. package/lib/constants.js +2 -0
  36. package/lib/dependencyManagement.js +3 -10
  37. package/lib/doctor/DiagnosticInfoBuilder.d.ts +2 -2
  38. package/lib/doctor/DiagnosticInfoBuilder.js +8 -8
  39. package/lib/doctor/Doctor.js +7 -9
  40. package/lib/errors/ProjectErrors.d.ts +0 -6
  41. package/lib/errors/ProjectErrors.js +0 -12
  42. package/lib/errors/ProjectValidationError.d.ts +2 -1
  43. package/lib/errors/ProjectValidationError.js +3 -1
  44. package/lib/getStartedV2Actions.js +14 -18
  45. package/lib/jsonOutput.d.ts +86 -0
  46. package/lib/jsonOutput.js +77 -0
  47. package/lib/npm/packageJson.js +1 -1
  48. package/lib/projects/ProjectLogsManager.js +1 -4
  49. package/lib/projects/config.d.ts +6 -5
  50. package/lib/projects/config.js +7 -38
  51. package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
  52. package/lib/projects/localDev/LocalDevLogger.js +18 -0
  53. package/lib/projects/localDev/LocalDevProcess.d.ts +11 -1
  54. package/lib/projects/localDev/LocalDevProcess.js +91 -9
  55. package/lib/projects/localDev/LocalDevState.d.ts +7 -1
  56. package/lib/projects/localDev/LocalDevState.js +19 -1
  57. package/lib/projects/localDev/LocalDevWatcher.js +1 -1
  58. package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +1 -0
  59. package/lib/projects/localDev/LocalDevWebsocketServer.js +23 -1
  60. package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +2 -1
  61. package/lib/projects/localDev/localDevWebsocketServerUtils.js +3 -0
  62. package/lib/projects/projectProfiles.d.ts +2 -2
  63. package/lib/projects/projectProfiles.js +0 -3
  64. package/lib/projects/release.js +1 -1
  65. package/lib/projects/uieLinting.js +0 -3
  66. package/lib/projects/validate.d.ts +9 -0
  67. package/lib/projects/validate.js +96 -0
  68. package/lib/theme/migrate.js +1 -4
  69. package/mcp-server/tools/project/GetBuildLogsTool.js +2 -3
  70. package/mcp-server/tools/project/GetBuildStatusTool.js +2 -3
  71. package/mcp-server/tools/project/UploadProjectTools.js +3 -5
  72. package/package.json +11 -10
  73. package/types/LocalDev.d.ts +7 -0
  74. package/types/Projects.d.ts +1 -5
  75. package/types/Yargs.d.ts +1 -0
package/lang/en.d.ts CHANGED
@@ -1466,7 +1466,6 @@ export declare const commands: {
1466
1466
  duplicateTargetAccount: (targetAccountId: number) => string;
1467
1467
  };
1468
1468
  errors: {
1469
- noProjectConfig: string;
1470
1469
  unsupportedPlatformVersion: string;
1471
1470
  profileExists: (profileName: string) => string;
1472
1471
  invalidTargetAccount: string;
@@ -1499,7 +1498,6 @@ export declare const commands: {
1499
1498
  deleteProjectPrompt: (accountId: number) => string;
1500
1499
  };
1501
1500
  errors: {
1502
- noProjectConfig: string;
1503
1501
  unsupportedPlatformVersion: string;
1504
1502
  noProfileFound: (profileName: string) => string;
1505
1503
  noProfilesFound: string;
@@ -1523,12 +1521,12 @@ export declare const commands: {
1523
1521
  testingAccountFlagExplanation: (accountId: number) => string;
1524
1522
  };
1525
1523
  errors: {
1526
- noProjectConfig: string;
1527
1524
  noAccount: (accountId: number) => string;
1528
1525
  unsupportedPlatformVersion: (platformVersion: string) => string;
1529
1526
  noRunnableComponents: string;
1530
1527
  accountNotCombined: string;
1531
1528
  localDevAlreadyRunning: string;
1529
+ autoUploadRequiresAutoDeploy: string;
1532
1530
  };
1533
1531
  examples: {
1534
1532
  default: string;
@@ -1541,6 +1539,7 @@ export declare const commands: {
1541
1539
  projectAccount: string;
1542
1540
  testingAccount: string;
1543
1541
  port: string;
1542
+ autoUpload: string;
1544
1543
  };
1545
1544
  };
1546
1545
  create: {
@@ -1842,6 +1841,8 @@ export declare const commands: {
1842
1841
  };
1843
1842
  examples: {
1844
1843
  default: string;
1844
+ withLimit: string;
1845
+ json: string;
1845
1846
  };
1846
1847
  };
1847
1848
  logs: {
@@ -1908,9 +1909,11 @@ export declare const commands: {
1908
1909
  autoDeployDisabled: (deployCommand: string) => string;
1909
1910
  autoDeploySkipped: (deployCommand: string) => string;
1910
1911
  releaseManagementRequired: (releaseCommand: string) => string;
1912
+ uploadingWithProfile: (profileName: string, accountId: number) => string;
1913
+ uploadingToAccount: (accountId: number) => string;
1914
+ uploadedToAccount: (accountId: number) => string;
1911
1915
  };
1912
1916
  errors: {
1913
- noProjectConfig: string;
1914
1917
  projectLockedError: string;
1915
1918
  previewRequiresTarget: string;
1916
1919
  targetRequiresPreview: string;
@@ -1972,7 +1975,6 @@ export declare const commands: {
1972
1975
  fileAlreadyQueued: (filePath: string) => string;
1973
1976
  };
1974
1977
  errors: {
1975
- projectConfigNotFound: string;
1976
1978
  projectLockedError: string;
1977
1979
  uploadFailed: (remotePath: string, filePath: string) => string;
1978
1980
  deleteFileFailed: (remotePath: string) => string;
@@ -2039,7 +2041,6 @@ export declare const commands: {
2039
2041
  installationSuccessful: (directory: string) => string;
2040
2042
  addingDependenciesToLocation: (dependencies: string, directory: string) => string;
2041
2043
  installingDependenciesFailed: (directory: string) => string;
2042
- noProjectConfig: string;
2043
2044
  noPackageJsonInProject: (projectName: string) => string;
2044
2045
  packageManagerNotInstalled: (packageManager: string) => string;
2045
2046
  };
@@ -2057,7 +2058,6 @@ export declare const commands: {
2057
2058
  addingLintScripts: string;
2058
2059
  linting: string;
2059
2060
  };
2060
- noProjectConfig: string;
2061
2061
  failedToReadPackageJson: (packageJsonPath: string) => string;
2062
2062
  installLintPackagesPrompt: (directories: string[], missingPackages: string[]) => string;
2063
2063
  skippingDirectoriesWarning: (directories: string[]) => string;
@@ -2087,7 +2087,6 @@ export declare const commands: {
2087
2087
  updateSuccessful: (directory: string) => string;
2088
2088
  updatingDependenciesToLocation: (dependencies: string, directory: string) => string;
2089
2089
  updatingDependenciesFailed: (directory: string) => string;
2090
- noProjectConfig: string;
2091
2090
  noPackageJsonInProject: (projectName: string) => string;
2092
2091
  packageManagerNotInstalled: (packageManager: string) => string;
2093
2092
  };
@@ -2157,7 +2156,6 @@ export declare const commands: {
2157
2156
  uid: string;
2158
2157
  };
2159
2158
  errors: {
2160
- noProjectConfig: string;
2161
2159
  projectNotFound: (projectName: string, accountId: number) => string;
2162
2160
  noDeployedBuild: string;
2163
2161
  unsupportedPlatformVersion: (platformVersion: string) => string;
@@ -2219,7 +2217,6 @@ export declare const commands: {
2219
2217
  profile: string;
2220
2218
  };
2221
2219
  errors: {
2222
- noProjectConfig: string;
2223
2220
  unsupportedPlatformVersion: (platformVersion: string) => string;
2224
2221
  failedToParseProject: string;
2225
2222
  noAppInProject: string;
@@ -2253,7 +2250,6 @@ export declare const commands: {
2253
2250
  json: string;
2254
2251
  };
2255
2252
  errors: {
2256
- noProjectConfig: string;
2257
2253
  unsupportedPlatformVersion: (platformVersion: string) => string;
2258
2254
  failedToParseProject: string;
2259
2255
  noAppInProject: string;
@@ -3376,6 +3372,7 @@ export declare const lib: {
3376
3372
  learnMoreLocalDevServer: string;
3377
3373
  running: (projectName: string, accountIdentifier: string) => string;
3378
3374
  quitHelper: string;
3375
+ autoUploadEnabled: string;
3379
3376
  viewProjectLink: (name: string, accountId: number) => string;
3380
3377
  viewLocalDevUILink: (accountId: number, showWelcomeScreen: boolean) => string;
3381
3378
  localDevUIAutoMessage: (accountId: number, showWelcomeScreen: boolean) => string;
@@ -3467,6 +3464,10 @@ export declare const lib: {
3467
3464
  LocalDevProcess: {
3468
3465
  projectConfigMismatch: string;
3469
3466
  uploadInitiated: string;
3467
+ autoUploadScheduled: (filePath: string) => string;
3468
+ autoUploadTriggered: string;
3469
+ autoUploadInProgress: string;
3470
+ autoUploadToggled: (enabled: boolean) => string;
3470
3471
  deployInitiated: string;
3471
3472
  uploadFailed: string;
3472
3473
  deployFailed: string;
@@ -3539,7 +3540,6 @@ export declare const lib: {
3539
3540
  };
3540
3541
  loadProfile: {
3541
3542
  errors: {
3542
- noProjectConfig: string;
3543
3543
  profileNotFound: (profileName: string) => string;
3544
3544
  missingAccountId: (profileName: string) => string;
3545
3545
  listedAccountNotFound: (accountId: number, profileName: string) => string;
@@ -3581,12 +3581,6 @@ export declare const lib: {
3581
3581
  };
3582
3582
  validateProjectConfig: {
3583
3583
  configNotFound: string;
3584
- configMissingFields: (missingFields: string[]) => string;
3585
- srcDirNotFound: (srcDir: string, projectDir: string) => string;
3586
- srcOutsideProjectDir: (projectConfig: string, srcDir: string) => string;
3587
- };
3588
- getProjectConfig: {
3589
- error: string;
3590
3584
  };
3591
3585
  platformVersion: {
3592
3586
  unsupported: (currentCliVersion: string, latestSupported: string, platformVersion?: string) => string;
@@ -4495,6 +4489,13 @@ export declare const lib: {
4495
4489
  copyingProjectFilesFailed: string;
4496
4490
  };
4497
4491
  };
4492
+ cms: {
4493
+ serverlessDevRuntime: {
4494
+ installStarted: (targetVersion: string) => string;
4495
+ installSucceeded: string;
4496
+ installFailed: string;
4497
+ };
4498
+ };
4498
4499
  theme: {
4499
4500
  cmsDevServerProcess: {
4500
4501
  installStarted: (targetVersion: string) => string;
package/lang/en.js CHANGED
@@ -1482,7 +1482,6 @@ export const commands = {
1482
1482
  duplicateTargetAccount: (targetAccountId) => `The account ${uiAccountDescription(targetAccountId)} is being used in an existing profile. Make sure to edit your project's name between uploads if you do not want to overwrite the existing project in this account.`,
1483
1483
  },
1484
1484
  errors: {
1485
- noProjectConfig: 'No project config found. Please run this command from a project directory.',
1486
1485
  unsupportedPlatformVersion: 'This command is only available for projects 2025.2 and later.',
1487
1486
  profileExists: (profileName) => `Profile ${chalk.bold(profileName)} already exists. Please choose a different name.`,
1488
1487
  invalidTargetAccount: 'Target account is not configured in the CLI',
@@ -1515,7 +1514,6 @@ export const commands = {
1515
1514
  deleteProjectPrompt: (accountId) => `Would you like to delete this project from ${uiAccountDescription(accountId)}?`,
1516
1515
  },
1517
1516
  errors: {
1518
- noProjectConfig: 'No project config found. Please run this command from a project directory.',
1519
1517
  unsupportedPlatformVersion: 'This command is only available for projects 2025.2 and later.',
1520
1518
  noProfileFound: (profileName) => `No profile with filename ${chalk.bold(profileName)} found in your project.`,
1521
1519
  noProfilesFound: 'No profiles found in your project.',
@@ -1539,12 +1537,12 @@ export const commands = {
1539
1537
  testingAccountFlagExplanation: (accountId) => `Using account ${uiAccountDescription(accountId)} provided by the --testing-account flag for testing`,
1540
1538
  },
1541
1539
  errors: {
1542
- noProjectConfig: 'No project detected. Please run this command again from a project directory.',
1543
1540
  noAccount: (accountId) => `An error occurred while reading account ${uiAccountDescription(accountId)} from your config. Run ${uiAuthCommandReference()} to re-auth this account.`,
1544
1541
  unsupportedPlatformVersion: (platformVersion) => `Local development is not supported for platform version ${chalk.bold(platformVersion)}. Please upgrade to 2025.2 or later.`,
1545
1542
  noRunnableComponents: `No supported components were found in this project. Run ${uiCommandReference('hs project add')} to see a list of available components and add one to your project.`,
1546
1543
  accountNotCombined: `\nLocal development of unified apps is currently only compatible with accounts that are opted into the unified apps beta. Make sure that this account is opted in or switch accounts using ${uiCommandReference('hs account use')}.`,
1547
1544
  localDevAlreadyRunning: `Another ${uiCommandReference('hs project dev')} process is already running. To proceed with local development of this project, stop the existing process and re-run ${uiCommandReference('hs project dev')}.`,
1545
+ autoUploadRequiresAutoDeploy: `${uiCommandReference('hs project dev --auto-upload')} requires auto-deploy to be enabled for this project, since auto-upload deploys every change. Enable auto-deploy in your project settings, or run ${uiCommandReference('hs project dev')} without it.`,
1548
1546
  },
1549
1547
  examples: {
1550
1548
  default: 'Start local dev for the current project',
@@ -1557,6 +1555,7 @@ export const commands = {
1557
1555
  projectAccount: 'The id of the account to upload your project to. Must be used with --testing-account.',
1558
1556
  testingAccount: 'The id of the account to install apps and test on. Must be used with --project-account.',
1559
1557
  port: `The port for the local dev server. Defaults to ${LOCAL_DEV_DEFAULT_PORT}.`,
1558
+ autoUpload: 'Automatically upload local changes to HubSpot as you edit files, instead of uploading manually from the Local Dev Panel.',
1560
1559
  },
1561
1560
  },
1562
1561
  create: {
@@ -1858,6 +1857,8 @@ export const commands = {
1858
1857
  },
1859
1858
  examples: {
1860
1859
  default: 'List the builds for the current project',
1860
+ withLimit: 'List the five most recent builds for the current project',
1861
+ json: 'Output the builds for the current project as JSON',
1861
1862
  },
1862
1863
  },
1863
1864
  logs: {
@@ -1924,9 +1925,11 @@ export const commands = {
1924
1925
  autoDeployDisabled: (deployCommand) => `Automatic deploys are disabled for this project. Run ${uiCommandReference(deployCommand)} to deploy this build.`,
1925
1926
  autoDeploySkipped: (deployCommand) => `Auto-deploy was skipped for this build. Run ${uiCommandReference(deployCommand)} to deploy this build.`,
1926
1927
  releaseManagementRequired: (releaseCommand) => `Run ${uiCommandReference(releaseCommand)} to create a release for this build.`,
1928
+ uploadingWithProfile: (profileName, accountId) => `Initializing project upload with ${chalk.bold(profileName)} profile: ${uiAccountDescription(accountId)}`,
1929
+ uploadingToAccount: (accountId) => `Initializing project upload to account ${uiAccountDescription(accountId)}`,
1930
+ uploadedToAccount: (accountId) => `Project uploaded to ${uiAccountDescription(accountId)}`,
1927
1931
  },
1928
1932
  errors: {
1929
- noProjectConfig: 'No project detected. Run this command from a project directory.',
1930
1933
  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.`,
1931
1934
  previewRequiresTarget: `${uiCommandReference('--preview')} requires ${uiCommandReference('--target=<portalId>')} to specify the portal to preview on.`,
1932
1935
  targetRequiresPreview: `${uiCommandReference('--target')} can only be used with ${uiCommandReference('--preview')}.`,
@@ -1988,7 +1991,6 @@ export const commands = {
1988
1991
  fileAlreadyQueued: (filePath) => `File "${filePath}" is already queued for upload`,
1989
1992
  },
1990
1993
  errors: {
1991
- projectConfigNotFound: 'No project config found. Please ensure that you are in a project directory.',
1992
1994
  projectLockedError: `Your project is locked. This may mean that another user is running the ${chalk.bold(`hs project dev`)} command for this project. If this is you, unlock the project in Projects UI.`,
1993
1995
  uploadFailed: (remotePath, filePath) => `Failed to upload file "${filePath}" to "${remotePath}"`,
1994
1996
  deleteFileFailed: (remotePath) => `Failed to delete file "${remotePath}"`,
@@ -2055,7 +2057,6 @@ export const commands = {
2055
2057
  installationSuccessful: (directory) => `Installed dependencies in ${directory}`,
2056
2058
  addingDependenciesToLocation: (dependencies, directory) => `Installing ${dependencies} in ${directory}`,
2057
2059
  installingDependenciesFailed: (directory) => `Installing dependencies for ${directory} failed`,
2058
- noProjectConfig: 'No project detected. Run this command from a project directory.',
2059
2060
  noPackageJsonInProject: (projectName) => `No dependencies to install. The project ${projectName} folder might be missing component or subcomponent files. ${uiLink('Learn how to create a project from scratch', 'https://developers.hubspot.com/docs/apps/developer-platform/build-apps/create-an-app#customize-a-new-project-using-the-cli')}`,
2060
2061
  packageManagerNotInstalled: (packageManager) => `This command depends on ${packageManager}, install ${uiLink(packageManager, 'https://docs.npmjs.com/downloading-and-installing-node-js-and-npm')}`,
2061
2062
  },
@@ -2073,7 +2074,6 @@ export const commands = {
2073
2074
  addingLintScripts: 'Adding lint scripts to package.json files…',
2074
2075
  linting: 'Linting…',
2075
2076
  },
2076
- noProjectConfig: 'No project detected. Run this command from a project directory.',
2077
2077
  failedToReadPackageJson: (packageJsonPath) => `Failed to read package.json at ${packageJsonPath}`,
2078
2078
  installLintPackagesPrompt: (directories, missingPackages) => {
2079
2079
  const uniquePackages = Array.from(new Set(missingPackages)).sort();
@@ -2115,7 +2115,6 @@ export const commands = {
2115
2115
  updateSuccessful: (directory) => `Updated dependencies in ${directory}`,
2116
2116
  updatingDependenciesToLocation: (dependencies, directory) => `Updating ${dependencies} in ${directory}`,
2117
2117
  updatingDependenciesFailed: (directory) => `Updating dependencies for ${directory} failed`,
2118
- noProjectConfig: 'No project detected. Run this command from a project directory.',
2119
2118
  noPackageJsonInProject: (projectName) => `No dependencies to update. The project ${projectName} folder might be missing component or subcomponent files. ${uiLink('Learn how to create a project from scratch', 'https://developers.hubspot.com/docs/apps/developer-platform/build-apps/create-an-app#customize-a-new-project-using-the-cli')}`,
2120
2119
  packageManagerNotInstalled: (packageManager) => `This command depends on ${packageManager}, install ${uiLink(packageManager, 'https://docs.npmjs.com/downloading-and-installing-node-js-and-npm')}`,
2121
2120
  },
@@ -2185,7 +2184,6 @@ export const commands = {
2185
2184
  uid: 'UID',
2186
2185
  },
2187
2186
  errors: {
2188
- noProjectConfig: `No project found in this directory.\n\nRun ${uiCommandReference('hs project create')} to start a new project, or change to a directory containing hsproject.json.`,
2189
2187
  projectNotFound: (projectName, accountId) => `Project "${projectName}" was not found in account ${uiAccountDescription(accountId)}. Make sure the project has been uploaded at least once.`,
2190
2188
  noDeployedBuild: `This project has not been deployed yet.\n\nRun ${uiCommandReference('hs project deploy')} to deploy your project.`,
2191
2189
  unsupportedPlatformVersion: (platformVersion) => `This command is not supported for platform version ${chalk.bold(platformVersion)}. Please upgrade to 2025.2 or later.`,
@@ -2244,7 +2242,6 @@ export const commands = {
2244
2242
  profile: 'The profile to target with this install',
2245
2243
  },
2246
2244
  errors: {
2247
- 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.`,
2248
2245
  unsupportedPlatformVersion: (platformVersion) => `This command is only supported for projects on platform version 2025.2 or later (detected: ${platformVersion}).`,
2249
2246
  failedToParseProject: 'Failed to parse the project. Check your project configuration is valid and try again.',
2250
2247
  noAppInProject: 'No app was found in the local project. This command requires a project that contains an app.',
@@ -2278,7 +2275,6 @@ export const commands = {
2278
2275
  json: 'Output install status as JSON',
2279
2276
  },
2280
2277
  errors: {
2281
- 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.`,
2282
2278
  unsupportedPlatformVersion: (platformVersion) => `This command is only supported for projects on platform version 2025.2 or later (detected: ${platformVersion}).`,
2283
2279
  failedToParseProject: 'Failed to parse the project. Check your project configuration is valid and try again.',
2284
2280
  noAppInProject: 'No app was found in the local project. Install status is only available for projects that contain an app.',
@@ -3401,6 +3397,7 @@ export const lib = {
3401
3397
  learnMoreLocalDevServer: uiLink('Learn more about the projects local dev server', 'https://developers.hubspot.com/docs/developer-tooling/local-development/hubspot-cli/project-commands'),
3402
3398
  running: (projectName, accountIdentifier) => chalk.hex(UI_COLORS.SORBET)(`Running ${chalk.bold(projectName)} locally on ${accountIdentifier}, waiting for changes ...`),
3403
3399
  quitHelper: `Press ${chalk.bold('q')} to stop the local dev server`,
3400
+ autoUploadEnabled: `${chalk.bold('Auto-upload is on.')} Changes you make locally will be uploaded to HubSpot automatically.`,
3404
3401
  viewProjectLink: (name, accountId) => uiLink('View project in HubSpot', getProjectDetailUrl(name, accountId) || ''),
3405
3402
  viewLocalDevUILink: (accountId, showWelcomeScreen) => uiLink('View local dev session in HubSpot', getLocalDevUiUrl(accountId, showWelcomeScreen)),
3406
3403
  localDevUIAutoMessage: (accountId, showWelcomeScreen) => `Opening your ${uiLink('local dev session in HubSpot', getLocalDevUiUrl(accountId, showWelcomeScreen))}...`,
@@ -3491,8 +3488,14 @@ export const lib = {
3491
3488
  },
3492
3489
  LocalDevProcess: {
3493
3490
  projectConfigMismatch: `Unable to upload project. The project config has been modified since starting ${uiCommandReference('hs project dev')}.`,
3494
- uploadInitiated: 'Project upload initiated from Local Dev UI.',
3495
- deployInitiated: 'Project deploy initiated from Local Dev UI.',
3491
+ uploadInitiated: 'Project upload initiated.',
3492
+ autoUploadScheduled: (filePath) => `Auto-upload: detected change to ${filePath}, upload scheduled.`,
3493
+ autoUploadTriggered: 'Auto-upload: debounce elapsed, uploading now.',
3494
+ autoUploadInProgress: 'Auto-upload: an upload is already running, will re-run after it finishes.',
3495
+ autoUploadToggled: (enabled) => enabled
3496
+ ? `${chalk.bold('Auto-upload turned on')} from the Local Dev Panel. Changes you make locally will be uploaded to HubSpot automatically.`
3497
+ : `${chalk.bold('Auto-upload turned off')} from the Local Dev Panel. Upload manually from the Local Dev Panel to push your changes.`,
3498
+ deployInitiated: 'Project deploy initiated from the Local Dev Panel.',
3496
3499
  uploadFailed: 'Project upload failed. To proceed with local development, fix any necessary errors, then re-upload your project.',
3497
3500
  deployFailed: 'Project deploy failed. To proceed with local development, fix any necessary errors, then re-deploy your project.',
3498
3501
  uploadSuccess: 'Project upload completed successfully. Resuming local dev...',
@@ -3576,7 +3579,6 @@ export const lib = {
3576
3579
  },
3577
3580
  loadProfile: {
3578
3581
  errors: {
3579
- noProjectConfig: 'No project config found. Please run this command from a project directory.',
3580
3582
  profileNotFound: (profileName) => `Profile ${chalk.bold(profileName)} not found.`,
3581
3583
  missingAccountId: (profileName) => `Profile ${chalk.bold(profileName)} is missing an account id.`,
3582
3584
  listedAccountNotFound: (accountId, profileName) => `The account ${uiAccountDescription(accountId)} is defined in your profile ${chalk.bold(profileName)}, but is missing in your config file`,
@@ -3618,12 +3620,6 @@ export const lib = {
3618
3620
  },
3619
3621
  validateProjectConfig: {
3620
3622
  configNotFound: `Unable to locate a project configuration file. Try running again from a project directory, or run ${uiCommandReference('hs project create')} to create a new project.`,
3621
- configMissingFields: (missingFields) => `The project configuration file is missing required field${missingFields.length > 1 ? 's' : ''}: ${missingFields.map(f => chalk.bold(f)).join(', ')}`,
3622
- srcDirNotFound: (srcDir, projectDir) => `Project source directory ${chalk.bold(srcDir)} could not be found in ${chalk.bold(projectDir)}.`,
3623
- srcOutsideProjectDir: (projectConfig, srcDir) => `Invalid value for 'srcDir' in ${projectConfig}: ${chalk.bold(`srcDir: "${srcDir}"`)}\n\t'srcDir' must be a relative path to a folder under the project root, such as "." or "./src"`,
3624
- },
3625
- getProjectConfig: {
3626
- error: 'Could not read from project config',
3627
3623
  },
3628
3624
  platformVersion: {
3629
3625
  unsupported: (currentCliVersion, latestSupported, platformVersion) => `Platform version${platformVersion ? ' ' + chalk.bold(platformVersion) : ''} is not officially supported by this version of the CLI (${chalk.bold(currentCliVersion)}).\n\nUpdate your CLI to the latest version with ${uiCommandReference('hs upgrade')} or use a compatible platform version (${chalk.bold(latestSupported)} or earlier).`,
@@ -4532,6 +4528,13 @@ export const lib = {
4532
4528
  copyingProjectFilesFailed: 'Unable to copy migrated project files',
4533
4529
  },
4534
4530
  },
4531
+ cms: {
4532
+ serverlessDevRuntime: {
4533
+ installStarted: (targetVersion) => `Installing serverless-dev-runtime ${targetVersion}...`,
4534
+ installSucceeded: 'serverless-dev-runtime setup complete',
4535
+ installFailed: 'Failed to install serverless-dev-runtime',
4536
+ },
4537
+ },
4535
4538
  theme: {
4536
4539
  cmsDevServerProcess: {
4537
4540
  installStarted: (targetVersion) => `Installing cms-dev-server ${targetVersion}...`,
@@ -39,7 +39,7 @@ export function getUnmigratableReason(reasonCode, projectName, accountId) {
39
39
  export function generateFilterAppsByProjectNameFunction(projectConfig) {
40
40
  return (app) => {
41
41
  if (projectConfig) {
42
- return app.projectName === projectConfig?.projectConfig?.name;
42
+ return app.projectName === projectConfig.projectConfig.name;
43
43
  }
44
44
  return true;
45
45
  };
@@ -70,7 +70,7 @@ export async function validateMigrationApps(appId, derivedAccountId, { migratabl
70
70
  if (allApps.length > 1 && projectConfig) {
71
71
  throw new Error(lib.migrate.errors.project.multipleApps);
72
72
  }
73
- if (!projectConfig?.projectConfig) {
73
+ if (!projectConfig) {
74
74
  allApps.forEach(app => {
75
75
  if (app.projectName) {
76
76
  app.isMigratable = false;
@@ -80,7 +80,7 @@ export async function validateMigrationApps(appId, derivedAccountId, { migratabl
80
80
  });
81
81
  }
82
82
  if (allApps.length === 0 && projectConfig) {
83
- throw new Error(lib.migrate.errors.noAppsForProject(projectConfig?.projectConfig?.name || ''));
83
+ throw new Error(lib.migrate.errors.noAppsForProject(projectConfig.projectConfig.name));
84
84
  }
85
85
  if (allApps.length === 0 || !allApps.some(app => app.isMigratable)) {
86
86
  const reasons = unmigratableApps.map(app => `${chalk.bold(app.appName)}: ${getUnmigratableReason(app.unmigratableReason, app.projectName, derivedAccountId)}`);
@@ -173,9 +173,7 @@ export async function handleMigrationSetup(derivedAccountId, options, projectCon
173
173
  return {};
174
174
  }
175
175
  // If it's a project we don't want to prompt for dest and name, so just return early
176
- if (projectConfig &&
177
- projectConfig?.projectConfig &&
178
- projectConfig?.projectDir) {
176
+ if (projectConfig) {
179
177
  return {
180
178
  appIdToMigrate,
181
179
  projectName: projectConfig.projectConfig.name,
@@ -309,7 +307,7 @@ export async function downloadProjectFiles(derivedAccountId, projectName, buildI
309
307
  });
310
308
  const { data: zippedProject } = await downloadProject(derivedAccountId, projectName, buildId);
311
309
  let absoluteDestPath;
312
- if (projectConfig?.projectConfig && projectConfig?.projectDir) {
310
+ if (projectConfig) {
313
311
  const { projectDir } = projectConfig;
314
312
  absoluteDestPath = projectDir;
315
313
  const { srcDir } = projectConfig.projectConfig;
@@ -0,0 +1 @@
1
+ export declare function startServerlessDevRuntime(options: any): Promise<void>;
@@ -0,0 +1,72 @@
1
+ import { spawn } from 'child_process';
2
+ import { createRequire } from 'module';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
+ import os from 'os';
6
+ import SpinniesManager from '../ui/SpinniesManager.js';
7
+ import { lib } from '../../lang/en.js';
8
+ const TARGET_SERVERLESS_RUNTIME_VERSION = '7.0.7';
9
+ const CACHE_DIR = path.join(os.homedir(), '.hscli', '.serverless-runtime-cache');
10
+ async function ensureServerlessRuntimeInstalled() {
11
+ const packageJsonPath = path.join(CACHE_DIR, 'node_modules', '@hubspot', 'serverless-dev-runtime', 'package.json');
12
+ let needsInstall = true;
13
+ if (fs.existsSync(packageJsonPath)) {
14
+ try {
15
+ const installed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
16
+ if (installed.version === TARGET_SERVERLESS_RUNTIME_VERSION) {
17
+ needsInstall = false;
18
+ }
19
+ }
20
+ catch {
21
+ needsInstall = true;
22
+ }
23
+ }
24
+ if (!needsInstall) {
25
+ return;
26
+ }
27
+ SpinniesManager.init({ succeedColor: 'white' });
28
+ SpinniesManager.add('serverless-runtime-install', {
29
+ text: lib.cms.serverlessDevRuntime.installStarted(TARGET_SERVERLESS_RUNTIME_VERSION),
30
+ });
31
+ fs.mkdirSync(CACHE_DIR, { recursive: true });
32
+ const nodeModulesDir = path.join(CACHE_DIR, 'node_modules');
33
+ if (fs.existsSync(nodeModulesDir)) {
34
+ fs.rmSync(nodeModulesDir, { recursive: true, force: true });
35
+ }
36
+ await new Promise((resolve, reject) => {
37
+ const installProcess = spawn('npm', [
38
+ 'install',
39
+ `@hubspot/serverless-dev-runtime@${TARGET_SERVERLESS_RUNTIME_VERSION}`,
40
+ '--production',
41
+ '--no-save',
42
+ '--loglevel=error',
43
+ ], { cwd: CACHE_DIR, stdio: 'ignore' });
44
+ installProcess.on('close', code => {
45
+ if (code === 0) {
46
+ SpinniesManager.succeed('serverless-runtime-install', {
47
+ text: lib.cms.serverlessDevRuntime.installSucceeded,
48
+ });
49
+ resolve();
50
+ }
51
+ else {
52
+ SpinniesManager.fail('serverless-runtime-install', {
53
+ text: lib.cms.serverlessDevRuntime.installFailed,
54
+ });
55
+ reject(new Error(lib.cms.serverlessDevRuntime.installFailed));
56
+ }
57
+ });
58
+ installProcess.on('error', error => {
59
+ SpinniesManager.fail('serverless-runtime-install', {
60
+ text: lib.cms.serverlessDevRuntime.installFailed,
61
+ });
62
+ reject(error);
63
+ });
64
+ });
65
+ }
66
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
+ export async function startServerlessDevRuntime(options) {
68
+ await ensureServerlessRuntimeInstalled();
69
+ const requireFromCache = createRequire(path.join(CACHE_DIR, 'package.json'));
70
+ const { start } = requireFromCache('@hubspot/serverless-dev-runtime');
71
+ start(options);
72
+ }
@@ -94,6 +94,7 @@ export declare const LOCAL_DEV_UI_MESSAGE_SEND_TYPES: {
94
94
  UPDATE_PROJECT_DATA: string;
95
95
  UPDATE_UPLOAD_WARNINGS: string;
96
96
  DEV_SERVERS_STARTED: string;
97
+ UPLOAD_IN_PROGRESS: string;
97
98
  };
98
99
  export declare const LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES: {
99
100
  UPLOAD: string;
@@ -102,6 +103,7 @@ export declare const LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES: {
102
103
  APP_INSTALL_SUCCESS: string;
103
104
  APP_INSTALL_INITIATED: string;
104
105
  APP_INSTALL_FAILURE: string;
106
+ SET_AUTO_UPLOAD: string;
105
107
  };
106
108
  export declare const APP_INSTALLATION_STATES: {
107
109
  readonly NOT_INSTALLED: "NOT_INSTALLED";
package/lib/constants.js CHANGED
@@ -86,6 +86,7 @@ export const LOCAL_DEV_UI_MESSAGE_SEND_TYPES = {
86
86
  UPDATE_PROJECT_DATA: 'server:updateProjectData',
87
87
  UPDATE_UPLOAD_WARNINGS: 'server:updateUploadWarnings',
88
88
  DEV_SERVERS_STARTED: 'server:devServersStarted',
89
+ UPLOAD_IN_PROGRESS: 'server:uploadInProgress',
89
90
  };
90
91
  export const LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES = {
91
92
  UPLOAD: 'client:upload',
@@ -94,6 +95,7 @@ export const LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES = {
94
95
  APP_INSTALL_SUCCESS: 'client:installSuccess',
95
96
  APP_INSTALL_INITIATED: 'client:installInitiated',
96
97
  APP_INSTALL_FAILURE: 'client:installFailure',
98
+ SET_AUTO_UPLOAD: 'client:setAutoUpload',
97
99
  };
98
100
  export const APP_INSTALLATION_STATES = {
99
101
  NOT_INSTALLED: 'NOT_INSTALLED',
@@ -182,21 +182,14 @@ async function updatePackagesInDirectory({ directory, packages, npmWorkspaceDire
182
182
  }
183
183
  }
184
184
  export async function getProjectPackageJsonLocations(dir, isUpdate = false) {
185
- const projectConfig = await getProjectConfig(dir);
186
- if (!projectConfig ||
187
- !projectConfig.projectDir ||
188
- !projectConfig.projectConfig) {
189
- throw new Error(isUpdate
190
- ? commands.project.updateDeps.noProjectConfig
191
- : commands.project.installDeps.noProjectConfig);
192
- }
193
- const { projectDir, projectConfig: { srcDir, name }, } = projectConfig;
185
+ const { projectConfig, projectDir } = getProjectConfig(dir);
186
+ const { srcDir, name } = projectConfig;
194
187
  if (!(await isGloballyInstalled(DEFAULT_PACKAGE_MANAGER))) {
195
188
  throw new Error(isUpdate
196
189
  ? commands.project.updateDeps.packageManagerNotInstalled(DEFAULT_PACKAGE_MANAGER)
197
190
  : commands.project.installDeps.packageManagerNotInstalled(DEFAULT_PACKAGE_MANAGER));
198
191
  }
199
- if (!fs.existsSync(projectConfig.projectDir) ||
192
+ if (!fs.existsSync(projectDir) ||
200
193
  !fs.existsSync(path.join(projectDir, srcDir))) {
201
194
  throw new NoPackageJsonFilesError(name, isUpdate);
202
195
  }
@@ -1,9 +1,9 @@
1
- import { getProjectConfig } from '../projects/config.js';
2
1
  import { Environment } from '@hubspot/local-dev-lib/types/Accounts';
3
2
  import { AccessToken, AccountType, AuthType } from '@hubspot/local-dev-lib/types/Accounts';
4
3
  import { Project } from '@hubspot/local-dev-lib/types/Project';
5
4
  import process from 'process';
6
- export type ProjectConfig = Awaited<ReturnType<typeof getProjectConfig>>;
5
+ import type { LoadedProjectConfig } from '../projects/config.js';
6
+ export type ProjectConfig = LoadedProjectConfig | undefined;
7
7
  declare const hubspotCli = "@hubspot/cli";
8
8
  interface FilesInfo {
9
9
  files: string[];
@@ -50,12 +50,15 @@ export class DiagnosticInfoBuilder {
50
50
  this.processInfo = processInfo;
51
51
  }
52
52
  async generateDiagnosticInfo() {
53
- this._projectConfig = await getProjectConfig();
54
- if (this._projectConfig?.projectConfig) {
53
+ try {
54
+ this._projectConfig = getProjectConfig();
55
+ }
56
+ catch {
57
+ this._projectConfig = undefined;
58
+ }
59
+ if (this._projectConfig) {
55
60
  await this.fetchProjectDetails();
56
61
  await this.fetchAccessToken();
57
- }
58
- if (this._projectConfig?.projectDir) {
59
62
  await this.fetchProjectFilenames();
60
63
  }
61
64
  const { platform, arch, versions: { node }, mainModule, } = this.processInfo;
@@ -89,9 +92,7 @@ export class DiagnosticInfoBuilder {
89
92
  }
90
93
  async fetchProjectDetails() {
91
94
  try {
92
- const { data } = await fetchProject(this.accountId,
93
- // We check that config exists before running this function
94
- this._projectConfig.projectConfig.name);
95
+ const { data } = await fetchProject(this.accountId, this._projectConfig.projectConfig.name);
95
96
  this.projectDetails = data;
96
97
  }
97
98
  catch (e) {
@@ -109,7 +110,6 @@ export class DiagnosticInfoBuilder {
109
110
  }
110
111
  async fetchProjectFilenames() {
111
112
  try {
112
- // We check that projectDir exists before running this function
113
113
  this.files = (await walk(this._projectConfig.projectDir, ['node_modules'])).map(filename => path.relative(this._projectConfig.projectDir, filename));
114
114
  }
115
115
  catch (e) {
@@ -26,7 +26,7 @@ import { isServerRunningAtUrl } from '../http.js';
26
26
  import { detectConfiguredMcpClients } from '../mcp/promotion.js';
27
27
  import { MCP_CLIENTS } from '../mcp/clients.js';
28
28
  import { WEBHOOKS_KEY, APP_KEY } from '@hubspot/project-parsing-lib/constants';
29
- import { validateProjectConfig } from '../projects/config.js';
29
+ import { validateProjectConfig } from '@hubspot/project-parsing-lib/projects';
30
30
  import { validateSourceDirectory, handleTranslate, } from '../projects/upload.js';
31
31
  import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
32
32
  import { validateProjectForProfile } from '../projects/projectProfiles.js';
@@ -57,7 +57,7 @@ export class Doctor {
57
57
  ...this.performCliChecks(),
58
58
  ...this.performCliConfigChecks(),
59
59
  ...this.performNetworkingChecks(),
60
- ...(this.projectConfig?.projectConfig ? this.performProjectChecks() : []),
60
+ ...(this.projectConfig ? this.performProjectChecks() : []),
61
61
  ]);
62
62
  this.performDefaultAccountOverrideFileChecks();
63
63
  this.performCliConfigSettingsChecks();
@@ -283,7 +283,7 @@ export class Doctor {
283
283
  for (const packageFile of this.diagnosticInfo?.packageFiles || []) {
284
284
  const packageDirName = path.dirname(packageFile);
285
285
  try {
286
- const needsInstall = await hasMissingPackages(path.join(this.projectConfig?.projectDir || '', packageDirName));
286
+ const needsInstall = await hasMissingPackages(path.join(this.projectConfig?.projectDir ?? '', packageDirName));
287
287
  if (needsInstall) {
288
288
  foundError = true;
289
289
  this.diagnosis?.addProjectSection({
@@ -331,7 +331,7 @@ export class Doctor {
331
331
  async checkProjectConfigJsonFiles() {
332
332
  let foundError = false;
333
333
  for (const jsonFile of this.diagnosticInfo?.jsonFiles || []) {
334
- const fileToCheck = path.join(this.projectConfig?.projectDir || '', jsonFile);
334
+ const fileToCheck = path.join(this.projectConfig?.projectDir ?? '', jsonFile);
335
335
  if (!(await this.isValidJsonFile(fileToCheck))) {
336
336
  foundError = true;
337
337
  this.diagnosis?.addProjectSection({
@@ -432,7 +432,7 @@ export class Doctor {
432
432
  const hsMetaFiles = this.diagnosticInfo?.hsMetaFiles || [];
433
433
  for (const metaFiles of hsMetaFiles) {
434
434
  try {
435
- const content = await fs.promises.readFile(path.join(this.projectConfig?.projectDir || '', metaFiles), 'utf8');
435
+ const content = await fs.promises.readFile(path.join(this.projectConfig?.projectDir ?? '', metaFiles), 'utf8');
436
436
  const contents = JSON.parse(content);
437
437
  if (contents.type === WEBHOOKS_KEY &&
438
438
  contents.config?.settings?.targetUrl) {
@@ -461,7 +461,7 @@ export class Doctor {
461
461
  const hsMetaFiles = this.diagnosticInfo?.hsMetaFiles || [];
462
462
  for (const metaFile of hsMetaFiles) {
463
463
  try {
464
- const content = await fs.promises.readFile(path.join(this.projectConfig?.projectDir || '', metaFile), 'utf8');
464
+ const content = await fs.promises.readFile(path.join(this.projectConfig?.projectDir ?? '', metaFile), 'utf8');
465
465
  const contents = JSON.parse(content);
466
466
  if (contents.type === APP_KEY &&
467
467
  Array.isArray(contents.config?.auth?.redirectUrls)) {
@@ -550,9 +550,7 @@ export class Doctor {
550
550
  }
551
551
  }
552
552
  async checkProjectValidation() {
553
- if (!this.projectConfig?.projectConfig ||
554
- !this.projectConfig?.projectDir ||
555
- !this.accountId) {
553
+ if (!this.projectConfig || !this.accountId) {
556
554
  return;
557
555
  }
558
556
  const { projectConfig, projectDir } = this.projectConfig;
@@ -1,12 +1,6 @@
1
1
  export declare class ProjectNestingError extends Error {
2
2
  constructor(message: string);
3
3
  }
4
- export declare class ProjectConfigNotFoundError extends Error {
5
- constructor(message: string);
6
- }
7
- export declare class ProjectValidationError extends Error {
8
- constructor(message: string);
9
- }
10
4
  export declare class ProjectUploadError extends Error {
11
5
  constructor(message: string, cause?: unknown);
12
6
  }