chat: point OpenCode at a fixed pm2-managed server (fixes empty model picker)
The per-cwd `opencode serve` spawning is replaced by a single fixed server (http://127.0.0.1:4096, OPENCODE_SERVER_URL) managed by pm2 — added as `officer-opencode` in ecosystem.config.cjs (cwd = home). Root-cause fix for the empty model selector: list-models shelled out to `opencode models`, which failed at runtime on the deployed server (the picker got only Claude tiers). It now reads the fixed server's GET /config/providers over HTTP — 11ms and reliable — so the curated OpenCode models (Big Pickle, Claude Haiku) show up. - server-manager.ts — drops spawning; exposes OPENCODE_SERVER_URL + a health check. - client.ts — createSession no longer binds a directory (sessions live in the one server's project). - send-opencode.ts / opencode-sessions.ts / chat.ts — use the fixed server; drop the cwd/home plumbing. Session list/load/delete/rename now hit :4096. Verified end-to-end against the live server: model list, streaming turn, session list, and transcript load all work with no spawning. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,6 @@ import {
|
||||
} from './opencode-sessions';
|
||||
import { listChatModels } from './list-models';
|
||||
import { logger } from './logger';
|
||||
import { getHomeDirForRole } from '../../data-path';
|
||||
import { readSttConfig } from '../server-settings/stt';
|
||||
import { transcribeAudio } from '../stt/transcribe';
|
||||
|
||||
@@ -26,11 +25,9 @@ export const chatRouter = createRouter();
|
||||
|
||||
// The working directory a request operates on: an explicit ?cwd= (a chosen pwd), else the default
|
||||
// claude_sessions dir. Claude groups sessions by cwd, so this selects which project group we read.
|
||||
// (OpenCode sessions all live in the one fixed server and ignore cwd.)
|
||||
const cwdOf = (ctx: Context, email: string): string => ctx.req.query('cwd')?.trim() || getClaudeSessionsCwd(email);
|
||||
|
||||
// The home dir an OpenCode `serve` runs under (so it reads the user's ~/.local/share/opencode auth).
|
||||
const homeOf = (ctx: Context): string => getHomeDirForRole(ctx.get('user').email, ctx.get('user').role);
|
||||
|
||||
// GET /chat/pwds — the default /chat dir plus every directory that already has Claude sessions.
|
||||
chatRouter.get('/pwds', (ctx) => {
|
||||
const email = ctx.get('user').email;
|
||||
@@ -43,7 +40,7 @@ chatRouter.get('/sessions', async (ctx) => {
|
||||
const email = ctx.get('user').email;
|
||||
const cwd = cwdOf(ctx, email);
|
||||
const claude = listClaudeSessions(email, cwd).map((s) => ({ ...s, harness: 'claude' as const }));
|
||||
const opencode = await listOpenCodeSessions(cwd, homeOf(ctx));
|
||||
const opencode = await listOpenCodeSessions();
|
||||
const sessions = [...claude, ...opencode].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
||||
return ctx.json({ sessions });
|
||||
});
|
||||
@@ -53,9 +50,7 @@ chatRouter.get('/sessions/:id', async (ctx) => {
|
||||
const email = ctx.get('user').email;
|
||||
const id = ctx.req.param('id');
|
||||
const cwd = cwdOf(ctx, email);
|
||||
const detail = isOpenCodeSessionId(id)
|
||||
? await loadOpenCodeSession(cwd, homeOf(ctx), id)
|
||||
: loadClaudeSession(email, cwd, id);
|
||||
const detail = isOpenCodeSessionId(id) ? await loadOpenCodeSession(id) : loadClaudeSession(email, cwd, id);
|
||||
if (!detail) return ctx.text('Not found', 404);
|
||||
return ctx.json(detail);
|
||||
});
|
||||
@@ -65,9 +60,7 @@ chatRouter.delete('/sessions/:id', async (ctx) => {
|
||||
const email = ctx.get('user').email;
|
||||
const id = ctx.req.param('id');
|
||||
const cwd = cwdOf(ctx, email);
|
||||
const ok = isOpenCodeSessionId(id)
|
||||
? await deleteOpenCodeSession(cwd, homeOf(ctx), id)
|
||||
: deleteClaudeSession(email, cwd, id);
|
||||
const ok = isOpenCodeSessionId(id) ? await deleteOpenCodeSession(id) : deleteClaudeSession(email, cwd, id);
|
||||
if (!ok) return ctx.text('Not found', 404);
|
||||
return ctx.json({ ok: true });
|
||||
});
|
||||
@@ -80,7 +73,7 @@ chatRouter.patch('/sessions/:id/title', async (ctx) => {
|
||||
const { title } = await ctx.req.json<{ title?: string }>();
|
||||
if (!title?.trim()) return ctx.text('title is required', 400);
|
||||
const ok = isOpenCodeSessionId(id)
|
||||
? await renameOpenCodeSession(cwd, homeOf(ctx), id, title.trim())
|
||||
? await renameOpenCodeSession(id, title.trim())
|
||||
: renameClaudeSession(email, cwd, id, title.trim());
|
||||
if (!ok) return ctx.text('Not found', 404);
|
||||
return ctx.json({ ok: true });
|
||||
|
||||
Reference in New Issue
Block a user