用Trae编辑器写一个Trae的AI对话记录导出脚本

前言

最近在尝试使用字节出的 Trae AI编辑器,并且想根据对话记录写一些文章分享,但发现一条完整的对话记录这玩意跟 Cursor 一样,是没有办法导出的。靠截图?放在文章里用户体验极差!不成

契机

前两天找到一位佬写的 Cursor 记录导出插件 , 插件方面不太可能是通用的,但是思路嘛,还是可以借鉴的。

在插件的文档中,我发现了 Cursor 工作区的存储位置,这里面存储了所有的对话记录和其他信息。

由于我使用的是Macos,所以后面的演示都以 Macos 为例。当然,Windows 其实也可以按照同样的路径去找找看。

bash 复制代码
~/Library/Application Support/Cursor/User/workspaceStorage

我们将工作区位置路径中的 Cursor 替换成 Trae 试试看,能不能进去

bash 复制代码
cd Library/Application\ Support/Trae/User/workspaceStorage

没错,是可以进去的

通过 open . 打开文件夹

发现是一堆文件夹,不慌,点击最新修改的一个文件夹进去看看

分析

1.打开 workspace.json 文件看看,内容如下

json 复制代码
{
  "folder": "file:///Users/peanut/Desktop/work/ai_all/trae_test"
}

只存了一个项目的路径,Pass

2.打开kiss开头的文件夹,发现里头是一堆图片,看了下是正在编辑的这篇文档中图片的缓存,Pass

3.那只有 state.vscdb 这个文件了,没见过这个文件格式,但猜测是 vscode 的数据库文件。

AI 介入

来,用 Trae AI 介入,问问看

注: 以下 AI 问答记录 就是基于 Trae AI 问答给出的 脚本 运行后导出的对话内容(听起来很绕是吧-_-)
和AI的详细对话内容 - 展开查看

用户 (2025/2/18 16:39:52)

你知道.vscdb格式文件吗?


助手 (2025/2/18 16:39:52)

是的,我了解 .vscdb 文件。这是 Visual Studio Code 使用的数据库文件格式,通常用于存储 VS Code 的各种状态和配置信息。具体来说:

  1. 文件格式:
  • 它是一个 SQLite 数据库文件
  • 使用二进制格式存储
  • 通常位于 VS Code 的用户数据目录中
  1. 主要用途:
  • 存储 VS Code 的状态信息
  • 保存工作区历史记录
  • 存储一些扩展的数据
  • 缓存某些配置信息
  1. 查看方式:
  • 可以使用任何支持 SQLite 的数据库查看工具打开
  • 常用工具包括:
    • SQLite Browser
    • DBeaver
    • VS Code 的 SQLite 扩展
  1. 注意事项:
  • 这些文件通常不需要手动修改
  • VS Code 运行时会自动管理这些文件
  • 如果文件损坏可能会影响 VS Code 的某些功能

如果你需要查看这类文件的内容,我建议使用 SQLite 浏览器工具,这样可以安全地查看而不会意外损坏文件。


用户 (2025/2/18 16:40:42)

如何解析.vscdb这个文件格式的内容为文本呢?


助手 (2025/2/18 16:40:42)

我可以帮你写一个 Python 脚本来解析 .vscdb 文件。这个脚本使用 SQLite3 模块来读取和显示文件内容。

python:/Users/peanut/Desktop/work/ai_all/trae_test/parse_vscdb.py 复制代码
import sqlite3
import json

def parse_vscdb(file_path):
    try:
        # 连接到 SQLite 数据库
        conn = sqlite3.connect(file_path)
        cursor = conn.cursor()

        # 获取所有表名
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
        tables = cursor.fetchall()

        result = {}
        
        # 遍历每个表并获取内容
        for table in tables:
            table_name = table[0]
            cursor.execute(f"SELECT * FROM {table_name}")
            columns = [description[0] for description in cursor.description]
            rows = cursor.fetchall()
            
            # 将结果转换为字典格式
            table_data = []
            for row in rows:
                row_dict = {}
                for i, column in enumerate(columns):
                    # 尝试解析 JSON 字符串
                    if isinstance(row[i], str):
                        try:
                            row_dict[column] = json.loads(row[i])
                        except json.JSONDecodeError:
                            row_dict[column] = row[i]
                    else:
                        row_dict[column] = row[i]
                table_data.append(row_dict)
            
            result[table_name] = table_data

        conn.close()
        return result

    except sqlite3.Error as e:
        print(f"SQLite 错误: {e}")
        return None
    except Exception as e:
        print(f"发生错误: {e}")
        return None

if __name__ == "__main__":
    # 使用示例
    vscdb_path = "你的.vscdb文件路径"  # 替换为实际的文件路径
    data = parse_vscdb(vscdb_path)
    
    if data:
        # 格式化输出结果
        for table_name, table_data in data.items():
            print(f"\n表名: {table_name}")
            print("-" * 50)
            for row in table_data:
                print(json.dumps(row, indent=2, ensure_ascii=False))
            print("-" * 50)

使用方法:

  1. 将上面的代码保存为 Python 文件
  2. 修改 vscdb_path 为你要解析的 .vscdb 文件的实际路径
  3. 运行脚本

这个脚本会:

  1. 连接到 .vscdb 文件
  2. 获取所有表的内容
  3. 尝试解析可能的 JSON 格式内容
  4. 以易读的格式输出所有数据

如果你想要在命令行中直接运行,可以这样使用:

bash 复制代码
python parse_vscdb.py

注意事项:

  1. 确保已安装 Python
  2. 运行脚本时确保 .vscdb 文件没有被其他程序占用
  3. 某些内容可能是二进制数据,会以原始格式显示
  4. 建议在解析大文件时先备份原文件

如果你需要针对特定表或内容进行解析,可以根据需要修改代码,添加过滤条件或特定的解析逻辑。


用户 (2025/2/18 16:41:08)

用nodejs可以解析吗?


助手 (2025/2/18 16:41:08)

是的,我们可以使用 Node.js 来解析 .vscdb 文件。下面是一个使用 better-sqlite3 模块的实现:

首先需要安装依赖:

bash 复制代码
npm install better-sqlite3

然后这是解析脚本:

javascript:/Users/peanut/Desktop/work/ai_all/trae_test/parse_vscdb.js 复制代码
const Database = require('better-sqlite3');

function parseVSCDB(filePath) {
    try {
        // 连接数据库
        const db = new Database(filePath, { readonly: true });

        // 获取所有表名
        const tables = db.prepare(`
            SELECT name 
            FROM sqlite_master 
            WHERE type='table'
        `).all();

        const result = {};

        // 遍历每个表并获取内容
        tables.forEach(table => {
            const tableName = table.name;
            const rows = db.prepare(`SELECT * FROM ${tableName}`).all();

            // 尝试解析每行中可能的 JSON 字符串
            const parsedRows = rows.map(row => {
                const parsedRow = {};
                Object.entries(row).forEach(([key, value]) => {
                    if (typeof value === 'string') {
                        try {
                            parsedRow[key] = JSON.parse(value);
                        } catch {
                            parsedRow[key] = value;
                        }
                    } else {
                        parsedRow[key] = value;
                    }
                });
                return parsedRow;
            });

            result[tableName] = parsedRows;
        });

        // 关闭数据库连接
        db.close();

        return result;
    } catch (error) {
        console.error('解析错误:', error.message);
        return null;
    }
}

// 使用示例
const vscdbPath = '你的.vscdb文件路径';  // 替换为实际的文件路径
const data = parseVSCDB(vscdbPath);

if (data) {
    // 格式化输出结果
    Object.entries(data).forEach(([tableName, tableData]) => {
        console.log(`\n表名: ${tableName}`);
        console.log('-'.repeat(50));
        tableData.forEach(row => {
            console.log(JSON.stringify(row, null, 2));
        });
        console.log('-'.repeat(50));
    });
}

使用方法:

  1. 保存上述代码为 .js 文件
  2. 修改 vscdbPath 为你要解析的 .vscdb 文件的实际路径
  3. 运行脚本:
bash 复制代码
node parse_vscdb.js

这个脚本的功能:

  1. 使用 better-sqlite3 连接数据库文件
  2. 获取所有表的内容
  3. 尝试解析可能的 JSON 格式内容
  4. 以易读的格式输出数据

优点:

  1. 使用 better-sqlite3 模块,性能较好
  2. 以只读模式打开文件,更安全
  3. 支持异步操作
  4. 自动处理 JSON 格式的内容

如果你需要针对特定表或内容进行解析,可以根据需要修改代码,添加过滤条件或特定的解析逻辑。


用户 (2025/2/18 16:52:12)

vscdb.js 将解析的内容写入一个json文件


助手 (2025/2/18 16:52:13)

我来帮你修改代码,添加将解析结果写入 JSON 文件的功能:

javascript:/Users/peanut/Desktop/work/ai_all/trae_test/vscdb.js 复制代码
// ... existing code ...

// 使用示例
const vscdbPath = './state.vscdb';
const data = parseVSCDB(vscdbPath);

if (data) {
    // 格式化输出结果到控制台
    Object.entries(data).forEach(([tableName, tableData]) => {
        console.log(`\n表名: ${tableName}`);
        console.log('-'.repeat(50));
        tableData.forEach(row => {
            console.log(JSON.stringify(row, null, 2));
        });
        console.log('-'.repeat(50));
    });

    // 将结果写入 JSON 文件
    const fs = require('fs');
    const outputPath = './vscdb_output.json';
    
    try {
        fs.writeFileSync(
            outputPath,
            JSON.stringify(data, null, 2),
            'utf8'
        );
        console.log(`数据已成功写入到 ${outputPath}`);
    } catch (err) {
        console.error('写入文件时发生错误:', err);
    }
}

主要修改说明:

  1. 添加了 fs 模块的引入
  2. 定义了输出文件路径 outputPath
  3. 使用 writeFileSync 将解析后的数据写入 JSON 文件
  4. 添加了错误处理和成功提示
  5. JSON 数据使用 2 空格缩进格式化,便于阅读

运行脚本后,会在同目录下生成 vscdb_output.json 文件,包含所有解析的数据。


用户 (2025/2/18 16:57:44)

输出的文件是个json,我只想保留key为inputHistory和key为ChatStore的数据


助手 (2025/2/18 16:57:44)

我来帮你修改代码,只保留指定的数据。修改 vscdb.js 文件,添加数据过滤功能:

javascript:/Users/peanut/Desktop/work/ai_all/trae_test/vscdb.js 复制代码
const Database = require('better-sqlite3');
const fs = require('fs');

function parseVSCDB(filePath) {
    try {
        // 连接数据库
        const db = new Database(filePath, { readonly: true });

        // 获取所有表名
        const tables = db.prepare(`
            SELECT name 
            FROM sqlite_master 
            WHERE type='table'
        `).all();

        const result = {};

        // 遍历每个表并获取内容
        tables.forEach(table => {
            const tableName = table.name;
            const rows = db.prepare(`SELECT * FROM ${tableName}`).all();

            // 尝试解析每行中可能的 JSON 字符串
            const parsedRows = rows.map(row => {
                const parsedRow = {};
                Object.entries(row).forEach(([key, value]) => {
                    if (typeof value === 'string') {
                        try {
                            parsedRow[key] = JSON.parse(value);
                        } catch {
                            parsedRow[key] = value;
                        }
                    } else {
                        parsedRow[key] = value;
                    }
                });
                return parsedRow;
            });

            result[tableName] = parsedRows;
        });

        // 关闭数据库连接
        db.close();

        // 过滤数据,只保留指定的 key
        const filteredData = {};
        Object.entries(result).forEach(([tableName, tableData]) => {
            tableData.forEach(row => {
                if (row.key === 'inputHistory' || row.key === 'ChatStore') {
                    if (!filteredData[tableName]) {
                        filteredData[tableName] = [];
                    }
                    filteredData[tableName].push(row);
                }
            });
        });

        return filteredData;
    } catch (error) {
        console.error('解析错误:', error.message);
        return null;
    }
}

// 使用示例
const vscdbPath = './state.vscdb';  // 替换为实际的文件路径
const data = parseVSCDB(vscdbPath);

if (data) {
    // 将过滤后的数据写入新的 JSON 文件
    try {
        fs.writeFileSync(
            'filtered_vscdb_output.json',
            JSON.stringify(data, null, 2),
            'utf8'
        );
        console.log('数据已成功写入到 filtered_vscdb_output.json');
    } catch (err) {
        console.error('写入文件时出错:', err);
    }
}

主要修改:

  1. 添加了数据过滤逻辑,只保留 key 为 'inputHistory' 和 'ChatStore' 的数据
  2. 输出文件名改为 filtered_vscdb_output.json,以区分原始输出
  3. 优化了数据结构,只包含需要的内容

运行后,新生成的 JSON 文件将只包含你指定的两个 key 的数据。


用户 (2025/2/18 17:01:37)

key属性以memento/icube-ai-ng-chat-storage开头的也要的


助手 (2025/2/18 17:01:37)

好的,我来修改代码,让它过滤出 key 为 'inputHistory'、'ChatStore' 以及以 'memento/icube-ai-ng-chat-storage' 开头的数据:

javascript:/Users/peanut/Desktop/work/ai_all/trae_test/vscdb.js 复制代码
const Database = require('better-sqlite3');
const fs = require('fs');

function parseVSCDB(filePath) {
    try {
        // 连接数据库
        const db = new Database(filePath, { readonly: true });

        // 获取所有表名
        const tables = db.prepare(`
            SELECT name 
            FROM sqlite_master 
            WHERE type='table'
        `).all();

        const result = {};

        // 遍历每个表并获取内容
        tables.forEach(table => {
            const tableName = table.name;
            const rows = db.prepare(`SELECT * FROM ${tableName}`).all();

            // 尝试解析每行中可能的 JSON 字符串
            const parsedRows = rows.map(row => {
                const parsedRow = {};
                Object.entries(row).forEach(([key, value]) => {
                    if (typeof value === 'string') {
                        try {
                            parsedRow[key] = JSON.parse(value);
                        } catch {
                            parsedRow[key] = value;
                        }
                    } else {
                        parsedRow[key] = value;
                    }
                });
                return parsedRow;
            });

            result[tableName] = parsedRows;
        });

        // 关闭数据库连接
        db.close();

        // 过滤数据,只保留指定的 key
        const filteredData = {};
        Object.entries(result).forEach(([tableName, tableData]) => {
            tableData.forEach(row => {
                if (row.key === 'inputHistory' || 
                    row.key === 'ChatStore' || 
                    (typeof row.key === 'string' && row.key.startsWith('memento/icube-ai-ng-chat-storage'))) {
                    if (!filteredData[tableName]) {
                        filteredData[tableName] = [];
                    }
                    filteredData[tableName].push(row);
                }
            });
        });

        return filteredData;
    } catch (error) {
        console.error('解析错误:', error.message);
        return null;
    }
}

// 使用示例
const vscdbPath = './state.vscdb';  // 替换为实际的文件路径
const data = parseVSCDB(vscdbPath);

if (data) {
    // 将过滤后的数据写入新的 JSON 文件
    try {
        fs.writeFileSync(
            'filtered_vscdb_output.json',
            JSON.stringify(data, null, 2),
            'utf8'
        );
        console.log('数据已成功写入到 filtered_vscdb_output.json');
    } catch (err) {
        console.error('写入文件时出错:', err);
    }
}

主要修改:

  1. 在过滤条件中添加了对 memento/icube-ai-ng-chat-storage 开头的 key 的判断
  2. 使用 startsWith() 方法来检查字符串前缀
  3. 添加了类型检查,确保 key 是字符串类型

运行后,新生成的 JSON 文件将包含:

  • key 为 'inputHistory' 的数据
  • key 为 'ChatStore' 的数据
  • key 以 'memento/icube-ai-ng-chat-storage' 开头的数据

用户 (2025/2/18 17:10:50)

filtered_vscdb_output.json再用nodejs写个脚本,解析对应的关系,将上一步解析出来的json文件的内容生成不同的markdown文件。名称用 parsedQuery命名即可。解析的内容是key以memento/icube-ai-ng-chat-storage开头的,value.list这个数组对象中的数据,每个对象生成一个markdown文件


助手 (2025/2/18 17:10:50)

我来帮你写一个 Node.js 脚本来处理这个需求:

javascript:/Users/peanut/Desktop/work/ai_all/trae_test/parse_chat_to_md.js 复制代码
const fs = require('fs');
const path = require('path');

// 读取 JSON 文件
function parseVSCDBToMarkdown() {
    try {
        // 读取 JSON 文件
        const jsonData = JSON.parse(fs.readFileSync('./filtered_vscdb_output.json', 'utf8'));
        
        // 创建输出目录
        const outputDir = './chat_markdown';
        if (!fs.existsSync(outputDir)) {
            fs.mkdirSync(outputDir);
        }

        // 查找以 memento/icube-ai-ng-chat-storage 开头的项
        const chatData = jsonData.ItemTable.find(item => 
            item.key.startsWith('memento/icube-ai-ng-chat-storage')
        );

        if (!chatData || !chatData.value || !chatData.value.list) {
            console.log('没有找到聊天数据');
            return;
        }

        // 处理每个会话
        chatData.value.list.forEach(session => {
            if (!session.messages || !session.messages.length) return;

            // 获取第一条用户消息作为文件名
            const firstUserMessage = session.messages.find(msg => 
                msg.role === 'user' && msg.parsedQuery && msg.parsedQuery.length
            );

            if (!firstUserMessage) return;

            const fileName = firstUserMessage.parsedQuery[0]
                .replace(/[<>:"/\\|?*]/g, '_') // 替换不合法的文件名字符
                .substring(0, 100) // 限制文件名长度
                + '.md';

            let markdownContent = '';

            // 生成 Markdown 内容
            session.messages.forEach(msg => {
                if (msg.status === 'deleted') return; // 跳过已删除的消息

                const role = msg.role === 'user' ? '**用户**' : '**助手**';
                const timestamp = new Date(msg.timestamp).toLocaleString();
                
                markdownContent += `### ${role} (${timestamp})\n\n`;
                markdownContent += `${msg.content}\n\n`;

                // 如果有多媒体内容
                if (msg.multiMedia && msg.multiMedia.length) {
                    markdownContent += '**多媒体内容:**\n';
                    msg.multiMedia.forEach(media => {
                        markdownContent += `- ${media}\n`;
                    });
                    markdownContent += '\n';
                }

                markdownContent += '---\n\n';
            });

            // 写入文件
            const filePath = path.join(outputDir, fileName);
            fs.writeFileSync(filePath, markdownContent, 'utf8');
            console.log(`已生成文件: ${fileName}`);
        });

        console.log('所有 Markdown 文件生成完成!');

    } catch (error) {
        console.error('处理过程中出错:', error);
    }
}

// 执行转换
parseVSCDBToMarkdown();

使用方法:

  1. 确保 filtered_vscdb_output.json 文件在同一目录下
  2. 运行脚本:
bash 复制代码
node parse_chat_to_md.js

这个脚本会:

  1. 创建一个 chat_markdown 目录
  2. 查找以 memento/icube-ai-ng-chat-storage 开头的数据
  3. 遍历其中的 list 数组
  4. 为每个会话生成一个 Markdown 文件:
    • 使用第一条用户消息的 parsedQuery 作为文件名
    • 按时间顺序组织对话内容
    • 标记用户和助手的角色
    • 包含时间戳
    • 跳过已删除的消息
    • 处理可能的多媒体内容

生成的 Markdown 文件格式如下:

markdown 复制代码
### **用户** (2024-02-19 10:30:00)

问题内容

---

### **助手** (2024-02-19 10:30:01)

回答内容

---

注意事项:

  1. 文件名会自动处理特殊字符,确保合法
  2. 文件名长度限制为 100 字符
  3. 自动跳过 status 为 deleted 的消息
  4. 保留了消息的时间戳信息

用户 (2025/2/18 17:13:25)

vscdb.jsparse_chat_to_md.js能将这两个脚本文件整合成一个脚本吗


助手 (2025/2/18 17:13:25)

是的,我来帮你整合这两个脚本文件:

javascript:/Users/peanut/Desktop/work/ai_all/trae_test/vscdb_parser.js 复制代码
const Database = require('better-sqlite3');
const fs = require('fs');
const path = require('path');

function parseVSCDB(filePath) {
    try {
        // 连接数据库
        const db = new Database(filePath, { readonly: true });

        // 获取所有表名
        const tables = db.prepare(`
            SELECT name 
            FROM sqlite_master 
            WHERE type='table'
        `).all();

        const result = {};

        // 遍历每个表并获取内容
        tables.forEach(table => {
            const tableName = table.name;
            const rows = db.prepare(`SELECT * FROM ${tableName}`).all();

            // 尝试解析每行中可能的 JSON 字符串
            const parsedRows = rows.map(row => {
                const parsedRow = {};
                Object.entries(row).forEach(([key, value]) => {
                    if (typeof value === 'string') {
                        try {
                            parsedRow[key] = JSON.parse(value);
                        } catch {
                            parsedRow[key] = value;
                        }
                    } else {
                        parsedRow[key] = value;
                    }
                });
                return parsedRow;
            });

            result[tableName] = parsedRows;
        });

        // 关闭数据库连接
        db.close();

        // 过滤数据,只保留指定的 key
        const filteredData = {};
        Object.entries(result).forEach(([tableName, tableData]) => {
            tableData.forEach(row => {
                if (row.key === 'inputHistory' || 
                    row.key === 'ChatStore' || 
                    (typeof row.key === 'string' && row.key.startsWith('memento/icube-ai-ng-chat-storage'))) {
                    if (!filteredData[tableName]) {
                        filteredData[tableName] = [];
                    }
                    filteredData[tableName].push(row);
                }
            });
        });

        return filteredData;
    } catch (error) {
        console.error('解析错误:', error.message);
        return null;
    }
}

function generateMarkdownFiles(jsonData) {
    try {
        // 创建输出目录
        const outputDir = './chat_markdown';
        if (!fs.existsSync(outputDir)) {
            fs.mkdirSync(outputDir);
        }

        // 查找以 memento/icube-ai-ng-chat-storage 开头的项
        const chatData = jsonData.ItemTable.find(item => 
            item.key.startsWith('memento/icube-ai-ng-chat-storage')
        );

        if (!chatData || !chatData.value || !chatData.value.list) {
            console.log('没有找到聊天数据');
            return;
        }

        // 处理每个会话
        chatData.value.list.forEach(session => {
            if (!session.messages || !session.messages.length) return;

            // 获取第一条用户消息作为文件名
            const firstUserMessage = session.messages.find(msg => 
                msg.role === 'user' && msg.parsedQuery && msg.parsedQuery.length
            );

            if (!firstUserMessage) return;

            const fileName = firstUserMessage.parsedQuery[0]
                .replace(/[<>:"/\\|?*]/g, '_') // 替换不合法的文件名字符
                .substring(0, 100) // 限制文件名长度
                + '.md';

            let markdownContent = '';

            // 生成 Markdown 内容
            session.messages.forEach(msg => {
                if (msg.status === 'deleted') return; // 跳过已删除的消息

                const role = msg.role === 'user' ? '**用户**' : '**助手**';
                const timestamp = new Date(msg.timestamp).toLocaleString();
                
                markdownContent += `### ${role} (${timestamp})\n\n`;
                markdownContent += `${msg.content}\n\n`;

                // 如果有多媒体内容
                if (msg.multiMedia && msg.multiMedia.length) {
                    markdownContent += '**多媒体内容:**\n';
                    msg.multiMedia.forEach(media => {
                        markdownContent += `- ${media}\n`;
                    });
                    markdownContent += '\n';
                }

                markdownContent += '---\n\n';
            });

            // 写入文件
            const filePath = path.join(outputDir, fileName);
            fs.writeFileSync(filePath, markdownContent, 'utf8');
            console.log(`已生成文件: ${fileName}`);
        });

        console.log('所有 Markdown 文件生成完成!');

    } catch (error) {
        console.error('处理过程中出错:', error);
    }
}

// 主函数
function main() {
    const vscdbPath = './state.vscdb';
    const data = parseVSCDB(vscdbPath);

    if (data) {
        // 将过滤后的数据写入 JSON 文件
        try {
            fs.writeFileSync(
                'filtered_vscdb_output.json',
                JSON.stringify(data, null, 2),
                'utf8'
            );
            console.log('数据已成功写入到 filtered_vscdb_output.json');

            // 直接使用解析后的数据生成 Markdown 文件
            generateMarkdownFiles(data);
        } catch (err) {
            console.error('处理过程中出错:', err);
        }
    }
}

// 执行程序
main();

主要改动:

  1. 将两个脚本合并为一个文件
  2. 重构为三个主要函数:
    • parseVSCDB: 解析数据库文件
    • generateMarkdownFiles: 生成 Markdown 文件
    • main: 主函数,协调整个流程
  3. 移除了中间 JSON 文件的读取步骤,直接使用内存中的数据
  4. 保留了 JSON 文件的输出,以便查看和调试

使用方法:

bash 复制代码
node vscdb_parser.js

这个整合后的脚本会:

  1. 解析 .vscdb 文件
  2. 保存过滤后的 JSON 数据到文件
  3. 直接使用解析后的数据生成 Markdown 文件
  4. 全程只需要执行一次命令

运行脚本 导出 markdown 文件

看不下去的朋友别着急,我简单解读一下

首先我问 AI 后得知 它是一个 SQLite 数据库文件,使用二进制格式存储等巴拉巴拉

接下来,我询问如何解析为纯文本,它给出我一个 python脚本。貌似让大模型给你脚本的时候,如果不指定开发语言,它默认都是用 python 写的

那不行啊,咱是前端开发,让它换成 nodejs

后续再一步一步引导:写入json文件、打开json文件并分析数据格式、数据过滤、最终生成 markdown 文件

运行一下,完美导出!

完整的脚本我再给下面放一份,大家有兴趣的可以去试试。windows 同理找到 state.vscdb 文件即可
完整脚本 - 展开查看

js:vscdb_parser.js 复制代码
const Database = require('better-sqlite3');
const fs = require('fs');
const path = require('path');

function parseVSCDB(filePath) {
    try {
        // 连接数据库
        const db = new Database(filePath, { readonly: true });

        // 获取所有表名
        const tables = db.prepare(`
            SELECT name 
            FROM sqlite_master 
            WHERE type='table'
        `).all();

        const result = {};

        // 遍历每个表并获取内容
        tables.forEach(table => {
            const tableName = table.name;
            const rows = db.prepare(`SELECT * FROM ${tableName}`).all();

            // 尝试解析每行中可能的 JSON 字符串
            const parsedRows = rows.map(row => {
                const parsedRow = {};
                Object.entries(row).forEach(([key, value]) => {
                    if (typeof value === 'string') {
                        try {
                            parsedRow[key] = JSON.parse(value);
                        } catch {
                            parsedRow[key] = value;
                        }
                    } else {
                        parsedRow[key] = value;
                    }
                });
                return parsedRow;
            });

            result[tableName] = parsedRows;
        });

        // 关闭数据库连接
        db.close();

        // 过滤数据,只保留指定的 key
        const filteredData = {};
        Object.entries(result).forEach(([tableName, tableData]) => {
            tableData.forEach(row => {
                if (row.key === 'inputHistory' || 
                    row.key === 'ChatStore' || 
                    (typeof row.key === 'string' && row.key.startsWith('memento/icube-ai-ng-chat-storage'))) {
                    if (!filteredData[tableName]) {
                        filteredData[tableName] = [];
                    }
                    filteredData[tableName].push(row);
                }
            });
        });

        return filteredData;
    } catch (error) {
        console.error('解析错误:', error.message);
        return null;
    }
}

function generateMarkdownFiles(jsonData) {
    try {
        // 创建输出目录
        const outputDir = './chat_markdown';
        if (!fs.existsSync(outputDir)) {
            fs.mkdirSync(outputDir);
        }

        // 查找以 memento/icube-ai-ng-chat-storage 开头的项
        const chatData = jsonData.ItemTable.find(item => 
            item.key.startsWith('memento/icube-ai-ng-chat-storage')
        );

        if (!chatData || !chatData.value || !chatData.value.list) {
            console.log('没有找到聊天数据');
            return;
        }

        // 处理每个会话
        chatData.value.list.forEach(session => {
            if (!session.messages || !session.messages.length) return;

            // 获取第一条用户消息作为文件名
            const firstUserMessage = session.messages.find(msg => 
                msg.role === 'user' && msg.parsedQuery && msg.parsedQuery.length
            );

            if (!firstUserMessage) return;

            const fileName = firstUserMessage.parsedQuery[0]
                .replace(/[<>:"/\\|?*]/g, '_') // 替换不合法的文件名字符
                .substring(0, 100) // 限制文件名长度
                + '.md';

            let markdownContent = '';

            // 生成 Markdown 内容
            session.messages.forEach(msg => {
                if (msg.status === 'deleted') return; // 跳过已删除的消息

                const role = msg.role === 'user' ? '**用户**' : '**助手**';
                const timestamp = new Date(msg.timestamp).toLocaleString();
                
                markdownContent += `### ${role} (${timestamp})\n\n`;
                markdownContent += `${msg.content}\n\n`;

                // 如果有多媒体内容
                if (msg.multiMedia && msg.multiMedia.length) {
                    markdownContent += '**多媒体内容:**\n';
                    msg.multiMedia.forEach(media => {
                        markdownContent += `- ${media}\n`;
                    });
                    markdownContent += '\n';
                }

                markdownContent += '---\n\n';
            });

            // 写入文件
            const filePath = path.join(outputDir, fileName);
            fs.writeFileSync(filePath, markdownContent, 'utf8');
            console.log(`已生成文件: ${fileName}`);
        });

        console.log('所有 Markdown 文件生成完成!');

    } catch (error) {
        console.error('处理过程中出错:', error);
    }
}

// 主函数
function main() {
    const vscdbPath = './state.vscdb';
    const data = parseVSCDB(vscdbPath);

    if (data) {
        // 将过滤后的数据写入 JSON 文件
        try {
            fs.writeFileSync(
                'filtered_vscdb_output.json',
                JSON.stringify(data, null, 2),
                'utf8'
            );
            console.log('数据已成功写入到 filtered_vscdb_output.json');

            // 直接使用解析后的数据生成 Markdown 文件
            generateMarkdownFiles(data);
        } catch (err) {
            console.error('处理过程中出错:', err);
        }
    }
}

// 执行程序
main();

总结

本文通过分析 Trae AI 编辑器的工作区存储机制,借鉴 Cursor 对话记录导出插件 的思路,实现了 Trae 编辑器的导出功能。

希望这个简单的实现能帮助到有相同需求的朋友,也期待未来 Trae 官方 原生支持对话导出功能。

好了,今天的分享就到这里了,这里是 极客密码,如果当前文章对你有帮助,欢迎点赞、收藏、关注。

相关推荐
小怪兽会微笑5 分钟前
PyTorch Tensor 形状变化操作详解
人工智能·pytorch·python
Erekys32 分钟前
视觉分析之边缘检测算法
人工智能·计算机视觉·音视频
柯腾啊1 小时前
VSCode 中使用 Snippets 设置常用代码块
开发语言·前端·javascript·ide·vscode·编辑器·代码片段
livefan1 小时前
我国首条大型无人机城际低空物流航线成功首航
人工智能·无人机
唔皇万睡万万睡1 小时前
数字水印嵌入及提取系统——基于小波变换GUI
人工智能·计算机视觉
Jackilina_Stone1 小时前
【论文阅读笔记】知识蒸馏:一项调查 | CVPR 2021 | 近万字翻译+解释
论文阅读·人工智能·深度学习·蒸馏
高力士等十万人1 小时前
OpenCV中的边缘检测
人工智能·opencv·计算机视觉
uhakadotcom1 小时前
Google DeepMind最近发布了SigLIP 2
人工智能·算法·架构
咩咩大主教1 小时前
人工智能神经网络
人工智能·python·深度学习·神经网络·机器学习·bp神经网络
banjin1 小时前
免费体验,在阿里云平台零门槛调用满血版DeepSeek-R1模型
阿里云·自然语言处理·云计算·ai编程·ai写作·deepseek