vite-plugin-opencode-assistant 1.0.13 → 1.0.14
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/index.js +384 -86
- package/es/core/api.d.ts +1 -0
- package/es/core/api.js +97 -15
- package/es/core/service.d.ts +8 -1
- package/es/core/service.js +84 -16
- package/es/endpoints/index.js +2 -0
- package/es/endpoints/sse.js +14 -5
- package/es/endpoints/types.d.ts +7 -1
- package/es/endpoints/warmup.d.ts +3 -0
- package/es/endpoints/warmup.js +45 -0
- package/es/index.js +12 -3
- package/lib/client/index.js +380 -85
- package/lib/client.js +2880 -2619
- package/lib/core/api.d.ts +1 -0
- package/lib/core/api.js +97 -15
- package/lib/core/service.d.ts +8 -1
- package/lib/core/service.js +83 -16
- package/lib/endpoints/index.js +2 -0
- package/lib/endpoints/sse.js +14 -5
- package/lib/endpoints/types.d.ts +7 -1
- package/lib/endpoints/warmup.d.ts +3 -0
- package/lib/endpoints/warmup.js +68 -0
- package/lib/index.js +12 -3
- package/lib/style.css +1 -1
- package/package.json +5 -5
package/es/core/api.js
CHANGED
|
@@ -35,13 +35,36 @@ function sleep(ms) {
|
|
|
35
35
|
function base64Encode(str) {
|
|
36
36
|
return Buffer.from(str).toString("base64");
|
|
37
37
|
}
|
|
38
|
+
function extractTextFromResponse(data) {
|
|
39
|
+
if (!data || typeof data !== "object") return null;
|
|
40
|
+
const obj = data;
|
|
41
|
+
if (obj.parts && Array.isArray(obj.parts)) {
|
|
42
|
+
const textParts = obj.parts.filter(
|
|
43
|
+
(p) => p && typeof p === "object" && p.type === "text"
|
|
44
|
+
).map((p) => p.text).filter(Boolean);
|
|
45
|
+
if (textParts.length > 0) return textParts.join("");
|
|
46
|
+
}
|
|
47
|
+
if (obj.text && typeof obj.text === "string") {
|
|
48
|
+
return obj.text;
|
|
49
|
+
}
|
|
50
|
+
if (obj.content && typeof obj.content === "string") {
|
|
51
|
+
return obj.content;
|
|
52
|
+
}
|
|
53
|
+
if (obj.message && typeof obj.message === "string") {
|
|
54
|
+
return obj.message;
|
|
55
|
+
}
|
|
56
|
+
if (typeof data === "string") {
|
|
57
|
+
return data;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
38
61
|
class OpenCodeAPI {
|
|
39
62
|
constructor(hostname, getPort, warmupChromeMcpConfig = false) {
|
|
40
63
|
__publicField(this, "hostname", hostname);
|
|
41
64
|
__publicField(this, "getPort", getPort);
|
|
42
65
|
__publicField(this, "warmupChromeMcpConfig", warmupChromeMcpConfig);
|
|
43
66
|
}
|
|
44
|
-
createHttpRequest(options, body) {
|
|
67
|
+
createHttpRequest(options, body, timeout) {
|
|
45
68
|
const timer = new PerformanceTimer("HTTP Request", {
|
|
46
69
|
operation: `${options.method || "GET"} ${options.path}`
|
|
47
70
|
});
|
|
@@ -64,6 +87,13 @@ class OpenCodeAPI {
|
|
|
64
87
|
timer.end("\u274C Request failed");
|
|
65
88
|
reject(e);
|
|
66
89
|
});
|
|
90
|
+
if (timeout) {
|
|
91
|
+
req.setTimeout(timeout, () => {
|
|
92
|
+
timer.end("\u274C Request timeout");
|
|
93
|
+
req.destroy();
|
|
94
|
+
reject(new Error(`Request timeout after ${timeout}ms`));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
67
97
|
if (body) req.write(body);
|
|
68
98
|
req.end();
|
|
69
99
|
});
|
|
@@ -217,24 +247,16 @@ class OpenCodeAPI {
|
|
|
217
247
|
try {
|
|
218
248
|
const warmupSession = yield this.createSession(DEFAULT_RETRIES, "__chrome_mcp_warmup__");
|
|
219
249
|
warmupSessionId = warmupSession.id;
|
|
220
|
-
let chromeToolIds;
|
|
221
|
-
try {
|
|
222
|
-
const toolIds = yield this.getToolIds();
|
|
223
|
-
chromeToolIds = toolIds.filter((toolId) => /chrome[-_]?devtools/i.test(toolId));
|
|
224
|
-
log.debug("Resolved Chrome MCP tool ids", {
|
|
225
|
-
chromeToolIds
|
|
226
|
-
});
|
|
227
|
-
} catch (e) {
|
|
228
|
-
log.debug("Failed to resolve Chrome MCP tool ids", { error: e });
|
|
229
|
-
}
|
|
230
250
|
const prompt = [
|
|
231
251
|
"Call the browser tool list_pages immediately to establish the Chrome DevTools MCP connection.",
|
|
232
252
|
viteOrigin ? `If there are no pages, call new_page with ${viteOrigin}.` : "If there are no pages, call new_page with about:blank.",
|
|
233
253
|
"Do not read or modify project files.",
|
|
234
254
|
"Do not use any non-browser tools.",
|
|
235
|
-
"After the tool call is complete, reply with exactly: ready"
|
|
255
|
+
"After the tool call is complete, reply with exactly: ready",
|
|
256
|
+
"If the tool call fails, reply with exactly: fail"
|
|
236
257
|
].join(" ");
|
|
237
|
-
|
|
258
|
+
const WARMUP_TIMEOUT = 3e4;
|
|
259
|
+
const data = yield this.createHttpRequest(
|
|
238
260
|
{
|
|
239
261
|
hostname: this.hostname,
|
|
240
262
|
port: this.getPort(),
|
|
@@ -244,14 +266,19 @@ class OpenCodeAPI {
|
|
|
244
266
|
},
|
|
245
267
|
JSON.stringify({
|
|
246
268
|
system: "You are warming up Chrome DevTools MCP during startup. You must use the available browser tools immediately before replying.",
|
|
247
|
-
tools: (chromeToolIds == null ? void 0 : chromeToolIds.length) ? chromeToolIds : void 0,
|
|
248
269
|
parts: [{ type: "text", text: prompt }]
|
|
249
|
-
})
|
|
270
|
+
}),
|
|
271
|
+
WARMUP_TIMEOUT
|
|
250
272
|
);
|
|
273
|
+
const responseText = extractTextFromResponse(data);
|
|
274
|
+
if (!(responseText == null ? void 0 : responseText.toLowerCase().includes("ready"))) {
|
|
275
|
+
throw new Error(`Chrome MCP warmup failed: ${responseText || "No response"}`);
|
|
276
|
+
}
|
|
251
277
|
timer.end("Chrome MCP warmed up");
|
|
252
278
|
} catch (e) {
|
|
253
279
|
log.warn("Failed to warm up Chrome MCP", { error: e });
|
|
254
280
|
timer.end("Chrome MCP warmup skipped");
|
|
281
|
+
throw e;
|
|
255
282
|
} finally {
|
|
256
283
|
if (warmupSessionId) {
|
|
257
284
|
try {
|
|
@@ -288,6 +315,61 @@ class OpenCodeAPI {
|
|
|
288
315
|
return url;
|
|
289
316
|
});
|
|
290
317
|
}
|
|
318
|
+
retryWarmupChromeMcp(viteOrigin) {
|
|
319
|
+
return __async(this, null, function* () {
|
|
320
|
+
const timer = log.timer("retryWarmupChromeMcp", { viteOrigin });
|
|
321
|
+
let warmupSessionId = null;
|
|
322
|
+
try {
|
|
323
|
+
const warmupSession = yield this.createSession(DEFAULT_RETRIES, "__chrome_mcp_warmup__");
|
|
324
|
+
warmupSessionId = warmupSession.id;
|
|
325
|
+
const prompt = [
|
|
326
|
+
"Call the browser tool list_pages immediately to establish the Chrome DevTools MCP connection.",
|
|
327
|
+
viteOrigin ? `If there are no pages, call new_page with ${viteOrigin}.` : "If there are no pages, call new_page with about:blank.",
|
|
328
|
+
"Do not read or modify project files.",
|
|
329
|
+
"Do not use any non-browser tools.",
|
|
330
|
+
"After the tool call is complete, reply with exactly: ready",
|
|
331
|
+
"If the tool call fails, reply with exactly: fail"
|
|
332
|
+
].join(" ");
|
|
333
|
+
const WARMUP_TIMEOUT = 6e4;
|
|
334
|
+
const data = yield this.createHttpRequest(
|
|
335
|
+
{
|
|
336
|
+
hostname: this.hostname,
|
|
337
|
+
port: this.getPort(),
|
|
338
|
+
path: `/session/${warmupSessionId}/message`,
|
|
339
|
+
method: "POST",
|
|
340
|
+
headers: { "Content-Type": "application/json" }
|
|
341
|
+
},
|
|
342
|
+
JSON.stringify({
|
|
343
|
+
system: "You are warming up Chrome DevTools MCP during startup. You must use the available browser tools immediately before replying.",
|
|
344
|
+
parts: [{ type: "text", text: prompt }]
|
|
345
|
+
}),
|
|
346
|
+
WARMUP_TIMEOUT
|
|
347
|
+
);
|
|
348
|
+
log.debug("Chrome MCP warmup response:", { data });
|
|
349
|
+
const responseText = extractTextFromResponse(data);
|
|
350
|
+
if (!(responseText == null ? void 0 : responseText.toLowerCase().includes("ready"))) {
|
|
351
|
+
throw new Error(`Chrome MCP warmup failed: ${responseText || "No response"}`);
|
|
352
|
+
}
|
|
353
|
+
timer.end("Chrome MCP warmed up successfully");
|
|
354
|
+
return true;
|
|
355
|
+
} catch (e) {
|
|
356
|
+
log.warn("Failed to retry warm up Chrome MCP", { error: e });
|
|
357
|
+
timer.end("Chrome MCP warmup retry failed");
|
|
358
|
+
return false;
|
|
359
|
+
} finally {
|
|
360
|
+
if (warmupSessionId) {
|
|
361
|
+
try {
|
|
362
|
+
yield this.deleteSession(warmupSessionId, 5);
|
|
363
|
+
} catch (e) {
|
|
364
|
+
log.warn("Failed to delete warmup session after retries", {
|
|
365
|
+
error: e,
|
|
366
|
+
warmupSessionId
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
}
|
|
291
373
|
}
|
|
292
374
|
export {
|
|
293
375
|
OpenCodeAPI
|
package/es/core/service.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ResultPromise } from "execa";
|
|
2
2
|
import type http from "http";
|
|
3
|
-
import type { OpenCodeOptions } from "@vite-plugin-opencode-assistant/shared";
|
|
3
|
+
import type { OpenCodeOptions, ServiceStartupTask } from "@vite-plugin-opencode-assistant/shared";
|
|
4
4
|
import type { OpenCodeAPI } from "./api.js";
|
|
5
5
|
export declare class OpenCodeService {
|
|
6
6
|
private config;
|
|
@@ -15,7 +15,14 @@ export declare class OpenCodeService {
|
|
|
15
15
|
private startPromise;
|
|
16
16
|
sessionUrl: string | null;
|
|
17
17
|
private proxyServer;
|
|
18
|
+
chromeMcpWarmupFailed: boolean;
|
|
19
|
+
currentTask: {
|
|
20
|
+
task: ServiceStartupTask;
|
|
21
|
+
data?: Record<string, unknown>;
|
|
22
|
+
} | null;
|
|
18
23
|
constructor(config: Required<OpenCodeOptions>, api: OpenCodeAPI, sseClients: Set<http.ServerResponse>, onPortAllocated: (port: number) => void, onProxyPortAllocated: (port: number) => void);
|
|
24
|
+
private sendTaskUpdate;
|
|
19
25
|
start(corsOrigins?: string[], contextApiUrl?: string, viteOrigin?: string): Promise<void>;
|
|
26
|
+
retryWarmupChromeMcp(viteOrigin?: string): Promise<boolean>;
|
|
20
27
|
stop(): Promise<void>;
|
|
21
28
|
}
|
package/es/core/service.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
2
5
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __spreadValues = (a, b) => {
|
|
7
|
+
for (var prop in b || (b = {}))
|
|
8
|
+
if (__hasOwnProp.call(b, prop))
|
|
9
|
+
__defNormalProp(a, prop, b[prop]);
|
|
10
|
+
if (__getOwnPropSymbols)
|
|
11
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
+
if (__propIsEnum.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
};
|
|
3
17
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
18
|
var __async = (__this, __arguments, generator) => {
|
|
5
19
|
return new Promise((resolve, reject) => {
|
|
@@ -49,10 +63,24 @@ class OpenCodeService {
|
|
|
49
63
|
__publicField(this, "startPromise", null);
|
|
50
64
|
__publicField(this, "sessionUrl", null);
|
|
51
65
|
__publicField(this, "proxyServer", null);
|
|
66
|
+
__publicField(this, "chromeMcpWarmupFailed", false);
|
|
67
|
+
__publicField(this, "currentTask", null);
|
|
52
68
|
var _a;
|
|
53
69
|
this.actualWebPort = config.webPort;
|
|
54
70
|
this.actualProxyPort = (_a = config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT;
|
|
55
71
|
}
|
|
72
|
+
sendTaskUpdate(task, data) {
|
|
73
|
+
this.currentTask = __spreadValues({ task }, data);
|
|
74
|
+
this.sseClients.forEach((client) => {
|
|
75
|
+
try {
|
|
76
|
+
client.write(`data: ${JSON.stringify(__spreadValues({ type: "TASK_UPDATE", task }, data))}
|
|
77
|
+
|
|
78
|
+
`);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
log.debug("Failed to send TASK_UPDATE event", { error: e });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
56
84
|
start(corsOrigins, contextApiUrl, viteOrigin) {
|
|
57
85
|
return __async(this, null, function* () {
|
|
58
86
|
if (this.isStarted && this.webProcess) {
|
|
@@ -75,6 +103,7 @@ class OpenCodeService {
|
|
|
75
103
|
if (orphanCount > 0) {
|
|
76
104
|
log.debug(`Killed ${orphanCount} orphan OpenCode process(es)`);
|
|
77
105
|
}
|
|
106
|
+
this.sendTaskUpdate("checking_opencode");
|
|
78
107
|
if (!(yield checkOpenCodeInstalled())) {
|
|
79
108
|
log.error(`OpenCode is not installed!
|
|
80
109
|
|
|
@@ -94,10 +123,13 @@ Please install OpenCode first:
|
|
|
94
123
|
mise use -g opencode # Any OS
|
|
95
124
|
nix run nixpkgs#opencode # or github:anomalyco/opencode for latest dev branch
|
|
96
125
|
`);
|
|
126
|
+
this.sendTaskUpdate("opencode_not_installed");
|
|
127
|
+
this.startPromise = null;
|
|
97
128
|
timer.end("\u274C OpenCode not installed");
|
|
98
129
|
return;
|
|
99
130
|
}
|
|
100
131
|
timer.checkpoint("OpenCode installation verified");
|
|
132
|
+
this.sendTaskUpdate("allocating_port");
|
|
101
133
|
this.actualWebPort = yield findAvailablePort(this.config.webPort, this.config.hostname);
|
|
102
134
|
this.onPortAllocated(this.actualWebPort);
|
|
103
135
|
if (this.actualWebPort !== this.config.webPort) {
|
|
@@ -106,8 +138,10 @@ Please install OpenCode first:
|
|
|
106
138
|
log.debug(`Using port ${this.actualWebPort}`);
|
|
107
139
|
}
|
|
108
140
|
timer.checkpoint("Port allocated");
|
|
141
|
+
this.sendTaskUpdate("preparing_runtime");
|
|
109
142
|
const configDir = prepareOpenCodeRuntime(process.cwd());
|
|
110
143
|
timer.checkpoint("Plugin setup complete");
|
|
144
|
+
this.sendTaskUpdate("starting_web");
|
|
111
145
|
log.debug("Starting OpenCode Web process...", {
|
|
112
146
|
port: this.actualWebPort,
|
|
113
147
|
hostname: this.config.hostname,
|
|
@@ -125,8 +159,18 @@ Please install OpenCode first:
|
|
|
125
159
|
timer.checkpoint("Web process started");
|
|
126
160
|
const webUrl = `http://${this.config.hostname}:${this.actualWebPort}`;
|
|
127
161
|
log.info(`Waiting for OpenCode Web to become ready at ${webUrl}...`);
|
|
128
|
-
|
|
129
|
-
|
|
162
|
+
this.sendTaskUpdate("waiting_web_ready");
|
|
163
|
+
try {
|
|
164
|
+
yield waitForServer(webUrl, SERVER_START_TIMEOUT);
|
|
165
|
+
log.info(`OpenCode Web started at ${webUrl}`);
|
|
166
|
+
} catch (e) {
|
|
167
|
+
log.error("OpenCode Web failed to start", { error: e });
|
|
168
|
+
this.sendTaskUpdate("web_start_timeout");
|
|
169
|
+
this.startPromise = null;
|
|
170
|
+
timer.end("\u274C Web start timeout");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.sendTaskUpdate("starting_proxy");
|
|
130
174
|
this.actualProxyPort = yield findAvailablePort(
|
|
131
175
|
(_a = this.config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT,
|
|
132
176
|
this.config.hostname
|
|
@@ -145,33 +189,57 @@ Please install OpenCode first:
|
|
|
145
189
|
settings: this.config.settings
|
|
146
190
|
});
|
|
147
191
|
timer.checkpoint("Proxy server started");
|
|
148
|
-
|
|
149
|
-
|
|
192
|
+
this.sendTaskUpdate("warming_up_chrome");
|
|
193
|
+
let warmupFailed = false;
|
|
194
|
+
try {
|
|
195
|
+
yield this.api.warmupChromeMcp(viteOrigin);
|
|
196
|
+
timer.checkpoint("Chrome MCP warmup complete");
|
|
197
|
+
} catch (e) {
|
|
198
|
+
log.warn("Chrome MCP warmup failed", { error: e });
|
|
199
|
+
this.chromeMcpWarmupFailed = true;
|
|
200
|
+
warmupFailed = true;
|
|
201
|
+
}
|
|
202
|
+
this.sendTaskUpdate("creating_session");
|
|
203
|
+
let sessionFailed = false;
|
|
150
204
|
try {
|
|
151
205
|
this.sessionUrl = yield this.api.getOrCreateSession();
|
|
152
206
|
timer.checkpoint("Session created");
|
|
153
207
|
log.debug(`Session URL: ${this.sessionUrl}`);
|
|
154
|
-
this.sseClients.forEach((client) => {
|
|
155
|
-
try {
|
|
156
|
-
client.write(
|
|
157
|
-
`data: ${JSON.stringify({ type: "SESSION_READY", sessionUrl: this.sessionUrl })}
|
|
158
|
-
|
|
159
|
-
`
|
|
160
|
-
);
|
|
161
|
-
} catch (e) {
|
|
162
|
-
log.debug("Failed to send SESSION_READY event", { error: e });
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
208
|
} catch (e) {
|
|
166
209
|
log.warn("Failed to get/create session", { error: e });
|
|
210
|
+
sessionFailed = true;
|
|
211
|
+
}
|
|
212
|
+
if (sessionFailed) {
|
|
213
|
+
this.sendTaskUpdate("session_creation_failed");
|
|
214
|
+
this.isStarted = false;
|
|
215
|
+
this.startPromise = null;
|
|
216
|
+
} else if (warmupFailed) {
|
|
217
|
+
this.sendTaskUpdate("chrome_mcp_failed", { sessionUrl: this.sessionUrl });
|
|
218
|
+
this.isStarted = true;
|
|
219
|
+
} else {
|
|
220
|
+
this.sendTaskUpdate("ready", { sessionUrl: this.sessionUrl });
|
|
221
|
+
}
|
|
222
|
+
if (!sessionFailed) {
|
|
223
|
+
this.isStarted = true;
|
|
224
|
+
} else {
|
|
225
|
+
this.sessionUrl = null;
|
|
167
226
|
}
|
|
168
|
-
this.isStarted = true;
|
|
169
227
|
log.debug(`OpenCode services started successfully: ${this.sessionUrl || webUrl}`);
|
|
170
228
|
timer.end("\u2713 Services started successfully");
|
|
171
229
|
}))();
|
|
172
230
|
return this.startPromise;
|
|
173
231
|
});
|
|
174
232
|
}
|
|
233
|
+
retryWarmupChromeMcp(viteOrigin) {
|
|
234
|
+
return __async(this, null, function* () {
|
|
235
|
+
const success = yield this.api.retryWarmupChromeMcp(viteOrigin);
|
|
236
|
+
if (success) {
|
|
237
|
+
this.chromeMcpWarmupFailed = false;
|
|
238
|
+
this.sendTaskUpdate("ready", { sessionUrl: this.sessionUrl });
|
|
239
|
+
}
|
|
240
|
+
return success;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
175
243
|
stop() {
|
|
176
244
|
return __async(this, null, function* () {
|
|
177
245
|
const timer = log.timer("stopServices");
|
package/es/endpoints/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { setupContextEndpoint } from "./context.js";
|
|
|
3
3
|
import { setupStartEndpoint } from "./start.js";
|
|
4
4
|
import { setupSseEndpoint } from "./sse.js";
|
|
5
5
|
import { setupSessionsEndpoint } from "./sessions.js";
|
|
6
|
+
import { setupWarmupEndpoint } from "./warmup.js";
|
|
6
7
|
export * from "./types.js";
|
|
7
8
|
function setupMiddlewares(server, ctx) {
|
|
8
9
|
setupWidgetEndpoints(server, ctx);
|
|
@@ -10,6 +11,7 @@ function setupMiddlewares(server, ctx) {
|
|
|
10
11
|
setupStartEndpoint(server, ctx);
|
|
11
12
|
setupSseEndpoint(server, ctx);
|
|
12
13
|
setupSessionsEndpoint(server, ctx);
|
|
14
|
+
setupWarmupEndpoint(server, ctx);
|
|
13
15
|
}
|
|
14
16
|
export {
|
|
15
17
|
setupMiddlewares
|
package/es/endpoints/sse.js
CHANGED
|
@@ -35,13 +35,22 @@ function setupSseEndpoint(server, ctx) {
|
|
|
35
35
|
res.write(`data: ${JSON.stringify({ type: "CONNECTED" })}
|
|
36
36
|
|
|
37
37
|
`);
|
|
38
|
+
const statusPayload = { type: "STATUS_SYNC" };
|
|
39
|
+
if (ctx.isServiceStarted !== void 0) {
|
|
40
|
+
statusPayload.isStarted = ctx.isServiceStarted;
|
|
41
|
+
}
|
|
42
|
+
if (ctx.currentTask) {
|
|
43
|
+
statusPayload.task = ctx.currentTask.task;
|
|
44
|
+
if (ctx.currentTask.data) {
|
|
45
|
+
Object.assign(statusPayload, ctx.currentTask.data);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
38
48
|
if (ctx.sessionUrl) {
|
|
39
|
-
|
|
40
|
-
`data: ${JSON.stringify({ type: "SESSION_READY", sessionUrl: ctx.sessionUrl })}
|
|
41
|
-
|
|
42
|
-
`
|
|
43
|
-
);
|
|
49
|
+
statusPayload.sessionUrl = ctx.sessionUrl;
|
|
44
50
|
}
|
|
51
|
+
res.write(`data: ${JSON.stringify(statusPayload)}
|
|
52
|
+
|
|
53
|
+
`);
|
|
45
54
|
req.on("close", () => {
|
|
46
55
|
ctx.sseClients.delete(res);
|
|
47
56
|
log.debug("SSE client disconnected", {
|
package/es/endpoints/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PageContext, SessionInfo } from "@vite-plugin-opencode-assistant/shared";
|
|
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
4
|
get sessionUrl(): string | null;
|
|
@@ -6,9 +6,15 @@ export interface EndpointContext {
|
|
|
6
6
|
get sseClients(): Set<http.ServerResponse>;
|
|
7
7
|
get pageContext(): PageContext;
|
|
8
8
|
set pageContext(ctx: PageContext);
|
|
9
|
+
get isServiceStarted(): boolean;
|
|
10
|
+
get currentTask(): {
|
|
11
|
+
task: ServiceStartupTask;
|
|
12
|
+
data?: Record<string, unknown>;
|
|
13
|
+
} | null;
|
|
9
14
|
getSessions: () => Promise<SessionInfo[]>;
|
|
10
15
|
createSession: () => Promise<SessionInfo>;
|
|
11
16
|
deleteSession: (id: string) => Promise<void>;
|
|
12
17
|
resolveWidgetPath: () => string;
|
|
13
18
|
resolveWidgetStylePath: () => string;
|
|
19
|
+
retryWarmupChromeMcp: () => Promise<boolean>;
|
|
14
20
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
var __async = (__this, __arguments, generator) => {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
var fulfilled = (value) => {
|
|
4
|
+
try {
|
|
5
|
+
step(generator.next(value));
|
|
6
|
+
} catch (e) {
|
|
7
|
+
reject(e);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var rejected = (value) => {
|
|
11
|
+
try {
|
|
12
|
+
step(generator.throw(value));
|
|
13
|
+
} catch (e) {
|
|
14
|
+
reject(e);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
18
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
import { createLogger } from "@vite-plugin-opencode-assistant/shared";
|
|
22
|
+
const log = createLogger("Endpoints:Warmup");
|
|
23
|
+
function setupWarmupEndpoint(server, ctx) {
|
|
24
|
+
server.middlewares.use("/__opencode_warmup__", (req, res) => __async(null, null, function* () {
|
|
25
|
+
if (req.method !== "POST") {
|
|
26
|
+
res.writeHead(405);
|
|
27
|
+
res.end("Method not allowed");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const success = yield ctx.retryWarmupChromeMcp();
|
|
32
|
+
res.setHeader("Content-Type", "application/json");
|
|
33
|
+
res.writeHead(200);
|
|
34
|
+
res.end(JSON.stringify({ success }));
|
|
35
|
+
} catch (e) {
|
|
36
|
+
log.error("Failed to retry warmup", { error: e });
|
|
37
|
+
res.setHeader("Content-Type", "application/json");
|
|
38
|
+
res.writeHead(500);
|
|
39
|
+
res.end(JSON.stringify({ success: false, error: String(e) }));
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
setupWarmupEndpoint
|
|
45
|
+
};
|
package/es/index.js
CHANGED
|
@@ -90,6 +90,8 @@ function createOpenCodePlugin(options = {}) {
|
|
|
90
90
|
return __async(this, null, function* () {
|
|
91
91
|
var _a2, _b2;
|
|
92
92
|
const timer = log.timer("configureServer");
|
|
93
|
+
let viteOrigin = "";
|
|
94
|
+
const getViteOrigin = () => viteOrigin;
|
|
93
95
|
setupMiddlewares(server, {
|
|
94
96
|
get sessionUrl() {
|
|
95
97
|
return service.sessionUrl;
|
|
@@ -106,11 +108,18 @@ function createOpenCodePlugin(options = {}) {
|
|
|
106
108
|
set pageContext(ctx) {
|
|
107
109
|
pageContext = ctx;
|
|
108
110
|
},
|
|
111
|
+
get isServiceStarted() {
|
|
112
|
+
return service.isStarted;
|
|
113
|
+
},
|
|
114
|
+
get currentTask() {
|
|
115
|
+
return service.currentTask;
|
|
116
|
+
},
|
|
109
117
|
getSessions: () => api.getSessions(),
|
|
110
118
|
createSession: () => api.createSession(),
|
|
111
119
|
deleteSession: (id) => api.deleteSession(id),
|
|
112
120
|
resolveWidgetPath,
|
|
113
|
-
resolveWidgetStylePath
|
|
121
|
+
resolveWidgetStylePath,
|
|
122
|
+
retryWarmupChromeMcp: () => service.retryWarmupChromeMcp(getViteOrigin())
|
|
114
123
|
});
|
|
115
124
|
(_a2 = server.httpServer) == null ? void 0 : _a2.on("listening", () => __async(null, null, function* () {
|
|
116
125
|
var _a3;
|
|
@@ -131,7 +140,7 @@ function createOpenCodePlugin(options = {}) {
|
|
|
131
140
|
vitePort = server.config.server.port || 5173;
|
|
132
141
|
viteHost = typeof host === "string" && host !== "0.0.0.0" && host !== "::" && host !== "::1" ? host : "localhost";
|
|
133
142
|
}
|
|
134
|
-
|
|
143
|
+
viteOrigin = `http://${viteHost}:${vitePort}`;
|
|
135
144
|
const contextApiUrl = `http://${viteHost}:${vitePort}${CONTEXT_API_PATH}`;
|
|
136
145
|
log.debug("Vite server ready", {
|
|
137
146
|
vitePort,
|
|
@@ -170,7 +179,7 @@ function createOpenCodePlugin(options = {}) {
|
|
|
170
179
|
open: config.open,
|
|
171
180
|
autoReload: config.autoReload,
|
|
172
181
|
cwd: process.cwd(),
|
|
173
|
-
|
|
182
|
+
// 不再注入 sessionUrl,客户端完全依赖 SSE 状态同步
|
|
174
183
|
hotkey: config.hotkey
|
|
175
184
|
});
|
|
176
185
|
timer.end();
|