| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- import { cmd } from "@/cli/cmd/cmd"
- import { tui } from "./app"
- import { Rpc } from "@/util/rpc"
- import { type rpc } from "./worker"
- import path from "path"
- import { UI } from "@/cli/ui"
- import { iife } from "@/util/iife"
- import { Log } from "@/util/log"
- declare global {
- const OPENCODE_WORKER_PATH: string
- }
- export const TuiThreadCommand = cmd({
- command: "$0 [project]",
- describe: "start opencode tui",
- builder: (yargs) =>
- yargs
- .positional("project", {
- type: "string",
- describe: "path to start opencode in",
- })
- .option("model", {
- type: "string",
- alias: ["m"],
- describe: "model to use in the format of provider/model",
- })
- .option("continue", {
- alias: ["c"],
- describe: "continue the last session",
- type: "boolean",
- })
- .option("session", {
- alias: ["s"],
- type: "string",
- describe: "session id to continue",
- })
- .option("prompt", {
- alias: ["p"],
- type: "string",
- describe: "prompt to use",
- })
- .option("agent", {
- type: "string",
- describe: "agent to use",
- })
- .option("port", {
- type: "number",
- describe: "port to listen on",
- default: 0,
- })
- .option("hostname", {
- type: "string",
- describe: "hostname to listen on",
- default: "127.0.0.1",
- }),
- handler: async (args) => {
- // Resolve relative paths against PWD to preserve behavior when using --cwd flag
- const baseCwd = process.env.PWD ?? process.cwd()
- const cwd = args.project ? path.resolve(baseCwd, args.project) : process.cwd()
- const defaultWorker = new URL("./worker.ts", import.meta.url)
- // Nix build creates a bundled worker next to the binary; prefer it when present.
- const execDir = path.dirname(process.execPath)
- const bundledWorker = path.join(execDir, "opencode-worker.js")
- const hasBundledWorker = await Bun.file(bundledWorker).exists()
- const workerPath = (() => {
- if (typeof OPENCODE_WORKER_PATH !== "undefined") return OPENCODE_WORKER_PATH
- if (hasBundledWorker) return bundledWorker
- return defaultWorker
- })()
- try {
- process.chdir(cwd)
- } catch (e) {
- UI.error("Failed to change directory to " + cwd)
- return
- }
- const worker = new Worker(workerPath, {
- env: Object.fromEntries(
- Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] !== undefined),
- ),
- })
- worker.onerror = (e) => {
- Log.Default.error(e)
- }
- const client = Rpc.client<typeof rpc>(worker)
- process.on("uncaughtException", (e) => {
- Log.Default.error(e)
- })
- process.on("unhandledRejection", (e) => {
- Log.Default.error(e)
- })
- const server = await client.call("server", {
- port: args.port,
- hostname: args.hostname,
- })
- const prompt = await iife(async () => {
- const piped = !process.stdin.isTTY ? await Bun.stdin.text() : undefined
- if (!args.prompt) return piped
- return piped ? piped + "\n" + args.prompt : args.prompt
- })
- const tuiPromise = tui({
- url: server.url,
- args: {
- continue: args.continue,
- sessionID: args.session,
- agent: args.agent,
- model: args.model,
- prompt,
- },
- onExit: async () => {
- await client.call("shutdown", undefined)
- },
- })
- setTimeout(() => {
- client.call("checkUpgrade", { directory: cwd }).catch(() => {})
- }, 1000)
- await tuiPromise
- },
- })
|