@vite-plugin-opencode-assistant/opencode 1.0.7
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/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/es/plugins/page-context.d.ts +7 -0
- package/es/plugins/page-context.js +265 -0
- package/es/web.d.ts +4 -0
- package/es/web.js +156 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +21 -0
- package/lib/plugins/page-context.d.ts +7 -0
- package/lib/plugins/page-context.js +196 -0
- package/lib/web.d.ts +4 -0
- package/lib/web.js +188 -0
- package/package.json +35 -0
package/es/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./web.js";
|
package/es/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./web.js";
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
const C = "[vite-plugin-opencode]";
|
|
2
|
+
var S = Object.defineProperty, N = Object.defineProperties, j = Object.getOwnPropertyDescriptors, b = Object.getOwnPropertySymbols, L = Object.prototype.hasOwnProperty, R = Object.prototype.propertyIsEnumerable, O = (e, t, r) => t in e ? S(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r, g = (e, t) => {
|
|
3
|
+
for (var r in t || (t = {}))
|
|
4
|
+
L.call(t, r) && O(e, r, t[r]);
|
|
5
|
+
if (b)
|
|
6
|
+
for (var r of b(t))
|
|
7
|
+
R.call(t, r) && O(e, r, t[r]);
|
|
8
|
+
return e;
|
|
9
|
+
}, m = (e, t) => N(e, j(t)), y = (e, t, r) => O(e, typeof t != "symbol" ? t + "" : t, r);
|
|
10
|
+
const u = {
|
|
11
|
+
reset: "\x1B[0m",
|
|
12
|
+
dim: "\x1B[2m",
|
|
13
|
+
bright: "\x1B[1m",
|
|
14
|
+
red: "\x1B[31m",
|
|
15
|
+
green: "\x1B[32m",
|
|
16
|
+
yellow: "\x1B[33m",
|
|
17
|
+
magenta: "\x1B[35m",
|
|
18
|
+
cyan: "\x1B[36m"
|
|
19
|
+
}, A = {
|
|
20
|
+
0: u.cyan,
|
|
21
|
+
1: u.green,
|
|
22
|
+
2: u.yellow,
|
|
23
|
+
3: u.red,
|
|
24
|
+
4: u.reset
|
|
25
|
+
}, I = {
|
|
26
|
+
0: "DEBUG",
|
|
27
|
+
1: "INFO",
|
|
28
|
+
2: "WARN",
|
|
29
|
+
3: "ERROR",
|
|
30
|
+
4: "NONE"
|
|
31
|
+
};
|
|
32
|
+
let x = {
|
|
33
|
+
level: 1,
|
|
34
|
+
showTrace: !1
|
|
35
|
+
};
|
|
36
|
+
function M() {
|
|
37
|
+
const e = /* @__PURE__ */ new Date(), t = String(e.getHours()).padStart(2, "0"), r = String(e.getMinutes()).padStart(2, "0"), o = String(e.getSeconds()).padStart(2, "0"), i = String(e.getMilliseconds()).padStart(3, "0");
|
|
38
|
+
return `${t}:${r}:${o}.${i}`;
|
|
39
|
+
}
|
|
40
|
+
function U(e = 3) {
|
|
41
|
+
const t = new Error().stack;
|
|
42
|
+
if (!t) return "";
|
|
43
|
+
const o = t.split(`
|
|
44
|
+
`)[e];
|
|
45
|
+
if (!o) return "";
|
|
46
|
+
const i = o.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/);
|
|
47
|
+
if (!i) return "";
|
|
48
|
+
const [, p, n, a] = i;
|
|
49
|
+
return `${n.split("/").pop() || n}:${a} ${p || "<anonymous>"}`;
|
|
50
|
+
}
|
|
51
|
+
function h(e, t = 0) {
|
|
52
|
+
if (t > 3) return "...";
|
|
53
|
+
if (e === null) return "null";
|
|
54
|
+
if (e === void 0) return "undefined";
|
|
55
|
+
if (typeof e == "string") return t > 0 ? `"${e}"` : e;
|
|
56
|
+
if (typeof e == "number" || typeof e == "boolean") return String(e);
|
|
57
|
+
if (e instanceof Error)
|
|
58
|
+
return `${e.name}: ${e.message}${e.stack ? `
|
|
59
|
+
${e.stack}` : ""}`;
|
|
60
|
+
if (Array.isArray(e))
|
|
61
|
+
return e.length === 0 ? "[]" : e.length > 5 ? `[${e.slice(0, 3).map((i) => h(i, t + 1)).join(", ")}, ... ${e.length - 3} more items]` : `[${e.map((o) => h(o, t + 1)).join(", ")}]`;
|
|
62
|
+
if (typeof e == "object") {
|
|
63
|
+
const r = Object.entries(e);
|
|
64
|
+
return r.length === 0 ? "{}" : r.length > 5 ? `{${r.slice(0, 3).map(([p, n]) => `${p}: ${h(n, t + 1)}`).join(", ")}, ... ${r.length - 3} more keys}` : `{${r.map(([i, p]) => `${i}: ${h(p, t + 1)}`).join(", ")}}`;
|
|
65
|
+
}
|
|
66
|
+
return String(e);
|
|
67
|
+
}
|
|
68
|
+
function k(e) {
|
|
69
|
+
if (!e || Object.keys(e).length === 0) return "";
|
|
70
|
+
const t = [];
|
|
71
|
+
e.module && t.push(`[${e.module}]`), e.operation && t.push(`(${e.operation})`), e.traceId && t.push(`trace:${e.traceId}`), e.duration !== void 0 && t.push(`${e.duration}ms`);
|
|
72
|
+
const r = Object.keys(e).filter(
|
|
73
|
+
(o) => !["module", "operation", "traceId", "duration", "error"].includes(o)
|
|
74
|
+
);
|
|
75
|
+
if (r.length > 0) {
|
|
76
|
+
const o = {};
|
|
77
|
+
r.forEach((i) => o[i] = e[i]), t.push(h(o));
|
|
78
|
+
}
|
|
79
|
+
return t.join(" ");
|
|
80
|
+
}
|
|
81
|
+
function E(e, t, r, ...o) {
|
|
82
|
+
if (e < x.level) return;
|
|
83
|
+
const i = [];
|
|
84
|
+
i.push(`${u.dim}[${process.pid}]${u.reset}`), i.push(`${u.dim}${M()}${u.reset}`);
|
|
85
|
+
const p = A[e], n = I[e].padEnd(5);
|
|
86
|
+
i.push(`${p}${n}${u.reset}`), i.push(`${u.bright}${C}${u.reset}`);
|
|
87
|
+
const a = k(r);
|
|
88
|
+
if (a && i.push(`${u.magenta}${a}${u.reset}`), i.push(t), e >= 2) {
|
|
89
|
+
const c = U(4);
|
|
90
|
+
c && i.push(`${u.dim}(${c})${u.reset}`);
|
|
91
|
+
}
|
|
92
|
+
const s = o.map((c) => h(c)).join(" ");
|
|
93
|
+
s && i.push(s);
|
|
94
|
+
const f = i.join(" ");
|
|
95
|
+
e >= 3 ? console.error(f) : e === 2 ? console.warn(f) : console.log(f), r != null && r.error && e >= 3 && x.showTrace;
|
|
96
|
+
}
|
|
97
|
+
const d = {
|
|
98
|
+
debug(e, t, ...r) {
|
|
99
|
+
E(0, e, t, ...r);
|
|
100
|
+
},
|
|
101
|
+
info(e, t, ...r) {
|
|
102
|
+
E(1, e, t, ...r);
|
|
103
|
+
},
|
|
104
|
+
warn(e, t, ...r) {
|
|
105
|
+
E(2, e, t, ...r);
|
|
106
|
+
},
|
|
107
|
+
error(e, t, ...r) {
|
|
108
|
+
E(3, e, t, ...r);
|
|
109
|
+
},
|
|
110
|
+
group(e, t) {
|
|
111
|
+
},
|
|
112
|
+
groupEnd() {
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
class B {
|
|
116
|
+
constructor(t, r) {
|
|
117
|
+
y(this, "startTime"), y(this, "context"), y(this, "operation"), this.operation = t, this.context = r || {}, this.startTime = performance.now(), d.debug(`⏱️ Starting: ${t}`, this.context);
|
|
118
|
+
}
|
|
119
|
+
end(t) {
|
|
120
|
+
const r = Math.round(performance.now() - this.startTime), o = t || `✓ Completed: ${this.operation}`;
|
|
121
|
+
return d.debug(o, m(g({}, this.context), { duration: r })), r;
|
|
122
|
+
}
|
|
123
|
+
checkpoint(t) {
|
|
124
|
+
const r = Math.round(performance.now() - this.startTime);
|
|
125
|
+
return d.debug(` ↳ ${t}`, m(g({}, this.context), { duration: r })), r;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function D(e) {
|
|
129
|
+
return {
|
|
130
|
+
debug(t, r, ...o) {
|
|
131
|
+
d.debug(t, m(g({}, r), { module: e }), ...o);
|
|
132
|
+
},
|
|
133
|
+
info(t, r, ...o) {
|
|
134
|
+
d.info(t, m(g({}, r), { module: e }), ...o);
|
|
135
|
+
},
|
|
136
|
+
warn(t, r, ...o) {
|
|
137
|
+
d.warn(t, m(g({}, r), { module: e }), ...o);
|
|
138
|
+
},
|
|
139
|
+
error(t, r, ...o) {
|
|
140
|
+
d.error(t, m(g({}, r), { module: e }), ...o);
|
|
141
|
+
},
|
|
142
|
+
timer(t, r) {
|
|
143
|
+
return new B(t, m(g({}, r), { module: e }));
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const T = 1e4, F = "__OPENCODE_CONTEXT__", l = D("OpenCodePluginPageContext"), X = async () => {
|
|
148
|
+
l.info("PageContextPlugin loading...");
|
|
149
|
+
const e = process.env.OPENCODE_CONTEXT_API_URL;
|
|
150
|
+
if (l.debug("Context API URL:", { contextApiUrl: e }), !e)
|
|
151
|
+
return l.warn("OPENCODE_CONTEXT_API_URL is not set, page context plugin will not work"), {};
|
|
152
|
+
const t = e;
|
|
153
|
+
l.info("Plugin initialized successfully");
|
|
154
|
+
async function r() {
|
|
155
|
+
try {
|
|
156
|
+
l.debug("Fetching context...", { apiUrl: t });
|
|
157
|
+
const n = await fetch(t);
|
|
158
|
+
if (!n.ok)
|
|
159
|
+
return l.error("Context API returned error status", {
|
|
160
|
+
status: n.status,
|
|
161
|
+
statusText: n.statusText,
|
|
162
|
+
apiUrl: t
|
|
163
|
+
}), null;
|
|
164
|
+
const a = await n.json();
|
|
165
|
+
return l.debug("Context received", { url: a.url, title: a.title }), {
|
|
166
|
+
url: a.url || "",
|
|
167
|
+
title: a.title || "",
|
|
168
|
+
selectedElements: a.selectedElements
|
|
169
|
+
};
|
|
170
|
+
} catch (n) {
|
|
171
|
+
const a = n instanceof Error ? n.message : String(n), s = n instanceof Error ? n.name : "UnknownError";
|
|
172
|
+
return l.error("Failed to get context", {
|
|
173
|
+
error: a,
|
|
174
|
+
errorType: s,
|
|
175
|
+
apiUrl: t
|
|
176
|
+
}), null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function o() {
|
|
180
|
+
try {
|
|
181
|
+
l.debug("Clearing selected elements", { apiUrl: t });
|
|
182
|
+
const n = await fetch(t, { method: "DELETE" });
|
|
183
|
+
l.debug("Clear response", { status: n.status });
|
|
184
|
+
} catch (n) {
|
|
185
|
+
const a = n instanceof Error ? n.message : String(n), s = n instanceof Error ? n.name : "UnknownError";
|
|
186
|
+
l.error("Failed to clear selected elements", {
|
|
187
|
+
error: a,
|
|
188
|
+
errorType: s,
|
|
189
|
+
apiUrl: t
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function i(n, a) {
|
|
194
|
+
var f;
|
|
195
|
+
const s = [];
|
|
196
|
+
if (s.push(`### 选中节点 ${a + 1}`), n.filePath) {
|
|
197
|
+
let c = n.filePath;
|
|
198
|
+
n.line && (c += `:${n.line}`, n.column && (c += `:${n.column}`)), s.push(`- **文件位置**: \`${c}\``);
|
|
199
|
+
}
|
|
200
|
+
if ((f = n.innerText) != null && f.trim()) {
|
|
201
|
+
const c = n.innerText.trim().substring(0, T), $ = n.innerText.length > T ? `
|
|
202
|
+
... (已省略部分内容)` : "";
|
|
203
|
+
s.push(`- **节点文本**:
|
|
204
|
+
\`\`\`text
|
|
205
|
+
${c}${$}
|
|
206
|
+
\`\`\``);
|
|
207
|
+
}
|
|
208
|
+
return s.join(`
|
|
209
|
+
`) + `
|
|
210
|
+
`;
|
|
211
|
+
}
|
|
212
|
+
function p(n) {
|
|
213
|
+
var f;
|
|
214
|
+
const a = n.title ? `[${n.title}](${n.url})` : n.url;
|
|
215
|
+
let s = `【系统提示:以下是用户当前正在浏览的页面上下文,请将其作为最高优先级的背景信息来理解和响应用户的请求。】
|
|
216
|
+
|
|
217
|
+
`;
|
|
218
|
+
return s += `用户现在正在浏览项目中的这个页面:${a}
|
|
219
|
+
|
|
220
|
+
`, (f = n.selectedElements) != null && f.length && (s += `用户选中了以下节点:
|
|
221
|
+
|
|
222
|
+
`, n.selectedElements.forEach((c, $) => {
|
|
223
|
+
s += i(c, $) + `
|
|
224
|
+
`;
|
|
225
|
+
})), s += `---
|
|
226
|
+
**用户的请求**:
|
|
227
|
+
|
|
228
|
+
`, s;
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
"experimental.chat.system.transform": async (n, a) => {
|
|
232
|
+
l.debug("System transform hook called");
|
|
233
|
+
const s = `
|
|
234
|
+
你是一个专业的前端开发助手,当前正集成在用户的 Vite 项目中(通过 vite-plugin-opencode-assistant)。
|
|
235
|
+
在对话中,用户可能会自动附加他们当前正在浏览的页面上下文(包括页面 URL、标题以及在页面上选中的 DOM 节点信息)。
|
|
236
|
+
|
|
237
|
+
处理这些上下文时,请遵循以下规则:
|
|
238
|
+
1. **理解上下文**:当看到“我现在正在浏览项目中的这个页面”或“选中节点”等信息时,请将其作为用户请求的背景。
|
|
239
|
+
2. **利用文件路径**:如果提供的节点信息中包含 \`文件位置\`,这通常对应于项目中的源代码文件。你可以直接分析或建议修改这些文件。
|
|
240
|
+
3. **精准定位**:结合节点的 \`节点文本\` 和 \`文件位置\`,帮助用户快速定位问题或实现功能。
|
|
241
|
+
4. **直接给出方案**:针对用户的实际请求,直接给出清晰、可执行的代码修改建议或解释,避免不必要的废话。
|
|
242
|
+
`.trim();
|
|
243
|
+
a.system.push(s);
|
|
244
|
+
},
|
|
245
|
+
"experimental.chat.messages.transform": async (n, a) => {
|
|
246
|
+
var P, w;
|
|
247
|
+
l.debug("Message transform hook called");
|
|
248
|
+
const s = await r();
|
|
249
|
+
if (l.debug("Context data", {
|
|
250
|
+
hasUrl: !!(s != null && s.url),
|
|
251
|
+
hasElements: !!((P = s == null ? void 0 : s.selectedElements) != null && P.length)
|
|
252
|
+
}), !(s != null && s.url)) return;
|
|
253
|
+
const f = [...a.messages].reverse().find((_) => _.info.role === "user");
|
|
254
|
+
if (!f) return;
|
|
255
|
+
const c = f.parts.find((_) => _.type === "text");
|
|
256
|
+
if (!c || !("text" in c) || c.text.includes(F)) return;
|
|
257
|
+
const $ = p(s);
|
|
258
|
+
c.text = $ + c.text, (w = s.selectedElements) != null && w.length && (l.debug("Selected elements found, clearing..."), await o());
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
export {
|
|
263
|
+
X as PageContextPlugin,
|
|
264
|
+
X as default
|
|
265
|
+
};
|
package/es/web.d.ts
ADDED
package/es/web.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
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 { execa } from "execa";
|
|
21
|
+
import fs from "fs";
|
|
22
|
+
import { createRequire } from "module";
|
|
23
|
+
import path from "path";
|
|
24
|
+
import { createLogger } from "@vite-plugin-opencode-assistant/shared";
|
|
25
|
+
const require2 = createRequire(path.join(process.cwd(), "package.json"));
|
|
26
|
+
const packageDir = resolvePackageDir();
|
|
27
|
+
const log = createLogger("OpenCodeWeb");
|
|
28
|
+
function prepareOpenCodeRuntime(cwd) {
|
|
29
|
+
const cacheDir = path.join(cwd, "node_modules", ".cache", "opencode");
|
|
30
|
+
const pluginsDir = path.join(cacheDir, "plugins");
|
|
31
|
+
log.debug("Setting up OpenCode runtime", { cacheDir, pluginsDir });
|
|
32
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
33
|
+
fs.mkdirSync(pluginsDir, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
const pluginSourcePath = resolvePluginSourcePath();
|
|
36
|
+
const pluginTargetPath = path.join(pluginsDir, "page-context.js");
|
|
37
|
+
if (!fs.existsSync(pluginSourcePath)) {
|
|
38
|
+
throw new Error(`Page context plugin not found: ${pluginSourcePath}`);
|
|
39
|
+
}
|
|
40
|
+
fs.copyFileSync(pluginSourcePath, pluginTargetPath);
|
|
41
|
+
const mcpConfigPath = path.join(cacheDir, "opencode.json");
|
|
42
|
+
fs.writeFileSync(
|
|
43
|
+
mcpConfigPath,
|
|
44
|
+
JSON.stringify(
|
|
45
|
+
{
|
|
46
|
+
mcp: {
|
|
47
|
+
"chrome-devtools": {
|
|
48
|
+
type: "local",
|
|
49
|
+
command: ["npx", "-y", "chrome-devtools-mcp@latest", "--autoConnect"],
|
|
50
|
+
enabled: true
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
null,
|
|
55
|
+
2
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
log.debug("OpenCode runtime ready", {
|
|
59
|
+
cacheDir,
|
|
60
|
+
pluginTargetPath,
|
|
61
|
+
mcpConfigPath
|
|
62
|
+
});
|
|
63
|
+
return cacheDir;
|
|
64
|
+
}
|
|
65
|
+
function startOpenCodeWeb(options) {
|
|
66
|
+
var _a, _b;
|
|
67
|
+
const { port, hostname, cwd, configDir, corsOrigins, contextApiUrl } = options;
|
|
68
|
+
const stateDir = createStateDirectory(cwd);
|
|
69
|
+
const pluginPath = path.join(stateDir, "plugins", "page-context.js");
|
|
70
|
+
log.debug("Building process environment", {
|
|
71
|
+
stateDir,
|
|
72
|
+
configDir,
|
|
73
|
+
contextApiUrl,
|
|
74
|
+
pluginPath
|
|
75
|
+
});
|
|
76
|
+
const env = buildProcessEnv(stateDir, configDir, contextApiUrl, pluginPath);
|
|
77
|
+
const args = ["serve", "--port", String(port), "--hostname", hostname];
|
|
78
|
+
if (corsOrigins && corsOrigins.length > 0) {
|
|
79
|
+
corsOrigins.forEach((origin) => {
|
|
80
|
+
args.push("--cors", origin);
|
|
81
|
+
});
|
|
82
|
+
log.debug("CORS origins added", { origins: corsOrigins });
|
|
83
|
+
}
|
|
84
|
+
log.debug("Spawning OpenCode process", {
|
|
85
|
+
command: "opencode",
|
|
86
|
+
args: args.join(" "),
|
|
87
|
+
cwd
|
|
88
|
+
});
|
|
89
|
+
const proc = execa("opencode", args, {
|
|
90
|
+
cwd,
|
|
91
|
+
env,
|
|
92
|
+
reject: false,
|
|
93
|
+
cleanup: true
|
|
94
|
+
});
|
|
95
|
+
(_a = proc.stdout) == null ? void 0 : _a.on("data", (data) => {
|
|
96
|
+
const output = data.toString().trim();
|
|
97
|
+
if (output) {
|
|
98
|
+
log.debug("[OpenCode stdout]", { output });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
(_b = proc.stderr) == null ? void 0 : _b.on("data", (data) => {
|
|
102
|
+
const output = data.toString().trim();
|
|
103
|
+
if (output) {
|
|
104
|
+
log.warn("[OpenCode stderr]", { output });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
return proc;
|
|
108
|
+
}
|
|
109
|
+
function createStateDirectory(cwd) {
|
|
110
|
+
const stateDir = path.join(cwd, "node_modules", ".cache", "opencode");
|
|
111
|
+
if (!fs.existsSync(stateDir)) {
|
|
112
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
113
|
+
log.debug("Created state directory", { stateDir });
|
|
114
|
+
}
|
|
115
|
+
return stateDir;
|
|
116
|
+
}
|
|
117
|
+
function resolvePackageDir() {
|
|
118
|
+
const entryPath = require2.resolve("@vite-plugin-opencode-assistant/opencode");
|
|
119
|
+
return path.resolve(path.dirname(entryPath), "..");
|
|
120
|
+
}
|
|
121
|
+
function resolvePluginSourcePath() {
|
|
122
|
+
const candidatePaths = [
|
|
123
|
+
path.join(packageDir, "es", "plugins", "page-context.js"),
|
|
124
|
+
path.join(packageDir, "lib", "plugins", "page-context.js")
|
|
125
|
+
];
|
|
126
|
+
for (const candidatePath of candidatePaths) {
|
|
127
|
+
if (fs.existsSync(candidatePath)) {
|
|
128
|
+
return candidatePath;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return candidatePaths[0];
|
|
132
|
+
}
|
|
133
|
+
function buildProcessEnv(stateDir, configDir, contextApiUrl, pluginPath) {
|
|
134
|
+
const env = __spreadProps(__spreadValues({}, Object.fromEntries(
|
|
135
|
+
Object.entries(process.env).filter(([, v]) => v !== void 0)
|
|
136
|
+
)), {
|
|
137
|
+
XDG_STATE_HOME: stateDir
|
|
138
|
+
});
|
|
139
|
+
if (configDir) {
|
|
140
|
+
env.OPENCODE_CONFIG_DIR = configDir;
|
|
141
|
+
log.debug("Set OPENCODE_CONFIG_DIR", { configDir });
|
|
142
|
+
}
|
|
143
|
+
if (contextApiUrl) {
|
|
144
|
+
env.OPENCODE_CONTEXT_API_URL = contextApiUrl;
|
|
145
|
+
log.debug("Set OPENCODE_CONTEXT_API_URL", { contextApiUrl });
|
|
146
|
+
}
|
|
147
|
+
if (pluginPath) {
|
|
148
|
+
env.OPENCODE_PLUGINS = pluginPath;
|
|
149
|
+
log.debug("Set OPENCODE_PLUGINS", { pluginPath });
|
|
150
|
+
}
|
|
151
|
+
return env;
|
|
152
|
+
}
|
|
153
|
+
export {
|
|
154
|
+
prepareOpenCodeRuntime,
|
|
155
|
+
startOpenCodeWeb
|
|
156
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./web.js";
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __copyProps = (to, from, except, desc) => {
|
|
6
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
7
|
+
for (let key of __getOwnPropNames(from))
|
|
8
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
9
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
10
|
+
}
|
|
11
|
+
return to;
|
|
12
|
+
};
|
|
13
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var lib_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(lib_exports);
|
|
17
|
+
__reExport(lib_exports, require("./web.js"), module.exports);
|
|
18
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
19
|
+
0 && (module.exports = {
|
|
20
|
+
...require("./web.js")
|
|
21
|
+
});
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var __async = (__this, __arguments, generator) => {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
var fulfilled = (value) => {
|
|
21
|
+
try {
|
|
22
|
+
step(generator.next(value));
|
|
23
|
+
} catch (e) {
|
|
24
|
+
reject(e);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var rejected = (value) => {
|
|
28
|
+
try {
|
|
29
|
+
step(generator.throw(value));
|
|
30
|
+
} catch (e) {
|
|
31
|
+
reject(e);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
35
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
var page_context_exports = {};
|
|
39
|
+
__export(page_context_exports, {
|
|
40
|
+
PageContextPlugin: () => PageContextPlugin,
|
|
41
|
+
default: () => page_context_default
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(page_context_exports);
|
|
44
|
+
var import_shared = require("@vite-plugin-opencode-assistant/shared");
|
|
45
|
+
const MAX_TEXT_LENGTH = 1e4;
|
|
46
|
+
const CONTEXT_MARKER = "__OPENCODE_CONTEXT__";
|
|
47
|
+
const log = (0, import_shared.createLogger)("OpenCodePluginPageContext");
|
|
48
|
+
const PageContextPlugin = () => __async(null, null, function* () {
|
|
49
|
+
log.info("PageContextPlugin loading...");
|
|
50
|
+
const contextApiUrl = process.env.OPENCODE_CONTEXT_API_URL;
|
|
51
|
+
log.debug("Context API URL:", { contextApiUrl });
|
|
52
|
+
if (!contextApiUrl) {
|
|
53
|
+
log.warn("OPENCODE_CONTEXT_API_URL is not set, page context plugin will not work");
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
const apiUrl = contextApiUrl;
|
|
57
|
+
log.info("Plugin initialized successfully");
|
|
58
|
+
function getPageContext() {
|
|
59
|
+
return __async(this, null, function* () {
|
|
60
|
+
try {
|
|
61
|
+
log.debug("Fetching context...", { apiUrl });
|
|
62
|
+
const response = yield fetch(apiUrl);
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
log.error("Context API returned error status", {
|
|
65
|
+
status: response.status,
|
|
66
|
+
statusText: response.statusText,
|
|
67
|
+
apiUrl
|
|
68
|
+
});
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const data = yield response.json();
|
|
72
|
+
log.debug("Context received", { url: data.url, title: data.title });
|
|
73
|
+
return {
|
|
74
|
+
url: data.url || "",
|
|
75
|
+
title: data.title || "",
|
|
76
|
+
selectedElements: data.selectedElements
|
|
77
|
+
};
|
|
78
|
+
} catch (error) {
|
|
79
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
80
|
+
const errorName = error instanceof Error ? error.name : "UnknownError";
|
|
81
|
+
log.error("Failed to get context", {
|
|
82
|
+
error: errorMessage,
|
|
83
|
+
errorType: errorName,
|
|
84
|
+
apiUrl
|
|
85
|
+
});
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function clearSelectedElements() {
|
|
91
|
+
return __async(this, null, function* () {
|
|
92
|
+
try {
|
|
93
|
+
log.debug("Clearing selected elements", { apiUrl });
|
|
94
|
+
const response = yield fetch(apiUrl, { method: "DELETE" });
|
|
95
|
+
log.debug("Clear response", { status: response.status });
|
|
96
|
+
} catch (error) {
|
|
97
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
98
|
+
const errorName = error instanceof Error ? error.name : "UnknownError";
|
|
99
|
+
log.error("Failed to clear selected elements", {
|
|
100
|
+
error: errorMessage,
|
|
101
|
+
errorType: errorName,
|
|
102
|
+
apiUrl
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function formatSelectedElement(element, index) {
|
|
108
|
+
var _a;
|
|
109
|
+
const parts = [];
|
|
110
|
+
parts.push(`### \u9009\u4E2D\u8282\u70B9 ${index + 1}`);
|
|
111
|
+
if (element.filePath) {
|
|
112
|
+
let location = element.filePath;
|
|
113
|
+
if (element.line) {
|
|
114
|
+
location += `:${element.line}`;
|
|
115
|
+
if (element.column) {
|
|
116
|
+
location += `:${element.column}`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
parts.push(`- **\u6587\u4EF6\u4F4D\u7F6E**: \`${location}\``);
|
|
120
|
+
}
|
|
121
|
+
if ((_a = element.innerText) == null ? void 0 : _a.trim()) {
|
|
122
|
+
const text = element.innerText.trim().substring(0, MAX_TEXT_LENGTH);
|
|
123
|
+
const suffix = element.innerText.length > MAX_TEXT_LENGTH ? "\n... (\u5DF2\u7701\u7565\u90E8\u5206\u5185\u5BB9)" : "";
|
|
124
|
+
parts.push(`- **\u8282\u70B9\u6587\u672C**:
|
|
125
|
+
\`\`\`text
|
|
126
|
+
${text}${suffix}
|
|
127
|
+
\`\`\``);
|
|
128
|
+
}
|
|
129
|
+
return parts.join("\n") + "\n";
|
|
130
|
+
}
|
|
131
|
+
function buildContextPrefix(context) {
|
|
132
|
+
var _a;
|
|
133
|
+
const pageLink = context.title ? `[${context.title}](${context.url})` : context.url;
|
|
134
|
+
let prefix = `\u3010\u7CFB\u7EDF\u63D0\u793A\uFF1A\u4EE5\u4E0B\u662F\u7528\u6237\u5F53\u524D\u6B63\u5728\u6D4F\u89C8\u7684\u9875\u9762\u4E0A\u4E0B\u6587\uFF0C\u8BF7\u5C06\u5176\u4F5C\u4E3A\u6700\u9AD8\u4F18\u5148\u7EA7\u7684\u80CC\u666F\u4FE1\u606F\u6765\u7406\u89E3\u548C\u54CD\u5E94\u7528\u6237\u7684\u8BF7\u6C42\u3002\u3011
|
|
135
|
+
|
|
136
|
+
`;
|
|
137
|
+
prefix += `\u7528\u6237\u73B0\u5728\u6B63\u5728\u6D4F\u89C8\u9879\u76EE\u4E2D\u7684\u8FD9\u4E2A\u9875\u9762\uFF1A${pageLink}
|
|
138
|
+
|
|
139
|
+
`;
|
|
140
|
+
if ((_a = context.selectedElements) == null ? void 0 : _a.length) {
|
|
141
|
+
prefix += `\u7528\u6237\u9009\u4E2D\u4E86\u4EE5\u4E0B\u8282\u70B9\uFF1A
|
|
142
|
+
|
|
143
|
+
`;
|
|
144
|
+
context.selectedElements.forEach((element, index) => {
|
|
145
|
+
prefix += formatSelectedElement(element, index) + "\n";
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
prefix += `---
|
|
149
|
+
**\u7528\u6237\u7684\u8BF7\u6C42**\uFF1A
|
|
150
|
+
|
|
151
|
+
`;
|
|
152
|
+
return prefix;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
"experimental.chat.system.transform": (_input, output) => __async(null, null, function* () {
|
|
156
|
+
log.debug("System transform hook called");
|
|
157
|
+
const systemPrompt = `
|
|
158
|
+
\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u524D\u7AEF\u5F00\u53D1\u52A9\u624B\uFF0C\u5F53\u524D\u6B63\u96C6\u6210\u5728\u7528\u6237\u7684 Vite \u9879\u76EE\u4E2D\uFF08\u901A\u8FC7 vite-plugin-opencode-assistant\uFF09\u3002
|
|
159
|
+
\u5728\u5BF9\u8BDD\u4E2D\uFF0C\u7528\u6237\u53EF\u80FD\u4F1A\u81EA\u52A8\u9644\u52A0\u4ED6\u4EEC\u5F53\u524D\u6B63\u5728\u6D4F\u89C8\u7684\u9875\u9762\u4E0A\u4E0B\u6587\uFF08\u5305\u62EC\u9875\u9762 URL\u3001\u6807\u9898\u4EE5\u53CA\u5728\u9875\u9762\u4E0A\u9009\u4E2D\u7684 DOM \u8282\u70B9\u4FE1\u606F\uFF09\u3002
|
|
160
|
+
|
|
161
|
+
\u5904\u7406\u8FD9\u4E9B\u4E0A\u4E0B\u6587\u65F6\uFF0C\u8BF7\u9075\u5FAA\u4EE5\u4E0B\u89C4\u5219\uFF1A
|
|
162
|
+
1. **\u7406\u89E3\u4E0A\u4E0B\u6587**\uFF1A\u5F53\u770B\u5230\u201C\u6211\u73B0\u5728\u6B63\u5728\u6D4F\u89C8\u9879\u76EE\u4E2D\u7684\u8FD9\u4E2A\u9875\u9762\u201D\u6216\u201C\u9009\u4E2D\u8282\u70B9\u201D\u7B49\u4FE1\u606F\u65F6\uFF0C\u8BF7\u5C06\u5176\u4F5C\u4E3A\u7528\u6237\u8BF7\u6C42\u7684\u80CC\u666F\u3002
|
|
163
|
+
2. **\u5229\u7528\u6587\u4EF6\u8DEF\u5F84**\uFF1A\u5982\u679C\u63D0\u4F9B\u7684\u8282\u70B9\u4FE1\u606F\u4E2D\u5305\u542B \`\u6587\u4EF6\u4F4D\u7F6E\`\uFF0C\u8FD9\u901A\u5E38\u5BF9\u5E94\u4E8E\u9879\u76EE\u4E2D\u7684\u6E90\u4EE3\u7801\u6587\u4EF6\u3002\u4F60\u53EF\u4EE5\u76F4\u63A5\u5206\u6790\u6216\u5EFA\u8BAE\u4FEE\u6539\u8FD9\u4E9B\u6587\u4EF6\u3002
|
|
164
|
+
3. **\u7CBE\u51C6\u5B9A\u4F4D**\uFF1A\u7ED3\u5408\u8282\u70B9\u7684 \`\u8282\u70B9\u6587\u672C\` \u548C \`\u6587\u4EF6\u4F4D\u7F6E\`\uFF0C\u5E2E\u52A9\u7528\u6237\u5FEB\u901F\u5B9A\u4F4D\u95EE\u9898\u6216\u5B9E\u73B0\u529F\u80FD\u3002
|
|
165
|
+
4. **\u76F4\u63A5\u7ED9\u51FA\u65B9\u6848**\uFF1A\u9488\u5BF9\u7528\u6237\u7684\u5B9E\u9645\u8BF7\u6C42\uFF0C\u76F4\u63A5\u7ED9\u51FA\u6E05\u6670\u3001\u53EF\u6267\u884C\u7684\u4EE3\u7801\u4FEE\u6539\u5EFA\u8BAE\u6216\u89E3\u91CA\uFF0C\u907F\u514D\u4E0D\u5FC5\u8981\u7684\u5E9F\u8BDD\u3002
|
|
166
|
+
`.trim();
|
|
167
|
+
output.system.push(systemPrompt);
|
|
168
|
+
}),
|
|
169
|
+
"experimental.chat.messages.transform": (_input, output) => __async(null, null, function* () {
|
|
170
|
+
var _a, _b;
|
|
171
|
+
log.debug("Message transform hook called");
|
|
172
|
+
const context = yield getPageContext();
|
|
173
|
+
log.debug("Context data", {
|
|
174
|
+
hasUrl: !!(context == null ? void 0 : context.url),
|
|
175
|
+
hasElements: !!((_a = context == null ? void 0 : context.selectedElements) == null ? void 0 : _a.length)
|
|
176
|
+
});
|
|
177
|
+
if (!(context == null ? void 0 : context.url)) return;
|
|
178
|
+
const lastUserMsg = [...output.messages].reverse().find((m) => m.info.role === "user");
|
|
179
|
+
if (!lastUserMsg) return;
|
|
180
|
+
const textPart = lastUserMsg.parts.find((p) => p.type === "text");
|
|
181
|
+
if (!textPart || !("text" in textPart)) return;
|
|
182
|
+
if (textPart.text.includes(CONTEXT_MARKER)) return;
|
|
183
|
+
const prefix = buildContextPrefix(context);
|
|
184
|
+
textPart.text = prefix + textPart.text;
|
|
185
|
+
if ((_b = context.selectedElements) == null ? void 0 : _b.length) {
|
|
186
|
+
log.debug("Selected elements found, clearing...");
|
|
187
|
+
yield clearSelectedElements();
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
var page_context_default = PageContextPlugin;
|
|
193
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
194
|
+
0 && (module.exports = {
|
|
195
|
+
PageContextPlugin
|
|
196
|
+
});
|
package/lib/web.d.ts
ADDED
package/lib/web.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
11
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
12
|
+
var __spreadValues = (a, b) => {
|
|
13
|
+
for (var prop in b || (b = {}))
|
|
14
|
+
if (__hasOwnProp.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
if (__getOwnPropSymbols)
|
|
17
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
18
|
+
if (__propIsEnum.call(b, prop))
|
|
19
|
+
__defNormalProp(a, prop, b[prop]);
|
|
20
|
+
}
|
|
21
|
+
return a;
|
|
22
|
+
};
|
|
23
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
24
|
+
var __export = (target, all) => {
|
|
25
|
+
for (var name in all)
|
|
26
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
27
|
+
};
|
|
28
|
+
var __copyProps = (to, from, except, desc) => {
|
|
29
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
30
|
+
for (let key of __getOwnPropNames(from))
|
|
31
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
32
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
33
|
+
}
|
|
34
|
+
return to;
|
|
35
|
+
};
|
|
36
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
37
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
38
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
39
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
40
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
41
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
42
|
+
mod
|
|
43
|
+
));
|
|
44
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
45
|
+
var web_exports = {};
|
|
46
|
+
__export(web_exports, {
|
|
47
|
+
prepareOpenCodeRuntime: () => prepareOpenCodeRuntime,
|
|
48
|
+
startOpenCodeWeb: () => startOpenCodeWeb
|
|
49
|
+
});
|
|
50
|
+
module.exports = __toCommonJS(web_exports);
|
|
51
|
+
var import_execa = require("execa");
|
|
52
|
+
var import_fs = __toESM(require("fs"));
|
|
53
|
+
var import_module = require("module");
|
|
54
|
+
var import_path = __toESM(require("path"));
|
|
55
|
+
var import_shared = require("@vite-plugin-opencode-assistant/shared");
|
|
56
|
+
const require2 = (0, import_module.createRequire)(import_path.default.join(process.cwd(), "package.json"));
|
|
57
|
+
const packageDir = resolvePackageDir();
|
|
58
|
+
const log = (0, import_shared.createLogger)("OpenCodeWeb");
|
|
59
|
+
function prepareOpenCodeRuntime(cwd) {
|
|
60
|
+
const cacheDir = import_path.default.join(cwd, "node_modules", ".cache", "opencode");
|
|
61
|
+
const pluginsDir = import_path.default.join(cacheDir, "plugins");
|
|
62
|
+
log.debug("Setting up OpenCode runtime", { cacheDir, pluginsDir });
|
|
63
|
+
if (!import_fs.default.existsSync(pluginsDir)) {
|
|
64
|
+
import_fs.default.mkdirSync(pluginsDir, { recursive: true });
|
|
65
|
+
}
|
|
66
|
+
const pluginSourcePath = resolvePluginSourcePath();
|
|
67
|
+
const pluginTargetPath = import_path.default.join(pluginsDir, "page-context.js");
|
|
68
|
+
if (!import_fs.default.existsSync(pluginSourcePath)) {
|
|
69
|
+
throw new Error(`Page context plugin not found: ${pluginSourcePath}`);
|
|
70
|
+
}
|
|
71
|
+
import_fs.default.copyFileSync(pluginSourcePath, pluginTargetPath);
|
|
72
|
+
const mcpConfigPath = import_path.default.join(cacheDir, "opencode.json");
|
|
73
|
+
import_fs.default.writeFileSync(
|
|
74
|
+
mcpConfigPath,
|
|
75
|
+
JSON.stringify(
|
|
76
|
+
{
|
|
77
|
+
mcp: {
|
|
78
|
+
"chrome-devtools": {
|
|
79
|
+
type: "local",
|
|
80
|
+
command: ["npx", "-y", "chrome-devtools-mcp@latest", "--autoConnect"],
|
|
81
|
+
enabled: true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
null,
|
|
86
|
+
2
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
log.debug("OpenCode runtime ready", {
|
|
90
|
+
cacheDir,
|
|
91
|
+
pluginTargetPath,
|
|
92
|
+
mcpConfigPath
|
|
93
|
+
});
|
|
94
|
+
return cacheDir;
|
|
95
|
+
}
|
|
96
|
+
function startOpenCodeWeb(options) {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
const { port, hostname, cwd, configDir, corsOrigins, contextApiUrl } = options;
|
|
99
|
+
const stateDir = createStateDirectory(cwd);
|
|
100
|
+
const pluginPath = import_path.default.join(stateDir, "plugins", "page-context.js");
|
|
101
|
+
log.debug("Building process environment", {
|
|
102
|
+
stateDir,
|
|
103
|
+
configDir,
|
|
104
|
+
contextApiUrl,
|
|
105
|
+
pluginPath
|
|
106
|
+
});
|
|
107
|
+
const env = buildProcessEnv(stateDir, configDir, contextApiUrl, pluginPath);
|
|
108
|
+
const args = ["serve", "--port", String(port), "--hostname", hostname];
|
|
109
|
+
if (corsOrigins && corsOrigins.length > 0) {
|
|
110
|
+
corsOrigins.forEach((origin) => {
|
|
111
|
+
args.push("--cors", origin);
|
|
112
|
+
});
|
|
113
|
+
log.debug("CORS origins added", { origins: corsOrigins });
|
|
114
|
+
}
|
|
115
|
+
log.debug("Spawning OpenCode process", {
|
|
116
|
+
command: "opencode",
|
|
117
|
+
args: args.join(" "),
|
|
118
|
+
cwd
|
|
119
|
+
});
|
|
120
|
+
const proc = (0, import_execa.execa)("opencode", args, {
|
|
121
|
+
cwd,
|
|
122
|
+
env,
|
|
123
|
+
reject: false,
|
|
124
|
+
cleanup: true
|
|
125
|
+
});
|
|
126
|
+
(_a = proc.stdout) == null ? void 0 : _a.on("data", (data) => {
|
|
127
|
+
const output = data.toString().trim();
|
|
128
|
+
if (output) {
|
|
129
|
+
log.debug("[OpenCode stdout]", { output });
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
(_b = proc.stderr) == null ? void 0 : _b.on("data", (data) => {
|
|
133
|
+
const output = data.toString().trim();
|
|
134
|
+
if (output) {
|
|
135
|
+
log.warn("[OpenCode stderr]", { output });
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
return proc;
|
|
139
|
+
}
|
|
140
|
+
function createStateDirectory(cwd) {
|
|
141
|
+
const stateDir = import_path.default.join(cwd, "node_modules", ".cache", "opencode");
|
|
142
|
+
if (!import_fs.default.existsSync(stateDir)) {
|
|
143
|
+
import_fs.default.mkdirSync(stateDir, { recursive: true });
|
|
144
|
+
log.debug("Created state directory", { stateDir });
|
|
145
|
+
}
|
|
146
|
+
return stateDir;
|
|
147
|
+
}
|
|
148
|
+
function resolvePackageDir() {
|
|
149
|
+
const entryPath = require2.resolve("@vite-plugin-opencode-assistant/opencode");
|
|
150
|
+
return import_path.default.resolve(import_path.default.dirname(entryPath), "..");
|
|
151
|
+
}
|
|
152
|
+
function resolvePluginSourcePath() {
|
|
153
|
+
const candidatePaths = [
|
|
154
|
+
import_path.default.join(packageDir, "es", "plugins", "page-context.js"),
|
|
155
|
+
import_path.default.join(packageDir, "lib", "plugins", "page-context.js")
|
|
156
|
+
];
|
|
157
|
+
for (const candidatePath of candidatePaths) {
|
|
158
|
+
if (import_fs.default.existsSync(candidatePath)) {
|
|
159
|
+
return candidatePath;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return candidatePaths[0];
|
|
163
|
+
}
|
|
164
|
+
function buildProcessEnv(stateDir, configDir, contextApiUrl, pluginPath) {
|
|
165
|
+
const env = __spreadProps(__spreadValues({}, Object.fromEntries(
|
|
166
|
+
Object.entries(process.env).filter(([, v]) => v !== void 0)
|
|
167
|
+
)), {
|
|
168
|
+
XDG_STATE_HOME: stateDir
|
|
169
|
+
});
|
|
170
|
+
if (configDir) {
|
|
171
|
+
env.OPENCODE_CONFIG_DIR = configDir;
|
|
172
|
+
log.debug("Set OPENCODE_CONFIG_DIR", { configDir });
|
|
173
|
+
}
|
|
174
|
+
if (contextApiUrl) {
|
|
175
|
+
env.OPENCODE_CONTEXT_API_URL = contextApiUrl;
|
|
176
|
+
log.debug("Set OPENCODE_CONTEXT_API_URL", { contextApiUrl });
|
|
177
|
+
}
|
|
178
|
+
if (pluginPath) {
|
|
179
|
+
env.OPENCODE_PLUGINS = pluginPath;
|
|
180
|
+
log.debug("Set OPENCODE_PLUGINS", { pluginPath });
|
|
181
|
+
}
|
|
182
|
+
return env;
|
|
183
|
+
}
|
|
184
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
185
|
+
0 && (module.exports = {
|
|
186
|
+
prepareOpenCodeRuntime,
|
|
187
|
+
startOpenCodeWeb
|
|
188
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vite-plugin-opencode-assistant/opencode",
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"module": "es/index.js",
|
|
7
|
+
"types": "es/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"es",
|
|
10
|
+
"lib"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./es/index.d.ts",
|
|
15
|
+
"import": "./es/index.js",
|
|
16
|
+
"require": "./lib/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public",
|
|
21
|
+
"registry": "https://registry.npmjs.org/"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"execa": "^9.6.1",
|
|
25
|
+
"@vite-plugin-opencode-assistant/shared": "1.0.7"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@opencode-ai/plugin": "^1.3.15",
|
|
29
|
+
"vite": "^5.4.21"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "pagoda-cli build && vite build -c vite.plugin.config.ts",
|
|
33
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
34
|
+
}
|
|
35
|
+
}
|