attach.ts 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import { cmd } from "../cmd"
  2. import { UI } from "@/cli/ui"
  3. import { tui } from "./app"
  4. import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32"
  5. export const AttachCommand = cmd({
  6. command: "attach <url>",
  7. describe: "attach to a running opencode server",
  8. builder: (yargs) =>
  9. yargs
  10. .positional("url", {
  11. type: "string",
  12. describe: "http://localhost:4096",
  13. demandOption: true,
  14. })
  15. .option("dir", {
  16. type: "string",
  17. description: "directory to run in",
  18. })
  19. .option("continue", {
  20. alias: ["c"],
  21. describe: "continue the last session",
  22. type: "boolean",
  23. })
  24. .option("session", {
  25. alias: ["s"],
  26. type: "string",
  27. describe: "session id to continue",
  28. })
  29. .option("fork", {
  30. type: "boolean",
  31. describe: "fork the session when continuing (use with --continue or --session)",
  32. })
  33. .option("password", {
  34. alias: ["p"],
  35. type: "string",
  36. describe: "basic auth password (defaults to OPENCODE_SERVER_PASSWORD)",
  37. }),
  38. handler: async (args) => {
  39. const unguard = win32InstallCtrlCGuard()
  40. try {
  41. win32DisableProcessedInput()
  42. if (args.fork && !args.continue && !args.session) {
  43. UI.error("--fork requires --continue or --session")
  44. process.exitCode = 1
  45. return
  46. }
  47. const directory = (() => {
  48. if (!args.dir) return undefined
  49. try {
  50. process.chdir(args.dir)
  51. return process.cwd()
  52. } catch {
  53. // If the directory doesn't exist locally (remote attach), pass it through.
  54. return args.dir
  55. }
  56. })()
  57. const headers = (() => {
  58. const password = args.password ?? process.env.OPENCODE_SERVER_PASSWORD
  59. if (!password) return undefined
  60. const auth = `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}`
  61. return { Authorization: auth }
  62. })()
  63. await tui({
  64. url: args.url,
  65. args: {
  66. continue: args.continue,
  67. sessionID: args.session,
  68. fork: args.fork,
  69. },
  70. directory,
  71. headers,
  72. })
  73. } finally {
  74. unguard?.()
  75. }
  76. },
  77. })