|
|
@@ -7,11 +7,39 @@ import { UI } from "@/cli/ui"
|
|
|
import { iife } from "@/util/iife"
|
|
|
import { Log } from "@/util/log"
|
|
|
import { withNetworkOptions, resolveNetworkOptions } from "@/cli/network"
|
|
|
+import type { Event } from "@opencode-ai/sdk/v2"
|
|
|
+import type { EventSource } from "./context/sdk"
|
|
|
|
|
|
declare global {
|
|
|
const OPENCODE_WORKER_PATH: string
|
|
|
}
|
|
|
|
|
|
+type RpcClient = ReturnType<typeof Rpc.client<typeof rpc>>
|
|
|
+
|
|
|
+function createWorkerFetch(client: RpcClient): typeof fetch {
|
|
|
+ const fn = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
|
|
|
+ const request = new Request(input, init)
|
|
|
+ const body = request.body ? await request.text() : undefined
|
|
|
+ const result = await client.call("fetch", {
|
|
|
+ url: request.url,
|
|
|
+ method: request.method,
|
|
|
+ headers: Object.fromEntries(request.headers.entries()),
|
|
|
+ body,
|
|
|
+ })
|
|
|
+ return new Response(result.body, {
|
|
|
+ status: result.status,
|
|
|
+ headers: result.headers,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ return fn as typeof fetch
|
|
|
+}
|
|
|
+
|
|
|
+function createEventSource(client: RpcClient): EventSource {
|
|
|
+ return {
|
|
|
+ on: (handler) => client.on<Event>("event", handler),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
export const TuiThreadCommand = cmd({
|
|
|
command: "$0 [project]",
|
|
|
describe: "start opencode tui",
|
|
|
@@ -80,16 +108,45 @@ export const TuiThreadCommand = cmd({
|
|
|
process.on("SIGUSR2", async () => {
|
|
|
await client.call("reload", undefined)
|
|
|
})
|
|
|
- const opts = await resolveNetworkOptions(args)
|
|
|
- const server = await client.call("server", opts)
|
|
|
+
|
|
|
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
|
|
|
})
|
|
|
|
|
|
+ // Check if server should be started (port or hostname explicitly set in CLI or config)
|
|
|
+ const networkOpts = await resolveNetworkOptions(args)
|
|
|
+ const shouldStartServer =
|
|
|
+ process.argv.includes("--port") ||
|
|
|
+ process.argv.includes("--hostname") ||
|
|
|
+ process.argv.includes("--mdns") ||
|
|
|
+ networkOpts.mdns ||
|
|
|
+ networkOpts.port !== 0 ||
|
|
|
+ networkOpts.hostname !== "127.0.0.1"
|
|
|
+
|
|
|
+ // Subscribe to events from worker
|
|
|
+ await client.call("subscribe", { directory: cwd })
|
|
|
+
|
|
|
+ let url: string
|
|
|
+ let customFetch: typeof fetch | undefined
|
|
|
+ let events: EventSource | undefined
|
|
|
+
|
|
|
+ if (shouldStartServer) {
|
|
|
+ // Start HTTP server for external access
|
|
|
+ const server = await client.call("server", networkOpts)
|
|
|
+ url = server.url
|
|
|
+ } else {
|
|
|
+ // Use direct RPC communication (no HTTP)
|
|
|
+ url = "http://opencode.internal"
|
|
|
+ customFetch = createWorkerFetch(client)
|
|
|
+ events = createEventSource(client)
|
|
|
+ }
|
|
|
+
|
|
|
const tuiPromise = tui({
|
|
|
- url: server.url,
|
|
|
+ url,
|
|
|
+ fetch: customFetch,
|
|
|
+ events,
|
|
|
args: {
|
|
|
continue: args.continue,
|
|
|
sessionID: args.session,
|