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/lib/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/lib/core/api.js CHANGED
@@ -1,10 +1,26 @@
1
1
  var __create = Object.create;
2
2
  var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
8
  var __getProtoOf = Object.getPrototypeOf;
6
9
  var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
11
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
12
+ var __spreadValues = (a, b) => {
13
+ for (var prop in b || (b = {}))
14
+ if (__hasOwnProp.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ if (__getOwnPropSymbols)
17
+ for (var prop of __getOwnPropSymbols(b)) {
18
+ if (__propIsEnum.call(b, prop))
19
+ __defNormalProp(a, prop, b[prop]);
20
+ }
21
+ return a;
22
+ };
23
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
8
24
  var __export = (target, all) => {
9
25
  for (var name in all)
10
26
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -55,40 +71,27 @@ module.exports = __toCommonJS(api_exports);
55
71
  var import_http = __toESM(require("http"));
56
72
  var import_shared = require("@vite-plugin-opencode-assistant/shared");
57
73
  const log = (0, import_shared.createLogger)("API");
58
- function sleep(ms) {
59
- return new Promise((resolve) => setTimeout(resolve, ms));
60
- }
61
- function base64Encode(str) {
62
- return Buffer.from(str).toString("base64");
63
- }
64
- function extractTextFromResponse(data) {
65
- if (!data || typeof data !== "object") return null;
66
- const obj = data;
67
- if (obj.parts && Array.isArray(obj.parts)) {
68
- const textParts = obj.parts.filter(
69
- (p) => p && typeof p === "object" && p.type === "text"
70
- ).map((p) => p.text).filter(Boolean);
71
- if (textParts.length > 0) return textParts.join("");
72
- }
73
- if (obj.text && typeof obj.text === "string") {
74
- return obj.text;
75
- }
76
- if (obj.content && typeof obj.content === "string") {
77
- return obj.content;
78
- }
79
- if (obj.message && typeof obj.message === "string") {
80
- return obj.message;
81
- }
82
- if (typeof data === "string") {
83
- return data;
84
- }
85
- return null;
86
- }
87
74
  class OpenCodeAPI {
88
- constructor(hostname, getPort, warmupChromeMcpConfig = false) {
75
+ constructor(hostname, getPort, getProxyPort, warmupChromeMcpConfig = false) {
89
76
  __publicField(this, "hostname", hostname);
90
77
  __publicField(this, "getPort", getPort);
78
+ __publicField(this, "getProxyPort", getProxyPort);
91
79
  __publicField(this, "warmupChromeMcpConfig", warmupChromeMcpConfig);
80
+ __publicField(this, "failedFreeModels", /* @__PURE__ */ new Set());
81
+ }
82
+ markModelAsFailed(providerID, modelID) {
83
+ const key = `${providerID}:${modelID}`;
84
+ this.failedFreeModels.add(key);
85
+ log.debug("Marked model as failed", {
86
+ providerID,
87
+ modelID,
88
+ key,
89
+ failedCount: this.failedFreeModels.size
90
+ });
91
+ }
92
+ clearFailedModels() {
93
+ this.failedFreeModels.clear();
94
+ log.debug("Cleared failed models cache");
92
95
  }
93
96
  createHttpRequest(options, body, timeout) {
94
97
  const timer = new import_shared.PerformanceTimer("HTTP Request", {
@@ -124,20 +127,26 @@ class OpenCodeAPI {
124
127
  req.end();
125
128
  });
126
129
  }
127
- getSessions() {
128
- return __async(this, arguments, function* (retries = import_shared.DEFAULT_RETRIES) {
129
- const timer = log.timer("getSessions", { retries });
130
+ getSessions(_0) {
131
+ return __async(this, arguments, function* (projectDir, retries = import_shared.DEFAULT_RETRIES) {
132
+ const timer = log.timer("getSessions", { retries, projectDir });
130
133
  let lastError = null;
131
134
  for (let i = 0; i < retries; i++) {
132
135
  try {
133
- log.debug(`Attempt ${i + 1}/${retries}`, { operation: "getSessions" });
136
+ log.debug(`Attempt ${i + 1}/${retries}`, { operation: "getSessions", projectDir });
134
137
  const sessions = yield this.createHttpRequest({
135
138
  hostname: this.hostname,
136
139
  port: this.getPort(),
137
- path: "/session"
140
+ path: "/session",
141
+ headers: {
142
+ "x-opencode-directory": encodeURIComponent(projectDir)
143
+ }
138
144
  });
145
+ const sessionsWithUrl = sessions.map((s) => __spreadProps(__spreadValues({}, s), {
146
+ url: `http://${this.hostname}:${this.getProxyPort()}/${(0, import_shared.base64Encode)(s.directory)}/session/${s.id}`
147
+ }));
139
148
  timer.end(`Found ${sessions.length} sessions`);
140
- return sessions;
149
+ return sessionsWithUrl;
141
150
  } catch (e) {
142
151
  lastError = e instanceof Error ? e : new Error(String(e));
143
152
  log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, {
@@ -147,7 +156,7 @@ class OpenCodeAPI {
147
156
  log.debug(`Retrying in ${import_shared.RETRY_DELAY}ms...`, {
148
157
  operation: "getSessions"
149
158
  });
150
- yield sleep(import_shared.RETRY_DELAY);
159
+ yield (0, import_shared.sleep)(import_shared.RETRY_DELAY);
151
160
  }
152
161
  }
153
162
  }
@@ -155,15 +164,16 @@ class OpenCodeAPI {
155
164
  throw lastError;
156
165
  });
157
166
  }
158
- createSession() {
159
- return __async(this, arguments, function* (retries = import_shared.DEFAULT_RETRIES, title) {
160
- const timer = log.timer("createSession", { retries, title });
167
+ createSession(_0) {
168
+ return __async(this, arguments, function* (projectDir, retries = import_shared.DEFAULT_RETRIES, title) {
169
+ const timer = log.timer("createSession", { retries, title, projectDir });
161
170
  let lastError = null;
162
171
  for (let i = 0; i < retries; i++) {
163
172
  try {
164
173
  log.debug(`Attempt ${i + 1}/${retries}`, {
165
174
  operation: "createSession",
166
- title
175
+ title,
176
+ projectDir
167
177
  });
168
178
  const requestBody = title ? JSON.stringify({ title }) : void 0;
169
179
  const session = yield this.createHttpRequest(
@@ -172,12 +182,17 @@ class OpenCodeAPI {
172
182
  port: this.getPort(),
173
183
  path: "/session",
174
184
  method: "POST",
175
- headers: requestBody ? { "Content-Type": "application/json" } : void 0
185
+ headers: __spreadProps(__spreadValues({}, requestBody ? { "Content-Type": "application/json" } : {}), {
186
+ "x-opencode-directory": encodeURIComponent(projectDir)
187
+ })
176
188
  },
177
189
  requestBody
178
190
  );
191
+ const sessionWithUrl = __spreadProps(__spreadValues({}, session), {
192
+ url: `http://${this.hostname}:${this.getProxyPort()}/${(0, import_shared.base64Encode)(projectDir)}/session/${session.id}`
193
+ });
179
194
  timer.end(`Created session: ${session.id}`);
180
- return session;
195
+ return sessionWithUrl;
181
196
  } catch (e) {
182
197
  lastError = e instanceof Error ? e : new Error(String(e));
183
198
  log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, {
@@ -187,7 +202,7 @@ class OpenCodeAPI {
187
202
  log.debug(`Retrying in ${import_shared.RETRY_DELAY}ms...`, {
188
203
  operation: "createSession"
189
204
  });
190
- yield sleep(import_shared.RETRY_DELAY);
205
+ yield (0, import_shared.sleep)(import_shared.RETRY_DELAY);
191
206
  }
192
207
  }
193
208
  }
@@ -195,6 +210,63 @@ class OpenCodeAPI {
195
210
  throw lastError;
196
211
  });
197
212
  }
213
+ getCheapestModel() {
214
+ return __async(this, null, function* () {
215
+ var _a, _b;
216
+ try {
217
+ const response = yield this.createHttpRequest({
218
+ hostname: this.hostname,
219
+ port: this.getPort(),
220
+ path: "/provider",
221
+ method: "GET"
222
+ });
223
+ const connectedProviders = new Set(response.connected);
224
+ const allModels = [];
225
+ for (const provider of response.all) {
226
+ if (!connectedProviders.has(provider.id)) {
227
+ log.debug("Skipping not connected provider", { providerID: provider.id });
228
+ continue;
229
+ }
230
+ for (const [modelID, model] of Object.entries(provider.models)) {
231
+ allModels.push({
232
+ providerID: provider.id,
233
+ modelID,
234
+ name: model.name,
235
+ inputCost: (_b = (_a = model.cost) == null ? void 0 : _a.input) != null ? _b : 0
236
+ });
237
+ }
238
+ }
239
+ allModels.sort((a, b) => a.inputCost - b.inputCost);
240
+ const availableModel = allModels.find(
241
+ (model) => !this.failedFreeModels.has(`${model.providerID}:${model.modelID}`)
242
+ );
243
+ if (!availableModel) {
244
+ log.debug("All models have failed", {
245
+ totalModels: allModels.length,
246
+ failedModels: this.failedFreeModels.size,
247
+ connectedProviders: response.connected
248
+ });
249
+ return null;
250
+ }
251
+ log.debug("Found cheapest available model for warmup", {
252
+ providerID: availableModel.providerID,
253
+ modelID: availableModel.modelID,
254
+ name: availableModel.name,
255
+ inputCost: availableModel.inputCost,
256
+ totalModels: allModels.length,
257
+ failedModels: this.failedFreeModels.size,
258
+ connectedProviders: response.connected
259
+ });
260
+ return {
261
+ providerID: availableModel.providerID,
262
+ modelID: availableModel.modelID
263
+ };
264
+ } catch (error) {
265
+ log.warn("Failed to get cheapest model", { error });
266
+ return null;
267
+ }
268
+ });
269
+ }
198
270
  deleteSession(_0) {
199
271
  return __async(this, arguments, function* (sessionId, retries = import_shared.DEFAULT_RETRIES) {
200
272
  const timer = log.timer("deleteSession", { sessionId, retries });
@@ -224,7 +296,7 @@ class OpenCodeAPI {
224
296
  operation: "deleteSession",
225
297
  sessionId
226
298
  });
227
- yield sleep(import_shared.RETRY_DELAY);
299
+ yield (0, import_shared.sleep)(import_shared.RETRY_DELAY);
228
300
  }
229
301
  }
230
302
  }
@@ -257,7 +329,7 @@ class OpenCodeAPI {
257
329
  log.debug(`Retrying in ${import_shared.RETRY_DELAY}ms...`, {
258
330
  operation: "getToolIds"
259
331
  });
260
- yield sleep(import_shared.RETRY_DELAY);
332
+ yield (0, import_shared.sleep)(import_shared.RETRY_DELAY);
261
333
  }
262
334
  }
263
335
  }
@@ -265,13 +337,33 @@ class OpenCodeAPI {
265
337
  throw lastError;
266
338
  });
267
339
  }
268
- warmupChromeMcp(viteOrigin) {
340
+ warmupChromeMcp(projectDir, viteOrigin) {
269
341
  return __async(this, null, function* () {
270
342
  if (!this.warmupChromeMcpConfig) return;
271
343
  const timer = log.timer("warmupChromeMcp", { viteOrigin });
272
344
  let warmupSessionId = null;
345
+ let freeModel = null;
346
+ const chromeAvailable = yield (0, import_shared.checkChromeDevToolsAvailable)();
347
+ if (!chromeAvailable) {
348
+ const error = new import_shared.ChromeMcpWarmupError(
349
+ import_shared.ChromeMcpWarmupErrorType.CHROME_NOT_CONNECTED,
350
+ "Chrome DevTools Protocol is not available",
351
+ "Chrome remote debugging is not enabled or not running on port 9222. Please enable Chrome remote debugging first."
352
+ );
353
+ log.warn("Chrome DevTools not available", {
354
+ port: import_shared.CHROME_DEVTOOLS_PORT,
355
+ hint: "Enable Chrome remote debugging at chrome://inspect/#remote-debugging"
356
+ });
357
+ timer.end("Chrome DevTools not available");
358
+ throw error;
359
+ }
360
+ log.debug("Chrome DevTools is available, proceeding with warmup");
273
361
  try {
274
- const warmupSession = yield this.createSession(import_shared.DEFAULT_RETRIES, "__chrome_mcp_warmup__");
362
+ const warmupSession = yield this.createSession(
363
+ projectDir,
364
+ import_shared.DEFAULT_RETRIES,
365
+ "__chrome_mcp_warmup__"
366
+ );
275
367
  warmupSessionId = warmupSession.id;
276
368
  const prompt = [
277
369
  "Call the browser tool list_pages immediately to establish the Chrome DevTools MCP connection.",
@@ -281,7 +373,16 @@ class OpenCodeAPI {
281
373
  "After the tool call is complete, reply with exactly: ready",
282
374
  "If the tool call fails, reply with exactly: fail"
283
375
  ].join(" ");
284
- const WARMUP_TIMEOUT = 3e4;
376
+ const WARMUP_TIMEOUT = 6e4;
377
+ freeModel = yield this.getCheapestModel();
378
+ if (freeModel) {
379
+ log.debug("Using cheapest model for warmup", {
380
+ providerID: freeModel.providerID,
381
+ modelID: freeModel.modelID
382
+ });
383
+ } else {
384
+ log.debug("No model available, using default model");
385
+ }
285
386
  const data = yield this.createHttpRequest(
286
387
  {
287
388
  hostname: this.hostname,
@@ -290,21 +391,80 @@ class OpenCodeAPI {
290
391
  method: "POST",
291
392
  headers: { "Content-Type": "application/json" }
292
393
  },
293
- JSON.stringify({
394
+ JSON.stringify(__spreadValues({
294
395
  system: "You are warming up Chrome DevTools MCP during startup. You must use the available browser tools immediately before replying.",
295
396
  parts: [{ type: "text", text: prompt }]
296
- }),
397
+ }, freeModel && {
398
+ model: {
399
+ providerID: freeModel.providerID,
400
+ modelID: freeModel.modelID
401
+ }
402
+ })),
297
403
  WARMUP_TIMEOUT
298
404
  );
299
- const responseText = extractTextFromResponse(data);
300
- if (!(responseText == null ? void 0 : responseText.toLowerCase().includes("ready"))) {
301
- throw new Error(`Chrome MCP warmup failed: ${responseText || "No response"}`);
405
+ const responseText = (0, import_shared.extractTextFromResponse)(data);
406
+ if (!responseText) {
407
+ throw new import_shared.ChromeMcpWarmupError(
408
+ import_shared.ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
409
+ "AI did not respond to the warmup request",
410
+ "Empty response from AI"
411
+ );
412
+ }
413
+ const lowerResponse = responseText.toLowerCase();
414
+ if (lowerResponse.includes("fail")) {
415
+ throw new import_shared.ChromeMcpWarmupError(
416
+ import_shared.ChromeMcpWarmupErrorType.CHROME_NOT_CONNECTED,
417
+ "Chrome DevTools MCP is not connected",
418
+ "AI reported that browser tools are not available. This should not happen if Chrome DevTools check passed."
419
+ );
420
+ }
421
+ if (!lowerResponse.includes("ready")) {
422
+ throw new import_shared.ChromeMcpWarmupError(
423
+ import_shared.ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
424
+ "AI response does not indicate success",
425
+ `AI responded with: ${responseText.substring(0, 200)}`
426
+ );
302
427
  }
303
428
  timer.end("Chrome MCP warmed up");
304
429
  } catch (e) {
305
- log.warn("Failed to warm up Chrome MCP", { error: e });
306
- timer.end("Chrome MCP warmup skipped");
307
- throw e;
430
+ if (e instanceof import_shared.ChromeMcpWarmupError) {
431
+ if (e.type === import_shared.ChromeMcpWarmupErrorType.SESSION_ERROR) {
432
+ timer.end("Session creation failed");
433
+ }
434
+ log.warn(`Chrome MCP warmup failed: ${e.type}`, {
435
+ message: e.message,
436
+ details: e.details
437
+ });
438
+ timer.end(`Chrome MCP warmup failed: ${e.type}`);
439
+ throw e;
440
+ }
441
+ if (freeModel) {
442
+ this.markModelAsFailed(freeModel.providerID, freeModel.modelID);
443
+ log.debug("Marked model as failed due to warmup error", {
444
+ providerID: freeModel.providerID,
445
+ modelID: freeModel.modelID,
446
+ error: e instanceof Error ? e.message : String(e)
447
+ });
448
+ }
449
+ const errorMessage = e instanceof Error ? e.message : String(e);
450
+ if (errorMessage.includes("timeout") || errorMessage.includes("Timeout")) {
451
+ const error2 = new import_shared.ChromeMcpWarmupError(
452
+ import_shared.ChromeMcpWarmupErrorType.AI_TIMEOUT,
453
+ "AI response timeout",
454
+ "AI did not respond within 30 seconds. Please check if the OpenCode AI model is properly configured and available."
455
+ );
456
+ log.warn("Chrome MCP warmup timeout", { error: errorMessage });
457
+ timer.end("Chrome MCP warmup timeout");
458
+ throw error2;
459
+ }
460
+ const error = new import_shared.ChromeMcpWarmupError(
461
+ import_shared.ChromeMcpWarmupErrorType.UNKNOWN,
462
+ "Unknown error during Chrome MCP warmup",
463
+ errorMessage
464
+ );
465
+ log.warn("Chrome MCP warmup failed with unknown error", { error: errorMessage });
466
+ timer.end("Chrome MCP warmup failed");
467
+ throw error;
308
468
  } finally {
309
469
  if (warmupSessionId) {
310
470
  try {
@@ -319,34 +479,53 @@ class OpenCodeAPI {
319
479
  }
320
480
  });
321
481
  }
322
- getOrCreateSession() {
482
+ getOrCreateSession(projectDir) {
323
483
  return __async(this, null, function* () {
324
- const timer = log.timer("getOrCreateSession");
325
- const projectDir = process.cwd();
484
+ const timer = log.timer("getOrCreateSession", { projectDir });
326
485
  log.debug("Getting sessions...", { projectDir });
327
- const sessions = yield this.getSessions();
486
+ const sessions = yield this.getSessions(projectDir);
328
487
  log.debug(`Found ${sessions.length} sessions`, {
329
488
  sessions: sessions.map((s) => ({ id: s.id, directory: s.directory }))
330
489
  });
331
490
  const matchingSession = sessions.find((s) => s.directory === projectDir);
332
491
  if (matchingSession) {
333
- const url2 = `http://${this.hostname}:${this.getPort()}/${base64Encode(projectDir)}/session/${matchingSession.id}`;
492
+ const url2 = `http://${this.hostname}:${this.getProxyPort()}/${(0, import_shared.base64Encode)(projectDir)}/session/${matchingSession.id}`;
334
493
  timer.end(`Using existing session: ${matchingSession.id}`);
335
494
  return url2;
336
495
  }
337
496
  log.debug("Creating new session...", { projectDir });
338
- const newSession = yield this.createSession();
339
- const url = `http://${this.hostname}:${this.getPort()}/${base64Encode(projectDir)}/session/${newSession.id}`;
497
+ const newSession = yield this.createSession(projectDir);
498
+ const url = `http://${this.hostname}:${this.getProxyPort()}/${(0, import_shared.base64Encode)(projectDir)}/session/${newSession.id}`;
340
499
  timer.end(`Created new session: ${newSession.id}`);
341
500
  return url;
342
501
  });
343
502
  }
344
- retryWarmupChromeMcp(viteOrigin) {
503
+ retryWarmupChromeMcp(projectDir, viteOrigin) {
345
504
  return __async(this, null, function* () {
346
505
  const timer = log.timer("retryWarmupChromeMcp", { viteOrigin });
347
506
  let warmupSessionId = null;
507
+ let freeModel = null;
508
+ const chromeAvailable = yield (0, import_shared.checkChromeDevToolsAvailable)();
509
+ if (!chromeAvailable) {
510
+ const error = new import_shared.ChromeMcpWarmupError(
511
+ import_shared.ChromeMcpWarmupErrorType.CHROME_NOT_CONNECTED,
512
+ "Chrome DevTools Protocol is not available",
513
+ "Chrome remote debugging is not enabled or not running on port 9222. Please enable Chrome remote debugging first."
514
+ );
515
+ log.warn("Chrome DevTools not available for retry", {
516
+ port: import_shared.CHROME_DEVTOOLS_PORT,
517
+ hint: "Enable Chrome remote debugging at chrome://inspect/#remote-debugging"
518
+ });
519
+ timer.end("Chrome DevTools not available for retry");
520
+ return { success: false, error };
521
+ }
522
+ log.debug("Chrome DevTools is available, proceeding with retry warmup");
348
523
  try {
349
- const warmupSession = yield this.createSession(import_shared.DEFAULT_RETRIES, "__chrome_mcp_warmup__");
524
+ const warmupSession = yield this.createSession(
525
+ projectDir,
526
+ import_shared.DEFAULT_RETRIES,
527
+ "__chrome_mcp_warmup__"
528
+ );
350
529
  warmupSessionId = warmupSession.id;
351
530
  const prompt = [
352
531
  "Call the browser tool list_pages immediately to establish the Chrome DevTools MCP connection.",
@@ -357,6 +536,15 @@ class OpenCodeAPI {
357
536
  "If the tool call fails, reply with exactly: fail"
358
537
  ].join(" ");
359
538
  const WARMUP_TIMEOUT = 6e4;
539
+ freeModel = yield this.getCheapestModel();
540
+ if (freeModel) {
541
+ log.debug("Using cheapest model for retry warmup", {
542
+ providerID: freeModel.providerID,
543
+ modelID: freeModel.modelID
544
+ });
545
+ } else {
546
+ log.debug("No model available for retry, using default model");
547
+ }
360
548
  const data = yield this.createHttpRequest(
361
549
  {
362
550
  hostname: this.hostname,
@@ -365,23 +553,82 @@ class OpenCodeAPI {
365
553
  method: "POST",
366
554
  headers: { "Content-Type": "application/json" }
367
555
  },
368
- JSON.stringify({
556
+ JSON.stringify(__spreadValues({
369
557
  system: "You are warming up Chrome DevTools MCP during startup. You must use the available browser tools immediately before replying.",
370
558
  parts: [{ type: "text", text: prompt }]
371
- }),
559
+ }, freeModel && {
560
+ model: {
561
+ providerID: freeModel.providerID,
562
+ modelID: freeModel.modelID
563
+ }
564
+ })),
372
565
  WARMUP_TIMEOUT
373
566
  );
374
567
  log.debug("Chrome MCP warmup response:", { data });
375
- const responseText = extractTextFromResponse(data);
376
- if (!(responseText == null ? void 0 : responseText.toLowerCase().includes("ready"))) {
377
- throw new Error(`Chrome MCP warmup failed: ${responseText || "No response"}`);
568
+ const responseText = (0, import_shared.extractTextFromResponse)(data);
569
+ if (!responseText) {
570
+ throw new import_shared.ChromeMcpWarmupError(
571
+ import_shared.ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
572
+ "AI did not respond to the warmup request",
573
+ "Empty response from AI"
574
+ );
575
+ }
576
+ const lowerResponse = responseText.toLowerCase();
577
+ if (lowerResponse.includes("fail")) {
578
+ throw new import_shared.ChromeMcpWarmupError(
579
+ import_shared.ChromeMcpWarmupErrorType.CHROME_NOT_CONNECTED,
580
+ "Chrome DevTools MCP is not connected",
581
+ "AI reported that browser tools are not available. This should not happen if Chrome DevTools check passed."
582
+ );
583
+ }
584
+ if (!lowerResponse.includes("ready")) {
585
+ throw new import_shared.ChromeMcpWarmupError(
586
+ import_shared.ChromeMcpWarmupErrorType.AI_RESPONSE_ERROR,
587
+ "AI response does not indicate success",
588
+ `AI responded with: ${responseText.substring(0, 200)}`
589
+ );
378
590
  }
379
591
  timer.end("Chrome MCP warmed up successfully");
380
- return true;
592
+ return { success: true };
381
593
  } catch (e) {
382
- log.warn("Failed to retry warm up Chrome MCP", { error: e });
594
+ if (e instanceof import_shared.ChromeMcpWarmupError) {
595
+ if (e.type === import_shared.ChromeMcpWarmupErrorType.SESSION_ERROR) {
596
+ timer.end("Session creation failed");
597
+ }
598
+ log.warn(`Chrome MCP warmup retry failed: ${e.type}`, {
599
+ message: e.message,
600
+ details: e.details
601
+ });
602
+ timer.end(`Chrome MCP warmup retry failed: ${e.type}`);
603
+ return { success: false, error: e };
604
+ }
605
+ if (freeModel) {
606
+ this.markModelAsFailed(freeModel.providerID, freeModel.modelID);
607
+ log.debug("Marked model as failed due to retry warmup error", {
608
+ providerID: freeModel.providerID,
609
+ modelID: freeModel.modelID,
610
+ error: e instanceof Error ? e.message : String(e)
611
+ });
612
+ }
613
+ const errorMessage = e instanceof Error ? e.message : String(e);
614
+ if (errorMessage.includes("timeout") || errorMessage.includes("Timeout")) {
615
+ const error2 = new import_shared.ChromeMcpWarmupError(
616
+ import_shared.ChromeMcpWarmupErrorType.AI_TIMEOUT,
617
+ "AI response timeout",
618
+ "AI did not respond within 60 seconds. Please check if the OpenCode AI model is properly configured and available."
619
+ );
620
+ log.warn("Chrome MCP warmup retry timeout", { error: errorMessage });
621
+ timer.end("Chrome MCP warmup retry timeout");
622
+ return { success: false, error: error2 };
623
+ }
624
+ const error = new import_shared.ChromeMcpWarmupError(
625
+ import_shared.ChromeMcpWarmupErrorType.UNKNOWN,
626
+ "Unknown error during Chrome MCP warmup retry",
627
+ errorMessage
628
+ );
629
+ log.warn("Chrome MCP warmup retry failed with unknown error", { error: errorMessage });
383
630
  timer.end("Chrome MCP warmup retry failed");
384
- return false;
631
+ return { success: false, error };
385
632
  } finally {
386
633
  if (warmupSessionId) {
387
634
  try {