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.
- package/es/client/index.js +71 -9
- package/es/core/proxy-server.d.ts +11 -0
- package/es/core/proxy-server.js +194 -0
- package/es/core/service.d.ts +4 -1
- package/es/core/service.js +36 -2
- package/es/endpoints/types.d.ts +1 -0
- package/es/index.js +20 -5
- package/lib/client/index.js +71 -9
- package/lib/client.js +2181 -2320
- package/lib/core/proxy-server.d.ts +11 -0
- package/lib/core/proxy-server.js +221 -0
- package/lib/core/service.d.ts +4 -1
- package/lib/core/service.js +31 -1
- package/lib/endpoints/types.d.ts +1 -0
- package/lib/index.js +19 -5
- package/lib/style.css +1 -1
- package/package.json +4 -4
package/es/client/index.js
CHANGED
|
@@ -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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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
|
+
};
|
package/es/core/service.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/es/core/service.js
CHANGED
|
@@ -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 {
|
|
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");
|
package/es/endpoints/types.d.ts
CHANGED
|
@@ -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(
|
|
70
|
-
|
|
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,
|
|
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
|
-
(
|
|
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,
|