| import http from 'node:http'; |
| import { execFile } from 'node:child_process'; |
| import { promises as fs } from 'node:fs'; |
| import path from 'node:path'; |
| import { ModelManager } from './model-manager.mjs'; |
| import { CommunityStore } from '../community/store.mjs'; |
| import { LspManager } from './lsp-manager.mjs'; |
| import { DapManager } from './dap-manager.mjs'; |
| import { WorkspaceManager } from './workspace-manager.mjs'; |
| import { TrainingManager } from './training-manager.mjs'; |
| import { ReplayStore } from './replay-store.mjs'; |
| import { ArenaManager } from './arena-manager.mjs'; |
|
|
| const HOST = '127.0.0.1'; |
| const PORT = Number(process.env.AIDE_DAEMON_PORT || 4777); |
| const WORKSPACE = path.resolve(process.env.AIDE_WORKSPACE || process.cwd()); |
| const MODEL_DIR = path.resolve(process.env.AIDE_MODEL_DIR || path.join(WORKSPACE, 'models')); |
| const MANIFEST = path.join(WORKSPACE, 'models', 'manifest.json'); |
| const modelManager = new ModelManager({ manifestPath: MANIFEST, modelDir: MODEL_DIR, binaryPath: process.env.AIDE_LLAMA_SERVER || '/root/runtime/llama-b10333/llama-server' }); |
| await modelManager.load().catch(() => {}); |
| const communityStore = new CommunityStore(path.join(WORKSPACE, 'community', 'store.json')); |
| await communityStore.load().catch(() => {}); |
| const lspManager = new LspManager({ manifestPath: path.join(WORKSPACE, 'languages', 'manifest.json'), workspace: WORKSPACE }); |
| await lspManager.load().catch(() => {}); |
| const dapManager = new DapManager({ manifestPath: path.join(WORKSPACE, 'debuggers', 'manifest.json'), workspace: WORKSPACE, pythonPath: process.env.AIDE_PYTHON || '' }); |
| await dapManager.load().catch(() => {}); |
| const workspaceManager = new WorkspaceManager(WORKSPACE); |
| const trainingManager = new TrainingManager({ manifestPath: path.join(WORKSPACE, 'training', 'manifest.json'), workspace: WORKSPACE }); |
| await trainingManager.load().catch(() => {}); |
| const replayStore = new ReplayStore(path.join(WORKSPACE, 'replays', 'store.json')); |
| await replayStore.load().catch(() => {}); |
| const arenaManager = new ArenaManager({ modelManager, manifestPath: MANIFEST, suitePath: path.join(WORKSPACE, 'benchmarks', 'manifest.json') }); |
|
|
| function json(response, status, body) { |
| const payload = JSON.stringify(body); |
| response.writeHead(status, { |
| 'Content-Type': 'application/json; charset=utf-8', |
| 'Content-Length': Buffer.byteLength(payload), |
| 'Access-Control-Allow-Origin': 'http://127.0.0.1:4173', |
| 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', |
| 'Access-Control-Allow-Headers': 'Content-Type' |
| }); |
| response.end(payload); |
| } |
|
|
| async function body(request) { |
| let data = ''; |
| for await (const chunk of request) { |
| data += chunk; |
| if (Buffer.byteLength(data) > 16 * 1024) throw new Error('request too large'); |
| } |
| return data ? JSON.parse(data) : {}; |
| } |
|
|
| function runGit(args) { |
| return new Promise((resolve, reject) => { |
| execFile('git', args, { cwd: WORKSPACE, timeout: 5000, maxBuffer: 256 * 1024 }, (error, stdout, stderr) => { |
| if (error) reject(new Error(stderr.trim() || error.message)); |
| else resolve(stdout); |
| }); |
| }); |
| } |
|
|
| async function workspaceSummary() { |
| const entries = await fs.readdir(WORKSPACE, { withFileTypes: true }); |
| return entries.filter(entry => !entry.name.startsWith('.')).slice(0, 200).map(entry => ({ |
| name: entry.name, |
| kind: entry.isDirectory() ? 'directory' : 'file' |
| })); |
| } |
|
|
| const server = http.createServer(async (request, response) => { |
| if (request.method === 'OPTIONS') return json(response, 204, {}); |
| try { |
| if (request.method === 'GET' && request.url === '/health') { |
| return json(response, 200, { ok: true, service: 'aide-local-daemon', host: HOST, workspace: WORKSPACE }); |
| } |
| if (request.method === 'GET' && request.url === '/api/workspace') { |
| return json(response, 200, { workspace: WORKSPACE, entries: await workspaceSummary() }); |
| } |
| if (request.method === 'GET' && request.url.startsWith('/api/file?')) { |
| const relativePath = new URL(request.url, 'http://127.0.0.1').searchParams.get('path'); |
| return json(response, 200, { path: relativePath, content: await workspaceManager.read(relativePath) }); |
| } |
| if (request.method === 'POST' && request.url === '/api/file/write') { |
| const input = await body(request); |
| return json(response, 200, await workspaceManager.write(input.path, input.content, input.approved)); |
| } |
| if (request.method === 'POST' && request.url === '/api/patch/apply') { |
| const input = await body(request); |
| return json(response, 200, await workspaceManager.applyPatch(input.patch, input.approved)); |
| } |
| if (request.method === 'GET' && request.url === '/api/git/status') { |
| try { |
| return json(response, 200, { workspace: WORKSPACE, status: await runGit(['status', '--short']) }); |
| } catch (error) { |
| return json(response, 200, { workspace: WORKSPACE, status: '', unavailable: error.message }); |
| } |
| } |
| if (request.method === 'GET' && request.url === '/api/models/status') { |
| return json(response, 200, { models: modelManager.status() }); |
| } |
| if (request.method === 'GET' && request.url === '/api/community') { |
| return json(response, 200, communityStore.list()); |
| } |
| if (request.method === 'GET' && request.url === '/api/lsp/status') { |
| return json(response, 200, { servers: lspManager.status() }); |
| } |
| if (request.method === 'GET' && request.url === '/api/dap/status') { |
| return json(response, 200, { adapters: dapManager.status() }); |
| } |
| if (request.method === 'GET' && request.url === '/api/training/status') { |
| return json(response, 200, trainingManager.status()); |
| } |
| if (request.method === 'GET' && request.url === '/api/replays') return json(response, 200, replayStore.list()); |
| if (request.method === 'POST' && request.url === '/api/replays') return json(response, 201, { replay: await replayStore.add(await body(request)) }); |
| if (request.method === 'POST' && request.url === '/api/arena/run') return json(response, 200, await arenaManager.run((await body(request)).approved)); |
| if (request.method === 'POST' && request.url === '/api/training/start') { |
| const input = await body(request); |
| return json(response, 200, trainingManager.start(input.id, input.approved)); |
| } |
| if (request.method === 'POST' && request.url === '/api/training/stop') { |
| return json(response, 200, trainingManager.stop()); |
| } |
| if (request.method === 'POST' && request.url === '/api/dap/start') { |
| return json(response, 200, await dapManager.start((await body(request)).id)); |
| } |
| if (request.method === 'POST' && request.url === '/api/dap/request') { |
| const input = await body(request); |
| return json(response, 200, await dapManager.request(input.id, input.request)); |
| } |
| if (request.method === 'POST' && request.url === '/api/dap/stop') { |
| return json(response, 200, await dapManager.stop((await body(request)).id)); |
| } |
| if (request.method === 'POST' && request.url === '/api/lsp/start') { |
| return json(response, 200, await lspManager.start((await body(request)).id)); |
| } |
| if (request.method === 'POST' && request.url === '/api/lsp/request') { |
| const input = await body(request); |
| return json(response, 200, await lspManager.request(input.id, input.message)); |
| } |
| if (request.method === 'POST' && request.url === '/api/lsp/notify') { |
| const input = await body(request); |
| return json(response, 200, lspManager.notify(input.id, input.message)); |
| } |
| if (request.method === 'POST' && request.url === '/api/lsp/stop') { |
| return json(response, 200, await lspManager.stop((await body(request)).id)); |
| } |
| if (request.method === 'POST' && request.url === '/api/community/items') { |
| const input = await body(request); |
| return json(response, 201, { item: await communityStore.add(input.type, input.item) }); |
| } |
| if (request.method === 'PUT' && request.url === '/api/community/items') { |
| const input = await body(request); |
| return json(response, 200, { item: await communityStore.update(input.type, input.index, input.item) }); |
| } |
| if (request.method === 'DELETE' && request.url === '/api/community/items') { |
| const input = await body(request); |
| return json(response, 200, { item: await communityStore.remove(input.type, input.index) }); |
| } |
| if (request.method === 'POST' && request.url === '/api/models/start') { |
| return json(response, 200, await modelManager.start((await body(request)).id)); |
| } |
| if (request.method === 'POST' && request.url === '/api/models/stop') { |
| const id = (await body(request)).id; |
| return json(response, 200, id ? await modelManager.stop(id) : (await modelManager.stopAll(), { status: 'stopped' })); |
| } |
| return json(response, 404, { error: 'not found' }); |
| } catch (error) { |
| return json(response, 500, { error: 'local daemon error' }); |
| } |
| }); |
|
|
| server.listen(PORT, HOST, () => { |
| console.log(`AIDE local daemon listening on http://${HOST}:${PORT}`); |
| console.log(`workspace: ${WORKSPACE}`); |
| }); |
|
|