From 02f3ded256e43130c3b4d88e869d018208cd9cb8 Mon Sep 17 00:00:00 2001 From: ypshidifu Date: Sun, 20 Sep 2026 23:35:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=EF=BC=9Aoc-session-all=20-=20=E6=98=BE=E7=A4=BA=E6=89=80?= =?UTF-8?q?=E6=9C=89=E4=BC=9A=E8=AF=9D=E8=AE=B0=E5=BD=95=EF=BC=8C=E7=BB=95?= =?UTF-8?q?=E8=BF=87=E7=9B=AE=E5=BD=95=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 96 +++++++++++++++++++++++++++++++ package.json | 20 +++++++ src/index.ts | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 273 insertions(+) create mode 100644 README.md create mode 100644 package.json create mode 100644 src/index.ts diff --git a/README.md b/README.md new file mode 100644 index 0000000..18d38f7 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# oc-session-all + +OpenCode 插件:在任意目录下显示所有会话记录,绕过目录过滤限制。 + +## 功能 + +- 拦截 `/session` 命令,显示所有项目的会话记录,而不仅仅是当前目录的 +- 提供 `/sessions-all` 命令作为替代方案 +- 按更新时间倒序显示会话列表 + +## 安装 + +### 方法 1:全局安装(推荐) + +1. 将 `src/index.ts` 复制到全局插件目录: + ```bash + # Windows + mkdir -p %USERPROFILE%\.config\opencode\plugins + copy src\index.ts %USERPROFILE%\.config\opencode\plugins\oc-session-all.ts + + # Linux/macOS + mkdir -p ~/.config/opencode/plugins + cp src/index.ts ~/.config/opencode/plugins/oc-session-all.ts + ``` + +2. 重启 OpenCode + +### 方法 2:项目级安装 + +1. 将 `src/index.ts` 复制到项目目录: + ```bash + mkdir -p .opencode/plugins + cp src/index.ts .opencode/plugins/oc-session-all.ts + ``` + +2. 重启 OpenCode + +## 使用方法 + +### 方法 1:拦截 /session 命令 + +插件会自动拦截 `/session` 命令,显示所有会话记录。 + +### 方法 2:使用新命令 + +运行 `/sessions-all` 命令来显示所有会话记录。 + +## 输出示例 + +``` +=== All Sessions (across all projects) === + +ID: ses_12af0b69cffeAMR3VcfAdp94Hi +Title: OpenCode WSL 迁移 +Directory: C:/Users/ypshidifu +Created: 2026/9/20 10:00:00 +Updated: 2026/9/20 15:30:00 +--- +ID: ses_12aeb915effez1zEjjIqf6yl1c +Title: Session 2 +Directory: D:/桌面/源码/mc_plugn/windowmsgbot +Created: 2026/9/20 09:00:00 +Updated: 2026/9/20 14:00:00 +--- + +Total: 68 sessions +``` + +## 工作原理 + +插件通过两种方式获取所有会话: + +1. **OpenCode Client API**:尝试使用 OpenCode 的客户端 API 获取会话列表 +2. **数据库直接查询**:如果 API 不可用,直接查询 SQLite 数据库 + +## 配置 + +插件无需额外配置,安装后即可使用。 + +## 故障排除 + +如果插件不工作: + +1. 确保插件文件已正确放置在插件目录 +2. 重启 OpenCode +3. 检查控制台日志是否有错误信息 +4. 确保 SQLite3 命令行工具可用(用于数据库查询回退方案) + +## 依赖 + +- OpenCode 1.18.11 或更新版本 +- SQLite3 命令行工具(可选,用于数据库查询回退方案) + +## 许可证 + +MIT \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..3a87c47 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "oc-session-all", + "version": "1.0.0", + "description": "OpenCode plugin to show all sessions regardless of current directory", + "main": "src/index.ts", + "type": "module", + "scripts": { + "build": "tsc", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": ["opencode", "plugin", "session"], + "author": "", + "license": "MIT", + "dependencies": { + "@opencode-ai/plugin": "latest" + }, + "devDependencies": { + "typescript": "^5.0.0" + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..da2079e --- /dev/null +++ b/src/index.ts @@ -0,0 +1,157 @@ +/** + * OpenCode Plugin: Show All Sessions + * + * This plugin provides /sessions-all command to show all sessions + * regardless of the current working directory. + */ + +import { tool } from "@opencode-ai/plugin"; +import { exec } from "child_process"; +import { promisify } from "util"; +import { join } from "path"; +import { existsSync } from "fs"; + +const execAsync = promisify(exec); + +interface Session { + id: string; + project_id: string; + directory: string; + title: string | null; + time_created: number; + time_updated: number; + cost: number; +} + +/** + * Get the path to the opencode database + */ +function getDbPath(): string { + const homeDir = process.env.HOME || process.env.USERPROFILE || ""; + + // Windows path + const winPath = join(homeDir, "AppData", "Roaming", "opencode", "opencode.db"); + if (existsSync(winPath)) return winPath; + + // Linux/macOS path + const unixPath = join(homeDir, ".local", "share", "opencode", "opencode.db"); + if (existsSync(unixPath)) return unixPath; + + // Fallback to Windows AppData + return winPath; +} + +/** + * Query all sessions from the database + */ +async function getAllSessionsFromDb(): Promise { + try { + const dbPath = getDbPath(); + console.log(`[oc-session-all] Database path: ${dbPath}`); + + const query = ` + SELECT id, project_id, directory, title, time_created, time_updated, cost + FROM session_v2 + ORDER BY time_updated DESC; + `; + + const { stdout } = await execAsync(`sqlite3 "${dbPath}" "${query}"`); + + if (!stdout.trim()) { + return []; + } + + const sessions: Session[] = []; + const lines = stdout.trim().split("\n"); + + for (const line of lines) { + const parts = line.split("|"); + if (parts.length >= 7) { + sessions.push({ + id: parts[0], + project_id: parts[1], + directory: parts[2], + title: parts[3] || null, + time_created: parseInt(parts[4]) || 0, + time_updated: parseInt(parts[5]) || 0, + cost: parseFloat(parts[6]) || 0, + }); + } + } + + return sessions; + } catch (error) { + console.error("[oc-session-all] Failed to query database:", error); + return []; + } +} + +/** + * Format a timestamp to local string + */ +function formatTime(timestamp: number): string { + if (!timestamp) return "Unknown"; + return new Date(timestamp * 1000).toLocaleString("zh-CN"); +} + +/** + * Format cost to display string + */ +function formatCost(cost: number): string { + if (!cost) return "Free"; + return `$${cost.toFixed(4)}`; +} + +export const OcSessionAll = async ({ project, client, $ }: any) => { + console.log("[oc-session-all] Plugin loaded"); + + return { + tool: { + "sessions-all": tool({ + description: "显示所有项目的会话记录,绕过目录过滤限制。Show all sessions from all projects.", + args: {}, + async execute() { + const sessions = await getAllSessionsFromDb(); + + if (!sessions || sessions.length === 0) { + return "没有找到任何会话记录。No sessions found."; + } + + let output = "=== 所有会话记录 (All Sessions) ===\n\n"; + + // Group sessions by project + const byProject = new Map(); + for (const session of sessions) { + const key = session.project_id || "unknown"; + if (!byProject.has(key)) { + byProject.set(key, []); + } + byProject.get(key)!.push(session); + } + + // Display sessions + let index = 1; + for (const session of sessions) { + const created = formatTime(session.time_created); + const updated = formatTime(session.time_updated); + const cost = formatCost(session.cost); + + output += `${index}. ${session.title || "Untitled"}\n`; + output += ` ID: ${session.id}\n`; + output += ` 目录: ${session.directory}\n`; + output += ` 创建: ${created} | 更新: ${updated}\n`; + output += ` 费用: ${cost}\n\n`; + + index++; + } + + output += `共 ${sessions.length} 个会话`; + + return output; + } + }) + } + }; +}; + +export default OcSessionAll; \ No newline at end of file