@hubspot/cli 8.0.8-experimental.6 → 8.0.8-experimental.7

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 (32) hide show
  1. package/commands/cms/theme/preview.js +11 -64
  2. package/lang/en.d.ts +0 -5
  3. package/lang/en.js +1 -6
  4. package/lib/__tests__/commandSuggestion.test.js +0 -2
  5. package/lib/cms/devServerProcess.d.ts +13 -0
  6. package/lib/cms/devServerProcess.js +122 -0
  7. package/lib/commandSuggestion.js +7 -1
  8. package/lib/getStartedV2Actions.d.ts +0 -13
  9. package/lib/getStartedV2Actions.js +0 -53
  10. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -20
  11. package/mcp-server/tools/project/AddFeatureToProjectTool.js +10 -6
  12. package/mcp-server/tools/project/CreateProjectTool.d.ts +4 -24
  13. package/mcp-server/tools/project/CreateProjectTool.js +10 -5
  14. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +8 -5
  15. package/mcp-server/tools/project/GetBuildLogsTool.d.ts +2 -2
  16. package/mcp-server/tools/project/GetBuildLogsTool.js +7 -6
  17. package/mcp-server/tools/project/GetBuildStatusTool.d.ts +1 -1
  18. package/mcp-server/tools/project/GetBuildStatusTool.js +4 -3
  19. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +1 -6
  20. package/mcp-server/tools/project/GuidedWalkthroughTool.js +6 -1
  21. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +32 -0
  22. package/mcp-server/tools/project/constants.d.ts +1 -12
  23. package/mcp-server/tools/project/constants.js +16 -12
  24. package/package.json +2 -2
  25. package/ui/components/getStarted/GetStartedFlow.js +2 -79
  26. package/ui/components/getStarted/reducer.d.ts +0 -20
  27. package/ui/components/getStarted/reducer.js +0 -36
  28. package/ui/components/getStarted/screens/ProjectSetupScreen.js +1 -2
  29. package/ui/lib/constants.d.ts +0 -1
  30. package/ui/lib/constants.js +0 -1
  31. package/ui/components/getStarted/screens/InstallationScreen.d.ts +0 -7
  32. package/ui/components/getStarted/screens/InstallationScreen.js +0 -16
@@ -1,16 +1,14 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import cliProgress from 'cli-progress';
4
3
  import { commands } from '../../../lang/en.js';
5
4
  import { getCwd } from '@hubspot/local-dev-lib/path';
6
- import { FILE_UPLOAD_RESULT_TYPES } from '@hubspot/local-dev-lib/constants/files';
7
5
  import { getThemeJSONPath } from '@hubspot/local-dev-lib/cms/themes';
8
- import { createDevServer } from '@hubspot/cms-dev-server';
9
- import { getUploadableFileList } from '../../../lib/upload.js';
6
+ // Subprocess approach - don't import cms-dev-server directly to avoid React version conflicts
7
+ // import { createDevServer } from '@hubspot/cms-dev-server';
8
+ import { spawnDevServer } from '../../../lib/cms/devServerProcess.js';
10
9
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
11
10
  import { previewPrompt, previewProjectPrompt, } from '../../../lib/prompts/previewPrompt.js';
12
11
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
13
- import { ApiErrorContext, logError } from '../../../lib/errorHandlers/index.js';
14
12
  import { getProjectConfig } from '../../../lib/projects/config.js';
15
13
  import { findProjectComponents } from '../../../lib/projects/structure.js';
16
14
  import { ComponentTypes } from '../../../types/Projects.js';
@@ -73,67 +71,16 @@ async function determineSrcAndDest(args) {
73
71
  async function handler(args) {
74
72
  const { derivedAccountId, noSsl, resetSession, port, generateFieldsTypes } = args;
75
73
  const { absoluteSrc, dest } = await determineSrcAndDest(args);
76
- const filePaths = await getUploadableFileList(absoluteSrc, false);
77
- function startProgressBar(numFiles) {
78
- const initialUploadProgressBar = new cliProgress.SingleBar({
79
- gracefulExit: true,
80
- format: '[{bar}] {percentage}% | {value}/{total} | {label}',
81
- hideCursor: true,
82
- }, cliProgress.Presets.rect);
83
- initialUploadProgressBar.start(numFiles, 0, {
84
- label: commands.cms.subcommands.theme.subcommands.preview
85
- .initialUploadProgressBar.start,
86
- });
87
- let uploadsHaveStarted = false;
88
- const uploadOptions = {
89
- onAttemptCallback: () => {
90
- /* Intentionally blank */
91
- },
92
- onSuccessCallback: () => {
93
- initialUploadProgressBar.increment();
94
- if (!uploadsHaveStarted) {
95
- uploadsHaveStarted = true;
96
- initialUploadProgressBar.update(0, {
97
- label: commands.cms.subcommands.theme.subcommands.preview
98
- .initialUploadProgressBar.uploading,
99
- });
100
- }
101
- },
102
- onFirstErrorCallback: () => {
103
- /* Intentionally blank */
104
- },
105
- onRetryCallback: () => {
106
- /* Intentionally blank */
107
- },
108
- onFinalErrorCallback: () => initialUploadProgressBar.increment(),
109
- onFinishCallback: (results) => {
110
- initialUploadProgressBar.update(numFiles, {
111
- label: commands.cms.subcommands.theme.subcommands.preview
112
- .initialUploadProgressBar.finish,
113
- });
114
- initialUploadProgressBar.stop();
115
- results.forEach(result => {
116
- if (result.resultType == FILE_UPLOAD_RESULT_TYPES.FAILURE) {
117
- uiLogger.error(commands.cms.subcommands.theme.subcommands.preview.errors.uploadFailed(result.file, dest));
118
- logError(result.error, new ApiErrorContext({
119
- accountId: derivedAccountId,
120
- request: dest,
121
- payload: result.file,
122
- }));
123
- }
124
- });
125
- },
126
- };
127
- return uploadOptions;
128
- }
129
74
  trackCommandUsage('preview', {}, derivedAccountId);
130
- if (port) {
131
- process.env['PORT'] = port.toString();
132
- }
133
- createDevServer(absoluteSrc, false, '', '', !noSsl, generateFieldsTypes, {
134
- filePaths,
75
+ // Spawn dev server as subprocess to isolate React versions
76
+ // Calls createDevServer as library function to support standalone themes
77
+ spawnDevServer({
78
+ absoluteSrc,
79
+ accountName: derivedAccountId?.toString(),
80
+ noSsl,
81
+ port,
82
+ generateFieldsTypes,
135
83
  resetSession: resetSession || false,
136
- startProgressBar,
137
84
  dest,
138
85
  });
139
86
  }
package/lang/en.d.ts CHANGED
@@ -43,11 +43,6 @@ export declare const commands: {
43
43
  checkOutConfig: (configPath: string) => string;
44
44
  pressEnterToInstall: (accountName: string) => string;
45
45
  pressKeyToExit: string;
46
- installingApp: (appName: string, accountName: string) => string;
47
- installInstructions: string;
48
- browserFailedToOpen: (url: string) => string;
49
- pollingTimeout: (minutes: number) => string;
50
- pressEnterToContinueSetup: string;
51
46
  prompts: {
52
47
  selectOptionV2: string;
53
48
  options: {
package/lang/en.js CHANGED
@@ -51,11 +51,6 @@ export const commands = {
51
51
  checkOutConfig: (configPath) => `Check out ${chalk.cyan(configPath)} for the full configuration.`,
52
52
  pressEnterToInstall: (accountName) => `? Press ${chalk.bold('<enter>')} to continue installing and previewing this app in ${chalk.bold(accountName)}`,
53
53
  pressKeyToExit: `Press any key to exit...`,
54
- installingApp: (appName, accountName) => `Installing ${chalk.bold(appName)} in ${chalk.bold(accountName)}...`,
55
- installInstructions: `We'll take you to your HubSpot account and walk you through installing your app.`,
56
- browserFailedToOpen: (url) => `⚠️ Failed to open browser automatically. Please open this URL manually:\n${chalk.cyan(url)}`,
57
- pollingTimeout: (minutes) => `⚠️ Installation polling timed out after ${minutes} minutes. The app may still be installing in the background.`,
58
- pressEnterToContinueSetup: `Press ${chalk.bold('<enter>')} to continue with card setup...`,
59
54
  prompts: {
60
55
  selectOptionV2: 'Choose a component type to get started',
61
56
  options: {
@@ -3081,7 +3076,7 @@ export const lib = {
3081
3076
  updateSucceeded: (latestVersion) => `Successfully updated HubSpot CLI to version ${chalk.bold(latestVersion)}`,
3082
3077
  notInstalledGlobally: 'Cannot auto-update the HubSpot CLI because NPM is not installed globally',
3083
3078
  updateFailed: (latestVersion) => `Failed to update HubSpot CLI to version ${chalk.bold(latestVersion)}`,
3084
- enableAutoUpdatesMessage: `The HubSpot CLI can automatically keep itself up to date.\n\nThis helps ensure compatibility with the HubSpot platform. You can change this later at any time.\n\nRun ${uiCommandReference('hs config set --allow-auto-updates=true')}`,
3079
+ enableAutoUpdatesMessage: `The HubSpot CLI can automatically keep itself up to date.\n\nThis helps ensure compatibility with the HubSpot platform. You can change this later at any time.\n\nRun${uiCommandReference('hs config set --allow-auto-updates=true')}`,
3085
3080
  },
3086
3081
  },
3087
3082
  projectProfiles: {
@@ -71,8 +71,6 @@ describe('lib/commandSuggestion', () => {
71
71
  // Create a mock yargs builder with strict method
72
72
  const mockYargsBuilder = {
73
73
  strict: vi.fn().mockReturnThis(),
74
- help: vi.fn().mockReturnThis(),
75
- version: vi.fn().mockReturnThis(),
76
74
  };
77
75
  await commandModule.builder(mockYargsBuilder);
78
76
  expect(mockYargsBuilder.strict).toHaveBeenCalledWith(false);
@@ -0,0 +1,13 @@
1
+ import { ChildProcess } from 'child_process';
2
+ interface DevServerOptions {
3
+ absoluteSrc: string;
4
+ accountName?: string;
5
+ configPath?: string;
6
+ noSsl?: boolean;
7
+ port?: number;
8
+ generateFieldsTypes?: boolean;
9
+ resetSession?: boolean;
10
+ dest?: string;
11
+ }
12
+ export declare function spawnDevServer(options: DevServerOptions): ChildProcess;
13
+ export {};
@@ -0,0 +1,122 @@
1
+ import { spawn } from 'child_process';
2
+ export function spawnDevServer(options) {
3
+ const { absoluteSrc, accountName, noSsl, port, generateFieldsTypes, resetSession, dest, } = options;
4
+ // Build a Node script that imports and calls createDevServer
5
+ // This bypasses the CLI's project directory requirement while still isolating React versions
6
+ const script = `
7
+ const { createDevServer } = await import('@hubspot/cms-dev-server');
8
+ const { walk } = await import('@hubspot/local-dev-lib/fs');
9
+ const { createIgnoreFilter } = await import('@hubspot/local-dev-lib/ignoreRules');
10
+ const { isAllowedExtension } = await import('@hubspot/local-dev-lib/path');
11
+ const { FILE_UPLOAD_RESULT_TYPES } = await import('@hubspot/local-dev-lib/constants/files');
12
+ const cliProgress = (await import('cli-progress')).default;
13
+
14
+ ${dest
15
+ ? `
16
+ // Get uploadable files for preview (inlined from lib/upload.ts)
17
+ let filePaths = [];
18
+ try {
19
+ filePaths = await walk(${JSON.stringify(absoluteSrc)});
20
+ } catch (e) {
21
+ console.error('Error walking directory:', e);
22
+ }
23
+ filePaths = filePaths
24
+ .filter(file => isAllowedExtension(file))
25
+ .filter(createIgnoreFilter(false));
26
+
27
+ // Create progress bar for initial upload
28
+ function startProgressBar(numFiles) {
29
+ const initialUploadProgressBar = new cliProgress.SingleBar(
30
+ {
31
+ gracefulExit: true,
32
+ format: '[{bar}] {percentage}% | {value}/{total} | {label}',
33
+ hideCursor: true,
34
+ },
35
+ cliProgress.Presets.rect
36
+ );
37
+ initialUploadProgressBar.start(numFiles, 0, {
38
+ label: 'Preparing upload...',
39
+ });
40
+ let uploadsHaveStarted = false;
41
+ return {
42
+ onAttemptCallback: () => {},
43
+ onSuccessCallback: () => {
44
+ initialUploadProgressBar.increment();
45
+ if (!uploadsHaveStarted) {
46
+ uploadsHaveStarted = true;
47
+ initialUploadProgressBar.update(0, {
48
+ label: 'Uploading files...',
49
+ });
50
+ }
51
+ },
52
+ onFirstErrorCallback: () => {},
53
+ onRetryCallback: () => {},
54
+ onFinalErrorCallback: () => initialUploadProgressBar.increment(),
55
+ onFinishCallback: (results) => {
56
+ initialUploadProgressBar.update(numFiles, {
57
+ label: 'Upload complete',
58
+ });
59
+ initialUploadProgressBar.stop();
60
+ results.forEach(result => {
61
+ if (result.resultType == FILE_UPLOAD_RESULT_TYPES.FAILURE) {
62
+ console.error(\`Failed to upload \${result.file}\`);
63
+ }
64
+ });
65
+ },
66
+ };
67
+ }
68
+
69
+ const themePreviewOptions = {
70
+ filePaths,
71
+ startProgressBar,
72
+ resetSession: ${Boolean(resetSession)},
73
+ dest: ${JSON.stringify(dest)},
74
+ };
75
+ `
76
+ : 'const themePreviewOptions = undefined;'}
77
+
78
+ createDevServer(
79
+ ${JSON.stringify(absoluteSrc)},
80
+ false, // storybook
81
+ '', // config (uses env var HUBSPOT_CONFIG_PATH)
82
+ ${JSON.stringify(accountName || '')},
83
+ ${!noSsl}, // sslEnabled
84
+ ${Boolean(generateFieldsTypes)}, // fieldGenEnabled
85
+ themePreviewOptions
86
+ );
87
+ `;
88
+ // Set environment variables
89
+ const env = { ...process.env };
90
+ if (port) {
91
+ env.PORT = port.toString();
92
+ }
93
+ // Spawn Node with the inline script
94
+ const devServer = spawn('node', ['--input-type=module', '-e', script], {
95
+ stdio: 'inherit',
96
+ env,
97
+ cwd: absoluteSrc,
98
+ });
99
+ // Handle process events
100
+ devServer.on('error', error => {
101
+ console.error('Failed to start dev server:', error);
102
+ process.exit(1);
103
+ });
104
+ devServer.on('exit', (code, signal) => {
105
+ if (code !== 0 && code !== null) {
106
+ console.error(`Dev server exited with code ${code}`);
107
+ process.exit(code);
108
+ }
109
+ if (signal) {
110
+ console.error(`Dev server killed with signal ${signal}`);
111
+ process.exit(1);
112
+ }
113
+ });
114
+ // Handle CLI termination
115
+ process.on('SIGINT', () => {
116
+ devServer.kill('SIGINT');
117
+ });
118
+ process.on('SIGTERM', () => {
119
+ devServer.kill('SIGTERM');
120
+ });
121
+ return devServer;
122
+ }
@@ -18,6 +18,12 @@ export const commandSuggestionMappings = {
18
18
  'theme generate-selectors': 'hs cms theme generate-selectors',
19
19
  'theme marketplace-validate': 'hs cms theme marketplace-validate',
20
20
  'theme preview': 'hs cms theme preview',
21
+ 'custom-object schema create': 'hs custom-object create-schema',
22
+ 'custom-object schema delete': 'hs custom-object delete-schema',
23
+ 'custom-object schema fetch-all': 'hs custom-object fetch-all-schemas',
24
+ 'custom-object schema fetch': 'hs custom-object fetch-schema',
25
+ 'custom-object schema list': 'hs custom-object list-schemas',
26
+ 'custom-object schema update': 'hs custom-object update-schema',
21
27
  };
22
28
  function createCommandSuggestionHandler(newCommand) {
23
29
  return () => {
@@ -29,7 +35,7 @@ function createCommandSuggestion(oldCommand, newCommand) {
29
35
  return {
30
36
  command: oldCommand,
31
37
  builder: async (yargs) => {
32
- return yargs.strict(false).help(false).version(false);
38
+ return yargs.strict(false);
33
39
  },
34
40
  handler: createCommandSuggestionHandler(newCommand),
35
41
  };
@@ -35,16 +35,3 @@ export declare function uploadAndDeployAction({ accountId, projectDest, }: {
35
35
  projectDest: string;
36
36
  }): Promise<UploadAndDeployResult>;
37
37
  export declare function trackGetStartedUsage(params: Record<string, unknown>, accountId: number): Promise<void>;
38
- export type PollAppInstallationOptions = {
39
- accountId: number;
40
- projectId: number;
41
- appUid: string;
42
- requiredScopes?: string[];
43
- optionalScopes?: string[];
44
- timeoutMs?: number;
45
- intervalMs?: number;
46
- onTimeout?: () => void;
47
- };
48
- export declare function pollAppInstallation({ accountId, projectId, appUid, requiredScopes, optionalScopes, timeoutMs, // 2 minutes
49
- intervalMs, // 2 seconds
50
- onTimeout, }: PollAppInstallationOptions): Promise<void>;
@@ -1,7 +1,6 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import { fetchPublicAppsForPortal } from '@hubspot/local-dev-lib/api/appsDev';
4
- import { fetchAppInstallationData } from '@hubspot/local-dev-lib/api/localDevAuth';
5
4
  import { fetchProject } from '@hubspot/local-dev-lib/api/projects';
6
5
  import { getConfigAccountEnvironment } from '@hubspot/local-dev-lib/config';
7
6
  import { cloneGithubRepo } from '@hubspot/local-dev-lib/github';
@@ -145,55 +144,3 @@ export async function uploadAndDeployAction({ accountId, projectDest, }) {
145
144
  export function trackGetStartedUsage(params, accountId) {
146
145
  return trackCommandMetadataUsage('get-started', params, accountId);
147
146
  }
148
- export async function pollAppInstallation({ accountId, projectId, appUid, requiredScopes = [], optionalScopes = [], timeoutMs = 2 * 60 * 1000, // 2 minutes
149
- intervalMs = 2000, // 2 seconds
150
- onTimeout, }) {
151
- return new Promise((resolve, reject) => {
152
- let consecutiveErrors = 0;
153
- const MAX_CONSECUTIVE_ERRORS = 5;
154
- let pollInterval = null;
155
- let pollTimeout = null;
156
- const cleanup = () => {
157
- if (pollInterval) {
158
- clearTimeout(pollInterval);
159
- pollInterval = null;
160
- }
161
- if (pollTimeout) {
162
- clearTimeout(pollTimeout);
163
- pollTimeout = null;
164
- }
165
- };
166
- pollTimeout = setTimeout(() => {
167
- cleanup();
168
- if (onTimeout) {
169
- onTimeout();
170
- }
171
- resolve(); // Resolve instead of reject to allow continuing with timeout state
172
- }, timeoutMs);
173
- const poll = async () => {
174
- try {
175
- const { data } = await fetchAppInstallationData(accountId, projectId, appUid, requiredScopes, optionalScopes);
176
- // Reset error counter on successful fetch
177
- consecutiveErrors = 0;
178
- if (data.isInstalledWithScopeGroups) {
179
- cleanup();
180
- resolve();
181
- }
182
- else if (pollInterval) {
183
- pollInterval = setTimeout(poll, intervalMs);
184
- }
185
- }
186
- catch (error) {
187
- consecutiveErrors++;
188
- if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
189
- cleanup();
190
- reject(new Error(`Failed to check app installation status after ${MAX_CONSECUTIVE_ERRORS} consecutive errors`, { cause: error }));
191
- }
192
- else if (pollInterval !== null) {
193
- pollInterval = setTimeout(poll, intervalMs);
194
- }
195
- }
196
- };
197
- pollInterval = setTimeout(poll, 0);
198
- });
199
- }
@@ -5,26 +5,9 @@ declare const inputSchemaZodObject: z.ZodObject<{
5
5
  absoluteProjectPath: z.ZodString;
6
6
  absoluteCurrentWorkingDirectory: z.ZodString;
7
7
  addApp: z.ZodBoolean;
8
- distribution: z.ZodOptional<z.ZodEnum<{
9
- marketplace: "marketplace";
10
- private: "private";
11
- }>>;
12
- auth: z.ZodOptional<z.ZodEnum<{
13
- oauth: "oauth";
14
- static: "static";
15
- }>>;
16
- features: z.ZodOptional<z.ZodArray<z.ZodEnum<{
17
- card: "card";
18
- settings: "settings";
19
- "app-event": "app-event";
20
- "workflow-action-tool": "workflow-action-tool";
21
- page: "page";
22
- webhooks: "webhooks";
23
- "workflow-action": "workflow-action";
24
- "app-function": "app-function";
25
- "app-object": "app-object";
26
- scim: "scim";
27
- }>>>;
8
+ distribution: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"marketplace">, z.ZodLiteral<"private">]>>;
9
+ auth: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"static">, z.ZodLiteral<"oauth">]>>;
10
+ features: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"app-event">, z.ZodLiteral<"scim">, z.ZodLiteral<"page">]>>>;
28
11
  }, z.core.$strip>;
29
12
  export type AddFeatureInputSchema = z.infer<typeof inputSchemaZodObject>;
30
13
  export declare class AddFeatureToProjectTool extends Tool<AddFeatureInputSchema> {
@@ -15,13 +15,17 @@ const inputSchema = {
15
15
  .boolean()
16
16
  .describe('Should an app be added? If there is no app in the project, an app must be added to add a feature'),
17
17
  distribution: z
18
- .enum([APP_DISTRIBUTION_TYPES.MARKETPLACE, APP_DISTRIBUTION_TYPES.PRIVATE])
19
- .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your app on the HubSpot marketplace. ')
20
- .optional(),
18
+ .optional(z.union([
19
+ z.literal(APP_DISTRIBUTION_TYPES.MARKETPLACE),
20
+ z.literal(APP_DISTRIBUTION_TYPES.PRIVATE),
21
+ ]))
22
+ .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your app on the HubSpot marketplace. '),
21
23
  auth: z
22
- .enum([APP_AUTH_TYPES.STATIC, APP_AUTH_TYPES.OAUTH])
23
- .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Static uses a static non changing authentication token, and is only available for private distribution. ')
24
- .optional(),
24
+ .optional(z.union([
25
+ z.literal(APP_AUTH_TYPES.STATIC),
26
+ z.literal(APP_AUTH_TYPES.OAUTH),
27
+ ]))
28
+ .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Static uses a static non changing authentication token, and is only available for private distribution. '),
25
29
  features,
26
30
  };
27
31
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -5,30 +5,10 @@ declare const inputSchemaZodObject: z.ZodObject<{
5
5
  absoluteCurrentWorkingDirectory: z.ZodString;
6
6
  name: z.ZodOptional<z.ZodString>;
7
7
  destination: z.ZodString;
8
- projectBase: z.ZodEnum<{
9
- app: "app";
10
- empty: "empty";
11
- }>;
12
- distribution: z.ZodOptional<z.ZodEnum<{
13
- marketplace: "marketplace";
14
- private: "private";
15
- }>>;
16
- auth: z.ZodOptional<z.ZodEnum<{
17
- oauth: "oauth";
18
- static: "static";
19
- }>>;
20
- features: z.ZodOptional<z.ZodArray<z.ZodEnum<{
21
- card: "card";
22
- settings: "settings";
23
- "app-event": "app-event";
24
- "workflow-action-tool": "workflow-action-tool";
25
- page: "page";
26
- webhooks: "webhooks";
27
- "workflow-action": "workflow-action";
28
- "app-function": "app-function";
29
- "app-object": "app-object";
30
- scim: "scim";
31
- }>>>;
8
+ projectBase: z.ZodUnion<readonly [z.ZodLiteral<"empty">, z.ZodLiteral<"app">]>;
9
+ distribution: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"marketplace">, z.ZodLiteral<"private">]>>;
10
+ auth: z.ZodOptional<z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"static">, z.ZodLiteral<"oauth">]>>>;
11
+ features: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"app-event">, z.ZodLiteral<"scim">, z.ZodLiteral<"page">]>>>;
32
12
  }, z.core.$strip>;
33
13
  export type CreateProjectInputSchema = z.infer<typeof inputSchemaZodObject>;
34
14
  export declare class CreateProjectTool extends Tool<CreateProjectInputSchema> {
@@ -18,14 +18,19 @@ const inputSchema = {
18
18
  .string()
19
19
  .describe('DO NOT use the current directory unless the user has explicitly stated to do so. Relative path to the directory the project will be created in.'),
20
20
  projectBase: z
21
- .enum([EMPTY_PROJECT, PROJECT_WITH_APP])
21
+ .union([z.literal(EMPTY_PROJECT), z.literal(PROJECT_WITH_APP)])
22
22
  .describe('Empty will create an empty project, and app will create a project with an app inside of it.'),
23
23
  distribution: z
24
- .enum([APP_DISTRIBUTION_TYPES.MARKETPLACE, APP_DISTRIBUTION_TYPES.PRIVATE])
25
- .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your app on the HubSpot marketplace. ')
26
- .optional(),
24
+ .optional(z.union([
25
+ z.literal(APP_DISTRIBUTION_TYPES.MARKETPLACE),
26
+ z.literal(APP_DISTRIBUTION_TYPES.PRIVATE),
27
+ ]))
28
+ .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your app on the HubSpot marketplace. '),
27
29
  auth: z
28
- .enum([APP_AUTH_TYPES.STATIC, APP_AUTH_TYPES.OAUTH])
30
+ .optional(z.union([
31
+ z.literal(APP_AUTH_TYPES.STATIC),
32
+ z.literal(APP_AUTH_TYPES.OAUTH),
33
+ ]))
29
34
  .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Static uses a static non changing authentication token, and is only available for private distribution. ')
30
35
  .optional(),
31
36
  features,
@@ -12,15 +12,18 @@ const inputSchema = {
12
12
  absoluteCurrentWorkingDirectory,
13
13
  appId: z
14
14
  .string()
15
- .describe('The numeric app ID as a string (e.g., "3003909"). Must contain only digits. Use get-apps-info to find available app IDs.'),
15
+ .regex(/^\d+$/, 'App ID must be a numeric string')
16
+ .describe('The numeric app ID as a string (e.g., "3003909"). Use get-apps-info to find available app IDs.'),
16
17
  startDate: z
17
18
  .string()
18
- .describe('Start date for the usage patterns query in YYYY-MM-DD format (e.g., 2025-01-01).')
19
- .optional(),
19
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'Start date must be in YYYY-MM-DD format')
20
+ .optional()
21
+ .describe('Start date for the usage patterns query in ISO 8601 format (e.g., 2025-01-01).'),
20
22
  endDate: z
21
23
  .string()
22
- .describe('End date for the usage patterns query in YYYY-MM-DD format (e.g., 2025-12-31).')
23
- .optional(),
24
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'End date must be in YYYY-MM-DD format')
25
+ .optional()
26
+ .describe('End date for the usage patterns query in ISO 8601 format (e.g., 2025-12-31).'),
24
27
  };
25
28
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
26
29
  const inputSchemaZodObject = z.object({ ...inputSchema });
@@ -5,12 +5,12 @@ declare const inputSchemaZodObject: z.ZodObject<{
5
5
  absoluteProjectPath: z.ZodString;
6
6
  absoluteCurrentWorkingDirectory: z.ZodString;
7
7
  buildId: z.ZodNumber;
8
- logLevel: z.ZodOptional<z.ZodEnum<{
8
+ logLevel: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
9
9
  ERROR: "ERROR";
10
10
  WARN: "WARN";
11
11
  INFO: "INFO";
12
12
  ALL: "ALL";
13
- }>>;
13
+ }>>>;
14
14
  }, z.core.$strip>;
15
15
  export type GetBuildLogsInputSchema = z.infer<typeof inputSchemaZodObject>;
16
16
  export declare class GetBuildLogsTool extends Tool<GetBuildLogsInputSchema> {
@@ -18,8 +18,9 @@ const inputSchema = {
18
18
  .describe('Build ID to fetch logs for. Use get-build-status to find recent build IDs.'),
19
19
  logLevel: z
20
20
  .enum(['ERROR', 'WARN', 'INFO', 'ALL'])
21
- .describe('Filter logs by level. ERROR: Show only errors, WARN: Show only warnings, INFO: Show only info, ALL: Show all logs. Defaults to ALL if not specified.')
22
- .optional(),
21
+ .optional()
22
+ .default('ALL')
23
+ .describe('Filter logs by level. ERROR: Show only errors, WARN: Show only warnings, INFO: Show only info, ALL: Show all logs.'),
23
24
  };
24
25
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
26
  const inputSchemaZodObject = z.object({ ...inputSchema });
@@ -84,14 +85,14 @@ export class GetBuildLogsTool extends Tool {
84
85
  if (allLogs.length === 0) {
85
86
  return formatTextContents(absoluteCurrentWorkingDirectory, `No logs found for build #${buildId} in project '${projectName}'.`);
86
87
  }
87
- const resolvedLogLevel = logLevel || 'ALL';
88
- const filteredLogs = filterLogsByLevel(allLogs, resolvedLogLevel);
88
+ const filteredLogs = filterLogsByLevel(allLogs, logLevel);
89
89
  let output;
90
90
  if (filteredLogs.length === 0) {
91
- output = `No ${resolvedLogLevel} level logs found for build #${buildId} in '${projectName}'.\nShowing all logs instead:\n\n${formatLogs(allLogs)}`;
91
+ // No logs match filter, show all logs instead
92
+ output = `No ${logLevel} level logs found for build #${buildId} in '${projectName}'.\nShowing all logs instead:\n\n${formatLogs(allLogs)}`;
92
93
  }
93
94
  else {
94
- output = `Logs for build #${buildId} in '${projectName}' (${resolvedLogLevel} level):\n\n${formatLogs(filteredLogs)}`;
95
+ output = `Logs for build #${buildId} in '${projectName}' (${logLevel} level):\n\n${formatLogs(filteredLogs)}`;
95
96
  }
96
97
  return formatTextContents(absoluteCurrentWorkingDirectory, output);
97
98
  }
@@ -5,7 +5,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
5
5
  absoluteProjectPath: z.ZodString;
6
6
  absoluteCurrentWorkingDirectory: z.ZodString;
7
7
  buildId: z.ZodOptional<z.ZodNumber>;
8
- limit: z.ZodOptional<z.ZodNumber>;
8
+ limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
9
9
  }, z.core.$strip>;
10
10
  export type GetBuildStatusInputSchema = z.infer<typeof inputSchemaZodObject>;
11
11
  export declare class GetBuildStatusTool extends Tool<GetBuildStatusInputSchema> {
@@ -19,8 +19,9 @@ const inputSchema = {
19
19
  .describe('Optional: Specific build ID to inspect. If omitted, shows recent builds to help identify the latest build.'),
20
20
  limit: z
21
21
  .number()
22
- .describe('Number of recent builds to fetch when buildId is not specified. Defaults to 3 if not specified.')
23
- .optional(),
22
+ .optional()
23
+ .default(3)
24
+ .describe('Number of recent builds to fetch when buildId is not specified.'),
24
25
  };
25
26
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
26
27
  const inputSchemaZodObject = z.object({ ...inputSchema });
@@ -126,7 +127,7 @@ export class GetBuildStatusTool extends Tool {
126
127
  }
127
128
  else {
128
129
  const response = await fetchProjectBuilds(accountId, projectName, {
129
- limit: limit || 3,
130
+ limit,
130
131
  });
131
132
  const { results } = response.data;
132
133
  if (!results || results.length === 0) {
@@ -3,12 +3,7 @@ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.
3
3
  import { z } from 'zod';
4
4
  declare const inputSchemaZodObject: z.ZodObject<{
5
5
  absoluteCurrentWorkingDirectory: z.ZodString;
6
- command: z.ZodOptional<z.ZodEnum<{
7
- "hs auth": "hs auth";
8
- "hs project create": "hs project create";
9
- "hs project upload": "hs project upload";
10
- "hs init": "hs init";
11
- }>>;
6
+ command: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"hs init">, z.ZodLiteral<"hs auth">, z.ZodLiteral<"hs project create">, z.ZodLiteral<"hs project upload">]>>;
12
7
  }, z.core.$strip>;
13
8
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
14
9
  export declare class GuidedWalkthroughTool extends Tool<InputSchemaType> {
@@ -14,7 +14,12 @@ const nextCommands = {
14
14
  const inputSchema = {
15
15
  absoluteCurrentWorkingDirectory,
16
16
  command: z
17
- .enum(['hs init', 'hs auth', 'hs project create', 'hs project upload'])
17
+ .union([
18
+ z.literal('hs init'),
19
+ z.literal('hs auth'),
20
+ z.literal('hs project create'),
21
+ z.literal('hs project upload'),
22
+ ])
18
23
  .describe('The command to learn more about. Start with `hs init`')
19
24
  .optional(),
20
25
  };
@@ -1,4 +1,5 @@
1
1
  import { GetApiUsagePatternsByAppIdTool } from '../GetApiUsagePatternsByAppIdTool.js';
2
+ import { z } from 'zod';
2
3
  import { getConfigDefaultAccountIfExists } from '@hubspot/local-dev-lib/config';
3
4
  import { http } from '@hubspot/local-dev-lib/http';
4
5
  import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
@@ -51,6 +52,37 @@ describe('mcp-server/tools/project/GetApiUsagePatternsByAppIdTool', () => {
51
52
  expect(result).toBe(mockRegisteredTool);
52
53
  });
53
54
  });
55
+ describe('input validation', () => {
56
+ const inputSchema = z.object({
57
+ appId: z
58
+ .string()
59
+ .describe('The application ID to get API usage patterns for.'),
60
+ startDate: z
61
+ .string()
62
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'Start date must be in YYYY-MM-DD format')
63
+ .optional()
64
+ .describe('Start date for the usage patterns query in ISO 8601 format (e.g., 2025-01-01).'),
65
+ endDate: z
66
+ .string()
67
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'End date must be in YYYY-MM-DD format')
68
+ .optional()
69
+ .describe('End date for the usage patterns query in ISO 8601 format (e.g., 2025-12-31).'),
70
+ });
71
+ it('should validate date format correctly', () => {
72
+ const validInput = {
73
+ appId: '12345',
74
+ startDate: '2025-01-01',
75
+ endDate: '2025-12-31',
76
+ };
77
+ const invalidInput = {
78
+ appId: '12345',
79
+ startDate: '2025-1-1',
80
+ endDate: '2025-12-31T00:00:00Z',
81
+ };
82
+ expect(() => inputSchema.parse(validInput)).not.toThrow();
83
+ expect(() => inputSchema.parse(invalidInput)).toThrow();
84
+ });
85
+ });
54
86
  describe('handler', () => {
55
87
  const input = {
56
88
  absoluteCurrentWorkingDirectory: '/test/dir',
@@ -1,17 +1,6 @@
1
1
  import z from 'zod';
2
2
  export declare const absoluteProjectPath: z.ZodString;
3
3
  export declare const absoluteCurrentWorkingDirectory: z.ZodString;
4
- export declare const features: z.ZodOptional<z.ZodArray<z.ZodEnum<{
5
- card: "card";
6
- settings: "settings";
7
- "app-event": "app-event";
8
- "workflow-action-tool": "workflow-action-tool";
9
- page: "page";
10
- webhooks: "webhooks";
11
- "workflow-action": "workflow-action";
12
- "app-function": "app-function";
13
- "app-object": "app-object";
14
- scim: "scim";
15
- }>>>;
4
+ export declare const features: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"app-event">, z.ZodLiteral<"scim">, z.ZodLiteral<"page">]>>>;
16
5
  export declare const docsSearchQuery: z.ZodString;
17
6
  export declare const docUrl: z.ZodString;
@@ -6,19 +6,23 @@ export const absoluteCurrentWorkingDirectory = z
6
6
  .string()
7
7
  .describe('The absolute path to the current working directory.');
8
8
  export const features = z
9
- .array(z.enum([
10
- 'card',
11
- 'settings',
12
- 'app-function',
13
- 'webhooks',
14
- 'workflow-action',
15
- 'workflow-action-tool',
16
- 'app-object',
17
- 'app-event',
18
- 'scim',
19
- 'page',
9
+ .array(z.union([
10
+ z.literal('card'),
11
+ z.literal('settings'),
12
+ z
13
+ .literal('app-function')
14
+ .describe('Also known as a public serverless function'),
15
+ z.literal('webhooks'),
16
+ z
17
+ .literal('workflow-action')
18
+ .describe('Also known as a custom workflow action.'),
19
+ z.literal('workflow-action-tool').describe('Also known as agent tools.'),
20
+ z.literal('app-object'),
21
+ z.literal('app-event'),
22
+ z.literal('scim'),
23
+ z.literal('page'),
20
24
  ]))
21
- .describe('The features to include in the project, multiple options can be selected. "app-function" is also known as a public serverless function. "workflow-action" is also known as a custom workflow action. "workflow-action-tool" is also known as agent tools.')
25
+ .describe('The features to include in the project, multiple options can be selected')
22
26
  .optional();
23
27
  export const docsSearchQuery = z
24
28
  .string()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "8.0.8-experimental.6",
3
+ "version": "8.0.8-experimental.7",
4
4
  "description": "The official CLI for developing on HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "https://github.com/HubSpot/hubspot-cli",
@@ -8,7 +8,7 @@
8
8
  "dependencies": {
9
9
  "@hubspot/cms-dev-server": "1.2.16",
10
10
  "@hubspot/local-dev-lib": "5.1.1",
11
- "@hubspot/project-parsing-lib": "0.2.0-experimental.1",
11
+ "@hubspot/project-parsing-lib": "0.12.0",
12
12
  "@hubspot/serverless-dev-runtime": "7.0.7",
13
13
  "@hubspot/ui-extensions-dev-server": "1.1.8",
14
14
  "@inquirer/prompts": "7.1.0",
@@ -1,15 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { sanitizeFileName, untildify } from '@hubspot/local-dev-lib/path';
3
3
  import { useApp, useFocus, useInput } from 'ink';
4
- import open from 'open';
5
4
  import { useCallback, useEffect, useReducer } from 'react';
6
5
  import { commands } from '../../../lang/en.js';
7
- import { createProjectAction, pollAppInstallation, trackGetStartedUsage, uploadAndDeployAction, } from '../../../lib/getStartedV2Actions.js';
8
- import { validateProjectDirectory } from '../../../lib/prompts/projectNameAndDestPrompt.js';
6
+ import { createProjectAction, trackGetStartedUsage, uploadAndDeployAction, } from '../../../lib/getStartedV2Actions.js';
9
7
  import { uiAccountDescription } from '../../../lib/ui/index.js';
10
8
  import { ACTION_STATUSES, GET_STARTED_FLOW_STEPS, } from '../../lib/constants.js';
11
9
  import { flowReducer } from './reducer.js';
12
- import { InstallationScreen } from './screens/InstallationScreen.js';
13
10
  import { ProjectSetupScreen } from './screens/ProjectSetupScreen.js';
14
11
  import { UploadScreen } from './screens/UploadScreen.js';
15
12
  import { getProject } from './selectors.js';
@@ -47,7 +44,6 @@ export function GetStartedFlow({ derivedAccountId, initialName, initialDest, })
47
44
  statuses: {
48
45
  create: initialName ? ACTION_STATUSES.RUNNING : ACTION_STATUSES.IDLE,
49
46
  upload: ACTION_STATUSES.IDLE,
50
- installApp: ACTION_STATUSES.IDLE,
51
47
  },
52
48
  });
53
49
  const [state, dispatch] = useReducer(flowReducer, getInitialState());
@@ -72,16 +68,6 @@ export function GetStartedFlow({ derivedAccountId, initialName, initialDest, })
72
68
  dispatch({ type: 'SET_STEP', payload: GET_STARTED_FLOW_STEPS.DEST_INPUT });
73
69
  }, []);
74
70
  const handleDestSubmit = useCallback(async () => {
75
- const validationResult = validateProjectDirectory(project.destination);
76
- if (validationResult !== true) {
77
- dispatch({
78
- type: 'SET_DEST_ERROR',
79
- payload: typeof validationResult === 'string'
80
- ? validationResult
81
- : commands.getStarted.v2.unknownError,
82
- });
83
- return;
84
- }
85
71
  dispatch({ type: 'SET_STEP', payload: GET_STARTED_FLOW_STEPS.CREATING });
86
72
  try {
87
73
  await createProjectAction({
@@ -122,57 +108,6 @@ export function GetStartedFlow({ derivedAccountId, initialName, initialDest, })
122
108
  dispatch({ type: 'UPLOAD_ERROR', payload: errorMessage });
123
109
  }
124
110
  }, [derivedAccountId, project.destination]);
125
- const handlePollInstallation = useCallback(async () => {
126
- const uploadApp = project.uploadResult?.app;
127
- const projectId = project.uploadResult?.projectId;
128
- if (!projectId || !uploadApp?.uid) {
129
- dispatch({
130
- type: 'INSTALL_APP_ERROR',
131
- payload: commands.getStarted.v2.unknownError,
132
- });
133
- return;
134
- }
135
- try {
136
- await pollAppInstallation({
137
- accountId: derivedAccountId,
138
- projectId,
139
- appUid: uploadApp.uid,
140
- requiredScopes: uploadApp.config?.auth?.requiredScopes,
141
- optionalScopes: uploadApp.config?.auth?.optionalScopes,
142
- onTimeout: () => {
143
- dispatch({ type: 'SET_POLLING_TIMED_OUT', payload: true });
144
- },
145
- });
146
- dispatch({ type: 'INSTALL_APP_DONE' });
147
- }
148
- catch (error) {
149
- dispatch({
150
- type: 'INSTALL_APP_ERROR',
151
- payload: error instanceof Error
152
- ? error.message
153
- : commands.getStarted.v2.unknownError,
154
- });
155
- }
156
- }, [project.uploadResult, derivedAccountId]);
157
- const handleBrowserOpen = useCallback(async (shouldOpen) => {
158
- await trackGetStartedUsage({
159
- step: 'open-install-page',
160
- type: shouldOpen ? 'opened' : 'declined',
161
- }, derivedAccountId);
162
- if (shouldOpen && project.uploadResult?.installUrl) {
163
- try {
164
- await open(project.uploadResult.installUrl, { url: true });
165
- }
166
- catch (error) {
167
- dispatch({
168
- type: 'SET_BROWSER_FAILED_URL',
169
- payload: project.uploadResult.installUrl,
170
- });
171
- }
172
- }
173
- dispatch({ type: 'START_INSTALL_APP' });
174
- await handlePollInstallation();
175
- }, [project.uploadResult, derivedAccountId, handlePollInstallation]);
176
111
  const handleNameChange = useCallback((value) => {
177
112
  dispatch({ type: 'SET_PROJECT_NAME', payload: value });
178
113
  }, []);
@@ -181,8 +116,7 @@ export function GetStartedFlow({ derivedAccountId, initialName, initialDest, })
181
116
  }, []);
182
117
  useInput((_, key) => {
183
118
  const hasError = state.statuses.create === ACTION_STATUSES.ERROR ||
184
- state.statuses.upload === ACTION_STATUSES.ERROR ||
185
- state.statuses.installApp === ACTION_STATUSES.ERROR;
119
+ state.statuses.upload === ACTION_STATUSES.ERROR;
186
120
  if (hasError) {
187
121
  exit();
188
122
  return;
@@ -192,22 +126,11 @@ export function GetStartedFlow({ derivedAccountId, initialName, initialDest, })
192
126
  if (state.step === GET_STARTED_FLOW_STEPS.COMPLETE) {
193
127
  handleUploadStart();
194
128
  }
195
- else if (state.step === GET_STARTED_FLOW_STEPS.OPEN_APP_PROMPT) {
196
- handleBrowserOpen(true);
197
- }
198
- else if (state.step === GET_STARTED_FLOW_STEPS.INSTALLING_APP &&
199
- state.statuses.installApp === ACTION_STATUSES.DONE) {
200
- // Ready for card setup - will be handled in PR3
201
- exit();
202
- }
203
129
  });
204
130
  if (state.step === GET_STARTED_FLOW_STEPS.UPLOADING ||
205
131
  state.step === GET_STARTED_FLOW_STEPS.OPEN_APP_PROMPT) {
206
132
  return _jsx(UploadScreen, { state: state, accountName: accountName });
207
133
  }
208
- if (state.step === GET_STARTED_FLOW_STEPS.INSTALLING_APP) {
209
- return _jsx(InstallationScreen, { state: state, accountName: accountName });
210
- }
211
134
  // Show project setup screen for initial flow
212
135
  return (_jsx(ProjectSetupScreen, { state: state, onSelectOption: handleSelect, onNameChange: handleNameChange, onNameSubmit: handleNameSubmit, onDestChange: handleDestChange, onDestSubmit: handleDestSubmit }));
213
136
  }
@@ -14,7 +14,6 @@ export type AppState = {
14
14
  export type ActionStatuses = {
15
15
  create: ActionStatus;
16
16
  upload: ActionStatus;
17
- installApp: ActionStatus;
18
17
  };
19
18
  export type FlowState = {
20
19
  step: FlowStep;
@@ -22,9 +21,6 @@ export type FlowState = {
22
21
  app: AppState;
23
22
  statuses: ActionStatuses;
24
23
  error?: string;
25
- destError?: string;
26
- browserFailedUrl?: string;
27
- pollingTimedOut?: boolean;
28
24
  };
29
25
  type FlowAction = {
30
26
  type: 'SET_STEP';
@@ -41,9 +37,6 @@ type FlowAction = {
41
37
  } | {
42
38
  type: 'SET_ERROR';
43
39
  payload: string;
44
- } | {
45
- type: 'SET_DEST_ERROR';
46
- payload: string;
47
40
  } | {
48
41
  type: 'CLEAR_ERROR';
49
42
  } | {
@@ -61,19 +54,6 @@ type FlowAction = {
61
54
  } | {
62
55
  type: 'UPLOAD_ERROR';
63
56
  payload: string;
64
- } | {
65
- type: 'START_INSTALL_APP';
66
- } | {
67
- type: 'INSTALL_APP_DONE';
68
- } | {
69
- type: 'INSTALL_APP_ERROR';
70
- payload: string;
71
- } | {
72
- type: 'SET_BROWSER_FAILED_URL';
73
- payload: string;
74
- } | {
75
- type: 'SET_POLLING_TIMED_OUT';
76
- payload: boolean;
77
57
  };
78
58
  export declare function flowReducer(state: FlowState, action: FlowAction): FlowState;
79
59
  export {};
@@ -17,13 +17,6 @@ export function flowReducer(state, action) {
17
17
  return {
18
18
  ...state,
19
19
  project: { ...state.project, destination: action.payload },
20
- destError: undefined,
21
- };
22
- case 'SET_DEST_ERROR':
23
- return {
24
- ...state,
25
- step: GET_STARTED_FLOW_STEPS.DEST_INPUT,
26
- destError: action.payload,
27
20
  };
28
21
  case 'SET_ERROR':
29
22
  return { ...state, error: action.payload };
@@ -73,35 +66,6 @@ export function flowReducer(state, action) {
73
66
  statuses: { ...state.statuses, upload: ACTION_STATUSES.ERROR },
74
67
  error: action.payload,
75
68
  };
76
- case 'START_INSTALL_APP':
77
- return {
78
- ...state,
79
- step: GET_STARTED_FLOW_STEPS.INSTALLING_APP,
80
- statuses: { ...state.statuses, installApp: ACTION_STATUSES.RUNNING },
81
- error: undefined,
82
- pollingTimedOut: false,
83
- };
84
- case 'INSTALL_APP_DONE':
85
- return {
86
- ...state,
87
- statuses: { ...state.statuses, installApp: ACTION_STATUSES.DONE },
88
- };
89
- case 'INSTALL_APP_ERROR':
90
- return {
91
- ...state,
92
- statuses: { ...state.statuses, installApp: ACTION_STATUSES.ERROR },
93
- error: action.payload,
94
- };
95
- case 'SET_BROWSER_FAILED_URL':
96
- return {
97
- ...state,
98
- browserFailedUrl: action.payload,
99
- };
100
- case 'SET_POLLING_TIMED_OUT':
101
- return {
102
- ...state,
103
- pollingTimedOut: action.payload,
104
- };
105
69
  default:
106
70
  return state;
107
71
  }
@@ -35,6 +35,5 @@ export function ProjectSetupScreen({ state, onSelectOption, onNameChange, onName
35
35
  return (_jsx(BoxWithTitle, { flexGrow: 1, title: "hs get-started", borderColor: INK_COLORS.HUBSPOT_ORANGE, titleBackgroundColor: INK_COLORS.HUBSPOT_ORANGE, children: _jsxs(Box, { flexDirection: "column", rowGap: 1, children: [_jsx(Text, { bold: true, children: titleText }), state.step === GET_STARTED_FLOW_STEPS.SELECT ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: overviewText }), _jsx(Text, { children: projectsText }), _jsxs(Box, { flexDirection: "row", flexWrap: "wrap", columnGap: 1, children: [_jsx(Text, { color: INK_COLORS.HUBSPOT_TEAL, children: "?" }), _jsx(Text, { children: selectPrompt })] }), _jsx(SelectInput, { items: GET_STARTED_FLOW_OPTIONS, onSelect: onSelectOption })] })) : (_jsxs(Box, { flexDirection: "row", flexWrap: "wrap", columnGap: 1, children: [_jsx(Text, { color: INK_COLORS.HUBSPOT_TEAL, children: "?" }), _jsx(Text, { children: `${selectPrompt}` }), _jsx(Text, { color: INK_COLORS.INFO_BLUE, children: state.app.selectedLabel })] })), _jsxs(ActionSection, { status: state.statuses.create, statusText: runningProjectCreateText, errorMessage: state.statuses.create === ACTION_STATUSES.ERROR
36
36
  ? `${state.error}\n\n${commands.getStarted.v2.pressKeyToExit}`
37
37
  : undefined, children: [state.step !== GET_STARTED_FLOW_STEPS.SELECT && (_jsx(InputField, { flag: "name", prompt: "Enter your project name", value: project.name, isEditing: state.step === GET_STARTED_FLOW_STEPS.NAME_INPUT, onChange: onNameChange, onSubmit: onNameSubmit })), state.step !== GET_STARTED_FLOW_STEPS.SELECT &&
38
- state.step !== GET_STARTED_FLOW_STEPS.NAME_INPUT && (_jsxs(_Fragment, { children: [_jsx(InputField, { flag: "dest", prompt: "Choose where to create the project", value: project.destination, isEditing: state.step === GET_STARTED_FLOW_STEPS.DEST_INPUT, onChange: onDestChange, onSubmit: onDestSubmit }), state.destError &&
39
- state.step === GET_STARTED_FLOW_STEPS.DEST_INPUT && (_jsx(Text, { color: INK_COLORS.ALERT_RED, children: state.destError }))] }))] }), state.step === GET_STARTED_FLOW_STEPS.COMPLETE && (_jsxs(Box, { flexDirection: "row", flexWrap: "wrap", columnGap: 1, children: [_jsx(Text, { color: INK_COLORS.HUBSPOT_TEAL, children: "?" }), _jsx(Text, { children: commands.getStarted.v2.pressEnterToContinueDeploy(state.app.selectedLabel) })] }))] }) }));
38
+ state.step !== GET_STARTED_FLOW_STEPS.NAME_INPUT && (_jsx(InputField, { flag: "dest", prompt: "Choose where to create the project", value: project.destination, isEditing: state.step === GET_STARTED_FLOW_STEPS.DEST_INPUT, onChange: onDestChange, onSubmit: onDestSubmit }))] }), state.step === GET_STARTED_FLOW_STEPS.COMPLETE && (_jsxs(Box, { flexDirection: "row", flexWrap: "wrap", columnGap: 1, children: [_jsx(Text, { color: INK_COLORS.HUBSPOT_TEAL, children: "?" }), _jsx(Text, { children: commands.getStarted.v2.pressEnterToContinueDeploy(state.app.selectedLabel) })] }))] }) }));
40
39
  }
@@ -12,6 +12,5 @@ export declare const GET_STARTED_FLOW_STEPS: {
12
12
  readonly INSTALLING: "installing";
13
13
  readonly UPLOADING: "uploading";
14
14
  readonly OPEN_APP_PROMPT: "open-app-prompt";
15
- readonly INSTALLING_APP: "installing-app";
16
15
  readonly COMPLETE: "complete";
17
16
  };
@@ -12,6 +12,5 @@ export const GET_STARTED_FLOW_STEPS = {
12
12
  INSTALLING: 'installing',
13
13
  UPLOADING: 'uploading',
14
14
  OPEN_APP_PROMPT: 'open-app-prompt',
15
- INSTALLING_APP: 'installing-app',
16
15
  COMPLETE: 'complete',
17
16
  };
@@ -1,7 +0,0 @@
1
- import { FlowState } from '../reducer.js';
2
- type InstallationScreenProps = {
3
- state: FlowState;
4
- accountName: string;
5
- };
6
- export declare function InstallationScreen({ state, accountName, }: InstallationScreenProps): import("react/jsx-runtime").JSX.Element;
7
- export {};
@@ -1,16 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
3
- import { commands } from '../../../../lang/en.js';
4
- import { ActionSection } from '../../ActionSection.js';
5
- import { BoxWithTitle } from '../../BoxWithTitle.js';
6
- import { INK_COLORS } from '../../../styles.js';
7
- import { getProject } from '../selectors.js';
8
- import { ACTION_STATUSES, GET_STARTED_FLOW_STEPS, } from '../../../lib/constants.js';
9
- export function InstallationScreen({ state, accountName, }) {
10
- const project = getProject(state);
11
- const titleText = commands.getStarted.v2.startTitle;
12
- // If we get to the installation screen, the app is uploaded and we have the name
13
- const appName = project.uploadResult?.app?.config.name;
14
- return (_jsx(BoxWithTitle, { flexGrow: 1, title: "hs get-started", borderColor: INK_COLORS.HUBSPOT_ORANGE, titleBackgroundColor: INK_COLORS.HUBSPOT_ORANGE, children: _jsxs(Box, { flexDirection: "column", rowGap: 1, children: [_jsx(Text, { bold: true, children: titleText }), _jsx(Text, { children: commands.getStarted.v2.installInstructions }), _jsx(ActionSection, { status: state.statuses.installApp, statusText: commands.getStarted.v2.installingApp(appName, accountName) }), state.browserFailedUrl && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Text, { color: INK_COLORS.WARNING_YELLOW, children: commands.getStarted.v2.browserFailedToOpen(state.browserFailedUrl) }) })), state.pollingTimedOut && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Text, { color: INK_COLORS.WARNING_YELLOW, children: commands.getStarted.v2.pollingTimeout(2) }) })), state.step === GET_STARTED_FLOW_STEPS.INSTALLING_APP &&
15
- state.statuses.installApp === ACTION_STATUSES.DONE && (_jsx(Text, { children: commands.getStarted.v2.pressEnterToContinueSetup }))] }) }));
16
- }