vite-plugin-opencode-assistant 1.0.3 → 1.0.4
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/README.md +189 -184
- package/dist/constants.d.ts +2 -1
- package/dist/constants.js +19 -18
- package/dist/constants.js.map +1 -1
- package/dist/opencode/plugins/page-context.js +41 -14
- package/dist/opencode/plugins/page-context.js.map +1 -1
- package/dist/types.d.ts +4 -2
- package/dist/types.js.map +1 -1
- package/dist/vite/client.js +580 -400
- package/dist/vite/client.js.map +1 -1
- package/dist/vite/index.d.ts +2 -2
- package/dist/vite/index.js +348 -171
- package/dist/vite/index.js.map +1 -1
- package/package.json +4 -1
|
@@ -228,7 +228,9 @@ const PageContextPlugin = async () => {
|
|
|
228
228
|
const contextApiUrl = process.env.OPENCODE_CONTEXT_API_URL;
|
|
229
229
|
log.debug("Context API URL:", { contextApiUrl });
|
|
230
230
|
if (!contextApiUrl) {
|
|
231
|
-
log.warn(
|
|
231
|
+
log.warn(
|
|
232
|
+
"OPENCODE_CONTEXT_API_URL is not set, page context plugin will not work"
|
|
233
|
+
);
|
|
232
234
|
return {};
|
|
233
235
|
}
|
|
234
236
|
const apiUrl = contextApiUrl;
|
|
@@ -278,51 +280,76 @@ const PageContextPlugin = async () => {
|
|
|
278
280
|
});
|
|
279
281
|
}
|
|
280
282
|
}
|
|
281
|
-
function formatSelectedElement(element) {
|
|
283
|
+
function formatSelectedElement(element, index) {
|
|
282
284
|
var _a;
|
|
283
285
|
const parts = [];
|
|
286
|
+
parts.push(`### 选中节点 ${index + 1}`);
|
|
284
287
|
if (element.filePath) {
|
|
285
|
-
let location =
|
|
288
|
+
let location = element.filePath;
|
|
286
289
|
if (element.line) {
|
|
287
290
|
location += `:${element.line}`;
|
|
288
291
|
if (element.column) {
|
|
289
292
|
location += `:${element.column}`;
|
|
290
293
|
}
|
|
291
294
|
}
|
|
292
|
-
parts.push(location);
|
|
295
|
+
parts.push(`- **文件位置**: \`${location}\``);
|
|
293
296
|
}
|
|
294
297
|
if ((_a = element.innerText) == null ? void 0 : _a.trim()) {
|
|
295
298
|
const text = element.innerText.trim().substring(0, MAX_TEXT_LENGTH);
|
|
296
|
-
const suffix = element.innerText.length > MAX_TEXT_LENGTH ? "..." : "";
|
|
297
|
-
parts.push(
|
|
299
|
+
const suffix = element.innerText.length > MAX_TEXT_LENGTH ? "\n... (已省略部分内容)" : "";
|
|
300
|
+
parts.push(`- **节点文本**:
|
|
301
|
+
\`\`\`text
|
|
302
|
+
${text}${suffix}
|
|
303
|
+
\`\`\``);
|
|
298
304
|
}
|
|
299
305
|
return parts.join("\n") + "\n";
|
|
300
306
|
}
|
|
301
307
|
function buildContextPrefix(context) {
|
|
302
308
|
var _a;
|
|
303
|
-
|
|
309
|
+
const pageLink = context.title ? `[${context.title}](${context.url})` : context.url;
|
|
310
|
+
let prefix = `【系统提示:以下是用户当前正在浏览的页面上下文,请将其作为最高优先级的背景信息来理解和响应用户的请求。】
|
|
311
|
+
|
|
312
|
+
`;
|
|
313
|
+
prefix += `用户现在正在浏览项目中的这个页面:${pageLink}
|
|
304
314
|
|
|
305
315
|
`;
|
|
306
316
|
if ((_a = context.selectedElements) == null ? void 0 : _a.length) {
|
|
307
|
-
prefix +=
|
|
317
|
+
prefix += `用户选中了以下节点:
|
|
308
318
|
|
|
309
319
|
`;
|
|
310
|
-
context.selectedElements.forEach((element) => {
|
|
311
|
-
prefix += formatSelectedElement(element) + "\n";
|
|
320
|
+
context.selectedElements.forEach((element, index) => {
|
|
321
|
+
prefix += formatSelectedElement(element, index) + "\n";
|
|
312
322
|
});
|
|
313
323
|
}
|
|
314
|
-
prefix +=
|
|
315
|
-
|
|
316
|
-
|
|
324
|
+
prefix += `---
|
|
325
|
+
**用户的请求**:
|
|
326
|
+
|
|
317
327
|
`;
|
|
318
328
|
return prefix;
|
|
319
329
|
}
|
|
320
330
|
return {
|
|
331
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
332
|
+
log.debug("System transform hook called");
|
|
333
|
+
const systemPrompt = `
|
|
334
|
+
你是一个专业的前端开发助手,当前正集成在用户的 Vite 项目中(通过 vite-plugin-opencode-assistant)。
|
|
335
|
+
在对话中,用户可能会自动附加他们当前正在浏览的页面上下文(包括页面 URL、标题以及在页面上选中的 DOM 节点信息)。
|
|
336
|
+
|
|
337
|
+
处理这些上下文时,请遵循以下规则:
|
|
338
|
+
1. **理解上下文**:当看到“我现在正在浏览项目中的这个页面”或“选中节点”等信息时,请将其作为用户请求的背景。
|
|
339
|
+
2. **利用文件路径**:如果提供的节点信息中包含 \`文件位置\`,这通常对应于项目中的源代码文件。你可以直接分析或建议修改这些文件。
|
|
340
|
+
3. **精准定位**:结合节点的 \`节点文本\` 和 \`文件位置\`,帮助用户快速定位问题或实现功能。
|
|
341
|
+
4. **直接给出方案**:针对用户的实际请求,直接给出清晰、可执行的代码修改建议或解释,避免不必要的废话。
|
|
342
|
+
`.trim();
|
|
343
|
+
output.system.push(systemPrompt);
|
|
344
|
+
},
|
|
321
345
|
"experimental.chat.messages.transform": async (_input, output) => {
|
|
322
346
|
var _a, _b;
|
|
323
347
|
log.debug("Message transform hook called");
|
|
324
348
|
const context = await getPageContext();
|
|
325
|
-
log.debug("Context data", {
|
|
349
|
+
log.debug("Context data", {
|
|
350
|
+
hasUrl: !!(context == null ? void 0 : context.url),
|
|
351
|
+
hasElements: !!((_a = context == null ? void 0 : context.selectedElements) == null ? void 0 : _a.length)
|
|
352
|
+
});
|
|
326
353
|
if (!(context == null ? void 0 : context.url)) return;
|
|
327
354
|
const lastUserMsg = [...output.messages].reverse().find((m) => m.info.role === "user");
|
|
328
355
|
if (!lastUserMsg) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-context.js","sources":["../../../src/constants.ts","../../../src/logger.ts","../../../src/opencode/plugins/page-context.ts"],"sourcesContent":["/**\n * @fileoverview OpenCode 插件常量定义\n */\n\n/** ==================== 网络相关 ==================== */\n\n/** 默认主机名 */\nexport const DEFAULT_HOSTNAME = '127.0.0.1'\n\n/** 默认 Web 服务端口 */\nexport const DEFAULT_WEB_PORT = 4097\n\n/** 服务器启动超时时间(毫秒) */\nexport const SERVER_START_TIMEOUT = 15000\n\n/** 服务器检查间隔(毫秒) */\nexport const SERVER_CHECK_INTERVAL = 100\n\n/** ==================== 重试相关 ==================== */\n\n/** 默认重试次数 */\nexport const DEFAULT_RETRIES = 5\n\n/** 重试延迟(毫秒) */\nexport const RETRY_DELAY = 500\n\n/** ==================== 端口查找 ==================== */\n\n/** 最大端口尝试次数 */\nexport const MAX_PORT_TRIES = 10\n\n/** ==================== 日志相关 ==================== */\n\n/** 插件日志前缀 */\nexport const LOG_PREFIX = '[vite-plugin-opencode]'\n\n/** OpenCode Web 日志前缀 */\nexport const WEB_LOG_PREFIX = '[OpenCode Web]'\n\n/** OpenCode Web 错误日志前缀 */\nexport const WEB_ERROR_PREFIX = '[OpenCode Web Error]'\n\n/** ==================== 挂件相关 ==================== */\n\n/** 挂件脚本路径 */\nexport const WIDGET_SCRIPT_PATH = '/__opencode_widget__.js'\n\n/** 配置数据属性名 */\nexport const CONFIG_DATA_ATTR = 'data-opencode-config'\n\n/** 上下文 API 路径 */\nexport const CONTEXT_API_PATH = '/__opencode_context__'\n\n/** 启动 API 路径 */\nexport const START_API_PATH = '/__opencode_start__'\n\n/** 会话列表 API 路径 */\nexport const SESSIONS_API_PATH = '/__opencode_sessions__'\n\n/** SSE 事件流路径 */\nexport const SSE_EVENTS_PATH = '/__opencode_events__'\n\n/** 上下文更新间隔(毫秒) */\nexport const CONTEXT_UPDATE_INTERVAL = 500\n\n/** 服务器同步间隔(毫秒) */\nexport const SERVER_SYNC_INTERVAL = 2000\n\n/** Vue Inspector 检查间隔(毫秒) */\nexport const INSPECTOR_CHECK_INTERVAL = 500\n\n/** 自动打开延迟(毫秒) */\nexport const AUTO_OPEN_DELAY = 1000\n\n/** 通知显示时间(毫秒) */\nexport const NOTIFICATION_DURATION = 3000\n\n/** ==================== 存储相关 ==================== */\n\n/** 初始化标记 */\nexport const INIT_MARKER = '__OPENCODE_INITIALIZED__'\n\n/** 选中元素存储键 */\nexport const SELECTED_ELEMENTS_KEY = '__opencode_selected_elements__'\n\n/** ==================== 文本处理 ==================== */\n\n/** 元素文本最大显示长度 */\nexport const MAX_TEXT_LENGTH = 100\n\n/** 元素上下文标记 */\nexport const CONTEXT_MARKER = '[元素上下文]'\n\n/** ==================== 默认配置 ==================== */\n\n/** 默认插件配置 */\nexport const DEFAULT_CONFIG = {\n enabled: true,\n webPort: DEFAULT_WEB_PORT,\n hostname: DEFAULT_HOSTNAME,\n position: 'bottom-right' as const,\n theme: 'auto' as const,\n open: false,\n autoReload: true,\n verbose: false,\n hotkey: 'ctrl+k',\n}\n","import { LOG_PREFIX } from './constants.js'\n\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\nexport interface LogContext {\n module?: string\n operation?: string\n traceId?: string\n duration?: number\n error?: Error | unknown\n [key: string]: unknown\n}\n\ninterface LoggerConfig {\n verbose: boolean\n level: LogLevel\n showTimestamp: boolean\n showCaller: boolean\n showTrace: boolean\n indent: string\n}\n\nconst COLORS = {\n reset: '\\x1b[0m',\n dim: '\\x1b[2m',\n bright: '\\x1b[1m',\n red: '\\x1b[31m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n blue: '\\x1b[34m',\n magenta: '\\x1b[35m',\n cyan: '\\x1b[36m',\n white: '\\x1b[37m',\n}\n\nconst LEVEL_COLORS: Record<LogLevel, string> = {\n [LogLevel.DEBUG]: COLORS.cyan,\n [LogLevel.INFO]: COLORS.green,\n [LogLevel.WARN]: COLORS.yellow,\n [LogLevel.ERROR]: COLORS.red,\n [LogLevel.NONE]: COLORS.reset,\n}\n\nconst LEVEL_NAMES: Record<LogLevel, string> = {\n [LogLevel.DEBUG]: 'DEBUG',\n [LogLevel.INFO]: 'INFO',\n [LogLevel.WARN]: 'WARN',\n [LogLevel.ERROR]: 'ERROR',\n [LogLevel.NONE]: 'NONE',\n}\n\nlet globalConfig: LoggerConfig = {\n verbose: false,\n level: LogLevel.INFO,\n showTimestamp: true,\n showCaller: true,\n showTrace: false,\n indent: ' ',\n}\n\nlet traceCounter = 0\n\nexport function configureLogger(options: Partial<LoggerConfig>): void {\n globalConfig = { ...globalConfig, ...options }\n}\n\nexport function setVerbose(verbose: boolean): void {\n globalConfig.verbose = verbose\n globalConfig.level = verbose ? LogLevel.DEBUG : LogLevel.INFO\n}\n\nfunction getTimestamp(): string {\n const now = new Date()\n const hours = String(now.getHours()).padStart(2, '0')\n const minutes = String(now.getMinutes()).padStart(2, '0')\n const seconds = String(now.getSeconds()).padStart(2, '0')\n const ms = String(now.getMilliseconds()).padStart(3, '0')\n return `${hours}:${minutes}:${seconds}.${ms}`\n}\n\nfunction getCallerInfo(depth: number = 3): string {\n const stack = new Error().stack\n if (!stack) return ''\n\n const lines = stack.split('\\n')\n const targetLine = lines[depth]\n if (!targetLine) return ''\n\n const match = targetLine.match(/at\\s+(?:(.+?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?/)\n if (!match) return ''\n\n const [, funcName, filePath, line] = match\n const fileName = filePath.split('/').pop() || filePath\n const func = funcName || '<anonymous>'\n return `${fileName}:${line} ${func}`\n}\n\nfunction formatValue(value: unknown, depth: number = 0): string {\n if (depth > 3) return '...'\n \n if (value === null) return 'null'\n if (value === undefined) return 'undefined'\n if (typeof value === 'string') return depth > 0 ? `\"${value}\"` : value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n if (value instanceof Error) {\n return `${value.name}: ${value.message}${value.stack ? `\\n${value.stack}` : ''}`\n }\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]'\n if (value.length > 5) {\n const items = value.slice(0, 3).map(v => formatValue(v, depth + 1))\n return `[${items.join(', ')}, ... ${value.length - 3} more items]`\n }\n const items = value.map(v => formatValue(v, depth + 1))\n return `[${items.join(', ')}]`\n }\n if (typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n if (entries.length === 0) return '{}'\n if (entries.length > 5) {\n const shown = entries.slice(0, 3).map(([k, v]) => `${k}: ${formatValue(v, depth + 1)}`)\n return `{${shown.join(', ')}, ... ${entries.length - 3} more keys}`\n }\n const formatted = entries.map(([k, v]) => `${k}: ${formatValue(v, depth + 1)}`)\n return `{${formatted.join(', ')}}`\n }\n return String(value)\n}\n\nfunction formatContext(context?: LogContext): string {\n if (!context || Object.keys(context).length === 0) return ''\n \n const parts: string[] = []\n \n if (context.module) parts.push(`[${context.module}]`)\n if (context.operation) parts.push(`(${context.operation})`)\n if (context.traceId) parts.push(`trace:${context.traceId}`)\n if (context.duration !== undefined) parts.push(`${context.duration}ms`)\n \n const extraKeys = Object.keys(context).filter(\n k => !['module', 'operation', 'traceId', 'duration', 'error'].includes(k)\n )\n if (extraKeys.length > 0) {\n const extra: Record<string, unknown> = {}\n extraKeys.forEach(k => extra[k] = context[k])\n parts.push(formatValue(extra))\n }\n \n return parts.join(' ')\n}\n\nfunction log(level: LogLevel, message: string, context?: LogContext, ...args: unknown[]): void {\n if (level < globalConfig.level) return\n \n const parts: string[] = []\n \n parts.push(`${COLORS.dim}[${process.pid}]${COLORS.reset}`)\n \n if (globalConfig.showTimestamp) {\n parts.push(`${COLORS.dim}${getTimestamp()}${COLORS.reset}`)\n }\n \n const levelColor = LEVEL_COLORS[level]\n const levelName = LEVEL_NAMES[level].padEnd(5)\n parts.push(`${levelColor}${levelName}${COLORS.reset}`)\n \n parts.push(`${COLORS.bright}${LOG_PREFIX}${COLORS.reset}`)\n \n const contextStr = formatContext(context)\n if (contextStr) {\n parts.push(`${COLORS.magenta}${contextStr}${COLORS.reset}`)\n }\n \n parts.push(message)\n \n if (globalConfig.showCaller && level >= LogLevel.WARN) {\n const caller = getCallerInfo(4)\n if (caller) {\n parts.push(`${COLORS.dim}(${caller})${COLORS.reset}`)\n }\n }\n \n const formattedArgs = args.map(a => formatValue(a)).join(' ')\n if (formattedArgs) {\n parts.push(formattedArgs)\n }\n \n const output = parts.join(' ')\n \n if (level >= LogLevel.ERROR) {\n console.error(output)\n } else if (level === LogLevel.WARN) {\n console.warn(output)\n } else {\n console.log(output)\n }\n \n if (context?.error && level >= LogLevel.ERROR && globalConfig.showTrace) {\n const err = context.error\n if (err instanceof Error && err.stack) {\n console.error(`${COLORS.dim}${err.stack}${COLORS.reset}`)\n }\n }\n}\n\nexport const logger = {\n debug(message: string, context?: LogContext, ...args: unknown[]): void {\n log(LogLevel.DEBUG, message, context, ...args)\n },\n \n info(message: string, context?: LogContext, ...args: unknown[]): void {\n log(LogLevel.INFO, message, context, ...args)\n },\n \n warn(message: string, context?: LogContext, ...args: unknown[]): void {\n log(LogLevel.WARN, message, context, ...args)\n },\n \n error(message: string, context?: LogContext, ...args: unknown[]): void {\n log(LogLevel.ERROR, message, context, ...args)\n },\n \n group(label: string, context?: LogContext): void {\n if (!globalConfig.verbose) return\n const contextStr = formatContext(context)\n console.log(`${COLORS.dim}[${process.pid}]${COLORS.reset} ${COLORS.bright}${LOG_PREFIX}${COLORS.reset} ${COLORS.blue}▼${COLORS.reset} ${label}${contextStr ? ` ${contextStr}` : ''}`)\n },\n \n groupEnd(): void {\n if (!globalConfig.verbose) return\n },\n}\n\nexport function generateTraceId(): string {\n traceCounter++\n const timestamp = Date.now().toString(36)\n const counter = traceCounter.toString(36).padStart(4, '0')\n return `${timestamp}-${counter}`\n}\n\nexport class PerformanceTimer {\n private startTime: number\n private context: LogContext\n private operation: string\n \n constructor(operation: string, context?: LogContext) {\n this.operation = operation\n this.context = context || {}\n this.startTime = performance.now()\n \n logger.debug(`⏱️ Starting: ${operation}`, this.context)\n }\n \n end(message?: string): number {\n const duration = Math.round(performance.now() - this.startTime)\n const msg = message || `✓ Completed: ${this.operation}`\n logger.debug(msg, { ...this.context, duration })\n return duration\n }\n \n checkpoint(label: string): number {\n const elapsed = Math.round(performance.now() - this.startTime)\n logger.debug(` ↳ ${label}`, { ...this.context, duration: elapsed })\n return elapsed\n }\n}\n\nexport class RequestContext {\n traceId: string\n method: string\n path: string\n startTime: number\n private checkpoints: Array<{ time: number; label: string }> = []\n \n constructor(method: string, path: string) {\n this.traceId = generateTraceId()\n this.method = method\n this.path = path\n this.startTime = performance.now()\n \n logger.debug(`→ ${method} ${path}`, { traceId: this.traceId, module: 'HTTP' })\n }\n \n checkpoint(label: string): void {\n const elapsed = Math.round(performance.now() - this.startTime)\n this.checkpoints.push({ time: elapsed, label })\n logger.debug(` → ${label}`, { traceId: this.traceId, duration: elapsed })\n }\n \n end(statusCode: number): void {\n const duration = Math.round(performance.now() - this.startTime)\n const statusColor = statusCode < 400 ? COLORS.green : COLORS.red\n logger.debug(`← ${this.method} ${this.path} ${statusColor}${statusCode}${COLORS.reset}`, {\n traceId: this.traceId,\n duration,\n checkpoints: this.checkpoints.length,\n })\n }\n \n error(error: Error | unknown): void {\n const duration = Math.round(performance.now() - this.startTime)\n logger.error(`✗ ${this.method} ${this.path}`, {\n traceId: this.traceId,\n duration,\n error,\n })\n }\n}\n\nexport function createLogger(module: string) {\n return {\n debug(message: string, context?: Omit<LogContext, 'module'>, ...args: unknown[]): void {\n logger.debug(message, { ...context, module }, ...args)\n },\n \n info(message: string, context?: Omit<LogContext, 'module'>, ...args: unknown[]): void {\n logger.info(message, { ...context, module }, ...args)\n },\n \n warn(message: string, context?: Omit<LogContext, 'module'>, ...args: unknown[]): void {\n logger.warn(message, { ...context, module }, ...args)\n },\n \n error(message: string, context?: Omit<LogContext, 'module'>, ...args: unknown[]): void {\n logger.error(message, { ...context, module }, ...args)\n },\n \n timer(operation: string, context?: Omit<LogContext, 'module'>): PerformanceTimer {\n return new PerformanceTimer(operation, { ...context, module })\n },\n }\n}\n\nexport function logMethod(\n target: unknown,\n propertyKey: string,\n descriptor: PropertyDescriptor\n): PropertyDescriptor {\n const originalMethod = descriptor.value\n const className = (target as { constructor: { name: string } }).constructor.name\n \n descriptor.value = async function (...args: unknown[]) {\n const timer = new PerformanceTimer(`${className}.${propertyKey}`)\n try {\n const result = await originalMethod.apply(this, args)\n timer.end()\n return result\n } catch (error) {\n timer.end('❌ Failed')\n throw error\n }\n }\n \n return descriptor\n}\n\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0B'\n const k = 1024\n const sizes = ['B', 'KB', 'MB', 'GB']\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))}${sizes[i]}`\n}\n\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`\n if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`\n const minutes = Math.floor(ms / 60000)\n const seconds = Math.round((ms % 60000) / 1000)\n return `${minutes}m ${seconds}s`\n}\n","/**\n * @fileoverview OpenCode 页面上下文插件\n * @description 用于将页面上下文信息注入到 AI 对话中\n */\n\nimport type { Plugin, Hooks } from \"@opencode-ai/plugin\"\nimport { createLogger } from '../../logger.js'\n\nconst MAX_TEXT_LENGTH = 10000\n\nconst CONTEXT_MARKER = '__OPENCODE_CONTEXT__'\n\nconst log = createLogger('OpenCodePluginPageContext')\n\ninterface SelectedElement {\n filePath: string | null\n line: number | null\n column: number | null\n innerText: string\n}\n\ninterface PageContextData {\n url: string\n title: string\n selectedElements?: SelectedElement[]\n}\n\nexport const PageContextPlugin: Plugin = async (): Promise<Hooks> => {\n log.info('PageContextPlugin loading...')\n \n const contextApiUrl = process.env.OPENCODE_CONTEXT_API_URL\n log.debug('Context API URL:', { contextApiUrl })\n\n if (!contextApiUrl) {\n log.warn('OPENCODE_CONTEXT_API_URL is not set, page context plugin will not work')\n return {}\n }\n\n const apiUrl = contextApiUrl as string\n log.info('Plugin initialized successfully')\n\n async function getPageContext(): Promise<PageContextData | null> {\n try {\n log.debug('Fetching context...', { apiUrl })\n const response = await fetch(apiUrl)\n \n if (!response.ok) {\n log.error('Context API returned error status', { \n status: response.status, \n statusText: response.statusText,\n apiUrl \n })\n return null\n }\n \n const data = await response.json() as PageContextData\n log.debug('Context received', { url: data.url, title: data.title })\n return {\n url: data.url || \"\",\n title: data.title || \"\",\n selectedElements: data.selectedElements\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n const errorName = error instanceof Error ? error.name : 'UnknownError'\n log.error('Failed to get context', { \n error: errorMessage,\n errorType: errorName,\n apiUrl \n })\n return null\n }\n }\n\n async function clearSelectedElements(): Promise<void> {\n try {\n log.debug('Clearing selected elements', { apiUrl })\n const response = await fetch(apiUrl, { method: 'DELETE' })\n log.debug('Clear response', { status: response.status })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n const errorName = error instanceof Error ? error.name : 'UnknownError'\n log.error('Failed to clear selected elements', { \n error: errorMessage,\n errorType: errorName,\n apiUrl \n })\n }\n }\n\n function formatSelectedElement(element: SelectedElement): string {\n const parts: string[] = []\n\n if (element.filePath) {\n let location = `文件: ${element.filePath}`\n if (element.line) {\n location += `:${element.line}`\n if (element.column) {\n location += `:${element.column}`\n }\n }\n parts.push(location)\n }\n\n if (element.innerText?.trim()) {\n const text = element.innerText.trim().substring(0, MAX_TEXT_LENGTH)\n const suffix = element.innerText.length > MAX_TEXT_LENGTH ? '...' : ''\n parts.push(`节点文本: \"${text}${suffix}\"`)\n }\n\n return parts.join('\\n') + '\\n'\n }\n\n function buildContextPrefix(context: PageContextData): string {\n let prefix = `我现在正在浏览项目中的这个页面:${context.url}\\n\\n`\n\n if (context.selectedElements?.length) {\n prefix += `我选中了以下节点:\\n\\n`\n context.selectedElements.forEach((element) => {\n prefix += formatSelectedElement(element) + \"\\n\"\n })\n }\n\n prefix += `我的请求:\\n`\n\n prefix += `\\n`\n return prefix\n }\n\n return {\n \"experimental.chat.messages.transform\": async (_input, output) => {\n log.debug('Message transform hook called')\n const context = await getPageContext()\n log.debug('Context data', { hasUrl: !!context?.url, hasElements: !!context?.selectedElements?.length })\n \n if (!context?.url) return\n\n const lastUserMsg = [...output.messages].reverse().find(m => m.info.role === \"user\")\n if (!lastUserMsg) return\n\n const textPart = lastUserMsg.parts.find(p => p.type === \"text\")\n if (!textPart || !(\"text\" in textPart)) return\n \n if (textPart.text.includes(CONTEXT_MARKER)) return\n\n const prefix = buildContextPrefix(context)\n textPart.text = prefix + textPart.text\n\n if (context.selectedElements?.length) {\n log.debug('Selected elements found, clearing...')\n await clearSelectedElements()\n }\n }\n }\n}\n\nexport default PageContextPlugin\n"],"names":["items","log"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"page-context.js","sources":["../../../src/constants.ts","../../../src/logger.ts","../../../src/opencode/plugins/page-context.ts"],"sourcesContent":["/**\n * @fileoverview OpenCode 插件常量定义\n */\n\n/** ==================== 网络相关 ==================== */\n\n/** 默认主机名 */\nexport const DEFAULT_HOSTNAME = \"127.0.0.1\";\n\n/** 默认 Web 服务端口 */\nexport const DEFAULT_WEB_PORT = 4097;\n\n/** 服务器启动超时时间(毫秒) */\nexport const SERVER_START_TIMEOUT = 300000;\n\n/** 服务器检查间隔(毫秒) */\nexport const SERVER_CHECK_INTERVAL = 100;\n\n/** ==================== 重试相关 ==================== */\n\n/** 默认重试次数 */\nexport const DEFAULT_RETRIES = 5;\n\n/** 重试延迟(毫秒) */\nexport const RETRY_DELAY = 500;\n\n/** ==================== 端口查找 ==================== */\n\n/** 最大端口尝试次数 */\nexport const MAX_PORT_TRIES = 10;\n\n/** ==================== 日志相关 ==================== */\n\n/** 插件日志前缀 */\nexport const LOG_PREFIX = \"[vite-plugin-opencode]\";\n\n/** OpenCode Web 日志前缀 */\nexport const WEB_LOG_PREFIX = \"[OpenCode Web]\";\n\n/** OpenCode Web 错误日志前缀 */\nexport const WEB_ERROR_PREFIX = \"[OpenCode Web Error]\";\n\n/** ==================== 挂件相关 ==================== */\n\n/** 挂件脚本路径 */\nexport const WIDGET_SCRIPT_PATH = \"/__opencode_widget__.js\";\n\n/** 配置数据属性名 */\nexport const CONFIG_DATA_ATTR = \"data-opencode-config\";\n\n/** 上下文 API 路径 */\nexport const CONTEXT_API_PATH = \"/__opencode_context__\";\n\n/** 启动 API 路径 */\nexport const START_API_PATH = \"/__opencode_start__\";\n\n/** 会话列表 API 路径 */\nexport const SESSIONS_API_PATH = \"/__opencode_sessions__\";\n\n/** SSE 事件流路径 */\nexport const SSE_EVENTS_PATH = \"/__opencode_events__\";\n\n/** 上下文更新间隔(毫秒) */\nexport const CONTEXT_UPDATE_INTERVAL = 500;\n\n/** 服务器同步间隔(毫秒) */\nexport const SERVER_SYNC_INTERVAL = 2000;\n\n/** Vue Inspector 检查间隔(毫秒) */\nexport const INSPECTOR_CHECK_INTERVAL = 500;\n\n/** 自动打开延迟(毫秒) */\nexport const AUTO_OPEN_DELAY = 1000;\n\n/** 通知显示时间(毫秒) */\nexport const NOTIFICATION_DURATION = 3000;\n\n/** ==================== 存储相关 ==================== */\n\n/** 初始化标记 */\nexport const INIT_MARKER = \"__OPENCODE_INITIALIZED__\";\n\n/** 选中元素存储键 */\nexport const SELECTED_ELEMENTS_KEY = \"__opencode_selected_elements__\";\n\n/** ==================== 文本处理 ==================== */\n\n/** 元素文本最大显示长度 */\nexport const MAX_TEXT_LENGTH = 100;\n\n/** 元素上下文标记 */\nexport const CONTEXT_MARKER = \"[元素上下文]\";\n\n/** ==================== 默认配置 ==================== */\n\n/** 默认插件配置 */\nexport const DEFAULT_CONFIG = {\n enabled: false,\n webPort: DEFAULT_WEB_PORT,\n hostname: DEFAULT_HOSTNAME,\n position: \"bottom-right\" as const,\n theme: \"auto\" as const,\n open: false,\n autoReload: true,\n verbose: false,\n hotkey: \"ctrl+k\",\n warmupChromeMcp: true,\n};\n","import { LOG_PREFIX } from './constants.js'\n\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n NONE = 4,\n}\n\nexport interface LogContext {\n module?: string\n operation?: string\n traceId?: string\n duration?: number\n error?: Error | unknown\n [key: string]: unknown\n}\n\ninterface LoggerConfig {\n verbose: boolean\n level: LogLevel\n showTimestamp: boolean\n showCaller: boolean\n showTrace: boolean\n indent: string\n}\n\nconst COLORS = {\n reset: '\\x1b[0m',\n dim: '\\x1b[2m',\n bright: '\\x1b[1m',\n red: '\\x1b[31m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n blue: '\\x1b[34m',\n magenta: '\\x1b[35m',\n cyan: '\\x1b[36m',\n white: '\\x1b[37m',\n}\n\nconst LEVEL_COLORS: Record<LogLevel, string> = {\n [LogLevel.DEBUG]: COLORS.cyan,\n [LogLevel.INFO]: COLORS.green,\n [LogLevel.WARN]: COLORS.yellow,\n [LogLevel.ERROR]: COLORS.red,\n [LogLevel.NONE]: COLORS.reset,\n}\n\nconst LEVEL_NAMES: Record<LogLevel, string> = {\n [LogLevel.DEBUG]: 'DEBUG',\n [LogLevel.INFO]: 'INFO',\n [LogLevel.WARN]: 'WARN',\n [LogLevel.ERROR]: 'ERROR',\n [LogLevel.NONE]: 'NONE',\n}\n\nlet globalConfig: LoggerConfig = {\n verbose: false,\n level: LogLevel.INFO,\n showTimestamp: true,\n showCaller: true,\n showTrace: false,\n indent: ' ',\n}\n\nlet traceCounter = 0\n\nexport function configureLogger(options: Partial<LoggerConfig>): void {\n globalConfig = { ...globalConfig, ...options }\n}\n\nexport function setVerbose(verbose: boolean): void {\n globalConfig.verbose = verbose\n globalConfig.level = verbose ? LogLevel.DEBUG : LogLevel.INFO\n}\n\nfunction getTimestamp(): string {\n const now = new Date()\n const hours = String(now.getHours()).padStart(2, '0')\n const minutes = String(now.getMinutes()).padStart(2, '0')\n const seconds = String(now.getSeconds()).padStart(2, '0')\n const ms = String(now.getMilliseconds()).padStart(3, '0')\n return `${hours}:${minutes}:${seconds}.${ms}`\n}\n\nfunction getCallerInfo(depth: number = 3): string {\n const stack = new Error().stack\n if (!stack) return ''\n\n const lines = stack.split('\\n')\n const targetLine = lines[depth]\n if (!targetLine) return ''\n\n const match = targetLine.match(/at\\s+(?:(.+?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?/)\n if (!match) return ''\n\n const [, funcName, filePath, line] = match\n const fileName = filePath.split('/').pop() || filePath\n const func = funcName || '<anonymous>'\n return `${fileName}:${line} ${func}`\n}\n\nfunction formatValue(value: unknown, depth: number = 0): string {\n if (depth > 3) return '...'\n \n if (value === null) return 'null'\n if (value === undefined) return 'undefined'\n if (typeof value === 'string') return depth > 0 ? `\"${value}\"` : value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n if (value instanceof Error) {\n return `${value.name}: ${value.message}${value.stack ? `\\n${value.stack}` : ''}`\n }\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]'\n if (value.length > 5) {\n const items = value.slice(0, 3).map(v => formatValue(v, depth + 1))\n return `[${items.join(', ')}, ... ${value.length - 3} more items]`\n }\n const items = value.map(v => formatValue(v, depth + 1))\n return `[${items.join(', ')}]`\n }\n if (typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n if (entries.length === 0) return '{}'\n if (entries.length > 5) {\n const shown = entries.slice(0, 3).map(([k, v]) => `${k}: ${formatValue(v, depth + 1)}`)\n return `{${shown.join(', ')}, ... ${entries.length - 3} more keys}`\n }\n const formatted = entries.map(([k, v]) => `${k}: ${formatValue(v, depth + 1)}`)\n return `{${formatted.join(', ')}}`\n }\n return String(value)\n}\n\nfunction formatContext(context?: LogContext): string {\n if (!context || Object.keys(context).length === 0) return ''\n \n const parts: string[] = []\n \n if (context.module) parts.push(`[${context.module}]`)\n if (context.operation) parts.push(`(${context.operation})`)\n if (context.traceId) parts.push(`trace:${context.traceId}`)\n if (context.duration !== undefined) parts.push(`${context.duration}ms`)\n \n const extraKeys = Object.keys(context).filter(\n k => !['module', 'operation', 'traceId', 'duration', 'error'].includes(k)\n )\n if (extraKeys.length > 0) {\n const extra: Record<string, unknown> = {}\n extraKeys.forEach(k => extra[k] = context[k])\n parts.push(formatValue(extra))\n }\n \n return parts.join(' ')\n}\n\nfunction log(level: LogLevel, message: string, context?: LogContext, ...args: unknown[]): void {\n if (level < globalConfig.level) return\n \n const parts: string[] = []\n \n parts.push(`${COLORS.dim}[${process.pid}]${COLORS.reset}`)\n \n if (globalConfig.showTimestamp) {\n parts.push(`${COLORS.dim}${getTimestamp()}${COLORS.reset}`)\n }\n \n const levelColor = LEVEL_COLORS[level]\n const levelName = LEVEL_NAMES[level].padEnd(5)\n parts.push(`${levelColor}${levelName}${COLORS.reset}`)\n \n parts.push(`${COLORS.bright}${LOG_PREFIX}${COLORS.reset}`)\n \n const contextStr = formatContext(context)\n if (contextStr) {\n parts.push(`${COLORS.magenta}${contextStr}${COLORS.reset}`)\n }\n \n parts.push(message)\n \n if (globalConfig.showCaller && level >= LogLevel.WARN) {\n const caller = getCallerInfo(4)\n if (caller) {\n parts.push(`${COLORS.dim}(${caller})${COLORS.reset}`)\n }\n }\n \n const formattedArgs = args.map(a => formatValue(a)).join(' ')\n if (formattedArgs) {\n parts.push(formattedArgs)\n }\n \n const output = parts.join(' ')\n \n if (level >= LogLevel.ERROR) {\n console.error(output)\n } else if (level === LogLevel.WARN) {\n console.warn(output)\n } else {\n console.log(output)\n }\n \n if (context?.error && level >= LogLevel.ERROR && globalConfig.showTrace) {\n const err = context.error\n if (err instanceof Error && err.stack) {\n console.error(`${COLORS.dim}${err.stack}${COLORS.reset}`)\n }\n }\n}\n\nexport const logger = {\n debug(message: string, context?: LogContext, ...args: unknown[]): void {\n log(LogLevel.DEBUG, message, context, ...args)\n },\n \n info(message: string, context?: LogContext, ...args: unknown[]): void {\n log(LogLevel.INFO, message, context, ...args)\n },\n \n warn(message: string, context?: LogContext, ...args: unknown[]): void {\n log(LogLevel.WARN, message, context, ...args)\n },\n \n error(message: string, context?: LogContext, ...args: unknown[]): void {\n log(LogLevel.ERROR, message, context, ...args)\n },\n \n group(label: string, context?: LogContext): void {\n if (!globalConfig.verbose) return\n const contextStr = formatContext(context)\n console.log(`${COLORS.dim}[${process.pid}]${COLORS.reset} ${COLORS.bright}${LOG_PREFIX}${COLORS.reset} ${COLORS.blue}▼${COLORS.reset} ${label}${contextStr ? ` ${contextStr}` : ''}`)\n },\n \n groupEnd(): void {\n if (!globalConfig.verbose) return\n },\n}\n\nexport function generateTraceId(): string {\n traceCounter++\n const timestamp = Date.now().toString(36)\n const counter = traceCounter.toString(36).padStart(4, '0')\n return `${timestamp}-${counter}`\n}\n\nexport class PerformanceTimer {\n private startTime: number\n private context: LogContext\n private operation: string\n \n constructor(operation: string, context?: LogContext) {\n this.operation = operation\n this.context = context || {}\n this.startTime = performance.now()\n \n logger.debug(`⏱️ Starting: ${operation}`, this.context)\n }\n \n end(message?: string): number {\n const duration = Math.round(performance.now() - this.startTime)\n const msg = message || `✓ Completed: ${this.operation}`\n logger.debug(msg, { ...this.context, duration })\n return duration\n }\n \n checkpoint(label: string): number {\n const elapsed = Math.round(performance.now() - this.startTime)\n logger.debug(` ↳ ${label}`, { ...this.context, duration: elapsed })\n return elapsed\n }\n}\n\nexport class RequestContext {\n traceId: string\n method: string\n path: string\n startTime: number\n private checkpoints: Array<{ time: number; label: string }> = []\n \n constructor(method: string, path: string) {\n this.traceId = generateTraceId()\n this.method = method\n this.path = path\n this.startTime = performance.now()\n \n logger.debug(`→ ${method} ${path}`, { traceId: this.traceId, module: 'HTTP' })\n }\n \n checkpoint(label: string): void {\n const elapsed = Math.round(performance.now() - this.startTime)\n this.checkpoints.push({ time: elapsed, label })\n logger.debug(` → ${label}`, { traceId: this.traceId, duration: elapsed })\n }\n \n end(statusCode: number): void {\n const duration = Math.round(performance.now() - this.startTime)\n const statusColor = statusCode < 400 ? COLORS.green : COLORS.red\n logger.debug(`← ${this.method} ${this.path} ${statusColor}${statusCode}${COLORS.reset}`, {\n traceId: this.traceId,\n duration,\n checkpoints: this.checkpoints.length,\n })\n }\n \n error(error: Error | unknown): void {\n const duration = Math.round(performance.now() - this.startTime)\n logger.error(`✗ ${this.method} ${this.path}`, {\n traceId: this.traceId,\n duration,\n error,\n })\n }\n}\n\nexport function createLogger(module: string) {\n return {\n debug(message: string, context?: Omit<LogContext, 'module'>, ...args: unknown[]): void {\n logger.debug(message, { ...context, module }, ...args)\n },\n \n info(message: string, context?: Omit<LogContext, 'module'>, ...args: unknown[]): void {\n logger.info(message, { ...context, module }, ...args)\n },\n \n warn(message: string, context?: Omit<LogContext, 'module'>, ...args: unknown[]): void {\n logger.warn(message, { ...context, module }, ...args)\n },\n \n error(message: string, context?: Omit<LogContext, 'module'>, ...args: unknown[]): void {\n logger.error(message, { ...context, module }, ...args)\n },\n \n timer(operation: string, context?: Omit<LogContext, 'module'>): PerformanceTimer {\n return new PerformanceTimer(operation, { ...context, module })\n },\n }\n}\n\nexport function logMethod(\n target: unknown,\n propertyKey: string,\n descriptor: PropertyDescriptor\n): PropertyDescriptor {\n const originalMethod = descriptor.value\n const className = (target as { constructor: { name: string } }).constructor.name\n \n descriptor.value = async function (...args: unknown[]) {\n const timer = new PerformanceTimer(`${className}.${propertyKey}`)\n try {\n const result = await originalMethod.apply(this, args)\n timer.end()\n return result\n } catch (error) {\n timer.end('❌ Failed')\n throw error\n }\n }\n \n return descriptor\n}\n\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0B'\n const k = 1024\n const sizes = ['B', 'KB', 'MB', 'GB']\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))}${sizes[i]}`\n}\n\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`\n if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`\n const minutes = Math.floor(ms / 60000)\n const seconds = Math.round((ms % 60000) / 1000)\n return `${minutes}m ${seconds}s`\n}\n","/**\n * @fileoverview OpenCode 页面上下文插件\n * @description 用于将页面上下文信息注入到 AI 对话中\n */\n\nimport type { Plugin, Hooks } from \"@opencode-ai/plugin\";\nimport { createLogger } from \"../../logger.js\";\n\nconst MAX_TEXT_LENGTH = 10000;\n\nconst CONTEXT_MARKER = \"__OPENCODE_CONTEXT__\";\n\nconst log = createLogger(\"OpenCodePluginPageContext\");\n\ninterface SelectedElement {\n filePath: string | null;\n line: number | null;\n column: number | null;\n innerText: string;\n}\n\ninterface PageContextData {\n url: string;\n title: string;\n selectedElements?: SelectedElement[];\n}\n\nexport const PageContextPlugin: Plugin = async (): Promise<Hooks> => {\n log.info(\"PageContextPlugin loading...\");\n\n const contextApiUrl = process.env.OPENCODE_CONTEXT_API_URL;\n log.debug(\"Context API URL:\", { contextApiUrl });\n\n if (!contextApiUrl) {\n log.warn(\n \"OPENCODE_CONTEXT_API_URL is not set, page context plugin will not work\",\n );\n return {};\n }\n\n const apiUrl = contextApiUrl as string;\n log.info(\"Plugin initialized successfully\");\n\n async function getPageContext(): Promise<PageContextData | null> {\n try {\n log.debug(\"Fetching context...\", { apiUrl });\n const response = await fetch(apiUrl);\n\n if (!response.ok) {\n log.error(\"Context API returned error status\", {\n status: response.status,\n statusText: response.statusText,\n apiUrl,\n });\n return null;\n }\n\n const data = (await response.json()) as PageContextData;\n log.debug(\"Context received\", { url: data.url, title: data.title });\n return {\n url: data.url || \"\",\n title: data.title || \"\",\n selectedElements: data.selectedElements,\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const errorName = error instanceof Error ? error.name : \"UnknownError\";\n log.error(\"Failed to get context\", {\n error: errorMessage,\n errorType: errorName,\n apiUrl,\n });\n return null;\n }\n }\n\n async function clearSelectedElements(): Promise<void> {\n try {\n log.debug(\"Clearing selected elements\", { apiUrl });\n const response = await fetch(apiUrl, { method: \"DELETE\" });\n log.debug(\"Clear response\", { status: response.status });\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const errorName = error instanceof Error ? error.name : \"UnknownError\";\n log.error(\"Failed to clear selected elements\", {\n error: errorMessage,\n errorType: errorName,\n apiUrl,\n });\n }\n }\n\n function formatSelectedElement(\n element: SelectedElement,\n index: number,\n ): string {\n const parts: string[] = [];\n\n parts.push(`### 选中节点 ${index + 1}`);\n\n if (element.filePath) {\n let location = element.filePath;\n if (element.line) {\n location += `:${element.line}`;\n if (element.column) {\n location += `:${element.column}`;\n }\n }\n parts.push(`- **文件位置**: \\`${location}\\``);\n }\n\n if (element.innerText?.trim()) {\n const text = element.innerText.trim().substring(0, MAX_TEXT_LENGTH);\n const suffix =\n element.innerText.length > MAX_TEXT_LENGTH\n ? \"\\n... (已省略部分内容)\"\n : \"\";\n parts.push(`- **节点文本**:\\n\\`\\`\\`text\\n${text}${suffix}\\n\\`\\`\\``);\n }\n\n return parts.join(\"\\n\") + \"\\n\";\n }\n\n function buildContextPrefix(context: PageContextData): string {\n const pageLink = context.title\n ? `[${context.title}](${context.url})`\n : context.url;\n let prefix = `【系统提示:以下是用户当前正在浏览的页面上下文,请将其作为最高优先级的背景信息来理解和响应用户的请求。】\\n\\n`;\n prefix += `用户现在正在浏览项目中的这个页面:${pageLink}\\n\\n`;\n\n if (context.selectedElements?.length) {\n prefix += `用户选中了以下节点:\\n\\n`;\n context.selectedElements.forEach((element, index) => {\n prefix += formatSelectedElement(element, index) + \"\\n\";\n });\n }\n\n prefix += `---\\n**用户的请求**:\\n\\n`;\n return prefix;\n }\n\n return {\n \"experimental.chat.system.transform\": async (_input, output) => {\n log.debug(\"System transform hook called\");\n\n const systemPrompt = `\n你是一个专业的前端开发助手,当前正集成在用户的 Vite 项目中(通过 vite-plugin-opencode-assistant)。\n在对话中,用户可能会自动附加他们当前正在浏览的页面上下文(包括页面 URL、标题以及在页面上选中的 DOM 节点信息)。\n\n处理这些上下文时,请遵循以下规则:\n1. **理解上下文**:当看到“我现在正在浏览项目中的这个页面”或“选中节点”等信息时,请将其作为用户请求的背景。\n2. **利用文件路径**:如果提供的节点信息中包含 \\`文件位置\\`,这通常对应于项目中的源代码文件。你可以直接分析或建议修改这些文件。\n3. **精准定位**:结合节点的 \\`节点文本\\` 和 \\`文件位置\\`,帮助用户快速定位问题或实现功能。\n4. **直接给出方案**:针对用户的实际请求,直接给出清晰、可执行的代码修改建议或解释,避免不必要的废话。\n`.trim();\n\n output.system.push(systemPrompt);\n },\n \"experimental.chat.messages.transform\": async (_input, output) => {\n log.debug(\"Message transform hook called\");\n const context = await getPageContext();\n log.debug(\"Context data\", {\n hasUrl: !!context?.url,\n hasElements: !!context?.selectedElements?.length,\n });\n\n if (!context?.url) return;\n\n const lastUserMsg = [...output.messages]\n .reverse()\n .find((m) => m.info.role === \"user\");\n if (!lastUserMsg) return;\n\n const textPart = lastUserMsg.parts.find((p) => p.type === \"text\");\n if (!textPart || !(\"text\" in textPart)) return;\n\n if (textPart.text.includes(CONTEXT_MARKER)) return;\n\n const prefix = buildContextPrefix(context);\n textPart.text = prefix + textPart.text;\n\n if (context.selectedElements?.length) {\n log.debug(\"Selected elements found, clearing...\");\n await clearSelectedElements();\n }\n },\n };\n};\n\nexport default PageContextPlugin;\n"],"names":["items","log"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -9,9 +9,9 @@ export interface OpenCodeOptions {
|
|
|
9
9
|
/** 服务主机名,默认 '127.0.0.1' */
|
|
10
10
|
hostname?: string;
|
|
11
11
|
/** 挂件位置,默认 'bottom-right' */
|
|
12
|
-
position?:
|
|
12
|
+
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
13
13
|
/** 主题模式,默认 'auto' */
|
|
14
|
-
theme?:
|
|
14
|
+
theme?: "light" | "dark" | "auto";
|
|
15
15
|
/** 是否自动打开面板,默认 false */
|
|
16
16
|
open?: boolean;
|
|
17
17
|
/** 是否自动重载,默认 true */
|
|
@@ -20,6 +20,8 @@ export interface OpenCodeOptions {
|
|
|
20
20
|
verbose?: boolean;
|
|
21
21
|
/** 快捷键配置,默认 'ctrl+k' */
|
|
22
22
|
hotkey?: string;
|
|
23
|
+
/** 服务启动后是否立即预热 Chrome MCP,默认 true */
|
|
24
|
+
warmupChromeMcp?: boolean;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* OpenCode Web 服务启动选项
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * OpenCode Vite 插件配置选项\n */\nexport interface OpenCodeOptions {\n /** 是否启用插件,默认 true */\n enabled?: boolean
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * OpenCode Vite 插件配置选项\n */\nexport interface OpenCodeOptions {\n /** 是否启用插件,默认 true */\n enabled?: boolean;\n /** Web 服务端口,默认 4097 */\n webPort?: number;\n /** 服务主机名,默认 '127.0.0.1' */\n hostname?: string;\n /** 挂件位置,默认 'bottom-right' */\n position?: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\";\n /** 主题模式,默认 'auto' */\n theme?: \"light\" | \"dark\" | \"auto\";\n /** 是否自动打开面板,默认 false */\n open?: boolean;\n /** 是否自动重载,默认 true */\n autoReload?: boolean;\n /** 是否输出详细日志,默认 false */\n verbose?: boolean;\n /** 快捷键配置,默认 'ctrl+k' */\n hotkey?: string;\n /** 服务启动后是否立即预热 Chrome MCP,默认 true */\n warmupChromeMcp?: boolean;\n}\n\n/**\n * OpenCode Web 服务启动选项\n */\nexport interface WebOptions {\n /** 服务端口 */\n port: number;\n /** 服务主机名 */\n hostname: string;\n /** 服务器 URL */\n serverUrl: string;\n /** 工作目录 */\n cwd: string;\n /** 配置目录路径 */\n configDir?: string;\n /** CORS 允许的源 */\n corsOrigins?: string[];\n /** 上下文 API URL */\n contextApiUrl?: string;\n}\n\n/**\n * 挂件注入配置选项\n */\nexport interface WidgetOptions {\n /** Web 服务 URL */\n webUrl: string;\n /** 服务器 URL */\n serverUrl: string;\n /** 挂件位置 */\n position: string;\n /** 主题模式 */\n theme: string;\n /** 是否自动打开 */\n open: boolean;\n /** 是否自动重载 */\n autoReload: boolean;\n /** 工作目录 */\n cwd: string;\n /** 会话 URL */\n sessionUrl?: string;\n /** 快捷键配置 */\n hotkey?: string;\n}\n\n/**\n * OpenCode 会话信息\n */\nexport interface SessionInfo {\n /** 会话 ID */\n id: string;\n /** 会话标识符 */\n slug: string;\n /** 项目 ID */\n projectID: string;\n /** 项目目录 */\n directory: string;\n /** 会话标题 */\n title: string;\n /** 版本号 */\n version: string;\n /** 代码变更统计 */\n summary: {\n /** 新增行数 */\n additions: number;\n /** 删除行数 */\n deletions: number;\n /** 修改文件数 */\n files: number;\n };\n /** 时间信息 */\n time: {\n /** 创建时间戳 */\n created: number;\n /** 更新时间戳 */\n updated: number;\n };\n}\n\n/**\n * 选中的元素信息\n */\nexport interface SelectedElement {\n /** 文件路径 */\n filePath: string | null;\n /** 行号 */\n line: number | null;\n /** 列号 */\n column: number | null;\n /** 元素内部文本 */\n innerText: string;\n /** 元素描述(标签名+选择器) */\n description?: string;\n}\n\n/**\n * 页面上下文数据\n */\nexport interface PageContext {\n /** 当前页面 URL */\n url: string;\n /** 当前页面标题 */\n title: string;\n /** 选中的元素列表 */\n selectedElements?: SelectedElement[];\n}\n"]}
|