vite-plugin-opencode-assistant 1.0.11 → 1.0.13

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.
@@ -41,11 +41,39 @@ import { createApp, ref, watch, onMounted, h, computed } from "vue";
41
41
  import { OpenCodeWidget } from "@vite-plugin-opencode-assistant/components";
42
42
  import "@vite-plugin-opencode-assistant/components/style.css";
43
43
  import { CONFIG_DATA_ATTR } from "@vite-plugin-opencode-assistant/shared";
44
+ function parseHotkey(hotkeyStr) {
45
+ if (!hotkeyStr) return { ctrl: true, shift: false, alt: false, key: "k" };
46
+ const parts = hotkeyStr.toLowerCase().split("+");
47
+ const key = parts.pop();
48
+ return {
49
+ ctrl: parts.includes("ctrl") || parts.includes("cmd") || parts.includes("meta"),
50
+ shift: parts.includes("shift"),
51
+ alt: parts.includes("alt"),
52
+ key: key || "k"
53
+ };
54
+ }
55
+ function matchHotkey(e, hotkeyConfig) {
56
+ const ctrlMatch = hotkeyConfig.ctrl ? e.ctrlKey || e.metaKey : !(e.ctrlKey || e.metaKey);
57
+ const shiftMatch = hotkeyConfig.shift ? e.shiftKey : !e.shiftKey;
58
+ const altMatch = hotkeyConfig.alt ? e.altKey : !e.altKey;
59
+ const keyMatch = e.key.toLowerCase() === hotkeyConfig.key.toLowerCase();
60
+ return ctrlMatch && shiftMatch && altMatch && keyMatch;
61
+ }
44
62
  function utf8ToBase64(str) {
45
63
  const bytes = new TextEncoder().encode(str);
46
64
  const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
47
65
  return btoa(binString);
48
66
  }
67
+ let proxyUrl = "";
68
+ function toProxyUrl(url) {
69
+ if (!url || !proxyUrl) return url;
70
+ try {
71
+ const urlObj = new URL(url, window.location.origin);
72
+ return `${proxyUrl}${urlObj.pathname}${urlObj.search}`;
73
+ } catch (e) {
74
+ return url;
75
+ }
76
+ }
49
77
  let config = {};
50
78
  const scriptTag = document.querySelector(`script[${CONFIG_DATA_ATTR}]`);
51
79
  if (scriptTag) {
@@ -74,15 +102,19 @@ const App = {
74
102
  const selectedElements = ref([]);
75
103
  const widgetRef = ref(null);
76
104
  const {
77
- webUrl = "",
78
105
  position = "bottom-right",
79
- theme = "auto",
106
+ theme: initialTheme = "auto",
80
107
  open: autoOpen = false,
81
108
  sessionUrl: initialSessionUrl = "",
109
+ proxyUrl: configProxyUrl = "",
82
110
  lazy = false,
83
111
  hotkey = "ctrl+k",
84
112
  cwd = ""
85
113
  } = config;
114
+ if (configProxyUrl) {
115
+ proxyUrl = configProxyUrl;
116
+ }
117
+ const theme = ref(initialTheme);
86
118
  const isWaitingForSession = ref(!initialSessionUrl);
87
119
  const computedLoading = computed(() => loading.value || isWaitingForSession.value);
88
120
  let servicesStarted = !lazy;
@@ -93,7 +125,7 @@ const App = {
93
125
  };
94
126
  currentSessionId.value = extractSessionId(initialSessionUrl);
95
127
  if (servicesStarted && initialSessionUrl) {
96
- iframeSrc.value = initialSessionUrl;
128
+ iframeSrc.value = toProxyUrl(initialSessionUrl);
97
129
  }
98
130
  try {
99
131
  const stored = sessionStorage.getItem("__opencode_selected_elements__");
@@ -140,7 +172,7 @@ const App = {
140
172
  updatedAt: Date.now()
141
173
  });
142
174
  currentSessionId.value = newSession.id;
143
- iframeSrc.value = `${webUrl}/${utf8ToBase64(cwd)}/session/${newSession.id}`;
175
+ iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${newSession.id}`;
144
176
  loadSessions();
145
177
  } catch (e) {
146
178
  showNotification("\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25");
@@ -155,7 +187,7 @@ const App = {
155
187
  if (sessions.value.length > 0) {
156
188
  const nextSession = sessions.value[0];
157
189
  currentSessionId.value = nextSession.id;
158
- iframeSrc.value = `${webUrl}/${utf8ToBase64(cwd)}/session/${nextSession.id}`;
190
+ iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${nextSession.id}`;
159
191
  } else {
160
192
  currentSessionId.value = null;
161
193
  iframeSrc.value = "";
@@ -169,7 +201,7 @@ const App = {
169
201
  if (currentSessionId.value === session.id) return;
170
202
  currentSessionId.value = session.id;
171
203
  loading.value = true;
172
- iframeSrc.value = `${webUrl}/${utf8ToBase64(cwd)}/session/${session.id}`;
204
+ iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${session.id}`;
173
205
  setTimeout(() => {
174
206
  loading.value = false;
175
207
  }, 500);
@@ -185,7 +217,7 @@ const App = {
185
217
  updateContext(true);
186
218
  } else if (data.type === "SESSION_READY") {
187
219
  if (data.sessionUrl && !iframeSrc.value) {
188
- iframeSrc.value = data.sessionUrl;
220
+ iframeSrc.value = toProxyUrl(data.sessionUrl);
189
221
  currentSessionId.value = extractSessionId(data.sessionUrl);
190
222
  }
191
223
  isWaitingForSession.value = false;
@@ -225,7 +257,7 @@ const App = {
225
257
  if (data.success) {
226
258
  servicesStarted = true;
227
259
  if (data.sessionUrl) {
228
- iframeSrc.value = data.sessionUrl;
260
+ iframeSrc.value = toProxyUrl(data.sessionUrl);
229
261
  currentSessionId.value = extractSessionId(data.sessionUrl);
230
262
  isWaitingForSession.value = false;
231
263
  }
@@ -236,6 +268,8 @@ const App = {
236
268
  }
237
269
  return false;
238
270
  });
271
+ const mainHotkey = parseHotkey(hotkey);
272
+ const selectHotkey = parseHotkey("ctrl+p");
239
273
  onMounted(() => {
240
274
  if (servicesStarted) {
241
275
  loadSessions();
@@ -265,6 +299,25 @@ const App = {
265
299
  if (document.head) {
266
300
  titleObserver.observe(document.head, { childList: true, subtree: true });
267
301
  }
302
+ const handleKeydown = (e) => {
303
+ if (matchHotkey(e, mainHotkey)) {
304
+ e.preventDefault();
305
+ handleToggle(!open.value);
306
+ }
307
+ if (matchHotkey(e, selectHotkey)) {
308
+ e.preventDefault();
309
+ const win = window;
310
+ if (win.__VUE_INSPECTOR__) {
311
+ selectMode.value = !selectMode.value;
312
+ } else {
313
+ showNotification("Vue Inspector \u672A\u52A0\u8F7D\uFF0C\u65E0\u6CD5\u4F7F\u7528\u5143\u7D20\u9009\u62E9\u529F\u80FD");
314
+ }
315
+ }
316
+ };
317
+ document.addEventListener("keydown", handleKeydown);
318
+ return () => {
319
+ document.removeEventListener("keydown", handleKeydown);
320
+ };
268
321
  });
269
322
  const handleToggle = (val) => __async(null, null, function* () {
270
323
  if (lazy && !servicesStarted && val) {
@@ -300,7 +353,7 @@ const App = {
300
353
  return h(OpenCodeWidget, {
301
354
  ref: widgetRef,
302
355
  position,
303
- theme,
356
+ theme: theme.value,
304
357
  open: open.value,
305
358
  selectMode: selectMode.value,
306
359
  sessionListCollapsed: sessionListCollapsed.value,
@@ -315,10 +368,19 @@ const App = {
315
368
  "onUpdate:open": handleToggle,
316
369
  "onUpdate:selectMode": (val) => {
317
370
  selectMode.value = val;
371
+ if (!val && !open.value) {
372
+ open.value = true;
373
+ }
318
374
  },
319
375
  "onUpdate:sessionListCollapsed": (val) => {
320
376
  sessionListCollapsed.value = val;
321
377
  },
378
+ "onUpdate:theme": (val) => {
379
+ theme.value = val;
380
+ },
381
+ "onToggle-theme": (val) => {
382
+ theme.value = val;
383
+ },
322
384
  "onCreate-session": createSession,
323
385
  "onDelete-session": deleteSession,
324
386
  "onSelect-session": selectSession,
@@ -0,0 +1,11 @@
1
+ import http from "http";
2
+ import { type OpenCodeSettings, type OpenCodeLanguage } from "@vite-plugin-opencode-assistant/shared";
3
+ export interface ProxyServerOptions {
4
+ /** 主题模式 */
5
+ theme?: "light" | "dark" | "auto";
6
+ /** OpenCode 界面语言 */
7
+ language?: OpenCodeLanguage;
8
+ /** OpenCode 内部设置 */
9
+ settings?: OpenCodeSettings;
10
+ }
11
+ export declare function startProxyServer(targetUrl: string, port: number, options?: ProxyServerOptions): http.Server;
@@ -0,0 +1,194 @@
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;
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));
20
+ import http from "http";
21
+ import {
22
+ createLogger,
23
+ DEFAULT_OPENCODE_SETTINGS,
24
+ OPENCODE_STORAGE_KEYS
25
+ } from "@vite-plugin-opencode-assistant/shared";
26
+ const log = createLogger("ProxyServer");
27
+ function mergeSettings(defaultSettings, userSettings) {
28
+ if (!userSettings) return defaultSettings;
29
+ const result = __spreadValues({}, defaultSettings);
30
+ if (userSettings.general) {
31
+ result.general = __spreadValues(__spreadValues({}, defaultSettings.general), userSettings.general);
32
+ }
33
+ if (userSettings.appearance) {
34
+ result.appearance = userSettings.appearance;
35
+ }
36
+ if (userSettings.permissions) {
37
+ result.permissions = userSettings.permissions;
38
+ }
39
+ if (userSettings.notifications) {
40
+ result.notifications = userSettings.notifications;
41
+ }
42
+ if (userSettings.sounds) {
43
+ result.sounds = userSettings.sounds;
44
+ }
45
+ return result;
46
+ }
47
+ function generateBridgeScript(options) {
48
+ const { theme = "auto", language, settings } = options;
49
+ const mergedSettings = mergeSettings(DEFAULT_OPENCODE_SETTINGS, settings);
50
+ return `
51
+ (function() {
52
+ const STORAGE_KEYS = ${JSON.stringify(OPENCODE_STORAGE_KEYS)};
53
+ const THEME_KEY = STORAGE_KEYS.COLOR_SCHEME;
54
+ const SETTINGS_KEY = STORAGE_KEYS.SETTINGS;
55
+
56
+ // === \u521D\u59CB\u5316\u914D\u7F6E ===
57
+ const initialConfig = {
58
+ theme: ${JSON.stringify(theme)},
59
+ language: ${JSON.stringify(language || null)},
60
+ settings: ${JSON.stringify(mergedSettings)}
61
+ };
62
+
63
+ // \u521D\u59CB\u5316\u4E3B\u9898
64
+ if (initialConfig.theme && initialConfig.theme !== "auto") {
65
+ localStorage.setItem(THEME_KEY, initialConfig.theme);
66
+ document.documentElement.setAttribute("data-color-scheme", initialConfig.theme);
67
+ }
68
+
69
+ // \u521D\u59CB\u5316\u8BBE\u7F6E
70
+ localStorage.setItem(SETTINGS_KEY, JSON.stringify(initialConfig.settings));
71
+
72
+ // === \u4E3B\u9898\u540C\u6B65\u51FD\u6570 ===
73
+ function getTheme() {
74
+ try {
75
+ return localStorage.getItem(THEME_KEY) || "system";
76
+ } catch {
77
+ return "system";
78
+ }
79
+ }
80
+
81
+ function setTheme(theme) {
82
+ try {
83
+ const oldTheme = localStorage.getItem(THEME_KEY);
84
+ localStorage.setItem(THEME_KEY, theme);
85
+ document.documentElement.setAttribute('data-color-scheme', theme);
86
+
87
+ if (oldTheme !== theme) {
88
+ window.dispatchEvent(new StorageEvent('storage', {
89
+ key: THEME_KEY,
90
+ oldValue: oldTheme,
91
+ newValue: theme,
92
+ url: window.location.href
93
+ }));
94
+ }
95
+ } catch {
96
+ // ignore
97
+ }
98
+ }
99
+
100
+ // === \u6D88\u606F\u76D1\u542C ===
101
+ window.addEventListener("message", function(event) {
102
+ if (event.data && event.data.type === "OPENCODE_SET_THEME") {
103
+ setTheme(event.data.theme);
104
+ }
105
+ });
106
+
107
+ // === \u5C31\u7EEA\u901A\u77E5 ===
108
+ window.addEventListener("load", function() {
109
+ if (window.parent !== window) {
110
+ window.parent.postMessage({ type: "OPENCODE_READY" }, "*");
111
+ }
112
+ });
113
+ })();
114
+ `;
115
+ }
116
+ function startProxyServer(targetUrl, port, options = {}) {
117
+ const target = new URL(targetUrl);
118
+ const bridgeScript = generateBridgeScript(options);
119
+ const server = http.createServer((req, res) => {
120
+ if (req.url === "/__opencode_bridge__.js") {
121
+ const body = bridgeScript;
122
+ res.writeHead(200, {
123
+ "content-type": "application/javascript; charset=utf-8",
124
+ "cache-control": "no-store",
125
+ "content-length": Buffer.byteLength(body)
126
+ });
127
+ res.end(body);
128
+ return;
129
+ }
130
+ const options2 = {
131
+ hostname: target.hostname,
132
+ port: target.port,
133
+ path: req.url,
134
+ method: req.method,
135
+ headers: __spreadProps(__spreadValues({}, req.headers), {
136
+ host: target.host,
137
+ // Don't accept compressed responses so we can modify HTML
138
+ "accept-encoding": "identity"
139
+ })
140
+ };
141
+ const proxyReq = http.request(options2, (proxyRes) => {
142
+ var _a;
143
+ const rawContentType = proxyRes.headers["content-type"];
144
+ const contentType = Array.isArray(rawContentType) ? (_a = rawContentType[0]) != null ? _a : "" : rawContentType != null ? rawContentType : "";
145
+ if (contentType.includes("text/html")) {
146
+ const chunks = [];
147
+ proxyRes.on("data", (chunk) => {
148
+ chunks.push(chunk);
149
+ });
150
+ proxyRes.on("end", () => {
151
+ let body = Buffer.concat(chunks).toString("utf-8");
152
+ if (body.match(/<\/head>/i)) {
153
+ body = body.replace(
154
+ /<\/head>/i,
155
+ '<script src="/__opencode_bridge__.js"></script></head>'
156
+ );
157
+ } else if (body.match(/<\/body>/i)) {
158
+ body = body.replace(
159
+ /<\/body>/i,
160
+ '<script src="/__opencode_bridge__.js"></script></body>'
161
+ );
162
+ } else {
163
+ body += '<script src="/__opencode_bridge__.js"></script>';
164
+ }
165
+ const headers = {};
166
+ for (const [key, value] of Object.entries(proxyRes.headers)) {
167
+ if (value !== void 0 && key !== "content-encoding" && key !== "transfer-encoding" && key !== "content-length") {
168
+ headers[key] = value;
169
+ }
170
+ }
171
+ headers["content-length"] = Buffer.byteLength(body);
172
+ res.writeHead(proxyRes.statusCode || 200, headers);
173
+ res.end(body);
174
+ });
175
+ } else {
176
+ res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
177
+ proxyRes.pipe(res);
178
+ }
179
+ });
180
+ proxyReq.on("error", (err) => {
181
+ log.error("Proxy error", { error: err.message, url: req.url });
182
+ res.writeHead(502);
183
+ res.end("Proxy error");
184
+ });
185
+ req.pipe(proxyReq);
186
+ });
187
+ server.listen(port, () => {
188
+ log.info(`Proxy server started on port ${port} -> ${targetUrl}`);
189
+ });
190
+ return server;
191
+ }
192
+ export {
193
+ startProxyServer
194
+ };
@@ -7,12 +7,15 @@ export declare class OpenCodeService {
7
7
  private api;
8
8
  private sseClients;
9
9
  private onPortAllocated;
10
+ private onProxyPortAllocated;
10
11
  webProcess: ResultPromise | null;
11
12
  actualWebPort: number;
13
+ actualProxyPort: number;
12
14
  isStarted: boolean;
13
15
  private startPromise;
14
16
  sessionUrl: string | null;
15
- constructor(config: Required<OpenCodeOptions>, api: OpenCodeAPI, sseClients: Set<http.ServerResponse>, onPortAllocated: (port: number) => void);
17
+ private proxyServer;
18
+ constructor(config: Required<OpenCodeOptions>, api: OpenCodeAPI, sseClients: Set<http.ServerResponse>, onPortAllocated: (port: number) => void, onProxyPortAllocated: (port: number) => void);
16
19
  start(corsOrigins?: string[], contextApiUrl?: string, viteOrigin?: string): Promise<void>;
17
20
  stop(): Promise<void>;
18
21
  }
@@ -22,26 +22,36 @@ var __async = (__this, __arguments, generator) => {
22
22
  });
23
23
  };
24
24
  import { prepareOpenCodeRuntime, startOpenCodeWeb } from "@vite-plugin-opencode-assistant/opencode";
25
- import { SERVER_START_TIMEOUT, createLogger } from "@vite-plugin-opencode-assistant/shared";
25
+ import {
26
+ DEFAULT_PROXY_PORT,
27
+ SERVER_START_TIMEOUT,
28
+ createLogger
29
+ } from "@vite-plugin-opencode-assistant/shared";
26
30
  import {
27
31
  checkOpenCodeInstalled,
28
32
  findAvailablePort,
29
33
  killOrphanOpenCodeProcesses,
30
34
  waitForServer
31
35
  } from "../utils/system.js";
36
+ import { startProxyServer } from "./proxy-server.js";
32
37
  const log = createLogger("Service");
33
38
  class OpenCodeService {
34
- constructor(config, api, sseClients, onPortAllocated) {
39
+ constructor(config, api, sseClients, onPortAllocated, onProxyPortAllocated) {
35
40
  __publicField(this, "config", config);
36
41
  __publicField(this, "api", api);
37
42
  __publicField(this, "sseClients", sseClients);
38
43
  __publicField(this, "onPortAllocated", onPortAllocated);
44
+ __publicField(this, "onProxyPortAllocated", onProxyPortAllocated);
39
45
  __publicField(this, "webProcess", null);
40
46
  __publicField(this, "actualWebPort");
47
+ __publicField(this, "actualProxyPort");
41
48
  __publicField(this, "isStarted", false);
42
49
  __publicField(this, "startPromise", null);
43
50
  __publicField(this, "sessionUrl", null);
51
+ __publicField(this, "proxyServer", null);
52
+ var _a;
44
53
  this.actualWebPort = config.webPort;
54
+ this.actualProxyPort = (_a = config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT;
45
55
  }
46
56
  start(corsOrigins, contextApiUrl, viteOrigin) {
47
57
  return __async(this, null, function* () {
@@ -54,6 +64,7 @@ class OpenCodeService {
54
64
  return this.startPromise;
55
65
  }
56
66
  this.startPromise = (() => __async(this, null, function* () {
67
+ var _a, _b, _c;
57
68
  const timer = log.timer("startServices", {
58
69
  corsOrigins,
59
70
  contextApiUrl,
@@ -116,6 +127,24 @@ Please install OpenCode first:
116
127
  log.info(`Waiting for OpenCode Web to become ready at ${webUrl}...`);
117
128
  yield waitForServer(webUrl, SERVER_START_TIMEOUT);
118
129
  log.info(`OpenCode Web started at ${webUrl}`);
130
+ this.actualProxyPort = yield findAvailablePort(
131
+ (_a = this.config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT,
132
+ this.config.hostname
133
+ );
134
+ this.onProxyPortAllocated(this.actualProxyPort);
135
+ if (this.actualProxyPort !== ((_b = this.config.proxyPort) != null ? _b : DEFAULT_PROXY_PORT)) {
136
+ log.info(
137
+ `Proxy port ${(_c = this.config.proxyPort) != null ? _c : DEFAULT_PROXY_PORT} is in use, using ${this.actualProxyPort} instead`
138
+ );
139
+ } else {
140
+ log.debug(`Using proxy port ${this.actualProxyPort}`);
141
+ }
142
+ this.proxyServer = startProxyServer(webUrl, this.actualProxyPort, {
143
+ theme: this.config.theme,
144
+ language: this.config.language,
145
+ settings: this.config.settings
146
+ });
147
+ timer.checkpoint("Proxy server started");
119
148
  yield this.api.warmupChromeMcp(viteOrigin);
120
149
  timer.checkpoint("Chrome MCP warmup complete");
121
150
  try {
@@ -147,6 +176,11 @@ Please install OpenCode first:
147
176
  return __async(this, null, function* () {
148
177
  const timer = log.timer("stopServices");
149
178
  log.info("Stopping OpenCode services...");
179
+ if (this.proxyServer) {
180
+ log.debug("Closing proxy server");
181
+ this.proxyServer.close();
182
+ this.proxyServer = null;
183
+ }
150
184
  if (this.webProcess) {
151
185
  log.debug("Killing web process", { pid: this.webProcess.pid });
152
186
  this.webProcess.kill("SIGTERM");
@@ -2,6 +2,7 @@ import type { PageContext, SessionInfo } from "@vite-plugin-opencode-assistant/s
2
2
  import type http from "http";
3
3
  export interface EndpointContext {
4
4
  get sessionUrl(): string | null;
5
+ get webUrl(): string | null;
5
6
  get sseClients(): Set<http.ServerResponse>;
6
7
  get pageContext(): PageContext;
7
8
  set pageContext(ctx: PageContext);
package/es/index.js CHANGED
@@ -38,6 +38,7 @@ import Inspector from "unplugin-vue-inspector/vite";
38
38
  import {
39
39
  CONTEXT_API_PATH,
40
40
  DEFAULT_CONFIG,
41
+ DEFAULT_PROXY_PORT,
41
42
  createLogger,
42
43
  setVerbose
43
44
  } from "@vite-plugin-opencode-assistant/shared";
@@ -59,16 +60,26 @@ function opencodePlugin(options = {}) {
59
60
  return plugins;
60
61
  }
61
62
  function createOpenCodePlugin(options = {}) {
63
+ var _a;
62
64
  const config = __spreadValues(__spreadValues({}, DEFAULT_CONFIG), options);
63
65
  setVerbose(config.verbose);
64
66
  const log = createLogger("Plugin");
65
67
  let actualWebPort = config.webPort;
68
+ let actualProxyPort = (_a = config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT;
66
69
  let pageContext = { url: "", title: "" };
67
70
  const sseClients = /* @__PURE__ */ new Set();
68
71
  const api = new OpenCodeAPI(config.hostname, () => actualWebPort, config.warmupChromeMcp);
69
- const service = new OpenCodeService(config, api, sseClients, (port) => {
70
- actualWebPort = port;
71
- });
72
+ const service = new OpenCodeService(
73
+ config,
74
+ api,
75
+ sseClients,
76
+ (port) => {
77
+ actualWebPort = port;
78
+ },
79
+ (port) => {
80
+ actualProxyPort = port;
81
+ }
82
+ );
72
83
  return {
73
84
  name: "vite-plugin-opencode",
74
85
  apply(_viteConfig, env) {
@@ -77,12 +88,15 @@ function createOpenCodePlugin(options = {}) {
77
88
  },
78
89
  configureServer(server) {
79
90
  return __async(this, null, function* () {
80
- var _a2, _b;
91
+ var _a2, _b2;
81
92
  const timer = log.timer("configureServer");
82
93
  setupMiddlewares(server, {
83
94
  get sessionUrl() {
84
95
  return service.sessionUrl;
85
96
  },
97
+ get webUrl() {
98
+ return actualWebPort ? `http://${config.hostname}:${actualWebPort}` : null;
99
+ },
86
100
  get sseClients() {
87
101
  return sseClients;
88
102
  },
@@ -131,7 +145,7 @@ function createOpenCodePlugin(options = {}) {
131
145
  log.error("Failed to start services", { error: e });
132
146
  }
133
147
  }));
134
- (_b = server.httpServer) == null ? void 0 : _b.on("close", () => {
148
+ (_b2 = server.httpServer) == null ? void 0 : _b2.on("close", () => {
135
149
  log.debug("HTTP server closing");
136
150
  service.stop();
137
151
  });
@@ -149,6 +163,7 @@ function createOpenCodePlugin(options = {}) {
149
163
  const timer = log.timer("transformIndexHtml");
150
164
  const widget = injectWidget({
151
165
  webUrl: `http://${config.hostname}:${actualWebPort}`,
166
+ proxyUrl: `http://${config.hostname}:${actualProxyPort}`,
152
167
  serverUrl: `http://${config.hostname}:${actualWebPort}`,
153
168
  position: config.position,
154
169
  theme: config.theme,