vite-plugin-opencode-assistant 1.0.0

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.
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @fileoverview OpenCode 页面上下文插件
3
+ * @description 用于将页面上下文信息注入到 AI 对话中
4
+ */
5
+ /** 最大文本长度 */
6
+ const MAX_TEXT_LENGTH = 10000;
7
+ /** 上下文标记 */
8
+ const CONTEXT_MARKER = '__OPENCODE_CONTEXT__';
9
+ /**
10
+ * OpenCode 页面上下文插件
11
+ * @returns 插件钩子
12
+ * @example
13
+ * ```ts
14
+ * // 在 opencode 配置中使用
15
+ * import { PageContextPlugin } from './plugins/page-context'
16
+ *
17
+ * export default {
18
+ * plugins: [PageContextPlugin]
19
+ * }
20
+ * ```
21
+ */
22
+ export const PageContextPlugin = async () => {
23
+ const contextApiUrl = process.env.OPENCODE_CONTEXT_API_URL;
24
+ if (!contextApiUrl) {
25
+ console.warn('OPENCODE_CONTEXT_API_URL is not set, page context plugin will not work');
26
+ return {};
27
+ }
28
+ const apiUrl = contextApiUrl;
29
+ /**
30
+ * 获取页面上下文
31
+ * @returns 页面上下文数据,获取失败时返回 null
32
+ */
33
+ async function getPageContext() {
34
+ try {
35
+ const response = await fetch(apiUrl);
36
+ const data = await response.json();
37
+ return {
38
+ url: data.url || "",
39
+ title: data.title || "",
40
+ selectedElements: data.selectedElements
41
+ };
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ /**
48
+ * 清除选中的元素
49
+ */
50
+ async function clearSelectedElements() {
51
+ try {
52
+ await fetch(apiUrl, { method: 'DELETE' });
53
+ }
54
+ catch {
55
+ // 忽略错误
56
+ }
57
+ }
58
+ /**
59
+ * 格式化选中元素信息
60
+ * @param element - 选中的元素
61
+ * @returns 格式化后的元素信息字符串
62
+ */
63
+ function formatSelectedElement(element) {
64
+ const parts = [];
65
+ if (element.filePath) {
66
+ let location = `文件: ${element.filePath}`;
67
+ if (element.line) {
68
+ location += `:${element.line}`;
69
+ if (element.column) {
70
+ location += `:${element.column}`;
71
+ }
72
+ }
73
+ parts.push(location);
74
+ }
75
+ if (element.innerText?.trim()) {
76
+ const text = element.innerText.trim().substring(0, MAX_TEXT_LENGTH);
77
+ const suffix = element.innerText.length > MAX_TEXT_LENGTH ? '...' : '';
78
+ parts.push(`节点文本: "${text}${suffix}"`);
79
+ }
80
+ return parts.join('\n') + '\n';
81
+ }
82
+ /**
83
+ * 构建上下文前缀
84
+ * @param context - 页面上下文
85
+ * @returns 上下文前缀字符串
86
+ */
87
+ function buildContextPrefix(context) {
88
+ let prefix = `我现在正在浏览这个页面:${context.url}\n\n`;
89
+ if (context.selectedElements?.length) {
90
+ prefix += `帮我找到以下节点,一定要精确到具体的节点:\n\n`;
91
+ context.selectedElements.forEach((element) => {
92
+ prefix += formatSelectedElement(element) + "\n";
93
+ });
94
+ }
95
+ prefix += `请根据以上信息,完成以下任务:\n`;
96
+ prefix += `\n`;
97
+ return prefix;
98
+ }
99
+ return {
100
+ "experimental.chat.messages.transform": async (_input, output) => {
101
+ const context = await getPageContext();
102
+ if (!context?.url)
103
+ return;
104
+ const lastUserMsg = [...output.messages].reverse().find(m => m.info.role === "user");
105
+ if (!lastUserMsg)
106
+ return;
107
+ const textPart = lastUserMsg.parts.find(p => p.type === "text");
108
+ if (!textPart || !("text" in textPart))
109
+ return;
110
+ if (textPart.text.includes(CONTEXT_MARKER))
111
+ return;
112
+ const prefix = buildContextPrefix(context);
113
+ textPart.text = prefix + textPart.text;
114
+ if (context.selectedElements?.length) {
115
+ await clearSelectedElements();
116
+ }
117
+ }
118
+ };
119
+ };
120
+ export default PageContextPlugin;
121
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFnZS1jb250ZXh0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbnMvcGFnZS1jb250ZXh0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUlILGFBQWE7QUFDYixNQUFNLGVBQWUsR0FBRyxLQUFLLENBQUE7QUFFN0IsWUFBWTtBQUNaLE1BQU0sY0FBYyxHQUFHLHNCQUFzQixDQUFBO0FBNEI3Qzs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBVyxLQUFLLElBQW9CLEVBQUU7SUFDbEUsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyx3QkFBd0IsQ0FBQTtJQUUxRCxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDbkIsT0FBTyxDQUFDLElBQUksQ0FBQyx3RUFBd0UsQ0FBQyxDQUFBO1FBQ3RGLE9BQU8sRUFBRSxDQUFBO0lBQ1gsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLGFBQXVCLENBQUE7SUFFdEM7OztPQUdHO0lBQ0gsS0FBSyxVQUFVLGNBQWM7UUFDM0IsSUFBSSxDQUFDO1lBQ0gsTUFBTSxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUE7WUFDcEMsTUFBTSxJQUFJLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFxQixDQUFBO1lBQ3JELE9BQU87Z0JBQ0wsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHLElBQUksRUFBRTtnQkFDbkIsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLElBQUksRUFBRTtnQkFDdkIsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLGdCQUFnQjthQUN4QyxDQUFBO1FBQ0gsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLE9BQU8sSUFBSSxDQUFBO1FBQ2IsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssVUFBVSxxQkFBcUI7UUFDbEMsSUFBSSxDQUFDO1lBQ0gsTUFBTSxLQUFL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SSxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUM7Z0JBQUUsT0FBTTtZQUVsRCxNQUFNLE1BQU0sR0FBRyxrQkFBa0IsQ0FBQyxPQUFPLENBQUMsQ0FBQTtZQUMxQyxRQUFRLENBQUMsSUFBSSxHQUFHLE1BQU0sR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFBO1lBRXRDLElBQUksT0FBTyxDQUFDLGdCQUFnQixFQUFFLE1BQU0sRUFBRSxDQUFDO2dCQUNyQyxNQUFNLHFCQUFxQixFQUFFLENBQUE7WUFDL0IsQ0FBQztRQUNILENBQUM7S0FDRixDQUFBO0FBQ0gsQ0FBQyxDQUFBO0FBRUQsZUFBZSxpQkFBaUIsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGZpbGVvdmVydmlldyBPcGVuQ29kZSDpobXpnaLkuIrkuIvmlofmj5Lku7ZcbiAqIEBkZXNjcmlwdGlvbiDnlKjkuo7lsIbpobXpnaLkuIrkuIvmlofkv6Hmga/ms6jlhaXliLAgQUkg5a+56K+d5LitXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBQbHVnaW4sIEhvb2tzIH0gZnJvbSBcIkBvcGVuY29kZS1haS9wbHVnaW5cIlxuXG4vKiog5pyA5aSn5paH5pys6ZW/5bqmICovXG5jb25zdCBNQVhfVEVYVF9MRU5HVEggPSAxMDAwMFxuXG4vKiog5LiK5LiL5paH5qCH6K6wICovXG5jb25zdCBDT05URVhUX01BUktFUiA9ICdfX09QRU5DT0RFX0NPTlRFWFRfXydcblxuLyoqXG4gKiDpgInkuK3nmoTlhYPntKDkv6Hmga9cbiAqL1xuaW50ZXJmYWNlIFNlbGVjdGVkRWxlbWVudCB7XG4gIC8qKiDmlofku7bot6/lvoQgKi9cbiAgZmlsZVBhdGg6IHN0cmluZyB8IG51bGxcbiAgLyoqIOihjOWPtyAqL1xuICBsaW5lOiBudW1iZXIgfCBudWxsXG4gIC8qKiDliJflj7cgKi9cbiAgY29sdW1uOiBudW1iZXIgfCBudWxsXG4gIC8qKiDlhYPntKDlhoXpg6jmlofmnKwgKi9cbiAgaW5uZXJUZXh0OiBzdHJpbmdcbn1cblxuLyoqXG4gKiDpobXpnaLkuIrkuIvmlofmlbDmja5cbiAqL1xuaW50ZXJmYWNlIFBhZ2VDb250ZXh0RGF0YSB7XG4gIC8qKiDlvZPliY3pobXpnaIgVVJMICovXG4gIHVybDogc3RyaW5nXG4gIC8qKiDlvZPliY3pobXpnaLmoIfpopggKi9cbiAgdGl0bGU6IHN0cmluZ1xuICAvKiog6YCJ5Lit55qE5YWD57Sg5YiX6KGoICovXG4gIHNlbGVjdGVkRWxlbWVudHM/OiBTZWxlY3RlZEVsZW1lbnRbXVxufVxuXG4vKipcbiAqIE9wZW5Db2RlIOmhtemdouS4iuS4i+aWh+aPkuS7tlxuICogQHJldHVybnMg5o+S5Lu26ZKp5a2QXG4gKiBAZXhhbXBsZVxuICogYGBgdHNcbiAqIC8vIOWcqCBvcGVuY29kZSDphY3nva7kuK3kvb/nlKhcbiAqIGltcG9ydCB7IFBhZ2VDb250ZXh0UGx1Z2luIH0gZnJvbSAnLi9wbHVnaW5zL3BhZ2UtY29udGV4dCdcbiAqXG4gKiBleHBvcnQgZGVmYXVsdCB7XG4gKiAgIHBsdWdpbnM6IFtQYWdlQ29udGV4dFBsdWdpbl1cbiAqIH1cbiAqIGBgYFxuICovXG5leHBvcnQgY29uc3QgUGFnZUNvbnRleHRQbHVnaW46IFBsdWdpbiA9IGFzeW5jICgpOiBQcm9taXNlPEhvb2tzPiA9PiB7XG4gIGNvbnN0IGNvbnRleHRBcGlVcmwgPSBwcm9jZXNzLmVudi5PUEVOQ09ERV9DT05URVhUX0FQSV9VUkxcblxuICBpZiAoIWNvbnRleHRBcGlVcmwpIHtcbiAgICBjb25zb2xlLndhcm4oJ09QRU5DT0RFX0NPTlRFWFRfQVBJX1VSTCBpcyBub3Qgc2V0LCBwYWdlIGNvbnRleHQgcGx1Z2luIHdpbGwgbm90IHdvcmsnKVxuICAgIHJldHVybiB7fVxuICB9XG5cbiAgY29uc3QgYXBpVXJsID0gY29udGV4dEFwaVVybCBhcyBzdHJpbmdcblxuICAvKipcbiAgICog6I635Y+W6aG16Z2i5LiK5LiL5paHXG4gICAqIEByZXR1cm5zIOmhtemdouS4iuS4i+aWh+aVsOaNru+8jOiOt+WPluWksei0peaXtui/lOWbniBudWxsXG4gICAqL1xuICBhc3luYyBmdW5jdGlvbiBnZXRQYWdlQ29udGV4dCgpOiBQcm9taXNlPFBhZ2VDb250ZXh0RGF0YSB8IG51bGw+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChhcGlVcmwpXG4gICAgICBjb25zdCBkYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpIGFzIFBhZ2VDb250ZXh0RGF0YVxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdXJsOiBkYXRhLnVybCB8fCBcIlwiLFxuICAgICAgICB0aXRsZTogZGF0YS50aXRsZSB8fCBcIlwiLFxuICAgICAgICBzZWxlY3RlZEVsZW1lbnRzOiBkYXRhLnNlbGVjdGVkRWxlbWVudHNcbiAgICAgIH1cbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiBudWxsXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIOa4hemZpOmAieS4reeahOWFg+e0oFxuICAgKi9cbiAgYXN5bmMgZnVuY3Rpb24gY2xlYXJTZWxlY3RlZEVsZW1lbnRzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBmZXRjaChhcGlVcmwsIHsgbWV0aG9kOiAnREVMRVRFJyB9KVxuICAgIH0gY2F0Y2gge1xuICAgICAgLy8g5b+955Wl6ZSZ6K+vXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIOagvOW8j+WMlumAieS4reWFg+e0oOS/oeaBr1xuICAgKiBAcGFyYW0gZWxlbWVudCAtIOmAieS4reeahOWFg+e0oFxuICAgKiBAcmV0dXJucyDmoLzlvI/ljJblkI7nmoTlhYPntKDkv6Hmga/lrZfnrKbkuLJcbiAgICovXG4gIGZ1bmN0aW9uIGZvcm1hdFNlbGVjdGVkRWxlbWVudChlbGVtZW50OiBTZWxlY3RlZEVsZW1lbnQpOiBzdHJpbmcge1xuICAgIGNvbnN0IHBhcnRzOiBzdHJpbmdbXSA9IFtdXG5cbiAgICBpZiAoZWxlbWVudC5maWxlUGF0aCkge1xuICAgICAgbGV0IGxvY2F0aW9uID0gYOaWh+S7tjogJHtlbGVtZW50LmZpbGVQYXRofWBcbiAgICAgIGlmIChlbGVtZW50LmxpbmUpIHtcbiAgICAgICAgbG9jYXRpb24gKz0gYDoke2VsZW1lbnQubGluZX1gXG4gICAgICAgIGlmIChlbGVtZW50LmNvbHVtbikge1xuICAgICAgICAgIGxvY2F0aW9uICs9IGA6JHtlbGVtZW50LmNvbHVtbn1gXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHBhcnRzLnB1c2gobG9jYXRpb24pXG4gICAgfVxuXG4gICAgaWYgKGVsZW1lbnQuaW5uZXJUZXh0Py50cmltKCkpIHtcbiAgICAgIGNvbnN0IHRleHQgPSBlbGVtZW50LmlubmVyVGV4dC50cmltKCkuc3Vic3RyaW5nKDAsIE1BWF9URVhUX0xFTkdUSClcbiAgICAgIGNvbnN0IHN1ZmZpeCA9IGVsZW1lbnQuaW5uZXJUZXh0Lmxlbmd0aCA+IE1BWF9URVhUX0xFTkdUSCA/ICcuLi4nIDogJydcbiAgICAgIHBhcnRzLnB1c2goYOiKgueCueaWh+acrDogXCIke3RleHR9JHtzdWZmaXh9XCJgKVxuICAgIH1cblxuICAgIHJldHVybiBwYXJ0cy5qb2luKCdcXG4nKSArICdcXG4nXG4gIH1cblxuICAvKipcbiAgICog5p6E5bu65LiK5LiL5paH5YmN57yAXG4gICAqIEBwYXJhbSBjb250ZXh0IC0g6aG16Z2i5LiK5LiL5paHXG4gICAqIEByZXR1cm5zIOS4iuS4i+aWh+WJjee8gOWtl+espuS4slxuICAgKi9cbiAgZnVuY3Rpb24gYnVpbGRDb250ZXh0UHJlZml4KGNvbnRleHQ6IFBhZ2VDb250ZXh0RGF0YSk6IHN0cmluZyB7XG4gICAgbGV0IHByZWZpeCA9IGDmiJHnjrDlnKjmraPlnKjmtY/op4jov5nkuKrpobXpnaLvvJoke2NvbnRleHQudXJsfVxcblxcbmBcblxuICAgIGlmIChjb250ZXh0LnNlbGVjdGVkRWxlbWVudHM/Lmxlbmd0aCkge1xuICAgICAgcHJlZml4ICs9IGDluK7miJHmib7liLDku6XkuIvoioLngrnvvIzkuIDlrpropoHnsr7noa7liLDlhbfkvZPnmoToioLngrnvvJpcXG5cXG5gXG4gICAgICBjb250ZXh0LnNlbGVjdGVkRWxlbWVudHMuZm9yRWFjaCgoZWxlbWVudCkgPT4ge1xuICAgICAgICBwcmVmaXggKz0gZm9ybWF0U2VsZWN0ZWRFbGVtZW50KGVsZW1lbnQpICsgXCJcXG5cIlxuICAgICAgfSlcbiAgICB9XG5cbiAgICBwcmVmaXggKz0gYOivt+agueaNruS7peS4iuS/oeaBr++8jOWujOaIkOS7peS4i+S7u+WKoe+8mlxcbmBcblxuICAgIHByZWZpeCArPSBgXFxuYFxuICAgIHJldHVybiBwcmVmaXhcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgXCJleHBlcmltZW50YWwuY2hhdC5tZXNzYWdlcy50cmFuc2Zvcm1cIjogYXN5bmMgKF9pbnB1dCwgb3V0cHV0KSA9PiB7XG4gICAgICBjb25zdCBjb250ZXh0ID0gYXdhaXQgZ2V0UGFnZUNvbnRleHQoKVxuICAgICAgXG4gICAgICBpZiAoIWNvbnRleHQ/LnVybCkgcmV0dXJuXG5cbiAgICAgIGNvbnN0IGxhc3RVc2VyTXNnID0gWy4uLm91dHB1dC5tZXNzYWdlc10ucmV2ZXJzZSgpLmZpbmQobSA9PiBtLmluZm8ucm9sZSA9PT0gXCJ1c2VyXCIpXG4gICAgICBpZiAoIWxhc3RVc2VyTXNnKSByZXR1cm5cblxuICAgICAgY29uc3QgdGV4dFBhcnQgPSBsYXN0VXNlck1zZy5wYXJ0cy5maW5kKHAgPT4gcC50eXBlID09PSBcInRleHRcIilcbiAgICAgIGlmICghdGV4dFBhcnQgfHwgIShcInRleHRcIiBpbiB0ZXh0UGFydCkpIHJldHVyblxuICAgICAgXG4gICAgICBpZiAodGV4dFBhcnQudGV4dC5pbmNsdWRlcyhDT05URVhUX01BUktFUikpIHJldHVyblxuXG4gICAgICBjb25zdCBwcmVmaXggPSBidWlsZENvbnRleHRQcmVmaXgoY29udGV4dClcbiAgICAgIHRleHRQYXJ0LnRleHQgPSBwcmVmaXggKyB0ZXh0UGFydC50ZXh0XG5cbiAgICAgIGlmIChjb250ZXh0LnNlbGVjdGVkRWxlbWVudHM/Lmxlbmd0aCkge1xuICAgICAgICBhd2FpdCBjbGVhclNlbGVjdGVkRWxlbWVudHMoKVxuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZGVmYXVsdCBQYWdlQ29udGV4dFBsdWdpblxuIl19
@@ -0,0 +1,130 @@
1
+ /**
2
+ * OpenCode Vite 插件配置选项
3
+ */
4
+ export interface OpenCodeOptions {
5
+ /** 是否启用插件,默认 true */
6
+ enabled?: boolean;
7
+ /** @deprecated 使用 webPort 代替 */
8
+ serverPort?: number;
9
+ /** Web 服务端口,默认 4097 */
10
+ webPort?: number;
11
+ /** 服务主机名,默认 '127.0.0.1' */
12
+ hostname?: string;
13
+ /** 挂件位置,默认 'bottom-right' */
14
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
15
+ /** 主题模式,默认 'auto' */
16
+ theme?: 'light' | 'dark' | 'auto';
17
+ /** 是否自动打开面板,默认 false */
18
+ open?: boolean;
19
+ /** 是否自动重载,默认 true */
20
+ autoReload?: boolean;
21
+ /** 是否输出详细日志,默认 false */
22
+ verbose?: boolean;
23
+ /** 是否懒加载服务,默认 false */
24
+ lazy?: boolean;
25
+ /** 快捷键配置,默认 'ctrl+k' */
26
+ hotkey?: string;
27
+ }
28
+ /**
29
+ * OpenCode Web 服务启动选项
30
+ */
31
+ export interface WebOptions {
32
+ /** 服务端口 */
33
+ port: number;
34
+ /** 服务主机名 */
35
+ hostname: string;
36
+ /** 服务器 URL */
37
+ serverUrl: string;
38
+ /** 工作目录 */
39
+ cwd: string;
40
+ /** 配置目录路径 */
41
+ configDir?: string;
42
+ /** CORS 允许的源 */
43
+ corsOrigins?: string[];
44
+ /** 上下文 API URL */
45
+ contextApiUrl?: string;
46
+ }
47
+ /**
48
+ * 挂件注入配置选项
49
+ */
50
+ export interface WidgetOptions {
51
+ /** Web 服务 URL */
52
+ webUrl: string;
53
+ /** 服务器 URL */
54
+ serverUrl: string;
55
+ /** 挂件位置 */
56
+ position: string;
57
+ /** 主题模式 */
58
+ theme: string;
59
+ /** 是否自动打开 */
60
+ open: boolean;
61
+ /** 是否自动重载 */
62
+ autoReload: boolean;
63
+ /** 工作目录 */
64
+ cwd: string;
65
+ /** 会话 URL */
66
+ sessionUrl?: string;
67
+ /** 是否懒加载 */
68
+ lazy?: boolean;
69
+ /** 快捷键配置 */
70
+ hotkey?: string;
71
+ }
72
+ /**
73
+ * OpenCode 会话信息
74
+ */
75
+ export interface SessionInfo {
76
+ /** 会话 ID */
77
+ id: string;
78
+ /** 会话标识符 */
79
+ slug: string;
80
+ /** 项目 ID */
81
+ projectID: string;
82
+ /** 项目目录 */
83
+ directory: string;
84
+ /** 会话标题 */
85
+ title: string;
86
+ /** 版本号 */
87
+ version: string;
88
+ /** 代码变更统计 */
89
+ summary: {
90
+ /** 新增行数 */
91
+ additions: number;
92
+ /** 删除行数 */
93
+ deletions: number;
94
+ /** 修改文件数 */
95
+ files: number;
96
+ };
97
+ /** 时间信息 */
98
+ time: {
99
+ /** 创建时间戳 */
100
+ created: number;
101
+ /** 更新时间戳 */
102
+ updated: number;
103
+ };
104
+ }
105
+ /**
106
+ * 选中的元素信息
107
+ */
108
+ export interface SelectedElement {
109
+ /** 文件路径 */
110
+ filePath: string | null;
111
+ /** 行号 */
112
+ line: number | null;
113
+ /** 列号 */
114
+ column: number | null;
115
+ /** 元素内部文本 */
116
+ innerText: string;
117
+ /** 元素描述(标签名+选择器) */
118
+ description?: string;
119
+ }
120
+ /**
121
+ * 页面上下文数据
122
+ */
123
+ export interface PageContext {
124
+ /** 当前页面 URL */
125
+ url: string;
126
+ /** 当前页面标题 */
127
+ title: string;
128
+ /** 选中的元素列表 */
129
+ selectedElements?: SelectedElement[];
130
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogT3BlbkNvZGUgVml0ZSDmj5Lku7bphY3nva7pgInpoblcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBPcGVuQ29kZU9wdGlvbnMge1xuICAvKiog5piv5ZCm5ZCv55So5o+S5Lu277yM6buY6K6kIHRydWUgKi9cbiAgZW5hYmxlZD86IGJvb2xlYW5cbiAgLyoqIEBkZXByZWNhdGVkIOS9v+eUqCB3ZWJQb3J0IOS7o+abvyAqL1xuICBzZXJ2ZXJQb3J0PzogbnVtYmVyXG4gIC8qKiBXZWIg5pyN5Yqh56uv5Y+j77yM6buY6K6kIDQwOTcgKi9cbiAgd2ViUG9ydD86IG51bWJlclxuICAvKiog5pyN5Yqh5Li75py65ZCN77yM6buY6K6kICcxMjcuMC4wLjEnICovXG4gIGhvc3RuYW1lPzogc3RyaW5nXG4gIC8qKiDmjILku7bkvY3nva7vvIzpu5jorqQgJ2JvdHRvbS1yaWdodCcgKi9cbiAgcG9zaXRpb24/OiAnYm90dG9tLXJpZ2h0JyB8ICdib3R0b20tbGVmdCcgfCAndG9wLXJpZ2h0JyB8ICd0b3AtbGVmdCdcbiAgLyoqIOS4u+mimOaooeW8j++8jOm7mOiupCAnYXV0bycgKi9cbiAgdGhlbWU/OiAnbGlnaHQnIHwgJ2RhcmsnIHwgJ2F1dG8nXG4gIC8qKiDmmK/lkKboh6rliqjmiZPlvIDpnaLmnb/vvIzpu5jorqQgZmFsc2UgKi9cbiAgb3Blbj86IGJvb2xlYW5cbiAgLyoqIOaYr+WQpuiHquWKqOmHjei9ve+8jOm7mOiupCB0cnVlICovXG4gIGF1dG9SZWxvYWQ/OiBib29sZWFuXG4gIC8qKiDmmK/lkKbovpPlh7ror6bnu4bml6Xlv5fvvIzpu5jorqQgZmFsc2UgKi9cbiAgdmVyYm9zZT86IGJvb2xlYW5cbiAgLyoqIOaYr+WQpuaHkuWKoOi9veacjeWKoe+8jOm7mOiupCBmYWxzZSAqL1xuICBsYXp5PzogYm9vbGVhblxuICAvKiog5b+r5o236ZSu6YWN572u77yM6buY6K6kICdjdHJsK2snICovXG4gIGhvdGtleT86IHN0cmluZ1xufVxuXG4vKipcbiAqIE9wZW5Db2RlIFdlYiDmnI3liqHlkK/liqjpgInpoblcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBXZWJPcHRpb25zIHtcbiAgLyoqIOacjeWKoeerr+WPoyAqL1xuICBwb3J0OiBudW1iZXJcbiAgLyoqIOacjeWKoeS4u+acuuWQjSAqL1xuICBob3N0bmFtZTogc3RyaW5nXG4gIC8qKiDmnI3liqHlmaggVVJMICovXG4gIHNlcnZlclVybDogc3RyaW5nXG4gIC8qKiDlt6XkvZznm67lvZUgKi9cbiAgY3dkOiBzdHJpbmdcbiAgLyoqIOmFjee9ruebruW9lei3r+W+hCAqL1xuICBjb25maWdEaXI/OiBzdHJpbmdcbiAgLyoqIENPUlMg5YWB6K6455qE5rqQICovXG4gIGNvcnNPcmlnaW5zPzogc3RyaW5nW11cbiAgLyoqIOS4iuS4i+aWhyBBUEkgVVJMICovXG4gIGNvbnRleHRBcGlVcmw/OiBzdHJpbmdcbn1cblxuLyoqXG4gKiDmjILku7bms6jlhaXphY3nva7pgInpoblcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBXaWRnZXRPcHRpb25zIHtcbiAgLyoqIFdlYiDmnI3liqEgVVJMICovXG4gIHdlYlVybDogc3RyaW5nXG4gIC8qKiDmnI3liqHlmaggVVJMICovXG4gIHNlcnZlclVybDogc3RyaW5nXG4gIC8qKiDmjILku7bkvY3nva4gKi9cbiAgcG9zaXRpb246IHN0cmluZ1xuICAvKiog5Li76aKY5qih5byPICovXG4gIHRoZW1lOiBzdHJpbmdcbiAgLyoqIOaYr+WQpuiHquWKqOaJk+W8gCAqL1xuICBvcGVuOiBib29sZWFuXG4gIC8qKiDmmK/lkKboh6rliqjph43ovb0gKi9cbiAgYXV0b1JlbG9hZDogYm9vbGVhblxuICAvKiog5bel5L2c55uu5b2VICovXG4gIGN3ZDogc3RyaW5nXG4gIC8qKiDkvJror50gVVJMICovXG4gIHNlc3Npb25Vcmw/OiBzdHJpbmdcbiAgLyoqIOaYr+WQpuaHkuWKoOi9vSAqL1xuICBsYXp5PzogYm9vbGVhblxuICAvKiog5b+r5o236ZSu6YWN572uICovXG4gIGhvdGtleT86IHN0cmluZ1xufVxuXG4vKipcbiAqIE9wZW5Db2RlIOS8muivneS/oeaBr1xuICovXG5leHBvcnQgaW50ZXJmYWNlIFNlc3Npb25JbmZvIHtcbiAgLyoqIOS8muivnSBJRCAqL1xuICBpZDogc3RyaW5nXG4gIC8qKiDkvJror53moIfor4bnrKYgKi9cbiAgc2x1Zzogc3RyaW5nXG4gIC8qKiDpobnnm64gSUQgKi9cbiAgcHJvamVjdElEOiBzdHJpbmdcbiAgLyoqIOmhueebruebruW9lSAqL1xuICBkaXJlY3Rvcnk6IHN0cmluZ1xuICAvKiog5Lya6K+d5qCH6aKYICovXG4gIHRpdGxlOiBzdHJpbmdcbiAgLyoqIOeJiOacrOWPtyAqL1xuICB2ZXJzaW9uOiBzdHJpbmdcbiAgLyoqIOS7o+eggeWPmOabtOe7n+iuoSAqL1xuICBzdW1tYXJ5OiB7XG4gICAgLyoqIOaWsOWinuihjOaVsCAqL1xuICAgIGFkZGl0aW9uczogbnVtYmVyXG4gICAgLyoqIOWIoOmZpOihjOaVsCAqL1xuICAgIGRlbGV0aW9uczogbnVtYmVyXG4gICAgLyoqIOS/ruaUueaWh+S7tuaVsCAqL1xuICAgIGZpbGVzOiBudW1iZXJcbiAgfVxuICAvKiog5pe26Ze05L+h5oGvICovXG4gIHRpbWU6IHtcbiAgICAvKiog5Yib5bu65pe26Ze05oizICovXG4gICAgY3JlYXRlZDogbnVtYmVyXG4gICAgLyoqIOabtOaWsOaXtumXtOaIsyAqL1xuICAgIHVwZGF0ZWQ6IG51bWJlclxuICB9XG59XG5cbi8qKlxuICog6YCJ5Lit55qE5YWD57Sg5L+h5oGvXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2VsZWN0ZWRFbGVtZW50IHtcbiAgLyoqIOaWh+S7tui3r+W+hCAqL1xuICBmaWxlUGF0aDogc3RyaW5nIHwgbnVsbFxuICAvKiog6KGM5Y+3ICovXG4gIGxpbmU6IG51bWJlciB8IG51bGxcbiAgLyoqIOWIl+WPtyAqL1xuICBjb2x1bW46IG51bWJlciB8IG51bGxcbiAgLyoqIOWFg+e0oOWGhemDqOaWh+acrCAqL1xuICBpbm5lclRleHQ6IHN0cmluZ1xuICAvKiog5YWD57Sg5o+P6L+w77yI5qCH562+5ZCNK+mAieaLqeWZqO+8iSAqL1xuICBkZXNjcmlwdGlvbj86IHN0cmluZ1xufVxuXG4vKipcbiAqIOmhtemdouS4iuS4i+aWh+aVsOaNrlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFBhZ2VDb250ZXh0IHtcbiAgLyoqIOW9k+WJjemhtemdoiBVUkwgKi9cbiAgdXJsOiBzdHJpbmdcbiAgLyoqIOW9k+WJjemhtemdouagh+mimCAqL1xuICB0aXRsZTogc3RyaW5nXG4gIC8qKiDpgInkuK3nmoTlhYPntKDliJfooaggKi9cbiAgc2VsZWN0ZWRFbGVtZW50cz86IFNlbGVjdGVkRWxlbWVudFtdXG59XG4iXX0=
@@ -0,0 +1,42 @@
1
+ /**
2
+ * 等待服务器就绪
3
+ * @param url - 服务器 URL
4
+ * @param timeout - 超时时间(毫秒),默认 10000
5
+ * @returns Promise,服务器就绪时 resolve
6
+ * @throws 超时后抛出错误
7
+ */
8
+ export declare function waitForServer(url: string, timeout?: number): Promise<void>;
9
+ /**
10
+ * 检查 OpenCode 是否已安装
11
+ * @returns 是否已安装
12
+ */
13
+ export declare function checkOpenCodeInstalled(): Promise<boolean>;
14
+ /**
15
+ * 检查端口是否可用
16
+ * @param port - 端口号
17
+ * @param hostname - 主机名,默认 '127.0.0.1'
18
+ * @returns 端口是否可用
19
+ */
20
+ export declare function isPortAvailable(port: number, hostname?: string): Promise<boolean>;
21
+ /**
22
+ * 查找可用端口
23
+ * @param startPort - 起始端口
24
+ * @param hostname - 主机名,默认 '127.0.0.1'
25
+ * @param maxTries - 最大尝试次数,默认 10
26
+ * @returns 可用的端口号
27
+ * @throws 找不到可用端口时抛出错误
28
+ */
29
+ export declare function findAvailablePort(startPort: number, hostname?: string, maxTries?: number): Promise<number>;
30
+ /**
31
+ * 终止占用指定端口的进程
32
+ * @param port - 端口号
33
+ * @param hostname - 主机名,默认 '127.0.0.1'
34
+ * @returns 是否成功终止进程
35
+ */
36
+ export declare function killProcessOnPort(port: number, hostname?: string): Promise<boolean>;
37
+ /**
38
+ * 检查 OpenCode 进程是否在运行
39
+ * @param port - 服务端口
40
+ * @returns 进程是否在运行
41
+ */
42
+ export declare function checkOpenCodeProcess(port: number): Promise<boolean>;
package/dist/utils.js ADDED
@@ -0,0 +1,156 @@
1
+ import http from 'http';
2
+ import { spawn } from 'child_process';
3
+ import net from 'net';
4
+ import { DEFAULT_HOSTNAME, SERVER_CHECK_INTERVAL, HEALTH_CHECK_TIMEOUT, MAX_PORT_TRIES, } from './constants';
5
+ /**
6
+ * 等待服务器就绪
7
+ * @param url - 服务器 URL
8
+ * @param timeout - 超时时间(毫秒),默认 10000
9
+ * @returns Promise,服务器就绪时 resolve
10
+ * @throws 超时后抛出错误
11
+ */
12
+ export function waitForServer(url, timeout = 10000) {
13
+ return new Promise((resolve, reject) => {
14
+ const startTime = Date.now();
15
+ const check = () => {
16
+ const req = http.get(url, (res) => {
17
+ if (res.statusCode && res.statusCode < 500) {
18
+ resolve();
19
+ }
20
+ else {
21
+ retryOrReject();
22
+ }
23
+ });
24
+ req.on('error', retryOrReject);
25
+ };
26
+ const retryOrReject = () => {
27
+ if (Date.now() - startTime < timeout) {
28
+ setTimeout(check, SERVER_CHECK_INTERVAL);
29
+ }
30
+ else {
31
+ reject(new Error(`Server not ready after ${timeout}ms`));
32
+ }
33
+ };
34
+ check();
35
+ });
36
+ }
37
+ /**
38
+ * 检查 OpenCode 是否已安装
39
+ * @returns 是否已安装
40
+ */
41
+ export async function checkOpenCodeInstalled() {
42
+ return new Promise((resolve) => {
43
+ const proc = spawn('opencode', ['--version'], { stdio: 'ignore' });
44
+ proc.on('close', (code) => resolve(code === 0));
45
+ proc.on('error', () => resolve(false));
46
+ });
47
+ }
48
+ /**
49
+ * 检查端口是否可用
50
+ * @param port - 端口号
51
+ * @param hostname - 主机名,默认 '127.0.0.1'
52
+ * @returns 端口是否可用
53
+ */
54
+ export async function isPortAvailable(port, hostname = DEFAULT_HOSTNAME) {
55
+ return new Promise((resolve) => {
56
+ const server = net.createServer();
57
+ server.once('error', () => resolve(false));
58
+ server.once('listening', () => {
59
+ server.close();
60
+ resolve(true);
61
+ });
62
+ server.listen(port, hostname);
63
+ });
64
+ }
65
+ /**
66
+ * 查找可用端口
67
+ * @param startPort - 起始端口
68
+ * @param hostname - 主机名,默认 '127.0.0.1'
69
+ * @param maxTries - 最大尝试次数,默认 10
70
+ * @returns 可用的端口号
71
+ * @throws 找不到可用端口时抛出错误
72
+ */
73
+ export async function findAvailablePort(startPort, hostname = DEFAULT_HOSTNAME, maxTries = MAX_PORT_TRIES) {
74
+ for (let port = startPort; port < startPort + maxTries; port++) {
75
+ if (await isPortAvailable(port, hostname)) {
76
+ return port;
77
+ }
78
+ }
79
+ throw new Error(`No available port found after ${maxTries} tries starting from ${startPort}`);
80
+ }
81
+ /**
82
+ * 终止占用指定端口的进程
83
+ * @param port - 端口号
84
+ * @param hostname - 主机名,默认 '127.0.0.1'
85
+ * @returns 是否成功终止进程
86
+ */
87
+ export async function killProcessOnPort(port, hostname = DEFAULT_HOSTNAME) {
88
+ return new Promise((resolve) => {
89
+ if (process.platform === 'win32') {
90
+ killProcessOnWindows(port, resolve);
91
+ }
92
+ else {
93
+ killProcessOnUnix(port, resolve);
94
+ }
95
+ });
96
+ }
97
+ /**
98
+ * 在 Windows 上终止占用端口的进程
99
+ */
100
+ function killProcessOnWindows(port, resolve) {
101
+ const proc = spawn('cmd', ['/c', `netstat -ano | findstr :${port}`], { stdio: 'pipe' });
102
+ let output = '';
103
+ proc.stdout?.on('data', (data) => {
104
+ output += data.toString();
105
+ });
106
+ proc.on('close', () => {
107
+ const match = output.match(/LISTENING\s+(\d+)/);
108
+ if (match) {
109
+ spawn('taskkill', ['/F', '/PID', match[1]], { stdio: 'ignore' })
110
+ .on('close', (code) => resolve(code === 0));
111
+ }
112
+ else {
113
+ resolve(false);
114
+ }
115
+ });
116
+ }
117
+ /**
118
+ * 在 Unix 系统上终止占用端口的进程
119
+ */
120
+ function killProcessOnUnix(port, resolve) {
121
+ const proc = spawn('lsof', ['-ti', `tcp:${port}`, '-sTCP:LISTEN'], { stdio: 'pipe' });
122
+ let output = '';
123
+ proc.stdout?.on('data', (data) => {
124
+ output += data.toString();
125
+ });
126
+ proc.on('close', () => {
127
+ const pids = output.trim().split('\n').filter(Boolean);
128
+ if (pids.length > 0) {
129
+ const killProc = spawn('kill', ['-9', ...pids], { stdio: 'ignore' });
130
+ killProc.on('close', (code) => resolve(code === 0));
131
+ }
132
+ else {
133
+ resolve(false);
134
+ }
135
+ });
136
+ }
137
+ /**
138
+ * 检查 OpenCode 进程是否在运行
139
+ * @param port - 服务端口
140
+ * @returns 进程是否在运行
141
+ */
142
+ export async function checkOpenCodeProcess(port) {
143
+ return new Promise((resolve) => {
144
+ const req = http.get({
145
+ hostname: DEFAULT_HOSTNAME,
146
+ port,
147
+ path: '/health',
148
+ timeout: HEALTH_CHECK_TIMEOUT,
149
+ }, (res) => {
150
+ resolve(res.statusCode === 200);
151
+ });
152
+ req.on('error', () => resolve(false));
153
+ req.end();
154
+ });
155
+ }
156
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFBO0FBQ3ZCLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFDckMsT0FBTyxHQUFHLE1BQU0sS0FBSyxDQUFBO0FBQ3JCLE9BQU8sRUFDTCxnQkFBZ0IsRUFDaEIscUJBQXFCLEVBQ3JCLG9CQUFvQixFQUNwQixjQUFjLEdBQ2YsTUFBTSxhQUFhLENBQUE7QUFFcEI7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLGFBQWEsQ0FBQyxHQUFXLEVBQUUsT0FBTyxHQUFHLEtBQUs7SUFDeEQsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUNyQyxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUE7UUFFNUIsTUFBTSxLQUFLLEdBQUcsR0FBUyxFQUFFO1lBQ3ZCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7Z0JBQ2hDLElBQUksR0FBRyxDQUFDLFVBQVUsSUFBSSxHQUFHLENBQUMsVUFBVSxHQUFHLEdBQUcsRUFBRSxDQUFDO29CQUMzQyxPQUFPLEVBQUUsQ0FBQTtnQkFDWCxDQUFDO3FCQUFNLENBQUM7b0JBQ04sYUFBYSxFQUFFLENBQUE7Z0JBQ2pCLENBQUM7WUFDSCxDQUFDLENBQUMsQ0FBQTtZQUVGLEdBQUcsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLGFBQWEsQ0FBQyxDQUFBO1FBQ2hDLENBQUMsQ0FBQTtRQUVELE1BQU0sYUFBYSxHQUFHLEdBQVMsRUFBRTtZQUMvQixJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxTQUFTLEdBQUcsT0FBTyxFQUFFLENBQUM7Z0JBQ3JDLFVBQVUsQ0FBQyxLQUFLLEVBQUUs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Q0FBQTtRQUNGLEdBQUcsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFBO1FBQ3JDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQTtJQUNYLENBQUMsQ0FBQyxDQUFBO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBodHRwIGZyb20gJ2h0dHAnXG5pbXBvcnQgeyBzcGF3biB9IGZyb20gJ2NoaWxkX3Byb2Nlc3MnXG5pbXBvcnQgbmV0IGZyb20gJ25ldCdcbmltcG9ydCB7XG4gIERFRkFVTFRfSE9TVE5BTUUsXG4gIFNFUlZFUl9DSEVDS19JTlRFUlZBTCxcbiAgSEVBTFRIX0NIRUNLX1RJTUVPVVQsXG4gIE1BWF9QT1JUX1RSSUVTLFxufSBmcm9tICcuL2NvbnN0YW50cydcblxuLyoqXG4gKiDnrYnlvoXmnI3liqHlmajlsLHnu6pcbiAqIEBwYXJhbSB1cmwgLSDmnI3liqHlmaggVVJMXG4gKiBAcGFyYW0gdGltZW91dCAtIOi2heaXtuaXtumXtO+8iOavq+enku+8ie+8jOm7mOiupCAxMDAwMFxuICogQHJldHVybnMgUHJvbWlzZe+8jOacjeWKoeWZqOWwsee7quaXtiByZXNvbHZlXG4gKiBAdGhyb3dzIOi2heaXtuWQjuaKm+WHuumUmeivr1xuICovXG5leHBvcnQgZnVuY3Rpb24gd2FpdEZvclNlcnZlcih1cmw6IHN0cmluZywgdGltZW91dCA9IDEwMDAwKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKVxuXG4gICAgY29uc3QgY2hlY2sgPSAoKTogdm9pZCA9PiB7XG4gICAgICBjb25zdCByZXEgPSBodHRwLmdldCh1cmwsIChyZXMpID0+IHtcbiAgICAgICAgaWYgKHJlcy5zdGF0dXNDb2RlICYmIHJlcy5zdGF0dXNDb2RlIDwgNTAwKSB7XG4gICAgICAgICAgcmVzb2x2ZSgpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0cnlPclJlamVjdCgpXG4gICAgICAgIH1cbiAgICAgIH0pXG5cbiAgICAgIHJlcS5vbignZXJyb3InLCByZXRyeU9yUmVqZWN0KVxuICAgIH1cblxuICAgIGNvbnN0IHJldHJ5T3JSZWplY3QgPSAoKTogdm9pZCA9PiB7XG4gICAgICBpZiAoRGF0ZS5ub3coKSAtIHN0YXJ0VGltZSA8IHRpbWVvdXQpIHtcbiAgICAgICAgc2V0VGltZW91dChjaGVjaywgU0VSVkVSX0NIRUNLX0lOVEVSVkFMKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgU2VydmVyIG5vdCByZWFkeSBhZnRlciAke3RpbWVvdXR9bXNgKSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjaGVjaygpXG4gIH0pXG59XG5cbi8qKlxuICog5qOA5p+lIE9wZW5Db2RlIOaYr+WQpuW3suWuieijhVxuICogQHJldHVybnMg5piv5ZCm5bey5a6J6KOFXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjaGVja09wZW5Db2RlSW5zdGFsbGVkKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICBjb25zdCBwcm9jID0gc3Bhd24oJ29wZW5jb2RlJywgWyctLXZlcnNpb24nXSwgeyBzdGRpbzogJ2lnbm9yZScgfSlcbiAgICBwcm9jLm9uKCdjbG9zZScsIChjb2RlKSA9PiByZXNvbHZlKGNvZGUgPT09IDApKVxuICAgIHByb2Mub24oJ2Vycm9yJywgKCkgPT4gcmVzb2x2ZShmYWxzZSkpXG4gIH0pXG59XG5cbi8qKlxuICog5qOA5p+l56uv5Y+j5piv5ZCm5Y+v55SoXG4gKiBAcGFyYW0gcG9ydCAtIOerr+WPo+WPt1xuICogQHBhcmFtIGhvc3RuYW1lIC0g5Li75py65ZCN77yM6buY6K6kICcxMjcuMC4wLjEnXG4gKiBAcmV0dXJucyDnq6/lj6PmmK/lkKblj6/nlKhcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGlzUG9ydEF2YWlsYWJsZShwb3J0OiBudW1iZXIsIGhvc3RuYW1lID0gREVGQVVMVF9IT1NUTkFNRSk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICBjb25zdCBzZXJ2ZXIgPSBuZXQuY3JlYXRlU2VydmVyKClcbiAgICBzZXJ2ZXIub25jZSgnZXJyb3InLCAoKSA9PiByZXNvbHZlKGZhbHNlKSlcbiAgICBzZXJ2ZXIub25jZSgnbGlzdGVuaW5nJywgKCkgPT4ge1xuICAgICAgc2VydmVyLmNsb3NlKClcbiAgICAgIHJlc29sdmUodHJ1ZSlcbiAgICB9KVxuICAgIHNlcnZlci5saXN0ZW4ocG9ydCwgaG9zdG5hbWUpXG4gIH0pXG59XG5cbi8qKlxuICog5p+l5om+5Y+v55So56uv5Y+jXG4gKiBAcGFyYW0gc3RhcnRQb3J0IC0g6LW35aeL56uv5Y+jXG4gKiBAcGFyYW0gaG9zdG5hbWUgLSDkuLvmnLrlkI3vvIzpu5jorqQgJzEyNy4wLjAuMSdcbiAqIEBwYXJhbSBtYXhUcmllcyAtIOacgOWkp+WwneivleasoeaVsO+8jOm7mOiupCAxMFxuICogQHJldHVybnMg5Y+v55So55qE56uv5Y+j5Y+3XG4gKiBAdGhyb3dzIOaJvuS4jeWIsOWPr+eUqOerr+WPo+aXtuaKm+WHuumUmeivr1xuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmluZEF2YWlsYWJsZVBvcnQoXG4gIHN0YXJ0UG9ydDogbnVtYmVyLFxuICBob3N0bmFtZSA9IERFRkFVTFRfSE9TVE5BTUUsXG4gIG1heFRyaWVzID0gTUFYX1BPUlRfVFJJRVNcbik6IFByb21pc2U8bnVtYmVyPiB7XG4gIGZvciAobGV0IHBvcnQgPSBzdGFydFBvcnQ7IHBvcnQgPCBzdGFydFBvcnQgKyBtYXhUcmllczsgcG9ydCsrKSB7XG4gICAgaWYgKGF3YWl0IGlzUG9ydEF2YWlsYWJsZShwb3J0LCBob3N0bmFtZSkpIHtcbiAgICAgIHJldHVybiBwb3J0XG4gICAgfVxuICB9XG4gIHRocm93IG5ldyBFcnJvcihgTm8gYXZhaWxhYmxlIHBvcnQgZm91bmQgYWZ0ZXIgJHttYXhUcmllc30gdHJpZXMgc3RhcnRpbmcgZnJvbSAke3N0YXJ0UG9ydH1gKVxufVxuXG4vKipcbiAqIOe7iOatouWNoOeUqOaMh+Wumuerr+WPo+eahOi/m+eoi1xuICogQHBhcmFtIHBvcnQgLSDnq6/lj6Plj7dcbiAqIEBwYXJhbSBob3N0bmFtZSAtIOS4u+acuuWQje+8jOm7mOiupCAnMTI3LjAuMC4xJ1xuICogQHJldHVybnMg5piv5ZCm5oiQ5Yqf57uI5q2i6L+b56iLXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBraWxsUHJvY2Vzc09uUG9ydChwb3J0OiBudW1iZXIsIGhvc3RuYW1lID0gREVGQVVMVF9IT1NUTkFNRSk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICBpZiAocHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJykge1xuICAgICAga2lsbFByb2Nlc3NPbldpbmRvd3MocG9ydCwgcmVzb2x2ZSlcbiAgICB9IGVsc2Uge1xuICAgICAga2lsbFByb2Nlc3NPblVuaXgocG9ydCwgcmVzb2x2ZSlcbiAgICB9XG4gIH0pXG59XG5cbi8qKlxuICog5ZyoIFdpbmRvd3Mg5LiK57uI5q2i5Y2g55So56uv5Y+j55qE6L+b56iLXG4gKi9cbmZ1bmN0aW9uIGtpbGxQcm9jZXNzT25XaW5kb3dzKHBvcnQ6IG51bWJlciwgcmVzb2x2ZTogKHZhbHVlOiBib29sZWFuKSA9PiB2b2lkKTogdm9pZCB7XG4gIGNvbnN0IHByb2MgPSBzcGF3bignY21kJywgWycvYycsIGBuZXRzdGF0IC1hbm8gfCBmaW5kc3RyIDoke3BvcnR9YF0sIHsgc3RkaW86ICdwaXBlJyB9KVxuICBsZXQgb3V0cHV0ID0gJydcblxuICBwcm9jLnN0ZG91dD8ub24oJ2RhdGEnLCAoZGF0YSkgPT4ge1xuICAgIG91dHB1dCArPSBkYXRhLnRvU3RyaW5nKClcbiAgfSlcblxuICBwcm9jLm9uKCdjbG9zZScsICgpID0+IHtcbiAgICBjb25zdCBtYXRjaCA9IG91dHB1dC5tYXRjaCgvTElTVEVOSU5HXFxzKyhcXGQrKS8pXG4gICAgaWYgKG1hdGNoKSB7XG4gICAgICBzcGF3bigndGFza2tpbGwnLCBbJy9GJywgJy9QSUQnLCBtYXRjaFsxXV0sIHsgc3RkaW86ICdpZ25vcmUnIH0pXG4gICAgICAgIC5vbignY2xvc2UnLCAoY29kZSkgPT4gcmVzb2x2ZShjb2RlID09PSAwKSlcbiAgICB9IGVsc2Uge1xuICAgICAgcmVzb2x2ZShmYWxzZSlcbiAgICB9XG4gIH0pXG59XG5cbi8qKlxuICog5ZyoIFVuaXgg57O757uf5LiK57uI5q2i5Y2g55So56uv5Y+j55qE6L+b56iLXG4gKi9cbmZ1bmN0aW9uIGtpbGxQcm9jZXNzT25Vbml4KHBvcnQ6IG51bWJlciwgcmVzb2x2ZTogKHZhbHVlOiBib29sZWFuKSA9PiB2b2lkKTogdm9pZCB7XG4gIGNvbnN0IHByb2MgPSBzcGF3bignbHNvZicsIFsnLXRpJywgYHRjcDoke3BvcnR9YCwgJy1zVENQOkxJU1RFTiddLCB7IHN0ZGlvOiAncGlwZScgfSlcbiAgbGV0IG91dHB1dCA9ICcnXG5cbiAgcHJvYy5zdGRvdXQ/Lm9uKCdkYXRhJywgKGRhdGEpID0+IHtcbiAgICBvdXRwdXQgKz0gZGF0YS50b1N0cmluZygpXG4gIH0pXG5cbiAgcHJvYy5vbignY2xvc2UnLCAoKSA9PiB7XG4gICAgY29uc3QgcGlkcyA9IG91dHB1dC50cmltKCkuc3BsaXQoJ1xcbicpLmZpbHRlcihCb29sZWFuKVxuICAgIGlmIChwaWRzLmxlbmd0aCA+IDApIHtcbiAgICAgIGNvbnN0IGtpbGxQcm9jID0gc3Bhd24oJ2tpbGwnLCBbJy05JywgLi4ucGlkc10sIHsgc3RkaW86ICdpZ25vcmUnIH0pXG4gICAgICBraWxsUHJvYy5vbignY2xvc2UnLCAoY29kZSkgPT4gcmVzb2x2ZShjb2RlID09PSAwKSlcbiAgICB9IGVsc2Uge1xuICAgICAgcmVzb2x2ZShmYWxzZSlcbiAgICB9XG4gIH0pXG59XG5cbi8qKlxuICog5qOA5p+lIE9wZW5Db2RlIOi/m+eoi+aYr+WQpuWcqOi/kOihjFxuICogQHBhcmFtIHBvcnQgLSDmnI3liqHnq6/lj6NcbiAqIEByZXR1cm5zIOi/m+eoi+aYr+WQpuWcqOi/kOihjFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2hlY2tPcGVuQ29kZVByb2Nlc3MocG9ydDogbnVtYmVyKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgIGNvbnN0IHJlcSA9IGh0dHAuZ2V0KHtcbiAgICAgIGhvc3RuYW1lOiBERUZBVUxUX0hPU1ROQU1FLFxuICAgICAgcG9ydCxcbiAgICAgIHBhdGg6ICcvaGVhbHRoJyxcbiAgICAgIHRpbWVvdXQ6IEhFQUxUSF9DSEVDS19USU1FT1VULFxuICAgIH0sIChyZXMpID0+IHtcbiAgICAgIHJlc29sdmUocmVzLnN0YXR1c0NvZGUgPT09IDIwMClcbiAgICB9KVxuICAgIHJlcS5vbignZXJyb3InLCAoKSA9PiByZXNvbHZlKGZhbHNlKSlcbiAgICByZXEuZW5kKClcbiAgfSlcbn1cbiJdfQ==
package/dist/web.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { ChildProcess } from 'child_process';
2
+ import { WebOptions } from './types';
3
+ /**
4
+ * 启动 OpenCode Web 服务
5
+ * @param options - Web 服务启动选项
6
+ * @returns 子进程实例
7
+ * @throws 服务启动超时时抛出错误
8
+ * @example
9
+ * ```ts
10
+ * const proc = await startOpenCodeWeb({
11
+ * port: 4097,
12
+ * hostname: '127.0.0.1',
13
+ * serverUrl: '',
14
+ * cwd: process.cwd()
15
+ * })
16
+ * ```
17
+ */
18
+ export declare function startOpenCodeWeb(options: WebOptions): Promise<ChildProcess>;
package/dist/web.js ADDED
@@ -0,0 +1,95 @@
1
+ import { spawn } from 'child_process';
2
+ import { waitForServer } from './utils';
3
+ import { SERVER_START_TIMEOUT, LOG_PREFIX, } from './constants';
4
+ import path from 'path';
5
+ import fs from 'fs';
6
+ /**
7
+ * 启动 OpenCode Web 服务
8
+ * @param options - Web 服务启动选项
9
+ * @returns 子进程实例
10
+ * @throws 服务启动超时时抛出错误
11
+ * @example
12
+ * ```ts
13
+ * const proc = await startOpenCodeWeb({
14
+ * port: 4097,
15
+ * hostname: '127.0.0.1',
16
+ * serverUrl: '',
17
+ * cwd: process.cwd()
18
+ * })
19
+ * ```
20
+ */
21
+ export async function startOpenCodeWeb(options) {
22
+ const { port, hostname, cwd, configDir, corsOrigins, contextApiUrl } = options;
23
+ const stateDir = createStateDirectory(cwd);
24
+ const pluginPath = path.join(stateDir, 'plugins', 'page-context.js');
25
+ const env = buildProcessEnv(stateDir, configDir, contextApiUrl, pluginPath);
26
+ const args = [
27
+ 'serve',
28
+ '--port', String(port),
29
+ '--hostname', hostname,
30
+ ];
31
+ if (corsOrigins && corsOrigins.length > 0) {
32
+ corsOrigins.forEach(origin => {
33
+ args.push('--cors', origin);
34
+ });
35
+ }
36
+ const proc = spawn('opencode', args, {
37
+ cwd,
38
+ stdio: 'pipe',
39
+ env,
40
+ });
41
+ setupProcessHandlers(proc, port, hostname);
42
+ await waitForServer(`http://${hostname}:${port}`, SERVER_START_TIMEOUT);
43
+ console.log(`\n\x1b[36m\x1b[1m${LOG_PREFIX}\x1b[0m ✨ OpenCode server started successfully\n`);
44
+ return proc;
45
+ }
46
+ /**
47
+ * 创建状态目录
48
+ * @param cwd - 工作目录
49
+ * @returns 状态目录路径
50
+ */
51
+ function createStateDirectory(cwd) {
52
+ const stateDir = path.join(cwd, 'node_modules', '.cache', 'opencode');
53
+ if (!fs.existsSync(stateDir)) {
54
+ fs.mkdirSync(stateDir, { recursive: true });
55
+ }
56
+ return stateDir;
57
+ }
58
+ /**
59
+ * 构建进程环境变量
60
+ * @param stateDir - 状态目录路径
61
+ * @param configDir - 配置目录路径
62
+ * @param contextApiUrl - 上下文 API URL
63
+ * @param pluginPath - 插件路径
64
+ * @returns 环境变量对象
65
+ */
66
+ function buildProcessEnv(stateDir, configDir, contextApiUrl, pluginPath) {
67
+ const env = {
68
+ ...Object.fromEntries(Object.entries(process.env).filter(([, v]) => v !== undefined)),
69
+ XDG_STATE_HOME: stateDir,
70
+ };
71
+ if (configDir) {
72
+ env.OPENCODE_CONFIG_DIR = configDir;
73
+ }
74
+ if (contextApiUrl) {
75
+ env.OPENCODE_CONTEXT_API_URL = contextApiUrl;
76
+ }
77
+ if (pluginPath) {
78
+ env.OPENCODE_PLUGINS = pluginPath;
79
+ }
80
+ return env;
81
+ }
82
+ /**
83
+ * 设置进程事件处理器
84
+ * @param proc - 子进程实例
85
+ */
86
+ function setupProcessHandlers(proc, port, hostname) {
87
+ proc.stdout?.on('data', () => {
88
+ });
89
+ proc.stderr?.on('data', () => {
90
+ });
91
+ proc.on('error', (err) => {
92
+ console.error(`\x1b[31m\x1b[1m${LOG_PREFIX}\x1b[0m ❌ Failed to start OpenCode server:`, err.message);
93
+ });
94
+ }
95
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2ViLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3dlYi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsS0FBSyxFQUFnQixNQUFNLGVBQWUsQ0FBQTtBQUNuRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBRXZDLE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsVUFBVSxHQUNYLE1BQU0sYUFBYSxDQUFBO0FBQ3BCLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQTtBQUN2QixPQUFPLEVBQUUsTUFBTSxJQUFJLENBQUE7QUFFbkI7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGdCQUFnQixDQUFDLE9BQW1CO0lBQ3hELE1BQU0sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxHQUFHLE9BQU8sQ0FBQTtJQUU5RSxNQUFNLFFBQVEsR0FBRyxvQkFBb0IsQ0FBQyxHQUFHLENBQUMsQ0FBQTtJQUMxQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxTQUFTLEVBQUUsaUJBQWlCLENBQUMsQ0FBQTtJQUNwRSxNQUFNLEdBQUcsR0FBRyxlQUFlLENBQUMsUUFBUSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsVUFBVSxDQUFDLENBQUE7SUFFM0UsTUFBTSxJQUFJLEdBQUc7UUFDWCxPQUFPO1FBQ1AsUUFBUSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUM7UUFDdEIsWUFBWSxFQUFFLFFBQVE7S0FDdkIsQ0FBQTtJQUVELElBQUksV0FBVyxJQUFJLFdBQVcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDMUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUMzQixJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsQ0FBQTtRQUM3QixDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUM7SUFFRCxNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsVUFBVSxFQUFFLElBQUksRUFBRTtRQUNuQyxHQUFHO1FBQ0gsS0FBSyxFQUFFLE1BQU07UUFDYixHQUFHO0tBQ0osQ0FBQyxDQUFBO0lBRUYsb0JBQW9CLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQTtJQUUxQyxNQUFNLGFBQWEsQ0FBQyxVQUFVLFFBQVEsSUFBSSxJQUFJLEVBQUUsRUFBRSxvQkFBb0IsQ0FBQyxDQUFBO0lBRXZFLE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLFVBQVUsa0RBQWtELENBQUMsQ0FBQTtJQUU3RixPQUFPLElBQUksQ0FBQTtBQUNiLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBUyxvQkFBb0IsQ0FBQyxHQUFXO0lBQ3ZDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLGNBQWMsRUFBRSxRQUFRLEVBQUUsVUFBVSxDQUFDLENBQUE7SUFFckUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUM3QixFQUFFLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFBO0lBQzdDLENBQUM7SUFFRCxPQUFPLFFBQVEsQ0FBQTtBQUNqQixDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILFNBQVMsZUFBZSxDQUFDLFFBQWdCLEVBQUUsU0FBa0IsRUFBRSxhQUFzQixFQUFFLFVBQW1CO0lBQ3hHLE1BQU0sR0FBRyxHQUEyQjtRQUNsQyxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQ25CLE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLFNBQVMsQ0FBQyxDQUNyQztRQUMzQixjQUFjLEVBQUUsUUFBUTtLQUN6QixDQUFBO0lBRUQsSUFBSSxTQUFTLEVBQUUsQ0FBQztRQUNkLEdBQUcsQ0FBQyxtQkFBbUIsR0FBRyxTQUFTLENBQUE7SUFDckMsQ0FBQztJQUVELElBQUksYUFBYSxFQUFFLENBQUM7UUFDbEIsR0FBRyxDQUFDLHdCQUF3QixHQUFHLGFBQWEsQ0FBQTtJQUM5QyxDQUFDO0lBRUQsSUFBSSxVQUFVLEVBQUUsQ0FBQztRQUNmLEdBQUcsQ0FBQyxnQkFBZ0IsR0FBRyxVQUFVLENBQUE7SUFDbkMsQ0FBQztJQUVELE9BQU8sR0FBRyxDQUFBO0FBQ1osQ0FBQztBQUVEOzs7R0FHRztBQUNILFNBQVMsb0JBQW9CLENBQUMsSUFBa0IsRUFBRSxJQUFZLEVBQUUsUUFBZ0I7SUFDOUUsSUFBSSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRTtJQUM3QixDQUFDLENBQUMsQ0FBQTtJQUVGLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUU7SUFDN0IsQ0FBQyxDQUFDLENBQUE7SUFFRixJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO1FBQ3ZCLE9BQU8sQ0FBQyxLQUFLLENBQUMsa0JBQWtCLFVBQVUsNENBQTRDLEVBQUUsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFBO0lBQ3RHLENBQUMsQ0FBQyxDQUFBO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHNwYXduLCBDaGlsZFByb2Nlc3MgfSBmcm9tICdjaGlsZF9wcm9jZXNzJ1xuaW1wb3J0IHsgd2FpdEZvclNlcnZlciB9IGZyb20gJy4vdXRpbHMnXG5pbXBvcnQgeyBXZWJPcHRpb25zIH0gZnJvbSAnLi90eXBlcydcbmltcG9ydCB7XG4gIFNFUlZFUl9TVEFSVF9USU1FT1VULFxuICBMT0dfUFJFRklYLFxufSBmcm9tICcuL2NvbnN0YW50cydcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnXG5pbXBvcnQgZnMgZnJvbSAnZnMnXG5cbi8qKlxuICog5ZCv5YqoIE9wZW5Db2RlIFdlYiDmnI3liqFcbiAqIEBwYXJhbSBvcHRpb25zIC0gV2ViIOacjeWKoeWQr+WKqOmAiemhuVxuICogQHJldHVybnMg5a2Q6L+b56iL5a6e5L6LXG4gKiBAdGhyb3dzIOacjeWKoeWQr+WKqOi2heaXtuaXtuaKm+WHuumUmeivr1xuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiBjb25zdCBwcm9jID0gYXdhaXQgc3RhcnRPcGVuQ29kZVdlYih7XG4gKiAgIHBvcnQ6IDQwOTcsXG4gKiAgIGhvc3RuYW1lOiAnMTI3LjAuMC4xJyxcbiAqICAgc2VydmVyVXJsOiAnJyxcbiAqICAgY3dkOiBwcm9jZXNzLmN3ZCgpXG4gKiB9KVxuICogYGBgXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydE9wZW5Db2RlV2ViKG9wdGlvbnM6IFdlYk9wdGlvbnMpOiBQcm9taXNlPENoaWxkUHJvY2Vzcz4ge1xuICBjb25zdCB7IHBvcnQsIGhvc3RuYW1lLCBjd2QsIGNvbmZpZ0RpciwgY29yc09yaWdpbnMsIGNvbnRleHRBcGlVcmwgfSA9IG9wdGlvbnNcblxuICBjb25zdCBzdGF0ZURpciA9IGNyZWF0ZVN0YXRlRGlyZWN0b3J5KGN3ZClcbiAgY29uc3QgcGx1Z2luUGF0aCA9IHBhdGguam9pbihzdGF0ZURpciwgJ3BsdWdpbnMnLCAncGFnZS1jb250ZXh0LmpzJylcbiAgY29uc3QgZW52ID0gYnVpbGRQcm9jZXNzRW52KHN0YXRlRGlyLCBjb25maWdEaXIsIGNvbnRleHRBcGlVcmwsIHBsdWdpblBhdGgpXG5cbiAgY29uc3QgYXJncyA9IFtcbiAgICAnc2VydmUnLFxuICAgICctLXBvcnQnLCBTdHJpbmcocG9ydCksXG4gICAgJy0taG9zdG5hbWUnLCBob3N0bmFtZSxcbiAgXVxuXG4gIGlmIChjb3JzT3JpZ2lucyAmJiBjb3JzT3JpZ2lucy5sZW5ndGggPiAwKSB7XG4gICAgY29yc09yaWdpbnMuZm9yRWFjaChvcmlnaW4gPT4ge1xuICAgICAgYXJncy5wdXNoKCctLWNvcnMnLCBvcmlnaW4pXG4gICAgfSlcbiAgfVxuXG4gIGNvbnN0IHByb2MgPSBzcGF3bignb3BlbmNvZGUnLCBhcmdzLCB7XG4gICAgY3dkLFxuICAgIHN0ZGlvOiAncGlwZScsXG4gICAgZW52LFxuICB9KVxuXG4gIHNldHVwUHJvY2Vzc0hhbmRsZXJzKHByb2MsIHBvcnQsIGhvc3RuYW1lKVxuXG4gIGF3YWl0IHdhaXRGb3JTZXJ2ZXIoYGh0dHA6Ly8ke2hvc3RuYW1lfToke3BvcnR9YCwgU0VSVkVSX1NUQVJUX1RJTUVPVVQpXG5cbiAgY29uc29sZS5sb2coYFxcblxceDFiWzM2bVxceDFiWzFtJHtMT0dfUFJFRklYfVxceDFiWzBtIOKcqCBPcGVuQ29kZSBzZXJ2ZXIgc3RhcnRlZCBzdWNjZXNzZnVsbHlcXG5gKVxuXG4gIHJldHVybiBwcm9jXG59XG5cbi8qKlxuICog5Yib5bu654q25oCB55uu5b2VXG4gKiBAcGFyYW0gY3dkIC0g5bel5L2c55uu5b2VXG4gKiBAcmV0dXJucyDnirbmgIHnm67lvZXot6/lvoRcbiAqL1xuZnVuY3Rpb24gY3JlYXRlU3RhdGVEaXJlY3RvcnkoY3dkOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBzdGF0ZURpciA9IHBhdGguam9pbihjd2QsICdub2RlX21vZHVsZXMnLCAnLmNhY2hlJywgJ29wZW5jb2RlJylcbiAgXG4gIGlmICghZnMuZXhpc3RzU3luYyhzdGF0ZURpcikpIHtcbiAgICBmcy5ta2RpclN5bmMoc3RhdGVEaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pXG4gIH1cbiAgXG4gIHJldHVybiBzdGF0ZURpclxufVxuXG4vKipcbiAqIOaehOW7uui/m+eoi+eOr+Wig+WPmOmHj1xuICogQHBhcmFtIHN0YXRlRGlyIC0g54q25oCB55uu5b2V6Lev5b6EXG4gKiBAcGFyYW0gY29uZmlnRGlyIC0g6YWN572u55uu5b2V6Lev5b6EXG4gKiBAcGFyYW0gY29udGV4dEFwaVVybCAtIOS4iuS4i+aWhyBBUEkgVVJMXG4gKiBAcGFyYW0gcGx1Z2luUGF0aCAtIOaPkuS7tui3r+W+hFxuICogQHJldHVybnMg546v5aKD5Y+Y6YeP5a+56LGhXG4gKi9cbmZ1bmN0aW9uIGJ1aWxkUHJvY2Vzc0VudihzdGF0ZURpcjogc3RyaW5nLCBjb25maWdEaXI/OiBzdHJpbmcsIGNvbnRleHRBcGlVcmw/OiBzdHJpbmcsIHBsdWdpblBhdGg/OiBzdHJpbmcpOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHtcbiAgY29uc3QgZW52OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgIC4uLk9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgIE9iamVjdC5lbnRyaWVzKHByb2Nlc3MuZW52KS5maWx0ZXIoKFssIHZdKSA9PiB2ICE9PSB1bmRlZmluZWQpXG4gICAgKSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+LFxuICAgIFhER19TVEFURV9IT01FOiBzdGF0ZURpcixcbiAgfVxuXG4gIGlmIChjb25maWdEaXIpIHtcbiAgICBlbnYuT1BFTkNPREVfQ09ORklHX0RJUiA9IGNvbmZpZ0RpclxuICB9XG5cbiAgaWYgKGNvbnRleHRBcGlVcmwpIHtcbiAgICBlbnYuT1BFTkNPREVfQ09OVEVYVF9BUElfVVJMID0gY29udGV4dEFwaVVybFxuICB9XG5cbiAgaWYgKHBsdWdpblBhdGgpIHtcbiAgICBlbnYuT1BFTkNPREVfUExVR0lOUyA9IHBsdWdpblBhdGhcbiAgfVxuXG4gIHJldHVybiBlbnZcbn1cblxuLyoqXG4gKiDorr7nva7ov5vnqIvkuovku7blpITnkIblmahcbiAqIEBwYXJhbSBwcm9jIC0g5a2Q6L+b56iL5a6e5L6LXG4gKi9cbmZ1bmN0aW9uIHNldHVwUHJvY2Vzc0hhbmRsZXJzKHByb2M6IENoaWxkUHJvY2VzcywgcG9ydDogbnVtYmVyLCBob3N0bmFtZTogc3RyaW5nKTogdm9pZCB7XG4gIHByb2Muc3Rkb3V0Py5vbignZGF0YScsICgpID0+IHtcbiAgfSlcblxuICBwcm9jLnN0ZGVycj8ub24oJ2RhdGEnLCAoKSA9PiB7XG4gIH0pXG5cbiAgcHJvYy5vbignZXJyb3InLCAoZXJyKSA9PiB7XG4gICAgY29uc29sZS5lcnJvcihgXFx4MWJbMzFtXFx4MWJbMW0ke0xPR19QUkVGSVh9XFx4MWJbMG0g4p2MIEZhaWxlZCB0byBzdGFydCBPcGVuQ29kZSBzZXJ2ZXI6YCwgZXJyLm1lc3NhZ2UpXG4gIH0pXG59XG4iXX0=