vite-plugin-aipanel 1.2.0-beta.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 +72 -0
- package/es/client/vue-devtools-bridge.d.ts +58 -0
- package/es/client/vue-devtools-bridge.mjs +150 -0
- package/es/core/injector.d.ts +2 -0
- package/es/core/injector.mjs +15 -0
- package/es/core/mcp-chrome.d.ts +56 -0
- package/es/core/mcp-chrome.mjs +133 -0
- package/es/core/mcp-proxy.d.ts +23 -0
- package/es/core/mcp-proxy.mjs +212 -0
- package/es/core/mcp-tools.d.ts +15 -0
- package/es/core/mcp-tools.mjs +671 -0
- package/es/core/provider-loader.d.ts +19 -0
- package/es/core/provider-loader.mjs +30 -0
- package/es/core/proxy-server.d.ts +12 -0
- package/es/core/proxy-server.mjs +148 -0
- package/es/core/service.d.ts +39 -0
- package/es/core/service.mjs +267 -0
- package/es/endpoints/context.d.ts +3 -0
- package/es/endpoints/context.mjs +106 -0
- package/es/endpoints/index.d.ts +9 -0
- package/es/endpoints/index.mjs +30 -0
- package/es/endpoints/logs.d.ts +9 -0
- package/es/endpoints/logs.mjs +86 -0
- package/es/endpoints/mcp.d.ts +6 -0
- package/es/endpoints/mcp.mjs +610 -0
- package/es/endpoints/sessions.d.ts +3 -0
- package/es/endpoints/sessions.mjs +62 -0
- package/es/endpoints/sse.d.ts +3 -0
- package/es/endpoints/sse.mjs +44 -0
- package/es/endpoints/start.d.ts +3 -0
- package/es/endpoints/start.mjs +21 -0
- package/es/endpoints/types.d.ts +34 -0
- package/es/endpoints/types.mjs +0 -0
- package/es/endpoints/vue-devtools.d.ts +9 -0
- package/es/endpoints/vue-devtools.mjs +107 -0
- package/es/endpoints/warmup.d.ts +3 -0
- package/es/endpoints/warmup.mjs +42 -0
- package/es/endpoints/widget.d.ts +3 -0
- package/es/endpoints/widget.mjs +36 -0
- package/es/env.d.ts +1 -0
- package/es/index.d.ts +22 -0
- package/es/index.mjs +342 -0
- package/es/utils/format-bridge.d.ts +9 -0
- package/es/utils/format-bridge.mjs +80 -0
- package/es/utils/paths.d.ts +3 -0
- package/es/utils/paths.mjs +45 -0
- package/es/utils/system.d.ts +3 -0
- package/es/utils/system.mjs +76 -0
- package/lib/client/vue-devtools-bridge.cjs +150 -0
- package/lib/client/vue-devtools-bridge.d.ts +58 -0
- package/lib/client.css +2 -0
- package/lib/client.js +6950 -0
- package/lib/core/injector.cjs +34 -0
- package/lib/core/injector.d.ts +2 -0
- package/lib/core/mcp-chrome.cjs +161 -0
- package/lib/core/mcp-chrome.d.ts +56 -0
- package/lib/core/mcp-proxy.cjs +244 -0
- package/lib/core/mcp-proxy.d.ts +23 -0
- package/lib/core/mcp-tools.cjs +694 -0
- package/lib/core/mcp-tools.d.ts +15 -0
- package/lib/core/provider-loader.cjs +53 -0
- package/lib/core/provider-loader.d.ts +19 -0
- package/lib/core/proxy-server.cjs +181 -0
- package/lib/core/proxy-server.d.ts +12 -0
- package/lib/core/service.cjs +289 -0
- package/lib/core/service.d.ts +39 -0
- package/lib/endpoints/context.cjs +129 -0
- package/lib/endpoints/context.d.ts +3 -0
- package/lib/endpoints/index.cjs +58 -0
- package/lib/endpoints/index.d.ts +9 -0
- package/lib/endpoints/logs.cjs +107 -0
- package/lib/endpoints/logs.d.ts +9 -0
- package/lib/endpoints/mcp.cjs +624 -0
- package/lib/endpoints/mcp.d.ts +6 -0
- package/lib/endpoints/sessions.cjs +85 -0
- package/lib/endpoints/sessions.d.ts +3 -0
- package/lib/endpoints/sse.cjs +67 -0
- package/lib/endpoints/sse.d.ts +3 -0
- package/lib/endpoints/start.cjs +44 -0
- package/lib/endpoints/start.d.ts +3 -0
- package/lib/endpoints/types.cjs +15 -0
- package/lib/endpoints/types.d.ts +34 -0
- package/lib/endpoints/vue-devtools.cjs +132 -0
- package/lib/endpoints/vue-devtools.d.ts +9 -0
- package/lib/endpoints/warmup.cjs +65 -0
- package/lib/endpoints/warmup.d.ts +3 -0
- package/lib/endpoints/widget.cjs +69 -0
- package/lib/endpoints/widget.d.ts +3 -0
- package/lib/env.d.ts +1 -0
- package/lib/index.cjs +361 -0
- package/lib/index.d.ts +22 -0
- package/lib/utils/format-bridge.cjs +102 -0
- package/lib/utils/format-bridge.d.ts +9 -0
- package/lib/utils/paths.cjs +80 -0
- package/lib/utils/paths.d.ts +3 -0
- package/lib/utils/system.cjs +110 -0
- package/lib/utils/system.d.ts +3 -0
- package/package.json +68 -0
package/lib/index.cjs
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var lib_exports = {};
|
|
29
|
+
__export(lib_exports, {
|
|
30
|
+
default: () => aipanelPlugin
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(lib_exports);
|
|
33
|
+
var import_crypto = __toESM(require("crypto"));
|
|
34
|
+
var import_fs = __toESM(require("fs"));
|
|
35
|
+
var import_vite = __toESM(require("unplugin-vue-inspector/vite"));
|
|
36
|
+
var import_core = require("@aipanel/core");
|
|
37
|
+
var import_node = require("@aipanel/core/node");
|
|
38
|
+
var import_endpoints = require("./endpoints/index.cjs");
|
|
39
|
+
var import_injector = require("./core/injector.cjs");
|
|
40
|
+
var import_provider_loader = require("./core/provider-loader.cjs");
|
|
41
|
+
var import_service = require("./core/service.cjs");
|
|
42
|
+
var import_mcp_proxy = require("./core/mcp-proxy.cjs");
|
|
43
|
+
var import_paths = require("./utils/paths.cjs");
|
|
44
|
+
var import_system = require("./utils/system.cjs");
|
|
45
|
+
const DEVTOOLS_BRIDGE_IMPORTEE = "virtual:aipanel-vue-devtools-bridge";
|
|
46
|
+
const DEVTOOLS_BRIDGE_QUERY = "aipanel_vue_devtools_bridge";
|
|
47
|
+
const BRIDGE_SOURCE_PATH = (0, import_paths.resolveVueDevtoolsBridgePath)();
|
|
48
|
+
const SILENT_CONTEXT_SCRIPT = `<script>
|
|
49
|
+
(function () {
|
|
50
|
+
var API = "/__aipanel_context__";
|
|
51
|
+
var last = "";
|
|
52
|
+
function report(force) {
|
|
53
|
+
if (document.visibilityState !== "visible") return;
|
|
54
|
+
var url = location.href;
|
|
55
|
+
var title = document.title;
|
|
56
|
+
var key = url + "|" + title;
|
|
57
|
+
if (!force && key === last) return;
|
|
58
|
+
last = key;
|
|
59
|
+
var sessionId = "";
|
|
60
|
+
try { sessionId = sessionStorage.getItem("_aipanel_pk") || ""; } catch (e) {}
|
|
61
|
+
fetch(API, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "Content-Type": "application/json" },
|
|
64
|
+
body: JSON.stringify({ url: url, title: title, sessionId: sessionId }),
|
|
65
|
+
}).catch(function () {});
|
|
66
|
+
}
|
|
67
|
+
var push = history.pushState.bind(history);
|
|
68
|
+
var replace = history.replaceState.bind(history);
|
|
69
|
+
history.pushState = function () { push.apply(this, arguments); setTimeout(function () { report(false); }, 0); };
|
|
70
|
+
history.replaceState = function () { replace.apply(this, arguments); setTimeout(function () { report(false); }, 0); };
|
|
71
|
+
window.addEventListener("popstate", function () { report(false); });
|
|
72
|
+
window.addEventListener("hashchange", function () { report(false); });
|
|
73
|
+
document.addEventListener("visibilitychange", function () {
|
|
74
|
+
// \u5207\u56DE\u672C\u6807\u7B7E\u65F6\u5F3A\u5236\u4E0A\u62A5\uFF1A\u672C\u9875 url/title \u672A\u53D8\uFF0C\u4F46\u670D\u52A1\u7AEF\u4E0A\u4E0B\u6587\u53EF\u80FD\u5DF2\u88AB\u5176\u4ED6\u6807\u7B7E\u8986\u76D6
|
|
75
|
+
if (document.visibilityState === "visible") report(true);
|
|
76
|
+
});
|
|
77
|
+
window.addEventListener("focus", function () {
|
|
78
|
+
// \u591A\u7A97\u53E3\uFF08\u5206\u5C4F/\u591A\u663E\u793A\u5668\uFF09\u4E0B\u6240\u6709\u6807\u7B7E\u9875\u90FD visible\uFF0Cvisibilitychange \u4E0D\u89E6\u53D1\uFF1B
|
|
79
|
+
// \u7A97\u53E3\u83B7\u5F97\u7126\u70B9\u662F"\u7528\u6237\u6B63\u5728\u770B\u8FD9\u4E2A\u9875\u9762"\u7684\u6700\u51C6\u786E\u4FE1\u53F7
|
|
80
|
+
report(true);
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
new MutationObserver(function () { report(false); })
|
|
84
|
+
.observe(document.head, { childList: true, subtree: true });
|
|
85
|
+
} catch (e) {}
|
|
86
|
+
report(false);
|
|
87
|
+
})();
|
|
88
|
+
</script>`;
|
|
89
|
+
function aipanelPlugin(options = {}) {
|
|
90
|
+
const plugins = [];
|
|
91
|
+
plugins.push(
|
|
92
|
+
...(0, import_vite.default)({
|
|
93
|
+
enabled: false,
|
|
94
|
+
toggleButtonVisibility: "never",
|
|
95
|
+
toggleComboKey: false
|
|
96
|
+
})
|
|
97
|
+
);
|
|
98
|
+
plugins.push(createAIPanelPlugin(options));
|
|
99
|
+
return plugins;
|
|
100
|
+
}
|
|
101
|
+
function createAIPanelPlugin(options = {}) {
|
|
102
|
+
const config = (0, import_core.resolvePluginConfig)(options);
|
|
103
|
+
(0, import_core.setVerbose)(config.verbose);
|
|
104
|
+
(0, import_node.initProcessLogCapture)({ maxSize: 500 });
|
|
105
|
+
const log = (0, import_node.createLogger)("Plugin");
|
|
106
|
+
let actualWebPort = config.webPort;
|
|
107
|
+
let actualProxyPort = config.proxyPort ?? import_core.DEFAULT_PROXY_PORT;
|
|
108
|
+
let projectRoot = "";
|
|
109
|
+
let vueDevtoolsApiUrl = "";
|
|
110
|
+
const pageContext = { url: "", title: "" };
|
|
111
|
+
const DEFAULT_TAB = "default";
|
|
112
|
+
const pageContexts = /* @__PURE__ */ new Map([[DEFAULT_TAB, pageContext]]);
|
|
113
|
+
let activeTabId = DEFAULT_TAB;
|
|
114
|
+
const serviceInstanceId = import_crypto.default.randomUUID();
|
|
115
|
+
const sseClients = /* @__PURE__ */ new Set();
|
|
116
|
+
const mcpProxy = new import_mcp_proxy.McpProxy({ idleTimeout: 5 * 60 * 1e3 });
|
|
117
|
+
let provider = null;
|
|
118
|
+
const service = new import_service.AIPanelService(
|
|
119
|
+
config,
|
|
120
|
+
sseClients,
|
|
121
|
+
(port) => {
|
|
122
|
+
actualWebPort = port;
|
|
123
|
+
},
|
|
124
|
+
(port) => {
|
|
125
|
+
actualProxyPort = port;
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
service.workspaceRoot = (0, import_system.findGitRoot)(process.cwd());
|
|
129
|
+
return {
|
|
130
|
+
name: "vite-plugin-aipanel",
|
|
131
|
+
apply(_viteConfig, env) {
|
|
132
|
+
if (!config.enabled) return false;
|
|
133
|
+
return env.command === "serve" && process.env.NODE_ENV !== "test";
|
|
134
|
+
},
|
|
135
|
+
async configureServer(server) {
|
|
136
|
+
const timer = log.timer("configureServer");
|
|
137
|
+
projectRoot = server.config.root;
|
|
138
|
+
let viteOrigin = "";
|
|
139
|
+
(0, import_endpoints.setupMiddlewares)(
|
|
140
|
+
server,
|
|
141
|
+
{
|
|
142
|
+
get webUrl() {
|
|
143
|
+
return actualWebPort ? `http://${config.hostname}:${actualWebPort}` : null;
|
|
144
|
+
},
|
|
145
|
+
get sseClients() {
|
|
146
|
+
return sseClients;
|
|
147
|
+
},
|
|
148
|
+
getPageContext() {
|
|
149
|
+
return pageContexts.get(activeTabId) || pageContexts.get(DEFAULT_TAB) || { url: "", title: "" };
|
|
150
|
+
},
|
|
151
|
+
setPageContext(tabId, ctx) {
|
|
152
|
+
pageContexts.set(tabId || DEFAULT_TAB, ctx);
|
|
153
|
+
},
|
|
154
|
+
setActiveTabId(tabId) {
|
|
155
|
+
activeTabId = tabId;
|
|
156
|
+
},
|
|
157
|
+
clearSelectedElements() {
|
|
158
|
+
const ctx = pageContexts.get(activeTabId);
|
|
159
|
+
if (ctx) {
|
|
160
|
+
ctx.selectedElements = [];
|
|
161
|
+
pageContexts.set(activeTabId, ctx);
|
|
162
|
+
}
|
|
163
|
+
const defaultCtx = pageContexts.get(DEFAULT_TAB);
|
|
164
|
+
if (defaultCtx) {
|
|
165
|
+
defaultCtx.selectedElements = [];
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
get isServiceStarted() {
|
|
169
|
+
return service.isStarted;
|
|
170
|
+
},
|
|
171
|
+
get currentTask() {
|
|
172
|
+
return service.currentTask;
|
|
173
|
+
},
|
|
174
|
+
get actualProxyPort() {
|
|
175
|
+
return actualProxyPort;
|
|
176
|
+
},
|
|
177
|
+
get actualWebPort() {
|
|
178
|
+
return actualWebPort;
|
|
179
|
+
},
|
|
180
|
+
get serviceInstanceId() {
|
|
181
|
+
return serviceInstanceId;
|
|
182
|
+
},
|
|
183
|
+
getSessions: () => {
|
|
184
|
+
if (!provider) return Promise.reject(new Error("Provider \u672A\u521D\u59CB\u5316"));
|
|
185
|
+
return provider.listSessions(service.workspaceRoot);
|
|
186
|
+
},
|
|
187
|
+
createSession: () => {
|
|
188
|
+
if (!provider) return Promise.reject(new Error("Provider \u672A\u521D\u59CB\u5316"));
|
|
189
|
+
return provider.createSession(service.workspaceRoot);
|
|
190
|
+
},
|
|
191
|
+
deleteSession: (id) => {
|
|
192
|
+
if (!provider) return Promise.reject(new Error("Provider \u672A\u521D\u59CB\u5316"));
|
|
193
|
+
return provider.deleteSession ? provider.deleteSession(id) : Promise.reject(new Error("\u5F53\u524D Provider \u4E0D\u652F\u6301\u5220\u9664\u4F1A\u8BDD"));
|
|
194
|
+
},
|
|
195
|
+
getCapabilities: () => provider?.capabilities ?? {},
|
|
196
|
+
resolveWidgetPath: import_paths.resolveWidgetPath,
|
|
197
|
+
resolveWidgetStylePath: import_paths.resolveWidgetStylePath,
|
|
198
|
+
retryWarmupChromeMcp: () => service.retryWarmupChromeMcp()
|
|
199
|
+
},
|
|
200
|
+
mcpProxy,
|
|
201
|
+
config.logFiles
|
|
202
|
+
);
|
|
203
|
+
if (!config.mcpOnly) {
|
|
204
|
+
try {
|
|
205
|
+
provider = await (0, import_provider_loader.loadProvider)(config.provider, {
|
|
206
|
+
hostname: config.hostname,
|
|
207
|
+
chromeDevtoolsPort: config.chromeDevtoolsPort,
|
|
208
|
+
getWebPort: () => actualWebPort,
|
|
209
|
+
getProxyPort: () => actualProxyPort,
|
|
210
|
+
options: config
|
|
211
|
+
});
|
|
212
|
+
} catch (e) {
|
|
213
|
+
log.error("\u52A0\u8F7D Web Provider \u5931\u8D25", {
|
|
214
|
+
provider: config.provider,
|
|
215
|
+
error: e instanceof Error ? e.message : String(e)
|
|
216
|
+
});
|
|
217
|
+
service.currentTask = {
|
|
218
|
+
task: "provider_not_installed",
|
|
219
|
+
data: { error: e instanceof Error ? e.message : String(e) }
|
|
220
|
+
};
|
|
221
|
+
timer.end("\u274C Provider \u52A0\u8F7D\u5931\u8D25");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
provider.applyConfig?.({ theme: config.theme });
|
|
225
|
+
service.setProvider(provider);
|
|
226
|
+
}
|
|
227
|
+
server.httpServer?.on("listening", async () => {
|
|
228
|
+
log.debug("Vite server listening event fired");
|
|
229
|
+
const address = server.httpServer?.address();
|
|
230
|
+
let vitePort;
|
|
231
|
+
let viteHost;
|
|
232
|
+
if (address && typeof address === "object") {
|
|
233
|
+
vitePort = address.port;
|
|
234
|
+
const addr = address.address;
|
|
235
|
+
if (addr === "::" || addr === "::1" || addr === "0.0.0.0" || !addr) {
|
|
236
|
+
viteHost = "localhost";
|
|
237
|
+
} else {
|
|
238
|
+
viteHost = addr;
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
const host = server.config.server.host;
|
|
242
|
+
vitePort = server.config.server.port || 5173;
|
|
243
|
+
viteHost = typeof host === "string" && host !== "0.0.0.0" && host !== "::" && host !== "::1" ? host : "localhost";
|
|
244
|
+
}
|
|
245
|
+
viteOrigin = `http://${viteHost}:${vitePort}`;
|
|
246
|
+
const contextApiUrl = `http://${viteHost}:${vitePort}${import_core.CONTEXT_API_PATH}`;
|
|
247
|
+
const logsApiUrl = `http://${viteHost}:${vitePort}${import_endpoints.LOGS_API_PATH}`;
|
|
248
|
+
vueDevtoolsApiUrl = `http://${viteHost}:${vitePort}${import_endpoints.VUE_DEVTOOLS_API_PATH}`;
|
|
249
|
+
const mcpApiUrl = `http://${viteHost}:${vitePort}${import_core.MCP_API_PATH}`;
|
|
250
|
+
log.debug("Vite server ready", {
|
|
251
|
+
vitePort,
|
|
252
|
+
viteHost,
|
|
253
|
+
viteOrigin,
|
|
254
|
+
contextApiUrl,
|
|
255
|
+
logsApiUrl,
|
|
256
|
+
vueDevtoolsApiUrl,
|
|
257
|
+
mcpApiUrl
|
|
258
|
+
});
|
|
259
|
+
log.info(`MCP endpoint: ${mcpApiUrl}`);
|
|
260
|
+
try {
|
|
261
|
+
await mcpProxy.start();
|
|
262
|
+
if (config.warmupChromeMcp) {
|
|
263
|
+
const warmup = await mcpProxy.verify();
|
|
264
|
+
if (!warmup.ok) {
|
|
265
|
+
log.debug("Chrome MCP warmup failed", { error: warmup.error });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (config.mcpOnly) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
await service.start(
|
|
272
|
+
vitePort,
|
|
273
|
+
[viteOrigin],
|
|
274
|
+
contextApiUrl,
|
|
275
|
+
logsApiUrl,
|
|
276
|
+
viteOrigin,
|
|
277
|
+
mcpProxy,
|
|
278
|
+
vueDevtoolsApiUrl
|
|
279
|
+
);
|
|
280
|
+
} catch (e) {
|
|
281
|
+
log.error("Failed to start services", { error: e });
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
server.httpServer?.on("close", () => {
|
|
285
|
+
log.debug("HTTP server closing");
|
|
286
|
+
service.stop();
|
|
287
|
+
mcpProxy.stop();
|
|
288
|
+
});
|
|
289
|
+
const cleanup = async () => {
|
|
290
|
+
log.debug("Process cleanup triggered");
|
|
291
|
+
mcpProxy.stop();
|
|
292
|
+
await service.stop();
|
|
293
|
+
process.exit(0);
|
|
294
|
+
};
|
|
295
|
+
process.on("SIGINT", cleanup);
|
|
296
|
+
process.on("SIGTERM", cleanup);
|
|
297
|
+
timer.end("\u2713 Server configured");
|
|
298
|
+
},
|
|
299
|
+
resolveId(id) {
|
|
300
|
+
if (id === DEVTOOLS_BRIDGE_IMPORTEE) {
|
|
301
|
+
return `${BRIDGE_SOURCE_PATH}?${DEVTOOLS_BRIDGE_QUERY}`;
|
|
302
|
+
}
|
|
303
|
+
return void 0;
|
|
304
|
+
},
|
|
305
|
+
load(id) {
|
|
306
|
+
const [filePath, query] = id.split("?", 2);
|
|
307
|
+
if (query === DEVTOOLS_BRIDGE_QUERY) {
|
|
308
|
+
return import_fs.default.readFileSync(filePath, "utf-8");
|
|
309
|
+
}
|
|
310
|
+
return void 0;
|
|
311
|
+
},
|
|
312
|
+
transformIndexHtml(html) {
|
|
313
|
+
const timer = log.timer("transformIndexHtml");
|
|
314
|
+
const tags = [
|
|
315
|
+
{
|
|
316
|
+
tag: "script",
|
|
317
|
+
injectTo: "head-prepend",
|
|
318
|
+
attrs: {
|
|
319
|
+
type: "module",
|
|
320
|
+
src: `/@id/${DEVTOOLS_BRIDGE_IMPORTEE}`
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
];
|
|
324
|
+
const titleInject = `<script>
|
|
325
|
+
(function () {
|
|
326
|
+
var KEY = "_aipanel_pk";
|
|
327
|
+
if (!sessionStorage.getItem(KEY)) {
|
|
328
|
+
sessionStorage.setItem(KEY, "[" + Math.random().toString(36).slice(2, 10) + "]");
|
|
329
|
+
}
|
|
330
|
+
})();
|
|
331
|
+
</script>`;
|
|
332
|
+
if (config.mcpOnly) {
|
|
333
|
+
timer.end("\u2713 mcp-only (skip widget bubble, keep vue-devtools bridge + context report)");
|
|
334
|
+
return {
|
|
335
|
+
html: html.replace("</body>", `${titleInject}
|
|
336
|
+
${SILENT_CONTEXT_SCRIPT}</body>`),
|
|
337
|
+
tags
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
const widget = (0, import_injector.injectWidget)({
|
|
341
|
+
theme: config.theme,
|
|
342
|
+
open: config.open,
|
|
343
|
+
hotkey: config.hotkey,
|
|
344
|
+
proxyPort: actualProxyPort,
|
|
345
|
+
proxyHost: config.hostname,
|
|
346
|
+
displayMode: config.displayMode === "extension" ? "extension-selector" : config.displayMode,
|
|
347
|
+
splitMode: config.splitMode,
|
|
348
|
+
serviceInstanceId,
|
|
349
|
+
webPort: actualWebPort,
|
|
350
|
+
projectRoot,
|
|
351
|
+
verbose: config.verbose
|
|
352
|
+
});
|
|
353
|
+
timer.end();
|
|
354
|
+
return {
|
|
355
|
+
html: html.replace("</body>", `${titleInject}
|
|
356
|
+
${widget}</body>`),
|
|
357
|
+
tags
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import type { PluginOptions } from "@aipanel/core";
|
|
3
|
+
import { type ProviderId } from "./core/provider-loader";
|
|
4
|
+
import type { OpenCodeProviderOptions } from "@aipanel/provider-opencode";
|
|
5
|
+
import type { DeepSeekProviderOptions } from "@aipanel/provider-deepseek";
|
|
6
|
+
export type { PluginOptions } from "@aipanel/core";
|
|
7
|
+
export type { OpenCodeProviderOptions } from "@aipanel/provider-opencode";
|
|
8
|
+
export type { DeepSeekProviderOptions } from "@aipanel/provider-deepseek";
|
|
9
|
+
export type { ProviderId };
|
|
10
|
+
/**
|
|
11
|
+
* 编译期映射:provider id → providerOptions schema
|
|
12
|
+
* 与 provider-loader 的运行时包名映射对应;未知 provider 回退为宽松对象。
|
|
13
|
+
*/
|
|
14
|
+
type ProviderOptionsSchema<ID extends ProviderId> = ID extends "default" | "opencode" ? OpenCodeProviderOptions : ID extends "deepseek" ? DeepSeekProviderOptions : Record<string, unknown>;
|
|
15
|
+
/**
|
|
16
|
+
* AIPanel Vite 插件
|
|
17
|
+
* provider 字段收窄为已登记的 ProviderId 联合类型(拼写错误在编译期报错);
|
|
18
|
+
* providerOptions 的类型根据 options.provider 自动推断(未配置时默认 "default" → opencode)。
|
|
19
|
+
*/
|
|
20
|
+
export default function aipanelPlugin<const P extends ProviderId = "default">(options?: Omit<PluginOptions<ProviderOptionsSchema<P>>, "provider"> & {
|
|
21
|
+
provider?: P;
|
|
22
|
+
}): Plugin[];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var import_fs = __toESM(require("fs"));
|
|
25
|
+
var import_http = __toESM(require("http"));
|
|
26
|
+
var import_path = __toESM(require("path"));
|
|
27
|
+
const filePath = process.argv[2];
|
|
28
|
+
if (!filePath) {
|
|
29
|
+
console.error("Usage: node format-bridge.cjs <filePath>");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
const absPath = import_path.default.resolve(filePath);
|
|
33
|
+
const PORT = parseInt(process.env.AIPANEL_VSCODE_PORT || "", 10) || 51939;
|
|
34
|
+
function touchFile() {
|
|
35
|
+
const now = /* @__PURE__ */ new Date();
|
|
36
|
+
import_fs.default.utimesSync(absPath, now, now);
|
|
37
|
+
}
|
|
38
|
+
function probeHealth() {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
const req = import_http.default.get(`http://127.0.0.1:${PORT}/health`, (res) => {
|
|
41
|
+
res.resume();
|
|
42
|
+
resolve(res.statusCode === 200);
|
|
43
|
+
});
|
|
44
|
+
req.setTimeout(500, () => {
|
|
45
|
+
req.destroy();
|
|
46
|
+
resolve(false);
|
|
47
|
+
});
|
|
48
|
+
req.on("error", () => resolve(false));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function formatViaExtension() {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const body = JSON.stringify({ filePath: absPath });
|
|
54
|
+
const req = import_http.default.request(
|
|
55
|
+
{
|
|
56
|
+
hostname: "127.0.0.1",
|
|
57
|
+
port: PORT,
|
|
58
|
+
path: "/format",
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: { "Content-Type": "application/json" },
|
|
61
|
+
timeout: 5e3
|
|
62
|
+
},
|
|
63
|
+
(res) => {
|
|
64
|
+
const chunks = [];
|
|
65
|
+
res.on("data", (c) => chunks.push(c));
|
|
66
|
+
res.on("end", () => {
|
|
67
|
+
try {
|
|
68
|
+
const result = JSON.parse(Buffer.concat(chunks).toString());
|
|
69
|
+
if (result.error) {
|
|
70
|
+
reject(new Error(result.error));
|
|
71
|
+
} else {
|
|
72
|
+
resolve(result);
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
reject(e);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
req.on("error", reject);
|
|
81
|
+
req.on("timeout", () => {
|
|
82
|
+
req.destroy();
|
|
83
|
+
reject(new Error("Timeout"));
|
|
84
|
+
});
|
|
85
|
+
req.write(body);
|
|
86
|
+
req.end();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async function main() {
|
|
90
|
+
const healthy = await probeHealth();
|
|
91
|
+
if (!healthy) {
|
|
92
|
+
touchFile();
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
await formatViaExtension();
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
touchFile();
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
main();
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var paths_exports = {};
|
|
29
|
+
__export(paths_exports, {
|
|
30
|
+
resolveVueDevtoolsBridgePath: () => resolveVueDevtoolsBridgePath,
|
|
31
|
+
resolveWidgetPath: () => resolveWidgetPath,
|
|
32
|
+
resolveWidgetStylePath: () => resolveWidgetStylePath
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(paths_exports);
|
|
35
|
+
var import_fs = __toESM(require("fs"));
|
|
36
|
+
var import_path = __toESM(require("path"));
|
|
37
|
+
var import_node = require("@aipanel/core/node");
|
|
38
|
+
const packageDir = (0, import_node.resolvePackageDir)("vite-plugin-aipanel");
|
|
39
|
+
function resolveWidgetPath() {
|
|
40
|
+
const candidatePaths = [
|
|
41
|
+
import_path.default.join(packageDir, "es", "client.js"),
|
|
42
|
+
import_path.default.join(packageDir, "lib", "client.js")
|
|
43
|
+
];
|
|
44
|
+
for (const candidatePath of candidatePaths) {
|
|
45
|
+
if (import_fs.default.existsSync(candidatePath)) {
|
|
46
|
+
return candidatePath;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return candidatePaths[0];
|
|
50
|
+
}
|
|
51
|
+
function resolveWidgetStylePath() {
|
|
52
|
+
const candidatePaths = [
|
|
53
|
+
import_path.default.join(packageDir, "es", "client.css"),
|
|
54
|
+
import_path.default.join(packageDir, "lib", "client.css")
|
|
55
|
+
];
|
|
56
|
+
for (const candidatePath of candidatePaths) {
|
|
57
|
+
if (import_fs.default.existsSync(candidatePath)) {
|
|
58
|
+
return candidatePath;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return candidatePaths[0];
|
|
62
|
+
}
|
|
63
|
+
function resolveVueDevtoolsBridgePath() {
|
|
64
|
+
const candidatePaths = [
|
|
65
|
+
import_path.default.join(packageDir, "es", "client", "vue-devtools-bridge.mjs"),
|
|
66
|
+
import_path.default.join(packageDir, "lib", "client", "vue-devtools-bridge.cjs")
|
|
67
|
+
];
|
|
68
|
+
for (const candidatePath of candidatePaths) {
|
|
69
|
+
if (import_fs.default.existsSync(candidatePath)) {
|
|
70
|
+
return candidatePath;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return candidatePaths[0];
|
|
74
|
+
}
|
|
75
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
76
|
+
0 && (module.exports = {
|
|
77
|
+
resolveVueDevtoolsBridgePath,
|
|
78
|
+
resolveWidgetPath,
|
|
79
|
+
resolveWidgetStylePath
|
|
80
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var system_exports = {};
|
|
29
|
+
__export(system_exports, {
|
|
30
|
+
findGitRoot: () => findGitRoot,
|
|
31
|
+
waitForServer: () => waitForServer
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(system_exports);
|
|
34
|
+
var import_fs = __toESM(require("fs"));
|
|
35
|
+
var import_http = __toESM(require("http"));
|
|
36
|
+
var import_path = __toESM(require("path"));
|
|
37
|
+
var import_core = require("@aipanel/core");
|
|
38
|
+
var import_node = require("@aipanel/core/node");
|
|
39
|
+
const log = (0, import_node.createLogger)("Utils");
|
|
40
|
+
function waitForServer(url, timeout = 1e4, process) {
|
|
41
|
+
const timer = new import_node.PerformanceTimer("waitForServer", { url, timeout });
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const startTime = Date.now();
|
|
44
|
+
let attempts = 0;
|
|
45
|
+
const check = () => {
|
|
46
|
+
attempts++;
|
|
47
|
+
log.debug(`Checking server availability (attempt ${attempts})`, { url });
|
|
48
|
+
if (process?.exitCode !== null && process?.exitCode !== void 0) {
|
|
49
|
+
timer.end(`\u274C Process exited with code ${process.exitCode}`);
|
|
50
|
+
reject(new Error(`Process exited with code ${process.exitCode}`));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const req = import_http.default.get(url, (res) => {
|
|
54
|
+
if (res.statusCode && res.statusCode < 500) {
|
|
55
|
+
timer.end(`\u2713 Server ready after ${attempts} attempts`);
|
|
56
|
+
resolve();
|
|
57
|
+
} else {
|
|
58
|
+
log.debug(`Server returned status ${res.statusCode}, retrying...`);
|
|
59
|
+
retryOrReject();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
req.on("error", (err) => {
|
|
63
|
+
log.debug(`Server check failed: ${err.message}`);
|
|
64
|
+
retryOrReject();
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const retryOrReject = () => {
|
|
68
|
+
const elapsed = Date.now() - startTime;
|
|
69
|
+
if (elapsed < timeout) {
|
|
70
|
+
setTimeout(check, import_core.SERVER_CHECK_INTERVAL);
|
|
71
|
+
} else {
|
|
72
|
+
timer.end("\u274C Timeout");
|
|
73
|
+
reject(new Error(`Server not ready after ${timeout}ms (${attempts} attempts)`));
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
check();
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function findGitRoot(startDir, maxDepth = 10) {
|
|
80
|
+
const timer = log.timer("findGitRoot", { startDir, maxDepth });
|
|
81
|
+
let currentDir = startDir;
|
|
82
|
+
let depth = 0;
|
|
83
|
+
while (depth < maxDepth) {
|
|
84
|
+
const gitDir = import_path.default.join(currentDir, ".git");
|
|
85
|
+
try {
|
|
86
|
+
if (import_fs.default.existsSync(gitDir)) {
|
|
87
|
+
timer.end(`\u2713 Found git root at depth ${depth}: ${currentDir}`);
|
|
88
|
+
return currentDir;
|
|
89
|
+
}
|
|
90
|
+
} catch (err) {
|
|
91
|
+
log.debug(`Error checking .git directory at ${currentDir}`, {
|
|
92
|
+
error: err.message
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const parentDir = import_path.default.dirname(currentDir);
|
|
96
|
+
if (parentDir === currentDir) {
|
|
97
|
+
log.debug("Reached filesystem root");
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
currentDir = parentDir;
|
|
101
|
+
depth++;
|
|
102
|
+
}
|
|
103
|
+
timer.end(`\u274C No git root found after ${depth} levels, using start directory`);
|
|
104
|
+
return startDir;
|
|
105
|
+
}
|
|
106
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
107
|
+
0 && (module.exports = {
|
|
108
|
+
findGitRoot,
|
|
109
|
+
waitForServer
|
|
110
|
+
});
|