import * as Log from "@opencode-ai/core/util/log" import { Effect } from "effect" import { effectCmd } from "../effect-cmd" import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk" import { ACP } from "@/acp/agent" import { Server } from "@/server/server" import { ServerAuth } from "@/server/auth" import { createOpencodeClient } from "@opencode-ai/sdk/v2" import { withNetworkOptions, resolveNetworkOptions } from "../network" import { ACPProfile } from "@/acp/profile" const log = Log.create({ service: "acp-command" }) export const AcpCommand = effectCmd({ command: "acp", describe: "start ACP (Agent Client Protocol) server", builder: (yargs) => { return withNetworkOptions(yargs).option("cwd", { describe: "working directory", type: "string", default: process.cwd(), }) }, handler: Effect.fn("Cli.acp")(function* (args) { ACPProfile.mark("cli.acp.handler") process.env.OPENCODE_CLIENT = "acp" const opts = yield* resolveNetworkOptions(args) const server = yield* Effect.promise(() => ACPProfile.measure("cli.acp.server.listen", () => Server.listen(opts))) const sdk = createOpencodeClient({ baseUrl: `http://${server.hostname}:${server.port}`, headers: ServerAuth.headers(), }) const input = new WritableStream({ write(chunk) { return new Promise((resolve, reject) => { process.stdout.write(chunk, (err) => { if (err) { reject(err) } else { resolve() } }) }) }, }) const output = new ReadableStream({ start(controller) { process.stdin.on("data", (chunk: Buffer) => { controller.enqueue(new Uint8Array(chunk)) }) process.stdin.on("end", () => controller.close()) process.stdin.on("error", (err) => controller.error(err)) }, }) const stream = ndJsonStream(input, output) const agent = ACP.init({ sdk }) new AgentSideConnection((conn) => { ACPProfile.mark("cli.acp.connection.create") return agent.create(conn) }, stream) log.info("setup connection") process.stdin.resume() yield* Effect.promise( () => new Promise((resolve, reject) => { process.stdin.on("end", () => resolve()) process.stdin.on("error", reject) }), ) }), })