vite-plugin-opencode-assistant 1.0.11 → 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 +29 -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 +29 -9
- package/lib/client.js +2173 -2349
- 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__");
|
|
@@ -140,7 +154,7 @@ const App = {
|
|
|
140
154
|
updatedAt: Date.now()
|
|
141
155
|
});
|
|
142
156
|
currentSessionId.value = newSession.id;
|
|
143
|
-
iframeSrc.value = `${
|
|
157
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${newSession.id}`;
|
|
144
158
|
loadSessions();
|
|
145
159
|
} catch (e) {
|
|
146
160
|
showNotification("\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25");
|
|
@@ -155,7 +169,7 @@ const App = {
|
|
|
155
169
|
if (sessions.value.length > 0) {
|
|
156
170
|
const nextSession = sessions.value[0];
|
|
157
171
|
currentSessionId.value = nextSession.id;
|
|
158
|
-
iframeSrc.value = `${
|
|
172
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${nextSession.id}`;
|
|
159
173
|
} else {
|
|
160
174
|
currentSessionId.value = null;
|
|
161
175
|
iframeSrc.value = "";
|
|
@@ -169,7 +183,7 @@ const App = {
|
|
|
169
183
|
if (currentSessionId.value === session.id) return;
|
|
170
184
|
currentSessionId.value = session.id;
|
|
171
185
|
loading.value = true;
|
|
172
|
-
iframeSrc.value = `${
|
|
186
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${session.id}`;
|
|
173
187
|
setTimeout(() => {
|
|
174
188
|
loading.value = false;
|
|
175
189
|
}, 500);
|
|
@@ -185,7 +199,7 @@ const App = {
|
|
|
185
199
|
updateContext(true);
|
|
186
200
|
} else if (data.type === "SESSION_READY") {
|
|
187
201
|
if (data.sessionUrl && !iframeSrc.value) {
|
|
188
|
-
iframeSrc.value = data.sessionUrl;
|
|
202
|
+
iframeSrc.value = toProxyUrl(data.sessionUrl);
|
|
189
203
|
currentSessionId.value = extractSessionId(data.sessionUrl);
|
|
190
204
|
}
|
|
191
205
|
isWaitingForSession.value = false;
|
|
@@ -225,7 +239,7 @@ const App = {
|
|
|
225
239
|
if (data.success) {
|
|
226
240
|
servicesStarted = true;
|
|
227
241
|
if (data.sessionUrl) {
|
|
228
|
-
iframeSrc.value = data.sessionUrl;
|
|
242
|
+
iframeSrc.value = toProxyUrl(data.sessionUrl);
|
|
229
243
|
currentSessionId.value = extractSessionId(data.sessionUrl);
|
|
230
244
|
isWaitingForSession.value = false;
|
|
231
245
|
}
|
|
@@ -300,7 +314,7 @@ const App = {
|
|
|
300
314
|
return h(OpenCodeWidget, {
|
|
301
315
|
ref: widgetRef,
|
|
302
316
|
position,
|
|
303
|
-
theme,
|
|
317
|
+
theme: theme.value,
|
|
304
318
|
open: open.value,
|
|
305
319
|
selectMode: selectMode.value,
|
|
306
320
|
sessionListCollapsed: sessionListCollapsed.value,
|
|
@@ -319,6 +333,12 @@ const App = {
|
|
|
319
333
|
"onUpdate:sessionListCollapsed": (val) => {
|
|
320
334
|
sessionListCollapsed.value = val;
|
|
321
335
|
},
|
|
336
|
+
"onUpdate:theme": (val) => {
|
|
337
|
+
theme.value = val;
|
|
338
|
+
},
|
|
339
|
+
"onToggle-theme": (val) => {
|
|
340
|
+
theme.value = val;
|
|
341
|
+
},
|
|
322
342
|
"onCreate-session": createSession,
|
|
323
343
|
"onDelete-session": deleteSession,
|
|
324
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__");
|
|
@@ -140,7 +154,7 @@ const App = {
|
|
|
140
154
|
updatedAt: Date.now()
|
|
141
155
|
});
|
|
142
156
|
currentSessionId.value = newSession.id;
|
|
143
|
-
iframeSrc.value = `${
|
|
157
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${newSession.id}`;
|
|
144
158
|
loadSessions();
|
|
145
159
|
} catch (e) {
|
|
146
160
|
showNotification("\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25");
|
|
@@ -155,7 +169,7 @@ const App = {
|
|
|
155
169
|
if (sessions.value.length > 0) {
|
|
156
170
|
const nextSession = sessions.value[0];
|
|
157
171
|
currentSessionId.value = nextSession.id;
|
|
158
|
-
iframeSrc.value = `${
|
|
172
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${nextSession.id}`;
|
|
159
173
|
} else {
|
|
160
174
|
currentSessionId.value = null;
|
|
161
175
|
iframeSrc.value = "";
|
|
@@ -169,7 +183,7 @@ const App = {
|
|
|
169
183
|
if (currentSessionId.value === session.id) return;
|
|
170
184
|
currentSessionId.value = session.id;
|
|
171
185
|
loading.value = true;
|
|
172
|
-
iframeSrc.value = `${
|
|
186
|
+
iframeSrc.value = `${proxyUrl}/${utf8ToBase64(cwd)}/session/${session.id}`;
|
|
173
187
|
setTimeout(() => {
|
|
174
188
|
loading.value = false;
|
|
175
189
|
}, 500);
|
|
@@ -185,7 +199,7 @@ const App = {
|
|
|
185
199
|
updateContext(true);
|
|
186
200
|
} else if (data.type === "SESSION_READY") {
|
|
187
201
|
if (data.sessionUrl && !iframeSrc.value) {
|
|
188
|
-
iframeSrc.value = data.sessionUrl;
|
|
202
|
+
iframeSrc.value = toProxyUrl(data.sessionUrl);
|
|
189
203
|
currentSessionId.value = extractSessionId(data.sessionUrl);
|
|
190
204
|
}
|
|
191
205
|
isWaitingForSession.value = false;
|
|
@@ -225,7 +239,7 @@ const App = {
|
|
|
225
239
|
if (data.success) {
|
|
226
240
|
servicesStarted = true;
|
|
227
241
|
if (data.sessionUrl) {
|
|
228
|
-
iframeSrc.value = data.sessionUrl;
|
|
242
|
+
iframeSrc.value = toProxyUrl(data.sessionUrl);
|
|
229
243
|
currentSessionId.value = extractSessionId(data.sessionUrl);
|
|
230
244
|
isWaitingForSession.value = false;
|
|
231
245
|
}
|
|
@@ -300,7 +314,7 @@ const App = {
|
|
|
300
314
|
return (0, import_vue.h)(import_components.OpenCodeWidget, {
|
|
301
315
|
ref: widgetRef,
|
|
302
316
|
position,
|
|
303
|
-
theme,
|
|
317
|
+
theme: theme.value,
|
|
304
318
|
open: open.value,
|
|
305
319
|
selectMode: selectMode.value,
|
|
306
320
|
sessionListCollapsed: sessionListCollapsed.value,
|
|
@@ -319,6 +333,12 @@ const App = {
|
|
|
319
333
|
"onUpdate:sessionListCollapsed": (val) => {
|
|
320
334
|
sessionListCollapsed.value = val;
|
|
321
335
|
},
|
|
336
|
+
"onUpdate:theme": (val) => {
|
|
337
|
+
theme.value = val;
|
|
338
|
+
},
|
|
339
|
+
"onToggle-theme": (val) => {
|
|
340
|
+
theme.value = val;
|
|
341
|
+
},
|
|
322
342
|
"onCreate-session": createSession,
|
|
323
343
|
"onDelete-session": deleteSession,
|
|
324
344
|
"onSelect-session": selectSession,
|