From f5ca2467e8d9a4e759612995d1c2c24770c2bcab Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 27 Nov 2025 13:13:45 +0000 Subject: [PATCH] feat: show remote host and cwd in taskbar when using attach When connecting to a remote opencode instance via 'attach', the taskbar now displays the remote host:port and the server's working directory instead of the local machine's cwd. - Add 'attached' flag to tui() to distinguish attach mode from normal mode - Parse and expose remoteHost from connection URL in SDK context - Fetch remote directory via /path endpoint during bootstrap - Update useDirectory() hook to show 'host:port /remote/path:branch' --- packages/opencode/src/cli/cmd/tui/app.tsx | 4 ++-- packages/opencode/src/cli/cmd/tui/attach.ts | 1 + .../opencode/src/cli/cmd/tui/context/directory.ts | 10 +++++++--- packages/opencode/src/cli/cmd/tui/context/sdk.tsx | 13 ++++++++++--- packages/opencode/src/cli/cmd/tui/context/sync.tsx | 4 ++++ 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 5ec7372568e..ea121ca7172 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -91,7 +91,7 @@ async function getTerminalBackgroundColor(): Promise<"dark" | "light"> { }) } -export function tui(input: { url: string; args: Args; onExit?: () => Promise }) { +export function tui(input: { url: string; args: Args; attached?: boolean; onExit?: () => Promise }) { // promise to prevent immediate exit return new Promise(async (resolve) => { const mode = await getTerminalBackgroundColor() @@ -109,7 +109,7 @@ export function tui(input: { url: string; args: Args; onExit?: () => Promise - + diff --git a/packages/opencode/src/cli/cmd/tui/attach.ts b/packages/opencode/src/cli/cmd/tui/attach.ts index 7da6507ea01..a325207e5b0 100644 --- a/packages/opencode/src/cli/cmd/tui/attach.ts +++ b/packages/opencode/src/cli/cmd/tui/attach.ts @@ -20,6 +20,7 @@ export const AttachCommand = cmd({ await tui({ url: args.url, args: {}, + attached: true, }) }, }) diff --git a/packages/opencode/src/cli/cmd/tui/context/directory.ts b/packages/opencode/src/cli/cmd/tui/context/directory.ts index 2ea8cf007d2..efef1f19402 100644 --- a/packages/opencode/src/cli/cmd/tui/context/directory.ts +++ b/packages/opencode/src/cli/cmd/tui/context/directory.ts @@ -1,12 +1,16 @@ import { createMemo } from "solid-js" import { useSync } from "./sync" +import { useSDK } from "./sdk" import { Global } from "@/global" export function useDirectory() { const sync = useSync() + const sdk = useSDK() return createMemo(() => { - const result = process.cwd().replace(Global.Path.home, "~") - if (sync.data.vcs?.branch) return result + ":" + sync.data.vcs.branch - return result + const remote = sdk.remoteHost + const cwd = remote ? (sync.data.path?.directory ?? "...") : process.cwd().replace(Global.Path.home, "~") + const prefix = remote ? `${remote} ` : "" + if (sync.data.vcs?.branch) return prefix + cwd + ":" + sync.data.vcs.branch + return prefix + cwd }) } diff --git a/packages/opencode/src/cli/cmd/tui/context/sdk.tsx b/packages/opencode/src/cli/cmd/tui/context/sdk.tsx index 401a53ab493..b88454888cb 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sdk.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sdk.tsx @@ -2,16 +2,23 @@ import { createOpencodeClient, type Event } from "@opencode-ai/sdk" import { createSimpleContext } from "./helper" import { createGlobalEmitter } from "@solid-primitives/event-bus" import { batch, onCleanup, onMount } from "solid-js" -import { iife } from "@/util/iife" + +function parseRemoteHost(url: string): string { + const parsed = new URL(url) + const host = parsed.hostname + const port = parsed.port + return port ? `${host}:${port}` : host +} export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ name: "SDK", - init: (props: { url: string }) => { + init: (props: { url: string; attached?: boolean }) => { const abort = new AbortController() const sdk = createOpencodeClient({ baseUrl: props.url, signal: abort.signal, }) + const remoteHost = props.attached ? parseRemoteHost(props.url) : undefined const emitter = createGlobalEmitter<{ [key in Event["type"]]: Extract @@ -67,6 +74,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ abort.abort() }) - return { client: sdk, event: emitter } + return { client: sdk, event: emitter, remoteHost } }, }) diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index b7ef8a2214b..acb285f8b05 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -15,6 +15,7 @@ import type { ProviderListResponse, ProviderAuthMethod, VcsInfo, + Path, } from "@opencode-ai/sdk" import { createStore, produce, reconcile } from "solid-js/store" import { useSDK } from "@tui/context/sdk" @@ -62,6 +63,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ } formatter: FormatterStatus[] vcs: VcsInfo | undefined + path: Path | undefined }>({ provider_next: { all: [], @@ -86,6 +88,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ mcp: {}, formatter: [], vcs: undefined, + path: undefined, }) const sdk = useSDK() @@ -286,6 +289,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ sdk.client.session.status().then((x) => setStore("session_status", x.data!)), sdk.client.provider.auth().then((x) => setStore("provider_auth", x.data ?? {})), sdk.client.vcs.get().then((x) => setStore("vcs", x.data)), + sdk.client.path.get().then((x) => setStore("path", x.data)), ]).then(() => { setStore("status", "complete") })