feat: add URL-driven workspace state and streamed Codex research

This commit is contained in:
2026-09-28 20:08:28 +09:00
parent 85d23a328b
commit 8085ad2f90
207 changed files with 15287 additions and 16026 deletions
-94
View File
@@ -1,94 +0,0 @@
import { spawn, spawnSync } from 'node:child_process'
import { mkdir } from 'node:fs/promises'
import { homedir } from 'node:os'
import { resolve } from 'node:path'
// This process is independent of Vite / the web backend. Keep it running while
// restarting the app. Codex owns its own login and refreshes that login normally.
const reportRoot = resolve(
process.env.TWITTER_LITE_REPORT_ROOT || '.data/research',
)
const endpoint = new URL(
process.env.TWITTER_LITE_CODEX_URL || 'ws://127.0.0.1:4500',
)
if (
endpoint.protocol !== 'ws:' ||
endpoint.hostname !== '127.0.0.1' ||
endpoint.username ||
endpoint.password
) {
throw new Error('The prototype Codex endpoint must be ws://127.0.0.1:<port>.')
}
await mkdir(reportRoot, { recursive: true, mode: 0o700 })
const env = {
HOME: homedir(),
PATH: process.env.PATH,
CODEX_HOME: process.env.CODEX_HOME || resolve(homedir(), '.codex'),
}
for (const name of [
'LANG',
'LC_ALL',
'SSL_CERT_FILE',
'SSL_CERT_DIR',
'NIX_SSL_CERT_FILE',
'TMPDIR',
]) {
if (process.env[name]) env[name] = process.env[name]
}
// Enumerate the effective config, including CLI-wrapper and project settings.
// Keep this output private: MCP transport settings can contain credentials.
const configured = spawnSync('codex', ['mcp', 'list', '--json'], {
cwd: reportRoot,
env,
encoding: 'utf8',
timeout: 15_000,
maxBuffer: 4 * 1024 * 1024,
})
let mcpOverrides
try {
if (configured.error || configured.status !== 0) throw new Error()
const servers = JSON.parse(configured.stdout)
if (!Array.isArray(servers)) throw new Error()
mcpOverrides = servers.flatMap((server) => {
if (
typeof server?.name !== 'string' ||
!/^[A-Za-z0-9_-]+$/.test(server.name)
)
throw new Error()
return ['-c', `mcp_servers.${server.name}.enabled=false`]
})
} catch {
throw new Error(
'Could not inspect Codex MCP configuration safely. Check codex mcp list locally.',
)
}
const child = spawn(
'codex',
[
// Keep overrides before the subcommand so they merge with wrapper settings.
'-c',
'shell_environment_policy.inherit="none"',
'-c',
'features.multi_agent=false',
'-c',
'features.hooks=false',
'-c',
'web_search="disabled"',
...mcpOverrides,
'app-server',
'--listen',
endpoint.origin,
],
{ cwd: reportRoot, env, stdio: 'inherit' },
)
child.on('error', () => {
console.error(
'Could not start Codex. Install codex and run codex login first.',
)
process.exitCode = 1
})
child.on('exit', (code) => {
process.exitCode = code ?? 1
})
for (const signal of ['SIGINT', 'SIGTERM'])
process.on(signal, () => child.kill(signal))