@xiaou66/vite-plugin-vue-mcp-next 1.0.3 → 1.1.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/README.md +54 -1
- package/dist/index.cjs +804 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +277 -5
- package/dist/index.d.ts +277 -5
- package/dist/index.js +755 -57
- package/dist/index.js.map +1 -1
- package/dist/runtime/client.cjs +639 -1
- package/dist/runtime/client.cjs.map +1 -1
- package/dist/runtime/client.js +639 -1
- package/dist/runtime/client.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -55,6 +55,11 @@ var DEFAULT_MASK_HEADERS = [
|
|
|
55
55
|
var DEFAULT_MCP_PATH = "/__mcp";
|
|
56
56
|
var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
|
|
57
57
|
var DEFAULT_SCREENSHOT_SAVE_DIR = ".vite-mcp/screenshot";
|
|
58
|
+
var DEFAULT_PERFORMANCE_SAVE_DIR = ".vite-mcp/performance";
|
|
59
|
+
var DEFAULT_PERFORMANCE_MAX_REPORTS = 100;
|
|
60
|
+
var DEFAULT_PERFORMANCE_MAX_DURATION_MS = 3e4;
|
|
61
|
+
var DEFAULT_PERFORMANCE_SAMPLE_INTERVAL_MS = 250;
|
|
62
|
+
var DEFAULT_PERFORMANCE_LONG_TASK_THRESHOLD_MS = 50;
|
|
58
63
|
var MCP_TOOL_NAMES = {
|
|
59
64
|
listPages: "list_pages",
|
|
60
65
|
reloadPage: "reload_page",
|
|
@@ -67,6 +72,11 @@ var MCP_TOOL_NAMES = {
|
|
|
67
72
|
getNetworkRequests: "get_network_requests",
|
|
68
73
|
getNetworkRequestDetail: "get_network_request_detail",
|
|
69
74
|
clearNetworkRequests: "clear_network_requests",
|
|
75
|
+
recordPerformance: "record_performance",
|
|
76
|
+
startPerformanceRecording: "start_performance_recording",
|
|
77
|
+
stopPerformanceRecording: "stop_performance_recording",
|
|
78
|
+
getPerformanceReport: "get_performance_report",
|
|
79
|
+
takeHeapSnapshot: "take_heap_snapshot",
|
|
70
80
|
getComponentTree: "get_component_tree",
|
|
71
81
|
getComponentState: "get_component_state",
|
|
72
82
|
editComponentState: "edit_component_state",
|
|
@@ -135,6 +145,19 @@ var DEFAULT_OPTIONS = {
|
|
|
135
145
|
options: {},
|
|
136
146
|
plugins: []
|
|
137
147
|
}
|
|
148
|
+
},
|
|
149
|
+
performance: {
|
|
150
|
+
mode: "auto",
|
|
151
|
+
maxDurationMs: DEFAULT_PERFORMANCE_MAX_DURATION_MS,
|
|
152
|
+
sampleIntervalMs: DEFAULT_PERFORMANCE_SAMPLE_INTERVAL_MS,
|
|
153
|
+
longTaskThresholdMs: DEFAULT_PERFORMANCE_LONG_TASK_THRESHOLD_MS,
|
|
154
|
+
saveDir: DEFAULT_PERFORMANCE_SAVE_DIR,
|
|
155
|
+
memory: {
|
|
156
|
+
enabled: true
|
|
157
|
+
},
|
|
158
|
+
stacks: {
|
|
159
|
+
enabled: true
|
|
160
|
+
}
|
|
138
161
|
}
|
|
139
162
|
};
|
|
140
163
|
function mergeMcpClientOptions(cursorConfig, mcpClients) {
|
|
@@ -204,6 +227,18 @@ function mergeOptions(options = {}) {
|
|
|
204
227
|
...DEFAULT_OPTIONS.screenshot.snapdom.plugins
|
|
205
228
|
]
|
|
206
229
|
}
|
|
230
|
+
},
|
|
231
|
+
performance: {
|
|
232
|
+
...DEFAULT_OPTIONS.performance,
|
|
233
|
+
...options.performance,
|
|
234
|
+
memory: {
|
|
235
|
+
...DEFAULT_OPTIONS.performance.memory,
|
|
236
|
+
...options.performance?.memory
|
|
237
|
+
},
|
|
238
|
+
stacks: {
|
|
239
|
+
...DEFAULT_OPTIONS.performance.stacks,
|
|
240
|
+
...options.performance?.stacks
|
|
241
|
+
}
|
|
207
242
|
}
|
|
208
243
|
};
|
|
209
244
|
}
|
|
@@ -231,24 +266,64 @@ function createRingBuffer(capacity) {
|
|
|
231
266
|
}
|
|
232
267
|
|
|
233
268
|
// src/context.ts
|
|
269
|
+
var MINUTE_MS = 60 * 1e3;
|
|
270
|
+
var DISCONNECTED_RUNTIME_TARGET_RETENTION_MS = 5 * MINUTE_MS;
|
|
271
|
+
function shouldPruneDisconnectedRuntimeTarget(target, now) {
|
|
272
|
+
return target.source === "runtime" && !target.connected && typeof target.disconnectedAt === "number" && now - target.disconnectedAt > DISCONNECTED_RUNTIME_TARGET_RETENTION_MS;
|
|
273
|
+
}
|
|
274
|
+
function pruneDisconnectedRuntimeTargets(targets, now) {
|
|
275
|
+
for (const [pageId, target] of targets) {
|
|
276
|
+
if (shouldPruneDisconnectedRuntimeTarget(target, now)) {
|
|
277
|
+
targets.delete(pageId);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function markTargetDisconnected(target, now) {
|
|
282
|
+
return {
|
|
283
|
+
...target,
|
|
284
|
+
connected: false,
|
|
285
|
+
disconnectedAt: target.disconnectedAt ?? now
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function shouldListPageTarget(target, options) {
|
|
289
|
+
return options.includeDisconnected === true || target.source !== "runtime" || target.connected;
|
|
290
|
+
}
|
|
291
|
+
function disconnectPreviousRuntimeClientTarget(targets, target, now) {
|
|
292
|
+
if (target.source !== "runtime" || !target.runtimeClientId) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
for (const [pageId, currentTarget] of targets) {
|
|
296
|
+
if (pageId === target.pageId || currentTarget.source !== "runtime" || currentTarget.runtimeClientId !== target.runtimeClientId || !currentTarget.connected) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
targets.set(pageId, markTargetDisconnected(currentTarget, now));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
234
302
|
function createPageTargetRegistry() {
|
|
235
303
|
const targets = /* @__PURE__ */ new Map();
|
|
236
304
|
return {
|
|
237
|
-
upsert(target) {
|
|
305
|
+
upsert(target, now = Date.now()) {
|
|
306
|
+
pruneDisconnectedRuntimeTargets(targets, now);
|
|
307
|
+
disconnectPreviousRuntimeClientTarget(targets, target, now);
|
|
238
308
|
targets.set(target.pageId, target);
|
|
239
309
|
},
|
|
240
310
|
get(pageId) {
|
|
241
311
|
return targets.get(pageId);
|
|
242
312
|
},
|
|
243
|
-
list() {
|
|
244
|
-
|
|
313
|
+
list(options = {}) {
|
|
314
|
+
const now = options.now ?? Date.now();
|
|
315
|
+
pruneDisconnectedRuntimeTargets(targets, now);
|
|
316
|
+
return [...targets.values()].filter(
|
|
317
|
+
(target) => shouldListPageTarget(target, options)
|
|
318
|
+
);
|
|
245
319
|
},
|
|
246
|
-
disconnect(pageId) {
|
|
320
|
+
disconnect(pageId, now = Date.now()) {
|
|
321
|
+
pruneDisconnectedRuntimeTargets(targets, now);
|
|
247
322
|
const target = targets.get(pageId);
|
|
248
323
|
if (!target) {
|
|
249
324
|
return;
|
|
250
325
|
}
|
|
251
|
-
targets.set(pageId,
|
|
326
|
+
targets.set(pageId, markTargetDisconnected(target, now));
|
|
252
327
|
}
|
|
253
328
|
};
|
|
254
329
|
}
|
|
@@ -259,7 +334,11 @@ function createVueMcpNextContext(options) {
|
|
|
259
334
|
rpcServer: void 0,
|
|
260
335
|
pages: createPageTargetRegistry(),
|
|
261
336
|
consoleRecords: createRingBuffer(options.console.maxRecords),
|
|
262
|
-
networkRecords: createRingBuffer(options.network.maxRecords)
|
|
337
|
+
networkRecords: createRingBuffer(options.network.maxRecords),
|
|
338
|
+
performanceReports: createRingBuffer(
|
|
339
|
+
DEFAULT_PERFORMANCE_MAX_REPORTS
|
|
340
|
+
),
|
|
341
|
+
performanceSessions: /* @__PURE__ */ new Map()
|
|
263
342
|
};
|
|
264
343
|
}
|
|
265
344
|
|
|
@@ -377,13 +456,13 @@ async function requestRuntimeData(ctx, trigger) {
|
|
|
377
456
|
return { ok: false, error: "runtime bridge is not connected" };
|
|
378
457
|
}
|
|
379
458
|
const event = (0, import_nanoid.nanoid)();
|
|
380
|
-
return new Promise((
|
|
459
|
+
return new Promise((resolve2) => {
|
|
381
460
|
const timeout = setTimeout(() => {
|
|
382
|
-
|
|
461
|
+
resolve2({ ok: false, error: "runtime bridge response timed out" });
|
|
383
462
|
}, 5e3);
|
|
384
463
|
ctx.hooks.hookOnce(event, (data) => {
|
|
385
464
|
clearTimeout(timeout);
|
|
386
|
-
|
|
465
|
+
resolve2(data);
|
|
387
466
|
});
|
|
388
467
|
trigger(event);
|
|
389
468
|
});
|
|
@@ -662,12 +741,598 @@ function registerNetworkTools(server, ctx) {
|
|
|
662
741
|
);
|
|
663
742
|
}
|
|
664
743
|
|
|
665
|
-
// src/mcp/tools/
|
|
744
|
+
// src/mcp/tools/performance.ts
|
|
666
745
|
var import_zod5 = require("zod");
|
|
667
746
|
|
|
747
|
+
// src/performance/summary.ts
|
|
748
|
+
function buildPerformanceSummary(input) {
|
|
749
|
+
const memory = buildMemorySummary(input.memorySamples);
|
|
750
|
+
const blockedTimeMs = input.longTasks.reduce((total, task) => {
|
|
751
|
+
return total + Math.max(0, task.durationMs - DEFAULT_PERFORMANCE_LONG_TASK_THRESHOLD_MS);
|
|
752
|
+
}, 0);
|
|
753
|
+
const longTaskCount = input.longTasks.length;
|
|
754
|
+
const durations = input.longTasks.map((task) => task.durationMs);
|
|
755
|
+
const maxTaskDurationMs = durations.length ? Math.max(...durations) : 0;
|
|
756
|
+
const averageTaskDurationMs = durations.length ? Math.round(
|
|
757
|
+
durations.reduce((total, duration) => total + duration, 0) / durations.length
|
|
758
|
+
) : void 0;
|
|
759
|
+
const suspectedJank = blockedTimeMs > 0 || memory.trend === "growing" || longTaskCount > 0;
|
|
760
|
+
const severity = resolveSeverity({
|
|
761
|
+
blockedTimeMs,
|
|
762
|
+
longTaskCount,
|
|
763
|
+
memoryTrend: memory.trend
|
|
764
|
+
});
|
|
765
|
+
return {
|
|
766
|
+
blockedTimeMs,
|
|
767
|
+
longTaskCount,
|
|
768
|
+
maxTaskDurationMs,
|
|
769
|
+
averageTaskDurationMs,
|
|
770
|
+
suspectedJank,
|
|
771
|
+
severity
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
function buildMemorySummary(samples) {
|
|
775
|
+
const first = samples[0]?.usedJSHeapSize;
|
|
776
|
+
const last = samples.at(-1)?.usedJSHeapSize;
|
|
777
|
+
const peak = samples.reduce((currentPeak, sample) => {
|
|
778
|
+
if (typeof sample.usedJSHeapSize !== "number") {
|
|
779
|
+
return currentPeak;
|
|
780
|
+
}
|
|
781
|
+
return Math.max(currentPeak, sample.usedJSHeapSize);
|
|
782
|
+
}, 0);
|
|
783
|
+
const trend = resolveMemoryTrend(first, last);
|
|
784
|
+
return {
|
|
785
|
+
samples,
|
|
786
|
+
initialUsedJSHeapSize: first,
|
|
787
|
+
finalUsedJSHeapSize: last,
|
|
788
|
+
peakUsedJSHeapSize: peak || void 0,
|
|
789
|
+
deltaUsedJSHeapSize: typeof first === "number" && typeof last === "number" ? last - first : void 0,
|
|
790
|
+
trend
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
function buildStackSummary(frames, options = {}) {
|
|
794
|
+
return {
|
|
795
|
+
topFrames: [...frames].sort(sortByHotness).slice(0, 10),
|
|
796
|
+
rawProfilePath: options.rawProfilePath,
|
|
797
|
+
limitation: options.limitation
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
function resolveSeverity(input) {
|
|
801
|
+
if (input.blockedTimeMs >= 1e3 || input.longTaskCount >= 10) {
|
|
802
|
+
return "critical";
|
|
803
|
+
}
|
|
804
|
+
if (input.blockedTimeMs > 0 || input.memoryTrend === "growing") {
|
|
805
|
+
return "warning";
|
|
806
|
+
}
|
|
807
|
+
return "ok";
|
|
808
|
+
}
|
|
809
|
+
function resolveMemoryTrend(first, last) {
|
|
810
|
+
if (typeof first !== "number" || typeof last !== "number") {
|
|
811
|
+
return "unknown";
|
|
812
|
+
}
|
|
813
|
+
if (last > first) {
|
|
814
|
+
return "growing";
|
|
815
|
+
}
|
|
816
|
+
return "stable";
|
|
817
|
+
}
|
|
818
|
+
function sortByHotness(left, right) {
|
|
819
|
+
return compareNumber(right.totalTimeMs, left.totalTimeMs) || compareNumber(right.selfTimeMs, left.selfTimeMs) || compareNumber(right.hitCount, left.hitCount);
|
|
820
|
+
}
|
|
821
|
+
function compareNumber(left, right) {
|
|
822
|
+
return (left ?? -1) - (right ?? -1);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// src/performance/output.ts
|
|
826
|
+
var import_promises = require("fs/promises");
|
|
827
|
+
var import_node_path = require("path");
|
|
828
|
+
var import_nanoid2 = require("nanoid");
|
|
829
|
+
async function writePerformanceArtifact(options) {
|
|
830
|
+
const root = (0, import_node_path.resolve)(options.root ?? process.cwd());
|
|
831
|
+
const dir = resolveOutputDirectory(root, options.saveDir);
|
|
832
|
+
const path8 = (0, import_node_path.resolve)(dir, createSafeFileName(options.fileName));
|
|
833
|
+
const data = typeof options.data === "string" ? Buffer.from(options.data) : options.data;
|
|
834
|
+
await (0, import_promises.mkdir)(dir, { recursive: true });
|
|
835
|
+
await (0, import_promises.writeFile)(path8, data);
|
|
836
|
+
return {
|
|
837
|
+
kind: options.kind,
|
|
838
|
+
path: path8,
|
|
839
|
+
relativePath: (0, import_node_path.relative)(root, path8),
|
|
840
|
+
byteLength: data.byteLength,
|
|
841
|
+
source: "cdp"
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
function resolveOutputDirectory(root, saveDir) {
|
|
845
|
+
return (0, import_node_path.resolve)(root, saveDir);
|
|
846
|
+
}
|
|
847
|
+
function createSafeFileName(fileName) {
|
|
848
|
+
const trimmed = fileName.trim();
|
|
849
|
+
if (!trimmed) {
|
|
850
|
+
return `${String(Date.now())}-${(0, import_nanoid2.nanoid)()}`;
|
|
851
|
+
}
|
|
852
|
+
const suffix = (0, import_nanoid2.nanoid)(6);
|
|
853
|
+
const dotIndex = trimmed.lastIndexOf(".");
|
|
854
|
+
if (dotIndex === -1) {
|
|
855
|
+
return `${trimmed}-${suffix}`;
|
|
856
|
+
}
|
|
857
|
+
const base = trimmed.slice(0, dotIndex);
|
|
858
|
+
const extension = trimmed.slice(dotIndex);
|
|
859
|
+
return `${base}-${suffix}${extension}`;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// src/cdp/cdpPerformance.ts
|
|
863
|
+
async function recordCdpPerformance(options) {
|
|
864
|
+
const session = await startCdpPerformanceRecording(options);
|
|
865
|
+
await waitForDuration(options.durationMs);
|
|
866
|
+
return stopCdpPerformanceRecording({
|
|
867
|
+
client: options.client,
|
|
868
|
+
session
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
async function startCdpPerformanceRecording(options) {
|
|
872
|
+
await options.client.Performance.enable();
|
|
873
|
+
await options.client.Profiler.enable();
|
|
874
|
+
await options.client.Profiler.start();
|
|
875
|
+
return {
|
|
876
|
+
recordingId: createRecordingId(options.pageId),
|
|
877
|
+
pageId: options.pageId,
|
|
878
|
+
startedAt: Date.now(),
|
|
879
|
+
includeMemory: options.includeMemory,
|
|
880
|
+
includeStacks: options.includeStacks,
|
|
881
|
+
saveDir: options.saveDir
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
async function stopCdpPerformanceRecording(options) {
|
|
885
|
+
const endedAt = Date.now();
|
|
886
|
+
const [metricsResult, profileResult] = await Promise.all([
|
|
887
|
+
options.client.Performance.getMetrics(),
|
|
888
|
+
options.client.Profiler.stop()
|
|
889
|
+
]);
|
|
890
|
+
const metrics = toMetricMap(metricsResult.metrics);
|
|
891
|
+
const longTasks = toLongTaskRecords(metrics);
|
|
892
|
+
const memorySamples = options.session.includeMemory ? [toMemorySample(metrics)] : [];
|
|
893
|
+
const memory = options.session.includeMemory ? buildMemorySummary(memorySamples) : void 0;
|
|
894
|
+
const stacks = options.session.includeStacks ? buildStackSummary(aggregateCpuProfile(profileResult.profile)) : void 0;
|
|
895
|
+
const artifact = await writeCpuProfileArtifact(
|
|
896
|
+
{
|
|
897
|
+
pageId: options.session.pageId,
|
|
898
|
+
saveDir: options.session.saveDir
|
|
899
|
+
},
|
|
900
|
+
profileResult.profile
|
|
901
|
+
);
|
|
902
|
+
const report = {
|
|
903
|
+
recordingId: options.session.recordingId,
|
|
904
|
+
pageId: options.session.pageId,
|
|
905
|
+
source: "cdp",
|
|
906
|
+
startedAt: options.session.startedAt,
|
|
907
|
+
endedAt,
|
|
908
|
+
durationMs: endedAt - options.session.startedAt,
|
|
909
|
+
summary: buildPerformanceSummary({
|
|
910
|
+
longTasks,
|
|
911
|
+
memorySamples
|
|
912
|
+
}),
|
|
913
|
+
longTasks,
|
|
914
|
+
memory,
|
|
915
|
+
stacks,
|
|
916
|
+
artifacts: [artifact],
|
|
917
|
+
limitations: [
|
|
918
|
+
"CDP path returns sampled CPU profile data, not a full instruction trace"
|
|
919
|
+
]
|
|
920
|
+
};
|
|
921
|
+
return report;
|
|
922
|
+
}
|
|
923
|
+
async function takeHeapSnapshot(options) {
|
|
924
|
+
const chunks = [];
|
|
925
|
+
await options.client.HeapProfiler.enable();
|
|
926
|
+
options.client.HeapProfiler.addHeapSnapshotChunk((event) => {
|
|
927
|
+
const payload = event;
|
|
928
|
+
if (payload.chunk) {
|
|
929
|
+
chunks.push(payload.chunk);
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
await options.client.HeapProfiler.takeHeapSnapshot({
|
|
933
|
+
reportProgress: false
|
|
934
|
+
});
|
|
935
|
+
const artifact = await writePerformanceArtifact({
|
|
936
|
+
root: process.cwd(),
|
|
937
|
+
saveDir: options.saveDir,
|
|
938
|
+
fileName: `${options.pageId}-heap-snapshot.heapsnapshot`,
|
|
939
|
+
kind: "heap-snapshot",
|
|
940
|
+
data: Buffer.from(chunks.join(""))
|
|
941
|
+
});
|
|
942
|
+
return artifact;
|
|
943
|
+
}
|
|
944
|
+
function createRecordingId(pageId) {
|
|
945
|
+
return `cdp-${pageId}-${String(Date.now())}`;
|
|
946
|
+
}
|
|
947
|
+
function waitForDuration(durationMs) {
|
|
948
|
+
return new Promise((resolve2) => {
|
|
949
|
+
setTimeout(() => {
|
|
950
|
+
resolve2();
|
|
951
|
+
}, durationMs);
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
function toMetricMap(metrics) {
|
|
955
|
+
return new Map(metrics.map((metric) => [metric.name, metric.value]));
|
|
956
|
+
}
|
|
957
|
+
function toLongTaskRecords(metrics) {
|
|
958
|
+
const taskDuration = metrics.get("TaskDuration") ?? 0;
|
|
959
|
+
if (taskDuration <= 0) {
|
|
960
|
+
return [];
|
|
961
|
+
}
|
|
962
|
+
return [
|
|
963
|
+
{
|
|
964
|
+
startTime: 0,
|
|
965
|
+
durationMs: taskDuration,
|
|
966
|
+
name: "TaskDuration",
|
|
967
|
+
source: "cpu-profile"
|
|
968
|
+
}
|
|
969
|
+
];
|
|
970
|
+
}
|
|
971
|
+
function toMemorySample(metrics) {
|
|
972
|
+
return {
|
|
973
|
+
timestamp: Date.now(),
|
|
974
|
+
usedJSHeapSize: metrics.get("JSHeapUsedSize"),
|
|
975
|
+
totalJSHeapSize: metrics.get("JSHeapTotalSize"),
|
|
976
|
+
jsHeapSizeLimit: metrics.get("JSHeapSizeLimit")
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
function aggregateCpuProfile(profile) {
|
|
980
|
+
const nodes = profile.nodes ?? [];
|
|
981
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
982
|
+
const nodeIndex = /* @__PURE__ */ new Map();
|
|
983
|
+
nodes.forEach((node) => nodeIndex.set(node.id, node));
|
|
984
|
+
(profile.samples ?? []).forEach((nodeId, index) => {
|
|
985
|
+
const node = nodeIndex.get(nodeId);
|
|
986
|
+
if (!node) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
const delta = profile.timeDeltas?.[index] ?? 0;
|
|
990
|
+
const current = nodeMap.get(nodeId) ?? {
|
|
991
|
+
functionName: node.callFrame.functionName || "<anonymous>",
|
|
992
|
+
url: node.callFrame.url,
|
|
993
|
+
lineNumber: node.callFrame.lineNumber,
|
|
994
|
+
columnNumber: node.callFrame.columnNumber,
|
|
995
|
+
selfTimeMs: 0,
|
|
996
|
+
totalTimeMs: 0,
|
|
997
|
+
hitCount: 0
|
|
998
|
+
};
|
|
999
|
+
nodeMap.set(nodeId, {
|
|
1000
|
+
...current,
|
|
1001
|
+
selfTimeMs: current.selfTimeMs + delta,
|
|
1002
|
+
totalTimeMs: current.totalTimeMs + delta,
|
|
1003
|
+
hitCount: current.hitCount + 1
|
|
1004
|
+
});
|
|
1005
|
+
});
|
|
1006
|
+
return [...nodeMap.values()];
|
|
1007
|
+
}
|
|
1008
|
+
async function writeCpuProfileArtifact(options, profile) {
|
|
1009
|
+
return writePerformanceArtifact({
|
|
1010
|
+
root: process.cwd(),
|
|
1011
|
+
saveDir: options.saveDir ?? ".vite-mcp/performance",
|
|
1012
|
+
fileName: `${options.pageId}-cpu-profile.cpuprofile`,
|
|
1013
|
+
kind: "cpu-profile",
|
|
1014
|
+
data: Buffer.from(JSON.stringify(profile, null, 2))
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// src/mcp/tools/performance.ts
|
|
1019
|
+
var PERFORMANCE_NOT_AVAILABLE_ERROR = "Performance diagnostics are disabled by configuration";
|
|
1020
|
+
var activeCdpPerformanceClients = /* @__PURE__ */ new Map();
|
|
1021
|
+
function registerPerformanceTools(server, ctx) {
|
|
1022
|
+
server.registerTool(
|
|
1023
|
+
MCP_TOOL_NAMES.recordPerformance,
|
|
1024
|
+
{
|
|
1025
|
+
description: "Record a performance sample for the selected page.",
|
|
1026
|
+
inputSchema: {
|
|
1027
|
+
pageId: import_zod5.z.string().optional(),
|
|
1028
|
+
durationMs: import_zod5.z.number().optional(),
|
|
1029
|
+
includeMemory: import_zod5.z.boolean().optional(),
|
|
1030
|
+
includeStacks: import_zod5.z.boolean().optional()
|
|
1031
|
+
}
|
|
1032
|
+
},
|
|
1033
|
+
async (input) => handleRecordPerformance(ctx, input)
|
|
1034
|
+
);
|
|
1035
|
+
server.registerTool(
|
|
1036
|
+
MCP_TOOL_NAMES.startPerformanceRecording,
|
|
1037
|
+
{
|
|
1038
|
+
description: "Start a performance recording session.",
|
|
1039
|
+
inputSchema: {
|
|
1040
|
+
pageId: import_zod5.z.string().optional(),
|
|
1041
|
+
includeMemory: import_zod5.z.boolean().optional(),
|
|
1042
|
+
includeStacks: import_zod5.z.boolean().optional()
|
|
1043
|
+
}
|
|
1044
|
+
},
|
|
1045
|
+
async (input) => handleStartPerformanceRecording(ctx, input)
|
|
1046
|
+
);
|
|
1047
|
+
server.registerTool(
|
|
1048
|
+
MCP_TOOL_NAMES.stopPerformanceRecording,
|
|
1049
|
+
{
|
|
1050
|
+
description: "Stop a performance recording session.",
|
|
1051
|
+
inputSchema: {
|
|
1052
|
+
recordingId: import_zod5.z.string()
|
|
1053
|
+
}
|
|
1054
|
+
},
|
|
1055
|
+
async (input) => handleStopPerformanceRecording(ctx, input)
|
|
1056
|
+
);
|
|
1057
|
+
server.registerTool(
|
|
1058
|
+
MCP_TOOL_NAMES.getPerformanceReport,
|
|
1059
|
+
{
|
|
1060
|
+
description: "Get cached performance reports and active sessions.",
|
|
1061
|
+
inputSchema: {
|
|
1062
|
+
pageId: import_zod5.z.string().optional(),
|
|
1063
|
+
recordingId: import_zod5.z.string().optional(),
|
|
1064
|
+
limit: import_zod5.z.number().optional()
|
|
1065
|
+
}
|
|
1066
|
+
},
|
|
1067
|
+
(input) => handleGetPerformanceReport(ctx, input)
|
|
1068
|
+
);
|
|
1069
|
+
server.registerTool(
|
|
1070
|
+
MCP_TOOL_NAMES.takeHeapSnapshot,
|
|
1071
|
+
{
|
|
1072
|
+
description: "Take a heap snapshot with CDP.",
|
|
1073
|
+
inputSchema: {
|
|
1074
|
+
pageId: import_zod5.z.string().optional()
|
|
1075
|
+
}
|
|
1076
|
+
},
|
|
1077
|
+
async (input) => handleTakeHeapSnapshot(ctx, input)
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
function appendPerformanceReport(ctx, report) {
|
|
1081
|
+
if (ctx.performanceReports.all().some((item) => item.recordingId === report.recordingId)) {
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
ctx.performanceReports.push(report);
|
|
1085
|
+
}
|
|
1086
|
+
async function handleRecordPerformance(ctx, input) {
|
|
1087
|
+
const durationMs = input.durationMs ?? ctx.options.performance.maxDurationMs;
|
|
1088
|
+
const includeMemory = input.includeMemory ?? ctx.options.performance.memory.enabled;
|
|
1089
|
+
const includeStacks = input.includeStacks ?? ctx.options.performance.stacks.enabled;
|
|
1090
|
+
if (ctx.options.performance.mode === "off") {
|
|
1091
|
+
return createToolError(PERFORMANCE_NOT_AVAILABLE_ERROR);
|
|
1092
|
+
}
|
|
1093
|
+
if (ctx.options.performance.mode !== "hook") {
|
|
1094
|
+
const cdpResult = await connectPerformanceCdp(ctx, input.pageId);
|
|
1095
|
+
const cdp = cdpResult.cdp;
|
|
1096
|
+
if (cdp) {
|
|
1097
|
+
try {
|
|
1098
|
+
const report = await recordCdpPerformance({
|
|
1099
|
+
client: cdp.client,
|
|
1100
|
+
pageId: input.pageId ?? "cdp",
|
|
1101
|
+
durationMs,
|
|
1102
|
+
includeMemory,
|
|
1103
|
+
includeStacks,
|
|
1104
|
+
saveDir: ctx.options.performance.saveDir
|
|
1105
|
+
});
|
|
1106
|
+
appendPerformanceReport(ctx, report);
|
|
1107
|
+
return createToolResponse(toStructuredRecord(report));
|
|
1108
|
+
} finally {
|
|
1109
|
+
await closeCdpClient(cdp.client);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
if (ctx.options.performance.mode === "cdp") {
|
|
1113
|
+
return createToolError(
|
|
1114
|
+
formatCdpUnavailableError(
|
|
1115
|
+
"CDP performance collection is unavailable",
|
|
1116
|
+
cdpResult.error
|
|
1117
|
+
)
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
const result = await requestRuntimeData(ctx, (event) => {
|
|
1122
|
+
void ctx.rpcServer?.recordPerformance({
|
|
1123
|
+
event,
|
|
1124
|
+
durationMs,
|
|
1125
|
+
includeMemory,
|
|
1126
|
+
includeStacks
|
|
1127
|
+
});
|
|
1128
|
+
});
|
|
1129
|
+
if (isPerformanceReport(result)) {
|
|
1130
|
+
appendPerformanceReport(ctx, result);
|
|
1131
|
+
}
|
|
1132
|
+
if (isPlainRecord(result)) {
|
|
1133
|
+
return createToolResponse(toStructuredRecord(result));
|
|
1134
|
+
}
|
|
1135
|
+
return createToolError("runtime bridge returned an invalid response");
|
|
1136
|
+
}
|
|
1137
|
+
async function handleStartPerformanceRecording(ctx, input) {
|
|
1138
|
+
const includeMemory = input.includeMemory ?? ctx.options.performance.memory.enabled;
|
|
1139
|
+
const includeStacks = input.includeStacks ?? ctx.options.performance.stacks.enabled;
|
|
1140
|
+
if (ctx.options.performance.mode === "off") {
|
|
1141
|
+
return createToolError(PERFORMANCE_NOT_AVAILABLE_ERROR);
|
|
1142
|
+
}
|
|
1143
|
+
if (ctx.options.performance.mode !== "hook") {
|
|
1144
|
+
const cdpResult = await connectPerformanceCdp(ctx, input.pageId);
|
|
1145
|
+
const cdp = cdpResult.cdp;
|
|
1146
|
+
if (cdp) {
|
|
1147
|
+
try {
|
|
1148
|
+
const session = await startCdpPerformanceRecording({
|
|
1149
|
+
client: cdp.client,
|
|
1150
|
+
pageId: input.pageId ?? "cdp",
|
|
1151
|
+
includeMemory,
|
|
1152
|
+
includeStacks,
|
|
1153
|
+
saveDir: ctx.options.performance.saveDir
|
|
1154
|
+
});
|
|
1155
|
+
ctx.performanceSessions.set(session.recordingId, {
|
|
1156
|
+
recordingId: session.recordingId,
|
|
1157
|
+
pageId: session.pageId,
|
|
1158
|
+
source: "cdp",
|
|
1159
|
+
startedAt: session.startedAt,
|
|
1160
|
+
includeMemory,
|
|
1161
|
+
includeStacks,
|
|
1162
|
+
mode: ctx.options.performance.mode
|
|
1163
|
+
});
|
|
1164
|
+
activeCdpPerformanceClients.set(session.recordingId, cdp.client);
|
|
1165
|
+
return createToolResponse(
|
|
1166
|
+
toStructuredRecord({
|
|
1167
|
+
...session,
|
|
1168
|
+
source: "cdp"
|
|
1169
|
+
})
|
|
1170
|
+
);
|
|
1171
|
+
} catch (error) {
|
|
1172
|
+
await closeCdpClient(cdp.client);
|
|
1173
|
+
return createToolError(
|
|
1174
|
+
error instanceof Error ? error.message : String(error)
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
if (ctx.options.performance.mode === "cdp") {
|
|
1179
|
+
return createToolError(
|
|
1180
|
+
formatCdpUnavailableError(
|
|
1181
|
+
"CDP performance collection is unavailable",
|
|
1182
|
+
cdpResult.error
|
|
1183
|
+
)
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
const result = await requestRuntimeData(ctx, (event) => {
|
|
1188
|
+
void ctx.rpcServer?.startPerformanceRecording({
|
|
1189
|
+
event,
|
|
1190
|
+
includeMemory,
|
|
1191
|
+
includeStacks
|
|
1192
|
+
});
|
|
1193
|
+
});
|
|
1194
|
+
if (isPerformanceStartResult(result)) {
|
|
1195
|
+
ctx.performanceSessions.set(result.recordingId, {
|
|
1196
|
+
recordingId: result.recordingId,
|
|
1197
|
+
pageId: input.pageId ?? "runtime",
|
|
1198
|
+
source: "hook",
|
|
1199
|
+
startedAt: result.startedAt,
|
|
1200
|
+
includeMemory,
|
|
1201
|
+
includeStacks,
|
|
1202
|
+
mode: ctx.options.performance.mode
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
if (isPlainRecord(result)) {
|
|
1206
|
+
return createToolResponse(toStructuredRecord(result));
|
|
1207
|
+
}
|
|
1208
|
+
return createToolError("runtime bridge returned an invalid response");
|
|
1209
|
+
}
|
|
1210
|
+
async function handleStopPerformanceRecording(ctx, input) {
|
|
1211
|
+
const session = ctx.performanceSessions.get(input.recordingId);
|
|
1212
|
+
if (!session) {
|
|
1213
|
+
return createToolError(`Performance recording not found: ${input.recordingId}`);
|
|
1214
|
+
}
|
|
1215
|
+
try {
|
|
1216
|
+
if (session.source === "cdp") {
|
|
1217
|
+
const client = activeCdpPerformanceClients.get(input.recordingId);
|
|
1218
|
+
if (!client) {
|
|
1219
|
+
return createToolError(
|
|
1220
|
+
`CDP client not found for recording: ${input.recordingId}`
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
const report = await stopCdpPerformanceRecording({
|
|
1224
|
+
client,
|
|
1225
|
+
session
|
|
1226
|
+
});
|
|
1227
|
+
appendPerformanceReport(ctx, report);
|
|
1228
|
+
return createToolResponse(toStructuredRecord(report));
|
|
1229
|
+
}
|
|
1230
|
+
const result = await requestRuntimeData(ctx, (event) => {
|
|
1231
|
+
void ctx.rpcServer?.stopPerformanceRecording({
|
|
1232
|
+
event,
|
|
1233
|
+
recordingId: input.recordingId
|
|
1234
|
+
});
|
|
1235
|
+
});
|
|
1236
|
+
if (isPerformanceReport(result)) {
|
|
1237
|
+
appendPerformanceReport(ctx, result);
|
|
1238
|
+
}
|
|
1239
|
+
if (isPlainRecord(result)) {
|
|
1240
|
+
return createToolResponse(toStructuredRecord(result));
|
|
1241
|
+
}
|
|
1242
|
+
return createToolError("runtime bridge returned an invalid response");
|
|
1243
|
+
} finally {
|
|
1244
|
+
ctx.performanceSessions.delete(input.recordingId);
|
|
1245
|
+
const client = activeCdpPerformanceClients.get(input.recordingId);
|
|
1246
|
+
if (client) {
|
|
1247
|
+
activeCdpPerformanceClients.delete(input.recordingId);
|
|
1248
|
+
await closeCdpClient(client);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
function handleGetPerformanceReport(ctx, input) {
|
|
1253
|
+
const reports = ctx.performanceReports.all().filter((report) => !input.pageId || report.pageId === input.pageId).filter(
|
|
1254
|
+
(report) => !input.recordingId || report.recordingId === input.recordingId
|
|
1255
|
+
);
|
|
1256
|
+
const limit = input.limit ?? reports.length;
|
|
1257
|
+
return createToolResponse(toStructuredRecord({
|
|
1258
|
+
report: reports.at(-1) ?? null,
|
|
1259
|
+
reports: reports.slice(-limit),
|
|
1260
|
+
sessions: [...ctx.performanceSessions.values()].filter(
|
|
1261
|
+
(session) => (!input.pageId || session.pageId === input.pageId) && (!input.recordingId || session.recordingId === input.recordingId)
|
|
1262
|
+
)
|
|
1263
|
+
}));
|
|
1264
|
+
}
|
|
1265
|
+
async function handleTakeHeapSnapshot(ctx, input) {
|
|
1266
|
+
if (ctx.options.performance.mode === "off" || ctx.options.performance.mode === "hook") {
|
|
1267
|
+
return createToolError(PERFORMANCE_NOT_AVAILABLE_ERROR);
|
|
1268
|
+
}
|
|
1269
|
+
const cdpResult = await connectPerformanceCdp(ctx, input.pageId);
|
|
1270
|
+
const cdp = cdpResult.cdp;
|
|
1271
|
+
if (!cdp) {
|
|
1272
|
+
return createToolError(
|
|
1273
|
+
formatCdpUnavailableError(
|
|
1274
|
+
"CDP heap snapshot is unavailable",
|
|
1275
|
+
cdpResult.error
|
|
1276
|
+
)
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
try {
|
|
1280
|
+
return createToolResponse(
|
|
1281
|
+
toStructuredRecord(await takeHeapSnapshot({
|
|
1282
|
+
client: cdp.client,
|
|
1283
|
+
pageId: input.pageId ?? "cdp",
|
|
1284
|
+
saveDir: ctx.options.performance.saveDir
|
|
1285
|
+
}))
|
|
1286
|
+
);
|
|
1287
|
+
} finally {
|
|
1288
|
+
await closeCdpClient(cdp.client);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
async function connectPerformanceCdp(ctx, pageId) {
|
|
1292
|
+
try {
|
|
1293
|
+
return { cdp: await connectCdpForPage(ctx, pageId) };
|
|
1294
|
+
} catch (error) {
|
|
1295
|
+
return {
|
|
1296
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
function formatCdpUnavailableError(message, reason) {
|
|
1301
|
+
if (!reason) {
|
|
1302
|
+
return message;
|
|
1303
|
+
}
|
|
1304
|
+
return `${message}: ${reason}`;
|
|
1305
|
+
}
|
|
1306
|
+
function isPerformanceReport(value) {
|
|
1307
|
+
if (!value || typeof value !== "object") {
|
|
1308
|
+
return false;
|
|
1309
|
+
}
|
|
1310
|
+
const report = value;
|
|
1311
|
+
return typeof report.recordingId === "string" && typeof report.pageId === "string" && (report.source === "hook" || report.source === "cdp") && typeof report.startedAt === "number" && typeof report.endedAt === "number" && typeof report.durationMs === "number" && Boolean(report.summary) && Array.isArray(report.longTasks) && Array.isArray(report.limitations);
|
|
1312
|
+
}
|
|
1313
|
+
function isPerformanceStartResult(value) {
|
|
1314
|
+
if (!value || typeof value !== "object") {
|
|
1315
|
+
return false;
|
|
1316
|
+
}
|
|
1317
|
+
const result = value;
|
|
1318
|
+
return typeof result.recordingId === "string" && typeof result.startedAt === "number";
|
|
1319
|
+
}
|
|
1320
|
+
function isPlainRecord(value) {
|
|
1321
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1322
|
+
}
|
|
1323
|
+
function toStructuredRecord(value) {
|
|
1324
|
+
if (isPlainRecord(value)) {
|
|
1325
|
+
return value;
|
|
1326
|
+
}
|
|
1327
|
+
return {};
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// src/mcp/tools/pages.ts
|
|
1331
|
+
var import_zod6 = require("zod");
|
|
1332
|
+
|
|
668
1333
|
// src/plugin/entryDiscovery.ts
|
|
669
1334
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
670
|
-
var
|
|
1335
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
671
1336
|
var import_vite = require("vite");
|
|
672
1337
|
function discoverHtmlEntries(server) {
|
|
673
1338
|
const root = server.config.root;
|
|
@@ -680,7 +1345,7 @@ function walkHtmlEntries(root, dir, entries) {
|
|
|
680
1345
|
if (item.name === "node_modules" || item.name.startsWith(".")) {
|
|
681
1346
|
continue;
|
|
682
1347
|
}
|
|
683
|
-
const fullPath =
|
|
1348
|
+
const fullPath = import_node_path2.default.join(dir, item.name);
|
|
684
1349
|
if (item.isDirectory()) {
|
|
685
1350
|
walkHtmlEntries(root, fullPath, entries);
|
|
686
1351
|
continue;
|
|
@@ -688,10 +1353,10 @@ function walkHtmlEntries(root, dir, entries) {
|
|
|
688
1353
|
if (!item.isFile() || !item.name.endsWith(".html")) {
|
|
689
1354
|
continue;
|
|
690
1355
|
}
|
|
691
|
-
const
|
|
1356
|
+
const relative2 = (0, import_vite.normalizePath)(import_node_path2.default.relative(root, fullPath));
|
|
692
1357
|
entries.push({
|
|
693
|
-
file:
|
|
694
|
-
pathname:
|
|
1358
|
+
file: relative2,
|
|
1359
|
+
pathname: relative2 === "index.html" ? "/" : `/${relative2}`
|
|
695
1360
|
});
|
|
696
1361
|
}
|
|
697
1362
|
}
|
|
@@ -701,9 +1366,12 @@ function registerPageTools(server, ctx, vite) {
|
|
|
701
1366
|
server.registerTool(
|
|
702
1367
|
MCP_TOOL_NAMES.listPages,
|
|
703
1368
|
{
|
|
704
|
-
description: "List Vite page entries and connected runtime/CDP targets."
|
|
1369
|
+
description: "List Vite page entries and connected runtime/CDP targets.",
|
|
1370
|
+
inputSchema: {
|
|
1371
|
+
includeDisconnected: import_zod6.z.boolean().optional()
|
|
1372
|
+
}
|
|
705
1373
|
},
|
|
706
|
-
async () => {
|
|
1374
|
+
async (input) => {
|
|
707
1375
|
const cdpResult = await listCdpPageTargets(ctx);
|
|
708
1376
|
for (const target of cdpResult.pages) {
|
|
709
1377
|
ctx.pages.upsert(target);
|
|
@@ -711,7 +1379,9 @@ function registerPageTools(server, ctx, vite) {
|
|
|
711
1379
|
}
|
|
712
1380
|
return createToolResponse({
|
|
713
1381
|
entries: discoverHtmlEntries(vite),
|
|
714
|
-
pages: ctx.pages.list(
|
|
1382
|
+
pages: ctx.pages.list({
|
|
1383
|
+
includeDisconnected: input.includeDisconnected
|
|
1384
|
+
}),
|
|
715
1385
|
cdpError: cdpResult.error
|
|
716
1386
|
});
|
|
717
1387
|
}
|
|
@@ -721,8 +1391,8 @@ function registerPageTools(server, ctx, vite) {
|
|
|
721
1391
|
{
|
|
722
1392
|
description: "Reload the selected page. CDP uses ignoreCache; Runtime Hook falls back to normal reload.",
|
|
723
1393
|
inputSchema: {
|
|
724
|
-
pageId:
|
|
725
|
-
ignoreCache:
|
|
1394
|
+
pageId: import_zod6.z.string().optional(),
|
|
1395
|
+
ignoreCache: import_zod6.z.boolean().optional()
|
|
726
1396
|
}
|
|
727
1397
|
},
|
|
728
1398
|
async (input) => {
|
|
@@ -781,16 +1451,16 @@ function resolveRuntimeReloadTarget(ctx, pageId) {
|
|
|
781
1451
|
function waitForRuntimePageReconnect(ctx) {
|
|
782
1452
|
let timeout;
|
|
783
1453
|
let cleanup;
|
|
784
|
-
const promise = new Promise((
|
|
1454
|
+
const promise = new Promise((resolve2) => {
|
|
785
1455
|
timeout = setTimeout(() => {
|
|
786
1456
|
cleanup?.();
|
|
787
|
-
|
|
1457
|
+
resolve2(null);
|
|
788
1458
|
}, 5e3);
|
|
789
1459
|
cleanup = ctx.hooks.hookOnce(RUNTIME_PAGE_RECONNECTED_EVENT, (payload) => {
|
|
790
1460
|
if (timeout) {
|
|
791
1461
|
clearTimeout(timeout);
|
|
792
1462
|
}
|
|
793
|
-
|
|
1463
|
+
resolve2(isPageTarget(payload) ? payload : null);
|
|
794
1464
|
});
|
|
795
1465
|
});
|
|
796
1466
|
return {
|
|
@@ -874,7 +1544,7 @@ function getPathname(url) {
|
|
|
874
1544
|
}
|
|
875
1545
|
|
|
876
1546
|
// src/mcp/tools/screenshot.ts
|
|
877
|
-
var
|
|
1547
|
+
var import_zod7 = require("zod");
|
|
878
1548
|
|
|
879
1549
|
// src/cdp/cdpScreenshot.ts
|
|
880
1550
|
async function cdpCaptureScreenshot(options) {
|
|
@@ -980,16 +1650,16 @@ function isElementRect(value) {
|
|
|
980
1650
|
|
|
981
1651
|
// src/mcp/tools/screenshotOutput.ts
|
|
982
1652
|
var import_node_crypto = require("crypto");
|
|
983
|
-
var
|
|
984
|
-
var
|
|
1653
|
+
var import_promises2 = require("fs/promises");
|
|
1654
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
985
1655
|
async function createScreenshotOutput(ctx, payload) {
|
|
986
1656
|
if (ctx.options.screenshot.type === "base64") {
|
|
987
1657
|
return payload;
|
|
988
1658
|
}
|
|
989
1659
|
const saveDir = resolveScreenshotSaveDir(ctx);
|
|
990
|
-
await (0,
|
|
991
|
-
const filePath =
|
|
992
|
-
await (0,
|
|
1660
|
+
await (0, import_promises2.mkdir)(saveDir, { recursive: true });
|
|
1661
|
+
const filePath = import_node_path3.default.join(saveDir, createScreenshotFileName(payload));
|
|
1662
|
+
await (0, import_promises2.writeFile)(filePath, Buffer.from(payload.data, "base64"));
|
|
993
1663
|
return {
|
|
994
1664
|
source: payload.source,
|
|
995
1665
|
target: payload.target,
|
|
@@ -1012,10 +1682,10 @@ function resolveScreenshotSaveDir(ctx) {
|
|
|
1012
1682
|
if (!root) {
|
|
1013
1683
|
throw new Error("Vite server root is required for screenshot path output");
|
|
1014
1684
|
}
|
|
1015
|
-
if (
|
|
1685
|
+
if (import_node_path3.default.isAbsolute(saveDir)) {
|
|
1016
1686
|
return saveDir;
|
|
1017
1687
|
}
|
|
1018
|
-
return
|
|
1688
|
+
return import_node_path3.default.resolve(root, saveDir);
|
|
1019
1689
|
}
|
|
1020
1690
|
function createScreenshotFileName(payload) {
|
|
1021
1691
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -1027,21 +1697,21 @@ function createProjectRelativePath(ctx, filePath) {
|
|
|
1027
1697
|
if (!root) {
|
|
1028
1698
|
throw new Error("Vite server root is required for screenshot path output");
|
|
1029
1699
|
}
|
|
1030
|
-
return
|
|
1700
|
+
return import_node_path3.default.relative(root, filePath).split(import_node_path3.default.sep).join("/");
|
|
1031
1701
|
}
|
|
1032
1702
|
|
|
1033
1703
|
// src/mcp/tools/screenshot.ts
|
|
1034
1704
|
var DEFAULT_SCREENSHOT_TARGET = "viewport";
|
|
1035
1705
|
var DEFAULT_SCREENSHOT_FORMAT = "png";
|
|
1036
1706
|
var screenshotInputSchema = {
|
|
1037
|
-
pageId:
|
|
1038
|
-
target:
|
|
1039
|
-
selector:
|
|
1040
|
-
format:
|
|
1041
|
-
prefer:
|
|
1042
|
-
quality:
|
|
1043
|
-
scale:
|
|
1044
|
-
snapdom:
|
|
1707
|
+
pageId: import_zod7.z.string().optional(),
|
|
1708
|
+
target: import_zod7.z.enum(["viewport", "fullPage", "element"]).optional(),
|
|
1709
|
+
selector: import_zod7.z.string().optional(),
|
|
1710
|
+
format: import_zod7.z.enum(["png", "jpeg", "webp"]).optional(),
|
|
1711
|
+
prefer: import_zod7.z.enum(["auto", "cdp", "runtime"]).optional(),
|
|
1712
|
+
quality: import_zod7.z.number().optional(),
|
|
1713
|
+
scale: import_zod7.z.number().optional(),
|
|
1714
|
+
snapdom: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.unknown()).optional()
|
|
1045
1715
|
};
|
|
1046
1716
|
function registerScreenshotTools(server, ctx) {
|
|
1047
1717
|
server.registerTool(
|
|
@@ -1114,7 +1784,7 @@ async function createRuntimeScreenshot(ctx, input, normalized) {
|
|
|
1114
1784
|
`screenshot is too large: ${String(result.byteLength)} bytes`
|
|
1115
1785
|
);
|
|
1116
1786
|
}
|
|
1117
|
-
if (!
|
|
1787
|
+
if (!isPlainRecord2(result)) {
|
|
1118
1788
|
return createToolError("runtime screenshot returned an invalid response");
|
|
1119
1789
|
}
|
|
1120
1790
|
if (result.ok === false) {
|
|
@@ -1143,9 +1813,9 @@ async function createScreenshotResponse(ctx, result) {
|
|
|
1143
1813
|
}
|
|
1144
1814
|
}
|
|
1145
1815
|
function isScreenshotTooLarge(ctx, result) {
|
|
1146
|
-
return
|
|
1816
|
+
return isPlainRecord2(result) && "byteLength" in result && typeof result.byteLength === "number" && result.byteLength > ctx.options.screenshot.maxBytes;
|
|
1147
1817
|
}
|
|
1148
|
-
function
|
|
1818
|
+
function isPlainRecord2(value) {
|
|
1149
1819
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1150
1820
|
}
|
|
1151
1821
|
function createMimeType(format) {
|
|
@@ -1160,8 +1830,8 @@ function isScreenshotImagePayload(result) {
|
|
|
1160
1830
|
}
|
|
1161
1831
|
|
|
1162
1832
|
// src/mcp/tools/vue.ts
|
|
1163
|
-
var
|
|
1164
|
-
var
|
|
1833
|
+
var import_nanoid3 = require("nanoid");
|
|
1834
|
+
var import_zod8 = require("zod");
|
|
1165
1835
|
function registerVueTools(server, ctx) {
|
|
1166
1836
|
server.registerTool(
|
|
1167
1837
|
MCP_TOOL_NAMES.getComponentTree,
|
|
@@ -1174,7 +1844,7 @@ function registerVueTools(server, ctx) {
|
|
|
1174
1844
|
MCP_TOOL_NAMES.getComponentState,
|
|
1175
1845
|
{
|
|
1176
1846
|
description: "Get Vue component state.",
|
|
1177
|
-
inputSchema: { componentName:
|
|
1847
|
+
inputSchema: { componentName: import_zod8.z.string() }
|
|
1178
1848
|
},
|
|
1179
1849
|
async ({ componentName }) => requestVueData(ctx, (event) => {
|
|
1180
1850
|
void ctx.rpcServer?.getInspectorState({ event, componentName });
|
|
@@ -1185,10 +1855,10 @@ function registerVueTools(server, ctx) {
|
|
|
1185
1855
|
{
|
|
1186
1856
|
description: "Edit Vue component state.",
|
|
1187
1857
|
inputSchema: {
|
|
1188
|
-
componentName:
|
|
1189
|
-
path:
|
|
1190
|
-
value:
|
|
1191
|
-
valueType:
|
|
1858
|
+
componentName: import_zod8.z.string(),
|
|
1859
|
+
path: import_zod8.z.array(import_zod8.z.string()),
|
|
1860
|
+
value: import_zod8.z.string(),
|
|
1861
|
+
valueType: import_zod8.z.enum(["string", "number", "boolean", "object", "array"])
|
|
1192
1862
|
}
|
|
1193
1863
|
},
|
|
1194
1864
|
({ componentName, path: path8, value, valueType }) => {
|
|
@@ -1208,7 +1878,7 @@ function registerVueTools(server, ctx) {
|
|
|
1208
1878
|
MCP_TOOL_NAMES.highlightComponent,
|
|
1209
1879
|
{
|
|
1210
1880
|
description: "Highlight a Vue component.",
|
|
1211
|
-
inputSchema: { componentName:
|
|
1881
|
+
inputSchema: { componentName: import_zod8.z.string() }
|
|
1212
1882
|
},
|
|
1213
1883
|
({ componentName }) => {
|
|
1214
1884
|
if (!ctx.rpcServer) {
|
|
@@ -1236,7 +1906,7 @@ function registerVueTools(server, ctx) {
|
|
|
1236
1906
|
MCP_TOOL_NAMES.getPiniaState,
|
|
1237
1907
|
{
|
|
1238
1908
|
description: "Get Pinia store state.",
|
|
1239
|
-
inputSchema: { storeName:
|
|
1909
|
+
inputSchema: { storeName: import_zod8.z.string() }
|
|
1240
1910
|
},
|
|
1241
1911
|
async ({ storeName }) => requestVueData(ctx, (event) => {
|
|
1242
1912
|
void ctx.rpcServer?.getPiniaState({ event, storeName });
|
|
@@ -1247,7 +1917,7 @@ async function requestVueData(ctx, trigger) {
|
|
|
1247
1917
|
if (!ctx.rpcServer) {
|
|
1248
1918
|
return vueBridgeUnavailable();
|
|
1249
1919
|
}
|
|
1250
|
-
const event = (0,
|
|
1920
|
+
const event = (0, import_nanoid3.nanoid)();
|
|
1251
1921
|
const data = await waitForVueHook(ctx, event, () => {
|
|
1252
1922
|
trigger(event);
|
|
1253
1923
|
});
|
|
@@ -1256,13 +1926,13 @@ async function requestVueData(ctx, trigger) {
|
|
|
1256
1926
|
};
|
|
1257
1927
|
}
|
|
1258
1928
|
function waitForVueHook(ctx, event, trigger) {
|
|
1259
|
-
return new Promise((
|
|
1929
|
+
return new Promise((resolve2) => {
|
|
1260
1930
|
const timeout = setTimeout(() => {
|
|
1261
|
-
|
|
1931
|
+
resolve2({ ok: false, error: "Vue runtime bridge response timed out" });
|
|
1262
1932
|
}, 5e3);
|
|
1263
1933
|
ctx.hooks.hookOnce(event, (data) => {
|
|
1264
1934
|
clearTimeout(timeout);
|
|
1265
|
-
|
|
1935
|
+
resolve2(data);
|
|
1266
1936
|
});
|
|
1267
1937
|
trigger();
|
|
1268
1938
|
});
|
|
@@ -1283,6 +1953,7 @@ function createMcpServer(ctx, vite) {
|
|
|
1283
1953
|
registerConsoleTools(server, ctx);
|
|
1284
1954
|
registerEvaluateTools(server, ctx);
|
|
1285
1955
|
registerNetworkTools(server, ctx);
|
|
1956
|
+
registerPerformanceTools(server, ctx);
|
|
1286
1957
|
registerVueTools(server, ctx);
|
|
1287
1958
|
return server;
|
|
1288
1959
|
}
|
|
@@ -1370,6 +2041,18 @@ function createServerVueRuntimeRpc(ctx) {
|
|
|
1370
2041
|
onScreenshotTaken: (event, data) => {
|
|
1371
2042
|
void ctx.hooks.callHook(event, data);
|
|
1372
2043
|
},
|
|
2044
|
+
recordPerformance: () => void 0,
|
|
2045
|
+
onPerformanceRecorded: (event, data) => {
|
|
2046
|
+
void ctx.hooks.callHook(event, data);
|
|
2047
|
+
},
|
|
2048
|
+
startPerformanceRecording: () => void 0,
|
|
2049
|
+
onPerformanceRecordingStarted: (event, data) => {
|
|
2050
|
+
void ctx.hooks.callHook(event, data);
|
|
2051
|
+
},
|
|
2052
|
+
stopPerformanceRecording: () => void 0,
|
|
2053
|
+
onPerformanceRecordingStopped: (event, data) => {
|
|
2054
|
+
void ctx.hooks.callHook(event, data);
|
|
2055
|
+
},
|
|
1373
2056
|
getInspectorTree: () => void 0,
|
|
1374
2057
|
onInspectorTreeUpdated: (event, data) => {
|
|
1375
2058
|
void ctx.hooks.callHook(event, data);
|
|
@@ -1396,7 +2079,7 @@ function createServerVueRuntimeRpc(ctx) {
|
|
|
1396
2079
|
}
|
|
1397
2080
|
|
|
1398
2081
|
// src/cdp/cdpConsole.ts
|
|
1399
|
-
var
|
|
2082
|
+
var import_nanoid4 = require("nanoid");
|
|
1400
2083
|
|
|
1401
2084
|
// src/shared/serialization.ts
|
|
1402
2085
|
function safeStringify(value) {
|
|
@@ -1425,7 +2108,7 @@ async function startCdpConsole(options) {
|
|
|
1425
2108
|
await options.client.Runtime.enable();
|
|
1426
2109
|
options.client.Runtime.consoleAPICalled((event) => {
|
|
1427
2110
|
options.push({
|
|
1428
|
-
id: (0,
|
|
2111
|
+
id: (0, import_nanoid4.nanoid)(),
|
|
1429
2112
|
pageId: options.pageId,
|
|
1430
2113
|
source: "cdp",
|
|
1431
2114
|
level: normalizeConsoleLevel(event.type),
|
|
@@ -1445,7 +2128,7 @@ function normalizeConsoleLevel(level) {
|
|
|
1445
2128
|
}
|
|
1446
2129
|
|
|
1447
2130
|
// src/cdp/cdpNetwork.ts
|
|
1448
|
-
var
|
|
2131
|
+
var import_nanoid5 = require("nanoid");
|
|
1449
2132
|
|
|
1450
2133
|
// src/shared/sanitize.ts
|
|
1451
2134
|
function maskHeaders(headers = {}, maskNames = []) {
|
|
@@ -1485,7 +2168,7 @@ async function startCdpNetwork(options) {
|
|
|
1485
2168
|
await options.client.Network.enable();
|
|
1486
2169
|
options.client.Network.requestWillBeSent((event) => {
|
|
1487
2170
|
records.set(event.requestId, {
|
|
1488
|
-
id: (0,
|
|
2171
|
+
id: (0, import_nanoid5.nanoid)(),
|
|
1489
2172
|
pageId: options.pageId,
|
|
1490
2173
|
source: "cdp",
|
|
1491
2174
|
url: event.request.url,
|
|
@@ -1656,7 +2339,7 @@ async function startCdpObservers(ctx, target, client) {
|
|
|
1656
2339
|
|
|
1657
2340
|
// src/plugin/injectRuntime.ts
|
|
1658
2341
|
var import_node_module = require("module");
|
|
1659
|
-
var
|
|
2342
|
+
var import_node_path4 = require("path");
|
|
1660
2343
|
function createRuntimeInjectionController(options, getConfig) {
|
|
1661
2344
|
return {
|
|
1662
2345
|
resolveId(importee) {
|
|
@@ -1740,7 +2423,7 @@ function createSnapdomLoaderModule(root) {
|
|
|
1740
2423
|
}
|
|
1741
2424
|
function canResolveSnapdomFromProject(root) {
|
|
1742
2425
|
try {
|
|
1743
|
-
(0, import_node_module.createRequire)((0,
|
|
2426
|
+
(0, import_node_module.createRequire)((0, import_node_path4.join)(root ?? process.cwd(), "package.json")).resolve(
|
|
1744
2427
|
"@zumer/snapdom"
|
|
1745
2428
|
);
|
|
1746
2429
|
return true;
|
|
@@ -1779,18 +2462,18 @@ function getPluginPath(plugin) {
|
|
|
1779
2462
|
}
|
|
1780
2463
|
|
|
1781
2464
|
// src/plugin/mcpClientConfig/index.ts
|
|
1782
|
-
var
|
|
1783
|
-
var
|
|
2465
|
+
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
2466
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
1784
2467
|
|
|
1785
2468
|
// src/plugin/mcpClientConfig/codexConfig.ts
|
|
1786
|
-
var
|
|
1787
|
-
var
|
|
2469
|
+
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
2470
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
1788
2471
|
async function updateCodexMcpClientConfig(options) {
|
|
1789
2472
|
try {
|
|
1790
2473
|
const current = await readOptionalTextFile(options.configPath);
|
|
1791
2474
|
const next = replaceOrAppendOwnedBlock(current, options);
|
|
1792
|
-
await
|
|
1793
|
-
await
|
|
2475
|
+
await import_promises3.default.mkdir(import_node_path5.default.dirname(options.configPath), { recursive: true });
|
|
2476
|
+
await import_promises3.default.writeFile(options.configPath, next);
|
|
1794
2477
|
} catch (error) {
|
|
1795
2478
|
console.warn(
|
|
1796
2479
|
`[vite-plugin-vue-mcp-next] Failed to update Codex MCP config at ${options.configPath}: ${formatError(error)}`
|
|
@@ -1862,7 +2545,7 @@ function renameServerTableHeader(line, fromServerName, toServerName) {
|
|
|
1862
2545
|
}
|
|
1863
2546
|
async function readOptionalTextFile(filePath) {
|
|
1864
2547
|
try {
|
|
1865
|
-
return await
|
|
2548
|
+
return await import_promises3.default.readFile(filePath, "utf-8");
|
|
1866
2549
|
} catch (error) {
|
|
1867
2550
|
if (isNodeError(error) && error.code === "ENOENT") {
|
|
1868
2551
|
return "";
|
|
@@ -1888,16 +2571,16 @@ function isNodeError(error) {
|
|
|
1888
2571
|
}
|
|
1889
2572
|
|
|
1890
2573
|
// src/plugin/mcpClientConfig/jsonConfig.ts
|
|
1891
|
-
var
|
|
1892
|
-
var
|
|
2574
|
+
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
2575
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
1893
2576
|
async function updateJsonMcpClientConfig(options) {
|
|
1894
2577
|
try {
|
|
1895
2578
|
const config = await readJsonConfig(options.configPath);
|
|
1896
|
-
if (!
|
|
2579
|
+
if (!isPlainRecord3(config)) {
|
|
1897
2580
|
warnConfigFailure(options, "config root must be a JSON object");
|
|
1898
2581
|
return;
|
|
1899
2582
|
}
|
|
1900
|
-
const mcpServers =
|
|
2583
|
+
const mcpServers = isPlainRecord3(config.mcpServers) ? config.mcpServers : {};
|
|
1901
2584
|
if (Object.hasOwn(mcpServers, options.serverName)) {
|
|
1902
2585
|
return;
|
|
1903
2586
|
}
|
|
@@ -1929,8 +2612,8 @@ function renameLegacyServer(mcpServers, options) {
|
|
|
1929
2612
|
);
|
|
1930
2613
|
}
|
|
1931
2614
|
async function writeJsonConfig(configPath, config) {
|
|
1932
|
-
await
|
|
1933
|
-
await
|
|
2615
|
+
await import_promises4.default.mkdir(import_node_path6.default.dirname(configPath), { recursive: true });
|
|
2616
|
+
await import_promises4.default.writeFile(configPath, `${JSON.stringify(config, null, 2)}
|
|
1934
2617
|
`);
|
|
1935
2618
|
}
|
|
1936
2619
|
async function readJsonConfig(configPath) {
|
|
@@ -1942,7 +2625,7 @@ async function readJsonConfig(configPath) {
|
|
|
1942
2625
|
}
|
|
1943
2626
|
async function readOptionalTextFile2(filePath) {
|
|
1944
2627
|
try {
|
|
1945
|
-
return await
|
|
2628
|
+
return await import_promises4.default.readFile(filePath, "utf-8");
|
|
1946
2629
|
} catch (error) {
|
|
1947
2630
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
1948
2631
|
return "{}";
|
|
@@ -1950,7 +2633,7 @@ async function readOptionalTextFile2(filePath) {
|
|
|
1950
2633
|
throw error;
|
|
1951
2634
|
}
|
|
1952
2635
|
}
|
|
1953
|
-
function
|
|
2636
|
+
function isPlainRecord3(value) {
|
|
1954
2637
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1955
2638
|
}
|
|
1956
2639
|
function warnConfigFailure(options, reason) {
|
|
@@ -1974,12 +2657,12 @@ async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options,
|
|
|
1974
2657
|
root,
|
|
1975
2658
|
clientName: "cursor",
|
|
1976
2659
|
enabled: options.cursor,
|
|
1977
|
-
entryPath:
|
|
2660
|
+
entryPath: import_node_path7.default.join(root, ".cursor"),
|
|
1978
2661
|
entryKind: "directory",
|
|
1979
2662
|
userOptions,
|
|
1980
2663
|
createJob: () => updateJsonMcpClientConfig({
|
|
1981
2664
|
clientName: "Cursor",
|
|
1982
|
-
configPath:
|
|
2665
|
+
configPath: import_node_path7.default.join(root, ".cursor", "mcp.json"),
|
|
1983
2666
|
mcpUrl: sseUrl,
|
|
1984
2667
|
serverName,
|
|
1985
2668
|
legacyServerNames
|
|
@@ -1989,11 +2672,11 @@ async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options,
|
|
|
1989
2672
|
root,
|
|
1990
2673
|
clientName: "codex",
|
|
1991
2674
|
enabled: options.codex,
|
|
1992
|
-
entryPath:
|
|
2675
|
+
entryPath: import_node_path7.default.join(root, ".codex"),
|
|
1993
2676
|
entryKind: "directory",
|
|
1994
2677
|
userOptions,
|
|
1995
2678
|
createJob: () => updateCodexMcpClientConfig({
|
|
1996
|
-
configPath:
|
|
2679
|
+
configPath: import_node_path7.default.join(root, ".codex", "config.toml"),
|
|
1997
2680
|
mcpUrl: streamableHttpUrl,
|
|
1998
2681
|
serverName,
|
|
1999
2682
|
legacyServerNames
|
|
@@ -2003,12 +2686,12 @@ async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options,
|
|
|
2003
2686
|
root,
|
|
2004
2687
|
clientName: "claudeCode",
|
|
2005
2688
|
enabled: options.claudeCode,
|
|
2006
|
-
entryPath:
|
|
2689
|
+
entryPath: import_node_path7.default.join(root, ".mcp.json"),
|
|
2007
2690
|
entryKind: "file",
|
|
2008
2691
|
userOptions,
|
|
2009
2692
|
createJob: () => updateJsonMcpClientConfig({
|
|
2010
2693
|
clientName: "Claude Code",
|
|
2011
|
-
configPath:
|
|
2694
|
+
configPath: import_node_path7.default.join(root, ".mcp.json"),
|
|
2012
2695
|
mcpUrl: sseUrl,
|
|
2013
2696
|
serverName,
|
|
2014
2697
|
legacyServerNames
|
|
@@ -2018,12 +2701,12 @@ async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options,
|
|
|
2018
2701
|
root,
|
|
2019
2702
|
clientName: "trae",
|
|
2020
2703
|
enabled: options.trae,
|
|
2021
|
-
entryPath:
|
|
2704
|
+
entryPath: import_node_path7.default.join(root, ".trae"),
|
|
2022
2705
|
entryKind: "directory",
|
|
2023
2706
|
userOptions,
|
|
2024
2707
|
createJob: () => updateJsonMcpClientConfig({
|
|
2025
2708
|
clientName: "Trae",
|
|
2026
|
-
configPath:
|
|
2709
|
+
configPath: import_node_path7.default.join(root, ".trae", "mcp.json"),
|
|
2027
2710
|
mcpUrl: sseUrl,
|
|
2028
2711
|
serverName,
|
|
2029
2712
|
legacyServerNames
|
|
@@ -2062,7 +2745,7 @@ function isClientExplicitlyConfigured(clientName, userOptions) {
|
|
|
2062
2745
|
}
|
|
2063
2746
|
async function hasExpectedEntry(entryPath, entryKind) {
|
|
2064
2747
|
try {
|
|
2065
|
-
const stat = await
|
|
2748
|
+
const stat = await import_promises5.default.stat(entryPath);
|
|
2066
2749
|
return entryKind === "directory" ? stat.isDirectory() : stat.isFile();
|
|
2067
2750
|
} catch (error) {
|
|
2068
2751
|
if (isNodeError3(error) && error.code === "ENOENT") {
|
|
@@ -2082,12 +2765,12 @@ function isNodeError3(error) {
|
|
|
2082
2765
|
}
|
|
2083
2766
|
|
|
2084
2767
|
// src/plugin/skillConfig/index.ts
|
|
2085
|
-
var
|
|
2086
|
-
var
|
|
2768
|
+
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
2769
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
2087
2770
|
|
|
2088
2771
|
// src/plugin/skillConfig/writers.ts
|
|
2089
|
-
var
|
|
2090
|
-
var
|
|
2772
|
+
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
2773
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
2091
2774
|
var GENERATED_SKILL_CONFIG_MARKER = "<!-- Generated by vite-plugin-vue-mcp-next. Safe to edit, but automatic updates only apply while this marker remains. -->";
|
|
2092
2775
|
async function writeGeneratedTextFile(options) {
|
|
2093
2776
|
try {
|
|
@@ -2098,8 +2781,8 @@ async function writeGeneratedTextFile(options) {
|
|
|
2098
2781
|
);
|
|
2099
2782
|
return;
|
|
2100
2783
|
}
|
|
2101
|
-
await
|
|
2102
|
-
await
|
|
2784
|
+
await import_promises6.default.mkdir(import_node_path8.default.dirname(options.filePath), { recursive: true });
|
|
2785
|
+
await import_promises6.default.writeFile(options.filePath, options.content);
|
|
2103
2786
|
} catch (error) {
|
|
2104
2787
|
console.warn(
|
|
2105
2788
|
`[vite-plugin-vue-mcp-next] Failed to update ${options.targetName} at ${options.filePath}: ${formatError4(error)}`
|
|
@@ -2108,7 +2791,7 @@ async function writeGeneratedTextFile(options) {
|
|
|
2108
2791
|
}
|
|
2109
2792
|
async function readOptionalTextFile3(filePath) {
|
|
2110
2793
|
try {
|
|
2111
|
-
return await
|
|
2794
|
+
return await import_promises6.default.readFile(filePath, "utf-8");
|
|
2112
2795
|
} catch (error) {
|
|
2113
2796
|
if (isNodeError4(error) && error.code === "ENOENT") {
|
|
2114
2797
|
return "";
|
|
@@ -2125,7 +2808,7 @@ function isNodeError4(error) {
|
|
|
2125
2808
|
|
|
2126
2809
|
// src/plugin/skillConfig/index.ts
|
|
2127
2810
|
var PACKAGE_NAME = "@xiaou66/vite-plugin-vue-mcp-next";
|
|
2128
|
-
var PACKAGED_SKILL_PATH =
|
|
2811
|
+
var PACKAGED_SKILL_PATH = import_node_path9.default.join("skills", "vite-mcp-next", "SKILL.md");
|
|
2129
2812
|
async function updateSkillConfigs(root, options) {
|
|
2130
2813
|
if (!options.autoConfig) {
|
|
2131
2814
|
return;
|
|
@@ -2141,13 +2824,13 @@ async function updateSkillConfigs(root, options) {
|
|
|
2141
2824
|
function createSkillConfigDescriptors(root) {
|
|
2142
2825
|
return [
|
|
2143
2826
|
{
|
|
2144
|
-
entryPath:
|
|
2145
|
-
filePath:
|
|
2827
|
+
entryPath: import_node_path9.default.join(root, ".codex"),
|
|
2828
|
+
filePath: import_node_path9.default.join(root, ".codex", "skills", "vite-mcp-next", "SKILL.md"),
|
|
2146
2829
|
targetName: "Codex skill"
|
|
2147
2830
|
},
|
|
2148
2831
|
{
|
|
2149
|
-
entryPath:
|
|
2150
|
-
filePath:
|
|
2832
|
+
entryPath: import_node_path9.default.join(root, ".claude"),
|
|
2833
|
+
filePath: import_node_path9.default.join(
|
|
2151
2834
|
root,
|
|
2152
2835
|
".claude",
|
|
2153
2836
|
"skills",
|
|
@@ -2157,8 +2840,8 @@ function createSkillConfigDescriptors(root) {
|
|
|
2157
2840
|
targetName: "Claude Code skill"
|
|
2158
2841
|
},
|
|
2159
2842
|
{
|
|
2160
|
-
entryPath:
|
|
2161
|
-
filePath:
|
|
2843
|
+
entryPath: import_node_path9.default.join(root, ".cursor"),
|
|
2844
|
+
filePath: import_node_path9.default.join(root, ".cursor", "rules", "vite-mcp-next.mdc"),
|
|
2162
2845
|
targetName: "Cursor rule"
|
|
2163
2846
|
}
|
|
2164
2847
|
];
|
|
@@ -2182,7 +2865,7 @@ async function readPackagedSkillContent(root) {
|
|
|
2182
2865
|
const candidates = getPackagedSkillCandidates(root);
|
|
2183
2866
|
for (const candidate of candidates) {
|
|
2184
2867
|
try {
|
|
2185
|
-
return await
|
|
2868
|
+
return await import_promises7.default.readFile(candidate, "utf-8");
|
|
2186
2869
|
} catch (error) {
|
|
2187
2870
|
if (isNodeError5(error) && error.code === "ENOENT") {
|
|
2188
2871
|
continue;
|
|
@@ -2206,14 +2889,14 @@ async function safelyReadPackagedSkillContent(root) {
|
|
|
2206
2889
|
}
|
|
2207
2890
|
function getPackagedSkillCandidates(root) {
|
|
2208
2891
|
return [
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2892
|
+
import_node_path9.default.resolve(root, "node_modules", PACKAGE_NAME, PACKAGED_SKILL_PATH),
|
|
2893
|
+
import_node_path9.default.resolve(process.cwd(), "node_modules", PACKAGE_NAME, PACKAGED_SKILL_PATH),
|
|
2894
|
+
import_node_path9.default.resolve(process.cwd(), PACKAGED_SKILL_PATH)
|
|
2212
2895
|
];
|
|
2213
2896
|
}
|
|
2214
2897
|
async function hasDirectoryEntry(entryPath) {
|
|
2215
2898
|
try {
|
|
2216
|
-
const stat = await
|
|
2899
|
+
const stat = await import_promises7.default.stat(entryPath);
|
|
2217
2900
|
return stat.isDirectory();
|
|
2218
2901
|
} catch (error) {
|
|
2219
2902
|
if (isNodeError5(error) && error.code === "ENOENT") {
|
|
@@ -2292,6 +2975,14 @@ function vueMcpNext(userOptions = {}) {
|
|
|
2292
2975
|
}
|
|
2293
2976
|
}
|
|
2294
2977
|
);
|
|
2978
|
+
server.ws.on(
|
|
2979
|
+
"vite-plugin-vue-mcp-next:performance-record",
|
|
2980
|
+
(payload) => {
|
|
2981
|
+
if (isPerformanceReport2(payload)) {
|
|
2982
|
+
appendPerformanceReport(ctx, payload);
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
);
|
|
2295
2986
|
const port = String(server.config.server.port || 5173);
|
|
2296
2987
|
const mcpSseUrl = `http://${options.host}:${port}${options.mcpPath}/sse`;
|
|
2297
2988
|
const mcpStreamableHttpUrl = `http://${options.host}:${port}${options.mcpPath}/mcp`;
|
|
@@ -2335,7 +3026,7 @@ function isRuntimePageTarget(payload) {
|
|
|
2335
3026
|
return false;
|
|
2336
3027
|
}
|
|
2337
3028
|
const target = payload;
|
|
2338
|
-
return target.source === "runtime" && typeof target.pageId === "string" && typeof target.url === "string" && typeof target.pathname === "string" && typeof target.connected === "boolean";
|
|
3029
|
+
return target.source === "runtime" && typeof target.pageId === "string" && typeof target.url === "string" && typeof target.pathname === "string" && typeof target.connected === "boolean" && (target.runtimeClientId === void 0 || typeof target.runtimeClientId === "string");
|
|
2339
3030
|
}
|
|
2340
3031
|
function isConsoleRecord(payload) {
|
|
2341
3032
|
if (!payload || typeof payload !== "object") {
|
|
@@ -2354,6 +3045,13 @@ function isNetworkRecord(payload) {
|
|
|
2354
3045
|
const record = payload;
|
|
2355
3046
|
return typeof record.id === "string" && typeof record.pageId === "string" && record.source === "hook" && typeof record.url === "string" && typeof record.method === "string" && typeof record.startedAt === "number";
|
|
2356
3047
|
}
|
|
3048
|
+
function isPerformanceReport2(payload) {
|
|
3049
|
+
if (!payload || typeof payload !== "object") {
|
|
3050
|
+
return false;
|
|
3051
|
+
}
|
|
3052
|
+
const report = payload;
|
|
3053
|
+
return typeof report.recordingId === "string" && typeof report.pageId === "string" && (report.source === "hook" || report.source === "cdp") && Boolean(report.summary) && Array.isArray(report.longTasks) && Array.isArray(report.limitations);
|
|
3054
|
+
}
|
|
2357
3055
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2358
3056
|
0 && (module.exports = {
|
|
2359
3057
|
vueMcpNext
|