@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/.turbo/turbo-build.log +25 -0
- package/dist/adapter.d.ts +1 -0
- package/dist/adapter.js +13 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +23 -0
- package/dist/opencode-acp/acp.js +250773 -0
- package/dist/opencode-acp/home/runner/work/agentos/agentos/registry/agent/opencode/dist/opencode-acp/acp.js +250760 -0
- package/dist/opencode-acp/home/runner/work/agentos/agentos/registry/agent/opencode/dist/opencode-acp/tree-sitter-3jzf13jk.wasm +0 -0
- package/dist/opencode-acp/home/runner/work/agentos/agentos/registry/agent/opencode/dist/opencode-acp/tree-sitter-bash-hq5s6fxb.wasm +0 -0
- package/dist/opencode-acp/home/runner/work/agentos/agentos/registry/agent/opencode/dist/opencode-acp/tree-sitter-powershell-ryb2ffqs.wasm +0 -0
- package/dist/opencode-acp/tree-sitter-3jzf13jk.wasm +0 -0
- package/dist/opencode-acp/tree-sitter-bash-hq5s6fxb.wasm +0 -0
- package/dist/opencode-acp/tree-sitter-powershell-ryb2ffqs.wasm +0 -0
- package/dist/opencode-acp.manifest.json +13 -0
- package/package.json +30 -0
- package/scripts/build-opencode-acp.mjs +3681 -0
- package/src/adapter.ts +27 -0
- package/src/index.ts +26 -0
- package/src/opencode-acp.mjs.d.ts +14 -0
- package/tsconfig.json +10 -0
- package/upstream/opencode-v1.3.13.patch +251 -0
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;
|
package/tsconfig.json
ADDED
|
@@ -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
|
+
}
|