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.
Files changed (79) hide show
  1. package/es/client/App.vue.d.ts +6 -0
  2. package/es/client/App.vue.js +317 -0
  3. package/es/client/components/ChromeWarmupError-sfc.css +1 -0
  4. package/es/client/components/ChromeWarmupError.vue.d.ts +11 -0
  5. package/es/client/components/ChromeWarmupError.vue.js +196 -0
  6. package/es/client/components/LoadingContent.vue.d.ts +5 -0
  7. package/es/client/components/LoadingContent.vue.js +39 -0
  8. package/es/client/composables/useContext.d.ts +8 -0
  9. package/es/client/composables/useContext.js +63 -0
  10. package/es/client/composables/useHotkey.d.ts +12 -0
  11. package/es/client/composables/useHotkey.js +41 -0
  12. package/es/client/composables/useSSE.d.ts +20 -0
  13. package/es/client/composables/useSSE.js +61 -0
  14. package/es/client/composables/useSelectedElements.d.ts +19 -0
  15. package/es/client/composables/useSelectedElements.js +43 -0
  16. package/es/client/composables/useServiceStatus.d.ts +13 -0
  17. package/es/client/composables/useServiceStatus.js +53 -0
  18. package/es/client/composables/useSessions.d.ts +26 -0
  19. package/es/client/composables/useSessions.js +127 -0
  20. package/es/client/composables/useTheme.d.ts +12 -0
  21. package/es/client/composables/useTheme.js +42 -0
  22. package/es/client/index.d.ts +1 -1
  23. package/es/client/index.js +5 -675
  24. package/es/client/styles.css +1 -0
  25. package/es/core/api.d.ts +18 -6
  26. package/es/core/api.js +345 -89
  27. package/es/core/proxy-server.js +266 -2
  28. package/es/core/service.d.ts +9 -2
  29. package/es/core/service.js +35 -31
  30. package/es/endpoints/index.js +1 -1
  31. package/es/endpoints/sse.js +0 -3
  32. package/es/endpoints/start.d.ts +1 -2
  33. package/es/endpoints/start.js +2 -2
  34. package/es/endpoints/types.d.ts +5 -2
  35. package/es/endpoints/warmup.js +15 -3
  36. package/es/index.js +8 -12
  37. package/es/utils/system.d.ts +1 -0
  38. package/es/utils/system.js +28 -0
  39. package/lib/client/App.vue.d.ts +6 -0
  40. package/lib/client/App.vue.js +344 -0
  41. package/lib/client/components/ChromeWarmupError-sfc.css +1 -0
  42. package/lib/client/components/ChromeWarmupError.vue.d.ts +11 -0
  43. package/lib/client/components/ChromeWarmupError.vue.js +215 -0
  44. package/lib/client/components/LoadingContent.vue.d.ts +5 -0
  45. package/lib/client/components/LoadingContent.vue.js +58 -0
  46. package/lib/client/composables/useContext.d.ts +8 -0
  47. package/lib/client/composables/useContext.js +86 -0
  48. package/lib/client/composables/useHotkey.d.ts +12 -0
  49. package/lib/client/composables/useHotkey.js +66 -0
  50. package/lib/client/composables/useSSE.d.ts +20 -0
  51. package/lib/client/composables/useSSE.js +84 -0
  52. package/lib/client/composables/useSelectedElements.d.ts +19 -0
  53. package/lib/client/composables/useSelectedElements.js +66 -0
  54. package/lib/client/composables/useServiceStatus.d.ts +13 -0
  55. package/lib/client/composables/useServiceStatus.js +76 -0
  56. package/lib/client/composables/useSessions.d.ts +26 -0
  57. package/lib/client/composables/useSessions.js +148 -0
  58. package/lib/client/composables/useTheme.d.ts +12 -0
  59. package/lib/client/composables/useTheme.js +65 -0
  60. package/lib/client/index.d.ts +1 -1
  61. package/lib/client/index.js +22 -667
  62. package/lib/client/styles.css +1 -0
  63. package/lib/client.js +3280 -3109
  64. package/lib/core/api.d.ts +18 -6
  65. package/lib/core/api.js +342 -94
  66. package/lib/core/proxy-server.js +266 -2
  67. package/lib/core/service.d.ts +9 -2
  68. package/lib/core/service.js +31 -30
  69. package/lib/endpoints/index.js +1 -1
  70. package/lib/endpoints/sse.js +0 -3
  71. package/lib/endpoints/start.d.ts +1 -2
  72. package/lib/endpoints/start.js +2 -2
  73. package/lib/endpoints/types.d.ts +5 -2
  74. package/lib/endpoints/warmup.js +15 -3
  75. package/lib/index.js +8 -12
  76. package/lib/style.css +1 -1
  77. package/lib/utils/system.d.ts +1 -0
  78. package/lib/utils/system.js +29 -0
  79. package/package.json +4 -4
package/es/core/api.d.ts CHANGED
@@ -1,15 +1,27 @@
1
1
  import type { SessionInfo } from "@vite-plugin-opencode-assistant/shared";
2
+ import { ChromeMcpWarmupError } from "@vite-plugin-opencode-assistant/shared";
2
3
  export declare class OpenCodeAPI {
3
4
  private hostname;
4
5
  private getPort;
6
+ private getProxyPort;
5
7
  private warmupChromeMcpConfig;
6
- constructor(hostname: string, getPort: () => number, warmupChromeMcpConfig?: boolean);
8
+ private failedFreeModels;
9
+ constructor(hostname: string, getPort: () => number, getProxyPort: () => number, warmupChromeMcpConfig?: boolean);
10
+ markModelAsFailed(providerID: string, modelID: string): void;
11
+ clearFailedModels(): void;
7
12
  private createHttpRequest;
8
- getSessions(retries?: number): Promise<SessionInfo[]>;
9
- createSession(retries?: number, title?: string): Promise<SessionInfo>;
13
+ getSessions(projectDir: string, retries?: number): Promise<SessionInfo[]>;
14
+ createSession(projectDir: string, retries?: number, title?: string): Promise<SessionInfo>;
15
+ getCheapestModel(): Promise<{
16
+ providerID: string;
17
+ modelID: string;
18
+ } | null>;
10
19
  deleteSession(sessionId: string, retries?: number): Promise<void>;
11
20
  getToolIds(retries?: number): Promise<string[]>;
12
- warmupChromeMcp(viteOrigin?: string): Promise<void>;
13
- getOrCreateSession(): Promise<string>;
14
- retryWarmupChromeMcp(viteOrigin?: string): Promise<boolean>;
21
+ warmupChromeMcp(projectDir: string, viteOrigin?: string): Promise<void>;
22
+ getOrCreateSession(projectDir: string): Promise<string>;
23
+ retryWarmupChromeMcp(projectDir: string, viteOrigin?: string): Promise<{
24
+ success: boolean;
25
+ error?: ChromeMcpWarmupError;
26
+ }>;
15
27
  }
package/es/core/api.js CHANGED
@@ -1,5 +1,22 @@
1
1
  var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
2
7
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
3
20
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
21
  var __async = (__this, __arguments, generator) => {
5
22
  return new Promise((resolve, reject) => {
@@ -26,43 +43,37 @@ import {
26
43
  PerformanceTimer,
27
44
  createLogger,
28
45
  DEFAULT_RETRIES,
29
- RETRY_DELAY
46
+ RETRY_DELAY,
47
+ ChromeMcpWarmupErrorType,
48
+ ChromeMcpWarmupError,
49
+ CHROME_DEVTOOLS_PORT,
50
+ checkChromeDevToolsAvailable,
51
+ sleep,
52
+ base64Encode,
53
+ extractTextFromResponse
30
54
  } from "@vite-plugin-opencode-assistant/shared";
31
55
  const log = createLogger("API");
32
- function sleep(ms) {
33
- return new Promise((resolve) => setTimeout(resolve, ms));
34
- }
35
- function base64Encode(str) {
36
- return Buffer.from(str).toString("base64");
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
- }
61
56
  class OpenCodeAPI {
62
- constructor(hostname, getPort, warmupChromeMcpConfig = false) {
57
+ constructor(hostname, getPort, getProxyPort, warmupChromeMcpConfig = false) {
63
58
  __publicField(this, "hostname", hostname);
64
59
  __publicField(this, "getPort", getPort);
60
+ __publicField(this, "getProxyPort", getProxyPort);
65
61
  __publicField(this, "warmupChromeMcpConfig", warmupChromeMcpConfig);
62
+ __publicField(this, "failedFreeModels", /* @__PURE__ */ new Set());
63
+ }
64
+ markModelAsFailed(providerID, modelID) {
65
+ const key = `${providerID}:${modelID}`;
66
+ this.failedFreeModels.add(key);
67
+ log.debug("Marked model as failed", {
68
+ providerID,
69
+ modelID,
70
+ key,
71
+ failedCount: this.failedFreeModels.size
72
+ });
73
+ }
74
+ clearFailedModels() {
75
+ this.failedFreeModels.clear();
76
+ log.debug("Cleared failed models cache");
66
77
  }
67
78
  createHttpRequest(options, body, timeout) {
68
79
  const timer = new PerformanceTimer("HTTP Request", {
@@ -98,20 +109,26 @@ class OpenCodeAPI {
98
109
  req.end();
99
110
  });
100
111
  }
101
- getSessions() {
102
- return __async(this, arguments, function* (retries = DEFAULT_RETRIES) {
103
- const timer = log.timer("getSessions", { retries });
112
+ getSessions(_0) {
113
+ return __async(this, arguments, function* (projectDir, retries = DEFAULT_RETRIES) {
114
+ const timer = log.timer("getSessions", { retries, projectDir });
104
115
  let lastError = null;
105
116
  for (let i = 0; i < retries; i++) {
106
117
  try {
107
- log.debug(`Attempt ${i + 1}/${retries}`, { operation: "getSessions" });
118
+ log.debug(`Attempt ${i + 1}/${retries}`, { operation: "getSessions", projectDir });
108
119
  const sessions = yield this.createHttpRequest({
109
120
  hostname: this.hostname,
110
121
  port: this.getPort(),
111
- path: "/session"
122
+ path: "/session",
123
+ headers: {
124
+ "x-opencode-directory": encodeURIComponent(projectDir)
125
+ }
112
126
  });
127
+ const sessionsWithUrl = sessions.map((s) => __spreadProps(__spreadValues({}, s), {
128
+ url: `http://${this.hostname}:${this.getProxyPort()}/${base64Encode(s.directory)}/session/${s.id}`
129
+ }));
113
130
  timer.end(`Found ${sessions.length} sessions`);
114
- return sessions;
131
+ return sessionsWithUrl;
115
132
  } catch (e) {
116
133
  lastError = e instanceof Error ? e : new Error(String(e));
117
134
  log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, {
@@ -129,15 +146,16 @@ class OpenCodeAPI {
129
146
  throw lastError;
130
147
  });
131
148
  }
132
- createSession() {
133
- return __async(this, arguments, function* (retries = DEFAULT_RETRIES, title) {
134
- const timer = log.timer("createSession", { retries, title });
149
+ createSession(_0) {
150
+ return __async(this, arguments, function* (projectDir, retries = DEFAULT_RETRIES, title) {
151
+ const timer = log.timer("createSession", { retries, title, projectDir });
135
152
  let lastError = null;
136
153
  for (let i = 0; i < retries; i++) {
137
154
  try {
138
155
  log.debug(`Attempt ${i + 1}/${retries}`, {
139
156
  operation: "createSession",
140
- title
157
+ title,
158
+ projectDir
141
159
  });
142
160
  const requestBody = title ? JSON.stringify({ title }) : void 0;
143
161
  const session = yield this.createHttpRequest(
@@ -146,12 +164,17 @@ class OpenCodeAPI {
146
164
  port: this.getPort(),
147
165
  path: "/session",
148
166
  method: "POST",
149
- headers: requestBody ? { "Content-Type": "application/json" } : void 0
167
+ headers: __spreadProps(__spreadValues({}, requestBody ? { "Content-Type": "application/json" } : {}), {
168
+ "x-opencode-directory": encodeURIComponent(projectDir)
169
+ })
150
170
  },
151
171
  requestBody
152
172
  );
173
+ const sessionWithUrl = __spreadProps(__spreadValues({}, session), {
174
+ url: `http://${this.hostname}:${this.getProxyPort()}/${base64Encode(projectDir)}/session/${session.id}`
175
+ });
153
176
  timer.end(`Created session: ${session.id}`);
154
- return session;
177
+ return sessionWithUrl;
155
178
  } catch (e) {
156
179
  lastError = e instanceof Error ? e : new Error(String(e));
157
180
  log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, {
@@ -169,6 +192,66 @@ class OpenCodeAPI {
169
192
  throw lastError;
170
193
  });
171
194
  }
195
+ getCheapestModel() {
196
+ return __async(this, null, function* () {
197
+ var _a, _b;
198
+ try {
199
+ const response = yield this.createHttpRequest({
200
+ hostname: this.hostname,
201
+ port: this.getPort(),
202
+ path: "/provider",
203
+ method: "GET"
204
+ });
205
+ const connectedProviders = new Set(response.connected);
206
+ const allModels = [];
207
+ for (const provider of response.all) {
208
+ if (provider.id === "opencode") continue;
209
+ if (!connectedProviders.has(provider.id)) {
210
+ log.debug("Skipping not connected provider", { providerID: provider.id });
211
+ continue;
212
+ }
213
+ for (const [modelID, model] of Object.entries(provider.models)) {
214
+ allModels.push({
215
+ providerID: provider.id,
216
+ modelID,
217
+ name: model.name,
218
+ inputCost: (_b = (_a = model.cost) == null ? void 0 : _a.input) != null ? _b : 0,
219
+ releaseDate: model.release_date
220
+ });
221
+ }
222
+ }
223
+ allModels.sort((a, b) => a.inputCost - b.inputCost);
224
+ const availableModel = allModels.find(
225
+ (model) => !this.failedFreeModels.has(`${model.providerID}:${model.modelID}`)
226
+ );
227
+ if (!availableModel) {
228
+ log.debug("All models have failed", {
229
+ totalModels: allModels.length,
230
+ failedModels: this.failedFreeModels.size,
231
+ connectedProviders: response.connected
232
+ });
233
+ return null;
234
+ }
235
+ log.debug("Found cheapest available model for warmup", {
236
+ providerID: availableModel.providerID,
237
+ modelID: availableModel.modelID,
238
+ name: availableModel.name,
239
+ inputCost: availableModel.inputCost,
240
+ releaseDate: availableModel.releaseDate,
241
+ totalModels: allModels.length,
242
+ failedModels: this.failedFreeModels.size,
243
+ connectedProviders: response.connected
244
+ });
245
+ return {
246
+ providerID: availableModel.providerID,
247
+ modelID: availableModel.modelID
248
+ };
249
+ } catch (error) {
250
+ log.warn("Failed to get cheapest model", { error });
251
+ return null;
252
+ }
253
+ });
254
+ }
172
255
  deleteSession(_0) {
173
256
  return __async(this, arguments, function* (sessionId, retries = DEFAULT_RETRIES) {
174
257
  const timer = log.timer("deleteSession", { sessionId, retries });
@@ -239,23 +322,44 @@ class OpenCodeAPI {
239
322
  throw lastError;
240
323
  });
241
324
  }
242
- warmupChromeMcp(viteOrigin) {
325
+ warmupChromeMcp(projectDir, viteOrigin) {
243
326
  return __async(this, null, function* () {
244
327
  if (!this.warmupChromeMcpConfig) return;
245
328
  const timer = log.timer("warmupChromeMcp", { viteOrigin });
246
329
  let warmupSessionId = null;
330
+ let freeModel = null;
331
+ const chromeAvailable = yield checkChromeDevToolsAvailable();
332
+ if (!chromeAvailable) {
333
+ const error = new ChromeMcpWarmupError(
334
+ ChromeMcpWarmupErrorType.CHROME_NOT_CONNECTED,
335
+ "Chrome DevTools Protocol is not available",
336
+ "Chrome remote debugging is not enabled or not running on port 9222. Please enable Chrome remote debugging first."
337
+ );
338
+ log.warn("Chrome DevTools not available", {
339
+ port: CHROME_DEVTOOLS_PORT,
340
+ hint: "Enable Chrome remote debugging at chrome://inspect/#remote-debugging"
341
+ });
342
+ timer.end("Chrome DevTools not available");
343
+ throw error;
344
+ }
345
+ log.debug("Chrome DevTools is available, proceeding with warmup");
247
346
  try {
248
- const warmupSession = yield this.createSession(DEFAULT_RETRIES, "__chrome_mcp_warmup__");
347
+ const warmupSession = yield this.createSession(
348
+ projectDir,
349
+ DEFAULT_RETRIES,
350
+ "__chrome_mcp_warmup__"
351
+ );
249
352
  warmupSessionId = warmupSession.id;
250
- const prompt = [
251
- "Call the browser tool list_pages immediately to establish the Chrome DevTools MCP connection.",
252
- viteOrigin ? `If there are no pages, call new_page with ${viteOrigin}.` : "If there are no pages, call new_page with about:blank.",
253
- "Do not read or modify project files.",
254
- "Do not use any non-browser tools.",
255
- "After the tool call is complete, reply with exactly: ready",
256
- "If the tool call fails, reply with exactly: fail"
257
- ].join(" ");
258
- const WARMUP_TIMEOUT = 3e4;
353
+ freeModel = yield this.getCheapestModel();
354
+ if (freeModel) {
355
+ log.debug("Using cheapest model for warmup", {
356
+ providerID: freeModel.providerID,
357
+ modelID: freeModel.modelID
358
+ });
359
+ } else {
360
+ log.debug("No model available, using default model");
361
+ }
362
+ const WARMUP_TIMEOUT = 6e4;
259
363
  const data = yield this.createHttpRequest(
260
364
  {
261
365
  hostname: this.hostname,
@@ -264,21 +368,87 @@ class OpenCodeAPI {
264
368
  method: "POST",
265
369
  headers: { "Content-Type": "application/json" }
266
370
  },
267
- JSON.stringify({
268
- system: "You are warming up Chrome DevTools MCP during startup. You must use the available browser tools immediately before replying.",
269
- parts: [{ type: "text", text: prompt }]
270
- }),
371
+ JSON.stringify(__spreadValues({
372
+ parts: [
373
+ {
374
+ type: "text",
375
+ text: "Test if the chrome-devtools_list_pages tool is available. If available, reply with: ready. If not available, explain why."
376
+ }
377
+ ]
378
+ }, freeModel && {
379
+ model: {
380
+ providerID: freeModel.providerID,
381
+ modelID: freeModel.modelID
382
+ }
383
+ })),
271
384
  WARMUP_TIMEOUT
272
385
  );
273
386
  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"}`);
387
+ if (!responseText) {
388
+ throw new ChromeMcpWarmupError(
389
+ ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
390
+ "AI did not respond to the warmup request",
391
+ "Empty response from AI"
392
+ );
393
+ }
394
+ const lowerResponse = responseText.toLowerCase();
395
+ if (!lowerResponse.includes("ready")) {
396
+ throw new ChromeMcpWarmupError(
397
+ ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
398
+ "AI response does not indicate success",
399
+ `AI responded with: ${responseText.substring(0, 200)}`
400
+ );
276
401
  }
277
402
  timer.end("Chrome MCP warmed up");
278
403
  } catch (e) {
279
- log.warn("Failed to warm up Chrome MCP", { error: e });
280
- timer.end("Chrome MCP warmup skipped");
281
- throw e;
404
+ if (e instanceof ChromeMcpWarmupError) {
405
+ if (e.type === ChromeMcpWarmupErrorType.SESSION_ERROR) {
406
+ timer.end("Session creation failed");
407
+ }
408
+ log.warn(`Chrome MCP warmup failed: ${e.type}`, __spreadValues({
409
+ message: e.message,
410
+ details: e.details
411
+ }, freeModel && {
412
+ model: `${freeModel.providerID}/${freeModel.modelID}`
413
+ }));
414
+ timer.end(`Chrome MCP warmup failed: ${e.type}`);
415
+ throw e;
416
+ }
417
+ if (freeModel) {
418
+ this.markModelAsFailed(freeModel.providerID, freeModel.modelID);
419
+ log.debug("Marked model as failed due to warmup error", {
420
+ providerID: freeModel.providerID,
421
+ modelID: freeModel.modelID,
422
+ error: e instanceof Error ? e.message : String(e)
423
+ });
424
+ }
425
+ const errorMessage = e instanceof Error ? e.message : String(e);
426
+ if (errorMessage.includes("timeout") || errorMessage.includes("Timeout")) {
427
+ const error2 = new ChromeMcpWarmupError(
428
+ ChromeMcpWarmupErrorType.AI_TIMEOUT,
429
+ "AI response timeout",
430
+ "AI did not respond within 30 seconds. Please check if the OpenCode AI model is properly configured and available."
431
+ );
432
+ log.warn("Chrome MCP warmup timeout", __spreadValues({
433
+ error: errorMessage
434
+ }, freeModel && {
435
+ model: `${freeModel.providerID}/${freeModel.modelID}`
436
+ }));
437
+ timer.end("Chrome MCP warmup timeout");
438
+ throw error2;
439
+ }
440
+ const error = new ChromeMcpWarmupError(
441
+ ChromeMcpWarmupErrorType.UNKNOWN,
442
+ "Unknown error during Chrome MCP warmup",
443
+ errorMessage
444
+ );
445
+ log.warn("Chrome MCP warmup failed with unknown error", __spreadValues({
446
+ error: errorMessage
447
+ }, freeModel && {
448
+ model: `${freeModel.providerID}/${freeModel.modelID}`
449
+ }));
450
+ timer.end("Chrome MCP warmup failed");
451
+ throw error;
282
452
  } finally {
283
453
  if (warmupSessionId) {
284
454
  try {
@@ -293,43 +463,63 @@ class OpenCodeAPI {
293
463
  }
294
464
  });
295
465
  }
296
- getOrCreateSession() {
466
+ getOrCreateSession(projectDir) {
297
467
  return __async(this, null, function* () {
298
- const timer = log.timer("getOrCreateSession");
299
- const projectDir = process.cwd();
468
+ const timer = log.timer("getOrCreateSession", { projectDir });
300
469
  log.debug("Getting sessions...", { projectDir });
301
- const sessions = yield this.getSessions();
470
+ const sessions = yield this.getSessions(projectDir);
302
471
  log.debug(`Found ${sessions.length} sessions`, {
303
472
  sessions: sessions.map((s) => ({ id: s.id, directory: s.directory }))
304
473
  });
305
474
  const matchingSession = sessions.find((s) => s.directory === projectDir);
306
475
  if (matchingSession) {
307
- const url2 = `http://${this.hostname}:${this.getPort()}/${base64Encode(projectDir)}/session/${matchingSession.id}`;
476
+ const url2 = `http://${this.hostname}:${this.getProxyPort()}/${base64Encode(projectDir)}/session/${matchingSession.id}`;
308
477
  timer.end(`Using existing session: ${matchingSession.id}`);
309
478
  return url2;
310
479
  }
311
480
  log.debug("Creating new session...", { projectDir });
312
- const newSession = yield this.createSession();
313
- const url = `http://${this.hostname}:${this.getPort()}/${base64Encode(projectDir)}/session/${newSession.id}`;
481
+ const newSession = yield this.createSession(projectDir);
482
+ const url = `http://${this.hostname}:${this.getProxyPort()}/${base64Encode(projectDir)}/session/${newSession.id}`;
314
483
  timer.end(`Created new session: ${newSession.id}`);
315
484
  return url;
316
485
  });
317
486
  }
318
- retryWarmupChromeMcp(viteOrigin) {
487
+ retryWarmupChromeMcp(projectDir, viteOrigin) {
319
488
  return __async(this, null, function* () {
320
489
  const timer = log.timer("retryWarmupChromeMcp", { viteOrigin });
321
490
  let warmupSessionId = null;
491
+ let freeModel = null;
492
+ const chromeAvailable = yield checkChromeDevToolsAvailable();
493
+ if (!chromeAvailable) {
494
+ const error = new ChromeMcpWarmupError(
495
+ ChromeMcpWarmupErrorType.CHROME_NOT_CONNECTED,
496
+ "Chrome DevTools Protocol is not available",
497
+ "Chrome remote debugging is not enabled or not running on port 9222. Please enable Chrome remote debugging first."
498
+ );
499
+ log.warn("Chrome DevTools not available for retry", {
500
+ port: CHROME_DEVTOOLS_PORT,
501
+ hint: "Enable Chrome remote debugging at chrome://inspect/#remote-debugging"
502
+ });
503
+ timer.end("Chrome DevTools not available for retry");
504
+ return { success: false, error };
505
+ }
506
+ log.debug("Chrome DevTools is available, proceeding with retry warmup");
322
507
  try {
323
- const warmupSession = yield this.createSession(DEFAULT_RETRIES, "__chrome_mcp_warmup__");
508
+ const warmupSession = yield this.createSession(
509
+ projectDir,
510
+ DEFAULT_RETRIES,
511
+ "__chrome_mcp_warmup__"
512
+ );
324
513
  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(" ");
514
+ freeModel = yield this.getCheapestModel();
515
+ if (freeModel) {
516
+ log.debug("Using cheapest model for retry warmup", {
517
+ providerID: freeModel.providerID,
518
+ modelID: freeModel.modelID
519
+ });
520
+ } else {
521
+ log.debug("No model available for retry, using default model");
522
+ }
333
523
  const WARMUP_TIMEOUT = 6e4;
334
524
  const data = yield this.createHttpRequest(
335
525
  {
@@ -339,23 +529,89 @@ class OpenCodeAPI {
339
529
  method: "POST",
340
530
  headers: { "Content-Type": "application/json" }
341
531
  },
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
- }),
532
+ JSON.stringify(__spreadValues({
533
+ parts: [
534
+ {
535
+ type: "text",
536
+ text: "Test if the chrome-devtools_list_pages tool is available. If available, reply with: ready. If not available, explain why."
537
+ }
538
+ ]
539
+ }, freeModel && {
540
+ model: {
541
+ providerID: freeModel.providerID,
542
+ modelID: freeModel.modelID
543
+ }
544
+ })),
346
545
  WARMUP_TIMEOUT
347
546
  );
348
547
  log.debug("Chrome MCP warmup response:", { data });
349
548
  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"}`);
549
+ if (!responseText) {
550
+ throw new ChromeMcpWarmupError(
551
+ ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
552
+ "AI did not respond to the warmup request",
553
+ "Empty response from AI"
554
+ );
555
+ }
556
+ const lowerResponse = responseText.toLowerCase();
557
+ if (!lowerResponse.includes("ready")) {
558
+ throw new ChromeMcpWarmupError(
559
+ ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
560
+ "AI response does not indicate success",
561
+ `AI responded with: ${responseText.substring(0, 200)}`
562
+ );
352
563
  }
353
564
  timer.end("Chrome MCP warmed up successfully");
354
- return true;
565
+ return { success: true };
355
566
  } catch (e) {
356
- log.warn("Failed to retry warm up Chrome MCP", { error: e });
567
+ if (e instanceof ChromeMcpWarmupError) {
568
+ if (e.type === ChromeMcpWarmupErrorType.SESSION_ERROR) {
569
+ timer.end("Session creation failed");
570
+ }
571
+ log.warn(`Chrome MCP warmup retry failed: ${e.type}`, __spreadValues({
572
+ message: e.message,
573
+ details: e.details
574
+ }, freeModel && {
575
+ model: `${freeModel.providerID}/${freeModel.modelID}`
576
+ }));
577
+ timer.end(`Chrome MCP warmup retry failed: ${e.type}`);
578
+ return { success: false, error: e };
579
+ }
580
+ if (freeModel) {
581
+ this.markModelAsFailed(freeModel.providerID, freeModel.modelID);
582
+ log.debug("Marked model as failed due to retry warmup error", {
583
+ providerID: freeModel.providerID,
584
+ modelID: freeModel.modelID,
585
+ error: e instanceof Error ? e.message : String(e)
586
+ });
587
+ }
588
+ const errorMessage = e instanceof Error ? e.message : String(e);
589
+ if (errorMessage.includes("timeout") || errorMessage.includes("Timeout")) {
590
+ const error2 = new ChromeMcpWarmupError(
591
+ ChromeMcpWarmupErrorType.AI_TIMEOUT,
592
+ "AI response timeout",
593
+ "AI did not respond within 60 seconds. Please check if the OpenCode AI model is properly configured and available."
594
+ );
595
+ log.warn("Chrome MCP warmup retry timeout", __spreadValues({
596
+ error: errorMessage
597
+ }, freeModel && {
598
+ model: `${freeModel.providerID}/${freeModel.modelID}`
599
+ }));
600
+ timer.end("Chrome MCP warmup retry timeout");
601
+ return { success: false, error: error2 };
602
+ }
603
+ const error = new ChromeMcpWarmupError(
604
+ ChromeMcpWarmupErrorType.UNKNOWN,
605
+ "Unknown error during Chrome MCP warmup retry",
606
+ errorMessage
607
+ );
608
+ log.warn("Chrome MCP warmup retry failed with unknown error", __spreadValues({
609
+ error: errorMessage
610
+ }, freeModel && {
611
+ model: `${freeModel.providerID}/${freeModel.modelID}`
612
+ }));
357
613
  timer.end("Chrome MCP warmup retry failed");
358
- return false;
614
+ return { success: false, error };
359
615
  } finally {
360
616
  if (warmupSessionId) {
361
617
  try {