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.
- package/es/core/proxy-server.d.ts +5 -1
- package/es/core/proxy-server.js +75 -69
- package/es/core/service.js +45 -13
- package/es/utils/system.d.ts +2 -1
- package/es/utils/system.js +11 -10
- package/lib/core/proxy-server.d.ts +5 -1
- package/lib/core/proxy-server.js +75 -69
- package/lib/core/service.js +45 -13
- package/lib/utils/system.d.ts +2 -1
- package/lib/utils/system.js +10 -5
- package/package.json +4 -4
|
@@ -8,4 +8,8 @@ export interface ProxyServerOptions {
|
|
|
8
8
|
/** OpenCode 内部设置 */
|
|
9
9
|
settings?: OpenCodeSettings;
|
|
10
10
|
}
|
|
11
|
-
export
|
|
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>;
|
package/es/core/proxy-server.js
CHANGED
|
@@ -114,80 +114,86 @@ function generateBridgeScript(options) {
|
|
|
114
114
|
`;
|
|
115
115
|
}
|
|
116
116
|
function startProxyServer(targetUrl, port, options = {}) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
151
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
package/es/core/service.js
CHANGED
|
@@ -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.
|
|
175
|
-
|
|
176
|
-
this.
|
|
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 !== ((
|
|
184
|
+
if (this.actualProxyPort !== ((_d = this.config.proxyPort) != null ? _d : DEFAULT_PROXY_PORT)) {
|
|
180
185
|
log.info(
|
|
181
|
-
`Proxy port ${(
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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;
|
package/es/utils/system.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
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>;
|
package/es/utils/system.js
CHANGED
|
@@ -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(
|
|
85
|
-
return __async(this,
|
|
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
|
|
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
|
|
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>;
|
package/lib/core/proxy-server.js
CHANGED
|
@@ -140,80 +140,86 @@ function generateBridgeScript(options) {
|
|
|
140
140
|
`;
|
|
141
141
|
}
|
|
142
142
|
function startProxyServer(targetUrl, port, options = {}) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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 = {
|
package/lib/core/service.js
CHANGED
|
@@ -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.
|
|
186
|
-
|
|
187
|
-
this.
|
|
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 !== ((
|
|
195
|
+
if (this.actualProxyPort !== ((_d = this.config.proxyPort) != null ? _d : import_shared.DEFAULT_PROXY_PORT)) {
|
|
191
196
|
log.info(
|
|
192
|
-
`Proxy port ${(
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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;
|
package/lib/utils/system.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
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>;
|
package/lib/utils/system.js
CHANGED
|
@@ -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(
|
|
117
|
-
return __async(this,
|
|
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
|
|
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.
|
|
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/
|
|
40
|
-
"@vite-plugin-opencode-assistant/opencode": "1.0.
|
|
41
|
-
"@vite-plugin-opencode-assistant/
|
|
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"
|