Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "Opencode Dev Environment",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"customizations": {
"vscode": {
"extensions": [
"Oven.bun-vscode"
]
}
},
"postCreateCommand": "curl -fsSL https://bun.sh/install | bash",
"remoteUser": "vscode"
}
1 change: 1 addition & 0 deletions packages/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"scripts": {
"typecheck": "tsgo -b",
"test": "bun test src/main/sidecar-command.test.ts",
"predev": "bun ./scripts/predev.ts",
"dev": "electron-vite dev",
"prebuild": "bun ./scripts/prebuild.ts",
Expand Down
51 changes: 51 additions & 0 deletions packages/desktop/src/main/sidecar-command.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { describe, expect, test } from "bun:test"
import { parseCommand } from "./sidecar-command"


describe("parseCommand", () => {
const valid = {
type: "start",
hostname: "127.0.0.1",
port: 4096,
password: "secret",
userDataPath: "/tmp/data",
}

test("parses a valid start command", () => {
expect(parseCommand(valid)).toEqual(valid)
})

test("parses a stop command", () => {
expect(parseCommand({ type: "stop" })).toEqual({ type: "stop" })
})

test("ignores extra fields on a start command", () => {
expect(parseCommand({ ...valid, extra: "nope" })).toEqual(valid)
})

test("rejects non-object values", () => {
expect(parseCommand(undefined)).toBeUndefined()
expect(parseCommand(null)).toBeUndefined()
expect(parseCommand("start")).toBeUndefined()
expect(parseCommand(42)).toBeUndefined()
})

test("rejects unknown command types", () => {
expect(parseCommand({ type: "restart" })).toBeUndefined()
expect(parseCommand({})).toBeUndefined()
})

test("rejects a start command with a missing field", () => {
for (const key of ["hostname", "port", "password", "userDataPath"]) {
const { [key]: _, ...partial } = valid as Record<string, unknown>
expect(parseCommand(partial)).toBeUndefined()
}
})

test("rejects a start command with wrong field types", () => {
expect(parseCommand({ ...valid, port: "4096" })).toBeUndefined()
expect(parseCommand({ ...valid, hostname: 123 })).toBeUndefined()
expect(parseCommand({ ...valid, password: null })).toBeUndefined()
expect(parseCommand({ ...valid, userDataPath: [] })).toBeUndefined()
})
})
25 changes: 25 additions & 0 deletions packages/desktop/src/main/sidecar-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export type StartCommand = {
type: "start"
hostname: string
port: number
password: string
userDataPath: string
}
export type StopCommand = { type: "stop" }
export type SidecarCommand = StartCommand | StopCommand

export function parseCommand(value: unknown): SidecarCommand | undefined {
if (!value || typeof value !== "object") return
const command = value as Partial<StartCommand | StopCommand>
if (command.type === "stop") return { type: "stop" }
if (command.type !== "start") return
const { hostname, port, password, userDataPath } = command
if (
typeof hostname !== "string" ||
typeof port !== "number" ||
typeof password !== "string" ||
typeof userDataPath !== "string"
)
return
return { type: "start", hostname, port, password, userDataPath }
}
30 changes: 1 addition & 29 deletions packages/desktop/src/main/sidecar.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as http from "node:http"
import * as tls from "node:tls"
import { parseCommand, type StartCommand } from "./sidecar-command"

type NodeHttpWithEnvProxy = typeof http & {
setGlobalProxyFromEnv: () => void
Expand All @@ -9,18 +10,6 @@ type NodeTlsWithSystemCertificates = typeof tls & {
getCACertificates: (type: "default" | "system") => string[]
setDefaultCACertificates: (certificates: string[]) => void
}

type StartCommand = {
type: "start"
hostname: string
port: number
password: string
userDataPath: string
}

type StopCommand = { type: "stop" }
type SidecarCommand = StartCommand | StopCommand

type SidecarMessage =
| { type: "ready" }
| { type: "stopped" }
Expand Down Expand Up @@ -127,23 +116,6 @@ function useEnvProxy() {
}
}

function parseCommand(value: unknown): SidecarCommand | undefined {
if (!value || typeof value !== "object") return
const command = value as Partial<StartCommand | StopCommand>
if (command.type === "stop") return { type: "stop" }
if (command.type !== "start") return
if (typeof command.hostname !== "string") return
if (typeof command.port !== "number") return
if (typeof command.password !== "string") return
if (typeof command.userDataPath !== "string") return
return {
type: "start",
hostname: command.hostname,
port: command.port,
password: command.password,
userDataPath: command.userDataPath,
}
}

function serializeError(error: unknown) {
if (error instanceof Error) return { message: error.message, stack: error.stack }
Expand Down
6 changes: 5 additions & 1 deletion turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
"@opencode-ai/session-ui#test": {
"dependsOn": ["^build"],
"outputs": []
}
},
"@opencode-ai/desktop#test": {
"dependsOn": ["^build"],
"outputs": []
}
}
}
Loading