Compare commits
5 commits
dev
...
desktop-v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6768da519 |
||
|
|
6fb752ad69 |
||
|
|
e96d0b3426 |
||
|
|
e8326c70e6 |
||
|
|
3292e6e5b2 |
11 changed files with 305 additions and 57 deletions
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
|
|
@ -219,7 +219,6 @@ jobs:
|
||||||
|
|
||||||
build-electron:
|
build-electron:
|
||||||
needs:
|
needs:
|
||||||
- build-cli
|
|
||||||
- version
|
- version
|
||||||
if: github.repository == 'anomalyco/opencode'
|
if: github.repository == 'anomalyco/opencode'
|
||||||
continue-on-error: false
|
continue-on-error: false
|
||||||
|
|
@ -316,10 +315,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
|
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
|
||||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
||||||
OPENCODE_CLI_ARTIFACT: ${{ (runner.os == 'Windows' && 'opencode-cli-windows') || 'opencode-cli' }}
|
|
||||||
RUST_TARGET: ${{ matrix.settings.target }}
|
RUST_TARGET: ${{ matrix.settings.target }}
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: bun run build
|
run: bun run build
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,7 @@
|
||||||
"@types/node": "catalog:"
|
"@types/node": "catalog:"
|
||||||
},
|
},
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
|
"@dnd-kit/dom@0.5.0": "patches/@dnd-kit%2Fdom@0.5.0.patch",
|
||||||
"@ff-labs/fff-bun@0.9.3": "patches/@ff-labs%2Ffff-bun@0.9.3.patch",
|
"@ff-labs/fff-bun@0.9.3": "patches/@ff-labs%2Ffff-bun@0.9.3.patch",
|
||||||
"@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch",
|
"@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch",
|
||||||
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
||||||
|
|
|
||||||
|
|
@ -56,3 +56,36 @@ test("keeps a hidden prod launcher for old Linux pins", async () => {
|
||||||
expect(desktop).toContain("StartupWMClass=ai.opencode.desktop")
|
expect(desktop).toContain("StartupWMClass=ai.opencode.desktop")
|
||||||
expect(desktop).toContain("NoDisplay=true")
|
expect(desktop).toContain("NoDisplay=true")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("bundles the CLI outside the dev app archive", async () => {
|
||||||
|
const previous = process.env.OPENCODE_CHANNEL
|
||||||
|
process.env.OPENCODE_CHANNEL = "dev"
|
||||||
|
const module = await import("./electron-builder.config.ts?cli-resource")
|
||||||
|
const config = module.default as Configuration
|
||||||
|
if (previous === undefined) delete process.env.OPENCODE_CHANNEL
|
||||||
|
else process.env.OPENCODE_CHANNEL = previous
|
||||||
|
|
||||||
|
expect(config.files).toContain("!resources/opencode-cli*")
|
||||||
|
expect(config.extraResources).toContainEqual({
|
||||||
|
from: "resources/",
|
||||||
|
to: "",
|
||||||
|
filter: ["opencode-cli*"],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const channel of ["beta", "prod"] as const) {
|
||||||
|
test(`does not bundle the CLI in ${channel} builds`, async () => {
|
||||||
|
const previous = process.env.OPENCODE_CHANNEL
|
||||||
|
process.env.OPENCODE_CHANNEL = channel
|
||||||
|
const module = await import(`./electron-builder.config.ts?no-cli-resource=${channel}`)
|
||||||
|
const config = module.default as Configuration
|
||||||
|
if (previous === undefined) delete process.env.OPENCODE_CHANNEL
|
||||||
|
else process.env.OPENCODE_CHANNEL = previous
|
||||||
|
|
||||||
|
expect(config.extraResources).not.toContainEqual({
|
||||||
|
from: "resources/",
|
||||||
|
to: "",
|
||||||
|
filter: ["opencode-cli*"],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,17 @@ const getBase = (appId: string): Configuration => ({
|
||||||
extraMetadata: {
|
extraMetadata: {
|
||||||
desktopName: `${appId}.desktop`,
|
desktopName: `${appId}.desktop`,
|
||||||
},
|
},
|
||||||
files: ["out/**/*", "resources/**/*"],
|
files: ["out/**/*", "resources/**/*", "!resources/opencode-cli*"],
|
||||||
extraResources: [
|
extraResources: [
|
||||||
|
...(channel === "dev"
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
from: "resources/",
|
||||||
|
to: "",
|
||||||
|
filter: ["opencode-cli*"],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
from: "native/",
|
from: "native/",
|
||||||
to: "native/",
|
to: "native/",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
|
|
||||||
import { resolveChannel } from "./utils"
|
import { downloadCliToResources, resolveChannel } from "./utils"
|
||||||
|
|
||||||
const channel = resolveChannel()
|
const channel = resolveChannel()
|
||||||
await $`bun ./scripts/copy-icons.ts ${channel}`
|
await $`bun ./scripts/copy-icons.ts ${channel}`
|
||||||
await $`bun ./scripts/copy-metainfo.ts ${channel}`
|
await $`bun ./scripts/copy-metainfo.ts ${channel}`
|
||||||
|
|
||||||
await $`cd ../opencode && bun script/build-node.ts`
|
await $`cd ../opencode && bun script/build-node.ts`
|
||||||
|
if (channel === "dev") await downloadCliToResources()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
|
import { downloadCliToResources } from "./utils"
|
||||||
|
|
||||||
await $`bun run install-electron`
|
await $`bun run install-electron`
|
||||||
|
|
||||||
await $`bun ./scripts/copy-icons.ts ${process.env.OPENCODE_CHANNEL ?? "dev"}`
|
await $`bun ./scripts/copy-icons.ts ${process.env.OPENCODE_CHANNEL ?? "dev"}`
|
||||||
|
|
||||||
await $`cd ../opencode && bun script/build-node.ts`
|
await $`cd ../opencode && bun script/build-node.ts`
|
||||||
|
await downloadCliToResources()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
|
import { chmod, copyFile, mkdtemp, rm } from "node:fs/promises"
|
||||||
|
import { tmpdir } from "node:os"
|
||||||
|
import { join } from "node:path"
|
||||||
|
|
||||||
|
const CLI_VERSION = "0.0.0-next-16350"
|
||||||
|
|
||||||
export type Channel = "dev" | "beta" | "prod"
|
export type Channel = "dev" | "beta" | "prod"
|
||||||
|
|
||||||
|
|
@ -8,36 +13,42 @@ export function resolveChannel(): Channel {
|
||||||
return "dev"
|
return "dev"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SIDECAR_BINARIES: Array<{ rustTarget: string; ocBinary: string; assetExt: string }> = [
|
export const CLI_BINARIES: Array<{ rustTarget: string; package: string; os: string; cpu: string }> = [
|
||||||
{
|
{
|
||||||
rustTarget: "aarch64-apple-darwin",
|
rustTarget: "aarch64-apple-darwin",
|
||||||
ocBinary: "opencode-darwin-arm64",
|
package: "@opencode-ai/cli-darwin-arm64",
|
||||||
assetExt: "zip",
|
os: "darwin",
|
||||||
|
cpu: "arm64",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rustTarget: "x86_64-apple-darwin",
|
rustTarget: "x86_64-apple-darwin",
|
||||||
ocBinary: "opencode-darwin-x64-baseline",
|
package: "@opencode-ai/cli-darwin-x64-baseline",
|
||||||
assetExt: "zip",
|
os: "darwin",
|
||||||
|
cpu: "x64",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rustTarget: "aarch64-pc-windows-msvc",
|
rustTarget: "aarch64-pc-windows-msvc",
|
||||||
ocBinary: "opencode-windows-arm64",
|
package: "@opencode-ai/cli-windows-arm64",
|
||||||
assetExt: "zip",
|
os: "win32",
|
||||||
|
cpu: "arm64",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rustTarget: "x86_64-pc-windows-msvc",
|
rustTarget: "x86_64-pc-windows-msvc",
|
||||||
ocBinary: "opencode-windows-x64-baseline",
|
package: "@opencode-ai/cli-windows-x64-baseline",
|
||||||
assetExt: "zip",
|
os: "win32",
|
||||||
|
cpu: "x64",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rustTarget: "x86_64-unknown-linux-gnu",
|
rustTarget: "x86_64-unknown-linux-gnu",
|
||||||
ocBinary: "opencode-linux-x64-baseline",
|
package: "@opencode-ai/cli-linux-x64-baseline",
|
||||||
assetExt: "tar.gz",
|
os: "linux",
|
||||||
|
cpu: "x64",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rustTarget: "aarch64-unknown-linux-gnu",
|
rustTarget: "aarch64-unknown-linux-gnu",
|
||||||
ocBinary: "opencode-linux-arm64",
|
package: "@opencode-ai/cli-linux-arm64",
|
||||||
assetExt: "tar.gz",
|
os: "linux",
|
||||||
|
cpu: "arm64",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -51,24 +62,33 @@ function nativeTarget() {
|
||||||
throw new Error(`Unsupported platform: ${platform}/${arch}`)
|
throw new Error(`Unsupported platform: ${platform}/${arch}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCurrentSidecar(target = RUST_TARGET ?? nativeTarget()) {
|
export function getCurrentCli(target = RUST_TARGET ?? nativeTarget()) {
|
||||||
const binaryConfig = SIDECAR_BINARIES.find((b) => b.rustTarget === target)
|
const binaryConfig = CLI_BINARIES.find((item) => item.rustTarget === target)
|
||||||
if (!binaryConfig) throw new Error(`Sidecar configuration not available for Rust target '${target}'`)
|
if (!binaryConfig) throw new Error(`CLI configuration not available for target '${target}'`)
|
||||||
|
|
||||||
return binaryConfig
|
return binaryConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function copyBinaryToSidecarFolder(source: string) {
|
export async function downloadCliToResources() {
|
||||||
const dir = `resources`
|
const cli = getCurrentCli()
|
||||||
await $`mkdir -p ${dir}`
|
const directory = await mkdtemp(join(tmpdir(), "opencode-cli-"))
|
||||||
const dest = windowsify(`${dir}/opencode-cli`)
|
const dest = windowsify("resources/opencode-cli")
|
||||||
await $`cp ${source} ${dest}`
|
try {
|
||||||
|
await $`bun install --no-save --cwd ${directory} ${`${cli.package}@${CLI_VERSION}`} ${`--os=${cli.os}`} ${`--cpu=${cli.cpu}`}`
|
||||||
|
await copyFile(
|
||||||
|
join(directory, "node_modules", cli.package, "bin", cli.os === "win32" ? "opencode2.exe" : "opencode2"),
|
||||||
|
dest,
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
await rm(directory, { recursive: true, force: true })
|
||||||
|
}
|
||||||
|
if (process.platform !== "win32") await chmod(dest, 0o755)
|
||||||
if (process.platform === "win32" && process.env.GITHUB_ACTIONS === "true") {
|
if (process.platform === "win32" && process.env.GITHUB_ACTIONS === "true") {
|
||||||
await $`pwsh -NoLogo -NoProfile -ExecutionPolicy Bypass -File ../../script/sign-windows.ps1 ${dest}`
|
await $`pwsh -NoLogo -NoProfile -ExecutionPolicy Bypass -File ../../script/sign-windows.ps1 ${dest}`
|
||||||
}
|
}
|
||||||
if (process.platform === "darwin") await $`codesign --force --sign - ${dest}`
|
if (process.platform === "darwin") await $`codesign --force --sign - ${dest}`
|
||||||
|
|
||||||
console.log(`Copied ${source} to ${dest}`)
|
console.log(`Copied ${cli.package} to ${dest}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function windowsify(path: string) {
|
export function windowsify(path: string) {
|
||||||
|
|
|
||||||
125
packages/desktop/src/main/background-cli.ts
Normal file
125
packages/desktop/src/main/background-cli.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
import { execFile } from "node:child_process"
|
||||||
|
import { existsSync } from "node:fs"
|
||||||
|
import { chmod, copyFile, mkdir, rename, rm } from "node:fs/promises"
|
||||||
|
import { dirname, join } from "node:path"
|
||||||
|
import { fileURLToPath } from "node:url"
|
||||||
|
import { promisify } from "node:util"
|
||||||
|
import { app } from "electron"
|
||||||
|
|
||||||
|
const execFileAsync = promisify(execFile)
|
||||||
|
const root = dirname(fileURLToPath(import.meta.url))
|
||||||
|
const stateHome = process.env.XDG_STATE_HOME
|
||||||
|
const desktopStateNames = ["ai.opencode.desktop.dev", "ai.opencode.desktop.beta", "ai.opencode.desktop"]
|
||||||
|
|
||||||
|
type Logger = {
|
||||||
|
log(message: string, meta?: Record<string, unknown>): void
|
||||||
|
error(message: string, meta?: Record<string, unknown>): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startBackgroundCli(logger: Logger, shellStateHome?: string) {
|
||||||
|
const bundled = app.isPackaged
|
||||||
|
? join(process.resourcesPath, executableName())
|
||||||
|
: join(root, "../../resources", executableName())
|
||||||
|
logger.log("v2 CLI executable resolved", { bundled, packaged: app.isPackaged })
|
||||||
|
const version = await run(bundled, ["--version"], logger)
|
||||||
|
const binary = app.isPackaged ? await installCli(bundled, version, logger) : bundled
|
||||||
|
|
||||||
|
const candidates = [
|
||||||
|
...new Set([stateHome, shellStateHome, ...desktopStateNames.map((name) => join(app.getPath("appData"), name))]),
|
||||||
|
].filter((candidate) => candidate === undefined || existsSync(candidate))
|
||||||
|
const discovered = await Promise.all(
|
||||||
|
candidates.map(async (candidate) => ({
|
||||||
|
stateHome: candidate,
|
||||||
|
url: serviceUrl(await run(binary, ["service", "status"], logger, { stateHome: candidate })),
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
const found = discovered.find((candidate) => candidate.url !== undefined)
|
||||||
|
logger.log("v2 CLI background instance checked", {
|
||||||
|
detected: Boolean(found),
|
||||||
|
...endpoint(found?.url),
|
||||||
|
})
|
||||||
|
|
||||||
|
const daemonStateHome = found?.stateHome ?? stateHome
|
||||||
|
const url = await run(binary, ["service", "start"], logger, { stateHome: daemonStateHome })
|
||||||
|
const password = await run(binary, ["service", "get", "password"], logger, {
|
||||||
|
redact: true,
|
||||||
|
stateHome: daemonStateHome,
|
||||||
|
})
|
||||||
|
logger.log("v2 CLI background service ready", {
|
||||||
|
existing: Boolean(found),
|
||||||
|
username: "opencode",
|
||||||
|
...endpoint(url),
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
url,
|
||||||
|
username: "opencode",
|
||||||
|
password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function installCli(source: string, version: string, logger: Logger) {
|
||||||
|
const directory = join(app.getPath("userData"), "cli", version.replace(/[^a-zA-Z0-9._-]/g, "-"))
|
||||||
|
const destination = join(directory, executableName())
|
||||||
|
if (existsSync(destination)) {
|
||||||
|
logger.log("v2 CLI staged executable reused", { path: destination, version })
|
||||||
|
return destination
|
||||||
|
}
|
||||||
|
|
||||||
|
const temp = destination + `.${process.pid}.tmp`
|
||||||
|
await mkdir(directory, { recursive: true })
|
||||||
|
await copyFile(source, temp)
|
||||||
|
if (process.platform !== "win32") await chmod(temp, 0o755)
|
||||||
|
await rename(temp, destination).catch(async (error) => {
|
||||||
|
await rm(temp, { force: true })
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
logger.log("v2 CLI executable staged", { source, path: destination, version })
|
||||||
|
return destination
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run(
|
||||||
|
binary: string,
|
||||||
|
args: string[],
|
||||||
|
logger: Logger,
|
||||||
|
options: { redact?: boolean; stateHome?: string } = {},
|
||||||
|
) {
|
||||||
|
logger.log("v2 CLI command started", { binary, args })
|
||||||
|
const env = { ...process.env }
|
||||||
|
if (options.stateHome === undefined) delete env.XDG_STATE_HOME
|
||||||
|
else env.XDG_STATE_HOME = options.stateHome
|
||||||
|
return execFileAsync(binary, args, { env, windowsHide: true }).then(
|
||||||
|
(result) => {
|
||||||
|
const stdout = result.stdout.trim()
|
||||||
|
const stderr = result.stderr.trim()
|
||||||
|
logger.log("v2 CLI command completed", { args, stdout: options.redact ? "[redacted]" : stdout, stderr })
|
||||||
|
return stdout
|
||||||
|
},
|
||||||
|
(error: unknown) => {
|
||||||
|
const output = error as { stdout?: string; stderr?: string }
|
||||||
|
logger.error("v2 CLI command failed", {
|
||||||
|
args,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
stdout: options.redact && output.stdout ? "[redacted]" : (output.stdout?.trim() ?? ""),
|
||||||
|
stderr: output.stderr?.trim() ?? "",
|
||||||
|
})
|
||||||
|
throw error
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function serviceUrl(status: string) {
|
||||||
|
if (URL.canParse(status)) return status
|
||||||
|
if (!status.startsWith("running ")) return
|
||||||
|
const url = status.slice("running ".length).trim()
|
||||||
|
return URL.canParse(url) ? url : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function endpoint(url: string | undefined) {
|
||||||
|
if (!url || !URL.canParse(url)) return {}
|
||||||
|
const parsed = new URL(url)
|
||||||
|
return { url, hostname: parsed.hostname, port: parsed.port }
|
||||||
|
}
|
||||||
|
|
||||||
|
function executableName() {
|
||||||
|
return process.platform === "win32" ? "opencode-cli.exe" : "opencode-cli"
|
||||||
|
}
|
||||||
|
|
@ -48,6 +48,7 @@ import { registerWslIpcHandlers } from "./wsl/ipc"
|
||||||
import { spawnWslSidecar } from "./wsl/sidecar"
|
import { spawnWslSidecar } from "./wsl/sidecar"
|
||||||
import { migrate } from "./migrate"
|
import { migrate } from "./migrate"
|
||||||
import { cleanupStoreFiles } from "./store-cleanup"
|
import { cleanupStoreFiles } from "./store-cleanup"
|
||||||
|
import { startBackgroundCli } from "./background-cli"
|
||||||
|
|
||||||
const APP_NAMES: Record<string, string> = {
|
const APP_NAMES: Record<string, string> = {
|
||||||
dev: "OpenCode Dev",
|
dev: "OpenCode Dev",
|
||||||
|
|
@ -60,6 +61,7 @@ const APP_IDS: Record<string, string> = {
|
||||||
prod: "ai.opencode.desktop",
|
prod: "ai.opencode.desktop",
|
||||||
}
|
}
|
||||||
const TEST_ONBOARDING = process.env.OPENCODE_TEST_ONBOARDING === "1"
|
const TEST_ONBOARDING = process.env.OPENCODE_TEST_ONBOARDING === "1"
|
||||||
|
const SIDECAR_VERSION = process.env.OPENCODE_SIDECAR_V2 === "1" ? "v2" : "v1"
|
||||||
const jsCallStackFeature = "DocumentPolicyIncludeJSCallStacksInCrashReports"
|
const jsCallStackFeature = "DocumentPolicyIncludeJSCallStacksInCrashReports"
|
||||||
|
|
||||||
let logger: ReturnType<typeof initLogging>
|
let logger: ReturnType<typeof initLogging>
|
||||||
|
|
@ -198,7 +200,7 @@ const main = Effect.gen(function* () {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
preferAppEnv(app.getPath("userData"))
|
const shellEnv = preferAppEnv(app.getPath("userData"))
|
||||||
|
|
||||||
app.on("second-instance", (_event: Event, argv: string[]) => {
|
app.on("second-instance", (_event: Event, argv: string[]) => {
|
||||||
const urls = argv.filter((arg: string) => arg.startsWith("opencode://"))
|
const urls = argv.filter((arg: string) => arg.startsWith("opencode://"))
|
||||||
|
|
@ -312,6 +314,29 @@ const main = Effect.gen(function* () {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const loadingTask = yield* Effect.gen(function* () {
|
||||||
|
logger.log("sidecar connection started", { version: SIDECAR_VERSION })
|
||||||
|
|
||||||
|
ensureLoopbackNoProxy()
|
||||||
|
useEnvProxy()
|
||||||
|
|
||||||
|
if (SIDECAR_VERSION === "v2") {
|
||||||
|
logger.log("spawning v2 sidecar")
|
||||||
|
const sidecar = yield* Effect.promise(() => startBackgroundCli(logger, shellEnv?.XDG_STATE_HOME))
|
||||||
|
yield* Deferred.succeed(serverReady, {
|
||||||
|
url: sidecar.url,
|
||||||
|
username: sidecar.username,
|
||||||
|
password: sidecar.password,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
void wslServers.initialize().catch((error) => logger.error("wsl server initialization failed", error))
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log("loading task finished")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const port = yield* Effect.gen(function* () {
|
const port = yield* Effect.gen(function* () {
|
||||||
const fromEnv = process.env.OPENCODE_PORT
|
const fromEnv = process.env.OPENCODE_PORT
|
||||||
if (fromEnv) {
|
if (fromEnv) {
|
||||||
|
|
@ -320,17 +345,17 @@ const main = Effect.gen(function* () {
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = yield* Deferred.make<number, unknown>()
|
const res = yield* Deferred.make<number, unknown>()
|
||||||
const server = createServer()
|
const socket = createServer()
|
||||||
server.on("error", (e) => Deferred.failSync(res, () => e))
|
socket.on("error", (e) => Deferred.failSync(res, () => e))
|
||||||
server.listen(0, "127.0.0.1", () => {
|
socket.listen(0, "127.0.0.1", () => {
|
||||||
const address = server.address()
|
const address = socket.address()
|
||||||
if (typeof address !== "object" || !address) {
|
if (typeof address !== "object" || !address) {
|
||||||
server.close()
|
socket.close()
|
||||||
Deferred.failSync(res, () => new Error("Failed to get port"))
|
Deferred.failSync(res, () => new Error("Failed to get port"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const port = address.port
|
const port = address.port
|
||||||
server.close(() => Effect.runSync(Deferred.succeed(res, port)))
|
socket.close(() => Effect.runSync(Deferred.succeed(res, port)))
|
||||||
})
|
})
|
||||||
|
|
||||||
return yield* Deferred.await(res)
|
return yield* Deferred.await(res)
|
||||||
|
|
@ -339,12 +364,6 @@ const main = Effect.gen(function* () {
|
||||||
const url = `http://${hostname}:${port}`
|
const url = `http://${hostname}:${port}`
|
||||||
const password = randomUUID()
|
const password = randomUUID()
|
||||||
|
|
||||||
const loadingTask = yield* Effect.gen(function* () {
|
|
||||||
logger.log("sidecar connection started", { url })
|
|
||||||
|
|
||||||
ensureLoopbackNoProxy()
|
|
||||||
useEnvProxy()
|
|
||||||
|
|
||||||
logger.log("spawning sidecar", { url })
|
logger.log("spawning sidecar", { url })
|
||||||
const { listener, health } = yield* Effect.promise(() =>
|
const { listener, health } = yield* Effect.promise(() =>
|
||||||
spawnLocalServer(hostname, port, password, {
|
spawnLocalServer(hostname, port, password, {
|
||||||
|
|
|
||||||
|
|
@ -43,13 +43,15 @@ export function setDefaultServerUrl(url: string | null) {
|
||||||
|
|
||||||
export function preferAppEnv(userDataPath: string) {
|
export function preferAppEnv(userDataPath: string) {
|
||||||
const shell = process.platform === "win32" ? null : getUserShell()
|
const shell = process.platform === "win32" ? null : getUserShell()
|
||||||
|
const shellEnv = shell ? loadShellEnv(shell, getLogger()) : null
|
||||||
Object.assign(process.env, {
|
Object.assign(process.env, {
|
||||||
...(shell ? loadShellEnv(shell, getLogger()) : null),
|
...shellEnv,
|
||||||
OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "true",
|
OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "true",
|
||||||
OPENCODE_EXPERIMENTAL_FILEWATCHER: "true",
|
OPENCODE_EXPERIMENTAL_FILEWATCHER: "true",
|
||||||
OPENCODE_CLIENT: "desktop",
|
OPENCODE_CLIENT: "desktop",
|
||||||
XDG_STATE_HOME: process.env.XDG_STATE_HOME ?? userDataPath,
|
XDG_STATE_HOME: process.env.XDG_STATE_HOME ?? userDataPath,
|
||||||
})
|
})
|
||||||
|
return shellEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function spawnLocalServer(
|
export async function spawnLocalServer(
|
||||||
|
|
|
||||||
40
patches/@dnd-kit%2Fdom@0.5.0.patch
Normal file
40
patches/@dnd-kit%2Fdom@0.5.0.patch
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
--- a/index.js
|
||||||
|
+++ b/index.js
|
||||||
|
@@ -2195,6 +2195,17 @@
|
||||||
|
sensors,
|
||||||
|
modifiers
|
||||||
|
}));
|
||||||
|
+ }
|
||||||
|
+ get plugins() {
|
||||||
|
+ return super.plugins;
|
||||||
|
+ }
|
||||||
|
+ set plugins(plugins) {
|
||||||
|
+ super.plugins = [
|
||||||
|
+ ScrollListener,
|
||||||
|
+ Scroller,
|
||||||
|
+ StyleInjector,
|
||||||
|
+ ...plugins
|
||||||
|
+ ];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var _element_dec, _handle_dec, _c, _init5, _handle, _element;
|
||||||
|
--- a/index.cjs
|
||||||
|
+++ b/index.cjs
|
||||||
|
@@ -2196,6 +2196,17 @@
|
||||||
|
sensors,
|
||||||
|
modifiers
|
||||||
|
}));
|
||||||
|
+ }
|
||||||
|
+ get plugins() {
|
||||||
|
+ return super.plugins;
|
||||||
|
+ }
|
||||||
|
+ set plugins(plugins) {
|
||||||
|
+ super.plugins = [
|
||||||
|
+ ScrollListener,
|
||||||
|
+ Scroller,
|
||||||
|
+ StyleInjector,
|
||||||
|
+ ...plugins
|
||||||
|
+ ];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var _element_dec, _handle_dec, _c, _init5, _handle, _element;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue