vite-plugin-opencode-assistant 1.0.15 → 1.0.16

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 +300 -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 +324 -69
  27. package/es/core/proxy-server.js +127 -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 +329 -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 +2988 -2973
  64. package/lib/core/api.d.ts +18 -6
  65. package/lib/core/api.js +321 -74
  66. package/lib/core/proxy-server.js +127 -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,63 @@ 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 (!connectedProviders.has(provider.id)) {
209
+ log.debug("Skipping not connected provider", { providerID: provider.id });
210
+ continue;
211
+ }
212
+ for (const [modelID, model] of Object.entries(provider.models)) {
213
+ allModels.push({
214
+ providerID: provider.id,
215
+ modelID,
216
+ name: model.name,
217
+ inputCost: (_b = (_a = model.cost) == null ? void 0 : _a.input) != null ? _b : 0
218
+ });
219
+ }
220
+ }
221
+ allModels.sort((a, b) => a.inputCost - b.inputCost);
222
+ const availableModel = allModels.find(
223
+ (model) => !this.failedFreeModels.has(`${model.providerID}:${model.modelID}`)
224
+ );
225
+ if (!availableModel) {
226
+ log.debug("All models have failed", {
227
+ totalModels: allModels.length,
228
+ failedModels: this.failedFreeModels.size,
229
+ connectedProviders: response.connected
230
+ });
231
+ return null;
232
+ }
233
+ log.debug("Found cheapest available model for warmup", {
234
+ providerID: availableModel.providerID,
235
+ modelID: availableModel.modelID,
236
+ name: availableModel.name,
237
+ inputCost: availableModel.inputCost,
238
+ totalModels: allModels.length,
239
+ failedModels: this.failedFreeModels.size,
240
+ connectedProviders: response.connected
241
+ });
242
+ return {
243
+ providerID: availableModel.providerID,
244
+ modelID: availableModel.modelID
245
+ };
246
+ } catch (error) {
247
+ log.warn("Failed to get cheapest model", { error });
248
+ return null;
249
+ }
250
+ });
251
+ }
172
252
  deleteSession(_0) {
173
253
  return __async(this, arguments, function* (sessionId, retries = DEFAULT_RETRIES) {
174
254
  const timer = log.timer("deleteSession", { sessionId, retries });
@@ -239,13 +319,33 @@ class OpenCodeAPI {
239
319
  throw lastError;
240
320
  });
241
321
  }
242
- warmupChromeMcp(viteOrigin) {
322
+ warmupChromeMcp(projectDir, viteOrigin) {
243
323
  return __async(this, null, function* () {
244
324
  if (!this.warmupChromeMcpConfig) return;
245
325
  const timer = log.timer("warmupChromeMcp", { viteOrigin });
246
326
  let warmupSessionId = null;
327
+ let freeModel = null;
328
+ const chromeAvailable = yield checkChromeDevToolsAvailable();
329
+ if (!chromeAvailable) {
330
+ const error = new ChromeMcpWarmupError(
331
+ ChromeMcpWarmupErrorType.CHROME_NOT_CONNECTED,
332
+ "Chrome DevTools Protocol is not available",
333
+ "Chrome remote debugging is not enabled or not running on port 9222. Please enable Chrome remote debugging first."
334
+ );
335
+ log.warn("Chrome DevTools not available", {
336
+ port: CHROME_DEVTOOLS_PORT,
337
+ hint: "Enable Chrome remote debugging at chrome://inspect/#remote-debugging"
338
+ });
339
+ timer.end("Chrome DevTools not available");
340
+ throw error;
341
+ }
342
+ log.debug("Chrome DevTools is available, proceeding with warmup");
247
343
  try {
248
- const warmupSession = yield this.createSession(DEFAULT_RETRIES, "__chrome_mcp_warmup__");
344
+ const warmupSession = yield this.createSession(
345
+ projectDir,
346
+ DEFAULT_RETRIES,
347
+ "__chrome_mcp_warmup__"
348
+ );
249
349
  warmupSessionId = warmupSession.id;
250
350
  const prompt = [
251
351
  "Call the browser tool list_pages immediately to establish the Chrome DevTools MCP connection.",
@@ -255,7 +355,16 @@ class OpenCodeAPI {
255
355
  "After the tool call is complete, reply with exactly: ready",
256
356
  "If the tool call fails, reply with exactly: fail"
257
357
  ].join(" ");
258
- const WARMUP_TIMEOUT = 3e4;
358
+ const WARMUP_TIMEOUT = 6e4;
359
+ freeModel = yield this.getCheapestModel();
360
+ if (freeModel) {
361
+ log.debug("Using cheapest model for warmup", {
362
+ providerID: freeModel.providerID,
363
+ modelID: freeModel.modelID
364
+ });
365
+ } else {
366
+ log.debug("No model available, using default model");
367
+ }
259
368
  const data = yield this.createHttpRequest(
260
369
  {
261
370
  hostname: this.hostname,
@@ -264,21 +373,80 @@ class OpenCodeAPI {
264
373
  method: "POST",
265
374
  headers: { "Content-Type": "application/json" }
266
375
  },
267
- JSON.stringify({
376
+ JSON.stringify(__spreadValues({
268
377
  system: "You are warming up Chrome DevTools MCP during startup. You must use the available browser tools immediately before replying.",
269
378
  parts: [{ type: "text", text: prompt }]
270
- }),
379
+ }, freeModel && {
380
+ model: {
381
+ providerID: freeModel.providerID,
382
+ modelID: freeModel.modelID
383
+ }
384
+ })),
271
385
  WARMUP_TIMEOUT
272
386
  );
273
387
  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"}`);
388
+ if (!responseText) {
389
+ throw new ChromeMcpWarmupError(
390
+ ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
391
+ "AI did not respond to the warmup request",
392
+ "Empty response from AI"
393
+ );
394
+ }
395
+ const lowerResponse = responseText.toLowerCase();
396
+ if (lowerResponse.includes("fail")) {
397
+ throw new ChromeMcpWarmupError(
398
+ ChromeMcpWarmupErrorType.CHROME_NOT_CONNECTED,
399
+ "Chrome DevTools MCP is not connected",
400
+ "AI reported that browser tools are not available. This should not happen if Chrome DevTools check passed."
401
+ );
402
+ }
403
+ if (!lowerResponse.includes("ready")) {
404
+ throw new ChromeMcpWarmupError(
405
+ ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
406
+ "AI response does not indicate success",
407
+ `AI responded with: ${responseText.substring(0, 200)}`
408
+ );
276
409
  }
277
410
  timer.end("Chrome MCP warmed up");
278
411
  } catch (e) {
279
- log.warn("Failed to warm up Chrome MCP", { error: e });
280
- timer.end("Chrome MCP warmup skipped");
281
- throw e;
412
+ if (e instanceof ChromeMcpWarmupError) {
413
+ if (e.type === ChromeMcpWarmupErrorType.SESSION_ERROR) {
414
+ timer.end("Session creation failed");
415
+ }
416
+ log.warn(`Chrome MCP warmup failed: ${e.type}`, {
417
+ message: e.message,
418
+ details: e.details
419
+ });
420
+ timer.end(`Chrome MCP warmup failed: ${e.type}`);
421
+ throw e;
422
+ }
423
+ if (freeModel) {
424
+ this.markModelAsFailed(freeModel.providerID, freeModel.modelID);
425
+ log.debug("Marked model as failed due to warmup error", {
426
+ providerID: freeModel.providerID,
427
+ modelID: freeModel.modelID,
428
+ error: e instanceof Error ? e.message : String(e)
429
+ });
430
+ }
431
+ const errorMessage = e instanceof Error ? e.message : String(e);
432
+ if (errorMessage.includes("timeout") || errorMessage.includes("Timeout")) {
433
+ const error2 = new ChromeMcpWarmupError(
434
+ ChromeMcpWarmupErrorType.AI_TIMEOUT,
435
+ "AI response timeout",
436
+ "AI did not respond within 30 seconds. Please check if the OpenCode AI model is properly configured and available."
437
+ );
438
+ log.warn("Chrome MCP warmup timeout", { error: errorMessage });
439
+ timer.end("Chrome MCP warmup timeout");
440
+ throw error2;
441
+ }
442
+ const error = new ChromeMcpWarmupError(
443
+ ChromeMcpWarmupErrorType.UNKNOWN,
444
+ "Unknown error during Chrome MCP warmup",
445
+ errorMessage
446
+ );
447
+ log.warn("Chrome MCP warmup failed with unknown error", { error: errorMessage });
448
+ timer.end("Chrome MCP warmup failed");
449
+ throw error;
282
450
  } finally {
283
451
  if (warmupSessionId) {
284
452
  try {
@@ -293,34 +461,53 @@ class OpenCodeAPI {
293
461
  }
294
462
  });
295
463
  }
296
- getOrCreateSession() {
464
+ getOrCreateSession(projectDir) {
297
465
  return __async(this, null, function* () {
298
- const timer = log.timer("getOrCreateSession");
299
- const projectDir = process.cwd();
466
+ const timer = log.timer("getOrCreateSession", { projectDir });
300
467
  log.debug("Getting sessions...", { projectDir });
301
- const sessions = yield this.getSessions();
468
+ const sessions = yield this.getSessions(projectDir);
302
469
  log.debug(`Found ${sessions.length} sessions`, {
303
470
  sessions: sessions.map((s) => ({ id: s.id, directory: s.directory }))
304
471
  });
305
472
  const matchingSession = sessions.find((s) => s.directory === projectDir);
306
473
  if (matchingSession) {
307
- const url2 = `http://${this.hostname}:${this.getPort()}/${base64Encode(projectDir)}/session/${matchingSession.id}`;
474
+ const url2 = `http://${this.hostname}:${this.getProxyPort()}/${base64Encode(projectDir)}/session/${matchingSession.id}`;
308
475
  timer.end(`Using existing session: ${matchingSession.id}`);
309
476
  return url2;
310
477
  }
311
478
  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}`;
479
+ const newSession = yield this.createSession(projectDir);
480
+ const url = `http://${this.hostname}:${this.getProxyPort()}/${base64Encode(projectDir)}/session/${newSession.id}`;
314
481
  timer.end(`Created new session: ${newSession.id}`);
315
482
  return url;
316
483
  });
317
484
  }
318
- retryWarmupChromeMcp(viteOrigin) {
485
+ retryWarmupChromeMcp(projectDir, viteOrigin) {
319
486
  return __async(this, null, function* () {
320
487
  const timer = log.timer("retryWarmupChromeMcp", { viteOrigin });
321
488
  let warmupSessionId = null;
489
+ let freeModel = null;
490
+ const chromeAvailable = yield checkChromeDevToolsAvailable();
491
+ if (!chromeAvailable) {
492
+ const error = new ChromeMcpWarmupError(
493
+ ChromeMcpWarmupErrorType.CHROME_NOT_CONNECTED,
494
+ "Chrome DevTools Protocol is not available",
495
+ "Chrome remote debugging is not enabled or not running on port 9222. Please enable Chrome remote debugging first."
496
+ );
497
+ log.warn("Chrome DevTools not available for retry", {
498
+ port: CHROME_DEVTOOLS_PORT,
499
+ hint: "Enable Chrome remote debugging at chrome://inspect/#remote-debugging"
500
+ });
501
+ timer.end("Chrome DevTools not available for retry");
502
+ return { success: false, error };
503
+ }
504
+ log.debug("Chrome DevTools is available, proceeding with retry warmup");
322
505
  try {
323
- const warmupSession = yield this.createSession(DEFAULT_RETRIES, "__chrome_mcp_warmup__");
506
+ const warmupSession = yield this.createSession(
507
+ projectDir,
508
+ DEFAULT_RETRIES,
509
+ "__chrome_mcp_warmup__"
510
+ );
324
511
  warmupSessionId = warmupSession.id;
325
512
  const prompt = [
326
513
  "Call the browser tool list_pages immediately to establish the Chrome DevTools MCP connection.",
@@ -331,6 +518,15 @@ class OpenCodeAPI {
331
518
  "If the tool call fails, reply with exactly: fail"
332
519
  ].join(" ");
333
520
  const WARMUP_TIMEOUT = 6e4;
521
+ freeModel = yield this.getCheapestModel();
522
+ if (freeModel) {
523
+ log.debug("Using cheapest model for retry warmup", {
524
+ providerID: freeModel.providerID,
525
+ modelID: freeModel.modelID
526
+ });
527
+ } else {
528
+ log.debug("No model available for retry, using default model");
529
+ }
334
530
  const data = yield this.createHttpRequest(
335
531
  {
336
532
  hostname: this.hostname,
@@ -339,23 +535,82 @@ class OpenCodeAPI {
339
535
  method: "POST",
340
536
  headers: { "Content-Type": "application/json" }
341
537
  },
342
- JSON.stringify({
538
+ JSON.stringify(__spreadValues({
343
539
  system: "You are warming up Chrome DevTools MCP during startup. You must use the available browser tools immediately before replying.",
344
540
  parts: [{ type: "text", text: prompt }]
345
- }),
541
+ }, freeModel && {
542
+ model: {
543
+ providerID: freeModel.providerID,
544
+ modelID: freeModel.modelID
545
+ }
546
+ })),
346
547
  WARMUP_TIMEOUT
347
548
  );
348
549
  log.debug("Chrome MCP warmup response:", { data });
349
550
  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"}`);
551
+ if (!responseText) {
552
+ throw new ChromeMcpWarmupError(
553
+ ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
554
+ "AI did not respond to the warmup request",
555
+ "Empty response from AI"
556
+ );
557
+ }
558
+ const lowerResponse = responseText.toLowerCase();
559
+ if (lowerResponse.includes("fail")) {
560
+ throw new ChromeMcpWarmupError(
561
+ ChromeMcpWarmupErrorType.CHROME_NOT_CONNECTED,
562
+ "Chrome DevTools MCP is not connected",
563
+ "AI reported that browser tools are not available. This should not happen if Chrome DevTools check passed."
564
+ );
565
+ }
566
+ if (!lowerResponse.includes("ready")) {
567
+ throw new ChromeMcpWarmupError(
568
+ ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
569
+ "AI response does not indicate success",
570
+ `AI responded with: ${responseText.substring(0, 200)}`
571
+ );
352
572
  }
353
573
  timer.end("Chrome MCP warmed up successfully");
354
- return true;
574
+ return { success: true };
355
575
  } catch (e) {
356
- log.warn("Failed to retry warm up Chrome MCP", { error: e });
576
+ if (e instanceof ChromeMcpWarmupError) {
577
+ if (e.type === ChromeMcpWarmupErrorType.SESSION_ERROR) {
578
+ timer.end("Session creation failed");
579
+ }
580
+ log.warn(`Chrome MCP warmup retry failed: ${e.type}`, {
581
+ message: e.message,
582
+ details: e.details
583
+ });
584
+ timer.end(`Chrome MCP warmup retry failed: ${e.type}`);
585
+ return { success: false, error: e };
586
+ }
587
+ if (freeModel) {
588
+ this.markModelAsFailed(freeModel.providerID, freeModel.modelID);
589
+ log.debug("Marked model as failed due to retry warmup error", {
590
+ providerID: freeModel.providerID,
591
+ modelID: freeModel.modelID,
592
+ error: e instanceof Error ? e.message : String(e)
593
+ });
594
+ }
595
+ const errorMessage = e instanceof Error ? e.message : String(e);
596
+ if (errorMessage.includes("timeout") || errorMessage.includes("Timeout")) {
597
+ const error2 = new ChromeMcpWarmupError(
598
+ ChromeMcpWarmupErrorType.AI_TIMEOUT,
599
+ "AI response timeout",
600
+ "AI did not respond within 60 seconds. Please check if the OpenCode AI model is properly configured and available."
601
+ );
602
+ log.warn("Chrome MCP warmup retry timeout", { error: errorMessage });
603
+ timer.end("Chrome MCP warmup retry timeout");
604
+ return { success: false, error: error2 };
605
+ }
606
+ const error = new ChromeMcpWarmupError(
607
+ ChromeMcpWarmupErrorType.UNKNOWN,
608
+ "Unknown error during Chrome MCP warmup retry",
609
+ errorMessage
610
+ );
611
+ log.warn("Chrome MCP warmup retry failed with unknown error", { error: errorMessage });
357
612
  timer.end("Chrome MCP warmup retry failed");
358
- return false;
613
+ return { success: false, error };
359
614
  } finally {
360
615
  if (warmupSessionId) {
361
616
  try {