vite-plugin-opencode-assistant 1.0.14 → 1.0.15

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.
@@ -8,4 +8,8 @@ export interface ProxyServerOptions {
8
8
  /** OpenCode 内部设置 */
9
9
  settings?: OpenCodeSettings;
10
10
  }
11
- export declare function startProxyServer(targetUrl: string, port: number, options?: ProxyServerOptions): http.Server;
11
+ export interface ProxyServerResult {
12
+ server: http.Server;
13
+ actualPort: number;
14
+ }
15
+ export declare function startProxyServer(targetUrl: string, port: number, options?: ProxyServerOptions): Promise<ProxyServerResult>;
@@ -114,80 +114,86 @@ function generateBridgeScript(options) {
114
114
  `;
115
115
  }
116
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);
117
+ return new Promise((resolve, reject) => {
118
+ const target = new URL(targetUrl);
119
+ const bridgeScript = generateBridgeScript(options);
120
+ const server = http.createServer((req, res) => {
121
+ if (req.url === "/__opencode_bridge__.js") {
122
+ const body = bridgeScript;
123
+ res.writeHead(200, {
124
+ "content-type": "application/javascript; charset=utf-8",
125
+ "cache-control": "no-store",
126
+ "content-length": Buffer.byteLength(body)
149
127
  });
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);
128
+ res.end(body);
129
+ return;
178
130
  }
131
+ const requestOptions = {
132
+ hostname: target.hostname,
133
+ port: target.port,
134
+ path: req.url,
135
+ method: req.method,
136
+ headers: __spreadProps(__spreadValues({}, req.headers), {
137
+ host: target.host,
138
+ "accept-encoding": "identity"
139
+ })
140
+ };
141
+ const proxyReq = http.request(requestOptions, (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);
179
186
  });
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");
187
+ server.on("error", (err) => {
188
+ reject(err);
189
+ });
190
+ server.listen(port, () => {
191
+ const address = server.address();
192
+ const actualPort = typeof address === "object" && address ? address.port : port;
193
+ log.info(`Proxy server started on port ${actualPort} -> ${targetUrl}`);
194
+ resolve({ server, actualPort });
184
195
  });
185
- req.pipe(proxyReq);
186
- });
187
- server.listen(port, () => {
188
- log.info(`Proxy server started on port ${port} -> ${targetUrl}`);
189
196
  });
190
- return server;
191
197
  }
192
198
  export {
193
199
  startProxyServer
@@ -92,7 +92,7 @@ class OpenCodeService {
92
92
  return this.startPromise;
93
93
  }
94
94
  this.startPromise = (() => __async(this, null, function* () {
95
- var _a, _b, _c;
95
+ var _a, _b, _c, _d, _e;
96
96
  const timer = log.timer("startServices", {
97
97
  corsOrigins,
98
98
  contextApiUrl,
@@ -161,7 +161,10 @@ Please install OpenCode first:
161
161
  log.info(`Waiting for OpenCode Web to become ready at ${webUrl}...`);
162
162
  this.sendTaskUpdate("waiting_web_ready");
163
163
  try {
164
- yield waitForServer(webUrl, SERVER_START_TIMEOUT);
164
+ yield waitForServer(webUrl, SERVER_START_TIMEOUT, this.webProcess);
165
+ if (((_a = this.webProcess) == null ? void 0 : _a.exitCode) !== null && ((_b = this.webProcess) == null ? void 0 : _b.exitCode) !== void 0) {
166
+ throw new Error(`OpenCode process exited with code ${this.webProcess.exitCode}`);
167
+ }
165
168
  log.info(`OpenCode Web started at ${webUrl}`);
166
169
  } catch (e) {
167
170
  log.error("OpenCode Web failed to start", { error: e });
@@ -171,23 +174,52 @@ Please install OpenCode first:
171
174
  return;
172
175
  }
173
176
  this.sendTaskUpdate("starting_proxy");
174
- this.actualProxyPort = yield findAvailablePort(
175
- (_a = this.config.proxyPort) != null ? _a : DEFAULT_PROXY_PORT,
176
- this.config.hostname
177
- );
177
+ let proxyStartPort = (_c = this.config.proxyPort) != null ? _c : DEFAULT_PROXY_PORT;
178
+ if (proxyStartPort === this.actualWebPort) {
179
+ proxyStartPort = this.actualWebPort + 1;
180
+ log.debug(`Proxy start port conflicts with web port, using ${proxyStartPort} instead`);
181
+ }
182
+ this.actualProxyPort = yield findAvailablePort(proxyStartPort, this.config.hostname);
178
183
  this.onProxyPortAllocated(this.actualProxyPort);
179
- if (this.actualProxyPort !== ((_b = this.config.proxyPort) != null ? _b : DEFAULT_PROXY_PORT)) {
184
+ if (this.actualProxyPort !== ((_d = this.config.proxyPort) != null ? _d : DEFAULT_PROXY_PORT)) {
180
185
  log.info(
181
- `Proxy port ${(_c = this.config.proxyPort) != null ? _c : DEFAULT_PROXY_PORT} is in use, using ${this.actualProxyPort} instead`
186
+ `Proxy port ${(_e = this.config.proxyPort) != null ? _e : DEFAULT_PROXY_PORT} is in use, using ${this.actualProxyPort} instead`
182
187
  );
183
188
  } else {
184
189
  log.debug(`Using proxy port ${this.actualProxyPort}`);
185
190
  }
186
- this.proxyServer = startProxyServer(webUrl, this.actualProxyPort, {
187
- theme: this.config.theme,
188
- language: this.config.language,
189
- settings: this.config.settings
190
- });
191
+ try {
192
+ const result = yield startProxyServer(webUrl, this.actualProxyPort, {
193
+ theme: this.config.theme,
194
+ language: this.config.language,
195
+ settings: this.config.settings
196
+ });
197
+ this.proxyServer = result.server;
198
+ if (result.actualPort !== this.actualProxyPort) {
199
+ log.info(
200
+ `Proxy port ${this.actualProxyPort} was taken, using ${result.actualPort} instead`
201
+ );
202
+ this.actualProxyPort = result.actualPort;
203
+ this.onProxyPortAllocated(this.actualProxyPort);
204
+ }
205
+ } catch (err) {
206
+ const nodeErr = err;
207
+ if (nodeErr.code === "EADDRINUSE") {
208
+ log.warn(`Proxy port ${this.actualProxyPort} became unavailable, trying next port...`);
209
+ const nextPort = yield findAvailablePort(this.actualProxyPort + 1, this.config.hostname);
210
+ const result = yield startProxyServer(webUrl, nextPort, {
211
+ theme: this.config.theme,
212
+ language: this.config.language,
213
+ settings: this.config.settings
214
+ });
215
+ this.proxyServer = result.server;
216
+ this.actualProxyPort = result.actualPort;
217
+ this.onProxyPortAllocated(this.actualProxyPort);
218
+ log.info(`Proxy server started on fallback port ${this.actualProxyPort}`);
219
+ } else {
220
+ throw err;
221
+ }
222
+ }
191
223
  timer.checkpoint("Proxy server started");
192
224
  this.sendTaskUpdate("warming_up_chrome");
193
225
  let warmupFailed = false;
@@ -1,4 +1,5 @@
1
- export declare function waitForServer(url: string, timeout?: number): Promise<void>;
1
+ import type { ResultPromise } from "execa";
2
+ export declare function waitForServer(url: string, timeout?: number, process?: ResultPromise): Promise<void>;
2
3
  export declare function checkOpenCodeInstalled(): Promise<boolean>;
3
4
  export declare function isPortAvailable(port: number, hostname?: string): Promise<boolean>;
4
5
  export declare function findAvailablePort(startPort: number, hostname?: string, maxTries?: number): Promise<number>;
@@ -21,14 +21,10 @@ var __async = (__this, __arguments, generator) => {
21
21
  import { spawn } from "child_process";
22
22
  import http from "http";
23
23
  import net from "net";
24
- import {
25
- DEFAULT_HOSTNAME,
26
- MAX_PORT_TRIES,
27
- SERVER_CHECK_INTERVAL
28
- } from "@vite-plugin-opencode-assistant/shared";
24
+ import { MAX_PORT_TRIES, SERVER_CHECK_INTERVAL } from "@vite-plugin-opencode-assistant/shared";
29
25
  import { PerformanceTimer, createLogger } from "@vite-plugin-opencode-assistant/shared";
30
26
  const log = createLogger("Utils");
31
- function waitForServer(url, timeout = 1e4) {
27
+ function waitForServer(url, timeout = 1e4, process2) {
32
28
  const timer = new PerformanceTimer("waitForServer", { url, timeout });
33
29
  return new Promise((resolve, reject) => {
34
30
  const startTime = Date.now();
@@ -36,6 +32,11 @@ function waitForServer(url, timeout = 1e4) {
36
32
  const check = () => {
37
33
  attempts++;
38
34
  log.debug(`Checking server availability (attempt ${attempts})`, { url });
35
+ if ((process2 == null ? void 0 : process2.exitCode) !== null && (process2 == null ? void 0 : process2.exitCode) !== void 0) {
36
+ timer.end(`\u274C Process exited with code ${process2.exitCode}`);
37
+ reject(new Error(`Process exited with code ${process2.exitCode}`));
38
+ return;
39
+ }
39
40
  const req = http.get(url, (res) => {
40
41
  if (res.statusCode && res.statusCode < 500) {
41
42
  timer.end(`\u2713 Server ready after ${attempts} attempts`);
@@ -81,8 +82,8 @@ function checkOpenCodeInstalled() {
81
82
  });
82
83
  });
83
84
  }
84
- function isPortAvailable(_0) {
85
- return __async(this, arguments, function* (port, hostname = DEFAULT_HOSTNAME) {
85
+ function isPortAvailable(port, hostname) {
86
+ return __async(this, null, function* () {
86
87
  return new Promise((resolve) => {
87
88
  const server = net.createServer();
88
89
  server.once("error", (err) => {
@@ -98,8 +99,8 @@ function isPortAvailable(_0) {
98
99
  });
99
100
  });
100
101
  }
101
- function findAvailablePort(_0) {
102
- return __async(this, arguments, function* (startPort, hostname = DEFAULT_HOSTNAME, maxTries = MAX_PORT_TRIES) {
102
+ function findAvailablePort(_0, _1) {
103
+ return __async(this, arguments, function* (startPort, hostname, maxTries = MAX_PORT_TRIES) {
103
104
  const timer = log.timer("findAvailablePort", { startPort, hostname, maxTries });
104
105
  log.debug(`Looking for available port starting from ${startPort}`);
105
106
  for (let port = startPort; port < startPort + maxTries; port++) {
@@ -8,4 +8,8 @@ export interface ProxyServerOptions {
8
8
  /** OpenCode 内部设置 */
9
9
  settings?: OpenCodeSettings;
10
10
  }
11
- export declare function startProxyServer(targetUrl: string, port: number, options?: ProxyServerOptions): http.Server;
11
+ export interface ProxyServerResult {
12
+ server: http.Server;
13
+ actualPort: number;
14
+ }
15
+ export declare function startProxyServer(targetUrl: string, port: number, options?: ProxyServerOptions): Promise<ProxyServerResult>;
@@ -140,80 +140,86 @@ function generateBridgeScript(options) {
140
140
  `;
141
141
  }
142
142
  function startProxyServer(targetUrl, port, options = {}) {
143
- const target = new URL(targetUrl);
144
- const bridgeScript = generateBridgeScript(options);
145
- const server = import_http.default.createServer((req, res) => {
146
- if (req.url === "/__opencode_bridge__.js") {
147
- const body = bridgeScript;
148
- res.writeHead(200, {
149
- "content-type": "application/javascript; charset=utf-8",
150
- "cache-control": "no-store",
151
- "content-length": Buffer.byteLength(body)
152
- });
153
- res.end(body);
154
- return;
155
- }
156
- const options2 = {
157
- hostname: target.hostname,
158
- port: target.port,
159
- path: req.url,
160
- method: req.method,
161
- headers: __spreadProps(__spreadValues({}, req.headers), {
162
- host: target.host,
163
- // Don't accept compressed responses so we can modify HTML
164
- "accept-encoding": "identity"
165
- })
166
- };
167
- const proxyReq = import_http.default.request(options2, (proxyRes) => {
168
- var _a;
169
- const rawContentType = proxyRes.headers["content-type"];
170
- const contentType = Array.isArray(rawContentType) ? (_a = rawContentType[0]) != null ? _a : "" : rawContentType != null ? rawContentType : "";
171
- if (contentType.includes("text/html")) {
172
- const chunks = [];
173
- proxyRes.on("data", (chunk) => {
174
- chunks.push(chunk);
143
+ return new Promise((resolve, reject) => {
144
+ const target = new URL(targetUrl);
145
+ const bridgeScript = generateBridgeScript(options);
146
+ const server = import_http.default.createServer((req, res) => {
147
+ if (req.url === "/__opencode_bridge__.js") {
148
+ const body = bridgeScript;
149
+ res.writeHead(200, {
150
+ "content-type": "application/javascript; charset=utf-8",
151
+ "cache-control": "no-store",
152
+ "content-length": Buffer.byteLength(body)
175
153
  });
176
- proxyRes.on("end", () => {
177
- let body = Buffer.concat(chunks).toString("utf-8");
178
- if (body.match(/<\/head>/i)) {
179
- body = body.replace(
180
- /<\/head>/i,
181
- '<script src="/__opencode_bridge__.js"></script></head>'
182
- );
183
- } else if (body.match(/<\/body>/i)) {
184
- body = body.replace(
185
- /<\/body>/i,
186
- '<script src="/__opencode_bridge__.js"></script></body>'
187
- );
188
- } else {
189
- body += '<script src="/__opencode_bridge__.js"></script>';
190
- }
191
- const headers = {};
192
- for (const [key, value] of Object.entries(proxyRes.headers)) {
193
- if (value !== void 0 && key !== "content-encoding" && key !== "transfer-encoding" && key !== "content-length") {
194
- headers[key] = value;
195
- }
196
- }
197
- headers["content-length"] = Buffer.byteLength(body);
198
- res.writeHead(proxyRes.statusCode || 200, headers);
199
- res.end(body);
200
- });
201
- } else {
202
- res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
203
- proxyRes.pipe(res);
154
+ res.end(body);
155
+ return;
204
156
  }
157
+ const requestOptions = {
158
+ hostname: target.hostname,
159
+ port: target.port,
160
+ path: req.url,
161
+ method: req.method,
162
+ headers: __spreadProps(__spreadValues({}, req.headers), {
163
+ host: target.host,
164
+ "accept-encoding": "identity"
165
+ })
166
+ };
167
+ const proxyReq = import_http.default.request(requestOptions, (proxyRes) => {
168
+ var _a;
169
+ const rawContentType = proxyRes.headers["content-type"];
170
+ const contentType = Array.isArray(rawContentType) ? (_a = rawContentType[0]) != null ? _a : "" : rawContentType != null ? rawContentType : "";
171
+ if (contentType.includes("text/html")) {
172
+ const chunks = [];
173
+ proxyRes.on("data", (chunk) => {
174
+ chunks.push(chunk);
175
+ });
176
+ proxyRes.on("end", () => {
177
+ let body = Buffer.concat(chunks).toString("utf-8");
178
+ if (body.match(/<\/head>/i)) {
179
+ body = body.replace(
180
+ /<\/head>/i,
181
+ '<script src="/__opencode_bridge__.js"></script></head>'
182
+ );
183
+ } else if (body.match(/<\/body>/i)) {
184
+ body = body.replace(
185
+ /<\/body>/i,
186
+ '<script src="/__opencode_bridge__.js"></script></body>'
187
+ );
188
+ } else {
189
+ body += '<script src="/__opencode_bridge__.js"></script>';
190
+ }
191
+ const headers = {};
192
+ for (const [key, value] of Object.entries(proxyRes.headers)) {
193
+ if (value !== void 0 && key !== "content-encoding" && key !== "transfer-encoding" && key !== "content-length") {
194
+ headers[key] = value;
195
+ }
196
+ }
197
+ headers["content-length"] = Buffer.byteLength(body);
198
+ res.writeHead(proxyRes.statusCode || 200, headers);
199
+ res.end(body);
200
+ });
201
+ } else {
202
+ res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
203
+ proxyRes.pipe(res);
204
+ }
205
+ });
206
+ proxyReq.on("error", (err) => {
207
+ log.error("Proxy error", { error: err.message, url: req.url });
208
+ res.writeHead(502);
209
+ res.end("Proxy error");
210
+ });
211
+ req.pipe(proxyReq);
205
212
  });
206
- proxyReq.on("error", (err) => {
207
- log.error("Proxy error", { error: err.message, url: req.url });
208
- res.writeHead(502);
209
- res.end("Proxy error");
213
+ server.on("error", (err) => {
214
+ reject(err);
215
+ });
216
+ server.listen(port, () => {
217
+ const address = server.address();
218
+ const actualPort = typeof address === "object" && address ? address.port : port;
219
+ log.info(`Proxy server started on port ${actualPort} -> ${targetUrl}`);
220
+ resolve({ server, actualPort });
210
221
  });
211
- req.pipe(proxyReq);
212
- });
213
- server.listen(port, () => {
214
- log.info(`Proxy server started on port ${port} -> ${targetUrl}`);
215
222
  });
216
- return server;
217
223
  }
218
224
  // Annotate the CommonJS export names for ESM import in node:
219
225
  0 && (module.exports = {
@@ -103,7 +103,7 @@ class OpenCodeService {
103
103
  return this.startPromise;
104
104
  }
105
105
  this.startPromise = (() => __async(this, null, function* () {
106
- var _a, _b, _c;
106
+ var _a, _b, _c, _d, _e;
107
107
  const timer = log.timer("startServices", {
108
108
  corsOrigins,
109
109
  contextApiUrl,
@@ -172,7 +172,10 @@ Please install OpenCode first:
172
172
  log.info(`Waiting for OpenCode Web to become ready at ${webUrl}...`);
173
173
  this.sendTaskUpdate("waiting_web_ready");
174
174
  try {
175
- yield (0, import_system.waitForServer)(webUrl, import_shared.SERVER_START_TIMEOUT);
175
+ yield (0, import_system.waitForServer)(webUrl, import_shared.SERVER_START_TIMEOUT, this.webProcess);
176
+ if (((_a = this.webProcess) == null ? void 0 : _a.exitCode) !== null && ((_b = this.webProcess) == null ? void 0 : _b.exitCode) !== void 0) {
177
+ throw new Error(`OpenCode process exited with code ${this.webProcess.exitCode}`);
178
+ }
176
179
  log.info(`OpenCode Web started at ${webUrl}`);
177
180
  } catch (e) {
178
181
  log.error("OpenCode Web failed to start", { error: e });
@@ -182,23 +185,52 @@ Please install OpenCode first:
182
185
  return;
183
186
  }
184
187
  this.sendTaskUpdate("starting_proxy");
185
- this.actualProxyPort = yield (0, import_system.findAvailablePort)(
186
- (_a = this.config.proxyPort) != null ? _a : import_shared.DEFAULT_PROXY_PORT,
187
- this.config.hostname
188
- );
188
+ let proxyStartPort = (_c = this.config.proxyPort) != null ? _c : import_shared.DEFAULT_PROXY_PORT;
189
+ if (proxyStartPort === this.actualWebPort) {
190
+ proxyStartPort = this.actualWebPort + 1;
191
+ log.debug(`Proxy start port conflicts with web port, using ${proxyStartPort} instead`);
192
+ }
193
+ this.actualProxyPort = yield (0, import_system.findAvailablePort)(proxyStartPort, this.config.hostname);
189
194
  this.onProxyPortAllocated(this.actualProxyPort);
190
- if (this.actualProxyPort !== ((_b = this.config.proxyPort) != null ? _b : import_shared.DEFAULT_PROXY_PORT)) {
195
+ if (this.actualProxyPort !== ((_d = this.config.proxyPort) != null ? _d : import_shared.DEFAULT_PROXY_PORT)) {
191
196
  log.info(
192
- `Proxy port ${(_c = this.config.proxyPort) != null ? _c : import_shared.DEFAULT_PROXY_PORT} is in use, using ${this.actualProxyPort} instead`
197
+ `Proxy port ${(_e = this.config.proxyPort) != null ? _e : import_shared.DEFAULT_PROXY_PORT} is in use, using ${this.actualProxyPort} instead`
193
198
  );
194
199
  } else {
195
200
  log.debug(`Using proxy port ${this.actualProxyPort}`);
196
201
  }
197
- this.proxyServer = (0, import_proxy_server.startProxyServer)(webUrl, this.actualProxyPort, {
198
- theme: this.config.theme,
199
- language: this.config.language,
200
- settings: this.config.settings
201
- });
202
+ try {
203
+ const result = yield (0, import_proxy_server.startProxyServer)(webUrl, this.actualProxyPort, {
204
+ theme: this.config.theme,
205
+ language: this.config.language,
206
+ settings: this.config.settings
207
+ });
208
+ this.proxyServer = result.server;
209
+ if (result.actualPort !== this.actualProxyPort) {
210
+ log.info(
211
+ `Proxy port ${this.actualProxyPort} was taken, using ${result.actualPort} instead`
212
+ );
213
+ this.actualProxyPort = result.actualPort;
214
+ this.onProxyPortAllocated(this.actualProxyPort);
215
+ }
216
+ } catch (err) {
217
+ const nodeErr = err;
218
+ if (nodeErr.code === "EADDRINUSE") {
219
+ log.warn(`Proxy port ${this.actualProxyPort} became unavailable, trying next port...`);
220
+ const nextPort = yield (0, import_system.findAvailablePort)(this.actualProxyPort + 1, this.config.hostname);
221
+ const result = yield (0, import_proxy_server.startProxyServer)(webUrl, nextPort, {
222
+ theme: this.config.theme,
223
+ language: this.config.language,
224
+ settings: this.config.settings
225
+ });
226
+ this.proxyServer = result.server;
227
+ this.actualProxyPort = result.actualPort;
228
+ this.onProxyPortAllocated(this.actualProxyPort);
229
+ log.info(`Proxy server started on fallback port ${this.actualProxyPort}`);
230
+ } else {
231
+ throw err;
232
+ }
233
+ }
202
234
  timer.checkpoint("Proxy server started");
203
235
  this.sendTaskUpdate("warming_up_chrome");
204
236
  let warmupFailed = false;
@@ -1,4 +1,5 @@
1
- export declare function waitForServer(url: string, timeout?: number): Promise<void>;
1
+ import type { ResultPromise } from "execa";
2
+ export declare function waitForServer(url: string, timeout?: number, process?: ResultPromise): Promise<void>;
2
3
  export declare function checkOpenCodeInstalled(): Promise<boolean>;
3
4
  export declare function isPortAvailable(port: number, hostname?: string): Promise<boolean>;
4
5
  export declare function findAvailablePort(startPort: number, hostname?: string, maxTries?: number): Promise<number>;
@@ -60,7 +60,7 @@ var import_net = __toESM(require("net"));
60
60
  var import_shared = require("@vite-plugin-opencode-assistant/shared");
61
61
  var import_shared2 = require("@vite-plugin-opencode-assistant/shared");
62
62
  const log = (0, import_shared2.createLogger)("Utils");
63
- function waitForServer(url, timeout = 1e4) {
63
+ function waitForServer(url, timeout = 1e4, process2) {
64
64
  const timer = new import_shared2.PerformanceTimer("waitForServer", { url, timeout });
65
65
  return new Promise((resolve, reject) => {
66
66
  const startTime = Date.now();
@@ -68,6 +68,11 @@ function waitForServer(url, timeout = 1e4) {
68
68
  const check = () => {
69
69
  attempts++;
70
70
  log.debug(`Checking server availability (attempt ${attempts})`, { url });
71
+ if ((process2 == null ? void 0 : process2.exitCode) !== null && (process2 == null ? void 0 : process2.exitCode) !== void 0) {
72
+ timer.end(`\u274C Process exited with code ${process2.exitCode}`);
73
+ reject(new Error(`Process exited with code ${process2.exitCode}`));
74
+ return;
75
+ }
71
76
  const req = import_http.default.get(url, (res) => {
72
77
  if (res.statusCode && res.statusCode < 500) {
73
78
  timer.end(`\u2713 Server ready after ${attempts} attempts`);
@@ -113,8 +118,8 @@ function checkOpenCodeInstalled() {
113
118
  });
114
119
  });
115
120
  }
116
- function isPortAvailable(_0) {
117
- return __async(this, arguments, function* (port, hostname = import_shared.DEFAULT_HOSTNAME) {
121
+ function isPortAvailable(port, hostname) {
122
+ return __async(this, null, function* () {
118
123
  return new Promise((resolve) => {
119
124
  const server = import_net.default.createServer();
120
125
  server.once("error", (err) => {
@@ -130,8 +135,8 @@ function isPortAvailable(_0) {
130
135
  });
131
136
  });
132
137
  }
133
- function findAvailablePort(_0) {
134
- return __async(this, arguments, function* (startPort, hostname = import_shared.DEFAULT_HOSTNAME, maxTries = import_shared.MAX_PORT_TRIES) {
138
+ function findAvailablePort(_0, _1) {
139
+ return __async(this, arguments, function* (startPort, hostname, maxTries = import_shared.MAX_PORT_TRIES) {
135
140
  const timer = log.timer("findAvailablePort", { startPort, hostname, maxTries });
136
141
  log.debug(`Looking for available port starting from ${startPort}`);
137
142
  for (let port = startPort; port < startPort + maxTries; port++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-opencode-assistant",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "Embed OpenCode Web UI in your Vite dev server for real-time code modification and preview",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -36,9 +36,9 @@
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
38
  "unplugin-vue-inspector": "^2.4.0",
39
- "@vite-plugin-opencode-assistant/shared": "1.0.14",
40
- "@vite-plugin-opencode-assistant/opencode": "1.0.14",
41
- "@vite-plugin-opencode-assistant/components": "1.0.14"
39
+ "@vite-plugin-opencode-assistant/components": "1.0.15",
40
+ "@vite-plugin-opencode-assistant/opencode": "1.0.15",
41
+ "@vite-plugin-opencode-assistant/shared": "1.0.15"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "vite": ">=4.0.0"