fix(orchestrator): fall back to release binaries during npm install (#1080)

Co-authored-by: Omar McAdam <omar@OpenWork-Studio.localdomain>
This commit is contained in:
Omar McAdam
2026-03-20 13:22:45 -07:00
committed by GitHub
parent 49cd28ca41
commit 3a658a20cf
5 changed files with 103 additions and 19 deletions

View File

@@ -678,10 +678,12 @@ jobs:
echo "exists=false" >> "$GITHUB_OUTPUT"
fi
- name: Build sidecar artifacts
- name: Build orchestrator release artifacts
env:
SOURCE_DATE_EPOCH: ${{ steps.source-date.outputs.epoch }}
run: pnpm --filter openwork-orchestrator build:sidecars
run: |
pnpm --filter openwork-orchestrator build:bin:all
pnpm --filter openwork-orchestrator build:sidecars
- name: Release review (strict)
env:
@@ -702,12 +704,12 @@ jobs:
--notes "$notes" \
--latest=false
- name: Upload sidecar assets
- name: Upload orchestrator release assets
env:
GH_TOKEN: ${{ github.token }}
run: |
tag="openwork-orchestrator-v${{ steps.sidecar-versions.outputs.orchestrator }}"
gh release upload "$tag" apps/orchestrator/dist/sidecars/* --repo "$GITHUB_REPOSITORY" --clobber
gh release upload "$tag" apps/orchestrator/dist/bin/* apps/orchestrator/dist/sidecars/* --repo "$GITHUB_REPOSITORY" --clobber
publish-npm:
name: Publish npm packages

View File

@@ -20,6 +20,10 @@ openwork serve --workspace /path/to/workspace
`openwork` ships as a compiled binary, so Bun is not required at runtime.
If npm skips the optional platform package, `postinstall` falls back to downloading the matching
binary from the `openwork-orchestrator-v<version>` GitHub release. Override the download host with
`OPENWORK_ORCHESTRATOR_DOWNLOAD_BASE_URL` when you need to use a mirror.
`openwork` downloads and caches the `openwork-server`, `opencode-router`, and `opencode` sidecars on
first run using a SHA-256 manifest. Use `--sidecar-dir` or `OPENWORK_SIDECAR_DIR` to control the
cache location, and `--sidecar-base-url` / `--sidecar-manifest` to point at a custom host.

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env node
const childProcess = require("child_process")
const fs = require("fs")
const os = require("os")
const path = require("path")
@@ -45,14 +46,18 @@ if (!arch) {
const pkg = `openwork-orchestrator-${platform}-${arch}`
const binary = platform === "windows" ? "openwork.exe" : "openwork"
const fallback = path.join(__dirname, "..", "dist", "bin", binary)
let pkgJsonPath
try {
pkgJsonPath = require.resolve(`${pkg}/package.json`)
} catch {
if (fs.existsSync(fallback)) {
run(fallback)
}
console.error(
`openwork: no prebuilt binary package found for ${platform}/${arch}.\n` +
`Try installing the platform package manually: ${pkg}`,
`Try reinstalling openwork-orchestrator or installing the platform package manually: ${pkg}`,
)
process.exit(1)
}

View File

@@ -1,9 +1,13 @@
#!/usr/bin/env node
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
import { dirname, join } from "node:path"
import os from "os"
import { createRequire } from "module"
import { fileURLToPath } from "node:url"
const require = createRequire(import.meta.url)
const rootDir = dirname(fileURLToPath(import.meta.url))
function detect() {
const platformMap = {
@@ -22,21 +26,90 @@ function detect() {
return { platform, arch }
}
function name() {
function packageName() {
const { platform, arch } = detect()
return `openwork-orchestrator-${platform}-${arch}`
}
try {
const pkg = name()
require.resolve(`${pkg}/package.json`)
console.log(`openwork-orchestrator: verified platform package: ${pkg}`)
} catch (error) {
const pkg = name()
console.error(
`openwork-orchestrator: failed to locate platform binary package (${pkg}).\n` +
`Your package manager may have skipped optionalDependencies.\n` +
`Try installing it manually: npm i -g ${pkg}`,
)
process.exit(1)
function binaryName() {
const { platform } = detect()
return platform === "windows" ? "openwork.exe" : "openwork"
}
function fallbackAssetName() {
const { platform, arch } = detect()
return `openwork-bun-${platform}-${arch}${platform === "windows" ? ".exe" : ""}`
}
function fallbackBinaryPath() {
return join(rootDir, "dist", "bin", binaryName())
}
function readOwnVersion() {
const pkg = JSON.parse(readFileSync(join(rootDir, "package.json"), "utf8"))
const version = String(pkg.version || "").trim()
if (!version) {
throw new Error("openwork-orchestrator: package version is missing")
}
return version
}
function resolveFallbackBaseUrl(version) {
const override = String(process.env.OPENWORK_ORCHESTRATOR_DOWNLOAD_BASE_URL || "").trim()
if (override) {
return override.replace(/\/$/, "")
}
return `https://github.com/different-ai/openwork/releases/download/openwork-orchestrator-v${version}`
}
async function downloadFallbackBinary() {
const version = readOwnVersion()
const asset = fallbackAssetName()
const url = `${resolveFallbackBaseUrl(version)}/${asset}`
const destination = fallbackBinaryPath()
console.log(`openwork-orchestrator: downloading fallback binary ${asset}`)
const response = await fetch(url)
if (!response.ok) {
throw new Error(`download failed (${response.status} ${response.statusText}) from ${url}`)
}
const buffer = Buffer.from(await response.arrayBuffer())
mkdirSync(dirname(destination), { recursive: true })
writeFileSync(destination, buffer)
if (binaryName() !== "openwork.exe") {
chmodSync(destination, 0o755)
}
console.log(`openwork-orchestrator: installed fallback binary at ${destination}`)
}
async function main() {
try {
const pkg = packageName()
require.resolve(`${pkg}/package.json`)
console.log(`openwork-orchestrator: verified platform package: ${pkg}`)
return
} catch {
if (existsSync(fallbackBinaryPath())) {
console.log(`openwork-orchestrator: using existing fallback binary at ${fallbackBinaryPath()}`)
return
}
}
try {
await downloadFallbackBinary()
} catch (error) {
const pkg = packageName()
const message = error instanceof Error ? error.message : String(error)
console.error(
`openwork-orchestrator: failed to locate platform binary package (${pkg}).\n` +
`Your package manager may have skipped optionalDependencies, or the package asset is unavailable.\n` +
`Fallback download failed: ${message}\n` +
`Try installing it manually: npm i -g ${pkg}`,
)
process.exit(1)
}
}
await main()

View File

@@ -117,7 +117,7 @@ writeJson(join(meta, "package.json"), {
"openwork-orchestrator": "./bin/openwork",
},
scripts: {
postinstall: "bun ./postinstall.mjs || node ./postinstall.mjs",
postinstall: "node ./postinstall.mjs",
},
optionalDependencies,
files: ["bin", "postinstall.mjs", "constants.json"],