@agentos-software/opencode 0.0.0-nathan-binding-workspace.abc7ec9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/adapter.ts ADDED
@@ -0,0 +1,27 @@
1
+ process.env.OPENCODE_DISABLE_CONFIG_DEP_INSTALL ??= "1";
2
+ process.env.OPENCODE_DISABLE_EMBEDDED_WEB_UI ??= "1";
3
+
4
+ // @ts-expect-error Generated at build time by scripts/build-opencode-acp.mjs.
5
+ const { AcpCommand } = (await import("./opencode-acp/acp.js")) as {
6
+ AcpCommand: {
7
+ handler(args: {
8
+ port: number;
9
+ hostname: string;
10
+ mdns: boolean;
11
+ "mdns-domain": string;
12
+ cors: string[];
13
+ cwd: string;
14
+ }): Promise<void>;
15
+ };
16
+ };
17
+
18
+ await AcpCommand.handler({
19
+ port: 0,
20
+ hostname: "127.0.0.1",
21
+ mdns: false,
22
+ "mdns-domain": "opencode.local",
23
+ cors: [],
24
+ cwd: process.cwd(),
25
+ });
26
+
27
+ export {};
package/src/index.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { defineSoftware } from "@rivet-dev/agentos-core";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const packageDir = resolve(__dirname, "..");
7
+
8
+ const opencode = defineSoftware({
9
+ name: "opencode",
10
+ type: "agent" as const,
11
+ packageDir,
12
+ requires: ["@agentos-software/opencode"],
13
+ agent: {
14
+ id: "opencode",
15
+ // OpenCode still speaks ACP natively, but Agent OS runs a source-built
16
+ // Node ACP bundle entirely inside the VM rather than a host binary wrapper.
17
+ acpAdapter: "@agentos-software/opencode",
18
+ agentPackage: "@agentos-software/opencode",
19
+ staticEnv: {
20
+ OPENCODE_DISABLE_CONFIG_DEP_INSTALL: "1",
21
+ OPENCODE_DISABLE_EMBEDDED_WEB_UI: "1",
22
+ },
23
+ },
24
+ });
25
+
26
+ export default opencode;
@@ -0,0 +1,14 @@
1
+ declare module "./opencode-acp.mjs" {
2
+ export const AcpCommand: {
3
+ handler(args: {
4
+ port: number;
5
+ hostname: string;
6
+ mdns: boolean;
7
+ "mdns-domain": string;
8
+ cors: string[];
9
+ cwd: string;
10
+ }): Promise<void>;
11
+ };
12
+ }
13
+
14
+ export {};
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "declaration": true,
5
+ "outDir": "./dist",
6
+ "rootDir": "./src"
7
+ },
8
+ "include": ["src/**/*"],
9
+ "exclude": ["node_modules", "dist"]
10
+ }
@@ -0,0 +1,251 @@
1
+ diff --git a/packages/opencode/src/cli/cmd/acp.ts b/packages/opencode/src/cli/cmd/acp.ts
2
+ index 99a9a81ab..2fb9038b0 100644
3
+ --- a/packages/opencode/src/cli/cmd/acp.ts
4
+ +++ b/packages/opencode/src/cli/cmd/acp.ts
5
+ @@ -23,7 +23,7 @@ export const AcpCommand = cmd({
6
+ process.env.OPENCODE_CLIENT = "acp"
7
+ await bootstrap(process.cwd(), async () => {
8
+ const opts = await resolveNetworkOptions(args)
9
+ - const server = Server.listen(opts)
10
+ + const server = await Server.listen(opts)
11
+
12
+ const sdk = createOpencodeClient({
13
+ baseUrl: `http://${server.hostname}:${server.port}`,
14
+ diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
15
+ index f86d8d32a..fdb4dc107 100644
16
+ --- a/packages/opencode/src/config/config.ts
17
+ +++ b/packages/opencode/src/config/config.ts
18
+ @@ -90,6 +90,11 @@ export namespace Config {
19
+ }
20
+
21
+ export async function installDependencies(dir: string, input?: InstallInput) {
22
+ + if (process.env.OPENCODE_DISABLE_CONFIG_DEP_INSTALL === "1") {
23
+ + log.info("skipping dependency install", { dir, reason: "disabled_by_env" })
24
+ + return
25
+ + }
26
+ +
27
+ if (!(await needsInstall(dir))) return
28
+
29
+ await using _ = await Flock.acquire(`config-install:${Filesystem.resolve(dir)}`, {
30
+ diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts
31
+ index b05dd8625..f2afc5979 100644
32
+ --- a/packages/opencode/src/plugin/index.ts
33
+ +++ b/packages/opencode/src/plugin/index.ts
34
+ @@ -50,7 +50,10 @@ export namespace Plugin {
35
+ export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Plugin") {}
36
+
37
+ // Built-in plugins that are directly imported (not installed from npm)
38
+ - const INTERNAL_PLUGINS: PluginInstance[] = [CodexAuthPlugin, CopilotAuthPlugin, GitlabAuthPlugin, PoeAuthPlugin]
39
+ + const INTERNAL_PLUGINS: PluginInstance[] =
40
+ + process.env.OPENCODE_ENABLE_INTERNAL_AUTH_PLUGINS === "1"
41
+ + ? [CodexAuthPlugin, CopilotAuthPlugin, GitlabAuthPlugin, PoeAuthPlugin]
42
+ + : []
43
+
44
+ function isServerPlugin(value: unknown): value is PluginInstance {
45
+ return typeof value === "function"
46
+ @@ -128,7 +131,9 @@ export namespace Plugin {
47
+ get serverUrl(): URL {
48
+ return Server.url ?? new URL("http://localhost:4096")
49
+ },
50
+ - $: Bun.$,
51
+ + $: ((..._args: unknown[]) => {
52
+ + throw new Error("Bun shell is unavailable in the Node ACP build")
53
+ + }) as PluginInput["$"],
54
+ }
55
+
56
+ for (const plugin of INTERNAL_PLUGINS) {
57
+ diff --git a/packages/opencode/src/server/instance.ts b/packages/opencode/src/server/instance.ts
58
+ index 4bb6efaf9..fc634f5c2 100644
59
+ --- a/packages/opencode/src/server/instance.ts
60
+ +++ b/packages/opencode/src/server/instance.ts
61
+ @@ -18,7 +18,6 @@ import { QuestionRoutes } from "./routes/question"
62
+ import { PermissionRoutes } from "./routes/permission"
63
+ import { ProjectRoutes } from "./routes/project"
64
+ import { SessionRoutes } from "./routes/session"
65
+ -import { PtyRoutes } from "./routes/pty"
66
+ import { McpRoutes } from "./routes/mcp"
67
+ import { FileRoutes } from "./routes/file"
68
+ import { ConfigRoutes } from "./routes/config"
69
+ @@ -44,7 +43,6 @@ export const InstanceRoutes = (app?: Hono) =>
70
+ (app ?? new Hono())
71
+ .onError(errorHandler(log))
72
+ .route("/project", ProjectRoutes())
73
+ - .route("/pty", PtyRoutes())
74
+ .route("/config", ConfigRoutes())
75
+ .route("/experimental", ExperimentalRoutes())
76
+ .route("/session", SessionRoutes())
77
+ diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts
78
+ index ec245ed59..8df8fe679 100644
79
+ --- a/packages/opencode/src/server/server.ts
80
+ +++ b/packages/opencode/src/server/server.ts
81
+ @@ -9,7 +9,6 @@ import { Auth } from "../auth"
82
+ import { Flag } from "../flag/flag"
83
+ import { ProviderID } from "../provider/schema"
84
+ import { WorkspaceRouterMiddleware } from "./router"
85
+ -import { websocket } from "hono/bun"
86
+ import { errors } from "./error"
87
+ import { GlobalRoutes } from "./routes/global"
88
+ import { MDNS } from "./mdns"
89
+ @@ -17,6 +16,9 @@ import { lazy } from "@/util/lazy"
90
+ import { errorHandler } from "./middleware"
91
+ import { InstanceRoutes } from "./instance"
92
+ import { initProjectors } from "./projectors"
93
+ +import { createServer, type IncomingMessage, type ServerResponse } from "node:http"
94
+ +import type { AddressInfo } from "node:net"
95
+ +import { Readable } from "node:stream"
96
+
97
+ // @ts-ignore This global is needed to prevent ai-sdk from logging warnings to stdout https://github.com/vercel/ai/blob/2dc67e0ef538307f21368db32d5a12345d98831b/packages/ai/src/logger/log-warnings.ts#L85
98
+ globalThis.AI_SDK_LOG_WARNINGS = false
99
+ @@ -264,7 +266,106 @@ export namespace Server {
100
+ /** @deprecated do not use this dumb shit */
101
+ export let url: URL
102
+
103
+ - export function listen(opts: {
104
+ + type NodeServeHandle = {
105
+ + hostname: string
106
+ + port: number
107
+ + stop: (closeActiveConnections?: boolean) => Promise<void>
108
+ + }
109
+ +
110
+ + function toRequestUrl(req: IncomingMessage, fallbackHost: string, fallbackPort: number) {
111
+ + const host = req.headers.host ?? `${fallbackHost}:${fallbackPort}`
112
+ + return new URL(req.url ?? "/", `http://${host}`)
113
+ + }
114
+ +
115
+ + function toHeaders(req: IncomingMessage) {
116
+ + const headers = new Headers()
117
+ + for (const [key, value] of Object.entries(req.headers)) {
118
+ + if (Array.isArray(value)) {
119
+ + for (const item of value) headers.append(key, item)
120
+ + continue
121
+ + }
122
+ + if (typeof value === "string") headers.set(key, value)
123
+ + }
124
+ + return headers
125
+ + }
126
+ +
127
+ + async function writeResponse(res: ServerResponse, response: Response) {
128
+ + res.statusCode = response.status
129
+ + res.statusMessage = response.statusText
130
+ + response.headers.forEach((value, key) => {
131
+ + res.setHeader(key, value)
132
+ + })
133
+ +
134
+ + if (!response.body) {
135
+ + res.end()
136
+ + return
137
+ + }
138
+ +
139
+ + await new Promise<void>((resolve, reject) => {
140
+ + const body = Readable.fromWeb(response.body as globalThis.ReadableStream<Uint8Array>)
141
+ + body.on("error", reject)
142
+ + res.on("error", reject)
143
+ + res.on("close", resolve)
144
+ + body.pipe(res)
145
+ + })
146
+ + }
147
+ +
148
+ + async function serveNode(opts: {
149
+ + hostname: string
150
+ + port: number
151
+ + fetch: (request: Request) => Response | Promise<Response>
152
+ + }): Promise<NodeServeHandle> {
153
+ + const server = createServer(async (req, res) => {
154
+ + try {
155
+ + const request = new Request(toRequestUrl(req, opts.hostname, opts.port), {
156
+ + method: req.method,
157
+ + headers: toHeaders(req),
158
+ + body:
159
+ + req.method && req.method !== "GET" && req.method !== "HEAD"
160
+ + ? (Readable.toWeb(req) as globalThis.ReadableStream<Uint8Array>)
161
+ + : undefined,
162
+ + duplex: "half",
163
+ + })
164
+ + const response = await opts.fetch(request)
165
+ + await writeResponse(res, response)
166
+ + } catch (error) {
167
+ + log.error("node server request failed", { error })
168
+ + if (!res.headersSent) {
169
+ + res.statusCode = 500
170
+ + res.setHeader("content-type", "text/plain; charset=utf-8")
171
+ + }
172
+ + res.end("Internal Server Error")
173
+ + }
174
+ + })
175
+ +
176
+ + await new Promise<void>((resolve, reject) => {
177
+ + server.once("error", reject)
178
+ + server.listen(opts.port, opts.hostname, () => {
179
+ + server.off("error", reject)
180
+ + resolve()
181
+ + })
182
+ + })
183
+ +
184
+ + const address = server.address()
185
+ + if (!address || typeof address === "string") {
186
+ + throw new Error("Failed to resolve server address")
187
+ + }
188
+ +
189
+ + return {
190
+ + hostname: opts.hostname,
191
+ + port: (address as AddressInfo).port,
192
+ + stop() {
193
+ + return new Promise<void>((resolve, reject) => {
194
+ + server.close((error) => {
195
+ + if (error) reject(error)
196
+ + else resolve()
197
+ + })
198
+ + })
199
+ + },
200
+ + }
201
+ + }
202
+ +
203
+ + export async function listen(opts: {
204
+ port: number
205
+ hostname: string
206
+ mdns?: boolean
207
+ @@ -273,20 +374,18 @@ export namespace Server {
208
+ }) {
209
+ url = new URL(`http://${opts.hostname}:${opts.port}`)
210
+ const app = ControlPlaneRoutes({ cors: opts.cors })
211
+ - const args = {
212
+ - hostname: opts.hostname,
213
+ - idleTimeout: 0,
214
+ - fetch: app.fetch,
215
+ - websocket: websocket,
216
+ - } as const
217
+ - const tryServe = (port: number) => {
218
+ + const tryServe = async (port: number) => {
219
+ try {
220
+ - return Bun.serve({ ...args, port })
221
+ + return await serveNode({
222
+ + hostname: opts.hostname,
223
+ + port,
224
+ + fetch: app.fetch,
225
+ + })
226
+ } catch {
227
+ return undefined
228
+ }
229
+ }
230
+ - const server = opts.port === 0 ? (tryServe(4096) ?? tryServe(0)) : tryServe(opts.port)
231
+ + const server = opts.port === 0 ? ((await tryServe(4096)) ?? (await tryServe(0))) : await tryServe(opts.port)
232
+ if (!server) throw new Error(`Failed to start server on port ${opts.port}`)
233
+
234
+ const shouldPublishMDNS =
235
+ @@ -296,7 +395,7 @@ export namespace Server {
236
+ opts.hostname !== "localhost" &&
237
+ opts.hostname !== "::1"
238
+ if (shouldPublishMDNS) {
239
+ - MDNS.publish(server.port!, opts.mdnsDomain)
240
+ + MDNS.publish(server.port, opts.mdnsDomain)
241
+ } else if (opts.mdns) {
242
+ log.warn("mDNS enabled but hostname is loopback; skipping mDNS publish")
243
+ }
244
+ @@ -307,6 +406,7 @@ export namespace Server {
245
+ return originalStop(closeActiveConnections)
246
+ }
247
+
248
+ + url = new URL(`http://${opts.hostname}:${server.port}`)
249
+ return server
250
+ }
251
+ }