feat(core): optional mdns service (#6192)

Co-authored-by: Github Action <action@github.com>
This commit is contained in:
Adam
2025-12-26 10:29:48 -06:00
committed by GitHub
parent dd569c927a
commit 26e7043718
17 changed files with 238 additions and 116 deletions

View File

@@ -3,8 +3,10 @@ import { bootstrap } from "../bootstrap"
import { cmd } from "./cmd"
import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk"
import { ACP } from "@/acp/agent"
import { Config } from "@/config/config"
import { Server } from "@/server/server"
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
import { withNetworkOptions, resolveNetworkOptions } from "../network"
const log = Log.create({ service: "acp-command" })
@@ -19,29 +21,17 @@ export const AcpCommand = cmd({
command: "acp",
describe: "start ACP (Agent Client Protocol) server",
builder: (yargs) => {
return yargs
.option("cwd", {
describe: "working directory",
type: "string",
default: process.cwd(),
})
.option("port", {
type: "number",
describe: "port to listen on",
default: 0,
})
.option("hostname", {
type: "string",
describe: "hostname to listen on",
default: "127.0.0.1",
})
return withNetworkOptions(yargs).option("cwd", {
describe: "working directory",
type: "string",
default: process.cwd(),
})
},
handler: async (args) => {
await bootstrap(process.cwd(), async () => {
const server = Server.listen({
port: args.port,
hostname: args.hostname,
})
const config = await Config.get()
const opts = resolveNetworkOptions(args, config)
const server = Server.listen(opts)
const sdk = createOpencodeClient({
baseUrl: `http://${server.hostname}:${server.port}`,

View File

@@ -1,29 +1,16 @@
import { Config } from "../../config/config"
import { Server } from "../../server/server"
import { cmd } from "./cmd"
import { withNetworkOptions, resolveNetworkOptions } from "../network"
export const ServeCommand = cmd({
command: "serve",
builder: (yargs) =>
yargs
.option("port", {
alias: ["p"],
type: "number",
describe: "port to listen on",
default: 0,
})
.option("hostname", {
type: "string",
describe: "hostname to listen on",
default: "127.0.0.1",
}),
builder: (yargs) => withNetworkOptions(yargs),
describe: "starts a headless opencode server",
handler: async (args) => {
const hostname = args.hostname
const port = args.port
const server = Server.listen({
port,
hostname,
})
const config = await Config.get()
const opts = resolveNetworkOptions(args, config)
const server = Server.listen(opts)
console.log(`opencode server listening on http://${server.hostname}:${server.port}`)
await new Promise(() => {})
await server.stop()

View File

@@ -1,33 +1,23 @@
import { cmd } from "@/cli/cmd/cmd"
import { Config } from "@/config/config"
import { Instance } from "@/project/instance"
import path from "path"
import { Server } from "@/server/server"
import { upgrade } from "@/cli/upgrade"
import { withNetworkOptions, resolveNetworkOptions } from "@/cli/network"
export const TuiSpawnCommand = cmd({
command: "spawn [project]",
builder: (yargs) =>
yargs
.positional("project", {
type: "string",
describe: "path to start opencode in",
})
.option("port", {
type: "number",
describe: "port to listen on",
default: 0,
})
.option("hostname", {
type: "string",
describe: "hostname to listen on",
default: "127.0.0.1",
}),
withNetworkOptions(yargs).positional("project", {
type: "string",
describe: "path to start opencode in",
}),
handler: async (args) => {
upgrade()
const server = Server.listen({
port: args.port,
hostname: "127.0.0.1",
})
const config = await Config.get()
const opts = resolveNetworkOptions(args, config)
const server = Server.listen(opts)
const bin = process.execPath
const cmd = []
let cwd = process.cwd()

View File

@@ -6,6 +6,8 @@ import path from "path"
import { UI } from "@/cli/ui"
import { iife } from "@/util/iife"
import { Log } from "@/util/log"
import { withNetworkOptions, resolveNetworkOptions } from "@/cli/network"
import { Config } from "@/config/config"
declare global {
const OPENCODE_WORKER_PATH: string
@@ -15,7 +17,7 @@ export const TuiThreadCommand = cmd({
command: "$0 [project]",
describe: "start opencode tui",
builder: (yargs) =>
yargs
withNetworkOptions(yargs)
.positional("project", {
type: "string",
describe: "path to start opencode in",
@@ -36,23 +38,12 @@ export const TuiThreadCommand = cmd({
describe: "session id to continue",
})
.option("prompt", {
alias: ["p"],
type: "string",
describe: "prompt to use",
})
.option("agent", {
type: "string",
describe: "agent to use",
})
.option("port", {
type: "number",
describe: "port to listen on",
default: 0,
})
.option("hostname", {
type: "string",
describe: "hostname to listen on",
default: "127.0.0.1",
}),
handler: async (args) => {
// Resolve relative paths against PWD to preserve behavior when using --cwd flag
@@ -87,10 +78,9 @@ export const TuiThreadCommand = cmd({
process.on("unhandledRejection", (e) => {
Log.Default.error(e)
})
const server = await client.call("server", {
port: args.port,
hostname: args.hostname,
})
const config = await Config.get()
const networkOpts = resolveNetworkOptions(args, config)
const server = await client.call("server", networkOpts)
const prompt = await iife(async () => {
const piped = !process.stdin.isTTY ? await Bun.stdin.text() : undefined
if (!args.prompt) return piped

View File

@@ -30,7 +30,7 @@ process.on("uncaughtException", (e) => {
let server: Bun.Server<BunWebSocketData>
export const rpc = {
async server(input: { port: number; hostname: string }) {
async server(input: { port: number; hostname: string; mdns?: boolean }) {
if (server) await server.stop(true)
try {
server = Server.listen(input)

View File

@@ -1,6 +1,8 @@
import { Config } from "../../config/config"
import { Server } from "../../server/server"
import { UI } from "../ui"
import { cmd } from "./cmd"
import { withNetworkOptions, resolveNetworkOptions } from "../network"
import open from "open"
import { networkInterfaces } from "os"
@@ -28,32 +30,17 @@ function getNetworkIPs() {
export const WebCommand = cmd({
command: "web",
builder: (yargs) =>
yargs
.option("port", {
alias: ["p"],
type: "number",
describe: "port to listen on",
default: 0,
})
.option("hostname", {
type: "string",
describe: "hostname to listen on",
default: "127.0.0.1",
}),
builder: (yargs) => withNetworkOptions(yargs),
describe: "starts a headless opencode server",
handler: async (args) => {
const hostname = args.hostname
const port = args.port
const server = Server.listen({
port,
hostname,
})
const config = await Config.get()
const opts = resolveNetworkOptions(args, config)
const server = Server.listen(opts)
UI.empty()
UI.println(UI.logo(" "))
UI.empty()
if (hostname === "0.0.0.0") {
if (opts.hostname === "0.0.0.0") {
// Show localhost for local access
const localhostUrl = `http://localhost:${server.port}`
UI.println(UI.Style.TEXT_INFO_BOLD + " Local access: ", UI.Style.TEXT_NORMAL, localhostUrl)
@@ -70,6 +57,10 @@ export const WebCommand = cmd({
}
}
if (opts.mdns) {
UI.println(UI.Style.TEXT_INFO_BOLD + " mDNS: ", UI.Style.TEXT_NORMAL, "opencode.local")
}
// Open localhost in browser
open(localhostUrl.toString()).catch(() => {})
} else {