@hubspot/cli 8.0.10-experimental.1 → 8.0.10-experimental.3
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.
- package/commands/account/auth.js +15 -5
- package/commands/account/use.js +14 -4
- package/commands/auth.js +10 -6
- package/commands/cms/__tests__/watch.test.js +0 -8
- package/commands/cms/function/logs.js +1 -0
- package/commands/cms/theme/preview.js +2 -4
- package/commands/cms/watch.d.ts +0 -1
- package/commands/cms/watch.js +2 -8
- package/commands/feedback.js +1 -1
- package/commands/hubdb/clear.js +4 -0
- package/commands/hubdb/delete.js +4 -0
- package/commands/hubdb/fetch.js +4 -0
- package/commands/init.js +4 -0
- package/commands/mcp/__tests__/start.test.js +8 -1
- package/commands/mcp/setup.js +1 -9
- package/commands/mcp/start.js +0 -1
- package/commands/project/__tests__/create.test.js +1 -1
- package/commands/project/create.js +2 -2
- package/commands/project/dev/index.js +29 -19
- package/commands/project/download.js +5 -1
- package/commands/project/watch.js +15 -2
- package/commands/sandbox/__tests__/create.test.js +1 -48
- package/commands/sandbox/create.js +3 -30
- package/commands/testAccount/create.js +4 -0
- package/lang/en.d.ts +13 -6
- package/lang/en.js +13 -6
- package/lib/__tests__/buildAccount.test.js +1 -52
- package/lib/__tests__/sandboxes.test.js +1 -29
- package/lib/__tests__/serverlessLogs.test.js +79 -64
- package/lib/accountAuth.js +4 -0
- package/lib/buildAccount.d.ts +1 -6
- package/lib/buildAccount.js +9 -42
- package/lib/constants.d.ts +1 -3
- package/lib/constants.js +1 -3
- package/lib/errors/PromptExitError.d.ts +4 -0
- package/lib/errors/PromptExitError.js +8 -0
- package/lib/generateSelectors.js +1 -2
- package/lib/mcp/__tests__/setup.test.js +357 -28
- package/lib/mcp/setup.d.ts +1 -0
- package/lib/mcp/setup.js +77 -30
- package/lib/projects/__tests__/components.test.js +14 -0
- package/lib/projects/components.js +12 -2
- package/lib/projects/create/__tests__/legacy.test.js +6 -24
- package/lib/projects/create/index.js +1 -4
- package/lib/projects/create/legacy.js +3 -8
- package/lib/projects/create/v2.js +1 -9
- package/lib/projects/ensureProjectExists.js +1 -2
- package/lib/projects/localDev/AppDevModeInterface.js +4 -0
- package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +4 -0
- package/lib/projects/localDev/helpers/account.js +5 -11
- package/lib/projects/pollProjectBuildAndDeploy.js +90 -85
- package/lib/projects/upload.d.ts +1 -0
- package/lib/projects/upload.js +37 -46
- package/lib/projects/watch.d.ts +2 -1
- package/lib/projects/watch.js +32 -24
- package/lib/prompts/downloadProjectPrompt.js +11 -10
- package/lib/prompts/installAppPrompt.js +3 -2
- package/lib/prompts/personalAccessKeyPrompt.js +3 -2
- package/lib/prompts/projectDevTargetAccountPrompt.js +13 -16
- package/lib/prompts/selectHubDBTablePrompt.js +8 -4
- package/lib/prompts/selectPublicAppForMigrationPrompt.js +12 -6
- package/lib/sandboxes.d.ts +1 -9
- package/lib/sandboxes.js +0 -21
- package/lib/serverlessLogs.js +50 -44
- package/lib/{cms/devServerProcess.d.ts → theme/cmsDevServerProcess.d.ts} +2 -3
- package/lib/theme/cmsDevServerProcess.js +148 -0
- package/lib/theme/cmsDevServerRunner.d.ts +14 -0
- package/lib/theme/cmsDevServerRunner.js +90 -0
- package/lib/usageTracking.js +8 -5
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +1 -1
- package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
- package/mcp-server/tools/cms/HsListTool.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -2
- package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -2
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -2
- package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +1 -2
- package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -2
- package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -2
- package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +1 -1
- package/mcp-server/tools/project/AddFeatureToProjectTool.js +1 -1
- package/mcp-server/tools/project/CreateProjectTool.d.ts +1 -1
- package/mcp-server/tools/project/CreateProjectTool.js +1 -1
- package/mcp-server/tools/project/CreateTestAccountTool.js +1 -1
- package/mcp-server/tools/project/DeployProjectTool.js +1 -1
- package/mcp-server/tools/project/UploadProjectTools.js +1 -1
- package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
- package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -2
- package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -2
- package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +1 -2
- package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -2
- package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +10 -2
- package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +2 -2
- package/mcp-server/tools/project/constants.d.ts +1 -1
- package/mcp-server/utils/__tests__/command.test.js +233 -3
- package/mcp-server/utils/__tests__/feedbackTracking.test.js +9 -64
- package/mcp-server/utils/command.d.ts +5 -0
- package/mcp-server/utils/command.js +24 -0
- package/mcp-server/utils/feedbackTracking.js +2 -17
- package/package.json +4 -5
- package/lib/__tests__/sandboxSync.test.d.ts +0 -1
- package/lib/__tests__/sandboxSync.test.js +0 -147
- package/lib/cms/devServerProcess.js +0 -200
- package/lib/sandboxSync.d.ts +0 -4
- package/lib/sandboxSync.js +0 -102
- package/mcp-server/utils/__tests__/project.test.d.ts +0 -1
- package/mcp-server/utils/__tests__/project.test.js +0 -140
- package/mcp-server/utils/project.d.ts +0 -5
- package/mcp-server/utils/project.js +0 -18
package/commands/account/auth.js
CHANGED
|
@@ -10,6 +10,7 @@ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
|
10
10
|
import { commands } from '../../lang/en.js';
|
|
11
11
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
12
12
|
import { authenticateNewAccount } from '../../lib/accountAuth.js';
|
|
13
|
+
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
13
14
|
const TRACKING_STATUS = {
|
|
14
15
|
STARTED: 'started',
|
|
15
16
|
ERROR: 'error',
|
|
@@ -35,11 +36,20 @@ async function handler(args) {
|
|
|
35
36
|
await trackAuthAction('account-auth', authType, TRACKING_STATUS.STARTED);
|
|
36
37
|
}
|
|
37
38
|
handleExit(deleteConfigFileIfEmpty);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
let updatedConfig;
|
|
40
|
+
try {
|
|
41
|
+
updatedConfig = await authenticateNewAccount({
|
|
42
|
+
env: args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD,
|
|
43
|
+
providedPersonalAccessKey,
|
|
44
|
+
accountId: parsedUserProvidedAccountId,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
if (e instanceof PromptExitError) {
|
|
49
|
+
process.exit(e.exitCode);
|
|
50
|
+
}
|
|
51
|
+
throw e;
|
|
52
|
+
}
|
|
43
53
|
if (!updatedConfig) {
|
|
44
54
|
if (!disableTracking) {
|
|
45
55
|
await trackAuthAction('account-auth', authType, TRACKING_STATUS.ERROR);
|
package/commands/account/use.js
CHANGED
|
@@ -7,6 +7,7 @@ import { uiLogger } from '../../lib/ui/logger.js';
|
|
|
7
7
|
import { selectAccountFromConfig, AUTHENTICATE_NEW_ACCOUNT_VALUE, } from '../../lib/prompts/accountsPrompt.js';
|
|
8
8
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
9
9
|
import { authenticateNewAccount } from '../../lib/accountAuth.js';
|
|
10
|
+
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
10
11
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
11
12
|
const command = 'use [account]';
|
|
12
13
|
const describe = commands.account.subcommands.use.describe;
|
|
@@ -24,10 +25,19 @@ async function handler(args) {
|
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
if (newDefaultAccount === AUTHENTICATE_NEW_ACCOUNT_VALUE) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
let updatedConfig;
|
|
29
|
+
try {
|
|
30
|
+
updatedConfig = await authenticateNewAccount({
|
|
31
|
+
env: args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD,
|
|
32
|
+
setAsDefaultAccount: true,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
if (e instanceof PromptExitError) {
|
|
37
|
+
process.exit(e.exitCode);
|
|
38
|
+
}
|
|
39
|
+
throw e;
|
|
40
|
+
}
|
|
31
41
|
if (!updatedConfig) {
|
|
32
42
|
process.exit(EXIT_CODES.ERROR);
|
|
33
43
|
}
|
package/commands/auth.js
CHANGED
|
@@ -16,6 +16,7 @@ import { authenticateWithOauth } from '../lib/oauth.js';
|
|
|
16
16
|
import { EXIT_CODES } from '../lib/enums/exitCodes.js';
|
|
17
17
|
import { uiFeatureHighlight } from '../lib/ui/index.js';
|
|
18
18
|
import { logError } from '../lib/errorHandlers/index.js';
|
|
19
|
+
import { PromptExitError } from '../lib/errors/PromptExitError.js';
|
|
19
20
|
import { commands } from '../lang/en.js';
|
|
20
21
|
import { uiLogger } from '../lib/ui/logger.js';
|
|
21
22
|
import { parseStringToNumber } from '../lib/parsing.js';
|
|
@@ -90,18 +91,21 @@ async function handler(args) {
|
|
|
90
91
|
}
|
|
91
92
|
break;
|
|
92
93
|
case PERSONAL_ACCESS_KEY_AUTH_METHOD.value:
|
|
93
|
-
const { personalAccessKey } = providedPersonalAccessKey
|
|
94
|
-
? { personalAccessKey: providedPersonalAccessKey }
|
|
95
|
-
: await personalAccessKeyPrompt({
|
|
96
|
-
env,
|
|
97
|
-
account: parsedUserProvidedAccountId,
|
|
98
|
-
});
|
|
99
94
|
try {
|
|
95
|
+
const { personalAccessKey } = providedPersonalAccessKey
|
|
96
|
+
? { personalAccessKey: providedPersonalAccessKey }
|
|
97
|
+
: await personalAccessKeyPrompt({
|
|
98
|
+
env,
|
|
99
|
+
account: parsedUserProvidedAccountId,
|
|
100
|
+
});
|
|
100
101
|
token = await getAccessToken(personalAccessKey, env);
|
|
101
102
|
defaultName = token.hubName ? toKebabCase(token.hubName) : undefined;
|
|
102
103
|
updatedConfig = await updateConfigWithAccessToken(token, personalAccessKey, env);
|
|
103
104
|
}
|
|
104
105
|
catch (e) {
|
|
106
|
+
if (e instanceof PromptExitError) {
|
|
107
|
+
process.exit(e.exitCode);
|
|
108
|
+
}
|
|
105
109
|
logError(e);
|
|
106
110
|
}
|
|
107
111
|
if (!updatedConfig) {
|
|
@@ -64,7 +64,6 @@ describe('commands/cms/watch', () => {
|
|
|
64
64
|
expect(positionalSpy).toHaveBeenCalledWith('dest', expect.objectContaining({ type: 'string' }));
|
|
65
65
|
expect(optionSpy).toHaveBeenCalledWith('remove', expect.objectContaining({ type: 'boolean', alias: 'r' }));
|
|
66
66
|
expect(optionSpy).toHaveBeenCalledWith('initial-upload', expect.objectContaining({ type: 'boolean', alias: 'i' }));
|
|
67
|
-
expect(optionSpy).toHaveBeenCalledWith('disable-initial', expect.objectContaining({ type: 'boolean' }));
|
|
68
67
|
expect(optionSpy).toHaveBeenCalledWith('notify', expect.objectContaining({ type: 'string', alias: 'n' }));
|
|
69
68
|
expect(optionSpy).toHaveBeenCalledWith('convert-fields', expect.objectContaining({ type: 'boolean' }));
|
|
70
69
|
});
|
|
@@ -78,7 +77,6 @@ describe('commands/cms/watch', () => {
|
|
|
78
77
|
derivedAccountId: 123456,
|
|
79
78
|
remove: false,
|
|
80
79
|
initialUpload: false,
|
|
81
|
-
disableInitial: false,
|
|
82
80
|
};
|
|
83
81
|
statSyncSpy.mockReturnValue({
|
|
84
82
|
isFile: () => false,
|
|
@@ -134,7 +132,6 @@ describe('commands/cms/watch', () => {
|
|
|
134
132
|
});
|
|
135
133
|
it('should start watching without initial upload by default', async () => {
|
|
136
134
|
await watchCommand.handler(args);
|
|
137
|
-
expect(uiLogger.info).toHaveBeenCalledWith(expect.stringContaining('not'));
|
|
138
135
|
expect(getUploadableFileListSpy).not.toHaveBeenCalled();
|
|
139
136
|
expect(watchSpy).toHaveBeenCalledWith(123456, path.resolve('/test/cwd', 'src'), '/dest', expect.objectContaining({
|
|
140
137
|
cmsPublishMode: 'publish',
|
|
@@ -152,11 +149,6 @@ describe('commands/cms/watch', () => {
|
|
|
152
149
|
filePaths: ['file1.js', 'file2.js'],
|
|
153
150
|
}), null, expect.any(Function), undefined, expect.any(Function));
|
|
154
151
|
});
|
|
155
|
-
it('should show disable initial message when disableInitial is true', async () => {
|
|
156
|
-
args.disableInitial = true;
|
|
157
|
-
await watchCommand.handler(args);
|
|
158
|
-
expect(uiLogger.info).toHaveBeenCalledWith(expect.stringContaining('disable'));
|
|
159
|
-
});
|
|
160
152
|
it('should pass remove option to watch', async () => {
|
|
161
153
|
args.remove = true;
|
|
162
154
|
await watchCommand.handler(args);
|
|
@@ -3,9 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import { commands } from '../../../lang/en.js';
|
|
4
4
|
import { getCwd } from '@hubspot/local-dev-lib/path';
|
|
5
5
|
import { getThemeJSONPath } from '@hubspot/local-dev-lib/cms/themes';
|
|
6
|
-
|
|
7
|
-
// import { createDevServer } from '@hubspot/cms-dev-server';
|
|
8
|
-
import { spawnDevServer } from '../../../lib/cms/devServerProcess.js';
|
|
6
|
+
import { spawnDevServer } from '../../../lib/theme/cmsDevServerProcess.js';
|
|
9
7
|
import { trackCommandUsage } from '../../../lib/usageTracking.js';
|
|
10
8
|
import { previewPrompt, previewProjectPrompt, } from '../../../lib/prompts/previewPrompt.js';
|
|
11
9
|
import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
@@ -74,7 +72,7 @@ async function handler(args) {
|
|
|
74
72
|
trackCommandUsage('preview', {}, derivedAccountId);
|
|
75
73
|
// Spawn dev server in isolated subprocess to avoid React version conflicts
|
|
76
74
|
// File listing and progress bars are handled within the subprocess
|
|
77
|
-
spawnDevServer({
|
|
75
|
+
await spawnDevServer({
|
|
78
76
|
absoluteSrc,
|
|
79
77
|
accountName: derivedAccountId?.toString(),
|
|
80
78
|
noSsl,
|
package/commands/cms/watch.d.ts
CHANGED
package/commands/cms/watch.js
CHANGED
|
@@ -15,7 +15,7 @@ import { uiLogger } from '../../lib/ui/logger.js';
|
|
|
15
15
|
const command = 'watch [src] [dest]';
|
|
16
16
|
const describe = commands.cms.subcommands.watch.describe;
|
|
17
17
|
const handler = async (args) => {
|
|
18
|
-
const { remove, initialUpload,
|
|
18
|
+
const { remove, initialUpload, notify, derivedAccountId } = args;
|
|
19
19
|
if (!validateCmsPublishMode(args)) {
|
|
20
20
|
process.exit(EXIT_CODES.ERROR);
|
|
21
21
|
}
|
|
@@ -40,13 +40,6 @@ const handler = async (args) => {
|
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
let filesToUpload = [];
|
|
43
|
-
if (disableInitial) {
|
|
44
|
-
uiLogger.info(commands.cms.subcommands.watch.warnings.disableInitial);
|
|
45
|
-
}
|
|
46
|
-
else if (!initialUpload) {
|
|
47
|
-
uiLogger.info(commands.cms.subcommands.watch.warnings.notUploaded(src));
|
|
48
|
-
uiLogger.info(commands.cms.subcommands.watch.warnings.initialUpload);
|
|
49
|
-
}
|
|
50
43
|
if (initialUpload) {
|
|
51
44
|
filesToUpload = await getUploadableFileList(absoluteSrcPath, args.convertFields);
|
|
52
45
|
}
|
|
@@ -99,6 +92,7 @@ function watchBuilder(yargs) {
|
|
|
99
92
|
describe: commands.cms.subcommands.watch.options.initialUpload,
|
|
100
93
|
type: 'boolean',
|
|
101
94
|
});
|
|
95
|
+
// TODO: remove this before the next major release
|
|
102
96
|
yargs.option('disable-initial', {
|
|
103
97
|
describe: commands.cms.subcommands.watch.options.disableInitial,
|
|
104
98
|
type: 'boolean',
|
package/commands/feedback.js
CHANGED
|
@@ -4,7 +4,7 @@ import { makeYargsBuilder } from '../lib/yargsUtils.js';
|
|
|
4
4
|
import { EXIT_CODES } from '../lib/enums/exitCodes.js';
|
|
5
5
|
import { commands } from '../lang/en.js';
|
|
6
6
|
import { uiLogger } from '../lib/ui/logger.js';
|
|
7
|
-
|
|
7
|
+
import { FEEDBACK_URL } from '../lib/constants.js';
|
|
8
8
|
const command = 'feedback';
|
|
9
9
|
const describe = commands.project.feedback.describe;
|
|
10
10
|
async function handler() {
|
package/commands/hubdb/clear.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
2
2
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
3
|
+
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
3
4
|
import { clearHubDbTableRows } from '@hubspot/local-dev-lib/hubdb';
|
|
4
5
|
import { publishTable } from '@hubspot/local-dev-lib/api/hubdb';
|
|
5
6
|
import { selectHubDBTablePrompt } from '../../lib/prompts/selectHubDBTablePrompt.js';
|
|
@@ -29,6 +30,9 @@ async function handler(args) {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
catch (e) {
|
|
33
|
+
if (e instanceof PromptExitError) {
|
|
34
|
+
process.exit(e.exitCode);
|
|
35
|
+
}
|
|
32
36
|
logError(e);
|
|
33
37
|
}
|
|
34
38
|
}
|
package/commands/hubdb/delete.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
2
2
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
3
|
+
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
3
4
|
import { deleteTable } from '@hubspot/local-dev-lib/api/hubdb';
|
|
4
5
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
5
6
|
import { selectHubDBTablePrompt } from '../../lib/prompts/selectHubDBTablePrompt.js';
|
|
@@ -34,6 +35,9 @@ async function handler(args) {
|
|
|
34
35
|
process.exit(EXIT_CODES.SUCCESS);
|
|
35
36
|
}
|
|
36
37
|
catch (e) {
|
|
38
|
+
if (e instanceof PromptExitError) {
|
|
39
|
+
process.exit(e.exitCode);
|
|
40
|
+
}
|
|
37
41
|
uiLogger.error(commands.hubdb.subcommands.delete.errors.delete(args.tableId || ''));
|
|
38
42
|
logError(e);
|
|
39
43
|
}
|
package/commands/hubdb/fetch.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
2
2
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
3
|
+
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
3
4
|
import { downloadHubDbTable } from '@hubspot/local-dev-lib/hubdb';
|
|
4
5
|
import { selectHubDBTablePrompt } from '../../lib/prompts/selectHubDBTablePrompt.js';
|
|
5
6
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
@@ -22,6 +23,9 @@ async function handler(args) {
|
|
|
22
23
|
uiLogger.success(commands.hubdb.subcommands.fetch.success.fetch(tableId, filePath));
|
|
23
24
|
}
|
|
24
25
|
catch (e) {
|
|
26
|
+
if (e instanceof PromptExitError) {
|
|
27
|
+
process.exit(e.exitCode);
|
|
28
|
+
}
|
|
25
29
|
logError(e);
|
|
26
30
|
}
|
|
27
31
|
}
|
package/commands/init.js
CHANGED
|
@@ -12,6 +12,7 @@ import { setCLILogLevel } from '../lib/commonOpts.js';
|
|
|
12
12
|
import { makeYargsBuilder } from '../lib/yargsUtils.js';
|
|
13
13
|
import { handleExit } from '../lib/process.js';
|
|
14
14
|
import { debugError, logError } from '../lib/errorHandlers/index.js';
|
|
15
|
+
import { PromptExitError } from '../lib/errors/PromptExitError.js';
|
|
15
16
|
import { trackCommandUsage, trackAuthAction } from '../lib/usageTracking.js';
|
|
16
17
|
import { promptUser } from '../lib/prompts/promptUtils.js';
|
|
17
18
|
import { OAUTH_FLOW, personalAccessKeyPrompt, } from '../lib/prompts/personalAccessKeyPrompt.js';
|
|
@@ -150,6 +151,9 @@ async function handler(args) {
|
|
|
150
151
|
process.exit(EXIT_CODES.SUCCESS);
|
|
151
152
|
}
|
|
152
153
|
catch (err) {
|
|
154
|
+
if (err instanceof PromptExitError) {
|
|
155
|
+
process.exit(err.exitCode);
|
|
156
|
+
}
|
|
153
157
|
logError(err);
|
|
154
158
|
if (!disableTracking) {
|
|
155
159
|
await trackAuthAction('init', authType, TRACKING_STATUS.ERROR, parsedUserProvidedAccountId);
|
|
@@ -7,14 +7,20 @@ import * as errorHandlers from '../../../lib/errorHandlers/index.js';
|
|
|
7
7
|
import * as usageTrackingLib from '../../../lib/usageTracking.js';
|
|
8
8
|
import * as processLib from '../../../lib/process.js';
|
|
9
9
|
import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
10
|
-
|
|
10
|
+
// Create a mock execAsync function before importing the module
|
|
11
|
+
const execAsyncMock = vi.fn();
|
|
11
12
|
vi.mock('yargs');
|
|
12
13
|
vi.mock('../../../lib/commonOpts');
|
|
13
14
|
vi.mock('node:child_process');
|
|
15
|
+
vi.mock('node:util', () => ({
|
|
16
|
+
promisify: vi.fn(() => execAsyncMock),
|
|
17
|
+
}));
|
|
14
18
|
vi.mock('fs');
|
|
15
19
|
vi.mock('@hubspot/local-dev-lib/config');
|
|
16
20
|
vi.mock('../../../lib/errorHandlers/index.js');
|
|
17
21
|
vi.mock('../../../lib/process.js');
|
|
22
|
+
// Import after mocks are set up
|
|
23
|
+
const startCommand = await import('../start.js').then(m => m.default);
|
|
18
24
|
const spawnSpy = vi.mocked(spawn);
|
|
19
25
|
const existsSyncSpy = vi.spyOn(fs, 'existsSync');
|
|
20
26
|
const trackCommandUsageSpy = vi.spyOn(usageTrackingLib, 'trackCommandUsage');
|
|
@@ -36,6 +42,7 @@ describe('commands/mcp/start', () => {
|
|
|
36
42
|
processExitSpy.mockImplementation(() => { });
|
|
37
43
|
// Mock config to prevent reading actual config file in CI
|
|
38
44
|
getConfigAccountIfExistsSpy.mockReturnValue(undefined);
|
|
45
|
+
execAsyncMock.mockClear();
|
|
39
46
|
});
|
|
40
47
|
describe('command', () => {
|
|
41
48
|
it('should have the correct command structure', () => {
|
package/commands/mcp/setup.js
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
2
2
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
3
3
|
import { commands } from '../../lang/en.js';
|
|
4
|
-
import { uiLogger } from '../../lib/ui/logger.js';
|
|
5
4
|
import { addMcpServerToConfig, supportedTools } from '../../lib/mcp/setup.js';
|
|
6
5
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
7
|
-
import { hasFeature } from '../../lib/hasFeature.js';
|
|
8
|
-
import { FEATURES } from '../../lib/constants.js';
|
|
9
6
|
const command = ['setup'];
|
|
10
7
|
const describe = commands.mcp.setup.describe;
|
|
11
8
|
async function handler(args) {
|
|
12
9
|
const { derivedAccountId } = args;
|
|
13
|
-
|
|
14
|
-
if (!hasMcpAccess) {
|
|
15
|
-
uiLogger.error(commands.mcp.setup.errors.needsMcpAccess(derivedAccountId));
|
|
16
|
-
process.exit(EXIT_CODES.ERROR);
|
|
17
|
-
}
|
|
18
|
-
trackCommandUsage('mcp-setup', {}, args.derivedAccountId);
|
|
10
|
+
await trackCommandUsage('mcp-setup', {}, derivedAccountId);
|
|
19
11
|
try {
|
|
20
12
|
await addMcpServerToConfig(args.client);
|
|
21
13
|
}
|
package/commands/mcp/start.js
CHANGED
|
@@ -28,7 +28,6 @@ async function startMcpServer(aiAgent) {
|
|
|
28
28
|
uiLogger.debug(commands.mcp.start.startingServer);
|
|
29
29
|
uiLogger.debug(commands.mcp.start.stopInstructions);
|
|
30
30
|
const args = [serverPath];
|
|
31
|
-
// Start the server using ts-node
|
|
32
31
|
const child = spawn(`node`, args, {
|
|
33
32
|
stdio: 'inherit',
|
|
34
33
|
env: {
|
|
@@ -50,7 +50,7 @@ describe('commands/project/create', () => {
|
|
|
50
50
|
expect(optionsCall['platform-version']).toEqual(expect.objectContaining({
|
|
51
51
|
describe: 'The target platform version for the new project.',
|
|
52
52
|
type: 'string',
|
|
53
|
-
choices: ['2025.1', '2025.2'],
|
|
53
|
+
choices: ['2025.1', '2025.2', '2026.03-beta'],
|
|
54
54
|
default: '2025.2',
|
|
55
55
|
}));
|
|
56
56
|
});
|
|
@@ -20,7 +20,7 @@ import { updateHsMetaFilesWithAutoGeneratedFields } from '../../lib/projects/com
|
|
|
20
20
|
import SpinniesManager from '../../lib/ui/SpinniesManager.js';
|
|
21
21
|
const command = ['create', 'init'];
|
|
22
22
|
const describe = commands.project.create.describe;
|
|
23
|
-
const { v2025_1, v2025_2 } = PLATFORM_VERSIONS;
|
|
23
|
+
const { v2025_1, v2025_2, v2026_03_beta } = PLATFORM_VERSIONS;
|
|
24
24
|
async function handler(args) {
|
|
25
25
|
const { derivedAccountId, platformVersion, templateSource } = args;
|
|
26
26
|
if (templateSource && !templateSource.includes('/')) {
|
|
@@ -125,7 +125,7 @@ function projectCreateBuilder(yargs) {
|
|
|
125
125
|
'platform-version': {
|
|
126
126
|
describe: commands.project.create.options.platformVersion.describe,
|
|
127
127
|
type: 'string',
|
|
128
|
-
choices: [v2025_1, v2025_2],
|
|
128
|
+
choices: [v2025_1, v2025_2, v2026_03_beta],
|
|
129
129
|
default: v2025_2,
|
|
130
130
|
},
|
|
131
131
|
'project-base': {
|
|
@@ -12,6 +12,7 @@ import { loadProfile } from '../../../lib/projects/projectProfiles.js';
|
|
|
12
12
|
import { commands } from '../../../lang/en.js';
|
|
13
13
|
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
14
14
|
import { logError } from '../../../lib/errorHandlers/index.js';
|
|
15
|
+
import { PromptExitError } from '../../../lib/errors/PromptExitError.js';
|
|
15
16
|
import path from 'path';
|
|
16
17
|
import { listPrompt } from '../../../lib/prompts/promptUtils.js';
|
|
17
18
|
const command = 'dev';
|
|
@@ -99,26 +100,35 @@ async function handler(args) {
|
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
trackCommandUsage('project-dev', {}, targetProjectAccountId);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
103
|
+
try {
|
|
104
|
+
if (isV2Project(projectConfig.platformVersion)) {
|
|
105
|
+
const targetTestingAccountId = testingAccount
|
|
106
|
+
? getConfigAccountIfExists(testingAccount)?.accountId
|
|
107
|
+
: undefined;
|
|
108
|
+
await unifiedProjectDevFlow({
|
|
109
|
+
args,
|
|
110
|
+
targetProjectAccountId,
|
|
111
|
+
providedTargetTestingAccountId: targetTestingAccountId,
|
|
112
|
+
projectConfig,
|
|
113
|
+
projectDir,
|
|
114
|
+
profileConfig: profile,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
await deprecatedProjectDevFlow({
|
|
119
|
+
args,
|
|
120
|
+
accountId: targetProjectAccountId,
|
|
121
|
+
projectConfig,
|
|
122
|
+
projectDir,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
114
125
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
});
|
|
126
|
+
catch (e) {
|
|
127
|
+
if (e instanceof PromptExitError) {
|
|
128
|
+
process.exit(e.exitCode);
|
|
129
|
+
}
|
|
130
|
+
logError(e);
|
|
131
|
+
process.exit(EXIT_CODES.ERROR);
|
|
122
132
|
}
|
|
123
133
|
}
|
|
124
134
|
function projectDevBuilder(yargs) {
|
|
@@ -3,6 +3,7 @@ import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
|
|
|
3
3
|
import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
|
|
4
4
|
import { downloadProject, fetchProjectBuilds, } from '@hubspot/local-dev-lib/api/projects';
|
|
5
5
|
import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
|
|
6
|
+
import { PromptExitError } from '../../lib/errors/PromptExitError.js';
|
|
6
7
|
import { getProjectConfig } from '../../lib/projects/config.js';
|
|
7
8
|
import { downloadProjectPrompt } from '../../lib/prompts/downloadProjectPrompt.js';
|
|
8
9
|
import { commands } from '../../lang/en.js';
|
|
@@ -19,10 +20,10 @@ async function handler(args) {
|
|
|
19
20
|
process.exit(EXIT_CODES.ERROR);
|
|
20
21
|
}
|
|
21
22
|
const { dest, build, derivedAccountId } = args;
|
|
22
|
-
const { project: projectName } = await downloadProjectPrompt(args);
|
|
23
23
|
let buildNumberToDownload = build;
|
|
24
24
|
trackCommandUsage('project-download', undefined, derivedAccountId);
|
|
25
25
|
try {
|
|
26
|
+
const { project: projectName } = await downloadProjectPrompt(args);
|
|
26
27
|
if (!buildNumberToDownload) {
|
|
27
28
|
const { data: projectBuildsResult } = await fetchProjectBuilds(derivedAccountId, projectName);
|
|
28
29
|
const { results: projectBuilds } = projectBuildsResult;
|
|
@@ -45,6 +46,9 @@ async function handler(args) {
|
|
|
45
46
|
process.exit(EXIT_CODES.SUCCESS);
|
|
46
47
|
}
|
|
47
48
|
catch (e) {
|
|
49
|
+
if (e instanceof PromptExitError) {
|
|
50
|
+
process.exit(e.exitCode);
|
|
51
|
+
}
|
|
48
52
|
logError(e, new ApiErrorContext({
|
|
49
53
|
accountId: derivedAccountId,
|
|
50
54
|
request: 'project download',
|
|
@@ -76,18 +76,30 @@ async function handler(args) {
|
|
|
76
76
|
try {
|
|
77
77
|
const { data: { results: builds }, } = await fetchProjectBuilds(derivedAccountId, projectConfig.name);
|
|
78
78
|
const hasNoBuilds = !builds || !builds.length;
|
|
79
|
+
const handleWatchTermination = (error) => {
|
|
80
|
+
if (error) {
|
|
81
|
+
logError(error, new ApiErrorContext({ accountId: derivedAccountId }));
|
|
82
|
+
process.exit(EXIT_CODES.ERROR);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
79
88
|
const startWatching = async () => {
|
|
80
|
-
await createWatcher(derivedAccountId, projectConfig, projectDir, handleBuildStatus, handleUserInput);
|
|
89
|
+
await createWatcher(derivedAccountId, projectConfig, projectDir, handleBuildStatus, handleUserInput, handleWatchTermination);
|
|
81
90
|
};
|
|
82
91
|
// Upload all files if no build exists for this project yet
|
|
83
92
|
if (initialUpload || hasNoBuilds) {
|
|
84
|
-
const { uploadError } = await handleProjectUpload({
|
|
93
|
+
const { uploadError, projectNotFound } = await handleProjectUpload({
|
|
85
94
|
accountId: derivedAccountId,
|
|
86
95
|
projectConfig,
|
|
87
96
|
projectDir,
|
|
88
97
|
callbackFunc: startWatching,
|
|
89
98
|
isUploadCommand: false,
|
|
90
99
|
});
|
|
100
|
+
if (projectNotFound) {
|
|
101
|
+
process.exit(EXIT_CODES.ERROR);
|
|
102
|
+
}
|
|
91
103
|
if (uploadError) {
|
|
92
104
|
if (isSpecifiedError(uploadError, {
|
|
93
105
|
subCategory: PROJECT_ERROR_TYPES.PROJECT_LOCKED,
|
|
@@ -111,6 +123,7 @@ async function handler(args) {
|
|
|
111
123
|
}
|
|
112
124
|
catch (e) {
|
|
113
125
|
logError(e, new ApiErrorContext({ accountId: derivedAccountId }));
|
|
126
|
+
process.exit(EXIT_CODES.ERROR);
|
|
114
127
|
}
|
|
115
128
|
}
|
|
116
129
|
function projectWatchBuilder(yargs) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import yargs from 'yargs';
|
|
2
2
|
import { addAccountOptions, addConfigOptions, addUseEnvironmentOptions, addTestingOptions, } from '../../../lib/commonOpts.js';
|
|
3
3
|
import sandboxCreateCommand from '../create.js';
|
|
4
|
-
import { hasFeature } from '../../../lib/hasFeature.js';
|
|
5
4
|
import * as sandboxPrompts from '../../../lib/prompts/sandboxesPrompt.js';
|
|
6
5
|
import * as accountNamePrompt from '../../../lib/prompts/accountNamePrompt.js';
|
|
7
6
|
import * as configUtils from '@hubspot/local-dev-lib/config';
|
|
@@ -12,7 +11,6 @@ import * as buildAccount from '../../../lib/buildAccount.js';
|
|
|
12
11
|
import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
13
12
|
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
14
13
|
import * as sandboxesLib from '../../../lib/sandboxes.js';
|
|
15
|
-
import * as sandboxSync from '../../../lib/sandboxSync.js';
|
|
16
14
|
import { vi } from 'vitest';
|
|
17
15
|
import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
|
|
18
16
|
vi.mock('@hubspot/local-dev-lib/config');
|
|
@@ -29,15 +27,10 @@ const getConfigAccountByIdSpy = vi.spyOn(configUtils, 'getConfigAccountById');
|
|
|
29
27
|
const promptUserSpy = vi.spyOn(promptUtils, 'promptUser');
|
|
30
28
|
const sandboxTypePromptSpy = vi.spyOn(sandboxPrompts, 'sandboxTypePrompt');
|
|
31
29
|
const processExitSpy = vi.spyOn(process, 'exit');
|
|
32
|
-
const buildSandboxSpy = vi.spyOn(buildAccount, 'buildSandbox');
|
|
33
30
|
const buildV2SandboxSpy = vi.spyOn(buildAccount, 'buildV2Sandbox');
|
|
34
31
|
const getConfigAccountEnvironmentSpy = vi.spyOn(configUtils, 'getConfigAccountEnvironment');
|
|
35
|
-
const getAvailableSyncTypesSpy = vi.spyOn(sandboxesLib, 'getAvailableSyncTypes');
|
|
36
|
-
const syncSandboxSpy = vi.spyOn(sandboxSync, 'syncSandbox');
|
|
37
32
|
const validateSandboxUsageLimitsSpy = vi.spyOn(sandboxesLib, 'validateSandboxUsageLimits');
|
|
38
33
|
const hubspotAccountNamePromptSpy = vi.spyOn(accountNamePrompt, 'hubspotAccountNamePrompt');
|
|
39
|
-
const mockedHasFeatureV2Sandboxes = hasFeature;
|
|
40
|
-
const mockedHasFeatureV2Cli = hasFeature;
|
|
41
34
|
describe('commands/sandbox/create', () => {
|
|
42
35
|
const yargsMock = yargs;
|
|
43
36
|
describe('command', () => {
|
|
@@ -103,22 +96,10 @@ describe('commands/sandbox/create', () => {
|
|
|
103
96
|
contactRecordsSyncPrompt: false,
|
|
104
97
|
});
|
|
105
98
|
validateSandboxUsageLimitsSpy.mockResolvedValue(undefined);
|
|
106
|
-
mockedHasFeatureV2Sandboxes.mockResolvedValue(false);
|
|
107
|
-
mockedHasFeatureV2Cli.mockResolvedValue(false);
|
|
108
99
|
getConfigAccountEnvironmentSpy.mockReturnValue(ENVIRONMENTS.PROD);
|
|
109
|
-
buildSandboxSpy.mockResolvedValue({
|
|
110
|
-
sandbox: mockSandbox,
|
|
111
|
-
personalAccessKey: 'mock-personal-access-key',
|
|
112
|
-
name: sandboxNameFromPrompt,
|
|
113
|
-
});
|
|
114
100
|
buildV2SandboxSpy.mockResolvedValue({
|
|
115
101
|
sandbox: { ...mockSandbox, version: 'V2' },
|
|
116
102
|
});
|
|
117
|
-
getAvailableSyncTypesSpy.mockResolvedValue([
|
|
118
|
-
{ type: 'object-schemas' },
|
|
119
|
-
{ type: 'workflows' },
|
|
120
|
-
]);
|
|
121
|
-
syncSandboxSpy.mockResolvedValue(undefined);
|
|
122
103
|
// Spy on process.exit so our tests don't close when it's called
|
|
123
104
|
// @ts-expect-error Doesn't match the actual signature because then the linter complains about unused variables
|
|
124
105
|
processExitSpy.mockImplementation(() => { });
|
|
@@ -167,35 +148,7 @@ describe('commands/sandbox/create', () => {
|
|
|
167
148
|
});
|
|
168
149
|
expect(promptUserSpy).toHaveBeenCalledTimes(1);
|
|
169
150
|
});
|
|
170
|
-
it('should build a
|
|
171
|
-
await sandboxCreateCommand.handler(args);
|
|
172
|
-
expect(buildSandboxSpy).toHaveBeenCalledTimes(1);
|
|
173
|
-
expect(buildSandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
|
|
174
|
-
accountId: 1234567890,
|
|
175
|
-
accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
|
|
176
|
-
env: 'prod',
|
|
177
|
-
}, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'prod', undefined // force
|
|
178
|
-
);
|
|
179
|
-
expect(getAvailableSyncTypesSpy).toHaveBeenCalledTimes(1);
|
|
180
|
-
expect(syncSandboxSpy).toHaveBeenCalledTimes(1);
|
|
181
|
-
});
|
|
182
|
-
it('should build a v1 sandbox if the parent account is ungated for sandboxes:v2:enabled but not ungated for sandboxes:v2:cliEnabled', async () => {
|
|
183
|
-
mockedHasFeatureV2Sandboxes.mockResolvedValue(true);
|
|
184
|
-
mockedHasFeatureV2Cli.mockResolvedValue(false);
|
|
185
|
-
await sandboxCreateCommand.handler(args);
|
|
186
|
-
expect(buildSandboxSpy).toHaveBeenCalledTimes(1);
|
|
187
|
-
expect(buildSandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
|
|
188
|
-
accountId: 1234567890,
|
|
189
|
-
accountType: HUBSPOT_ACCOUNT_TYPES.STANDARD,
|
|
190
|
-
env: 'prod',
|
|
191
|
-
}, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, 'prod', undefined // force
|
|
192
|
-
);
|
|
193
|
-
expect(getAvailableSyncTypesSpy).toHaveBeenCalledTimes(1);
|
|
194
|
-
expect(syncSandboxSpy).toHaveBeenCalledTimes(1);
|
|
195
|
-
});
|
|
196
|
-
it('should build a v2 sandbox if the parent account is ungated for both sandboxes:v2:enabled and sandboxes:v2:cliEnabled', async () => {
|
|
197
|
-
mockedHasFeatureV2Sandboxes.mockResolvedValue(true);
|
|
198
|
-
mockedHasFeatureV2Cli.mockResolvedValue(true);
|
|
151
|
+
it('should build a v2 sandbox', async () => {
|
|
199
152
|
await sandboxCreateCommand.handler(args);
|
|
200
153
|
expect(buildV2SandboxSpy).toHaveBeenCalledTimes(1);
|
|
201
154
|
expect(buildV2SandboxSpy).toHaveBeenCalledWith(sandboxNameFromPrompt, {
|