// bench/import-kimi-code.js
//
// Import all workspaces + sessions from ~/.kimi-code into minidb and build a
// full-text index over the session content, then measure import + search speed.
//
// Run: node bench/import-kimi-code.js [--data ~/.kimi-code] [--out
]
import fs from 'node:fs/promises';
import { existsSync, readFileSync } from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { MiniDb } from '../src/index.js';
const argv = process.argv.slice(2);
const arg = (name, def) => {
const i = argv.indexOf(`--${name}`);
return i === -1 ? def : argv[i + 1];
};
const DATA = path.resolve(arg('data', path.join(os.homedir(), '.kimi-code')));
const OUT = path.resolve(arg('out', path.join(os.tmpdir(), 'minidb-kimi-code-' + Date.now())));
const FULL = argv.includes('--full'); // also index full tool results (stress test)
const fmt = (n) => n.toLocaleString('en-US', { maximumFractionDigits: 0 });
const mib = (n) => (n / 1024 / 1024).toFixed(1) + ' MiB';
function extractWireText(wirePath, full = false) {
// Pull searchable text out of the wire protocol:
// - user + assistant message text (context.append_message)
// - tool-call intent: tool name + concise args (command/pattern/path/...)
// - (full mode) tool-result output as well (stress test; noisy)
const ARG_FIELDS = ['command', 'pattern', 'path', 'description', 'query', 'prompt', 'file_path'];
let raw;
try {
raw = readFileSync(wirePath, 'utf8');
} catch {
return { text: '', messages: 0 };
}
const parts = [];
let messages = 0;
for (const line of raw.split('\n')) {
if (!line) continue;
let o;
try {
o = JSON.parse(line);
} catch {
continue;
}
if (o.type === 'context.append_message' && o.message && o.message.content) {
let got = false;
for (const c of o.message.content) {
if (c && c.type === 'text' && typeof c.text === 'string') {
parts.push(c.text);
got = true;
}
}
if (got) messages++;
} else if (
o.type === 'context.append_loop_event' &&
o.event &&
o.event.type === 'tool.call'
) {
const e = o.event;
const bits = [e.name];
for (const k of ARG_FIELDS) {
const v = e.args && e.args[k];
if (typeof v === 'string' && v) bits.push(v.length > 2000 ? v.slice(0, 2000) : v);
}
parts.push(bits.join(' '));
} else if (
full &&
o.type === 'context.append_loop_event' &&
o.event &&
o.event.type === 'tool.result'
) {
const r = o.event.result;
let out = '';
if (typeof r === 'string') out = r;
else if (r && typeof r.output === 'string') out = r.output;
else if (r && typeof r.content === 'string') out = r.content;
if (out) parts.push(out.length > 5000 ? out.slice(0, 5000) : out);
}
}
return { text: parts.join('\n'), messages };
}
async function dirSize(dir) {
let total = 0;
async function walk(d) {
let ents;
try {
ents = await fs.readdir(d, { withFileTypes: true });
} catch {
return;
}
for (const e of ents) {
const p = path.join(d, e.name);
if (e.isDirectory()) await walk(p);
else {
try {
total += (await fs.stat(p)).size;
} catch {}
}
}
}
await walk(dir);
return total;
}
async function main() {
console.log(`data: ${DATA}`);
console.log(`out: ${OUT}`);
await fs.rm(OUT, { recursive: true, force: true });
// workspaces
const wsRaw = JSON.parse(readFileSync(path.join(DATA, 'workspaces.json'), 'utf8'));
const workspaces = wsRaw.workspaces || wsRaw;
// session index
const lines = readFileSync(path.join(DATA, 'session_index.jsonl'), 'utf8').trim().split('\n');
const db = await MiniDb.open({
dir: OUT,
valueCodec: 'json',
fsyncPolicy: 'no',
autoCompact: false,
});
await db.createTextIndex('body', { fields: ['text'] });
await db.createIndex('byWorkspace', { field: 'workspaceName' });
const t0 = performance.now();
let imported = 0;
let skipped = 0;
let totalTextBytes = 0;
let totalMessages = 0;
let last = performance.now();
for (const line of lines) {
let meta;
try {
meta = JSON.parse(line);
} catch {
continue;
}
const { sessionId, sessionDir, workDir } = meta;
const wirePath = path.join(sessionDir, 'agents', 'main', 'wire.jsonl');
if (!existsSync(wirePath)) {
skipped++;
continue;
}
let state = {};
try {
state = JSON.parse(readFileSync(path.join(sessionDir, 'state.json'), 'utf8'));
} catch {}
const { text, messages } = extractWireText(wirePath, FULL);
totalTextBytes += Buffer.byteLength(text, 'utf8');
totalMessages += messages;
const wsId = path.basename(path.dirname(sessionDir)); // /
const ws = workspaces[wsId] || {};
const doc = {
title: state.title || '',
workspaceId: wsId,
workspaceName: ws.name || '',
workDir: workDir || '',
text: (state.title ? state.title + '\n' : '') + text,
messageCount: messages,
};
const updated = state.updatedAt ? Date.parse(state.updatedAt) : 0;
const created = state.createdAt ? Date.parse(state.createdAt) : 0;
await db.set(sessionId, doc, { dt: { updated, created } });
imported++;
const now = performance.now();
if (now - last > 1000) {
const rate = (imported / (now - t0)) * 1000;
process.stdout.write(`\r imported ${fmt(imported)} (${fmt(rate | 0)} sess/s, ${mib(totalTextBytes)} text)`);
last = now;
}
}
const importMs = performance.now() - t0;
process.stdout.write('\n');
// force a compaction so the on-disk size is the compact snapshot size
const ct0 = performance.now();
await db.compact();
const compactMs = performance.now() - ct0;
const sz = await dirSize(OUT);
console.log(`\n=== import done ===`);
console.log(` sessions imported: ${fmt(imported)} (skipped ${skipped})`);
console.log(` messages indexed : ${fmt(totalMessages)}`);
console.log(` text indexed : ${mib(totalTextBytes)}`);
console.log(` import time : ${(importMs / 1000).toFixed(1)} s (${fmt((imported / importMs) * 1000 | 0)} sess/s, ${mib(totalTextBytes / (importMs / 1000))}/s text)`);
console.log(` compact time : ${compactMs.toFixed(0)} ms`);
console.log(` db size on disk : ${mib(sz)} (${(sz / totalTextBytes).toFixed(2)}x raw text)`);
console.log(` postings terms : ${fmt(db.text.get('body').postings.size)}`);
console.log(` indexed docs : ${db.text.get('body').N}`);
if (global.gc) global.gc();
console.log(` heap used : ${mib(process.memoryUsage().heapUsed)}`);
// ---- sample searches ----
const queries = ['lark-approval', 'database compaction', '北京', 'Redis 持久化', 'worktree init', 'nonexistentxyz123'];
console.log(`\n=== sample searches ===`);
for (const q of queries) {
const s0 = performance.now();
const res = db.search('body', q, { limit: 5 });
const ms = performance.now() - s0;
console.log(` "${q}" -> ${res.length} hits in ${ms.toFixed(1)} ms`);
for (const r of res.slice(0, 3)) {
const title = (r.value && r.value.title) || '';
console.log(` [${r.score.toFixed(3)}] ${r.value.workspaceName} :: ${title.slice(0, 60)}`);
}
}
// combined query: text + dt range
const q0 = performance.now();
const recent = db.query({
text: { index: 'body', q: 'database' },
sort: { 'workspaceName': 1 },
limit: 5,
project: ['title', 'workspaceName'],
});
console.log(`\n composed query (text "database" + sort + project) -> ${recent.length} in ${(performance.now() - q0).toFixed(1)} ms`);
// dt range: sessions updated in the last 7 days
const week = Date.now() - 7 * 864e5;
const d0 = performance.now();
const recentDt = db.dtRange('updated', { gte: week, limit: 10 });
console.log(` dt range (updated in last 7d) -> ${recentDt.length} shown in ${(performance.now() - d0).toFixed(2)} ms`);
// secondary index lookup by workspace
const w0 = performance.now();
const byWs = db.findEq('byWorkspace', 'kimi-code-dev-1');
console.log(` index lookup (workspace=kimi-code-dev-1) -> ${byWs.length} in ${(performance.now() - w0).toFixed(2)} ms`);
await db.close();
console.log(`\ndone. db at: ${OUT}`);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});