Skip to content

Commit b2f45d5

Browse files
committed
Reapply "feat(core): optional mdns service (anomalyco#6192)"
This reverts commit 505068d.
1 parent 1e2ef07 commit b2f45d5

File tree

15 files changed

+234
-112
lines changed

15 files changed

+234
-112
lines changed

bun.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/opencode/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
"@standard-schema/spec": "1.0.0",
8989
"@zip.js/zip.js": "2.7.62",
9090
"ai": "catalog:",
91+
"bonjour-service": "1.3.0",
9192
"bun-pty": "0.4.2",
9293
"chokidar": "4.0.3",
9394
"clipboardy": "4.0.0",

packages/opencode/src/cli/cmd/acp.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { bootstrap } from "../bootstrap"
33
import { cmd } from "./cmd"
44
import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk"
55
import { ACP } from "@/acp/agent"
6+
import { Config } from "@/config/config"
67
import { Server } from "@/server/server"
78
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
9+
import { withNetworkOptions, resolveNetworkOptions } from "../network"
810

911
const log = Log.create({ service: "acp-command" })
1012

@@ -19,29 +21,17 @@ export const AcpCommand = cmd({
1921
command: "acp",
2022
describe: "start ACP (Agent Client Protocol) server",
2123
builder: (yargs) => {
22-
return yargs
23-
.option("cwd", {
24-
describe: "working directory",
25-
type: "string",
26-
default: process.cwd(),
27-
})
28-
.option("port", {
29-
type: "number",
30-
describe: "port to listen on",
31-
default: 0,
32-
})
33-
.option("hostname", {
34-
type: "string",
35-
describe: "hostname to listen on",
36-
default: "127.0.0.1",
37-
})
24+
return withNetworkOptions(yargs).option("cwd", {
25+
describe: "working directory",
26+
type: "string",
27+
default: process.cwd(),
28+
})
3829
},
3930
handler: async (args) => {
4031
await bootstrap(process.cwd(), async () => {
41-
const server = Server.listen({
42-
port: args.port,
43-
hostname: args.hostname,
44-
})
32+
const config = await Config.get()
33+
const opts = resolveNetworkOptions(args, config)
34+
const server = Server.listen(opts)
4535

4636
const sdk = createOpencodeClient({
4737
baseUrl: `http://${server.hostname}:${server.port}`,

packages/opencode/src/cli/cmd/serve.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,16 @@
1+
import { Config } from "../../config/config"
12
import { Server } from "../../server/server"
23
import { cmd } from "./cmd"
4+
import { withNetworkOptions, resolveNetworkOptions } from "../network"
35

46
export const ServeCommand = cmd({
57
command: "serve",
6-
builder: (yargs) =>
7-
yargs
8-
.option("port", {
9-
alias: ["p"],
10-
type: "number",
11-
describe: "port to listen on",
12-
default: 0,
13-
})
14-
.option("hostname", {
15-
type: "string",
16-
describe: "hostname to listen on",
17-
default: "127.0.0.1",
18-
}),
8+
builder: (yargs) => withNetworkOptions(yargs),
199
describe: "starts a headless opencode server",
2010
handler: async (args) => {
21-
const hostname = args.hostname
22-
const port = args.port
23-
const server = Server.listen({
24-
port,
25-
hostname,
26-
})
11+
const config = await Config.get()
12+
const opts = resolveNetworkOptions(args, config)
13+
const server = Server.listen(opts)
2714
console.log(`opencode server listening on http://${server.hostname}:${server.port}`)
2815
await new Promise(() => {})
2916
await server.stop()

packages/opencode/src/cli/cmd/tui/spawn.ts

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,23 @@
11
import { cmd } from "@/cli/cmd/cmd"
2+
import { Config } from "@/config/config"
23
import { Instance } from "@/project/instance"
34
import path from "path"
45
import { Server } from "@/server/server"
56
import { upgrade } from "@/cli/upgrade"
7+
import { withNetworkOptions, resolveNetworkOptions } from "@/cli/network"
68

79
export const TuiSpawnCommand = cmd({
810
command: "spawn [project]",
911
builder: (yargs) =>
10-
yargs
11-
.positional("project", {
12-
type: "string",
13-
describe: "path to start opencode in",
14-
})
15-
.option("port", {
16-
type: "number",
17-
describe: "port to listen on",
18-
default: 0,
19-
})
20-
.option("hostname", {
21-
type: "string",
22-
describe: "hostname to listen on",
23-
default: "127.0.0.1",
24-
}),
12+
withNetworkOptions(yargs).positional("project", {
13+
type: "string",
14+
describe: "path to start opencode in",
15+
}),
2516
handler: async (args) => {
2617
upgrade()
27-
const server = Server.listen({
28-
port: args.port,
29-
hostname: "127.0.0.1",
30-
})
18+
const config = await Config.get()
19+
const opts = resolveNetworkOptions(args, config)
20+
const server = Server.listen(opts)
3121
const bin = process.execPath
3222
const cmd = []
3323
let cwd = process.cwd()

packages/opencode/src/cli/cmd/tui/thread.ts

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import path from "path"
66
import { UI } from "@/cli/ui"
77
import { iife } from "@/util/iife"
88
import { Log } from "@/util/log"
9+
import { withNetworkOptions, resolveNetworkOptions } from "@/cli/network"
10+
import { Config } from "@/config/config"
911

1012
declare global {
1113
const OPENCODE_WORKER_PATH: string
@@ -15,7 +17,7 @@ export const TuiThreadCommand = cmd({
1517
command: "$0 [project]",
1618
describe: "start opencode tui",
1719
builder: (yargs) =>
18-
yargs
20+
withNetworkOptions(yargs)
1921
.positional("project", {
2022
type: "string",
2123
describe: "path to start opencode in",
@@ -36,23 +38,12 @@ export const TuiThreadCommand = cmd({
3638
describe: "session id to continue",
3739
})
3840
.option("prompt", {
39-
alias: ["p"],
4041
type: "string",
4142
describe: "prompt to use",
4243
})
4344
.option("agent", {
4445
type: "string",
4546
describe: "agent to use",
46-
})
47-
.option("port", {
48-
type: "number",
49-
describe: "port to listen on",
50-
default: 0,
51-
})
52-
.option("hostname", {
53-
type: "string",
54-
describe: "hostname to listen on",
55-
default: "127.0.0.1",
5647
}),
5748
handler: async (args) => {
5849
// Resolve relative paths against PWD to preserve behavior when using --cwd flag
@@ -87,10 +78,9 @@ export const TuiThreadCommand = cmd({
8778
process.on("unhandledRejection", (e) => {
8879
Log.Default.error(e)
8980
})
90-
const server = await client.call("server", {
91-
port: args.port,
92-
hostname: args.hostname,
93-
})
81+
const config = await Config.get()
82+
const networkOpts = resolveNetworkOptions(args, config)
83+
const server = await client.call("server", networkOpts)
9484
const prompt = await iife(async () => {
9585
const piped = !process.stdin.isTTY ? await Bun.stdin.text() : undefined
9686
if (!args.prompt) return piped

packages/opencode/src/cli/cmd/tui/worker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ process.on("uncaughtException", (e) => {
3030

3131
let server: Bun.Server<BunWebSocketData>
3232
export const rpc = {
33-
async server(input: { port: number; hostname: string }) {
33+
async server(input: { port: number; hostname: string; mdns?: boolean }) {
3434
if (server) await server.stop(true)
3535
try {
3636
server = Server.listen(input)

packages/opencode/src/cli/cmd/web.ts

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { Config } from "../../config/config"
12
import { Server } from "../../server/server"
23
import { UI } from "../ui"
34
import { cmd } from "./cmd"
5+
import { withNetworkOptions, resolveNetworkOptions } from "../network"
46
import open from "open"
57
import { networkInterfaces } from "os"
68

@@ -28,32 +30,17 @@ function getNetworkIPs() {
2830

2931
export const WebCommand = cmd({
3032
command: "web",
31-
builder: (yargs) =>
32-
yargs
33-
.option("port", {
34-
alias: ["p"],
35-
type: "number",
36-
describe: "port to listen on",
37-
default: 0,
38-
})
39-
.option("hostname", {
40-
type: "string",
41-
describe: "hostname to listen on",
42-
default: "127.0.0.1",
43-
}),
33+
builder: (yargs) => withNetworkOptions(yargs),
4434
describe: "starts a headless opencode server",
4535
handler: async (args) => {
46-
const hostname = args.hostname
47-
const port = args.port
48-
const server = Server.listen({
49-
port,
50-
hostname,
51-
})
36+
const config = await Config.get()
37+
const opts = resolveNetworkOptions(args, config)
38+
const server = Server.listen(opts)
5239
UI.empty()
5340
UI.println(UI.logo(" "))
5441
UI.empty()
5542

56-
if (hostname === "0.0.0.0") {
43+
if (opts.hostname === "0.0.0.0") {
5744
// Show localhost for local access
5845
const localhostUrl = `http://localhost:${server.port}`
5946
UI.println(UI.Style.TEXT_INFO_BOLD + " Local access: ", UI.Style.TEXT_NORMAL, localhostUrl)
@@ -70,6 +57,10 @@ export const WebCommand = cmd({
7057
}
7158
}
7259

60+
if (opts.mdns) {
61+
UI.println(UI.Style.TEXT_INFO_BOLD + " mDNS: ", UI.Style.TEXT_NORMAL, "opencode.local")
62+
}
63+
7364
// Open localhost in browser
7465
open(localhostUrl.toString()).catch(() => {})
7566
} else {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { Argv, InferredOptionTypes } from "yargs"
2+
import type { Config } from "../config/config"
3+
4+
const options = {
5+
port: {
6+
type: "number" as const,
7+
describe: "port to listen on",
8+
default: 0,
9+
},
10+
hostname: {
11+
type: "string" as const,
12+
describe: "hostname to listen on",
13+
default: "127.0.0.1",
14+
},
15+
mdns: {
16+
type: "boolean" as const,
17+
describe: "enable mDNS service discovery (defaults hostname to 0.0.0.0)",
18+
default: false,
19+
},
20+
}
21+
22+
export type NetworkOptions = InferredOptionTypes<typeof options>
23+
24+
export function withNetworkOptions<T>(yargs: Argv<T>) {
25+
return yargs.options(options)
26+
}
27+
28+
export function resolveNetworkOptions(args: NetworkOptions, config?: Config.Info) {
29+
const portExplicitlySet = process.argv.includes("--port")
30+
const hostnameExplicitlySet = process.argv.includes("--hostname")
31+
const mdnsExplicitlySet = process.argv.includes("--mdns")
32+
33+
const mdns = mdnsExplicitlySet ? args.mdns : (config?.server?.mdns ?? args.mdns)
34+
const port = portExplicitlySet ? args.port : (config?.server?.port ?? args.port)
35+
const hostname = hostnameExplicitlySet
36+
? args.hostname
37+
: mdns && !config?.server?.hostname
38+
? "0.0.0.0"
39+
: (config?.server?.hostname ?? args.hostname)
40+
41+
return { hostname, port, mdns }
42+
}

packages/opencode/src/config/config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,17 @@ export namespace Config {
587587
.describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"),
588588
})
589589

590+
export const Server = z
591+
.object({
592+
port: z.number().int().positive().optional().describe("Port to listen on"),
593+
hostname: z.string().optional().describe("Hostname to listen on"),
594+
mdns: z.boolean().optional().describe("Enable mDNS service discovery"),
595+
})
596+
.strict()
597+
.meta({
598+
ref: "ServerConfig",
599+
})
600+
590601
export const Layout = z.enum(["auto", "stretch"]).meta({
591602
ref: "LayoutConfig",
592603
})
@@ -635,6 +646,7 @@ export namespace Config {
635646
keybinds: Keybinds.optional().describe("Custom keybind configurations"),
636647
logLevel: Log.Level.optional().describe("Log level"),
637648
tui: TUI.optional().describe("TUI specific settings"),
649+
server: Server.optional().describe("Server configuration for opencode serve and web commands"),
638650
command: z
639651
.record(z.string(), Command)
640652
.optional()

0 commit comments

Comments
 (0)