cloudaxe-opencode/packages/opencode/src/cli/cmd/attach.ts

107 lines
3.2 KiB
TypeScript
Raw Normal View History

import { cmd } from "./cmd"
import { UI } from "@/cli/ui"
import { errorMessage } from "@opencode-ai/tui/util/error"
import { ServerAuth } from "@/server/auth"
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
import { SessionID } from "@/session/schema"
import { Schema } from "effect"
2025-10-31 19:07:36 +00:00
export const AttachCommand = cmd({
command: "attach <url>",
describe: "attach to a running opencode server",
builder: (yargs) =>
yargs
.positional("url", {
type: "string",
describe: "http://localhost:4096",
demandOption: true,
})
.option("dir", {
type: "string",
description: "directory to run in",
})
.option("continue", {
alias: ["c"],
describe: "continue the last session",
type: "boolean",
})
.option("session", {
alias: ["s"],
type: "string",
describe: "session id to continue",
})
.option("fork", {
type: "boolean",
describe: "fork the session when continuing (use with --continue or --session)",
})
.option("password", {
alias: ["p"],
type: "string",
describe: "basic auth password (defaults to OPENCODE_SERVER_PASSWORD)",
})
.option("username", {
alias: ["u"],
type: "string",
describe: "basic auth username (defaults to OPENCODE_SERVER_USERNAME or 'opencode')",
2025-10-31 19:07:36 +00:00
}),
handler: async (args) => {
const { TuiConfig } = await import("@/config/tui")
if (args.fork && !args.continue && !args.session) {
UI.error("--fork requires --continue or --session")
process.exitCode = 1
return
}
const directory = (() => {
if (!args.dir) return undefined
try {
process.chdir(args.dir)
return process.cwd()
} catch {
// If the directory doesn't exist locally (remote attach), pass it through.
return args.dir
}
})()
const headers = ServerAuth.headers({ password: args.password, username: args.username })
const sdk = createOpencodeClient({ baseUrl: args.url, directory, headers })
const config = await TuiConfig.get()
try {
if (args.session) {
const sessionID = (() => {
try {
return Schema.decodeUnknownSync(SessionID)(args.session)
} catch (error) {
throw new Error(`Invalid session ID: ${error instanceof Error ? error.message : "unknown error"}`, {
cause: error,
})
}
})()
await sdk.session.get({ sessionID }, { throwOnError: true })
}
} catch (error) {
UI.error(errorMessage(error))
process.exitCode = 1
return
}
const { Effect } = await import("effect")
const { Global } = await import("@opencode-ai/core/global")
const { run } = await import("@opencode-ai/tui")
const { createLegacyTuiPluginHost } = await import("@/plugin/tui/runtime")
await Effect.runPromise(
run({
sdk,
2026-02-25 22:53:09 +00:00
config,
pluginHost: createLegacyTuiPluginHost(),
args: {
continue: args.continue,
sessionID: args.session,
fork: args.fork,
},
directory,
}).pipe(Effect.provide(Global.defaultLayer)),
)
2025-10-31 19:07:36 +00:00
},
})