test-stdin-stream.ts 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import path from "path"
  2. import { fileURLToPath } from "url"
  3. import readline from "readline"
  4. import { execa } from "execa"
  5. const __dirname = path.dirname(fileURLToPath(import.meta.url))
  6. const cliRoot = path.resolve(__dirname, "..")
  7. async function main() {
  8. const child = execa(
  9. "pnpm",
  10. ["dev", "--print", "--stdin-prompt-stream", "--provider", "roo", "--output-format", "stream-json"],
  11. {
  12. cwd: cliRoot,
  13. stdin: "pipe",
  14. stdout: "pipe",
  15. stderr: "pipe",
  16. reject: false,
  17. forceKillAfterDelay: 2_000,
  18. },
  19. )
  20. child.stdout?.on("data", (chunk) => process.stdout.write(chunk))
  21. child.stderr?.on("data", (chunk) => process.stderr.write(chunk))
  22. console.log("[wrapper] Type a message and press Enter to send it.")
  23. console.log("[wrapper] Type /exit to close stdin and let the CLI finish.")
  24. const rl = readline.createInterface({
  25. input: process.stdin,
  26. output: process.stdout,
  27. terminal: true,
  28. })
  29. rl.on("line", (line) => {
  30. if (line.trim() === "/exit") {
  31. console.log("[wrapper] Closing stdin...")
  32. child.stdin?.end()
  33. rl.close()
  34. return
  35. }
  36. if (!child.stdin?.destroyed) {
  37. child.stdin?.write(`${line}\n`)
  38. }
  39. })
  40. const onSignal = (signal: NodeJS.Signals) => {
  41. console.log(`[wrapper] Received ${signal}, forwarding to CLI...`)
  42. rl.close()
  43. child.kill(signal)
  44. }
  45. process.on("SIGINT", () => onSignal("SIGINT"))
  46. process.on("SIGTERM", () => onSignal("SIGTERM"))
  47. const result = await child
  48. rl.close()
  49. console.log(`[wrapper] CLI exited with code ${result.exitCode}`)
  50. process.exit(result.exitCode ?? 1)
  51. }
  52. main().catch((error) => {
  53. console.error("[wrapper] Fatal error:", error)
  54. process.exit(1)
  55. })