Origem: D:/Documents/Github/PiAgentUI/PiAgentUi Arquivos omitidos (segredos, bancos de dados, logs, >50 MB): 4
168 lines
5.1 KiB
JavaScript
168 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
import http from 'node:http'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import crypto from 'node:crypto'
|
|
|
|
const agentDir = process.env.PI_CODING_AGENT_DIR
|
|
const sessionDir = process.env.PI_CODING_AGENT_SESSION_DIR
|
|
const markerDir = process.env.PIAGENTUI_E2E_MARKER_DIR
|
|
|
|
if (!agentDir || !sessionDir) {
|
|
console.error('PI_CODING_AGENT_DIR and PI_CODING_AGENT_SESSION_DIR are required')
|
|
process.exit(2)
|
|
}
|
|
|
|
fs.mkdirSync(agentDir, { recursive: true })
|
|
fs.mkdirSync(sessionDir, { recursive: true })
|
|
if (markerDir) fs.mkdirSync(markerDir, { recursive: true })
|
|
|
|
const token = `e2e-${crypto.randomUUID()}`
|
|
let apiKeyConfigured = false
|
|
let lastApiKeyLength
|
|
const requests = []
|
|
|
|
const providers = () => [
|
|
{
|
|
id: 'opencode',
|
|
name: 'OpenCode Zen',
|
|
models: [
|
|
{
|
|
id: 'deepseek-v4-flash',
|
|
name: 'DeepSeek V4 Flash',
|
|
providerId: 'opencode',
|
|
providerName: 'OpenCode Zen',
|
|
},
|
|
],
|
|
auth: { method: 'apikey', authorization: { configured: apiKeyConfigured } },
|
|
},
|
|
{
|
|
id: 'anthropic',
|
|
name: 'Anthropic',
|
|
models: [{ id: 'claude-sonnet-4', name: 'Claude Sonnet 4', providerId: 'anthropic', providerName: 'Anthropic' }],
|
|
auth: { method: 'oauth', authorization: { configured: false } },
|
|
},
|
|
{
|
|
id: 'google',
|
|
name: 'Google',
|
|
models: [{ id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash', providerId: 'google', providerName: 'Google' }],
|
|
auth: { method: 'apikey', authorization: { configured: false } },
|
|
},
|
|
]
|
|
|
|
function writeMarker(extra = {}) {
|
|
if (!markerDir) return
|
|
fs.writeFileSync(
|
|
path.join(markerDir, 'fake-pi-state.json'),
|
|
JSON.stringify(
|
|
{
|
|
pid: process.pid,
|
|
agentDir,
|
|
sessionDir,
|
|
startupArgs: process.argv.slice(2),
|
|
apiKeyConfigured,
|
|
lastApiKeyLength,
|
|
requests,
|
|
...extra,
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
'utf8',
|
|
)
|
|
}
|
|
|
|
function sendJson(res, status, body) {
|
|
res.writeHead(status, { 'Content-Type': 'application/json' })
|
|
res.end(JSON.stringify(body))
|
|
}
|
|
|
|
async function readBody(req) {
|
|
const chunks = []
|
|
for await (const chunk of req) chunks.push(chunk)
|
|
const raw = Buffer.concat(chunks).toString('utf8')
|
|
if (!raw) return {}
|
|
return JSON.parse(raw)
|
|
}
|
|
|
|
const server = http.createServer(async (req, res) => {
|
|
const url = new URL(req.url ?? '/', 'http://127.0.0.1')
|
|
requests.push({ method: req.method, path: url.pathname })
|
|
writeMarker()
|
|
|
|
if (req.method === 'GET' && url.pathname === '/global/health') {
|
|
return sendJson(res, 200, { ok: true, mode: 'rpc' })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/api/health') {
|
|
return sendJson(res, 200, { status: 'ok', version: 'e2e' })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/api/providers') {
|
|
return sendJson(res, 200, { providers: providers(), default: {} })
|
|
}
|
|
|
|
if (req.method === 'POST' && url.pathname === '/api/providers/opencode/api-key') {
|
|
const body = await readBody(req)
|
|
apiKeyConfigured = Boolean(body.apiKey)
|
|
lastApiKeyLength = typeof body.apiKey === 'string' ? body.apiKey.length : 0
|
|
writeMarker()
|
|
return sendJson(res, 200, { ok: true })
|
|
}
|
|
|
|
if (req.method === 'POST' && url.pathname === '/api/providers/opencode/logout') {
|
|
apiKeyConfigured = false
|
|
writeMarker()
|
|
return sendJson(res, 200, { ok: true })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/api/models') {
|
|
return sendJson(res, 200, { models: providers().flatMap(provider => provider.models) })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/api/sessions') {
|
|
return sendJson(res, 200, { sessions: [] })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/api/runtime/capabilities') {
|
|
return sendJson(res, 200, { commands: true, extensions: true, providerAuth: true })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/api/commands') {
|
|
return sendJson(res, 200, { commands: [] })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/api/agents') {
|
|
return sendJson(res, 200, { agents: [] })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/api/skills') {
|
|
return sendJson(res, 200, { skills: {} })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/api/mcp/status') {
|
|
return sendJson(res, 200, { servers: {}, tools: [] })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/api/network/access') {
|
|
return sendJson(res, 200, { lanAccessEnabled: false, urls: [] })
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname.startsWith('/api/settings/')) {
|
|
return sendJson(res, 200, {})
|
|
}
|
|
|
|
sendJson(res, 404, { error: `Unhandled fake Pi route: ${req.method} ${url.pathname}` })
|
|
})
|
|
|
|
server.listen(0, '127.0.0.1', () => {
|
|
const address = server.address()
|
|
if (!address || typeof address === 'string') throw new Error('No TCP address')
|
|
const discovery = { port: address.port, token, pid: process.pid, startedAt: new Date().toISOString() }
|
|
fs.writeFileSync(path.join(agentDir, 'piagentui-port.json'), JSON.stringify(discovery, null, 2), 'utf8')
|
|
writeMarker({ port: address.port })
|
|
})
|
|
|
|
process.on('SIGTERM', () => server.close(() => process.exit(0)))
|
|
process.on('SIGINT', () => server.close(() => process.exit(0)))
|