wip: remove opencode, searxng, resources; fix user settings read

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 07:27:19 +00:00
co-authored by Claude Opus 4.6
parent 5925ac49a1
commit 7bbcccabf1
46 changed files with 325 additions and 2037 deletions
+25 -141
View File
@@ -1,12 +1,9 @@
import { Hono } from 'hono';
import { mkdir, readdir, rename, rm } from 'node:fs/promises';
import { join } from 'node:path';
import { getClaudeDir, getSessionDir, getArchivedSessionDir, getOpencodeDir, getOpencodeSessionDir, getPiMonoDir, getPiMonoSessionDir } from '@@/data-path';
import { getClaudeDir, getSessionDir, getArchivedSessionDir, getPiMonoDir, getPiMonoSessionDir } from '@@/data-path';
import type { HonoVariables } from '@@/create-router';
const OPENCODE_PORT = process.env.OPENCODE_PORT ?? '10006';
const OPENCODE_BASE = `http://localhost:${OPENCODE_PORT}`;
export const sessionsRouter = new Hono<{ Variables: HonoVariables }>();
// --- List all sessions (merged from both providers) ---
@@ -14,13 +11,9 @@ export const sessionsRouter = new Hono<{ Variables: HonoVariables }>();
sessionsRouter.get('/sessions', async (ctx) => {
const { email } = ctx.get('user');
const [claudeSessions, opencodeSessions, piMonoSessions] = await Promise.all([
fetchClaudeSessions(email),
fetchOpencodeSessions(email),
fetchPiMonoSessions(email),
]);
const [claudeSessions, piMonoSessions] = await Promise.all([fetchClaudeSessions(email), fetchPiMonoSessions(email)]);
const merged = [...claudeSessions, ...opencodeSessions, ...piMonoSessions].sort((a, b) => b.createdAt - a.createdAt);
const merged = [...claudeSessions, ...piMonoSessions].sort((a, b) => b.createdAt - a.createdAt);
return ctx.json(merged);
});
@@ -34,17 +27,21 @@ sessionsRouter.get('/sessions/:provider/:id/messages', async (ctx) => {
if (provider === 'claude') {
const file = Bun.file(join(getSessionDir(email, id), 'messages.json'));
if (!(await file.exists())) return ctx.json([]);
try { return ctx.json(await file.json()); } catch { return ctx.json([]); }
}
if (provider === 'opencode') {
return ctx.json(await fetchOpencodeMessages(id));
try {
return ctx.json(await file.json());
} catch {
return ctx.json([]);
}
}
if (provider === 'pi-mono') {
const file = Bun.file(join(getPiMonoSessionDir(email, id), 'messages.json'));
if (!(await file.exists())) return ctx.json([]);
try { return ctx.json(await file.json()); } catch { return ctx.json([]); }
try {
return ctx.json(await file.json());
} catch {
return ctx.json([]);
}
}
return ctx.json({ error: 'invalid provider' }, 400);
@@ -55,7 +52,6 @@ sessionsRouter.put('/sessions/:provider/:id/messages', async (ctx) => {
const provider = ctx.req.param('provider');
const id = ctx.req.param('id');
if (provider === 'opencode') return ctx.json({ error: 'opencode sessions are read-only' }, 400);
if (provider !== 'claude' && provider !== 'pi-mono') return ctx.json({ error: 'invalid provider' }, 400);
const messages = ctx.get('body');
@@ -79,37 +75,26 @@ sessionsRouter.put('/sessions/:provider/:id', async (ctx) => {
const metaFile = Bun.file(join(dir, 'meta.json'));
if (!(await metaFile.exists())) return ctx.json({ error: 'session not found' }, 404);
let meta: Record<string, unknown>;
try { meta = await metaFile.json(); } catch { return ctx.json({ error: 'corrupted session' }, 500); }
try {
meta = await metaFile.json();
} catch {
return ctx.json({ error: 'corrupted session' }, 500);
}
meta.title = body.title.slice(0, 200);
await Bun.write(join(dir, 'meta.json'), JSON.stringify(meta));
return ctx.json({ ok: true });
}
if (provider === 'opencode') {
const dir = getOpencodeSessionDir(email, id);
const metaFile = Bun.file(join(dir, 'meta.json'));
if (!(await metaFile.exists())) return ctx.json({ error: 'session not found' }, 404);
let meta: Record<string, unknown>;
try { meta = await metaFile.json(); } catch { return ctx.json({ error: 'corrupted session' }, 500); }
meta.title = body.title.slice(0, 200);
await Bun.write(join(dir, 'meta.json'), JSON.stringify(meta));
// Best-effort sync to OpenCode API
fetch(`${OPENCODE_BASE}/session/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: meta.title }),
}).catch(() => {});
return ctx.json({ ok: true });
}
if (provider === 'pi-mono') {
const dir = getPiMonoSessionDir(email, id);
const metaFile = Bun.file(join(dir, 'meta.json'));
if (!(await metaFile.exists())) return ctx.json({ error: 'session not found' }, 404);
let meta: Record<string, unknown>;
try { meta = await metaFile.json(); } catch { return ctx.json({ error: 'corrupted session' }, 500); }
try {
meta = await metaFile.json();
} catch {
return ctx.json({ error: 'corrupted session' }, 500);
}
meta.title = body.title.slice(0, 200);
await Bun.write(join(dir, 'meta.json'), JSON.stringify(meta));
return ctx.json({ ok: true });
@@ -135,18 +120,6 @@ sessionsRouter.delete('/sessions/:provider/:id', async (ctx) => {
return ctx.json({ ok: true });
}
if (provider === 'opencode') {
const dir = getOpencodeSessionDir(email, id);
try {
await rm(dir, { recursive: true });
} catch {
// dir may not exist
}
// Best-effort sync to OpenCode API
fetch(`${OPENCODE_BASE}/session/${id}`, { method: 'DELETE' }).catch(() => {});
return ctx.json({ ok: true });
}
if (provider === 'pi-mono') {
const dir = getPiMonoSessionDir(email, id);
try {
@@ -167,7 +140,7 @@ sessionsRouter.post('/sessions/:provider/:id/archive', async (ctx) => {
const provider = ctx.req.param('provider');
const id = ctx.req.param('id');
if (provider === 'opencode' || provider === 'pi-mono') return ctx.json({ error: `${provider} sessions cannot be archived` }, 400);
if (provider === 'pi-mono') return ctx.json({ error: 'pi-mono sessions cannot be archived' }, 400);
if (provider !== 'claude') return ctx.json({ error: 'invalid provider' }, 400);
const src = getSessionDir(email, id);
@@ -183,7 +156,7 @@ type SessionMeta = {
id: string;
title: string;
createdAt: number;
provider: 'claude' | 'opencode' | 'pi-mono';
provider: 'claude' | 'pi-mono';
model?: string | null;
};
@@ -211,28 +184,6 @@ async function fetchClaudeSessions(email: string): Promise<SessionMeta[]> {
}
}
async function fetchOpencodeSessions(email: string): Promise<SessionMeta[]> {
const dir = getOpencodeDir(email);
try {
const entries = await readdir(dir);
const sessions = await Promise.all(
entries.map(async (id) => {
try {
const metaFile = Bun.file(join(dir, id, 'meta.json'));
if (!(await metaFile.exists())) return null;
const meta = await metaFile.json();
return { ...meta, provider: 'opencode' as const };
} catch {
return null;
}
}),
);
return sessions.filter((s): s is SessionMeta => s !== null);
} catch {
return [];
}
}
async function fetchPiMonoSessions(email: string): Promise<SessionMeta[]> {
const dir = getPiMonoDir(email);
try {
@@ -254,70 +205,3 @@ async function fetchPiMonoSessions(email: string): Promise<SessionMeta[]> {
return [];
}
}
async function fetchOpencodeMessages(id: string) {
try {
const res = await fetch(`${OPENCODE_BASE}/session/${id}/message`);
if (!res.ok) return [];
const data = (await res.json()) as any[];
const messages = Array.isArray(data) ? data : Object.values(data);
const chatMessages: any[] = [];
for (const msg of messages) {
const role = msg.info?.role ?? msg.role;
if (role === 'user') {
const text = Array.isArray(msg.parts)
? msg.parts
.filter((p: any) => p.type === 'text')
.map((p: any) => p.text ?? p.content ?? '')
.join('')
: typeof msg.content === 'string'
? msg.content
: '';
if (text) chatMessages.push({ role: 'user', text });
} else if (role === 'assistant') {
if (Array.isArray(msg.parts)) {
for (const part of msg.parts) {
if (part.type === 'text' && (part.text || part.content)) {
chatMessages.push({ role: 'assistant', text: part.text ?? part.content ?? '' });
} else if (part.type === 'tool') {
chatMessages.push({
role: 'tool',
toolName: part.tool ?? 'unknown',
toolInput: part.state?.input ?? {},
toolUseId: part.callID ?? part.id ?? '',
output:
part.state?.output != null
? typeof part.state.output === 'string'
? part.state.output
: JSON.stringify(part.state.output)
: undefined,
isError: part.state?.status === 'error',
});
} else if (part.type === 'tool-invocation') {
const inv = part.toolInvocation ?? part;
chatMessages.push({
role: 'tool',
toolName: inv.toolName ?? 'unknown',
toolInput: inv.args ?? {},
toolUseId: inv.toolCallId ?? part.id ?? '',
output:
inv.result != null
? typeof inv.result === 'string'
? inv.result
: JSON.stringify(inv.result)
: undefined,
isError: !!part.isError,
});
}
}
}
}
}
return chatMessages;
} catch {
return [];
}
}