{"repo": "alexlmoney83-oss/claude-", "files": {"server/index.js": {"1bc13e2897a3f0154440165eb0c3a0ebf07fbe3f": {"t": 1785745530.361954, "n": 21218, "h": "86f4b0d10f1429b0", "c": "import Fastify from 'fastify'\nimport fastifyCors from '@fastify/cors'\nimport fastifyWebsocket from '@fastify/websocket'\nimport fastifyStatic from '@fastify/static'\nimport { fileURLToPath } from 'url'\nimport { dirname, join, resolve } from 'path'\nimport { spawn } from 'child_process'\nimport { randomUUID } from 'crypto'\nimport { createHash } from 'crypto'\nimport fs from 'fs'\nimport fse from 'fs-extra'\nimport { createTwoFilesPatch, applyPatch } from 'diff'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst PORT = process.env.PORT ? Number(process.env.PORT) : 5178\n\nconst fastify = Fastify({ logger: false })\n\n// --- DB / data paths ---\nconst DB_DIR = resolve(__dirname, 'db')\nconst SESSIONS_DIR = join(DB_DIR, 'sessions')\nconst DATA_DIR = resolve(__dirname, 'data', 'projects')\ntry { fs.mkdirSync(DB_DIR, { recursive: true }); fs.mkdirSync(SESSIONS_DIR, { recursive: true }); fs.mkdirSync(DATA_DIR, { recursive: true }) } catch {}\n\nfunction idxPath() { return join(DB_DIR, 'session-index.json') }\nfunction loadIndex() { try { return JSON.parse(fs.readFileSync(idxPath(), 'utf-8')) } catch { return { sessions: [] } } }\nfunction saveIndex(obj) { fs.writeFileSync(idxPath(), JSON.stringify(obj, null, 2)) }\nfunction appendJsonl(id, obj) { fs.appendFileSync(join(SESSIONS_DIR, id + '.jsonl'), JSON.stringify({ ts: Date.now(), ...obj }) + '\\n') }\n\nfunction computeEtag(text) { return '\"' + createHash('sha1').update(text).digest('hex').slice(0, 16) + '\"' }\n\nawait fastify.register(fastifyCors, { origin: true })\nawait fastify.register(fastifyWebsocket)\nawait fastify.register(fastifyStatic, { root: resolve(__dirname, '../frontend-vue/dist'), prefix: '/' })\n\n// In-memory settings\nconst settings = { env: {} }\n\n// Load persisted user settings on startup\n;(function loadPersistedSettings() {\n try {\n const userFile = join(DB_DIR, 'settings.user.json')\n if (fs.existsSync(userFile)) {\n const data = JSON.parse(fs.readFileSync(userFile, 'utf-8'))\n if (data?.env) settings.env = { ...settings.env, ...data.env }\n }\n } catch {}\n})()\n\n// Engine sessions map: id -> { clients: Set, projectId, teamId }\nconst engineSessions = new Map()\n\nfunction sseWrite(res, event, data) {\n try { res.raw.write(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`) } catch {}\n}\n\n// ---- Helpers ----\nfunction findCliRoot() {\n const candidates = [\n resolve(__dirname, '../claude-code-source-main'),\n resolve(process.cwd(), 'claude-code-source-main'),\n process.env.CC_CLI_ROOT,\n ].filter(Boolean)\n for (const c of candidates) {\n try {\n if (!c) continue\n if (fs.existsSync(join(c, 'dist', 'cli.js'))) return c\n if (fs.existsSync(join(c, 'src', 'entrypoints', 'cli.tsx'))) return c\n } catch {}\n }\n return null\n}\nfunction getWorkDir(projectId) {\n if (projectId) {\n const proj = loadProjectMeta(projectId)\n if (proj?.workspaceRoot && fs.existsSync(proj.workspaceRoot)) return proj.workspaceRoot\n }\n const p = process.env.CC_WORKDIR || resolve(__dirname, '../workspace')\n try { fs.mkdirSync(p, { recursive: true }) } catch {}\n return p\n}\nfunction safeJoin(root, p) {\n const resolved = resolve(root, p)\n if (!resolved.startsWith(resolve(root))) throw new Error('path outside workspace')\n return resolved\n}\n\n// ---- Layered Settings (M3) ----\nconst SETTINGS_FILES = {\n user: join(DB_DIR, 'settings.user.json'),\n local: join(DB_DIR, 'settings.local.json'),\n policy: join(DB_DIR, 'settings.policy.json'),\n}\nfunction projectSettingsPath(pid) { return resolve(DATA_DIR, pid, '.claude', 'settings.project.json') }\nfunction ensureDirOf(file) { try { fs.mkdirSync(join(file, '..'), { recursive: true }) } catch {} }\nfunction readJson(file) { try { return JSON.parse(fs.readFileSync(file, 'utf-8')) } catch { return {} } }\nfunction deepMerge(a, b) {\n a = (a && typeof a === 'object') ? a : {}\n if (!b || typeof b !== 'object') return a\n const out = Array.isArray(a) ? [...a] : { ...a }\n for (const [k, v] of Object.entries(b)) {\n if (v && typeof v === 'object' && !Array.isArray(v)) out[k] = deepMerge(out[k] || {}, v)\n else out[k] = v\n }\n return out\n}\nfunction writeJsonWithBackup(file, obj) {\n ensureDirOf(file)\n const text = JSON.stringify(obj, null, 2)\n const backupDir = resolve(DB_DIR, 'backups'); try { fs.mkdirSync(backupDir, { recursive: true }) } catch {}\n if (fs.existsSync(file)) {\n const stamp = new Date().toISOString().replace(/[:.]/g, '-')\n fs.copyFileSync(file, join(backupDir, file.replace(/[\\\\/:]/g, '__') + '.' + stamp + '.bak'))\n }\n fs.writeFileSync(file, text, 'utf-8')\n return computeEtag(text)\n}\nfunction loadLayer(scope, pid) {\n if (scope === 'user') return readJson(SETTINGS_FILES.user)\n if (scope === 'local') return readJson(SETTINGS_FILES.local)\n if (scope === 'policy') return readJson(SETTINGS_FILES.policy)\n if (scope === 'project') { if (!pid) return {}; return readJson(projectSettingsPath(pid)) }\n if (scope === 'flags') { return { flags: {} } }\n return {}\n}\nfunction effectiveSettings(pid) {\n let eff = {}\n for (const s of ['user', 'project', 'local', 'policy', 'flags']) eff = deepMerge(eff, loadLayer(s, pid))\n return eff\n}\n\nfastify.get('/api/engine/settings/sources', async (req, reply) => {\n const q = (req.query || {}); const pid = q.projectId || q.pid || ''\n const list = [\n { scope: 'user', path: SETTINGS_FILES.user, writable: true },\n { scope: 'project', path: pid ? projectSettingsPath(pid) : null, writable: !!pid },\n { scope: 'local', path: SETTINGS_FILES.local, writable: true },\n { scope: 'policy', path: SETTINGS_FILES.policy, writable: false },\n { scope: 'flags', path: 'build-time', writable: false },\n ]\n reply.send({ sources: list })\n})\nfastify.get('/api/engine/settings', async (req, reply) => {\n const q = (req.query || {}); const scope = String(q.scope || 'effective'); const pid = q.projectId || q.pid || ''\n let data\n if (scope === 'effective') data = effectiveSettings(pid)\n else if (['user', 'project', 'local', 'policy', 'flags'].includes(scope)) data = loadLayer(scope, pid)\n else return reply.code(400).send({ error: 'invalid_scope' })\n reply.header('ETag', computeEtag(JSON.stringify(data))).send(data)\n})\nfastify.put('/api/engine/settings', async (req, reply) => {\n const q = (req.query || {}); const scope = String(q.scope || '').toLowerCase(); const pid = q.projectId || q.pid || ''\n if (!['user', 'project', 'local'].includes(scope)) return reply.code(400).send({ error: 'scope_not_writable' })\n const file = scope === 'project' ? (pid ? projectSettingsPath(pid) : null) : SETTINGS_FILES[scope]\n if (!file) return reply.code(400).send({ error: 'project_required' })\n const ifMatch = req.headers['if-match']\n const prevText = fs.existsSync(file) ? fs.readFileSync(file, 'utf-8') : ''\n if (ifMatch && computeEtag(prevText) !== ifMatch) return reply.code(412).send({ error: 'etag_mismatch', current: computeEtag(prevText) })\n let body = (req.body || {})\n if (typeof body === 'string') { try { body = JSON.parse(body) } catch { body = {} } }\n const etag = writeJsonWithBackup(file, body)\n // Reload env into memory after user settings update\n try { const eff = effectiveSettings(pid || null); if (eff && eff.env) settings.env = eff.env } catch {}\n try { for (const sess of engineSessions.values()) { for (const c of Array.from(sess.clients)) sseWrite(c, 'settings_updated', { scope, projectId: pid || null, etag }) } } catch {}\n reply.header('ETag', etag).send({ ok: true, etag })\n})\n\n// ---- Projects API ----\nconst PROJECTS_INDEX = join(DB_DIR, 'projects.json')\nfunction loadProjects() { try { return JSON.parse(fs.readFileSync(PROJECTS_INDEX, 'utf-8')) } catch { return { projects: [] } } }\nfunction saveProjects(obj) { fs.writeFileSync(PROJECTS_INDEX, JSON.stringify(obj, null, 2)) }\nfunction loadProjectMeta(pid) { return (loadProjects().projects || []).find(p => p.id === pid) || null }\nfunction projectDir(pid) { return resolve(DATA_DIR, pid) }\n\nfastify.get('/api/projects', async (_req, reply) => {\n reply.send(loadProjects())\n})\nfastify.post('/api/projects', async (req, reply) => {\n const body = req.body || {}\n const name = String(body.name || '').trim()\n if (!name) return reply.code(400).send({ error: 'name_required' })\n const id = randomUUID()\n const workspaceRoot = body.workspaceRoot ? String(body.workspaceRoot).trim() : null\n // Create project directory structure\n const dir = projectDir(id)\n fs.mkdirSync(resolve(dir, '.claude', 'teams'), { recursive: true })\n // Create workspace folder if specified and doesn't exist\n if (workspaceRoot) {\n try { fs.mkdirSync(workspaceRoot, { recursive: true }) } catch {}\n }\n const project = { id, name, workspaceRoot: workspaceRoot || null, createdAt: Date.now() }\n const db = loadProjects(); db.projects = [...(db.projects || []), project]; saveProjects(db)\n reply.code(201).send(project)\n})\nfastify.delete('/api/projects/:id', async (req, reply) => {\n const id = req.params.id\n const db = loadProjects()\n const project = (db.projects || []).find(p => p.id === id)\n if (!project) return reply.code(404).send({ error: 'not_found' })\n // Remove project data directory\n try { fs.rmSync(projectDir(id), { recursive: true, force: true }) } catch {}\n // Remove workspace folder only if it was created by us (workspaceRoot set and exists)\n // We do NOT delete user-specified external workspaceRoot to avoid data loss\n db.projects = db.projects.filter(p => p.id !== id); saveProjects(db)\n reply.send({ ok: true })\n})\n\n// ---- Teams API ----\nfunction teamsDir(pid) { return resolve(projectDir(pid), '.claude', 'teams') }\nfunction teamFile(pid, tid) { return resolve(teamsDir(pid), tid, 'config.json') }\n\nfastify.get('/api/projects/:pid/teams', async (req, reply) => {\n const pid = req.params.pid\n const dir = teamsDir(pid)\n if (!fs.existsSync(dir)) return reply.send({ teams: [] })\n const teams = fs.readdirSync(dir, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => {\n const cfg = readJson(teamFile(pid, e.name))\n return { id: e.name, name: cfg.name || e.name, memberCount: (cfg.members || []).length }\n })\n reply.send({ teams })\n})\nfastify.post('/api/projects/:pid/teams', async (req, reply) => {\n const pid = req.params.pid\n const body = req.body || {}\n const name = String(body.name || '').trim()\n if (!name) return reply.code(400).send({ error: 'name_required' })\n const tid = randomUUID()\n const dir = resolve(teamsDir(pid), tid)\n fs.mkdirSync(dir, { recursive: true })\n const cfg = { name, members: [], routing: 'sequential', createdAt: Date.now() }\n const text = JSON.stringify(cfg, null, 2)\n fs.writeFileSync(teamFile(pid, tid), text)\n const etag = computeEtag(text)\n reply.code(201).header('ETag', etag).send({ id: tid, ...cfg })\n})\nfastify.get('/api/projects/:pid/teams/:tid', async (req, reply) => {\n const { pid, tid } = req.params\n const file = teamFile(pid, tid)\n if (!fs.existsSync(file)) return reply.code(404).send({ error: 'not_found' })\n const text = fs.readFileSync(file, 'utf-8')\n const etag = computeEtag(text)\n reply.header('ETag', etag).send(JSON.parse(text))\n})\nfastify.put('/api/projects/:pid/teams/:tid', async (req, reply) => {\n const { pid, tid } = req.params\n const file = teamFile(pid, tid)\n const ifMatch = req.headers['if-match']\n const prevText = fs.existsSync(file) ? fs.readFileSync(file, 'utf-8') : ''\n if (ifMatch && computeEtag(prevText) !== ifMatch) return reply.code(412).send({ error: 'etag_mismatch', current: computeEtag(prevText) })\n let body = req.body || {}\n if (typeof body === 'string') { try { body = JSON.parse(body) } catch { body = {} } }\n const text = JSON.stringify(body, null, 2)\n fs.mkdirSync(resolve(teamsDir(pid), tid), { recursive: true })\n fs.writeFileSync(file, text)\n const etag = computeEtag(text)\n reply.header('ETag', etag).send({ ok: true, etag })\n})\nfastify.delete('/api/projects/:pid/teams/:tid', async (req, reply) => {\n const { pid, tid } = req.params\n try { fs.rmSync(resolve(teamsDir(pid), tid), { recursive: true, force: true }) } catch {}\n reply.send({ ok: true })\n})\n\n// ---- Engine Sessions API ----\nfastify.post('/api/engine/session', async (req, reply) => {\n const body = req.body || {}\n const id = randomUUID()\n engineSessions.set(id, { clients: new Set(), projectId: body.projectId || null, teamId: body.teamId || null, createdAt: Date.now() })\n appendJsonl(id, { kind: 'session_created', projectId: body.projectId || null, teamId: body.teamId || null })\n const idx = loadIndex(); const now = Date.now()\n idx.sessions = [...(idx.sessions || []), { id, createdAt: now, lastActive: now, title: '' }]\n saveIndex(idx)\n reply.code(201).send({ id })\n})\n\nfastify.get('/api/engine/:id/events', async (req, reply) => {\n const id = req.params.id\n const sess = engineSessions.get(id)\n if (!sess) return reply.code(404).send({ error: 'not_found' })\n reply.raw.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' })\n reply.raw.flushHeaders()\n sess.clients.add(reply)\n req.raw.on('close', () => { sess.clients.delete(reply) })\n})\n\nfastify.post('/api/engine/:id/input', async (req, reply) => {\n const id = req.params.id\n const sess = engineSessions.get(id)\n if (!sess) return reply.code(404).send({ error: 'not_found' })\n const body = req.body || {}\n\n if (body.controlResponse) {\n for (const c of Array.from(sess.clients)) sseWrite(c, 'control_response', body.controlResponse)\n return reply.send({ ok: true })\n }\n\n const text = String(body.text || '')\n appendJsonl(id, { kind: 'user_input', text })\n const idx = loadIndex(); const now = Date.now()\n idx.sessions = (idx.sessions || []).map(s => s.id === id ? { ...s, lastActive: now, title: s.title || text.slice(0, 120) } : s)\n saveIndex(idx)\n\n const cliRoot = findCliRoot()\n if (!cliRoot) {\n for (const c of Array.from(sess.clients)) sseWrite(c, 'error', { text: 'No CLI found. Build dist/cli.js or install Bun.' })\n return reply.send({ ok: false, error: 'no_cli' })\n }\n const dist = resolve(cliRoot, 'dist', 'cli.js')\n const exe = fs.existsSync(dist) ? process.execPath : 'bun'\n const cliArgs = fs.existsSync(dist) ? [dist] : [resolve(cliRoot, 'src', 'entrypoints', 'cli.tsx')]\n const workDir = getWorkDir(sess.projectId)\n const args = [...cliArgs, '-p', '--output-format', 'stream-json', '--verbose', '--include-partial-messages', '--resume', id, '--add-dir', workDir]\n const env = { ...process.env, ...settings.env }\n const child = spawn(exe, args, { cwd: workDir, env })\n\n let buf = ''\n child.stdout.on('data', chunk => {\n buf += chunk.toString()\n let nl\n while ((nl = buf.indexOf('\\n')) !== -1) {\n const line = buf.slice(0, nl).trim(); buf = buf.slice(nl + 1)\n if (!line) continue\n try {\n const obj = JSON.parse(line)\n for (const c of Array.from(sess.clients)) sseWrite(c, 'event', obj)\n appendJsonl(id, { kind: 'event', event: obj })\n } catch {\n for (const c of Array.from(sess.clients)) sseWrite(c, 'text', { data: line })\n }\n }\n })\n child.stderr.on('data', chunk => {\n const data = chunk.toString()\n for (const c of Array.from(sess.clients)) sseWrite(c, 'stderr', { chunk: data })\n appendJsonl(id, { kind: 'stderr', data })\n })\n child.on('close', code => {\n for (const c of Array.from(sess.clients)) sseWrite(c, 'complete', { code })\n appendJsonl(id, { kind: 'complete', code })\n })\n\n reply.send({ ok: true })\n})\n\n// ---- Sessions history API ----\nfastify.get('/api/sessions', async (_req, reply) => {\n const idx = loadIndex()\n const list = (idx.sessions || []).sort((a, b) => (b.lastActive || 0) - (a.lastActive || 0))\n reply.send({ sessions: list })\n})\nfastify.get('/api/sessions/:id/history', async (req, reply) => {\n const id = req.params.id\n const p = join(SESSIONS_DIR, id + '.jsonl')\n if (!fs.existsSync(p)) return reply.code(404).send({ error: 'not found' })\n const lines = fs.readFileSync(p, 'utf-8').split('\\n').filter(Boolean).slice(-500)\n reply.send({ lines: lines.map(l => { try { return JSON.parse(l) } catch { return { raw: l } } }) })\n})\nfastify.delete('/api/sessions/:id', async (req, reply) => {\n const id = req.params.id\n const p = join(SESSIONS_DIR, id + '.jsonl')\n try { if (fs.existsSync(p)) fs.rmSync(p) } catch {}\n const idx = loadIndex(); idx.sessions = (idx.sessions || []).filter(s => s.id !== id); saveIndex(idx)\n engineSessions.delete(id)\n reply.send({ ok: true })\n})\n\n// ---- Files API (workspace only) ----\nfastify.get('/api/files/tree', async (req, reply) => {\n const q = req.query || {}\n const root = getWorkDir(q.projectId || null)\n const rel = q.path || '.'; const depth = Math.min(Number(q.depth || 2), 4)\n const start = safeJoin(root, rel)\n function walk(dir, d) {\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n return entries.map(e => {\n const p = join(dir, e.name)\n const node = { name: e.name, path: p.substring(root.length + 1), dir: e.isDirectory() }\n if (e.isDirectory() && d > 0) node.children = walk(p, d - 1)\n return node\n })\n }\n reply.send({ tree: walk(start, depth) })\n})\nfastify.get('/api/files', async (req, reply) => {\n const q = req.query || {}\n const root = getWorkDir(q.projectId || null); const rel = q.path || ''; const p = safeJoin(root, rel)\n if (!fs.existsSync(p)) return reply.code(404).send({ error: 'not found' })\n reply.send({ path: rel, text: fs.readFileSync(p, 'utf-8') })\n})\nfastify.post('/api/files/diff', async (req, reply) => {\n const body = req.body || {}\n const root = getWorkDir(body.projectId || null); const rel = body.path || ''; const nextText = String(body.nextText || '')\n const p = safeJoin(root, rel); const prevText = fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : ''\n reply.send({ patch: createTwoFilesPatch(rel, rel, prevText, nextText) })\n})\nfastify.post('/api/files/patch', async (req, reply) => {\n const body = req.body || {}\n const root = getWorkDir(body.projectId || null); const rel = body.path || ''; const patch = String(body.patch || '')\n const p = safeJoin(root, rel); const prevText = fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : ''\n const nextText = applyPatch(prevText, patch)\n if (nextText === false) return reply.code(400).send({ ok: false, error: 'failed_to_apply_patch' })\n await fse.ensureDir(join(p, '..'))\n const backupDir = resolve(DB_DIR, 'backups'); try { fse.ensureDirSync(backupDir) } catch {}\n const stamp = new Date().toISOString().replace(/[:.]/g, '-')\n if (fs.existsSync(p)) fs.copyFileSync(p, join(backupDir, rel.replace(/[\\\\/]/g, '__') + '.' + stamp + '.bak'))\n fs.writeFileSync(p, nextText, 'utf-8')\n reply.send({ ok: true })\n})\n\n// ---- LLM direct generate ----\nfastify.post('/api/llm/generate', async (req, reply) => {\n const body = req.body || {}\n const provider = String(body.provider || 'openai')\n const model = String(body.model || '')\n const prompt = String(body.prompt || '')\n const env = settings.env\n\n let baseURL, apiKey, keyHeader, keyPrefix\n if (provider === 'openai') {\n baseURL = env.OPENAI_BASE_URL || 'https://api.openai.com/v1'\n apiKey = env.OPENAI_API_KEY || ''; keyHeader = env.OPENAI_API_KEY_HEADER || 'Authorization'; keyPrefix = env.OPENAI_API_KEY_PREFIX || 'Bearer '\n } else if (provider === 'qwen') {\n baseURL = env.QWEN_BASE_URL || 'https://dashscope.aliyuncs.com/compatible-mode/v1'\n apiKey = env.QWEN_API_KEY || env.DASHSCOPE_API_KEY || ''; keyHeader = env.QWEN_API_KEY_HEADER || 'Authorization'; keyPrefix = env.QWEN_API_KEY_PREFIX || 'Bearer '\n } else if (provider === 'deepseek') {\n baseURL = env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com/v1'\n apiKey = env.DEEPSEEK_API_KEY || ''; keyHeader = env.DEEPSEEK_API_KEY_HEADER || 'Authorization'; keyPrefix = env.DEEPSEEK_API_KEY_PREFIX || 'Bearer '\n } else if (provider === 'anthropic') {\n baseURL = env.ANTHROPIC_BASE_URL || 'https://api.anthropic.com'\n apiKey = env.ANTHROPIC_API_KEY || ''\n } else {\n return reply.code(400).send({ error: 'unknown_provider' })\n }\n\n try {\n if (provider === 'anthropic') {\n const res = await fetch(`${baseURL}/v1/messages`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' },\n body: JSON.stringify({ model: model || 'claude-3-haiku-20240307', max_tokens: 4096, messages: [{ role: 'user', content: prompt }] })\n })\n const d = await res.json()\n const text = d?.content?.[0]?.text || ''\n return reply.send({ text })\n } else {\n const res = await fetch(`${baseURL}/chat/completions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', [keyHeader]: `${keyPrefix}${apiKey}` },\n body: JSON.stringify({ model, messages: [{ role: 'user', content: prompt }] })\n })\n const d = await res.json()\n const text = d?.choices?.[0]?.message?.content || ''\n return reply.send({ text })\n }\n } catch (e) {\n reply.code(500).send({ error: String(e) })\n }\n})\n\nfastify.listen({ port: PORT, host: '0.0.0.0' }, (err, address) => {\n if (err) { console.error(err); process.exit(1) }\n console.log(`[server] listening at ${address}`)\n})\n"}, "699e5c6640c8891f2b6641e3176a70be683cc4e0": {"t": 1785753900.876859, "n": 21218, "h": "86f4b0d10f1429b0", "c": "import Fastify from 'fastify'\nimport fastifyCors from '@fastify/cors'\nimport fastifyWebsocket from '@fastify/websocket'\nimport fastifyStatic from '@fastify/static'\nimport { fileURLToPath } from 'url'\nimport { dirname, join, resolve } from 'path'\nimport { spawn } from 'child_process'\nimport { randomUUID } from 'crypto'\nimport { createHash } from 'crypto'\nimport fs from 'fs'\nimport fse from 'fs-extra'\nimport { createTwoFilesPatch, applyPatch } from 'diff'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst PORT = process.env.PORT ? Number(process.env.PORT) : 5178\n\nconst fastify = Fastify({ logger: false })\n\n// --- DB / data paths ---\nconst DB_DIR = resolve(__dirname, 'db')\nconst SESSIONS_DIR = join(DB_DIR, 'sessions')\nconst DATA_DIR = resolve(__dirname, 'data', 'projects')\ntry { fs.mkdirSync(DB_DIR, { recursive: true }); fs.mkdirSync(SESSIONS_DIR, { recursive: true }); fs.mkdirSync(DATA_DIR, { recursive: true }) } catch {}\n\nfunction idxPath() { return join(DB_DIR, 'session-index.json') }\nfunction loadIndex() { try { return JSON.parse(fs.readFileSync(idxPath(), 'utf-8')) } catch { return { sessions: [] } } }\nfunction saveIndex(obj) { fs.writeFileSync(idxPath(), JSON.stringify(obj, null, 2)) }\nfunction appendJsonl(id, obj) { fs.appendFileSync(join(SESSIONS_DIR, id + '.jsonl'), JSON.stringify({ ts: Date.now(), ...obj }) + '\\n') }\n\nfunction computeEtag(text) { return '\"' + createHash('sha1').update(text).digest('hex').slice(0, 16) + '\"' }\n\nawait fastify.register(fastifyCors, { origin: true })\nawait fastify.register(fastifyWebsocket)\nawait fastify.register(fastifyStatic, { root: resolve(__dirname, '../frontend-vue/dist'), prefix: '/' })\n\n// In-memory settings\nconst settings = { env: {} }\n\n// Load persisted user settings on startup\n;(function loadPersistedSettings() {\n try {\n const userFile = join(DB_DIR, 'settings.user.json')\n if (fs.existsSync(userFile)) {\n const data = JSON.parse(fs.readFileSync(userFile, 'utf-8'))\n if (data?.env) settings.env = { ...settings.env, ...data.env }\n }\n } catch {}\n})()\n\n// Engine sessions map: id -> { clients: Set, projectId, teamId }\nconst engineSessions = new Map()\n\nfunction sseWrite(res, event, data) {\n try { res.raw.write(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`) } catch {}\n}\n\n// ---- Helpers ----\nfunction findCliRoot() {\n const candidates = [\n resolve(__dirname, '../claude-code-source-main'),\n resolve(process.cwd(), 'claude-code-source-main'),\n process.env.CC_CLI_ROOT,\n ].filter(Boolean)\n for (const c of candidates) {\n try {\n if (!c) continue\n if (fs.existsSync(join(c, 'dist', 'cli.js'))) return c\n if (fs.existsSync(join(c, 'src', 'entrypoints', 'cli.tsx'))) return c\n } catch {}\n }\n return null\n}\nfunction getWorkDir(projectId) {\n if (projectId) {\n const proj = loadProjectMeta(projectId)\n if (proj?.workspaceRoot && fs.existsSync(proj.workspaceRoot)) return proj.workspaceRoot\n }\n const p = process.env.CC_WORKDIR || resolve(__dirname, '../workspace')\n try { fs.mkdirSync(p, { recursive: true }) } catch {}\n return p\n}\nfunction safeJoin(root, p) {\n const resolved = resolve(root, p)\n if (!resolved.startsWith(resolve(root))) throw new Error('path outside workspace')\n return resolved\n}\n\n// ---- Layered Settings (M3) ----\nconst SETTINGS_FILES = {\n user: join(DB_DIR, 'settings.user.json'),\n local: join(DB_DIR, 'settings.local.json'),\n policy: join(DB_DIR, 'settings.policy.json'),\n}\nfunction projectSettingsPath(pid) { return resolve(DATA_DIR, pid, '.claude', 'settings.project.json') }\nfunction ensureDirOf(file) { try { fs.mkdirSync(join(file, '..'), { recursive: true }) } catch {} }\nfunction readJson(file) { try { return JSON.parse(fs.readFileSync(file, 'utf-8')) } catch { return {} } }\nfunction deepMerge(a, b) {\n a = (a && typeof a === 'object') ? a : {}\n if (!b || typeof b !== 'object') return a\n const out = Array.isArray(a) ? [...a] : { ...a }\n for (const [k, v] of Object.entries(b)) {\n if (v && typeof v === 'object' && !Array.isArray(v)) out[k] = deepMerge(out[k] || {}, v)\n else out[k] = v\n }\n return out\n}\nfunction writeJsonWithBackup(file, obj) {\n ensureDirOf(file)\n const text = JSON.stringify(obj, null, 2)\n const backupDir = resolve(DB_DIR, 'backups'); try { fs.mkdirSync(backupDir, { recursive: true }) } catch {}\n if (fs.existsSync(file)) {\n const stamp = new Date().toISOString().replace(/[:.]/g, '-')\n fs.copyFileSync(file, join(backupDir, file.replace(/[\\\\/:]/g, '__') + '.' + stamp + '.bak'))\n }\n fs.writeFileSync(file, text, 'utf-8')\n return computeEtag(text)\n}\nfunction loadLayer(scope, pid) {\n if (scope === 'user') return readJson(SETTINGS_FILES.user)\n if (scope === 'local') return readJson(SETTINGS_FILES.local)\n if (scope === 'policy') return readJson(SETTINGS_FILES.policy)\n if (scope === 'project') { if (!pid) return {}; return readJson(projectSettingsPath(pid)) }\n if (scope === 'flags') { return { flags: {} } }\n return {}\n}\nfunction effectiveSettings(pid) {\n let eff = {}\n for (const s of ['user', 'project', 'local', 'policy', 'flags']) eff = deepMerge(eff, loadLayer(s, pid))\n return eff\n}\n\nfastify.get('/api/engine/settings/sources', async (req, reply) => {\n const q = (req.query || {}); const pid = q.projectId || q.pid || ''\n const list = [\n { scope: 'user', path: SETTINGS_FILES.user, writable: true },\n { scope: 'project', path: pid ? projectSettingsPath(pid) : null, writable: !!pid },\n { scope: 'local', path: SETTINGS_FILES.local, writable: true },\n { scope: 'policy', path: SETTINGS_FILES.policy, writable: false },\n { scope: 'flags', path: 'build-time', writable: false },\n ]\n reply.send({ sources: list })\n})\nfastify.get('/api/engine/settings', async (req, reply) => {\n const q = (req.query || {}); const scope = String(q.scope || 'effective'); const pid = q.projectId || q.pid || ''\n let data\n if (scope === 'effective') data = effectiveSettings(pid)\n else if (['user', 'project', 'local', 'policy', 'flags'].includes(scope)) data = loadLayer(scope, pid)\n else return reply.code(400).send({ error: 'invalid_scope' })\n reply.header('ETag', computeEtag(JSON.stringify(data))).send(data)\n})\nfastify.put('/api/engine/settings', async (req, reply) => {\n const q = (req.query || {}); const scope = String(q.scope || '').toLowerCase(); const pid = q.projectId || q.pid || ''\n if (!['user', 'project', 'local'].includes(scope)) return reply.code(400).send({ error: 'scope_not_writable' })\n const file = scope === 'project' ? (pid ? projectSettingsPath(pid) : null) : SETTINGS_FILES[scope]\n if (!file) return reply.code(400).send({ error: 'project_required' })\n const ifMatch = req.headers['if-match']\n const prevText = fs.existsSync(file) ? fs.readFileSync(file, 'utf-8') : ''\n if (ifMatch && computeEtag(prevText) !== ifMatch) return reply.code(412).send({ error: 'etag_mismatch', current: computeEtag(prevText) })\n let body = (req.body || {})\n if (typeof body === 'string') { try { body = JSON.parse(body) } catch { body = {} } }\n const etag = writeJsonWithBackup(file, body)\n // Reload env into memory after user settings update\n try { const eff = effectiveSettings(pid || null); if (eff && eff.env) settings.env = eff.env } catch {}\n try { for (const sess of engineSessions.values()) { for (const c of Array.from(sess.clients)) sseWrite(c, 'settings_updated', { scope, projectId: pid || null, etag }) } } catch {}\n reply.header('ETag', etag).send({ ok: true, etag })\n})\n\n// ---- Projects API ----\nconst PROJECTS_INDEX = join(DB_DIR, 'projects.json')\nfunction loadProjects() { try { return JSON.parse(fs.readFileSync(PROJECTS_INDEX, 'utf-8')) } catch { return { projects: [] } } }\nfunction saveProjects(obj) { fs.writeFileSync(PROJECTS_INDEX, JSON.stringify(obj, null, 2)) }\nfunction loadProjectMeta(pid) { return (loadProjects().projects || []).find(p => p.id === pid) || null }\nfunction projectDir(pid) { return resolve(DATA_DIR, pid) }\n\nfastify.get('/api/projects', async (_req, reply) => {\n reply.send(loadProjects())\n})\nfastify.post('/api/projects', async (req, reply) => {\n const body = req.body || {}\n const name = String(body.name || '').trim()\n if (!name) return reply.code(400).send({ error: 'name_required' })\n const id = randomUUID()\n const workspaceRoot = body.workspaceRoot ? String(body.workspaceRoot).trim() : null\n // Create project directory structure\n const dir = projectDir(id)\n fs.mkdirSync(resolve(dir, '.claude', 'teams'), { recursive: true })\n // Create workspace folder if specified and doesn't exist\n if (workspaceRoot) {\n try { fs.mkdirSync(workspaceRoot, { recursive: true }) } catch {}\n }\n const project = { id, name, workspaceRoot: workspaceRoot || null, createdAt: Date.now() }\n const db = loadProjects(); db.projects = [...(db.projects || []), project]; saveProjects(db)\n reply.code(201).send(project)\n})\nfastify.delete('/api/projects/:id', async (req, reply) => {\n const id = req.params.id\n const db = loadProjects()\n const project = (db.projects || []).find(p => p.id === id)\n if (!project) return reply.code(404).send({ error: 'not_found' })\n // Remove project data directory\n try { fs.rmSync(projectDir(id), { recursive: true, force: true }) } catch {}\n // Remove workspace folder only if it was created by us (workspaceRoot set and exists)\n // We do NOT delete user-specified external workspaceRoot to avoid data loss\n db.projects = db.projects.filter(p => p.id !== id); saveProjects(db)\n reply.send({ ok: true })\n})\n\n// ---- Teams API ----\nfunction teamsDir(pid) { return resolve(projectDir(pid), '.claude', 'teams') }\nfunction teamFile(pid, tid) { return resolve(teamsDir(pid), tid, 'config.json') }\n\nfastify.get('/api/projects/:pid/teams', async (req, reply) => {\n const pid = req.params.pid\n const dir = teamsDir(pid)\n if (!fs.existsSync(dir)) return reply.send({ teams: [] })\n const teams = fs.readdirSync(dir, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => {\n const cfg = readJson(teamFile(pid, e.name))\n return { id: e.name, name: cfg.name || e.name, memberCount: (cfg.members || []).length }\n })\n reply.send({ teams })\n})\nfastify.post('/api/projects/:pid/teams', async (req, reply) => {\n const pid = req.params.pid\n const body = req.body || {}\n const name = String(body.name || '').trim()\n if (!name) return reply.code(400).send({ error: 'name_required' })\n const tid = randomUUID()\n const dir = resolve(teamsDir(pid), tid)\n fs.mkdirSync(dir, { recursive: true })\n const cfg = { name, members: [], routing: 'sequential', createdAt: Date.now() }\n const text = JSON.stringify(cfg, null, 2)\n fs.writeFileSync(teamFile(pid, tid), text)\n const etag = computeEtag(text)\n reply.code(201).header('ETag', etag).send({ id: tid, ...cfg })\n})\nfastify.get('/api/projects/:pid/teams/:tid', async (req, reply) => {\n const { pid, tid } = req.params\n const file = teamFile(pid, tid)\n if (!fs.existsSync(file)) return reply.code(404).send({ error: 'not_found' })\n const text = fs.readFileSync(file, 'utf-8')\n const etag = computeEtag(text)\n reply.header('ETag', etag).send(JSON.parse(text))\n})\nfastify.put('/api/projects/:pid/teams/:tid', async (req, reply) => {\n const { pid, tid } = req.params\n const file = teamFile(pid, tid)\n const ifMatch = req.headers['if-match']\n const prevText = fs.existsSync(file) ? fs.readFileSync(file, 'utf-8') : ''\n if (ifMatch && computeEtag(prevText) !== ifMatch) return reply.code(412).send({ error: 'etag_mismatch', current: computeEtag(prevText) })\n let body = req.body || {}\n if (typeof body === 'string') { try { body = JSON.parse(body) } catch { body = {} } }\n const text = JSON.stringify(body, null, 2)\n fs.mkdirSync(resolve(teamsDir(pid), tid), { recursive: true })\n fs.writeFileSync(file, text)\n const etag = computeEtag(text)\n reply.header('ETag', etag).send({ ok: true, etag })\n})\nfastify.delete('/api/projects/:pid/teams/:tid', async (req, reply) => {\n const { pid, tid } = req.params\n try { fs.rmSync(resolve(teamsDir(pid), tid), { recursive: true, force: true }) } catch {}\n reply.send({ ok: true })\n})\n\n// ---- Engine Sessions API ----\nfastify.post('/api/engine/session', async (req, reply) => {\n const body = req.body || {}\n const id = randomUUID()\n engineSessions.set(id, { clients: new Set(), projectId: body.projectId || null, teamId: body.teamId || null, createdAt: Date.now() })\n appendJsonl(id, { kind: 'session_created', projectId: body.projectId || null, teamId: body.teamId || null })\n const idx = loadIndex(); const now = Date.now()\n idx.sessions = [...(idx.sessions || []), { id, createdAt: now, lastActive: now, title: '' }]\n saveIndex(idx)\n reply.code(201).send({ id })\n})\n\nfastify.get('/api/engine/:id/events', async (req, reply) => {\n const id = req.params.id\n const sess = engineSessions.get(id)\n if (!sess) return reply.code(404).send({ error: 'not_found' })\n reply.raw.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' })\n reply.raw.flushHeaders()\n sess.clients.add(reply)\n req.raw.on('close', () => { sess.clients.delete(reply) })\n})\n\nfastify.post('/api/engine/:id/input', async (req, reply) => {\n const id = req.params.id\n const sess = engineSessions.get(id)\n if (!sess) return reply.code(404).send({ error: 'not_found' })\n const body = req.body || {}\n\n if (body.controlResponse) {\n for (const c of Array.from(sess.clients)) sseWrite(c, 'control_response', body.controlResponse)\n return reply.send({ ok: true })\n }\n\n const text = String(body.text || '')\n appendJsonl(id, { kind: 'user_input', text })\n const idx = loadIndex(); const now = Date.now()\n idx.sessions = (idx.sessions || []).map(s => s.id === id ? { ...s, lastActive: now, title: s.title || text.slice(0, 120) } : s)\n saveIndex(idx)\n\n const cliRoot = findCliRoot()\n if (!cliRoot) {\n for (const c of Array.from(sess.clients)) sseWrite(c, 'error', { text: 'No CLI found. Build dist/cli.js or install Bun.' })\n return reply.send({ ok: false, error: 'no_cli' })\n }\n const dist = resolve(cliRoot, 'dist', 'cli.js')\n const exe = fs.existsSync(dist) ? process.execPath : 'bun'\n const cliArgs = fs.existsSync(dist) ? [dist] : [resolve(cliRoot, 'src', 'entrypoints', 'cli.tsx')]\n const workDir = getWorkDir(sess.projectId)\n const args = [...cliArgs, '-p', '--output-format', 'stream-json', '--verbose', '--include-partial-messages', '--resume', id, '--add-dir', workDir]\n const env = { ...process.env, ...settings.env }\n const child = spawn(exe, args, { cwd: workDir, env })\n\n let buf = ''\n child.stdout.on('data', chunk => {\n buf += chunk.toString()\n let nl\n while ((nl = buf.indexOf('\\n')) !== -1) {\n const line = buf.slice(0, nl).trim(); buf = buf.slice(nl + 1)\n if (!line) continue\n try {\n const obj = JSON.parse(line)\n for (const c of Array.from(sess.clients)) sseWrite(c, 'event', obj)\n appendJsonl(id, { kind: 'event', event: obj })\n } catch {\n for (const c of Array.from(sess.clients)) sseWrite(c, 'text', { data: line })\n }\n }\n })\n child.stderr.on('data', chunk => {\n const data = chunk.toString()\n for (const c of Array.from(sess.clients)) sseWrite(c, 'stderr', { chunk: data })\n appendJsonl(id, { kind: 'stderr', data })\n })\n child.on('close', code => {\n for (const c of Array.from(sess.clients)) sseWrite(c, 'complete', { code })\n appendJsonl(id, { kind: 'complete', code })\n })\n\n reply.send({ ok: true })\n})\n\n// ---- Sessions history API ----\nfastify.get('/api/sessions', async (_req, reply) => {\n const idx = loadIndex()\n const list = (idx.sessions || []).sort((a, b) => (b.lastActive || 0) - (a.lastActive || 0))\n reply.send({ sessions: list })\n})\nfastify.get('/api/sessions/:id/history', async (req, reply) => {\n const id = req.params.id\n const p = join(SESSIONS_DIR, id + '.jsonl')\n if (!fs.existsSync(p)) return reply.code(404).send({ error: 'not found' })\n const lines = fs.readFileSync(p, 'utf-8').split('\\n').filter(Boolean).slice(-500)\n reply.send({ lines: lines.map(l => { try { return JSON.parse(l) } catch { return { raw: l } } }) })\n})\nfastify.delete('/api/sessions/:id', async (req, reply) => {\n const id = req.params.id\n const p = join(SESSIONS_DIR, id + '.jsonl')\n try { if (fs.existsSync(p)) fs.rmSync(p) } catch {}\n const idx = loadIndex(); idx.sessions = (idx.sessions || []).filter(s => s.id !== id); saveIndex(idx)\n engineSessions.delete(id)\n reply.send({ ok: true })\n})\n\n// ---- Files API (workspace only) ----\nfastify.get('/api/files/tree', async (req, reply) => {\n const q = req.query || {}\n const root = getWorkDir(q.projectId || null)\n const rel = q.path || '.'; const depth = Math.min(Number(q.depth || 2), 4)\n const start = safeJoin(root, rel)\n function walk(dir, d) {\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n return entries.map(e => {\n const p = join(dir, e.name)\n const node = { name: e.name, path: p.substring(root.length + 1), dir: e.isDirectory() }\n if (e.isDirectory() && d > 0) node.children = walk(p, d - 1)\n return node\n })\n }\n reply.send({ tree: walk(start, depth) })\n})\nfastify.get('/api/files', async (req, reply) => {\n const q = req.query || {}\n const root = getWorkDir(q.projectId || null); const rel = q.path || ''; const p = safeJoin(root, rel)\n if (!fs.existsSync(p)) return reply.code(404).send({ error: 'not found' })\n reply.send({ path: rel, text: fs.readFileSync(p, 'utf-8') })\n})\nfastify.post('/api/files/diff', async (req, reply) => {\n const body = req.body || {}\n const root = getWorkDir(body.projectId || null); const rel = body.path || ''; const nextText = String(body.nextText || '')\n const p = safeJoin(root, rel); const prevText = fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : ''\n reply.send({ patch: createTwoFilesPatch(rel, rel, prevText, nextText) })\n})\nfastify.post('/api/files/patch', async (req, reply) => {\n const body = req.body || {}\n const root = getWorkDir(body.projectId || null); const rel = body.path || ''; const patch = String(body.patch || '')\n const p = safeJoin(root, rel); const prevText = fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : ''\n const nextText = applyPatch(prevText, patch)\n if (nextText === false) return reply.code(400).send({ ok: false, error: 'failed_to_apply_patch' })\n await fse.ensureDir(join(p, '..'))\n const backupDir = resolve(DB_DIR, 'backups'); try { fse.ensureDirSync(backupDir) } catch {}\n const stamp = new Date().toISOString().replace(/[:.]/g, '-')\n if (fs.existsSync(p)) fs.copyFileSync(p, join(backupDir, rel.replace(/[\\\\/]/g, '__') + '.' + stamp + '.bak'))\n fs.writeFileSync(p, nextText, 'utf-8')\n reply.send({ ok: true })\n})\n\n// ---- LLM direct generate ----\nfastify.post('/api/llm/generate', async (req, reply) => {\n const body = req.body || {}\n const provider = String(body.provider || 'openai')\n const model = String(body.model || '')\n const prompt = String(body.prompt || '')\n const env = settings.env\n\n let baseURL, apiKey, keyHeader, keyPrefix\n if (provider === 'openai') {\n baseURL = env.OPENAI_BASE_URL || 'https://api.openai.com/v1'\n apiKey = env.OPENAI_API_KEY || ''; keyHeader = env.OPENAI_API_KEY_HEADER || 'Authorization'; keyPrefix = env.OPENAI_API_KEY_PREFIX || 'Bearer '\n } else if (provider === 'qwen') {\n baseURL = env.QWEN_BASE_URL || 'https://dashscope.aliyuncs.com/compatible-mode/v1'\n apiKey = env.QWEN_API_KEY || env.DASHSCOPE_API_KEY || ''; keyHeader = env.QWEN_API_KEY_HEADER || 'Authorization'; keyPrefix = env.QWEN_API_KEY_PREFIX || 'Bearer '\n } else if (provider === 'deepseek') {\n baseURL = env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com/v1'\n apiKey = env.DEEPSEEK_API_KEY || ''; keyHeader = env.DEEPSEEK_API_KEY_HEADER || 'Authorization'; keyPrefix = env.DEEPSEEK_API_KEY_PREFIX || 'Bearer '\n } else if (provider === 'anthropic') {\n baseURL = env.ANTHROPIC_BASE_URL || 'https://api.anthropic.com'\n apiKey = env.ANTHROPIC_API_KEY || ''\n } else {\n return reply.code(400).send({ error: 'unknown_provider' })\n }\n\n try {\n if (provider === 'anthropic') {\n const res = await fetch(`${baseURL}/v1/messages`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' },\n body: JSON.stringify({ model: model || 'claude-3-haiku-20240307', max_tokens: 4096, messages: [{ role: 'user', content: prompt }] })\n })\n const d = await res.json()\n const text = d?.content?.[0]?.text || ''\n return reply.send({ text })\n } else {\n const res = await fetch(`${baseURL}/chat/completions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', [keyHeader]: `${keyPrefix}${apiKey}` },\n body: JSON.stringify({ model, messages: [{ role: 'user', content: prompt }] })\n })\n const d = await res.json()\n const text = d?.choices?.[0]?.message?.content || ''\n return reply.send({ text })\n }\n } catch (e) {\n reply.code(500).send({ error: String(e) })\n }\n})\n\nfastify.listen({ port: PORT, host: '0.0.0.0' }, (err, address) => {\n if (err) { console.error(err); process.exit(1) }\n console.log(`[server] listening at ${address}`)\n})\n"}, "cf5c024d9abf66b6e26f3784f508e991e41ff2da": {"t": 1785753902.1291888, "n": 10311, "h": "47ce7801d4c6435f", "c": "import Fastify from 'fastify'\nimport fastifyCors from '@fastify/cors'\nimport fastifyWebsocket from '@fastify/websocket'\nimport fastifyStatic from '@fastify/static'\nimport { fileURLToPath } from 'url'\nimport { dirname, join, resolve } from 'path'\nimport { spawn } from 'child_process'\nimport { randomUUID } from 'crypto'\nimport fs from 'fs'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst PORT = process.env.PORT ? Number(process.env.PORT) : 5178\n\nconst fastify = Fastify({ logger: false })\\n// --- DB paths ---\\nconst DB_DIR = resolve(__dirname, 'db')\\nconst SESSIONS_DIR = join(DB_DIR, 'sessions')\\ntry { fs.mkdirSync(DB_DIR, { recursive: true }); fs.mkdirSync(SESSIONS_DIR, { recursive: true }) } catch {}\\nfunction idxPath() { return join(DB_DIR, 'session-index.json') }\\nfunction loadIndex() { try { return JSON.parse(fs.readFileSync(idxPath(), 'utf-8')) } catch { return { sessions: [] } } }\\nfunction saveIndex(obj) { fs.writeFileSync(idxPath(), JSON.stringify(obj, null, 2)) }\\nfunction appendJsonl(id, obj) { fs.appendFileSync(join(SESSIONS_DIR, id + '.jsonl'), JSON.stringify({ ts: Date.now(), ...obj }) + '\\n') }\nawait fastify.register(fastifyCors, { origin: true })\nawait fastify.register(fastifyWebsocket)\nawait fastify.register(fastifyStatic, { root: resolve(__dirname, '../web'), prefix: '/' })\n\n// In-memory settings (can be extended or persisted later)\nconst ALLOWED_ENV = new Set([,\n 'OPENAI_API_KEY','OPENAI_BASE_URL','OPENAI_API_KEY_HEADER','OPENAI_API_KEY_PREFIX','OPENAI_DEFAULT_MODEL',\n 'QWEN_API_KEY','DASHSCOPE_API_KEY','QWEN_BASE_URL','QWEN_API_KEY_HEADER','QWEN_API_KEY_PREFIX','QWEN_DEFAULT_MODEL',\n 'DEEPSEEK_API_KEY','DEEPSEEK_BASE_URL','DEEPSEEK_API_KEY_HEADER','DEEPSEEK_API_KEY_PREFIX','DEEPSEEK_DEFAULT_MODEL'\n])\nconst settings = { env: {} as Record }\n\nconst sessions = new Map()\n\nfunction findCliRoot() {\n const candidates = [\n resolve(__dirname, '../claude-code-source-main'),\n resolve(process.cwd(), 'claude-code-source-main'),\n process.env.CC_CLI_ROOT,\n ].filter(Boolean) as string[]\n for (const c of candidates) {\n try {\n if (!c) continue\n if (fs.existsSync(join(c, 'dist', 'cli.js'))) return c\n if (fs.existsSync(join(c, 'src', 'entrypoints', 'cli.tsx'))) return c\n } catch {}\n }\n return null\n}\nfunction hasBun() {\n try {\n const { spawnSync } = await import('child_process')\n return spawnSync('bun', ['-v'], { stdio: 'ignore' }).status === 0\n } catch { return false }\n}\nfunction getEngineCommand(cliRoot:string) {\n const dist = join(cliRoot, 'dist', 'cli.js')\n if (fs.existsSync(dist)) return { exe: process.execPath, args: [dist] as string[] }\n // Dev path: use bun to run entry if present\n if (await hasBun()) {\n const entry = join(cliRoot, 'src', 'entrypoints', 'cli.tsx')\n const envFile = join(cliRoot, '.env')\n const args: string[] = []\n if (fs.existsSync(envFile)) args.push(`--env-file=${envFile}`)\n args.push(entry)\n return { exe: 'bun', args }\n }\n return null\n}\n\n// Settings endpoints\nfastify.get('/api/settings', async (_req, reply) => {\n reply.send({ env: settings.env })\n})\nfastify.post('/api/settings', async (req, reply) => {\n const body = (req as any).body || {}\n const inputEnv = body.env || {}\n for (const [k,v] of Object.entries(inputEnv)) {\n if (typeof v === 'string' && ALLOWED_ENV.has(k)) {\n settings.env[k] = v\n }\n }\n reply.send({ ok: true, env: settings.env })\n})\n\n// Create session\nfastify.post('/api/sessions', async (_req, reply) => {\n const id = randomUUID()\n sessions.set(id, { createdAt: Date.now(), turns: 0 })\n return reply.send({ id })\n})\n\n// Streaming WS\nfastify.get('/ws/:id', { websocket: true }, (connection, req) => {\n const id = (req.params as any).id as string\n if (!sessions.has(id)) { connection.socket.close(); return }\n\n let lastChild: import('child_process').ChildProcessWithoutNullStreams | null = null\\n connection.socket.on('message', async (raw) => {\\n let msg: any\\n try { msg = JSON.parse(raw.toString()) } catch { return }\\n if (msg.type === 'user_input') {\\n // persist user input\\n appendJsonl(id, { kind: 'user_input', text: String(msg.text||'') })\\n const idx = loadIndex();\\n const now = Date.now();\\n if (!(idx.sessions||[]).some(s=>s.id===id)) { idx.sessions.push({ id, createdAt: now, lastActive: now, title: String(msg.text||'').slice(0,120) }) }\\n else { idx.sessions = idx.sessions.map(s=> s.id===id? { ...s, lastActive: now, title: s.title|| String(msg.text||'').slice(0,120) }: s) }\\n saveIndex(idx)\\n\n const cliRoot = findCliRoot()\n const cmd = cliRoot ? await getEngineCommand(cliRoot) : null\n if (!cmd) {\n connection.socket.send(JSON.stringify({ type: 'status', level: 'error', text: 'No CLI found. Build dist/cli.js or install Bun.' }))\n return\n }\n // Kill previous if any\n try { lastChild?.kill() } catch {}\n\n // Use stream-json with verbose and partial messages\n const args = ['-p', '--output-format', 'stream-json', '--verbose', '--include-partial-messages', '--resume', id]\n const env = { ...process.env, ...settings.env }\n const child = spawn(cmd.exe, [...cmd.args, ...args, '--add-dir', getWorkDir()], { cwd: getWorkDir(), env })\n lastChild = child\n\n let buf = ''\n child.stdout.on('data', (chunk) => {\n buf += chunk.toString()\n let idx\n while ((idx = buf.indexOf('\\n')) !== -1) {\n const line = buf.slice(0, idx).trim()\n buf = buf.slice(idx + 1)\n if (!line) continue\n try {\n const obj = JSON.parse(line)\n connection.socket.send(JSON.stringify({ type: 'event', event: obj })); try { appendJsonl(id, { kind: 'event', event: obj }) } catch {}\\n } catch {\n // Fallback: forward as text\n connection.socket.send(JSON.stringify({ type: 'text', data: line }))\n }\n }\n })\n child.stderr.on('data', (buf) => {\n connection.socket.send(JSON.stringify({ type: 'stderr', chunk: buf.toString() })); try { appendJsonl(id, { kind: 'stderr', data: buf.toString() }) } catch {}\\n })\\n child.on('close', (code) => {\\n connection.socket.send(JSON.stringify({ type: 'complete', code })); try { appendJsonl(id, { kind: 'complete', code }) } catch {}\\n })\\n })\n\n const text = typeof msg.text === 'string' ? msg.text : ''\n if (text) {\n child.stdin.write(text)\n }\n child.stdin.end('\\n')\n }\n })\n})\n\n\n// ---- Sessions API ----\nfastify.get('/api/sessions', async (_req, reply) => {\n const idx = loadIndex()\n const list = (idx.sessions || []).sort((a,b)=> (b.lastActive||0)-(a.lastActive||0))\n reply.send({ sessions: list })\n})\nfastify.get('/api/sessions/:id/history', async (req, reply) => {\n const id = (req.params||{}).id\n const p = join(SESSIONS_DIR, id + '.jsonl')\n if (!fs.existsSync(p)) return reply.code(404).send({ error: 'not found' })\n const lines = fs.readFileSync(p, 'utf-8').split('\\n').filter(Boolean).slice(-500)\n reply.send({ lines: lines.map(l=> { try { return JSON.parse(l) } catch { return { raw: l } } }) })\n})\nfastify.delete('/api/sessions/:id', async (req, reply) => {\n const id = (req.params||{}).id\n const p = join(SESSIONS_DIR, id + '.jsonl')\n try { if (fs.existsSync(p)) fs.rmSync(p) } catch {}\n const idx = loadIndex(); idx.sessions = (idx.sessions||[]).filter(s=>s.id!==id); saveIndex(idx)\n reply.send({ ok:true })\n})\n\n// ---- Files API (CC_WORKDIR only) ----\nimport fse from 'fs-extra'\nimport { createTwoFilesPatch, applyPatch } from 'diff'\nfunction getWorkDir() { const p = process.env.CC_WORKDIR || resolve(__dirname, '../workspace'); try { fs.mkdirSync(p, { recursive: true }) } catch {}; return p }\nfunction safeJoin(root, p) { const resolved = resolve(root, p); if (!resolved.startsWith(resolve(root))) throw new Error('path outside workspace'); return resolved }\n\nfastify.get('/api/files/tree', async (req, reply) => {\n const root = getWorkDir(); const rel = (req.query||{}).path || '.'; const depth = Math.min(Number((req.query||{}).depth||2), 4)\n const start = safeJoin(root, rel)\n function walk(dir, d){\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n return entries.map(e=>{\n const p = join(dir, e.name)\n const node = { name: e.name, path: p.substring(root.length+1), dir: e.isDirectory() }\n if (e.isDirectory() && d>0) node.children = walk(p, d-1)\n return node\n })\n }\n reply.send({ tree: walk(start, depth) })\n})\nfastify.get('/api/files', async (req, reply) => {\n const root = getWorkDir(); const rel = (req.query||{}).path||''; const p = safeJoin(root, rel)\n if (!fs.existsSync(p)) return reply.code(404).send({ error: 'not found' })\n const text = fs.readFileSync(p, 'utf-8')\n reply.send({ path: rel, text })\n})\nfastify.post('/api/files/diff', async (req, reply) => {\n const body = (req.body||{})\n const root = getWorkDir(); const rel = body.path||''; const nextText = String(body.nextText||'')\n const p = safeJoin(root, rel); const prevText = fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : ''\n const patch = createTwoFilesPatch(rel, rel, prevText, nextText)\n reply.send({ patch })\n})\nfastify.post('/api/files/patch', async (req, reply) => {\n const body = (req.body||{})\n const root = getWorkDir(); const rel = body.path||''; const patch = String(body.patch||'')\n const p = safeJoin(root, rel); const prevText = fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : ''\n const nextText = applyPatch(prevText, patch)\n if (nextText === false) return reply.code(400).send({ ok:false, error:'failed_to_apply_patch' })\n await fse.ensureDir(join(p, '..'))\n const backupDir = resolve(DB_DIR, 'backups'); try { fse.ensureDirSync(backupDir) } catch {}\n const stamp = new Date().toISOString().replace(/[:.]/g,'-')\n if (fs.existsSync(p)) fs.copyFileSync(p, join(backupDir, rel.replace(/[\\\\/]/g,'__') + '.' + stamp + '.bak'))\n fs.writeFileSync(p, nextText, 'utf-8')\n reply.send({ ok:true })\n})\nfastify.listen({ port: PORT, host: '0.0.0.0' }, (err, address) => {\n if (err) { console.error(err); process.exit(1) }\n console.log(`[server] listening at ${address}`)\n console.log(`[server] open http://localhost:${PORT}/`)\n})\n\n\n\n\n\n"}, "f4fbe8b10170301313e35a13667925dd5e39bb54": {"t": 1785753903.507009, "n": 10072, "h": "01b671645659b5e8", "c": "import Fastify from 'fastify'\nimport fastifyCors from '@fastify/cors'\nimport fastifyWebsocket from '@fastify/websocket'\nimport fastifyStatic from '@fastify/static'\nimport { fileURLToPath } from 'url'\nimport { dirname, join, resolve } from 'path'\nimport { spawn } from 'child_process'\nimport { randomUUID } from 'crypto'\nimport fs from 'fs'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst PORT = process.env.PORT ? Number(process.env.PORT) : 5178\n\nconst fastify = Fastify({ logger: false })\\n// --- DB paths ---\\nconst DB_DIR = resolve(__dirname, 'db')\\nconst SESSIONS_DIR = join(DB_DIR, 'sessions')\\ntry { fs.mkdirSync(DB_DIR, { recursive: true }); fs.mkdirSync(SESSIONS_DIR, { recursive: true }) } catch {}\\nfunction idxPath() { return join(DB_DIR, 'session-index.json') }\\nfunction loadIndex() { try { return JSON.parse(fs.readFileSync(idxPath(), 'utf-8')) } catch { return { sessions: [] } } }\\nfunction saveIndex(obj) { fs.writeFileSync(idxPath(), JSON.stringify(obj, null, 2)) }\\nfunction appendJsonl(id, obj) { fs.appendFileSync(join(SESSIONS_DIR, id + '.jsonl'), JSON.stringify({ ts: Date.now(), ...obj }) + '\\n') }\nawait fastify.register(fastifyCors, { origin: true })\nawait fastify.register(fastifyWebsocket)\nawait fastify.register(fastifyStatic, { root: resolve(__dirname, '../web'), prefix: '/' })\n\n// In-memory settings (can be extended or persisted later)\nconst ALLOWED_ENV = new Set([,\n 'OPENAI_API_KEY','OPENAI_BASE_URL','OPENAI_API_KEY_HEADER','OPENAI_API_KEY_PREFIX','OPENAI_DEFAULT_MODEL'\n])\nconst settings = { env: {} as Record }\n\nconst sessions = new Map()\n\nfunction findCliRoot() {\n const candidates = [\n resolve(__dirname, '../claude-code-source-main'),\n resolve(process.cwd(), 'claude-code-source-main'),\n process.env.CC_CLI_ROOT,\n ].filter(Boolean) as string[]\n for (const c of candidates) {\n try {\n if (!c) continue\n if (fs.existsSync(join(c, 'dist', 'cli.js'))) return c\n if (fs.existsSync(join(c, 'src', 'entrypoints', 'cli.tsx'))) return c\n } catch {}\n }\n return null\n}\nfunction hasBun() {\n try {\n const { spawnSync } = await import('child_process')\n return spawnSync('bun', ['-v'], { stdio: 'ignore' }).status === 0\n } catch { return false }\n}\nfunction getEngineCommand(cliRoot:string) {\n const dist = join(cliRoot, 'dist', 'cli.js')\n if (fs.existsSync(dist)) return { exe: process.execPath, args: [dist] as string[] }\n // Dev path: use bun to run entry if present\n if (await hasBun()) {\n const entry = join(cliRoot, 'src', 'entrypoints', 'cli.tsx')\n const envFile = join(cliRoot, '.env')\n const args: string[] = []\n if (fs.existsSync(envFile)) args.push(`--env-file=${envFile}`)\n args.push(entry)\n return { exe: 'bun', args }\n }\n return null\n}\n\n// Settings endpoints\nfastify.get('/api/settings', async (_req, reply) => {\n reply.send({ env: settings.env })\n})\nfastify.post('/api/settings', async (req, reply) => {\n const body = (req as any).body || {}\n const inputEnv = body.env || {}\n for (const [k,v] of Object.entries(inputEnv)) {\n if (typeof v === 'string' && ALLOWED_ENV.has(k)) {\n settings.env[k] = v\n }\n }\n reply.send({ ok: true, env: settings.env })\n})\n\n// Create session\nfastify.post('/api/sessions', async (_req, reply) => {\n const id = randomUUID()\n sessions.set(id, { createdAt: Date.now(), turns: 0 })\n return reply.send({ id })\n})\n\n// Streaming WS\nfastify.get('/ws/:id', { websocket: true }, (connection, req) => {\n const id = (req.params as any).id as string\n if (!sessions.has(id)) { connection.socket.close(); return }\n\n let lastChild: import('child_process').ChildProcessWithoutNullStreams | null = null\\n connection.socket.on('message', async (raw) => {\\n let msg: any\\n try { msg = JSON.parse(raw.toString()) } catch { return }\\n if (msg.type === 'user_input') {\\n // persist user input\\n appendJsonl(id, { kind: 'user_input', text: String(msg.text||'') })\\n const idx = loadIndex();\\n const now = Date.now();\\n if (!(idx.sessions||[]).some(s=>s.id===id)) { idx.sessions.push({ id, createdAt: now, lastActive: now, title: String(msg.text||'').slice(0,120) }) }\\n else { idx.sessions = idx.sessions.map(s=> s.id===id? { ...s, lastActive: now, title: s.title|| String(msg.text||'').slice(0,120) }: s) }\\n saveIndex(idx)\\n\n const cliRoot = findCliRoot()\n const cmd = cliRoot ? await getEngineCommand(cliRoot) : null\n if (!cmd) {\n connection.socket.send(JSON.stringify({ type: 'status', level: 'error', text: 'No CLI found. Build dist/cli.js or install Bun.' }))\n return\n }\n // Kill previous if any\n try { lastChild?.kill() } catch {}\n\n // Use stream-json with verbose and partial messages\n const args = ['-p', '--output-format', 'stream-json', '--verbose', '--include-partial-messages', '--resume', id]\n const env = { ...process.env, ...settings.env }\n const child = spawn(cmd.exe, [...cmd.args, ...args, '--add-dir', getWorkDir()], { cwd: getWorkDir(), env })\n lastChild = child\n\n let buf = ''\n child.stdout.on('data', (chunk) => {\n buf += chunk.toString()\n let idx\n while ((idx = buf.indexOf('\\n')) !== -1) {\n const line = buf.slice(0, idx).trim()\n buf = buf.slice(idx + 1)\n if (!line) continue\n try {\n const obj = JSON.parse(line)\n connection.socket.send(JSON.stringify({ type: 'event', event: obj })); try { appendJsonl(id, { kind: 'event', event: obj }) } catch {}\\n } catch {\n // Fallback: forward as text\n connection.socket.send(JSON.stringify({ type: 'text', data: line }))\n }\n }\n })\n child.stderr.on('data', (buf) => {\n connection.socket.send(JSON.stringify({ type: 'stderr', chunk: buf.toString() })); try { appendJsonl(id, { kind: 'stderr', data: buf.toString() }) } catch {}\\n })\\n child.on('close', (code) => {\\n connection.socket.send(JSON.stringify({ type: 'complete', code })); try { appendJsonl(id, { kind: 'complete', code }) } catch {}\\n })\\n })\n\n const text = typeof msg.text === 'string' ? msg.text : ''\n if (text) {\n child.stdin.write(text)\n }\n child.stdin.end('\\n')\n }\n })\n})\n\n\n// ---- Sessions API ----\nfastify.get('/api/sessions', async (_req, reply) => {\n const idx = loadIndex()\n const list = (idx.sessions || []).sort((a,b)=> (b.lastActive||0)-(a.lastActive||0))\n reply.send({ sessions: list })\n})\nfastify.get('/api/sessions/:id/history', async (req, reply) => {\n const id = (req.params||{}).id\n const p = join(SESSIONS_DIR, id + '.jsonl')\n if (!fs.existsSync(p)) return reply.code(404).send({ error: 'not found' })\n const lines = fs.readFileSync(p, 'utf-8').split('\\n').filter(Boolean).slice(-500)\n reply.send({ lines: lines.map(l=> { try { return JSON.parse(l) } catch { return { raw: l } } }) })\n})\nfastify.delete('/api/sessions/:id', async (req, reply) => {\n const id = (req.params||{}).id\n const p = join(SESSIONS_DIR, id + '.jsonl')\n try { if (fs.existsSync(p)) fs.rmSync(p) } catch {}\n const idx = loadIndex(); idx.sessions = (idx.sessions||[]).filter(s=>s.id!==id); saveIndex(idx)\n reply.send({ ok:true })\n})\n\n// ---- Files API (CC_WORKDIR only) ----\nimport fse from 'fs-extra'\nimport { createTwoFilesPatch, applyPatch } from 'diff'\nfunction getWorkDir() { const p = process.env.CC_WORKDIR || resolve(__dirname, '../workspace'); try { fs.mkdirSync(p, { recursive: true }) } catch {}; return p }\nfunction safeJoin(root, p) { const resolved = resolve(root, p); if (!resolved.startsWith(resolve(root))) throw new Error('path outside workspace'); return resolved }\n\nfastify.get('/api/files/tree', async (req, reply) => {\n const root = getWorkDir(); const rel = (req.query||{}).path || '.'; const depth = Math.min(Number((req.query||{}).depth||2), 4)\n const start = safeJoin(root, rel)\n function walk(dir, d){\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n return entries.map(e=>{\n const p = join(dir, e.name)\n const node = { name: e.name, path: p.substring(root.length+1), dir: e.isDirectory() }\n if (e.isDirectory() && d>0) node.children = walk(p, d-1)\n return node\n })\n }\n reply.send({ tree: walk(start, depth) })\n})\nfastify.get('/api/files', async (req, reply) => {\n const root = getWorkDir(); const rel = (req.query||{}).path||''; const p = safeJoin(root, rel)\n if (!fs.existsSync(p)) return reply.code(404).send({ error: 'not found' })\n const text = fs.readFileSync(p, 'utf-8')\n reply.send({ path: rel, text })\n})\nfastify.post('/api/files/diff', async (req, reply) => {\n const body = (req.body||{})\n const root = getWorkDir(); const rel = body.path||''; const nextText = String(body.nextText||'')\n const p = safeJoin(root, rel); const prevText = fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : ''\n const patch = createTwoFilesPatch(rel, rel, prevText, nextText)\n reply.send({ patch })\n})\nfastify.post('/api/files/patch', async (req, reply) => {\n const body = (req.body||{})\n const root = getWorkDir(); const rel = body.path||''; const patch = String(body.patch||'')\n const p = safeJoin(root, rel); const prevText = fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : ''\n const nextText = applyPatch(prevText, patch)\n if (nextText === false) return reply.code(400).send({ ok:false, error:'failed_to_apply_patch' })\n await fse.ensureDir(join(p, '..'))\n const backupDir = resolve(DB_DIR, 'backups'); try { fse.ensureDirSync(backupDir) } catch {}\n const stamp = new Date().toISOString().replace(/[:.]/g,'-')\n if (fs.existsSync(p)) fs.copyFileSync(p, join(backupDir, rel.replace(/[\\\\/]/g,'__') + '.' + stamp + '.bak'))\n fs.writeFileSync(p, nextText, 'utf-8')\n reply.send({ ok:true })\n})\nfastify.listen({ port: PORT, host: '0.0.0.0' }, (err, address) => {\n if (err) { console.error(err); process.exit(1) }\n console.log(`[server] listening at ${address}`)\n console.log(`[server] open http://localhost:${PORT}/`)\n})\n\n\n\n\n"}, "5466456b351fc6a2069aa790e12439219985c4c6": {"t": 1785753904.7277634, "n": 10467, "h": "3b4b89a1c86d34f2", "c": "import Fastify from 'fastify'\nimport fastifyCors from '@fastify/cors'\nimport fastifyWebsocket from '@fastify/websocket'\nimport fastifyStatic from '@fastify/static'\nimport { fileURLToPath } from 'url'\nimport { dirname, join, resolve } from 'path'\nimport { spawn } from 'child_process'\nimport { randomUUID } from 'crypto'\nimport fs from 'fs'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst PORT = process.env.PORT ? Number(process.env.PORT) : 5178\n\nconst fastify = Fastify({ logger: false })\\n// --- DB paths ---\\nconst DB_DIR = resolve(__dirname, 'db')\\nconst SESSIONS_DIR = join(DB_DIR, 'sessions')\\ntry { fs.mkdirSync(DB_DIR, { recursive: true }); fs.mkdirSync(SESSIONS_DIR, { recursive: true }) } catch {}\\nfunction idxPath() { return join(DB_DIR, 'session-index.json') }\\nfunction loadIndex() { try { return JSON.parse(fs.readFileSync(idxPath(), 'utf-8')) } catch { return { sessions: [] } } }\\nfunction saveIndex(obj) { fs.writeFileSync(idxPath(), JSON.stringify(obj, null, 2)) }\\nfunction appendJsonl(id, obj) { fs.appendFileSync(join(SESSIONS_DIR, id + '.jsonl'), JSON.stringify({ ts: Date.now(), ...obj }) + '\\n') }\nawait fastify.register(fastifyCors, { origin: true })\nawait fastify.register(fastifyWebsocket)\nawait fastify.register(fastifyStatic, { root: resolve(__dirname, '../web'), prefix: '/' })\n\n// In-memory settings (can be extended or persisted later)\nconst ALLOWED_ENV = new Set([\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_AUTH_TOKEN',\n 'ANTHROPIC_BASE_URL',\n 'ANTHROPIC_MODEL',\n 'ANTHROPIC_DEFAULT_OPUS_MODEL',\n 'ANTHROPIC_DEFAULT_SONNET_MODEL',\n 'ANTHROPIC_DEFAULT_HAIKU_MODEL',\n 'API_TIMEOUT_MS',\n // 3P providers toggles\n 'CLAUDE_CODE_USE_BEDROCK',\n 'CLAUDE_CODE_USE_VERTEX',\n 'CLAUDE_CODE_USE_FOUNDRY',\n // Foundry\n 'ANTHROPIC_FOUNDRY_RESOURCE',\n 'ANTHROPIC_FOUNDRY_BASE_URL',\n 'ANTHROPIC_FOUNDRY_API_KEY',\n // Vertex\n 'ANTHROPIC_VERTEX_PROJECT_ID',\n 'CLOUD_ML_REGION',\n])\nconst settings = { env: {} as Record }\n\nconst sessions = new Map()\n\nfunction findCliRoot() {\n const candidates = [\n resolve(__dirname, '../claude-code-source-main'),\n resolve(process.cwd(), 'claude-code-source-main'),\n process.env.CC_CLI_ROOT,\n ].filter(Boolean) as string[]\n for (const c of candidates) {\n try {\n if (!c) continue\n if (fs.existsSync(join(c, 'dist', 'cli.js'))) return c\n if (fs.existsSync(join(c, 'src', 'entrypoints', 'cli.tsx'))) return c\n } catch {}\n }\n return null\n}\nfunction hasBun() {\n try {\n const { spawnSync } = await import('child_process')\n return spawnSync('bun', ['-v'], { stdio: 'ignore' }).status === 0\n } catch { return false }\n}\nfunction getEngineCommand(cliRoot:string) {\n const dist = join(cliRoot, 'dist', 'cli.js')\n if (fs.existsSync(dist)) return { exe: process.execPath, args: [dist] as string[] }\n // Dev path: use bun to run entry if present\n if (await hasBun()) {\n const entry = join(cliRoot, 'src', 'entrypoints', 'cli.tsx')\n const envFile = join(cliRoot, '.env')\n const args: string[] = []\n if (fs.existsSync(envFile)) args.push(`--env-file=${envFile}`)\n args.push(entry)\n return { exe: 'bun', args }\n }\n return null\n}\n\n// Settings endpoints\nfastify.get('/api/settings', async (_req, reply) => {\n reply.send({ env: settings.env })\n})\nfastify.post('/api/settings', async (req, reply) => {\n const body = (req as any).body || {}\n const inputEnv = body.env || {}\n for (const [k,v] of Object.entries(inputEnv)) {\n if (typeof v === 'string' && ALLOWED_ENV.has(k)) {\n settings.env[k] = v\n }\n }\n reply.send({ ok: true, env: settings.env })\n})\n\n// Create session\nfastify.post('/api/sessions', async (_req, reply) => {\n const id = randomUUID()\n sessions.set(id, { createdAt: Date.now(), turns: 0 })\n return reply.send({ id })\n})\n\n// Streaming WS\nfastify.get('/ws/:id', { websocket: true }, (connection, req) => {\n const id = (req.params as any).id as string\n if (!sessions.has(id)) { connection.socket.close(); return }\n\n let lastChild: import('child_process').ChildProcessWithoutNullStreams | null = null\\n connection.socket.on('message', async (raw) => {\\n let msg: any\\n try { msg = JSON.parse(raw.toString()) } catch { return }\\n if (msg.type === 'user_input') {\\n // persist user input\\n appendJsonl(id, { kind: 'user_input', text: String(msg.text||'') })\\n const idx = loadIndex();\\n const now = Date.now();\\n if (!(idx.sessions||[]).some(s=>s.id===id)) { idx.sessions.push({ id, createdAt: now, lastActive: now, title: String(msg.text||'').slice(0,120) }) }\\n else { idx.sessions = idx.sessions.map(s=> s.id===id? { ...s, lastActive: now, title: s.title|| String(msg.text||'').slice(0,120) }: s) }\\n saveIndex(idx)\\n\n const cliRoot = findCliRoot()\n const cmd = cliRoot ? await getEngineCommand(cliRoot) : null\n if (!cmd) {\n connection.socket.send(JSON.stringify({ type: 'status', level: 'error', text: 'No CLI found. Build dist/cli.js or install Bun.' }))\n return\n }\n // Kill previous if any\n try { lastChild?.kill() } catch {}\n\n // Use stream-json with verbose and partial messages\n const args = ['-p', '--output-format', 'stream-json', '--verbose', '--include-partial-messages', '--resume', id]\n const env = { ...process.env, ...settings.env }\n const child = spawn(cmd.exe, [...cmd.args, ...args, '--add-dir', getWorkDir()], { cwd: getWorkDir(), env })\n lastChild = child\n\n let buf = ''\n child.stdout.on('data', (chunk) => {\n buf += chunk.toString()\n let idx\n while ((idx = buf.indexOf('\\n')) !== -1) {\n const line = buf.slice(0, idx).trim()\n buf = buf.slice(idx + 1)\n if (!line) continue\n try {\n const obj = JSON.parse(line)\n connection.socket.send(JSON.stringify({ type: 'event', event: obj })); try { appendJsonl(id, { kind: 'event', event: obj }) } catch {}\\n } catch {\n // Fallback: forward as text\n connection.socket.send(JSON.stringify({ type: 'text', data: line }))\n }\n }\n })\n child.stderr.on('data', (buf) => {\n connection.socket.send(JSON.stringify({ type: 'stderr', chunk: buf.toString() })); try { appendJsonl(id, { kind: 'stderr', data: buf.toString() }) } catch {}\\n })\\n child.on('close', (code) => {\\n connection.socket.send(JSON.stringify({ type: 'complete', code })); try { appendJsonl(id, { kind: 'complete', code }) } catch {}\\n })\\n })\n\n const text = typeof msg.text === 'string' ? msg.text : ''\n if (text) {\n child.stdin.write(text)\n }\n child.stdin.end('\\n')\n }\n })\n})\n\n\n// ---- Sessions API ----\nfastify.get('/api/sessions', async (_req, reply) => {\n const idx = loadIndex()\n const list = (idx.sessions || []).sort((a,b)=> (b.lastActive||0)-(a.lastActive||0))\n reply.send({ sessions: list })\n})\nfastify.get('/api/sessions/:id/history', async (req, reply) => {\n const id = (req.params||{}).id\n const p = join(SESSIONS_DIR, id + '.jsonl')\n if (!fs.existsSync(p)) return reply.code(404).send({ error: 'not found' })\n const lines = fs.readFileSync(p, 'utf-8').split('\\n').filter(Boolean).slice(-500)\n reply.send({ lines: lines.map(l=> { try { return JSON.parse(l) } catch { return { raw: l } } }) })\n})\nfastify.delete('/api/sessions/:id', async (req, reply) => {\n const id = (req.params||{}).id\n const p = join(SESSIONS_DIR, id + '.jsonl')\n try { if (fs.existsSync(p)) fs.rmSync(p) } catch {}\n const idx = loadIndex(); idx.sessions = (idx.sessions||[]).filter(s=>s.id!==id); saveIndex(idx)\n reply.send({ ok:true })\n})\n\n// ---- Files API (CC_WORKDIR only) ----\nimport fse from 'fs-extra'\nimport { createTwoFilesPatch, applyPatch } from 'diff'\nfunction getWorkDir() { const p = process.env.CC_WORKDIR || resolve(__dirname, '../workspace'); try { fs.mkdirSync(p, { recursive: true }) } catch {}; return p }\nfunction safeJoin(root, p) { const resolved = resolve(root, p); if (!resolved.startsWith(resolve(root))) throw new Error('path outside workspace'); return resolved }\n\nfastify.get('/api/files/tree', async (req, reply) => {\n const root = getWorkDir(); const rel = (req.query||{}).path || '.'; const depth = Math.min(Number((req.query||{}).depth||2), 4)\n const start = safeJoin(root, rel)\n function walk(dir, d){\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n return entries.map(e=>{\n const p = join(dir, e.name)\n const node = { name: e.name, path: p.substring(root.length+1), dir: e.isDirectory() }\n if (e.isDirectory() && d>0) node.children = walk(p, d-1)\n return node\n })\n }\n reply.send({ tree: walk(start, depth) })\n})\nfastify.get('/api/files', async (req, reply) => {\n const root = getWorkDir(); const rel = (req.query||{}).path||''; const p = safeJoin(root, rel)\n if (!fs.existsSync(p)) return reply.code(404).send({ error: 'not found' })\n const text = fs.readFileSync(p, 'utf-8')\n reply.send({ path: rel, text })\n})\nfastify.post('/api/files/diff', async (req, reply) => {\n const body = (req.body||{})\n const root = getWorkDir(); const rel = body.path||''; const nextText = String(body.nextText||'')\n const p = safeJoin(root, rel); const prevText = fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : ''\n const patch = createTwoFilesPatch(rel, rel, prevText, nextText)\n reply.send({ patch })\n})\nfastify.post('/api/files/patch', async (req, reply) => {\n const body = (req.body||{})\n const root = getWorkDir(); const rel = body.path||''; const patch = String(body.patch||'')\n const p = safeJoin(root, rel); const prevText = fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : ''\n const nextText = applyPatch(prevText, patch)\n if (nextText === false) return reply.code(400).send({ ok:false, error:'failed_to_apply_patch' })\n await fse.ensureDir(join(p, '..'))\n const backupDir = resolve(DB_DIR, 'backups'); try { fse.ensureDirSync(backupDir) } catch {}\n const stamp = new Date().toISOString().replace(/[:.]/g,'-')\n if (fs.existsSync(p)) fs.copyFileSync(p, join(backupDir, rel.replace(/[\\\\/]/g,'__') + '.' + stamp + '.bak'))\n fs.writeFileSync(p, nextText, 'utf-8')\n reply.send({ ok:true })\n})\nfastify.listen({ port: PORT, host: '0.0.0.0' }, (err, address) => {\n if (err) { console.error(err); process.exit(1) }\n console.log(`[server] listening at ${address}`)\n console.log(`[server] open http://localhost:${PORT}/`)\n})\n\n\n\n"}, "f6c14766e876e9eb77a87dee854f81878db56c6a": {"t": 1785753907.1594274, "n": 5618, "h": "0fde4897b56d8f4c", "c": "import Fastify from 'fastify'\nimport fastifyCors from '@fastify/cors'\nimport fastifyWebsocket from '@fastify/websocket'\nimport fastifyStatic from '@fastify/static'\nimport { fileURLToPath } from 'url'\nimport { dirname, join, resolve } from 'path'\nimport { spawn } from 'child_process'\nimport { randomUUID } from 'crypto'\nimport fs from 'fs'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst PORT = process.env.PORT ? Number(process.env.PORT) : 5178\n\nconst fastify = Fastify({ logger: false })\nawait fastify.register(fastifyCors, { origin: true })\nawait fastify.register(fastifyWebsocket)\nawait fastify.register(fastifyStatic, { root: resolve(__dirname, '../web'), prefix: '/' })\n\n// In-memory settings (can be extended or persisted later)\nconst ALLOWED_ENV = new Set([\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_AUTH_TOKEN',\n 'ANTHROPIC_BASE_URL',\n 'ANTHROPIC_MODEL',\n 'ANTHROPIC_DEFAULT_OPUS_MODEL',\n 'ANTHROPIC_DEFAULT_SONNET_MODEL',\n 'ANTHROPIC_DEFAULT_HAIKU_MODEL',\n 'API_TIMEOUT_MS',\n // 3P providers toggles\n 'CLAUDE_CODE_USE_BEDROCK',\n 'CLAUDE_CODE_USE_VERTEX',\n 'CLAUDE_CODE_USE_FOUNDRY',\n // Foundry\n 'ANTHROPIC_FOUNDRY_RESOURCE',\n 'ANTHROPIC_FOUNDRY_BASE_URL',\n 'ANTHROPIC_FOUNDRY_API_KEY',\n // Vertex\n 'ANTHROPIC_VERTEX_PROJECT_ID',\n 'CLOUD_ML_REGION',\n])\nconst settings = { env: {} as Record }\n\nconst sessions = new Map()\n\nfunction findCliRoot() {\n const candidates = [\n resolve(__dirname, '../claude-code-source-main'),\n resolve(process.cwd(), 'claude-code-source-main'),\n process.env.CC_CLI_ROOT,\n ].filter(Boolean) as string[]\n for (const c of candidates) {\n try {\n if (!c) continue\n if (fs.existsSync(join(c, 'dist', 'cli.js'))) return c\n if (fs.existsSync(join(c, 'src', 'entrypoints', 'cli.tsx'))) return c\n } catch {}\n }\n return null\n}\nfunction hasBun() {\n try {\n const { spawnSync } = await import('child_process')\n return spawnSync('bun', ['-v'], { stdio: 'ignore' }).status === 0\n } catch { return false }\n}\nfunction getEngineCommand(cliRoot:string) {\n const dist = join(cliRoot, 'dist', 'cli.js')\n if (fs.existsSync(dist)) return { exe: process.execPath, args: [dist] as string[] }\n // Dev path: use bun to run entry if present\n if (await hasBun()) {\n const entry = join(cliRoot, 'src', 'entrypoints', 'cli.tsx')\n const envFile = join(cliRoot, '.env')\n const args: string[] = []\n if (fs.existsSync(envFile)) args.push(`--env-file=${envFile}`)\n args.push(entry)\n return { exe: 'bun', args }\n }\n return null\n}\n\n// Settings endpoints\nfastify.get('/api/settings', async (_req, reply) => {\n reply.send({ env: settings.env })\n})\nfastify.post('/api/settings', async (req, reply) => {\n const body = (req as any).body || {}\n const inputEnv = body.env || {}\n for (const [k,v] of Object.entries(inputEnv)) {\n if (typeof v === 'string' && ALLOWED_ENV.has(k)) {\n settings.env[k] = v\n }\n }\n reply.send({ ok: true, env: settings.env })\n})\n\n// Create session\nfastify.post('/api/sessions', async (_req, reply) => {\n const id = randomUUID()\n sessions.set(id, { createdAt: Date.now(), turns: 0 })\n return reply.send({ id })\n})\n\n// Streaming WS\nfastify.get('/ws/:id', { websocket: true }, (connection, req) => {\n const id = (req.params as any).id as string\n if (!sessions.has(id)) { connection.socket.close(); return }\n\n let lastChild: import('child_process').ChildProcessWithoutNullStreams | null = null\n connection.socket.on('message', async (raw) => {\n let msg: any\n try { msg = JSON.parse(raw.toString()) } catch { return }\n if (msg.type === 'user_input') {\n const cliRoot = findCliRoot()\n const cmd = cliRoot ? await getEngineCommand(cliRoot) : null\n if (!cmd) {\n connection.socket.send(JSON.stringify({ type: 'status', level: 'error', text: 'No CLI found. Build dist/cli.js or install Bun.' }))\n return\n }\n // Kill previous if any\n try { lastChild?.kill() } catch {}\n\n // Use stream-json with verbose and partial messages\n const args = ['-p', '--output-format', 'stream-json', '--verbose', '--include-partial-messages', '--resume', id]\n const env = { ...process.env, ...settings.env }\n const child = spawn(cmd.exe, [...cmd.args, ...args, '--add-dir', getWorkDir()], { cwd: getWorkDir(), env })\n lastChild = child\n\n let buf = ''\n child.stdout.on('data', (chunk) => {\n buf += chunk.toString()\n let idx\n while ((idx = buf.indexOf('\\n')) !== -1) {\n const line = buf.slice(0, idx).trim()\n buf = buf.slice(idx + 1)\n if (!line) continue\n try {\n const obj = JSON.parse(line)\n connection.socket.send(JSON.stringify({ type: 'event', event: obj }))\n } catch {\n // Fallback: forward as text\n connection.socket.send(JSON.stringify({ type: 'text', data: line }))\n }\n }\n })\n child.stderr.on('data', (buf) => {\n connection.socket.send(JSON.stringify({ type: 'stderr', chunk: buf.toString() }))\n })\n child.on('close', (code) => {\n connection.socket.send(JSON.stringify({ type: 'complete', code }))\n })\n\n const text = typeof msg.text === 'string' ? msg.text : ''\n if (text) {\n child.stdin.write(text)\n }\n child.stdin.end('\\n')\n }\n })\n})\n\nfastify.listen({ port: PORT, host: '0.0.0.0' }, (err, address) => {\n if (err) { console.error(err); process.exit(1) }\n console.log(`[server] listening at ${address}`)\n console.log(`[server] open http://localhost:${PORT}/`)\n})\n\n"}, "4ac6c0a0d3a5abd24aaadb4613db42b7ff0861e0": {"t": 1785753910.3448498, "n": 5586, "h": "8e474e9c2d57636e", "c": "import Fastify from 'fastify'\nimport fastifyCors from '@fastify/cors'\nimport fastifyWebsocket from '@fastify/websocket'\nimport fastifyStatic from '@fastify/static'\nimport { fileURLToPath } from 'url'\nimport { dirname, join, resolve } from 'path'\nimport { spawn } from 'child_process'\nimport { randomUUID } from 'crypto'\nimport fs from 'fs'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst PORT = process.env.PORT ? Number(process.env.PORT) : 5178\n\nconst fastify = Fastify({ logger: false })\nawait fastify.register(fastifyCors, { origin: true })\nawait fastify.register(fastifyWebsocket)\nawait fastify.register(fastifyStatic, { root: resolve(__dirname, '../web'), prefix: '/' })\n\n// In-memory settings (can be extended or persisted later)\nconst ALLOWED_ENV = new Set([\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_AUTH_TOKEN',\n 'ANTHROPIC_BASE_URL',\n 'ANTHROPIC_MODEL',\n 'ANTHROPIC_DEFAULT_OPUS_MODEL',\n 'ANTHROPIC_DEFAULT_SONNET_MODEL',\n 'ANTHROPIC_DEFAULT_HAIKU_MODEL',\n 'API_TIMEOUT_MS',\n // 3P providers toggles\n 'CLAUDE_CODE_USE_BEDROCK',\n 'CLAUDE_CODE_USE_VERTEX',\n 'CLAUDE_CODE_USE_FOUNDRY',\n // Foundry\n 'ANTHROPIC_FOUNDRY_RESOURCE',\n 'ANTHROPIC_FOUNDRY_BASE_URL',\n 'ANTHROPIC_FOUNDRY_API_KEY',\n // Vertex\n 'ANTHROPIC_VERTEX_PROJECT_ID',\n 'CLOUD_ML_REGION',\n])\nconst settings = { env: {} as Record }\n\nconst sessions = new Map()\n\nfunction findCliRoot() {\n const candidates = [\n resolve(__dirname, '../claude-code-source-main'),\n resolve(process.cwd(), 'claude-code-source-main'),\n process.env.CC_CLI_ROOT,\n ].filter(Boolean) as string[]\n for (const c of candidates) {\n try {\n if (!c) continue\n if (fs.existsSync(join(c, 'dist', 'cli.js'))) return c\n if (fs.existsSync(join(c, 'src', 'entrypoints', 'cli.tsx'))) return c\n } catch {}\n }\n return null\n}\nfunction hasBun() {\n try {\n const { spawnSync } = await import('child_process')\n return spawnSync('bun', ['-v'], { stdio: 'ignore' }).status === 0\n } catch { return false }\n}\nfunction getEngineCommand(cliRoot:string) {\n const dist = join(cliRoot, 'dist', 'cli.js')\n if (fs.existsSync(dist)) return { exe: process.execPath, args: [dist] as string[] }\n // Dev path: use bun to run entry if present\n if (await hasBun()) {\n const entry = join(cliRoot, 'src', 'entrypoints', 'cli.tsx')\n const envFile = join(cliRoot, '.env')\n const args: string[] = []\n if (fs.existsSync(envFile)) args.push(`--env-file=${envFile}`)\n args.push(entry)\n return { exe: 'bun', args }\n }\n return null\n}\n\n// Settings endpoints\nfastify.get('/api/settings', async (_req, reply) => {\n reply.send({ env: settings.env })\n})\nfastify.post('/api/settings', async (req, reply) => {\n const body = (req as any).body || {}\n const inputEnv = body.env || {}\n for (const [k,v] of Object.entries(inputEnv)) {\n if (typeof v === 'string' && ALLOWED_ENV.has(k)) {\n settings.env[k] = v\n }\n }\n reply.send({ ok: true, env: settings.env })\n})\n\n// Create session\nfastify.post('/api/sessions', async (_req, reply) => {\n const id = randomUUID()\n sessions.set(id, { createdAt: Date.now(), turns: 0 })\n return reply.send({ id })\n})\n\n// Streaming WS\nfastify.get('/ws/:id', { websocket: true }, (connection, req) => {\n const id = (req.params as any).id as string\n if (!sessions.has(id)) { connection.socket.close(); return }\n\n let lastChild: import('child_process').ChildProcessWithoutNullStreams | null = null\n connection.socket.on('message', async (raw) => {\n let msg: any\n try { msg = JSON.parse(raw.toString()) } catch { return }\n if (msg.type === 'user_input') {\n const cliRoot = findCliRoot()\n const cmd = cliRoot ? await getEngineCommand(cliRoot) : null\n if (!cmd) {\n connection.socket.send(JSON.stringify({ type: 'status', level: 'error', text: 'No CLI found. Build dist/cli.js or install Bun.' }))\n return\n }\n // Kill previous if any\n try { lastChild?.kill() } catch {}\n\n // Use stream-json with verbose and partial messages\n const args = ['-p', '--output-format', 'stream-json', '--verbose', '--include-partial-messages', '--resume', id]\n const env = { ...process.env, ...settings.env }\n const child = spawn(cmd.exe, [...cmd.args, ...args], { cwd: cliRoot!, env })\n lastChild = child\n\n let buf = ''\n child.stdout.on('data', (chunk) => {\n buf += chunk.toString()\n let idx\n while ((idx = buf.indexOf('\\n')) !== -1) {\n const line = buf.slice(0, idx).trim()\n buf = buf.slice(idx + 1)\n if (!line) continue\n try {\n const obj = JSON.parse(line)\n connection.socket.send(JSON.stringify({ type: 'event', event: obj }))\n } catch {\n // Fallback: forward as text\n connection.socket.send(JSON.stringify({ type: 'text', data: line }))\n }\n }\n })\n child.stderr.on('data', (buf) => {\n connection.socket.send(JSON.stringify({ type: 'stderr', chunk: buf.toString() }))\n })\n child.on('close', (code) => {\n connection.socket.send(JSON.stringify({ type: 'complete', code }))\n })\n\n const text = typeof msg.text === 'string' ? msg.text : ''\n if (text) {\n child.stdin.write(text)\n }\n child.stdin.end('\\n')\n }\n })\n})\n\nfastify.listen({ port: PORT, host: '0.0.0.0' }, (err, address) => {\n if (err) { console.error(err); process.exit(1) }\n console.log(`[server] listening at ${address}`)\n console.log(`[server] open http://localhost:${PORT}/`)\n})\n"}, "5bfa61d4a57d28b7677352d63c088935dca44db3": {"t": 1785753914.3629723, "n": 3659, "h": "bf9b865f767cb2e3", "c": "import Fastify from 'fastify'\nimport fastifyCors from '@fastify/cors'\nimport fastifyWebsocket from '@fastify/websocket'\nimport fastifyStatic from '@fastify/static'\nimport { fileURLToPath } from 'url'\nimport { dirname, join, resolve } from 'path'\nimport { spawn } from 'child_process'\nimport { randomUUID } from 'crypto'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst PORT = process.env.PORT ? Number(process.env.PORT) : 5178\n\nconst fastify = Fastify({ logger: false })\nawait fastify.register(fastifyCors, { origin: true })\nawait fastify.register(fastifyWebsocket)\nawait fastify.register(fastifyStatic, { root: resolve(__dirname, '../web'), prefix: '/' })\n\nconst sessions = new Map()\n\nfunction findCliRoot() {\n const candidates = [\n resolve(__dirname, '../claude-code-source-main'),\n resolve(process.cwd(), 'claude-code-source-main'),\n process.env.CC_CLI_ROOT,\n ].filter(Boolean)\n for (const c of candidates) {\n try {\n const fs = require('fs')\n if (!c) continue\n if (fs.existsSync(join(c, 'dist', 'cli.js'))) return c\n if (fs.existsSync(join(c, 'src', 'entrypoints', 'cli.tsx'))) return c\n } catch {}\n }\n return null\n}\nfunction hasBun() {\n try {\n const { spawnSync } = require('child_process')\n return spawnSync('bun', ['-v'], { stdio: 'ignore' }).status === 0\n } catch { return false }\n}\nfunction getEngineCommand(cliRoot) {\n const fs = require('fs')\n const dist = join(cliRoot, 'dist', 'cli.js')\n if (fs.existsSync(dist)) return { exe: process.execPath, args: [dist] }\n if (hasBun()) {\n const entry = join(cliRoot, 'src', 'entrypoints', 'cli.tsx')\n const envFile = join(cliRoot, '.env')\n const args = []\n if (fs.existsSync(envFile)) args.push(`--env-file=${envFile}`)\n args.push(entry)\n return { exe: 'bun', args }\n }\n return null\n}\n\nfastify.post('/api/sessions', async (_req, reply) => {\n const id = randomUUID()\n sessions.set(id, { createdAt: Date.now(), turns: 0 })\n return reply.send({ id })\n})\n\nfastify.get('/ws/:id', { websocket: true }, (connection, req) => {\n const id = req.params.id\n if (!sessions.has(id)) {\n connection.socket.close()\n return\n }\n connection.socket.on('message', async (raw) => {\n let msg\n try { msg = JSON.parse(raw.toString()) } catch { return }\n if (msg.type === 'user_input') {\n const cliRoot = findCliRoot()\n const cmd = cliRoot ? getEngineCommand(cliRoot) : null\n if (!cmd) {\n connection.socket.send(JSON.stringify({ type: 'status', level: 'error', text: 'No CLI found. Build dist/cli.js or install Bun.' }))\n return\n }\n const args = ['-p', '--output-format', 'text', '--resume', id]\n const child = spawn(cmd.exe, [...cmd.args, ...args], { cwd: cliRoot, env: process.env })\n child.stdout.on('data', (buf) => {\n connection.socket.send(JSON.stringify({ type: 'model_text', chunk: buf.toString() }))\n })\n child.stderr.on('data', (buf) => {\n connection.socket.send(JSON.stringify({ type: 'stderr', chunk: buf.toString() }))\n })\n child.on('close', (code) => {\n connection.socket.send(JSON.stringify({ type: 'complete', code }))\n })\n if (msg.text) {\n child.stdin.write(msg.text)\n child.stdin.end('\\n')\n } else if (msg.stdin) {\n child.stdin.write(String(msg.stdin))\n child.stdin.end('\\n')\n } else {\n child.stdin.end('\\n')\n }\n }\n })\n})\n\nfastify.listen({ port: PORT, host: '0.0.0.0' }, (err, address) => {\n if (err) { console.error(err); process.exit(1) }\n console.log(`[server] listening at ${address}`)\n console.log(`[server] open http://localhost:${PORT}/`)\n})\n"}}}}