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