初始化插件:oc-session-all - 显示所有会话记录,绕过目录过滤
This commit is contained in:
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
+157
@@ -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<Session[]> {
|
||||
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<string, Session[]>();
|
||||
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;
|
||||
Reference in New Issue
Block a user