vite-plugin-opencode-assistant 1.0.15 → 1.0.17
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/es/client/App.vue.d.ts +6 -0
- package/es/client/App.vue.js +317 -0
- package/es/client/components/ChromeWarmupError-sfc.css +1 -0
- package/es/client/components/ChromeWarmupError.vue.d.ts +11 -0
- package/es/client/components/ChromeWarmupError.vue.js +196 -0
- package/es/client/components/LoadingContent.vue.d.ts +5 -0
- package/es/client/components/LoadingContent.vue.js +39 -0
- package/es/client/composables/useContext.d.ts +8 -0
- package/es/client/composables/useContext.js +63 -0
- package/es/client/composables/useHotkey.d.ts +12 -0
- package/es/client/composables/useHotkey.js +41 -0
- package/es/client/composables/useSSE.d.ts +20 -0
- package/es/client/composables/useSSE.js +61 -0
- package/es/client/composables/useSelectedElements.d.ts +19 -0
- package/es/client/composables/useSelectedElements.js +43 -0
- package/es/client/composables/useServiceStatus.d.ts +13 -0
- package/es/client/composables/useServiceStatus.js +53 -0
- package/es/client/composables/useSessions.d.ts +26 -0
- package/es/client/composables/useSessions.js +127 -0
- package/es/client/composables/useTheme.d.ts +12 -0
- package/es/client/composables/useTheme.js +42 -0
- package/es/client/index.d.ts +1 -1
- package/es/client/index.js +5 -675
- package/es/client/styles.css +1 -0
- package/es/core/api.d.ts +18 -6
- package/es/core/api.js +345 -89
- package/es/core/proxy-server.js +266 -2
- package/es/core/service.d.ts +9 -2
- package/es/core/service.js +35 -31
- package/es/endpoints/index.js +1 -1
- package/es/endpoints/sse.js +0 -3
- package/es/endpoints/start.d.ts +1 -2
- package/es/endpoints/start.js +2 -2
- package/es/endpoints/types.d.ts +5 -2
- package/es/endpoints/warmup.js +15 -3
- package/es/index.js +8 -12
- package/es/utils/system.d.ts +1 -0
- package/es/utils/system.js +28 -0
- package/lib/client/App.vue.d.ts +6 -0
- package/lib/client/App.vue.js +344 -0
- package/lib/client/components/ChromeWarmupError-sfc.css +1 -0
- package/lib/client/components/ChromeWarmupError.vue.d.ts +11 -0
- package/lib/client/components/ChromeWarmupError.vue.js +215 -0
- package/lib/client/components/LoadingContent.vue.d.ts +5 -0
- package/lib/client/components/LoadingContent.vue.js +58 -0
- package/lib/client/composables/useContext.d.ts +8 -0
- package/lib/client/composables/useContext.js +86 -0
- package/lib/client/composables/useHotkey.d.ts +12 -0
- package/lib/client/composables/useHotkey.js +66 -0
- package/lib/client/composables/useSSE.d.ts +20 -0
- package/lib/client/composables/useSSE.js +84 -0
- package/lib/client/composables/useSelectedElements.d.ts +19 -0
- package/lib/client/composables/useSelectedElements.js +66 -0
- package/lib/client/composables/useServiceStatus.d.ts +13 -0
- package/lib/client/composables/useServiceStatus.js +76 -0
- package/lib/client/composables/useSessions.d.ts +26 -0
- package/lib/client/composables/useSessions.js +148 -0
- package/lib/client/composables/useTheme.d.ts +12 -0
- package/lib/client/composables/useTheme.js +65 -0
- package/lib/client/index.d.ts +1 -1
- package/lib/client/index.js +22 -667
- package/lib/client/styles.css +1 -0
- package/lib/client.js +3280 -3109
- package/lib/core/api.d.ts +18 -6
- package/lib/core/api.js +342 -94
- package/lib/core/proxy-server.js +266 -2
- package/lib/core/service.d.ts +9 -2
- package/lib/core/service.js +31 -30
- package/lib/endpoints/index.js +1 -1
- package/lib/endpoints/sse.js +0 -3
- package/lib/endpoints/start.d.ts +1 -2
- package/lib/endpoints/start.js +2 -2
- package/lib/endpoints/types.d.ts +5 -2
- package/lib/endpoints/warmup.js +15 -3
- package/lib/index.js +8 -12
- package/lib/style.css +1 -1
- package/lib/utils/system.d.ts +1 -0
- package/lib/utils/system.js +29 -0
- package/package.json +4 -4
package/es/core/proxy-server.js
CHANGED
|
@@ -102,13 +102,268 @@ function generateBridgeScript(options) {
|
|
|
102
102
|
if (event.data && event.data.type === "OPENCODE_SET_THEME") {
|
|
103
103
|
setTheme(event.data.theme);
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
if (event.data && event.data.type === "OPENCODE_INSERT_FILE_PART") {
|
|
107
|
+
insertFilePart(event.data.element);
|
|
108
|
+
}
|
|
105
109
|
});
|
|
106
110
|
|
|
111
|
+
// === \u4FDD\u5B58\u8F93\u5165\u6846\u5149\u6807\u4F4D\u7F6E ===
|
|
112
|
+
let savedRange = null;
|
|
113
|
+
|
|
114
|
+
function setupPromptInputListener() {
|
|
115
|
+
const promptInput = document.querySelector('[data-component="prompt-input"]');
|
|
116
|
+
if (!promptInput) return;
|
|
117
|
+
|
|
118
|
+
promptInput.addEventListener('blur', function() {
|
|
119
|
+
const selection = window.getSelection();
|
|
120
|
+
if (selection && selection.rangeCount > 0) {
|
|
121
|
+
const range = selection.getRangeAt(0);
|
|
122
|
+
if (promptInput.contains(range.commonAncestorContainer)) {
|
|
123
|
+
savedRange = range.cloneRange();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
promptInput.addEventListener('focus', function() {
|
|
129
|
+
savedRange = null;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// === \u63D2\u5165 File Part \u5230\u8F93\u5165\u6846 ===
|
|
134
|
+
function insertFilePart(element) {
|
|
135
|
+
const promptInput = document.querySelector('[data-component="prompt-input"]');
|
|
136
|
+
if (!promptInput) {
|
|
137
|
+
console.warn('[OpenCode Bridge] Prompt input not found');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const { filePath, line, column, description, innerText, previewPageUrl, previewPageTitle } = element;
|
|
142
|
+
|
|
143
|
+
const selector = description || 'element';
|
|
144
|
+
let textPreview = '';
|
|
145
|
+
if (innerText && innerText.trim()) {
|
|
146
|
+
const trimmed = innerText.trim();
|
|
147
|
+
textPreview = trimmed.length > 5 ? trimmed.substring(0, 5) + '...' : trimmed;
|
|
148
|
+
}
|
|
149
|
+
const displayText = '@' + selector + (textPreview ? '(' + textPreview + ')' : '');
|
|
150
|
+
|
|
151
|
+
const jsonStr = JSON.stringify({
|
|
152
|
+
pageContext: {
|
|
153
|
+
url: previewPageUrl || '',
|
|
154
|
+
title: previewPageTitle || '',
|
|
155
|
+
},
|
|
156
|
+
nodeContext: {
|
|
157
|
+
filePath,
|
|
158
|
+
line,
|
|
159
|
+
column,
|
|
160
|
+
description,
|
|
161
|
+
innerText: innerText ? innerText.substring(0, 500) : ''
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const span = document.createElement('span');
|
|
166
|
+
span.setAttribute('data-type', 'file');
|
|
167
|
+
span.setAttribute('data-path', jsonStr);
|
|
168
|
+
span.setAttribute('contenteditable', 'false');
|
|
169
|
+
|
|
170
|
+
span.textContent = displayText;
|
|
171
|
+
|
|
172
|
+
if (savedRange) {
|
|
173
|
+
const range = savedRange;
|
|
174
|
+
range.collapse(false);
|
|
175
|
+
range.insertNode(span);
|
|
176
|
+
|
|
177
|
+
const space = document.createTextNode('\\u00A0');
|
|
178
|
+
span.parentNode.insertBefore(space, span.nextSibling);
|
|
179
|
+
|
|
180
|
+
const newRange = document.createRange();
|
|
181
|
+
newRange.setStartAfter(space);
|
|
182
|
+
newRange.collapse(true);
|
|
183
|
+
|
|
184
|
+
promptInput.focus();
|
|
185
|
+
|
|
186
|
+
const selection = window.getSelection();
|
|
187
|
+
if (selection) {
|
|
188
|
+
selection.removeAllRanges();
|
|
189
|
+
selection.addRange(newRange);
|
|
190
|
+
}
|
|
191
|
+
savedRange = null;
|
|
192
|
+
|
|
193
|
+
promptInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const selection = window.getSelection();
|
|
198
|
+
if (selection && selection.rangeCount > 0) {
|
|
199
|
+
const range = selection.getRangeAt(0);
|
|
200
|
+
|
|
201
|
+
if (promptInput.contains(range.commonAncestorContainer)) {
|
|
202
|
+
range.collapse(false);
|
|
203
|
+
range.insertNode(span);
|
|
204
|
+
|
|
205
|
+
const space = document.createTextNode('\\u00A0');
|
|
206
|
+
span.parentNode.insertBefore(space, span.nextSibling);
|
|
207
|
+
|
|
208
|
+
const newRange = document.createRange();
|
|
209
|
+
newRange.setStartAfter(space);
|
|
210
|
+
newRange.collapse(true);
|
|
211
|
+
selection.removeAllRanges();
|
|
212
|
+
selection.addRange(newRange);
|
|
213
|
+
|
|
214
|
+
promptInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
promptInput.appendChild(span);
|
|
220
|
+
const space = document.createTextNode('\\u00A0');
|
|
221
|
+
promptInput.appendChild(space);
|
|
222
|
+
|
|
223
|
+
const newRange = document.createRange();
|
|
224
|
+
newRange.setStartAfter(space);
|
|
225
|
+
newRange.collapse(true);
|
|
226
|
+
const newSelection = window.getSelection();
|
|
227
|
+
if (newSelection) {
|
|
228
|
+
newSelection.removeAllRanges();
|
|
229
|
+
newSelection.addRange(newRange);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
promptInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
233
|
+
promptInput.focus();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// === \u601D\u8003\u72B6\u6001\u76D1\u542C (\u5B8C\u5168\u590D\u523B OpenCode Web \u5B9E\u73B0) ===
|
|
237
|
+
// OpenCode Web \u6838\u5FC3\u903B\u8F91:
|
|
238
|
+
// working = !!pending() || sessionStatus().type !== "idle"
|
|
239
|
+
// pending = \u6700\u540E\u4E00\u6761\u672A\u5B8C\u6210\u7684 assistant \u6D88\u606F (time.completed \u4E0D\u662F\u6570\u5B57)
|
|
240
|
+
// sessionStatus = sync.data.session_status[sessionID]
|
|
241
|
+
|
|
242
|
+
let eventSource = null;
|
|
243
|
+
const sessionStatus = {};
|
|
244
|
+
const pendingMessages = {};
|
|
245
|
+
|
|
246
|
+
function getCurrentSessionID() {
|
|
247
|
+
const match = window.location.pathname.match(/\\/session\\/([^\\/]+)/);
|
|
248
|
+
return match ? match[1] : null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function isPending(message) {
|
|
252
|
+
return message.role === 'assistant' && typeof message.time?.completed !== 'number';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function updateThinkingState(sessionID) {
|
|
256
|
+
const status = sessionStatus[sessionID];
|
|
257
|
+
const pending = pendingMessages[sessionID];
|
|
258
|
+
|
|
259
|
+
const isThinking = !!pending || (status && status.type !== 'idle');
|
|
260
|
+
|
|
261
|
+
if (window.parent !== window) {
|
|
262
|
+
window.parent.postMessage({
|
|
263
|
+
type: 'OPENCODE_THINKING_STATE',
|
|
264
|
+
thinking: isThinking,
|
|
265
|
+
sessionID: sessionID,
|
|
266
|
+
statusType: status?.type || 'idle',
|
|
267
|
+
hasPending: !!pending
|
|
268
|
+
}, '*');
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function handleEvent(payload) {
|
|
273
|
+
const type = payload.type;
|
|
274
|
+
const props = payload.properties;
|
|
275
|
+
|
|
276
|
+
switch (type) {
|
|
277
|
+
case 'session.status': {
|
|
278
|
+
const sessionID = props.sessionID;
|
|
279
|
+
sessionStatus[sessionID] = props.status;
|
|
280
|
+
updateThinkingState(sessionID);
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
case 'message.updated': {
|
|
285
|
+
const info = props.info;
|
|
286
|
+
if (!info || !info.sessionID) break;
|
|
287
|
+
const sessionID = info.sessionID;
|
|
288
|
+
|
|
289
|
+
if (info.role === 'assistant') {
|
|
290
|
+
if (isPending(info)) {
|
|
291
|
+
pendingMessages[sessionID] = info;
|
|
292
|
+
} else {
|
|
293
|
+
delete pendingMessages[sessionID];
|
|
294
|
+
}
|
|
295
|
+
updateThinkingState(sessionID);
|
|
296
|
+
}
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
case 'message.part.delta': {
|
|
301
|
+
const sessionID = props.sessionID;
|
|
302
|
+
if (sessionID && !pendingMessages[sessionID]) {
|
|
303
|
+
pendingMessages[sessionID] = { role: 'assistant', time: {} };
|
|
304
|
+
updateThinkingState(sessionID);
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function setupThinkingListener() {
|
|
312
|
+
if (eventSource) {
|
|
313
|
+
eventSource.close();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
eventSource = new EventSource('/global/event');
|
|
317
|
+
|
|
318
|
+
eventSource.onmessage = function(event) {
|
|
319
|
+
try {
|
|
320
|
+
const data = JSON.parse(event.data);
|
|
321
|
+
const payload = data.payload;
|
|
322
|
+
if (!payload) return;
|
|
323
|
+
handleEvent(payload);
|
|
324
|
+
} catch (e) {
|
|
325
|
+
// ignore parse errors
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
eventSource.onerror = function(err) {
|
|
330
|
+
console.warn('[OpenCode Bridge] SSE connection error, retrying in 3s...');
|
|
331
|
+
eventSource.close();
|
|
332
|
+
setTimeout(setupThinkingListener, 3000);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
console.log('[OpenCode Bridge] SSE listener setup complete');
|
|
336
|
+
}
|
|
337
|
+
|
|
107
338
|
// === \u5C31\u7EEA\u901A\u77E5 ===
|
|
108
|
-
|
|
339
|
+
function init() {
|
|
109
340
|
if (window.parent !== window) {
|
|
110
341
|
window.parent.postMessage({ type: "OPENCODE_READY" }, "*");
|
|
111
342
|
}
|
|
343
|
+
setupThinkingListener();
|
|
344
|
+
setupPromptInputListener();
|
|
345
|
+
|
|
346
|
+
const observer = new MutationObserver(function(mutations) {
|
|
347
|
+
const promptInput = document.querySelector('[data-component="prompt-input"]');
|
|
348
|
+
if (promptInput && !promptInput._opencodeListenerAttached) {
|
|
349
|
+
setupPromptInputListener();
|
|
350
|
+
promptInput._opencodeListenerAttached = true;
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (document.readyState === 'loading') {
|
|
357
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
358
|
+
} else {
|
|
359
|
+
init();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
window.addEventListener('beforeunload', function() {
|
|
363
|
+
if (eventSource) {
|
|
364
|
+
eventSource.close();
|
|
365
|
+
eventSource = null;
|
|
366
|
+
}
|
|
112
367
|
});
|
|
113
368
|
})();
|
|
114
369
|
`;
|
|
@@ -136,7 +391,8 @@ function startProxyServer(targetUrl, port, options = {}) {
|
|
|
136
391
|
headers: __spreadProps(__spreadValues({}, req.headers), {
|
|
137
392
|
host: target.host,
|
|
138
393
|
"accept-encoding": "identity"
|
|
139
|
-
})
|
|
394
|
+
}),
|
|
395
|
+
timeout: 0
|
|
140
396
|
};
|
|
141
397
|
const proxyReq = http.request(requestOptions, (proxyRes) => {
|
|
142
398
|
var _a;
|
|
@@ -182,11 +438,19 @@ function startProxyServer(targetUrl, port, options = {}) {
|
|
|
182
438
|
res.writeHead(502);
|
|
183
439
|
res.end("Proxy error");
|
|
184
440
|
});
|
|
441
|
+
proxyReq.on("socket", (socket) => {
|
|
442
|
+
socket.setTimeout(0);
|
|
443
|
+
});
|
|
444
|
+
req.on("socket", (socket) => {
|
|
445
|
+
socket.setTimeout(0);
|
|
446
|
+
});
|
|
185
447
|
req.pipe(proxyReq);
|
|
186
448
|
});
|
|
187
449
|
server.on("error", (err) => {
|
|
188
450
|
reject(err);
|
|
189
451
|
});
|
|
452
|
+
server.timeout = 0;
|
|
453
|
+
server.keepAliveTimeout = 0;
|
|
190
454
|
server.listen(port, () => {
|
|
191
455
|
const address = server.address();
|
|
192
456
|
const actualPort = typeof address === "object" && address ? address.port : port;
|
package/es/core/service.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ResultPromise } from "execa";
|
|
2
2
|
import type http from "http";
|
|
3
3
|
import type { OpenCodeOptions, ServiceStartupTask } from "@vite-plugin-opencode-assistant/shared";
|
|
4
|
+
import { ChromeMcpWarmupErrorType } from "@vite-plugin-opencode-assistant/shared";
|
|
4
5
|
import type { OpenCodeAPI } from "./api.js";
|
|
5
6
|
export declare class OpenCodeService {
|
|
6
7
|
private config;
|
|
@@ -13,16 +14,22 @@ export declare class OpenCodeService {
|
|
|
13
14
|
actualProxyPort: number;
|
|
14
15
|
isStarted: boolean;
|
|
15
16
|
private startPromise;
|
|
16
|
-
sessionUrl: string | null;
|
|
17
17
|
private proxyServer;
|
|
18
18
|
chromeMcpWarmupFailed: boolean;
|
|
19
|
+
chromeMcpWarmupErrorType: ChromeMcpWarmupErrorType | null;
|
|
20
|
+
chromeMcpWarmupErrorMessage: string | null;
|
|
19
21
|
currentTask: {
|
|
20
22
|
task: ServiceStartupTask;
|
|
21
23
|
data?: Record<string, unknown>;
|
|
22
24
|
} | null;
|
|
25
|
+
workspaceRoot: string | null;
|
|
23
26
|
constructor(config: Required<OpenCodeOptions>, api: OpenCodeAPI, sseClients: Set<http.ServerResponse>, onPortAllocated: (port: number) => void, onProxyPortAllocated: (port: number) => void);
|
|
24
27
|
private sendTaskUpdate;
|
|
25
28
|
start(corsOrigins?: string[], contextApiUrl?: string, viteOrigin?: string): Promise<void>;
|
|
26
|
-
retryWarmupChromeMcp(viteOrigin?: string): Promise<
|
|
29
|
+
retryWarmupChromeMcp(viteOrigin?: string): Promise<{
|
|
30
|
+
success: boolean;
|
|
31
|
+
errorType?: string;
|
|
32
|
+
errorMessage?: string;
|
|
33
|
+
}>;
|
|
27
34
|
stop(): Promise<void>;
|
|
28
35
|
}
|
package/es/core/service.js
CHANGED
|
@@ -39,11 +39,14 @@ import { prepareOpenCodeRuntime, startOpenCodeWeb } from "@vite-plugin-opencode-
|
|
|
39
39
|
import {
|
|
40
40
|
DEFAULT_PROXY_PORT,
|
|
41
41
|
SERVER_START_TIMEOUT,
|
|
42
|
-
createLogger
|
|
42
|
+
createLogger,
|
|
43
|
+
ChromeMcpWarmupError,
|
|
44
|
+
ChromeMcpWarmupErrorType
|
|
43
45
|
} from "@vite-plugin-opencode-assistant/shared";
|
|
44
46
|
import {
|
|
45
47
|
checkOpenCodeInstalled,
|
|
46
48
|
findAvailablePort,
|
|
49
|
+
findGitRoot,
|
|
47
50
|
killOrphanOpenCodeProcesses,
|
|
48
51
|
waitForServer
|
|
49
52
|
} from "../utils/system.js";
|
|
@@ -61,10 +64,12 @@ class OpenCodeService {
|
|
|
61
64
|
__publicField(this, "actualProxyPort");
|
|
62
65
|
__publicField(this, "isStarted", false);
|
|
63
66
|
__publicField(this, "startPromise", null);
|
|
64
|
-
__publicField(this, "sessionUrl", null);
|
|
65
67
|
__publicField(this, "proxyServer", null);
|
|
66
68
|
__publicField(this, "chromeMcpWarmupFailed", false);
|
|
69
|
+
__publicField(this, "chromeMcpWarmupErrorType", null);
|
|
70
|
+
__publicField(this, "chromeMcpWarmupErrorMessage", null);
|
|
67
71
|
__publicField(this, "currentTask", null);
|
|
72
|
+
__publicField(this, "workspaceRoot", null);
|
|
68
73
|
var _a;
|
|
69
74
|
this.actualWebPort = config.webPort;
|
|
70
75
|
this.actualProxyPort = (_a = config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT;
|
|
@@ -138,8 +143,10 @@ Please install OpenCode first:
|
|
|
138
143
|
log.debug(`Using port ${this.actualWebPort}`);
|
|
139
144
|
}
|
|
140
145
|
timer.checkpoint("Port allocated");
|
|
146
|
+
this.workspaceRoot = findGitRoot(process.cwd());
|
|
147
|
+
log.info(`Using workspace root: ${this.workspaceRoot}`);
|
|
141
148
|
this.sendTaskUpdate("preparing_runtime");
|
|
142
|
-
const configDir = prepareOpenCodeRuntime(
|
|
149
|
+
const configDir = prepareOpenCodeRuntime(this.workspaceRoot);
|
|
143
150
|
timer.checkpoint("Plugin setup complete");
|
|
144
151
|
this.sendTaskUpdate("starting_web");
|
|
145
152
|
log.debug("Starting OpenCode Web process...", {
|
|
@@ -151,7 +158,7 @@ Please install OpenCode first:
|
|
|
151
158
|
port: this.actualWebPort,
|
|
152
159
|
hostname: this.config.hostname,
|
|
153
160
|
serverUrl: "",
|
|
154
|
-
cwd:
|
|
161
|
+
cwd: this.workspaceRoot,
|
|
155
162
|
configDir,
|
|
156
163
|
corsOrigins,
|
|
157
164
|
contextApiUrl
|
|
@@ -224,39 +231,30 @@ Please install OpenCode first:
|
|
|
224
231
|
this.sendTaskUpdate("warming_up_chrome");
|
|
225
232
|
let warmupFailed = false;
|
|
226
233
|
try {
|
|
227
|
-
yield this.api.warmupChromeMcp(viteOrigin);
|
|
234
|
+
yield this.api.warmupChromeMcp(this.workspaceRoot, viteOrigin);
|
|
228
235
|
timer.checkpoint("Chrome MCP warmup complete");
|
|
229
236
|
} catch (e) {
|
|
230
237
|
log.warn("Chrome MCP warmup failed", { error: e });
|
|
231
238
|
this.chromeMcpWarmupFailed = true;
|
|
232
239
|
warmupFailed = true;
|
|
240
|
+
if (e instanceof ChromeMcpWarmupError) {
|
|
241
|
+
this.chromeMcpWarmupErrorType = e.type;
|
|
242
|
+
this.chromeMcpWarmupErrorMessage = e.message;
|
|
243
|
+
} else {
|
|
244
|
+
this.chromeMcpWarmupErrorType = ChromeMcpWarmupErrorType.UNKNOWN;
|
|
245
|
+
this.chromeMcpWarmupErrorMessage = e instanceof Error ? e.message : String(e);
|
|
246
|
+
}
|
|
233
247
|
}
|
|
234
248
|
this.sendTaskUpdate("creating_session");
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
} catch (e) {
|
|
241
|
-
log.warn("Failed to get/create session", { error: e });
|
|
242
|
-
sessionFailed = true;
|
|
243
|
-
}
|
|
244
|
-
if (sessionFailed) {
|
|
245
|
-
this.sendTaskUpdate("session_creation_failed");
|
|
246
|
-
this.isStarted = false;
|
|
247
|
-
this.startPromise = null;
|
|
248
|
-
} else if (warmupFailed) {
|
|
249
|
-
this.sendTaskUpdate("chrome_mcp_failed", { sessionUrl: this.sessionUrl });
|
|
250
|
-
this.isStarted = true;
|
|
251
|
-
} else {
|
|
252
|
-
this.sendTaskUpdate("ready", { sessionUrl: this.sessionUrl });
|
|
253
|
-
}
|
|
254
|
-
if (!sessionFailed) {
|
|
249
|
+
if (warmupFailed) {
|
|
250
|
+
this.sendTaskUpdate("chrome_mcp_failed", {
|
|
251
|
+
errorType: this.chromeMcpWarmupErrorType,
|
|
252
|
+
errorMessage: this.chromeMcpWarmupErrorMessage
|
|
253
|
+
});
|
|
255
254
|
this.isStarted = true;
|
|
256
255
|
} else {
|
|
257
|
-
this.
|
|
256
|
+
this.sendTaskUpdate("ready");
|
|
258
257
|
}
|
|
259
|
-
log.debug(`OpenCode services started successfully: ${this.sessionUrl || webUrl}`);
|
|
260
258
|
timer.end("\u2713 Services started successfully");
|
|
261
259
|
}))();
|
|
262
260
|
return this.startPromise;
|
|
@@ -264,12 +262,18 @@ Please install OpenCode first:
|
|
|
264
262
|
}
|
|
265
263
|
retryWarmupChromeMcp(viteOrigin) {
|
|
266
264
|
return __async(this, null, function* () {
|
|
267
|
-
const
|
|
268
|
-
if (success) {
|
|
265
|
+
const result = yield this.api.retryWarmupChromeMcp(this.workspaceRoot, viteOrigin);
|
|
266
|
+
if (result.success) {
|
|
269
267
|
this.chromeMcpWarmupFailed = false;
|
|
270
|
-
this.sendTaskUpdate("ready"
|
|
268
|
+
this.sendTaskUpdate("ready");
|
|
269
|
+
return { success: true };
|
|
271
270
|
}
|
|
272
|
-
|
|
271
|
+
const error = result.error;
|
|
272
|
+
return {
|
|
273
|
+
success: false,
|
|
274
|
+
errorType: error == null ? void 0 : error.type,
|
|
275
|
+
errorMessage: (error == null ? void 0 : error.message) || "Unknown error"
|
|
276
|
+
};
|
|
273
277
|
});
|
|
274
278
|
}
|
|
275
279
|
stop() {
|
package/es/endpoints/index.js
CHANGED
|
@@ -8,7 +8,7 @@ export * from "./types.js";
|
|
|
8
8
|
function setupMiddlewares(server, ctx) {
|
|
9
9
|
setupWidgetEndpoints(server, ctx);
|
|
10
10
|
setupContextEndpoint(server, ctx);
|
|
11
|
-
setupStartEndpoint(server
|
|
11
|
+
setupStartEndpoint(server);
|
|
12
12
|
setupSseEndpoint(server, ctx);
|
|
13
13
|
setupSessionsEndpoint(server, ctx);
|
|
14
14
|
setupWarmupEndpoint(server, ctx);
|
package/es/endpoints/sse.js
CHANGED
package/es/endpoints/start.d.ts
CHANGED
package/es/endpoints/start.js
CHANGED
|
@@ -20,13 +20,13 @@ var __async = (__this, __arguments, generator) => {
|
|
|
20
20
|
};
|
|
21
21
|
import { START_API_PATH } from "@vite-plugin-opencode-assistant/shared";
|
|
22
22
|
import { RequestContext } from "@vite-plugin-opencode-assistant/shared";
|
|
23
|
-
function setupStartEndpoint(server
|
|
23
|
+
function setupStartEndpoint(server) {
|
|
24
24
|
server.middlewares.use(START_API_PATH, (_req, res) => __async(null, null, function* () {
|
|
25
25
|
const reqCtx = new RequestContext("GET", START_API_PATH);
|
|
26
26
|
res.setHeader("Content-Type", "application/json");
|
|
27
27
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
28
28
|
res.writeHead(200);
|
|
29
|
-
res.end(JSON.stringify({ success: true
|
|
29
|
+
res.end(JSON.stringify({ success: true }));
|
|
30
30
|
reqCtx.end(200);
|
|
31
31
|
}));
|
|
32
32
|
}
|
package/es/endpoints/types.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { PageContext, SessionInfo, ServiceStartupTask } from "@vite-plugin-opencode-assistant/shared";
|
|
2
2
|
import type http from "http";
|
|
3
3
|
export interface EndpointContext {
|
|
4
|
-
get sessionUrl(): string | null;
|
|
5
4
|
get webUrl(): string | null;
|
|
6
5
|
get sseClients(): Set<http.ServerResponse>;
|
|
7
6
|
get pageContext(): PageContext;
|
|
@@ -16,5 +15,9 @@ export interface EndpointContext {
|
|
|
16
15
|
deleteSession: (id: string) => Promise<void>;
|
|
17
16
|
resolveWidgetPath: () => string;
|
|
18
17
|
resolveWidgetStylePath: () => string;
|
|
19
|
-
retryWarmupChromeMcp: () => Promise<
|
|
18
|
+
retryWarmupChromeMcp: () => Promise<{
|
|
19
|
+
success: boolean;
|
|
20
|
+
errorType?: string;
|
|
21
|
+
errorMessage?: string;
|
|
22
|
+
}>;
|
|
20
23
|
}
|
package/es/endpoints/warmup.js
CHANGED
|
@@ -28,15 +28,27 @@ function setupWarmupEndpoint(server, ctx) {
|
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
try {
|
|
31
|
-
const
|
|
31
|
+
const result = yield ctx.retryWarmupChromeMcp();
|
|
32
32
|
res.setHeader("Content-Type", "application/json");
|
|
33
33
|
res.writeHead(200);
|
|
34
|
-
|
|
34
|
+
if (result.success) {
|
|
35
|
+
res.end(JSON.stringify({ success: true }));
|
|
36
|
+
} else {
|
|
37
|
+
res.end(JSON.stringify({
|
|
38
|
+
success: false,
|
|
39
|
+
errorType: result.errorType,
|
|
40
|
+
error: result.errorMessage
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
35
43
|
} catch (e) {
|
|
36
44
|
log.error("Failed to retry warmup", { error: e });
|
|
37
45
|
res.setHeader("Content-Type", "application/json");
|
|
38
46
|
res.writeHead(500);
|
|
39
|
-
res.end(JSON.stringify({
|
|
47
|
+
res.end(JSON.stringify({
|
|
48
|
+
success: false,
|
|
49
|
+
errorType: "UNKNOWN",
|
|
50
|
+
error: String(e)
|
|
51
|
+
}));
|
|
40
52
|
}
|
|
41
53
|
}));
|
|
42
54
|
}
|
package/es/index.js
CHANGED
|
@@ -68,7 +68,12 @@ function createOpenCodePlugin(options = {}) {
|
|
|
68
68
|
let actualProxyPort = (_a = config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT;
|
|
69
69
|
let pageContext = { url: "", title: "" };
|
|
70
70
|
const sseClients = /* @__PURE__ */ new Set();
|
|
71
|
-
const api = new OpenCodeAPI(
|
|
71
|
+
const api = new OpenCodeAPI(
|
|
72
|
+
config.hostname,
|
|
73
|
+
() => actualWebPort,
|
|
74
|
+
() => actualProxyPort,
|
|
75
|
+
config.warmupChromeMcp
|
|
76
|
+
);
|
|
72
77
|
const service = new OpenCodeService(
|
|
73
78
|
config,
|
|
74
79
|
api,
|
|
@@ -93,9 +98,6 @@ function createOpenCodePlugin(options = {}) {
|
|
|
93
98
|
let viteOrigin = "";
|
|
94
99
|
const getViteOrigin = () => viteOrigin;
|
|
95
100
|
setupMiddlewares(server, {
|
|
96
|
-
get sessionUrl() {
|
|
97
|
-
return service.sessionUrl;
|
|
98
|
-
},
|
|
99
101
|
get webUrl() {
|
|
100
102
|
return actualWebPort ? `http://${config.hostname}:${actualWebPort}` : null;
|
|
101
103
|
},
|
|
@@ -114,8 +116,8 @@ function createOpenCodePlugin(options = {}) {
|
|
|
114
116
|
get currentTask() {
|
|
115
117
|
return service.currentTask;
|
|
116
118
|
},
|
|
117
|
-
getSessions: () => api.getSessions(),
|
|
118
|
-
createSession: () => api.createSession(),
|
|
119
|
+
getSessions: () => api.getSessions(service.workspaceRoot),
|
|
120
|
+
createSession: () => api.createSession(service.workspaceRoot),
|
|
119
121
|
deleteSession: (id) => api.deleteSession(id),
|
|
120
122
|
resolveWidgetPath,
|
|
121
123
|
resolveWidgetStylePath,
|
|
@@ -171,15 +173,9 @@ function createOpenCodePlugin(options = {}) {
|
|
|
171
173
|
transformIndexHtml(html) {
|
|
172
174
|
const timer = log.timer("transformIndexHtml");
|
|
173
175
|
const widget = injectWidget({
|
|
174
|
-
webUrl: `http://${config.hostname}:${actualWebPort}`,
|
|
175
|
-
proxyUrl: `http://${config.hostname}:${actualProxyPort}`,
|
|
176
|
-
serverUrl: `http://${config.hostname}:${actualWebPort}`,
|
|
177
176
|
position: config.position,
|
|
178
177
|
theme: config.theme,
|
|
179
178
|
open: config.open,
|
|
180
|
-
autoReload: config.autoReload,
|
|
181
|
-
cwd: process.cwd(),
|
|
182
|
-
// 不再注入 sessionUrl,客户端完全依赖 SSE 状态同步
|
|
183
179
|
hotkey: config.hotkey
|
|
184
180
|
});
|
|
185
181
|
timer.end();
|
package/es/utils/system.d.ts
CHANGED
|
@@ -4,3 +4,4 @@ export declare function checkOpenCodeInstalled(): Promise<boolean>;
|
|
|
4
4
|
export declare function isPortAvailable(port: number, hostname?: string): Promise<boolean>;
|
|
5
5
|
export declare function findAvailablePort(startPort: number, hostname?: string, maxTries?: number): Promise<number>;
|
|
6
6
|
export declare function killOrphanOpenCodeProcesses(): Promise<number>;
|
|
7
|
+
export declare function findGitRoot(startDir: string, maxDepth?: number): string;
|
package/es/utils/system.js
CHANGED
|
@@ -19,8 +19,10 @@ var __async = (__this, __arguments, generator) => {
|
|
|
19
19
|
});
|
|
20
20
|
};
|
|
21
21
|
import { spawn } from "child_process";
|
|
22
|
+
import fs from "fs";
|
|
22
23
|
import http from "http";
|
|
23
24
|
import net from "net";
|
|
25
|
+
import path from "path";
|
|
24
26
|
import { MAX_PORT_TRIES, SERVER_CHECK_INTERVAL } from "@vite-plugin-opencode-assistant/shared";
|
|
25
27
|
import { PerformanceTimer, createLogger } from "@vite-plugin-opencode-assistant/shared";
|
|
26
28
|
const log = createLogger("Utils");
|
|
@@ -182,6 +184,31 @@ function killOrphanProcessesOnWindows(resolve, timer) {
|
|
|
182
184
|
resolve(0);
|
|
183
185
|
});
|
|
184
186
|
}
|
|
187
|
+
function findGitRoot(startDir, maxDepth = 10) {
|
|
188
|
+
const timer = log.timer("findGitRoot", { startDir, maxDepth });
|
|
189
|
+
let currentDir = startDir;
|
|
190
|
+
let depth = 0;
|
|
191
|
+
while (depth < maxDepth) {
|
|
192
|
+
const gitDir = path.join(currentDir, ".git");
|
|
193
|
+
try {
|
|
194
|
+
if (fs.existsSync(gitDir)) {
|
|
195
|
+
timer.end(`\u2713 Found git root at depth ${depth}: ${currentDir}`);
|
|
196
|
+
return currentDir;
|
|
197
|
+
}
|
|
198
|
+
} catch (err) {
|
|
199
|
+
log.debug(`Error checking .git directory at ${currentDir}`, { error: err.message });
|
|
200
|
+
}
|
|
201
|
+
const parentDir = path.dirname(currentDir);
|
|
202
|
+
if (parentDir === currentDir) {
|
|
203
|
+
log.debug("Reached filesystem root");
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
currentDir = parentDir;
|
|
207
|
+
depth++;
|
|
208
|
+
}
|
|
209
|
+
timer.end(`\u274C No git root found after ${depth} levels, using start directory`);
|
|
210
|
+
return startDir;
|
|
211
|
+
}
|
|
185
212
|
function killOrphanProcessesOnUnix(resolve, timer) {
|
|
186
213
|
var _a;
|
|
187
214
|
log.debug("Using Unix method to find orphan processes");
|
|
@@ -236,6 +263,7 @@ function killOrphanProcessesOnUnix(resolve, timer) {
|
|
|
236
263
|
export {
|
|
237
264
|
checkOpenCodeInstalled,
|
|
238
265
|
findAvailablePort,
|
|
266
|
+
findGitRoot,
|
|
239
267
|
isPortAvailable,
|
|
240
268
|
killOrphanOpenCodeProcesses,
|
|
241
269
|
waitForServer
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { WidgetOptions } from "@vite-plugin-opencode-assistant/shared";
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
config: Partial<WidgetOptions>;
|
|
4
|
+
};
|
|
5
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
export default _default;
|