@xiaou66/vite-plugin-vue-mcp-next 0.0.6 → 0.0.8
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 +140 -92
- package/dist/index.cjs +272 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +266 -33
- package/dist/index.js.map +1 -1
- package/dist/runtime/client.cjs +31 -13
- package/dist/runtime/client.cjs.map +1 -1
- package/dist/runtime/client.d.cts +23 -1
- package/dist/runtime/client.d.ts +23 -1
- package/dist/runtime/client.js +30 -3
- package/dist/runtime/client.js.map +1 -1
- package/package.json +8 -2
package/dist/index.cjs
CHANGED
|
@@ -54,8 +54,10 @@ var DEFAULT_MASK_HEADERS = [
|
|
|
54
54
|
// src/constants.ts
|
|
55
55
|
var DEFAULT_MCP_PATH = "/__mcp";
|
|
56
56
|
var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
|
|
57
|
+
var DEFAULT_SCREENSHOT_SAVE_DIR = ".vite-mcp/screenshot";
|
|
57
58
|
var MCP_TOOL_NAMES = {
|
|
58
59
|
listPages: "list_pages",
|
|
60
|
+
reloadPage: "reload_page",
|
|
59
61
|
getPageState: "get_page_state",
|
|
60
62
|
getDomTree: "get_dom_tree",
|
|
61
63
|
queryDom: "query_dom",
|
|
@@ -78,7 +80,10 @@ var VIRTUAL_RUNTIME_ID = "virtual:vite-plugin-vue-mcp-next/runtime";
|
|
|
78
80
|
var RESOLVED_VIRTUAL_RUNTIME_ID = `\0${VIRTUAL_RUNTIME_ID}`;
|
|
79
81
|
var VIRTUAL_SCREENSHOT_CONFIG_ID = "virtual:vite-plugin-vue-mcp-next/screenshot-config";
|
|
80
82
|
var RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID = `\0${VIRTUAL_SCREENSHOT_CONFIG_ID}`;
|
|
83
|
+
var VIRTUAL_SNAPDOM_LOADER_ID = "virtual:vite-plugin-vue-mcp-next/snapdom-loader";
|
|
84
|
+
var RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID = `\0${VIRTUAL_SNAPDOM_LOADER_ID}`;
|
|
81
85
|
var DEFAULT_MCP_CLIENT_SERVER_NAME = "vue-mcp-next";
|
|
86
|
+
var RUNTIME_PAGE_RECONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-reconnected";
|
|
82
87
|
var DEFAULT_OPTIONS = {
|
|
83
88
|
mcpPath: DEFAULT_MCP_PATH,
|
|
84
89
|
host: "localhost",
|
|
@@ -119,6 +124,8 @@ var DEFAULT_OPTIONS = {
|
|
|
119
124
|
maxRecords: DEFAULT_CONSOLE_MAX_RECORDS
|
|
120
125
|
},
|
|
121
126
|
screenshot: {
|
|
127
|
+
type: "path",
|
|
128
|
+
saveDir: DEFAULT_SCREENSHOT_SAVE_DIR,
|
|
122
129
|
prefer: "auto",
|
|
123
130
|
maxBytes: DEFAULT_SCREENSHOT_MAX_BYTES,
|
|
124
131
|
snapdom: {
|
|
@@ -648,6 +655,9 @@ function registerNetworkTools(server, ctx) {
|
|
|
648
655
|
);
|
|
649
656
|
}
|
|
650
657
|
|
|
658
|
+
// src/mcp/tools/pages.ts
|
|
659
|
+
var import_zod5 = require("zod");
|
|
660
|
+
|
|
651
661
|
// src/plugin/entryDiscovery.ts
|
|
652
662
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
653
663
|
var import_node_path = __toESM(require("path"), 1);
|
|
@@ -699,6 +709,118 @@ function registerPageTools(server, ctx, vite) {
|
|
|
699
709
|
});
|
|
700
710
|
}
|
|
701
711
|
);
|
|
712
|
+
server.registerTool(
|
|
713
|
+
MCP_TOOL_NAMES.reloadPage,
|
|
714
|
+
{
|
|
715
|
+
description: "Reload the selected page. CDP uses ignoreCache; Runtime Hook falls back to normal reload.",
|
|
716
|
+
inputSchema: {
|
|
717
|
+
pageId: import_zod5.z.string().optional(),
|
|
718
|
+
ignoreCache: import_zod5.z.boolean().optional()
|
|
719
|
+
}
|
|
720
|
+
},
|
|
721
|
+
async (input) => {
|
|
722
|
+
if (hasCdpConfig(ctx)) {
|
|
723
|
+
return reloadPageWithCdp(ctx, input.pageId, input.ignoreCache ?? true);
|
|
724
|
+
}
|
|
725
|
+
const target = resolveRuntimeReloadTarget(ctx, input.pageId);
|
|
726
|
+
if (!target.ok) {
|
|
727
|
+
return createToolError(target.error);
|
|
728
|
+
}
|
|
729
|
+
const reconnect = waitForRuntimePageReconnect(ctx);
|
|
730
|
+
ctx.pages.disconnect(target.page.pageId);
|
|
731
|
+
const result = await requestRuntimeData(ctx, (event) => {
|
|
732
|
+
void ctx.rpcServer?.reloadPage({ event });
|
|
733
|
+
});
|
|
734
|
+
if (!isRecord(result) || result.ok === false) {
|
|
735
|
+
reconnect.cancel();
|
|
736
|
+
return createToolResponse(
|
|
737
|
+
isRecord(result) ? result : { ok: false, error: "Invalid runtime reload response" }
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
const page = await reconnect.promise;
|
|
741
|
+
return createToolResponse(
|
|
742
|
+
page ? { ...result, reconnected: true, pageId: page.pageId, page } : {
|
|
743
|
+
...result,
|
|
744
|
+
reconnected: false,
|
|
745
|
+
error: "runtime page reconnect timed out"
|
|
746
|
+
}
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
function hasCdpConfig(ctx) {
|
|
752
|
+
return Boolean(ctx.options.cdp.browserUrl || ctx.options.cdp.wsEndpoint);
|
|
753
|
+
}
|
|
754
|
+
function isRecord(value) {
|
|
755
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
756
|
+
}
|
|
757
|
+
function resolveRuntimeReloadTarget(ctx, pageId) {
|
|
758
|
+
try {
|
|
759
|
+
const page = resolvePageTarget(ctx, pageId);
|
|
760
|
+
if (page.source !== "runtime") {
|
|
761
|
+
return {
|
|
762
|
+
ok: false,
|
|
763
|
+
error: "Runtime reload requires a runtime page target"
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
return { ok: true, page };
|
|
767
|
+
} catch (error) {
|
|
768
|
+
return {
|
|
769
|
+
ok: false,
|
|
770
|
+
error: error instanceof Error ? error.message : String(error)
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function waitForRuntimePageReconnect(ctx) {
|
|
775
|
+
let timeout;
|
|
776
|
+
let cleanup;
|
|
777
|
+
const promise = new Promise((resolve) => {
|
|
778
|
+
timeout = setTimeout(() => {
|
|
779
|
+
cleanup?.();
|
|
780
|
+
resolve(null);
|
|
781
|
+
}, 5e3);
|
|
782
|
+
cleanup = ctx.hooks.hookOnce(RUNTIME_PAGE_RECONNECTED_EVENT, (payload) => {
|
|
783
|
+
if (timeout) {
|
|
784
|
+
clearTimeout(timeout);
|
|
785
|
+
}
|
|
786
|
+
resolve(isPageTarget(payload) ? payload : null);
|
|
787
|
+
});
|
|
788
|
+
});
|
|
789
|
+
return {
|
|
790
|
+
promise,
|
|
791
|
+
cancel() {
|
|
792
|
+
if (timeout) {
|
|
793
|
+
clearTimeout(timeout);
|
|
794
|
+
}
|
|
795
|
+
cleanup?.();
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
function isPageTarget(value) {
|
|
800
|
+
if (!isRecord(value)) {
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
return typeof value.pageId === "string" && (value.source === "runtime" || value.source === "cdp") && typeof value.url === "string" && typeof value.pathname === "string" && typeof value.connected === "boolean";
|
|
804
|
+
}
|
|
805
|
+
async function reloadPageWithCdp(ctx, pageId, ignoreCache) {
|
|
806
|
+
const cdp = await connectCdpForPage(ctx, pageId);
|
|
807
|
+
if (!cdp) {
|
|
808
|
+
return createToolError("CDP target is unavailable for page reload");
|
|
809
|
+
}
|
|
810
|
+
try {
|
|
811
|
+
await cdp.client.Page.enable();
|
|
812
|
+
const loaded = cdp.client.Page.loadEventFired();
|
|
813
|
+
await cdp.client.Page.reload({ ignoreCache });
|
|
814
|
+
await loaded;
|
|
815
|
+
return createToolResponse({
|
|
816
|
+
ok: true,
|
|
817
|
+
source: "cdp",
|
|
818
|
+
ignoreCache,
|
|
819
|
+
pageId
|
|
820
|
+
});
|
|
821
|
+
} finally {
|
|
822
|
+
await closeCdpClient(cdp.client);
|
|
823
|
+
}
|
|
702
824
|
}
|
|
703
825
|
async function listCdpPageTargets(ctx) {
|
|
704
826
|
if (ctx.options.cdp.wsEndpoint) {
|
|
@@ -745,7 +867,7 @@ function getPathname(url) {
|
|
|
745
867
|
}
|
|
746
868
|
|
|
747
869
|
// src/mcp/tools/screenshot.ts
|
|
748
|
-
var
|
|
870
|
+
var import_zod6 = require("zod");
|
|
749
871
|
|
|
750
872
|
// src/cdp/cdpScreenshot.ts
|
|
751
873
|
async function cdpCaptureScreenshot(options) {
|
|
@@ -849,18 +971,70 @@ function isElementRect(value) {
|
|
|
849
971
|
return typeof rect.x === "number" && typeof rect.y === "number" && typeof rect.width === "number" && rect.width > 0 && typeof rect.height === "number" && rect.height > 0;
|
|
850
972
|
}
|
|
851
973
|
|
|
974
|
+
// src/mcp/tools/screenshotOutput.ts
|
|
975
|
+
var import_node_crypto = require("crypto");
|
|
976
|
+
var import_promises = require("fs/promises");
|
|
977
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
978
|
+
async function createScreenshotOutput(ctx, payload) {
|
|
979
|
+
if (ctx.options.screenshot.type === "base64") {
|
|
980
|
+
return payload;
|
|
981
|
+
}
|
|
982
|
+
const saveDir = resolveScreenshotSaveDir(ctx);
|
|
983
|
+
await (0, import_promises.mkdir)(saveDir, { recursive: true });
|
|
984
|
+
const filePath = import_node_path2.default.join(saveDir, createScreenshotFileName(payload));
|
|
985
|
+
await (0, import_promises.writeFile)(filePath, Buffer.from(payload.data, "base64"));
|
|
986
|
+
return {
|
|
987
|
+
source: payload.source,
|
|
988
|
+
target: payload.target,
|
|
989
|
+
format: payload.format,
|
|
990
|
+
width: payload.width,
|
|
991
|
+
height: payload.height,
|
|
992
|
+
mimeType: payload.mimeType,
|
|
993
|
+
byteLength: payload.byteLength,
|
|
994
|
+
limitations: payload.limitations,
|
|
995
|
+
path: filePath,
|
|
996
|
+
relativePath: createProjectRelativePath(ctx, filePath)
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
function resolveScreenshotSaveDir(ctx) {
|
|
1000
|
+
const saveDir = ctx.options.screenshot.saveDir.trim();
|
|
1001
|
+
if (!saveDir) {
|
|
1002
|
+
throw new Error("screenshot.saveDir must be a non-empty string");
|
|
1003
|
+
}
|
|
1004
|
+
const root = ctx.server?.config.root;
|
|
1005
|
+
if (!root) {
|
|
1006
|
+
throw new Error("Vite server root is required for screenshot path output");
|
|
1007
|
+
}
|
|
1008
|
+
if (import_node_path2.default.isAbsolute(saveDir)) {
|
|
1009
|
+
return saveDir;
|
|
1010
|
+
}
|
|
1011
|
+
return import_node_path2.default.resolve(root, saveDir);
|
|
1012
|
+
}
|
|
1013
|
+
function createScreenshotFileName(payload) {
|
|
1014
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1015
|
+
const suffix = (0, import_node_crypto.randomUUID)().slice(0, 8);
|
|
1016
|
+
return `${timestamp}-${payload.source}-${payload.target}-${suffix}.${payload.format}`;
|
|
1017
|
+
}
|
|
1018
|
+
function createProjectRelativePath(ctx, filePath) {
|
|
1019
|
+
const root = ctx.server?.config.root;
|
|
1020
|
+
if (!root) {
|
|
1021
|
+
throw new Error("Vite server root is required for screenshot path output");
|
|
1022
|
+
}
|
|
1023
|
+
return import_node_path2.default.relative(root, filePath).split(import_node_path2.default.sep).join("/");
|
|
1024
|
+
}
|
|
1025
|
+
|
|
852
1026
|
// src/mcp/tools/screenshot.ts
|
|
853
1027
|
var DEFAULT_SCREENSHOT_TARGET = "viewport";
|
|
854
1028
|
var DEFAULT_SCREENSHOT_FORMAT = "png";
|
|
855
1029
|
var screenshotInputSchema = {
|
|
856
|
-
pageId:
|
|
857
|
-
target:
|
|
858
|
-
selector:
|
|
859
|
-
format:
|
|
860
|
-
prefer:
|
|
861
|
-
quality:
|
|
862
|
-
scale:
|
|
863
|
-
snapdom:
|
|
1030
|
+
pageId: import_zod6.z.string().optional(),
|
|
1031
|
+
target: import_zod6.z.enum(["viewport", "fullPage", "element"]).optional(),
|
|
1032
|
+
selector: import_zod6.z.string().optional(),
|
|
1033
|
+
format: import_zod6.z.enum(["png", "jpeg", "webp"]).optional(),
|
|
1034
|
+
prefer: import_zod6.z.enum(["auto", "cdp", "runtime"]).optional(),
|
|
1035
|
+
quality: import_zod6.z.number().optional(),
|
|
1036
|
+
scale: import_zod6.z.number().optional(),
|
|
1037
|
+
snapdom: import_zod6.z.record(import_zod6.z.string(), import_zod6.z.unknown()).optional()
|
|
864
1038
|
};
|
|
865
1039
|
function registerScreenshotTools(server, ctx) {
|
|
866
1040
|
server.registerTool(
|
|
@@ -890,7 +1064,7 @@ async function handleTakeScreenshot(ctx, input) {
|
|
|
890
1064
|
format,
|
|
891
1065
|
quality: input.quality
|
|
892
1066
|
});
|
|
893
|
-
return createScreenshotResponse(ctx, {
|
|
1067
|
+
return await createScreenshotResponse(ctx, {
|
|
894
1068
|
source: "cdp",
|
|
895
1069
|
target,
|
|
896
1070
|
format,
|
|
@@ -936,15 +1110,30 @@ async function createRuntimeScreenshot(ctx, input, normalized) {
|
|
|
936
1110
|
if (!isPlainRecord(result)) {
|
|
937
1111
|
return createToolError("runtime screenshot returned an invalid response");
|
|
938
1112
|
}
|
|
939
|
-
|
|
1113
|
+
if (result.ok === false) {
|
|
1114
|
+
return createToolResponse(result);
|
|
1115
|
+
}
|
|
1116
|
+
if (!isScreenshotImagePayload(result)) {
|
|
1117
|
+
return createToolError("runtime screenshot returned an invalid response");
|
|
1118
|
+
}
|
|
1119
|
+
return createScreenshotResponse(ctx, {
|
|
1120
|
+
...result,
|
|
1121
|
+
target: normalized.target,
|
|
1122
|
+
format: normalized.format
|
|
1123
|
+
});
|
|
940
1124
|
}
|
|
941
|
-
function createScreenshotResponse(ctx, result) {
|
|
1125
|
+
async function createScreenshotResponse(ctx, result) {
|
|
942
1126
|
if (result.byteLength > ctx.options.screenshot.maxBytes) {
|
|
943
1127
|
return createToolError(
|
|
944
1128
|
`screenshot is too large: ${String(result.byteLength)} bytes`
|
|
945
1129
|
);
|
|
946
1130
|
}
|
|
947
|
-
|
|
1131
|
+
try {
|
|
1132
|
+
return createToolResponse(await createScreenshotOutput(ctx, result));
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1135
|
+
return createToolError(`failed to create screenshot output: ${message}`);
|
|
1136
|
+
}
|
|
948
1137
|
}
|
|
949
1138
|
function isScreenshotTooLarge(ctx, result) {
|
|
950
1139
|
return isPlainRecord(result) && "byteLength" in result && typeof result.byteLength === "number" && result.byteLength > ctx.options.screenshot.maxBytes;
|
|
@@ -959,10 +1148,13 @@ function getBase64ByteLength(data) {
|
|
|
959
1148
|
const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
|
|
960
1149
|
return Math.ceil(data.length * 3 / 4) - padding;
|
|
961
1150
|
}
|
|
1151
|
+
function isScreenshotImagePayload(result) {
|
|
1152
|
+
return (result.source === "cdp" || result.source === "snapdom") && typeof result.data === "string" && typeof result.width === "number" && typeof result.height === "number" && typeof result.mimeType === "string" && typeof result.byteLength === "number";
|
|
1153
|
+
}
|
|
962
1154
|
|
|
963
1155
|
// src/mcp/tools/vue.ts
|
|
964
1156
|
var import_nanoid2 = require("nanoid");
|
|
965
|
-
var
|
|
1157
|
+
var import_zod7 = require("zod");
|
|
966
1158
|
function registerVueTools(server, ctx) {
|
|
967
1159
|
server.registerTool(
|
|
968
1160
|
MCP_TOOL_NAMES.getComponentTree,
|
|
@@ -975,7 +1167,7 @@ function registerVueTools(server, ctx) {
|
|
|
975
1167
|
MCP_TOOL_NAMES.getComponentState,
|
|
976
1168
|
{
|
|
977
1169
|
description: "Get Vue component state.",
|
|
978
|
-
inputSchema: { componentName:
|
|
1170
|
+
inputSchema: { componentName: import_zod7.z.string() }
|
|
979
1171
|
},
|
|
980
1172
|
async ({ componentName }) => requestVueData(ctx, (event) => {
|
|
981
1173
|
void ctx.rpcServer?.getInspectorState({ event, componentName });
|
|
@@ -986,19 +1178,19 @@ function registerVueTools(server, ctx) {
|
|
|
986
1178
|
{
|
|
987
1179
|
description: "Edit Vue component state.",
|
|
988
1180
|
inputSchema: {
|
|
989
|
-
componentName:
|
|
990
|
-
path:
|
|
991
|
-
value:
|
|
992
|
-
valueType:
|
|
1181
|
+
componentName: import_zod7.z.string(),
|
|
1182
|
+
path: import_zod7.z.array(import_zod7.z.string()),
|
|
1183
|
+
value: import_zod7.z.string(),
|
|
1184
|
+
valueType: import_zod7.z.enum(["string", "number", "boolean", "object", "array"])
|
|
993
1185
|
}
|
|
994
1186
|
},
|
|
995
|
-
({ componentName, path:
|
|
1187
|
+
({ componentName, path: path6, value, valueType }) => {
|
|
996
1188
|
if (!ctx.rpcServer) {
|
|
997
1189
|
return vueBridgeUnavailable();
|
|
998
1190
|
}
|
|
999
1191
|
void ctx.rpcServer.editComponentState({
|
|
1000
1192
|
componentName,
|
|
1001
|
-
path:
|
|
1193
|
+
path: path6,
|
|
1002
1194
|
value,
|
|
1003
1195
|
valueType
|
|
1004
1196
|
});
|
|
@@ -1009,7 +1201,7 @@ function registerVueTools(server, ctx) {
|
|
|
1009
1201
|
MCP_TOOL_NAMES.highlightComponent,
|
|
1010
1202
|
{
|
|
1011
1203
|
description: "Highlight a Vue component.",
|
|
1012
|
-
inputSchema: { componentName:
|
|
1204
|
+
inputSchema: { componentName: import_zod7.z.string() }
|
|
1013
1205
|
},
|
|
1014
1206
|
({ componentName }) => {
|
|
1015
1207
|
if (!ctx.rpcServer) {
|
|
@@ -1037,7 +1229,7 @@ function registerVueTools(server, ctx) {
|
|
|
1037
1229
|
MCP_TOOL_NAMES.getPiniaState,
|
|
1038
1230
|
{
|
|
1039
1231
|
description: "Get Pinia store state.",
|
|
1040
|
-
inputSchema: { storeName:
|
|
1232
|
+
inputSchema: { storeName: import_zod7.z.string() }
|
|
1041
1233
|
},
|
|
1042
1234
|
async ({ storeName }) => requestVueData(ctx, (event) => {
|
|
1043
1235
|
void ctx.rpcServer?.getPiniaState({ event, storeName });
|
|
@@ -1159,6 +1351,10 @@ function createServerVueRuntimeRpc(ctx) {
|
|
|
1159
1351
|
onDomQueryUpdated: (event, data) => {
|
|
1160
1352
|
void ctx.hooks.callHook(event, data);
|
|
1161
1353
|
},
|
|
1354
|
+
reloadPage: () => void 0,
|
|
1355
|
+
onPageReloaded: (event, data) => {
|
|
1356
|
+
void ctx.hooks.callHook(event, data);
|
|
1357
|
+
},
|
|
1162
1358
|
evaluateScript: () => void 0,
|
|
1163
1359
|
onEvaluateScriptUpdated: (event, data) => {
|
|
1164
1360
|
void ctx.hooks.callHook(event, data);
|
|
@@ -1452,6 +1648,8 @@ async function startCdpObservers(ctx, target, client) {
|
|
|
1452
1648
|
}
|
|
1453
1649
|
|
|
1454
1650
|
// src/plugin/injectRuntime.ts
|
|
1651
|
+
var import_node_module = require("module");
|
|
1652
|
+
var import_node_path3 = require("path");
|
|
1455
1653
|
function createRuntimeInjectionController(options, getConfig) {
|
|
1456
1654
|
return {
|
|
1457
1655
|
resolveId(importee) {
|
|
@@ -1461,6 +1659,9 @@ function createRuntimeInjectionController(options, getConfig) {
|
|
|
1461
1659
|
if (importee === VIRTUAL_SCREENSHOT_CONFIG_ID) {
|
|
1462
1660
|
return RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID;
|
|
1463
1661
|
}
|
|
1662
|
+
if (importee === VIRTUAL_SNAPDOM_LOADER_ID) {
|
|
1663
|
+
return RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID;
|
|
1664
|
+
}
|
|
1464
1665
|
return void 0;
|
|
1465
1666
|
},
|
|
1466
1667
|
load(id) {
|
|
@@ -1470,6 +1671,9 @@ function createRuntimeInjectionController(options, getConfig) {
|
|
|
1470
1671
|
if (id === RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID) {
|
|
1471
1672
|
return createScreenshotConfigModule(options);
|
|
1472
1673
|
}
|
|
1674
|
+
if (id === RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID) {
|
|
1675
|
+
return createSnapdomLoaderModule(getConfig()?.root);
|
|
1676
|
+
}
|
|
1473
1677
|
return void 0;
|
|
1474
1678
|
},
|
|
1475
1679
|
transformIndexHtml(html) {
|
|
@@ -1507,12 +1711,39 @@ ${code}`;
|
|
|
1507
1711
|
}
|
|
1508
1712
|
function createRuntimeModule() {
|
|
1509
1713
|
return [
|
|
1510
|
-
"import { setScreenshotModuleRegistry, startRuntimeClient } from '@xiaou66/vite-plugin-vue-mcp-next/runtime/client';",
|
|
1714
|
+
"import { setScreenshotModuleRegistry, setSnapdomLoader, startRuntimeClient } from '@xiaou66/vite-plugin-vue-mcp-next/runtime/client';",
|
|
1511
1715
|
`import { screenshotModuleRegistry } from '${VIRTUAL_SCREENSHOT_CONFIG_ID}';`,
|
|
1716
|
+
`import { loadSnapdom } from '${VIRTUAL_SNAPDOM_LOADER_ID}';`,
|
|
1512
1717
|
"setScreenshotModuleRegistry(screenshotModuleRegistry);",
|
|
1718
|
+
"setSnapdomLoader(loadSnapdom);",
|
|
1513
1719
|
"void startRuntimeClient();"
|
|
1514
1720
|
].join("\n");
|
|
1515
1721
|
}
|
|
1722
|
+
function createSnapdomLoaderModule(root) {
|
|
1723
|
+
if (!canResolveSnapdomFromProject(root)) {
|
|
1724
|
+
return [
|
|
1725
|
+
"export const loadSnapdom = () =>",
|
|
1726
|
+
` Promise.reject(new Error(${JSON.stringify(createMissingSnapdomMessage())}));`
|
|
1727
|
+
].join("\n");
|
|
1728
|
+
}
|
|
1729
|
+
return [
|
|
1730
|
+
"import { snapdom } from '@zumer/snapdom';",
|
|
1731
|
+
"export const loadSnapdom = () => Promise.resolve({ snapdom });"
|
|
1732
|
+
].join("\n");
|
|
1733
|
+
}
|
|
1734
|
+
function canResolveSnapdomFromProject(root) {
|
|
1735
|
+
try {
|
|
1736
|
+
(0, import_node_module.createRequire)((0, import_node_path3.join)(root ?? process.cwd(), "package.json")).resolve(
|
|
1737
|
+
"@zumer/snapdom"
|
|
1738
|
+
);
|
|
1739
|
+
return true;
|
|
1740
|
+
} catch {
|
|
1741
|
+
return false;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
function createMissingSnapdomMessage() {
|
|
1745
|
+
return "\u7F3A\u5C11\u53EF\u9009\u4F9D\u8D56 @zumer/snapdom\u3002DOM \u622A\u56FE\u964D\u7EA7\u9700\u8981\u8BE5\u4F9D\u8D56\uFF0C\u8BF7\u6267\u884C\uFF1Apnpm add -D @zumer/snapdom";
|
|
1746
|
+
}
|
|
1516
1747
|
function createScreenshotConfigModule(options) {
|
|
1517
1748
|
const paths = collectScreenshotImportPaths(options);
|
|
1518
1749
|
const imports = paths.map((item, index) => `import * as m${String(index)} from ${JSON.stringify(item)};`).join("\n");
|
|
@@ -1541,17 +1772,17 @@ function getPluginPath(plugin) {
|
|
|
1541
1772
|
}
|
|
1542
1773
|
|
|
1543
1774
|
// src/plugin/mcpClientConfig/index.ts
|
|
1544
|
-
var
|
|
1775
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
1545
1776
|
|
|
1546
1777
|
// src/plugin/mcpClientConfig/codexConfig.ts
|
|
1547
|
-
var
|
|
1548
|
-
var
|
|
1778
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
1779
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
1549
1780
|
async function updateCodexMcpClientConfig(options) {
|
|
1550
1781
|
try {
|
|
1551
1782
|
const current = await readOptionalTextFile(options.configPath);
|
|
1552
1783
|
const next = replaceOrAppendOwnedBlock(current, options);
|
|
1553
|
-
await
|
|
1554
|
-
await
|
|
1784
|
+
await import_promises2.default.mkdir(import_node_path4.default.dirname(options.configPath), { recursive: true });
|
|
1785
|
+
await import_promises2.default.writeFile(options.configPath, next);
|
|
1555
1786
|
} catch (error) {
|
|
1556
1787
|
console.warn(
|
|
1557
1788
|
`[vite-plugin-vue-mcp-next] Failed to update Codex MCP config at ${options.configPath}: ${formatError(error)}`
|
|
@@ -1593,7 +1824,7 @@ function quoteTomlString(value) {
|
|
|
1593
1824
|
}
|
|
1594
1825
|
async function readOptionalTextFile(filePath) {
|
|
1595
1826
|
try {
|
|
1596
|
-
return await
|
|
1827
|
+
return await import_promises2.default.readFile(filePath, "utf-8");
|
|
1597
1828
|
} catch (error) {
|
|
1598
1829
|
if (isNodeError(error) && error.code === "ENOENT") {
|
|
1599
1830
|
return "";
|
|
@@ -1619,8 +1850,8 @@ function isNodeError(error) {
|
|
|
1619
1850
|
}
|
|
1620
1851
|
|
|
1621
1852
|
// src/plugin/mcpClientConfig/jsonConfig.ts
|
|
1622
|
-
var
|
|
1623
|
-
var
|
|
1853
|
+
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
1854
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
1624
1855
|
async function updateJsonMcpClientConfig(options) {
|
|
1625
1856
|
try {
|
|
1626
1857
|
const config = await readJsonConfig(options.configPath);
|
|
@@ -1634,8 +1865,8 @@ async function updateJsonMcpClientConfig(options) {
|
|
|
1634
1865
|
}
|
|
1635
1866
|
mcpServers[options.serverName] = { type: "sse", url: options.mcpUrl };
|
|
1636
1867
|
config.mcpServers = mcpServers;
|
|
1637
|
-
await
|
|
1638
|
-
await
|
|
1868
|
+
await import_promises3.default.mkdir(import_node_path5.default.dirname(options.configPath), { recursive: true });
|
|
1869
|
+
await import_promises3.default.writeFile(
|
|
1639
1870
|
options.configPath,
|
|
1640
1871
|
`${JSON.stringify(config, null, 2)}
|
|
1641
1872
|
`
|
|
@@ -1653,7 +1884,7 @@ async function readJsonConfig(configPath) {
|
|
|
1653
1884
|
}
|
|
1654
1885
|
async function readOptionalTextFile2(filePath) {
|
|
1655
1886
|
try {
|
|
1656
|
-
return await
|
|
1887
|
+
return await import_promises3.default.readFile(filePath, "utf-8");
|
|
1657
1888
|
} catch (error) {
|
|
1658
1889
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
1659
1890
|
return "{}";
|
|
@@ -1684,7 +1915,7 @@ async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options)
|
|
|
1684
1915
|
jobs.push(
|
|
1685
1916
|
updateJsonMcpClientConfig({
|
|
1686
1917
|
clientName: "Cursor",
|
|
1687
|
-
configPath:
|
|
1918
|
+
configPath: import_node_path6.default.join(root, ".cursor", "mcp.json"),
|
|
1688
1919
|
mcpUrl: sseUrl,
|
|
1689
1920
|
serverName
|
|
1690
1921
|
})
|
|
@@ -1693,7 +1924,7 @@ async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options)
|
|
|
1693
1924
|
if (options.codex) {
|
|
1694
1925
|
jobs.push(
|
|
1695
1926
|
updateCodexMcpClientConfig({
|
|
1696
|
-
configPath:
|
|
1927
|
+
configPath: import_node_path6.default.join(root, ".codex", "config.toml"),
|
|
1697
1928
|
mcpUrl: streamableHttpUrl,
|
|
1698
1929
|
serverName
|
|
1699
1930
|
})
|
|
@@ -1703,7 +1934,7 @@ async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options)
|
|
|
1703
1934
|
jobs.push(
|
|
1704
1935
|
updateJsonMcpClientConfig({
|
|
1705
1936
|
clientName: "Claude Code",
|
|
1706
|
-
configPath:
|
|
1937
|
+
configPath: import_node_path6.default.join(root, ".mcp.json"),
|
|
1707
1938
|
mcpUrl: sseUrl,
|
|
1708
1939
|
serverName
|
|
1709
1940
|
})
|
|
@@ -1713,7 +1944,7 @@ async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options)
|
|
|
1713
1944
|
jobs.push(
|
|
1714
1945
|
updateJsonMcpClientConfig({
|
|
1715
1946
|
clientName: "Trae",
|
|
1716
|
-
configPath:
|
|
1947
|
+
configPath: import_node_path6.default.join(root, ".trae", "mcp.json"),
|
|
1717
1948
|
mcpUrl: sseUrl,
|
|
1718
1949
|
serverName
|
|
1719
1950
|
})
|
|
@@ -1742,6 +1973,7 @@ function vueMcpNext(userOptions = {}) {
|
|
|
1742
1973
|
config = resolvedConfig;
|
|
1743
1974
|
},
|
|
1744
1975
|
async configureServer(server) {
|
|
1976
|
+
ctx.server = server;
|
|
1745
1977
|
ctx.rpcServer = (0, import_vite_dev_rpc.createRPCServer)(
|
|
1746
1978
|
"vite-plugin-vue-mcp-next",
|
|
1747
1979
|
server.ws,
|
|
@@ -1760,6 +1992,7 @@ function vueMcpNext(userOptions = {}) {
|
|
|
1760
1992
|
(payload) => {
|
|
1761
1993
|
if (isRuntimePageTarget(payload)) {
|
|
1762
1994
|
ctx.pages.upsert(payload);
|
|
1995
|
+
void ctx.hooks.callHook(RUNTIME_PAGE_RECONNECTED_EVENT, payload);
|
|
1763
1996
|
void cdpLifecycle.connectPage(payload);
|
|
1764
1997
|
}
|
|
1765
1998
|
}
|