fix: avoid embedded opencode port conflicts
This commit is contained in:
+46
-3
@@ -4,6 +4,7 @@ import {
|
||||
type OpencodeClient,
|
||||
} from "@opencode-ai/sdk/v2";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { createServer } from "node:net";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
import { config } from "../config.js";
|
||||
@@ -44,6 +45,34 @@ type RuntimeMessage = {
|
||||
const getRuntimeMessageRole = (message: RuntimeMessage) => message.info.role;
|
||||
const getRuntimeMessageId = (message: RuntimeMessage) => message.info.id;
|
||||
|
||||
const isTcpPortAvailable = (hostname: string, port: number) =>
|
||||
new Promise<boolean>((resolve, reject) => {
|
||||
const server = createServer();
|
||||
server.unref();
|
||||
server.once("error", (error: NodeJS.ErrnoException) => {
|
||||
if (error.code === "EADDRINUSE") {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
server.listen({ host: hostname, port, exclusive: true }, () => {
|
||||
server.close((error) => (error ? reject(error) : resolve(true)));
|
||||
});
|
||||
});
|
||||
|
||||
export const resolveEmbeddedOpencodePort = async (
|
||||
hostname: string,
|
||||
configuredPort: number,
|
||||
) => {
|
||||
if (configuredPort === 0) {
|
||||
return 0;
|
||||
}
|
||||
return (await isTcpPortAvailable(hostname, configuredPort))
|
||||
? configuredPort
|
||||
: 0;
|
||||
};
|
||||
|
||||
export class OpencodeRuntimeAdapter {
|
||||
private clientPromise: Promise<OpencodeClient> | null = null;
|
||||
private closeServer: (() => void) | null = null;
|
||||
@@ -408,10 +437,24 @@ export class OpencodeRuntimeAdapter {
|
||||
process.env.TJWATER_AGENT_INTERNAL_TOKEN ??
|
||||
"";
|
||||
|
||||
const port = await resolveEmbeddedOpencodePort(
|
||||
config.OPENCODE_HOSTNAME,
|
||||
config.OPENCODE_PORT,
|
||||
);
|
||||
if (port !== config.OPENCODE_PORT) {
|
||||
logger.warn(
|
||||
{
|
||||
hostname: config.OPENCODE_HOSTNAME,
|
||||
configuredPort: config.OPENCODE_PORT,
|
||||
},
|
||||
"configured opencode port is occupied; falling back to a dynamic port",
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
{
|
||||
hostname: config.OPENCODE_HOSTNAME,
|
||||
port: config.OPENCODE_PORT,
|
||||
port,
|
||||
model: config.OPENCODE_MODEL,
|
||||
mode: config.OPENCODE_MODE,
|
||||
},
|
||||
@@ -423,7 +466,7 @@ export class OpencodeRuntimeAdapter {
|
||||
try {
|
||||
runtime = await createOpencode({
|
||||
hostname: config.OPENCODE_HOSTNAME,
|
||||
port: config.OPENCODE_PORT,
|
||||
port,
|
||||
timeout: config.OPENCODE_TIMEOUT_MS,
|
||||
config: buildOpencodeConfig(),
|
||||
});
|
||||
@@ -440,7 +483,7 @@ export class OpencodeRuntimeAdapter {
|
||||
{
|
||||
elapsedMs: Math.max(0, Date.now() - startedAt),
|
||||
hostname: config.OPENCODE_HOSTNAME,
|
||||
port: config.OPENCODE_PORT,
|
||||
baseUrl: runtime.server.url,
|
||||
mode: config.OPENCODE_MODE,
|
||||
},
|
||||
"opencode server started in embedded mode",
|
||||
|
||||
Reference in New Issue
Block a user