@hubspot/cli 8.11.0-beta.0 → 8.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/account/clean.js +1 -1
- package/commands/account/list.js +1 -1
- package/commands/account/remove.js +1 -1
- package/commands/account/rename.js +1 -1
- package/commands/account/use.js +1 -1
- package/commands/api.d.ts +4 -1
- package/commands/api.js +11 -14
- package/commands/project/appInstallStatus.d.ts +2 -1
- package/commands/project/appInstallStatus.js +19 -17
- package/commands/project/create.js +0 -1
- package/commands/project/deploy.d.ts +2 -1
- package/commands/project/deploy.js +31 -14
- package/commands/project/dev/index.js +18 -64
- package/commands/project/info.d.ts +2 -1
- package/commands/project/info.js +8 -6
- package/commands/project/installApp.d.ts +2 -1
- package/commands/project/installApp.js +25 -26
- package/commands/project/list.js +1 -1
- package/commands/project/release/create.d.ts +2 -1
- package/commands/project/release/create.js +68 -69
- package/commands/project/release/info.d.ts +2 -1
- package/commands/project/release/info.js +7 -6
- package/commands/project/release/list.d.ts +2 -1
- package/commands/project/release/list.js +14 -6
- package/commands/project/upload.d.ts +2 -1
- package/commands/project/upload.js +23 -20
- package/commands/project.js +2 -2
- package/commands/testAccount/create.d.ts +2 -1
- package/commands/testAccount/create.js +7 -7
- package/lang/en.d.ts +21 -28
- package/lang/en.js +30 -37
- package/lib/commonOpts.js +6 -1
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +1 -0
- package/lib/jsonOutput.d.ts +109 -0
- package/lib/jsonOutput.js +94 -0
- package/lib/projects/builds.d.ts +2 -0
- package/lib/projects/builds.js +22 -0
- package/lib/projects/localDev/DevSessionManager.d.ts +2 -2
- package/lib/projects/localDev/DevSessionManager.js +4 -30
- package/lib/projects/localDev/helpers/account.d.ts +0 -7
- package/lib/projects/localDev/helpers/account.js +1 -80
- package/lib/projects/release.d.ts +4 -0
- package/lib/projects/release.js +90 -0
- package/lib/yargs/makeWrappedYargsHandler.d.ts +5 -1
- package/lib/yargs/makeWrappedYargsHandler.js +123 -65
- package/mcp-server/Tool.js +32 -26
- package/mcp-server/utils/logger.d.ts +3 -0
- package/mcp-server/utils/logger.js +30 -0
- package/package.json +10 -8
- package/types/Yargs.d.ts +3 -1
- package/commands/project/dev/deprecatedFlow.d.ts +0 -11
- package/commands/project/dev/deprecatedFlow.js +0 -180
- package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +0 -43
- package/lib/projects/localDev/DevServerManager_DEPRECATED.js +0 -128
- package/lib/projects/localDev/LocalDevManager_DEPRECATED.d.ts +0 -64
- package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +0 -394
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { fetchProjectBuilds, getBuildStatus, } from '@hubspot/local-dev-lib/api/projects';
|
|
3
|
+
import { BUILD_STATUS } from '@hubspot/local-dev-lib/enums/build';
|
|
4
|
+
import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
|
|
5
|
+
import { meetsMinimumPlatformVersion } from '@hubspot/project-parsing-lib/projects';
|
|
6
|
+
import { PLATFORM_VERSIONS } from '@hubspot/project-parsing-lib/constants';
|
|
7
|
+
import { getLastSuccessfulBuild } from './builds.js';
|
|
8
|
+
import { logError, ApiErrorContext } from '../errorHandlers/index.js';
|
|
9
|
+
import { uiLogger } from '../ui/logger.js';
|
|
10
|
+
import { listPrompt } from '../prompts/promptUtils.js';
|
|
11
|
+
import { commands } from '../../lang/en.js';
|
|
12
|
+
import { createRelease } from '../../api/releases.js';
|
|
13
|
+
function buildChoiceName(build) {
|
|
14
|
+
const base = build.uploadMessage
|
|
15
|
+
? `${build.buildId} — ${build.uploadMessage}`
|
|
16
|
+
: `${build.buildId} — ${commands.project.release.create.noUploadMessage}`;
|
|
17
|
+
return build.status !== BUILD_STATUS.SUCCESS
|
|
18
|
+
? `[${chalk.yellow('DISABLED')}] ${base}`
|
|
19
|
+
: base;
|
|
20
|
+
}
|
|
21
|
+
export async function resolveBuildId(accountId, projectName, buildOption, force) {
|
|
22
|
+
if (buildOption) {
|
|
23
|
+
return buildOption;
|
|
24
|
+
}
|
|
25
|
+
if (force) {
|
|
26
|
+
const lastSuccessful = await getLastSuccessfulBuild(accountId, projectName);
|
|
27
|
+
return lastSuccessful ? lastSuccessful.buildId : null;
|
|
28
|
+
}
|
|
29
|
+
let results;
|
|
30
|
+
try {
|
|
31
|
+
({
|
|
32
|
+
data: { results },
|
|
33
|
+
} = await fetchProjectBuilds(accountId, projectName));
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
if (isSpecifiedError(e, { statusCode: 404 }) ||
|
|
37
|
+
isSpecifiedError(e, { statusCode: 400 })) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
throw e;
|
|
41
|
+
}
|
|
42
|
+
if (results.length === 0) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const lastSuccessful = await getLastSuccessfulBuild(accountId, projectName);
|
|
46
|
+
if (!lastSuccessful) {
|
|
47
|
+
uiLogger.error(commands.project.release.create.errors.noSuccessfulBuilds(projectName));
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return listPrompt(commands.project.release.create.buildIdPrompt, {
|
|
51
|
+
choices: results.map(b => ({
|
|
52
|
+
name: buildChoiceName(b),
|
|
53
|
+
value: b.buildId,
|
|
54
|
+
disabled: b.status !== BUILD_STATUS.SUCCESS
|
|
55
|
+
? `– ${commands.project.release.create.buildStatus[b.status] ?? b.status}`
|
|
56
|
+
: undefined,
|
|
57
|
+
})),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
export async function validateBuildForRelease(accountId, projectName, buildId) {
|
|
61
|
+
try {
|
|
62
|
+
const { data: build } = await getBuildStatus(accountId, projectName, buildId);
|
|
63
|
+
return meetsMinimumPlatformVersion(build.platformVersion, PLATFORM_VERSIONS.v2026_09_BETA);
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
if (isSpecifiedError(e, { statusCode: 404 })) {
|
|
67
|
+
uiLogger.error(commands.project.release.create.errors.buildNotFound(buildId, projectName));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
logError(e, new ApiErrorContext({
|
|
71
|
+
accountId,
|
|
72
|
+
request: 'project release create',
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
throw e;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export async function executeRelease(accountId, projectName, buildId) {
|
|
79
|
+
try {
|
|
80
|
+
const { data: release } = await createRelease(accountId, projectName, buildId);
|
|
81
|
+
return release;
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
logError(e, new ApiErrorContext({
|
|
85
|
+
accountId,
|
|
86
|
+
request: 'project release create',
|
|
87
|
+
}));
|
|
88
|
+
throw e;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { ArgumentsCamelCase } from 'yargs';
|
|
2
|
+
import { z } from 'zod';
|
|
2
3
|
import { CommonArgs } from '../../types/Yargs.js';
|
|
3
|
-
export
|
|
4
|
+
export type WrappedHandlerOptions = {
|
|
5
|
+
jsonOutputSchema?: z.ZodType;
|
|
6
|
+
};
|
|
7
|
+
export declare function makeWrappedYargsHandler<T extends CommonArgs>(trackingName: string, handler: (args: ArgumentsCamelCase<T>) => Promise<void>, options?: WrappedHandlerOptions): (args: ArgumentsCamelCase<T>) => Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os from 'os';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { z } from 'zod';
|
|
3
4
|
import { getConfig } from '@hubspot/local-dev-lib/config';
|
|
4
5
|
import { getStateValue, setStateValue, } from '@hubspot/local-dev-lib/config/state';
|
|
5
6
|
import { STATE_FLAGS } from '@hubspot/local-dev-lib/constants/config';
|
|
@@ -11,7 +12,8 @@ import { lib } from '../../lang/en.js';
|
|
|
11
12
|
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
12
13
|
import { isPromptExitError } from '../errors/PromptExitError.js';
|
|
13
14
|
import { debugError } from '../errorHandlers/index.js';
|
|
14
|
-
|
|
15
|
+
import { MAX_LOG_FILES } from '../constants.js';
|
|
16
|
+
const HANDLER_LOG_DIR = path.join(os.homedir(), '.hscli', 'logs', 'cli');
|
|
15
17
|
function logUsageTrackingMessage(isJsonOutput) {
|
|
16
18
|
if (isJsonOutput) {
|
|
17
19
|
return;
|
|
@@ -32,90 +34,146 @@ function logUsageTrackingMessage(isJsonOutput) {
|
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
37
|
+
function createUsageTracker(trackingName, derivedAccountId) {
|
|
38
|
+
const startTime = Date.now();
|
|
39
|
+
const meta = {};
|
|
40
|
+
let fired = false;
|
|
41
|
+
const addMetadata = (newMeta) => {
|
|
42
|
+
Object.assign(meta, newMeta);
|
|
43
|
+
};
|
|
44
|
+
const track = async (successful) => {
|
|
45
|
+
if (fired) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
fired = true;
|
|
49
|
+
try {
|
|
50
|
+
const { accountId: overrideAccountId, ...trackingMeta } = meta;
|
|
51
|
+
trackingMeta.successful = successful;
|
|
52
|
+
trackingMeta.executionTime = Date.now() - startTime;
|
|
53
|
+
await _trackCommandUsage(trackingName, trackingMeta, overrideAccountId ?? derivedAccountId);
|
|
54
|
+
}
|
|
55
|
+
catch (_e) { }
|
|
56
|
+
};
|
|
57
|
+
const onForcedExit = () => {
|
|
58
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
59
|
+
};
|
|
60
|
+
const onSigint = async () => {
|
|
61
|
+
process.removeListener('SIGINT', onSigint);
|
|
62
|
+
process.on('SIGINT', onForcedExit);
|
|
63
|
+
try {
|
|
64
|
+
await track(false);
|
|
65
|
+
}
|
|
66
|
+
catch (_e) { }
|
|
67
|
+
process.removeListener('SIGINT', onForcedExit);
|
|
68
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
69
|
+
};
|
|
70
|
+
process.on('SIGINT', onSigint);
|
|
71
|
+
const trackAndCleanup = async (successful) => {
|
|
72
|
+
await track(successful);
|
|
73
|
+
process.removeListener('SIGINT', onSigint);
|
|
74
|
+
process.removeListener('SIGINT', onForcedExit);
|
|
75
|
+
};
|
|
76
|
+
return { addMetadata, trackAndCleanup };
|
|
77
|
+
}
|
|
78
|
+
function createJsonOutputManager(isJsonOutput, schema) {
|
|
79
|
+
const data = {};
|
|
80
|
+
let emitted = false;
|
|
81
|
+
const add = (newData) => {
|
|
82
|
+
Object.assign(data, newData);
|
|
83
|
+
};
|
|
84
|
+
const emit = () => {
|
|
85
|
+
if (!emitted && isJsonOutput && Object.keys(data).length > 0) {
|
|
86
|
+
emitted = true;
|
|
87
|
+
if (schema) {
|
|
88
|
+
const result = schema.safeParse(data);
|
|
89
|
+
if (!result.success) {
|
|
90
|
+
uiLogger.json(data);
|
|
91
|
+
uiLogger.warn(lib.jsonSchema.validationFailed);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
55
94
|
}
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
95
|
+
uiLogger.json(data);
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
};
|
|
99
|
+
return { add, emit };
|
|
100
|
+
}
|
|
101
|
+
function createLogFileWriter(trackingName, isJsonOutput) {
|
|
102
|
+
const writeLogFile = () => {
|
|
103
|
+
if (isJsonOutput) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
return ldlLogger.writeBufferedLogsToFile({
|
|
107
|
+
dir: HANDLER_LOG_DIR,
|
|
108
|
+
filenamePrefix: trackingName,
|
|
109
|
+
maxFiles: MAX_LOG_FILES,
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
const writeFailureLogFile = () => {
|
|
113
|
+
const savedPath = writeLogFile();
|
|
114
|
+
if (savedPath) {
|
|
115
|
+
uiLogger.log('');
|
|
116
|
+
uiLogger.error(lib.handlerLogFile.saved(savedPath));
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
return { writeLogFile, writeFailureLogFile };
|
|
120
|
+
}
|
|
121
|
+
export function makeWrappedYargsHandler(trackingName, handler, options) {
|
|
122
|
+
return async (args) => {
|
|
123
|
+
const wrappedHandlerArgs = args;
|
|
124
|
+
const isJsonOutput = Boolean(wrappedHandlerArgs.json || wrappedHandlerArgs.formatOutputAsJson);
|
|
125
|
+
const schema = options?.jsonOutputSchema;
|
|
126
|
+
const tracker = createUsageTracker(trackingName, args.derivedAccountId);
|
|
127
|
+
if (wrappedHandlerArgs.jsonSchema) {
|
|
128
|
+
if (schema) {
|
|
129
|
+
uiLogger.json(z.toJSONSchema(schema));
|
|
66
130
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
70
|
-
};
|
|
71
|
-
process.on('SIGINT', onSigint);
|
|
72
|
-
const trackCommandUsageAndRemoveListeners = async (successful) => {
|
|
73
|
-
await trackCommandUsage(successful);
|
|
74
|
-
process.removeListener('SIGINT', onSigint);
|
|
75
|
-
process.removeListener('SIGINT', onForcedExit);
|
|
76
|
-
};
|
|
77
|
-
const jsonArgs = args;
|
|
78
|
-
const isJsonOutput = Boolean(jsonArgs.json || jsonArgs.formatOutputAsJson);
|
|
79
|
-
const writeFailureLogFile = () => {
|
|
80
|
-
// Skip in JSON output modes so the side effect + stderr message don't
|
|
81
|
-
// interfere with structured output consumers.
|
|
82
|
-
if (isJsonOutput) {
|
|
83
|
-
return;
|
|
131
|
+
else {
|
|
132
|
+
uiLogger.json({ error: lib.jsonSchema.noSchemaForCommand });
|
|
84
133
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
134
|
+
await tracker.trackAndCleanup(true);
|
|
135
|
+
return process.exit(EXIT_CODES.SUCCESS);
|
|
136
|
+
}
|
|
137
|
+
const json = createJsonOutputManager(isJsonOutput, schema);
|
|
138
|
+
const logs = createLogFileWriter(trackingName, isJsonOutput);
|
|
139
|
+
wrappedHandlerArgs.addUsageMetadata = tracker.addMetadata;
|
|
140
|
+
wrappedHandlerArgs.addJsonOutput = json.add;
|
|
141
|
+
wrappedHandlerArgs.exit = async (code) => {
|
|
142
|
+
const jsonValid = json.emit();
|
|
143
|
+
const exitCode = !jsonValid && code === EXIT_CODES.SUCCESS ? EXIT_CODES.WARNING : code;
|
|
144
|
+
await tracker.trackAndCleanup(exitCode !== EXIT_CODES.ERROR);
|
|
145
|
+
if (exitCode === EXIT_CODES.ERROR) {
|
|
146
|
+
logs.writeFailureLogFile();
|
|
92
147
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
await trackCommandUsageAndRemoveListeners(code !== EXIT_CODES.ERROR);
|
|
96
|
-
if (code === EXIT_CODES.ERROR) {
|
|
97
|
-
writeFailureLogFile();
|
|
148
|
+
else {
|
|
149
|
+
logs.writeLogFile();
|
|
98
150
|
}
|
|
99
|
-
return process.exit(
|
|
151
|
+
return process.exit(exitCode);
|
|
100
152
|
};
|
|
101
153
|
logUsageTrackingMessage(isJsonOutput);
|
|
102
154
|
try {
|
|
103
|
-
await handler(
|
|
155
|
+
await handler(wrappedHandlerArgs);
|
|
104
156
|
}
|
|
105
157
|
catch (e) {
|
|
106
158
|
const isSuccessfulPromptExit = isPromptExitError(e)
|
|
107
159
|
? e.exitCode !== EXIT_CODES.ERROR
|
|
108
160
|
: false;
|
|
109
|
-
await
|
|
161
|
+
await tracker.trackAndCleanup(isSuccessfulPromptExit);
|
|
110
162
|
if (isPromptExitError(e)) {
|
|
163
|
+
logs.writeLogFile();
|
|
111
164
|
return process.exit(e.exitCode);
|
|
112
165
|
}
|
|
113
166
|
else {
|
|
114
167
|
debugError(e);
|
|
115
|
-
writeFailureLogFile();
|
|
168
|
+
logs.writeFailureLogFile();
|
|
116
169
|
return process.exit(EXIT_CODES.ERROR);
|
|
117
170
|
}
|
|
118
171
|
}
|
|
119
|
-
|
|
172
|
+
const jsonValid = json.emit();
|
|
173
|
+
await tracker.trackAndCleanup(true);
|
|
174
|
+
logs.writeLogFile();
|
|
175
|
+
if (!jsonValid) {
|
|
176
|
+
return process.exit(EXIT_CODES.WARNING);
|
|
177
|
+
}
|
|
120
178
|
};
|
|
121
179
|
}
|
package/mcp-server/Tool.js
CHANGED
|
@@ -22,6 +22,7 @@ export class Tool {
|
|
|
22
22
|
return runCommandInDir(directory, command, async (chunk) => {
|
|
23
23
|
try {
|
|
24
24
|
const message = `${chunk.trimEnd()}`;
|
|
25
|
+
this.logger.debug(this.toolName, message);
|
|
25
26
|
const token = extra?._meta?.progressToken;
|
|
26
27
|
if (token !== undefined && extra?.sendNotification) {
|
|
27
28
|
progressCount++;
|
|
@@ -43,31 +44,36 @@ export class Tool {
|
|
|
43
44
|
getTrackingMeta(_input) {
|
|
44
45
|
return undefined;
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
47
|
+
wrappedHandler(input, extra) {
|
|
48
|
+
return this.logger.runWithBuffer(async () => {
|
|
49
|
+
const startTime = Date.now();
|
|
50
|
+
try {
|
|
51
|
+
// `input` is logged unredacted. Tool input schemas MUST NOT include
|
|
52
|
+
// credentials or other sensitive values, since MCP clients (Claude
|
|
53
|
+
// Desktop, Inspector, etc.) will display these logs.
|
|
54
|
+
this.logger.debug(this.toolName, {
|
|
55
|
+
message: 'Tool invoked',
|
|
56
|
+
args: input,
|
|
57
|
+
});
|
|
58
|
+
await trackToolUsage(this.toolName, this.getTrackingMeta(input));
|
|
59
|
+
const result = await this.handler(input, extra);
|
|
60
|
+
this.logger.debug(this.toolName, {
|
|
61
|
+
message: 'Tool completed',
|
|
62
|
+
durationMs: Date.now() - startTime,
|
|
63
|
+
});
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
this.logger.error(this.toolName, {
|
|
68
|
+
message: 'Tool failed',
|
|
69
|
+
error: error instanceof Error ? error.message : String(error),
|
|
70
|
+
durationMs: Date.now() - startTime,
|
|
71
|
+
});
|
|
72
|
+
return formatTextContents(getErrorMessage(error));
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
this.logger.flushLogsToFile(this.toolName);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
72
78
|
}
|
|
73
79
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
export declare class McpLogger {
|
|
3
3
|
private mcpServer;
|
|
4
|
+
private invocationStorage;
|
|
4
5
|
constructor(mcpServer: McpServer);
|
|
5
6
|
private log;
|
|
6
7
|
debug(logger: string, data: unknown): void;
|
|
7
8
|
info(logger: string, data: unknown): void;
|
|
8
9
|
warn(logger: string, data: unknown): void;
|
|
9
10
|
error(logger: string, data: unknown): void;
|
|
11
|
+
runWithBuffer<T>(fn: () => Promise<T>): Promise<T>;
|
|
12
|
+
flushLogsToFile(filenamePrefix: string): string | null;
|
|
10
13
|
}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
4
|
+
import { LogBuffer } from '@hubspot/local-dev-lib/LogBuffer';
|
|
5
|
+
import { MAX_LOG_FILES } from '../../lib/constants.js';
|
|
6
|
+
const MCP_LOG_DIR = path.join(os.homedir(), '.hscli', 'logs', 'tools');
|
|
1
7
|
export class McpLogger {
|
|
2
8
|
mcpServer;
|
|
9
|
+
invocationStorage = new AsyncLocalStorage();
|
|
3
10
|
constructor(mcpServer) {
|
|
4
11
|
this.mcpServer = mcpServer;
|
|
5
12
|
}
|
|
6
13
|
log(level, logger, data) {
|
|
7
14
|
try {
|
|
15
|
+
const buffer = this.invocationStorage.getStore();
|
|
16
|
+
if (buffer) {
|
|
17
|
+
const serialized = typeof data === 'string' ? data : JSON.stringify(data);
|
|
18
|
+
buffer.record(level, [logger, serialized]);
|
|
19
|
+
}
|
|
8
20
|
this.mcpServer.sendLoggingMessage({ level, logger, data });
|
|
9
21
|
}
|
|
10
22
|
catch (error) {
|
|
@@ -26,4 +38,22 @@ export class McpLogger {
|
|
|
26
38
|
error(logger, data) {
|
|
27
39
|
this.log('error', logger, data);
|
|
28
40
|
}
|
|
41
|
+
// Runs fn in a fresh per-invocation log buffer context. All log calls made
|
|
42
|
+
// within fn (including nested async callbacks like progress chunks) record
|
|
43
|
+
// into this buffer rather than a shared one, so concurrent tool calls never
|
|
44
|
+
// intermix their logs.
|
|
45
|
+
runWithBuffer(fn) {
|
|
46
|
+
return this.invocationStorage.run(new LogBuffer(), fn);
|
|
47
|
+
}
|
|
48
|
+
flushLogsToFile(filenamePrefix) {
|
|
49
|
+
const buffer = this.invocationStorage.getStore();
|
|
50
|
+
if (!buffer) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return buffer.writeToFile({
|
|
54
|
+
dir: MCP_LOG_DIR,
|
|
55
|
+
filenamePrefix,
|
|
56
|
+
maxFiles: MAX_LOG_FILES,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
29
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cli",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.12.0",
|
|
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",
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"!**/__tests__/**"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@hubspot/local-dev-lib": "5.10.
|
|
14
|
-
"@hubspot/project-parsing-lib": "0.
|
|
13
|
+
"@hubspot/local-dev-lib": "5.10.1",
|
|
14
|
+
"@hubspot/project-parsing-lib": "0.21.0",
|
|
15
15
|
"@hubspot/serverless-dev-runtime": "7.0.7",
|
|
16
|
-
"@hubspot/ui-extensions-dev-server": "2.0.
|
|
16
|
+
"@hubspot/ui-extensions-dev-server": "2.0.13",
|
|
17
17
|
"@inquirer/prompts": "7.1.0",
|
|
18
18
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
19
19
|
"archiver": "7.0.1",
|
|
@@ -40,10 +40,11 @@
|
|
|
40
40
|
"update-notifier": "7.3.1",
|
|
41
41
|
"ws": "8.20.0",
|
|
42
42
|
"yargs": "17.7.2",
|
|
43
|
-
"yargs-parser": "21.1.1"
|
|
43
|
+
"yargs-parser": "21.1.1",
|
|
44
|
+
"zod": "^4.4.3"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@hubspot/npm-scripts": "0.
|
|
47
|
+
"@hubspot/npm-scripts": "0.3.1",
|
|
47
48
|
"@types/archiver": "^6.0.3",
|
|
48
49
|
"@types/cli-progress": "^3.11.6",
|
|
49
50
|
"@types/express": "^5.0.0",
|
|
@@ -60,7 +61,7 @@
|
|
|
60
61
|
"@typescript-eslint/eslint-plugin": "^8.30.1",
|
|
61
62
|
"@typescript-eslint/parser": "^8.11.0",
|
|
62
63
|
"@vitest/coverage-v8": "^2.1.9",
|
|
63
|
-
"axios": "1.
|
|
64
|
+
"axios": "1.18.1",
|
|
64
65
|
"eslint": "^8.56.0",
|
|
65
66
|
"eslint-plugin-import": "^2.31.0",
|
|
66
67
|
"husky": "^4.3.8",
|
|
@@ -123,6 +124,7 @@
|
|
|
123
124
|
"registry": "https://registry.npmjs.org/"
|
|
124
125
|
},
|
|
125
126
|
"resolutions": {
|
|
126
|
-
"eslint-visitor-keys": "4.2.0"
|
|
127
|
+
"eslint-visitor-keys": "4.2.0",
|
|
128
|
+
"@eslint-community/eslint-utils": "4.9.0"
|
|
127
129
|
}
|
|
128
130
|
}
|
package/types/Yargs.d.ts
CHANGED
|
@@ -38,9 +38,11 @@ export type OverwriteArgs = Options & {
|
|
|
38
38
|
export type StringArgType = Options & {
|
|
39
39
|
type: 'string';
|
|
40
40
|
};
|
|
41
|
-
export type JSONOutputArgs = Options & {
|
|
41
|
+
export type JSONOutputArgs<J extends Record<string, unknown> = Record<string, unknown>> = Options & {
|
|
42
42
|
json?: boolean;
|
|
43
|
+
jsonSchema?: boolean;
|
|
43
44
|
formatOutputAsJson?: boolean;
|
|
45
|
+
addJsonOutput: (data: Partial<J>) => void;
|
|
44
46
|
};
|
|
45
47
|
export type ProjectDevArgs = CommonArgs & ConfigArgs & EnvironmentArgs & {
|
|
46
48
|
profile?: string;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { ArgumentsCamelCase } from 'yargs';
|
|
2
|
-
import { ProjectConfig } from '../../../types/Projects.js';
|
|
3
|
-
import { ProjectDevArgs } from '../../../types/Yargs.js';
|
|
4
|
-
type DeprecatedProjectDevFlowArgs = {
|
|
5
|
-
args: ArgumentsCamelCase<ProjectDevArgs>;
|
|
6
|
-
accountId: number;
|
|
7
|
-
projectConfig: ProjectConfig;
|
|
8
|
-
projectDir: string;
|
|
9
|
-
};
|
|
10
|
-
export declare function deprecatedProjectDevFlow({ args, accountId, projectConfig, projectDir, }: DeprecatedProjectDevFlowArgs): Promise<void>;
|
|
11
|
-
export {};
|