Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
656fa19
refactor: lsp server and core improvements
thdxr Mar 10, 2026
e8ee1e2
sync
thdxr Mar 10, 2026
173128d
Update packages/opencode/src/npm/index.ts
thdxr Mar 10, 2026
27f3598
Update packages/opencode/src/util/which.ts
thdxr Mar 10, 2026
0e176d3
sync
thdxr Mar 10, 2026
528daf5
core: dynamically resolve formatter executable paths at runtime
thdxr Mar 10, 2026
a9b01be
core: disable npm bin links to fix package installation in sandboxed …
thdxr Mar 10, 2026
0cdd4e4
core: fix dependency installation failures behind corporate proxies o…
thdxr Mar 10, 2026
4c57e39
core: enable npm bin links on non-Windows platforms to allow plugin e…
thdxr Mar 10, 2026
85c2bb3
core: fix npm dependency installation on Windows CI by disabling bin …
thdxr Mar 10, 2026
124a8ab
tui: export sessions using consistent Filesystem API instead of Bun.w…
thdxr Mar 10, 2026
b1a15d5
sync
thdxr Mar 10, 2026
ceb79c7
core: fix CLI tools from npm packages not being accessible after inst…
thdxr Mar 10, 2026
0ff8bfe
sync
thdxr Mar 10, 2026
58cf092
core: log npm install errors to console for debugging dependency fail…
thdxr Mar 10, 2026
0faa191
sync
thdxr Mar 10, 2026
58a4cd0
sync
thdxr Mar 10, 2026
2678ceb
sync
thdxr Mar 10, 2026
3c2fda4
core: fix custom tool loading to properly resolve module paths
thdxr Mar 10, 2026
b2eae86
tui: fix Windows plugin loading by using direct paths instead of file…
thdxr Mar 10, 2026
2f41d89
fix: work around Bun/Windows UV_FS_O_FILEMAP incompatibility in tar (…
Hona Mar 10, 2026
5dc8b4e
core: add Node.js runtime support
thdxr Mar 10, 2026
406d216
refactor(server): replace Bun serve with Hono node adapters
thdxr Mar 9, 2026
070c167
core: bundle database migrations into node build and auto-start serve…
thdxr Mar 10, 2026
d4e51e0
sync
thdxr Mar 10, 2026
d67e877
core: remove shell execution and server URL from plugin API
thdxr Mar 10, 2026
5f277d1
core: return structured server info with stop method from workspace s…
thdxr Mar 10, 2026
21e72cb
core: cleaner error output and more flexible custom tool directories
thdxr Mar 10, 2026
4d81e2d
sync
thdxr Mar 10, 2026
a28648f
core: enable running in non-Bun environments by using standard Node.j…
thdxr Mar 10, 2026
4d5da96
sync
thdxr Mar 10, 2026
040700d
unbreak
thdxr Mar 10, 2026
b99de41
refactor(npm): inline pkgPath and lockPath variables
thdxr Mar 10, 2026
cb5674e
sync
thdxr Mar 10, 2026
6ad171d
Merge branch 'dev' into opencode-2-0
thdxr Mar 10, 2026
7910ce5
fix: guard Npm.which() against infinite loop when .bin is empty (#16961)
Hona Mar 11, 2026
870a573
refactor: lsp server and core improvements
thdxr Mar 10, 2026
6722ee2
sync
thdxr Mar 10, 2026
e6bf830
Update packages/opencode/src/npm/index.ts
thdxr Mar 10, 2026
c10b588
Update packages/opencode/src/util/which.ts
thdxr Mar 10, 2026
4a6a18c
sync
thdxr Mar 10, 2026
721b240
core: dynamically resolve formatter executable paths at runtime
thdxr Mar 10, 2026
8e102d1
core: disable npm bin links to fix package installation in sandboxed …
thdxr Mar 10, 2026
1b408cf
core: fix dependency installation failures behind corporate proxies o…
thdxr Mar 10, 2026
0b5d54f
core: enable npm bin links on non-Windows platforms to allow plugin e…
thdxr Mar 10, 2026
ca26e63
core: fix npm dependency installation on Windows CI by disabling bin …
thdxr Mar 10, 2026
fac0aec
tui: export sessions using consistent Filesystem API instead of Bun.w…
thdxr Mar 10, 2026
791e27d
sync
thdxr Mar 10, 2026
dd0c258
core: fix CLI tools from npm packages not being accessible after inst…
thdxr Mar 10, 2026
655fe20
sync
thdxr Mar 10, 2026
ced125a
core: log npm install errors to console for debugging dependency fail…
thdxr Mar 10, 2026
a18528a
sync
thdxr Mar 10, 2026
5ea92ea
sync
thdxr Mar 10, 2026
2a98920
sync
thdxr Mar 10, 2026
5325b2e
core: fix custom tool loading to properly resolve module paths
thdxr Mar 10, 2026
5e069aa
tui: fix Windows plugin loading by using direct paths instead of file…
thdxr Mar 10, 2026
4f82248
fix: work around Bun/Windows UV_FS_O_FILEMAP incompatibility in tar (…
Hona Mar 10, 2026
0ec4258
core: add Node.js runtime support
thdxr Mar 10, 2026
88dae67
refactor(server): replace Bun serve with Hono node adapters
thdxr Mar 9, 2026
66342ac
core: bundle database migrations into node build and auto-start serve…
thdxr Mar 10, 2026
4cba561
sync
thdxr Mar 10, 2026
0b686b8
core: remove shell execution and server URL from plugin API
thdxr Mar 10, 2026
3154f0a
core: return structured server info with stop method from workspace s…
thdxr Mar 10, 2026
190319f
core: cleaner error output and more flexible custom tool directories
thdxr Mar 10, 2026
1ac3971
sync
thdxr Mar 10, 2026
bca723e
core: enable running in non-Bun environments by using standard Node.j…
thdxr Mar 10, 2026
27ab51f
sync
thdxr Mar 10, 2026
be6f590
unbreak
thdxr Mar 10, 2026
f0b7c8c
refactor(npm): inline pkgPath and lockPath variables
thdxr Mar 10, 2026
2e04b66
sync
thdxr Mar 10, 2026
fb63fd7
cleanup
thdxr Mar 11, 2026
04954a9
Merge remote-tracking branch 'origin/opencode-2-0' into opencode-2-0
thdxr Mar 11, 2026
63af295
Merge origin/dev into opencode-2-0
thdxr Mar 19, 2026
bd7a4ce
sync
thdxr Mar 19, 2026
b5ebc54
Merge remote-tracking branch 'origin/dev' into opencode-2-0
thdxr Mar 19, 2026
48e867e
Merge remote-tracking branch 'origin/dev' into opencode-2-0
thdxr Mar 19, 2026
850dbb9
Merge remote-tracking branch 'origin/dev' into opencode-2-0
thdxr Mar 19, 2026
0293a8b
chore: revert changes overlapping with #18308
thdxr Mar 20, 2026
08b6d9c
sync
thdxr Mar 20, 2026
fcf1bb0
chore: update lockfile and package.json
thdxr Mar 20, 2026
2bfe81e
chore: extract SQLite abstraction into separate PR (#refactor/sqlite-…
thdxr Mar 20, 2026
9439a56
chore: revert drizzle upgrade (extracted to sqlite PR)
thdxr Mar 20, 2026
f5783c4
chore: extract portable process changes into #18318
thdxr Mar 20, 2026
d473b7e
chore: extract which/global changes into #18320
thdxr Mar 20, 2026
b9b210a
Merge branch 'dev' into opencode-2-0
thdxr Mar 20, 2026
cbc40a5
chore: extract node entry point into #18324
thdxr Mar 20, 2026
65e7862
chore: extract OAuth changes into #18327
thdxr Mar 20, 2026
3eeeec3
chore: extract misc fixes into #18328
thdxr Mar 20, 2026
b77c797
Merge branch 'dev' into refactor/hono-server
thdxr Mar 20, 2026
37ff5aa
Merge branch 'dev' into refactor/hono-server
thdxr Mar 20, 2026
a6bff14
Merge branch 'dev' into refactor/hono-server
thdxr Mar 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
642 changes: 343 additions & 299 deletions bun.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/opencode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@
"@effect/platform-node": "catalog:",
"@gitlab/gitlab-ai-provider": "3.6.0",
"@gitlab/opencode-gitlab-auth": "1.3.3",
"@hono/node-server": "1.19.11",
"@hono/node-ws": "1.3.0",
"@hono/standard-validator": "0.1.5",
"@hono/zod-validator": "catalog:",
"@modelcontextprotocol/sdk": "1.25.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/cli/cmd/acp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const AcpCommand = cmd({
process.env.OPENCODE_CLIENT = "acp"
await bootstrap(process.cwd(), async () => {
const opts = await resolveNetworkOptions(args)
const server = Server.listen(opts)
const server = await Server.listen(opts)

const sdk = createOpencodeClient({
baseUrl: `http://${server.hostname}:${server.port}`,
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/cli/cmd/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const ServeCommand = cmd({
console.log("Warning: OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
}
const opts = await resolveNetworkOptions(args)
const server = Server.listen(opts)
const server = await Server.listen(opts)
console.log(`opencode server listening on http://${server.hostname}:${server.port}`)

await new Promise(() => {})
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/cli/cmd/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const WebCommand = cmd({
UI.println(UI.Style.TEXT_WARNING_BOLD + "! " + "OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
}
const opts = await resolveNetworkOptions(args)
const server = Server.listen(opts)
const server = await Server.listen(opts)
UI.empty()
UI.println(UI.logo(" "))
UI.empty()
Expand Down
21 changes: 18 additions & 3 deletions packages/opencode/src/control-plane/workspace-server/server.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createAdaptorServer } from "@hono/node-server"
import { Hono } from "hono"
import { Instance } from "../../project/instance"
import { InstanceBootstrap } from "../../project/bootstrap"
Expand Down Expand Up @@ -56,10 +57,24 @@ export namespace WorkspaceServer {
}

export function Listen(opts: { hostname: string; port: number }) {
return Bun.serve({
hostname: opts.hostname,
port: opts.port,
const server = createAdaptorServer({
fetch: App().fetch,
})
server.listen(opts.port, opts.hostname)
return {
hostname: opts.hostname,
port: opts.port,
stop() {
return new Promise<void>((resolve, reject) => {
server.close((err) => {
if (err) {
reject(err)
return
}
resolve()
})
})
},
}
}
Comment on lines 59 to 79
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Listen returns before the server is ready and silently swallows bind errors

server.listen() is asynchronous — the server is not actually bound to the port when Listen returns. Unlike the analogous Server.listen() in server.ts (which wraps the call in a Promise and resolves on the "listening" event), this implementation returns immediately, creating a race condition for callers. Additionally, any "error" event (e.g. EADDRINUSE) is silently ignored.

The function should await the "listening" event and reject on "error", matching the pattern in the main server:

export function Listen(opts: { hostname: string; port: number }) {
  return new Promise<{ hostname: string; port: number; stop: () => Promise<void> }>((resolve, reject) => {
    const server = createAdaptorServer({ fetch: App().fetch })
    const cleanup = () => { server.off("error", fail); server.off("listening", ready) }
    const fail = (err: Error) => { cleanup(); reject(err) }
    const ready = () => {
      cleanup()
      resolve({
        hostname: opts.hostname,
        port: opts.port,
        stop() {
          return new Promise<void>((res, rej) => server.close((e) => e ? rej(e) : res()))
        },
      })
    }
    server.once("error", fail)
    server.once("listening", ready)
    server.listen(opts.port, opts.hostname)
  })
}

}
21 changes: 10 additions & 11 deletions packages/opencode/src/pty/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export namespace Pty {
close: (code?: number, reason?: string) => void
}

const key = (ws: Socket) => (ws.data && typeof ws.data === "object" ? ws.data : ws)

// WebSocket control frame: 0x00 + UTF-8 JSON.
const meta = (cursor: number) => {
const json = JSON.stringify({ cursor })
Expand Down Expand Up @@ -97,9 +99,9 @@ export namespace Pty {
try {
session.process.kill()
} catch {}
for (const [key, ws] of session.subscribers.entries()) {
for (const [id, ws] of session.subscribers.entries()) {
try {
if (ws.data === key) ws.close()
if (key(ws) === id) ws.close()
} catch {
// ignore
}
Expand Down Expand Up @@ -230,9 +232,9 @@ export namespace Pty {
try {
session.process.kill()
} catch {}
for (const [key, ws] of session.subscribers.entries()) {
for (const [id, ws] of session.subscribers.entries()) {
try {
if (ws.data === key) ws.close()
if (key(ws) === id) ws.close()
} catch {
// ignore
}
Expand Down Expand Up @@ -263,16 +265,13 @@ export namespace Pty {
}
log.info("client connected to session", { id })

// Use ws.data as the unique key for this connection lifecycle.
// If ws.data is undefined, fallback to ws object.
const connectionKey = ws.data && typeof ws.data === "object" ? ws.data : ws
const sub = key(ws)

// Optionally cleanup if the key somehow exists
session.subscribers.delete(connectionKey)
session.subscribers.set(connectionKey, ws)
session.subscribers.delete(sub)
session.subscribers.set(sub, ws)

const cleanup = () => {
session.subscribers.delete(connectionKey)
session.subscribers.delete(sub)
}

const start = session.bufferCursor
Expand Down
11 changes: 5 additions & 6 deletions packages/opencode/src/server/routes/pty.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import { upgradeWebSocket } from "hono/bun"
import type { UpgradeWebSocket } from "hono/ws"
import z from "zod"
import { Pty } from "@/pty"
import { PtyID } from "@/pty/schema"
import { NotFoundError } from "../../storage/db"
import { errors } from "../error"
import { lazy } from "../../util/lazy"

export const PtyRoutes = lazy(() =>
new Hono()
export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) {
return new Hono()
.get(
"/",
describeRoute({
Expand Down Expand Up @@ -197,5 +196,5 @@ export const PtyRoutes = lazy(() =>
},
}
}),
),
)
)
}
Loading
Loading