vite-plugin-opencode-assistant 1.0.10 → 1.0.12
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 +35 -10
- 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 +35 -10
- package/lib/client.js +2176 -2348
- 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
|
@@ -46,6 +46,16 @@ function utf8ToBase64(str) {
|
|
|
46
46
|
const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
|
|
47
47
|
return btoa(binString);
|
|
48
48
|
}
|
|
49
|
+
let proxyUrl = "";
|
|
50
|
+
function toProxyUrl(url) {
|
|
51
|
+
if (!url || !proxyUrl) return url;
|
|
52
|
+
try {
|
|
53
|
+
const urlObj = new URL(url, window.location.origin);
|
|
54
|
+
return `${proxyUrl}${urlObj.pathname}${urlObj.search}`;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
return url;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
49
59
|
let config = {};
|
|
50
60
|
const scriptTag = document.querySelector(`script[${CONFIG_DATA_ATTR}]`);
|
|
51
61
|
if (scriptTag) {
|
|
@@ -74,15 +84,19 @@ const App = {
|
|
|
74
84
|
const selectedElements = ref([]);
|
|
75
85
|
const widgetRef = ref(null);
|
|
76
86
|
const {
|
|
77
|
-
webUrl = "",
|
|
78
87
|
position = "bottom-right",
|
|
79
|
-
theme = "auto",
|
|
88
|
+
theme: initialTheme = "auto",
|
|
80
89
|
open: autoOpen = false,
|
|
81
90
|
sessionUrl: initialSessionUrl = "",
|
|
91
|
+
proxyUrl: configProxyUrl = "",
|
|
82
92
|
lazy = false,
|
|
83
93
|
hotkey = "ctrl+k",
|
|
84
94
|
cwd = ""
|
|
85
95
|
} = config;
|
|
96
|
+
if (configProxyUrl) {
|
|
97
|
+
proxyUrl = configProxyUrl;
|
|
98
|
+
}
|
|
99
|
+
const theme = ref(initialTheme);
|
|
86
100
|
const isWaitingForSession = ref(!initialSessionUrl);
|
|
87
101
|
const computedLoading = computed(() => loading.value || isWaitingForSession.value);
|
|
88
102
|
let servicesStarted = !lazy;
|
|
@@ -93,7 +107,7 @@ const App = {
|
|
|
93
107
|
};
|
|
94
108
|
currentSessionId.value = extractSessionId(initialSessionUrl);
|
|
95
109
|
if (servicesStarted && initialSessionUrl) {
|
|
96
|
-
iframeSrc.value = initialSessionUrl;
|
|
110
|
+
iframeSrc.value = toProxyUrl(initialSessionUrl);
|
|
97
111
|
}
|
|
98
112
|
try {
|
|
99
113
|
const stored = sessionStorage.getItem("__opencode_selected_elements__");
|
|
@@ -134,9 +148,14 @@ const App = {
|
|
|
134
148
|
try {
|
|
135
149
|
const response = yield fetch("/__opencode_sessions__", { method: "POST" });
|
|
136
150
|
const newSession = yield response.json();
|
|
137
|
-
|
|
151
|
+
sessions.value.unshift({
|
|
152
|
+
id: newSession.id,
|
|
153
|
+
title: "\u65B0\u4F1A\u8BDD",
|
|
154
|
+
updatedAt: Date.now()
|
|
155
|
+
});
|
|
138
156
|
currentSessionId.value = newSession.id;
|
|
139
|
-
iframeSrc.value = `${
|
|
157
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${newSession.id}`;
|
|
158
|
+
loadSessions();
|
|
140
159
|
} catch (e) {
|
|
141
160
|
showNotification("\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25");
|
|
142
161
|
}
|
|
@@ -150,7 +169,7 @@ const App = {
|
|
|
150
169
|
if (sessions.value.length > 0) {
|
|
151
170
|
const nextSession = sessions.value[0];
|
|
152
171
|
currentSessionId.value = nextSession.id;
|
|
153
|
-
iframeSrc.value = `${
|
|
172
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${nextSession.id}`;
|
|
154
173
|
} else {
|
|
155
174
|
currentSessionId.value = null;
|
|
156
175
|
iframeSrc.value = "";
|
|
@@ -164,7 +183,7 @@ const App = {
|
|
|
164
183
|
if (currentSessionId.value === session.id) return;
|
|
165
184
|
currentSessionId.value = session.id;
|
|
166
185
|
loading.value = true;
|
|
167
|
-
iframeSrc.value = `${
|
|
186
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${session.id}`;
|
|
168
187
|
setTimeout(() => {
|
|
169
188
|
loading.value = false;
|
|
170
189
|
}, 500);
|
|
@@ -180,7 +199,7 @@ const App = {
|
|
|
180
199
|
updateContext(true);
|
|
181
200
|
} else if (data.type === "SESSION_READY") {
|
|
182
201
|
if (data.sessionUrl && !iframeSrc.value) {
|
|
183
|
-
iframeSrc.value = data.sessionUrl;
|
|
202
|
+
iframeSrc.value = toProxyUrl(data.sessionUrl);
|
|
184
203
|
currentSessionId.value = extractSessionId(data.sessionUrl);
|
|
185
204
|
}
|
|
186
205
|
isWaitingForSession.value = false;
|
|
@@ -220,7 +239,7 @@ const App = {
|
|
|
220
239
|
if (data.success) {
|
|
221
240
|
servicesStarted = true;
|
|
222
241
|
if (data.sessionUrl) {
|
|
223
|
-
iframeSrc.value = data.sessionUrl;
|
|
242
|
+
iframeSrc.value = toProxyUrl(data.sessionUrl);
|
|
224
243
|
currentSessionId.value = extractSessionId(data.sessionUrl);
|
|
225
244
|
isWaitingForSession.value = false;
|
|
226
245
|
}
|
|
@@ -295,7 +314,7 @@ const App = {
|
|
|
295
314
|
return h(OpenCodeWidget, {
|
|
296
315
|
ref: widgetRef,
|
|
297
316
|
position,
|
|
298
|
-
theme,
|
|
317
|
+
theme: theme.value,
|
|
299
318
|
open: open.value,
|
|
300
319
|
selectMode: selectMode.value,
|
|
301
320
|
sessionListCollapsed: sessionListCollapsed.value,
|
|
@@ -314,6 +333,12 @@ const App = {
|
|
|
314
333
|
"onUpdate:sessionListCollapsed": (val) => {
|
|
315
334
|
sessionListCollapsed.value = val;
|
|
316
335
|
},
|
|
336
|
+
"onUpdate:theme": (val) => {
|
|
337
|
+
theme.value = val;
|
|
338
|
+
},
|
|
339
|
+
"onToggle-theme": (val) => {
|
|
340
|
+
theme.value = val;
|
|
341
|
+
},
|
|
317
342
|
"onCreate-session": createSession,
|
|
318
343
|
"onDelete-session": deleteSession,
|
|
319
344
|
"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,
|
package/lib/client/index.js
CHANGED
|
@@ -46,6 +46,16 @@ function utf8ToBase64(str) {
|
|
|
46
46
|
const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
|
|
47
47
|
return btoa(binString);
|
|
48
48
|
}
|
|
49
|
+
let proxyUrl = "";
|
|
50
|
+
function toProxyUrl(url) {
|
|
51
|
+
if (!url || !proxyUrl) return url;
|
|
52
|
+
try {
|
|
53
|
+
const urlObj = new URL(url, window.location.origin);
|
|
54
|
+
return `${proxyUrl}${urlObj.pathname}${urlObj.search}`;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
return url;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
49
59
|
let config = {};
|
|
50
60
|
const scriptTag = document.querySelector(`script[${import_shared.CONFIG_DATA_ATTR}]`);
|
|
51
61
|
if (scriptTag) {
|
|
@@ -74,15 +84,19 @@ const App = {
|
|
|
74
84
|
const selectedElements = (0, import_vue.ref)([]);
|
|
75
85
|
const widgetRef = (0, import_vue.ref)(null);
|
|
76
86
|
const {
|
|
77
|
-
webUrl = "",
|
|
78
87
|
position = "bottom-right",
|
|
79
|
-
theme = "auto",
|
|
88
|
+
theme: initialTheme = "auto",
|
|
80
89
|
open: autoOpen = false,
|
|
81
90
|
sessionUrl: initialSessionUrl = "",
|
|
91
|
+
proxyUrl: configProxyUrl = "",
|
|
82
92
|
lazy = false,
|
|
83
93
|
hotkey = "ctrl+k",
|
|
84
94
|
cwd = ""
|
|
85
95
|
} = config;
|
|
96
|
+
if (configProxyUrl) {
|
|
97
|
+
proxyUrl = configProxyUrl;
|
|
98
|
+
}
|
|
99
|
+
const theme = (0, import_vue.ref)(initialTheme);
|
|
86
100
|
const isWaitingForSession = (0, import_vue.ref)(!initialSessionUrl);
|
|
87
101
|
const computedLoading = (0, import_vue.computed)(() => loading.value || isWaitingForSession.value);
|
|
88
102
|
let servicesStarted = !lazy;
|
|
@@ -93,7 +107,7 @@ const App = {
|
|
|
93
107
|
};
|
|
94
108
|
currentSessionId.value = extractSessionId(initialSessionUrl);
|
|
95
109
|
if (servicesStarted && initialSessionUrl) {
|
|
96
|
-
iframeSrc.value = initialSessionUrl;
|
|
110
|
+
iframeSrc.value = toProxyUrl(initialSessionUrl);
|
|
97
111
|
}
|
|
98
112
|
try {
|
|
99
113
|
const stored = sessionStorage.getItem("__opencode_selected_elements__");
|
|
@@ -134,9 +148,14 @@ const App = {
|
|
|
134
148
|
try {
|
|
135
149
|
const response = yield fetch("/__opencode_sessions__", { method: "POST" });
|
|
136
150
|
const newSession = yield response.json();
|
|
137
|
-
|
|
151
|
+
sessions.value.unshift({
|
|
152
|
+
id: newSession.id,
|
|
153
|
+
title: "\u65B0\u4F1A\u8BDD",
|
|
154
|
+
updatedAt: Date.now()
|
|
155
|
+
});
|
|
138
156
|
currentSessionId.value = newSession.id;
|
|
139
|
-
iframeSrc.value = `${
|
|
157
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${newSession.id}`;
|
|
158
|
+
loadSessions();
|
|
140
159
|
} catch (e) {
|
|
141
160
|
showNotification("\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25");
|
|
142
161
|
}
|
|
@@ -150,7 +169,7 @@ const App = {
|
|
|
150
169
|
if (sessions.value.length > 0) {
|
|
151
170
|
const nextSession = sessions.value[0];
|
|
152
171
|
currentSessionId.value = nextSession.id;
|
|
153
|
-
iframeSrc.value = `${
|
|
172
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${nextSession.id}`;
|
|
154
173
|
} else {
|
|
155
174
|
currentSessionId.value = null;
|
|
156
175
|
iframeSrc.value = "";
|
|
@@ -164,7 +183,7 @@ const App = {
|
|
|
164
183
|
if (currentSessionId.value === session.id) return;
|
|
165
184
|
currentSessionId.value = session.id;
|
|
166
185
|
loading.value = true;
|
|
167
|
-
iframeSrc.value = `${
|
|
186
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${session.id}`;
|
|
168
187
|
setTimeout(() => {
|
|
169
188
|
loading.value = false;
|
|
170
189
|
}, 500);
|
|
@@ -180,7 +199,7 @@ const App = {
|
|
|
180
199
|
updateContext(true);
|
|
181
200
|
} else if (data.type === "SESSION_READY") {
|
|
182
201
|
if (data.sessionUrl && !iframeSrc.value) {
|
|
183
|
-
iframeSrc.value = data.sessionUrl;
|
|
202
|
+
iframeSrc.value = toProxyUrl(data.sessionUrl);
|
|
184
203
|
currentSessionId.value = extractSessionId(data.sessionUrl);
|
|
185
204
|
}
|
|
186
205
|
isWaitingForSession.value = false;
|
|
@@ -220,7 +239,7 @@ const App = {
|
|
|
220
239
|
if (data.success) {
|
|
221
240
|
servicesStarted = true;
|
|
222
241
|
if (data.sessionUrl) {
|
|
223
|
-
iframeSrc.value = data.sessionUrl;
|
|
242
|
+
iframeSrc.value = toProxyUrl(data.sessionUrl);
|
|
224
243
|
currentSessionId.value = extractSessionId(data.sessionUrl);
|
|
225
244
|
isWaitingForSession.value = false;
|
|
226
245
|
}
|
|
@@ -295,7 +314,7 @@ const App = {
|
|
|
295
314
|
return (0, import_vue.h)(import_components.OpenCodeWidget, {
|
|
296
315
|
ref: widgetRef,
|
|
297
316
|
position,
|
|
298
|
-
theme,
|
|
317
|
+
theme: theme.value,
|
|
299
318
|
open: open.value,
|
|
300
319
|
selectMode: selectMode.value,
|
|
301
320
|
sessionListCollapsed: sessionListCollapsed.value,
|
|
@@ -314,6 +333,12 @@ const App = {
|
|
|
314
333
|
"onUpdate:sessionListCollapsed": (val) => {
|
|
315
334
|
sessionListCollapsed.value = val;
|
|
316
335
|
},
|
|
336
|
+
"onUpdate:theme": (val) => {
|
|
337
|
+
theme.value = val;
|
|
338
|
+
},
|
|
339
|
+
"onToggle-theme": (val) => {
|
|
340
|
+
theme.value = val;
|
|
341
|
+
},
|
|
317
342
|
"onCreate-session": createSession,
|
|
318
343
|
"onDelete-session": deleteSession,
|
|
319
344
|
"onSelect-session": selectSession,
|