mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
* feat(cli): add NDJSON stdin protocol, list subcommands, and modularize run.ts Overhaul the stdin prompt stream from raw text lines to a structured NDJSON command protocol (start/message/cancel/ping/shutdown) with requestId correlation, ack/done/error lifecycle events, and queue telemetry. Add list subcommands (commands, modes, models) for programmatic discovery. Extract stdin stream logic from run.ts into stdin-stream.ts and add shared isRecord guard utility. Includes unit tests for all new modules. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(core): fix Task.ts bug affecting CLI operation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
85 lines
2 KiB
TypeScript
85 lines
2 KiB
TypeScript
import path from "path"
|
|
import { fileURLToPath } from "url"
|
|
import readline from "readline"
|
|
|
|
import { execa } from "execa"
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const cliRoot = path.resolve(__dirname, "..")
|
|
|
|
async function main() {
|
|
const child = execa(
|
|
"pnpm",
|
|
["dev", "--print", "--stdin-prompt-stream", "--provider", "roo", "--output-format", "stream-json"],
|
|
{
|
|
cwd: cliRoot,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
reject: false,
|
|
forceKillAfterDelay: 2_000,
|
|
},
|
|
)
|
|
|
|
child.stdout?.on("data", (chunk) => process.stdout.write(chunk))
|
|
child.stderr?.on("data", (chunk) => process.stderr.write(chunk))
|
|
|
|
console.log("[wrapper] Type a message and press Enter to send it.")
|
|
console.log("[wrapper] Type /exit to close stdin and let the CLI finish.")
|
|
|
|
let requestCounter = 0
|
|
let hasStartedTask = false
|
|
|
|
const sendCommand = (payload: Record<string, unknown>) => {
|
|
if (child.stdin?.destroyed) {
|
|
return
|
|
}
|
|
child.stdin?.write(JSON.stringify(payload) + "\n")
|
|
}
|
|
|
|
const rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout,
|
|
terminal: true,
|
|
})
|
|
|
|
rl.on("line", (line) => {
|
|
if (line.trim() === "/exit") {
|
|
console.log("[wrapper] Closing stdin...")
|
|
sendCommand({
|
|
command: "shutdown",
|
|
requestId: `shutdown-${Date.now()}-${++requestCounter}`,
|
|
})
|
|
child.stdin?.end()
|
|
rl.close()
|
|
return
|
|
}
|
|
|
|
const command = hasStartedTask ? "message" : "start"
|
|
sendCommand({
|
|
command,
|
|
requestId: `${command}-${Date.now()}-${++requestCounter}`,
|
|
prompt: line,
|
|
})
|
|
hasStartedTask = true
|
|
})
|
|
|
|
const onSignal = (signal: NodeJS.Signals) => {
|
|
console.log(`[wrapper] Received ${signal}, forwarding to CLI...`)
|
|
rl.close()
|
|
child.kill(signal)
|
|
}
|
|
|
|
process.on("SIGINT", () => onSignal("SIGINT"))
|
|
process.on("SIGTERM", () => onSignal("SIGTERM"))
|
|
|
|
const result = await child
|
|
rl.close()
|
|
console.log(`[wrapper] CLI exited with code ${result.exitCode}`)
|
|
process.exit(result.exitCode ?? 1)
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error("[wrapper] Fatal error:", error)
|
|
process.exit(1)
|
|
})
|