feat: source the opencode engine from a self-maintained fork#2166
feat: source the opencode engine from a self-maintained fork#2166benjaminshafii wants to merge 1 commit into
Conversation
Centralize the engine origin in constants.json (opencodeRepo) next to the existing opencodeVersion pin, and point both at different-ai/opencode v1.16.2-openwork.0 (identical rebuild of anomalyco/opencode v1.16.2). - prepare-sidecar.mjs: default repo from constants.json (env overrides kept) - orchestrator: inject __OPENWORK_PINNED_OPENCODE_REPO__ at build time and use it for runtime engine downloads (was hardcoded) - den-worker install-opencode.mjs + both Dockerfiles: repo from constants.json via OPENCODE_GITHUB_REPO build arg - ci-tests/nightly-evals: fallback repo from constants.json - opencode-agents: install from pinned release assets instead of opencode.ai/install (which cannot resolve fork versions) - runtime.mjs: fork-aware engine install hint - new engine-bump.yml: opens a CI-gated bump PR when the fork publishes a new vX.Y.Z-openwork.N release (fork tracks upstream cadence via its own upstream-sync workflow)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
5 issues found across 14 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/electron/runtime.mjs">
<violation number="1" location="apps/desktop/electron/runtime.mjs:941">
P2: Engine download flow skips checksum/signature verification before extracting a binary to the executable path.</violation>
<violation number="2" location="apps/desktop/electron/runtime.mjs:943">
P2: Unsupported-architecture fallback returns a non-command string that `bash -lc` executes and fails immediately.</violation>
</file>
<file name="ee/apps/den-worker-runtime/scripts/install-opencode.mjs">
<violation number="1" location="ee/apps/den-worker-runtime/scripts/install-opencode.mjs:157">
P2: Bundle cache invalidation ignores `opencodeRepo`, so repo changes with the same version can leave a stale binary from the wrong source.</violation>
</file>
<file name=".github/workflows/engine-bump.yml">
<violation number="1" location=".github/workflows/engine-bump.yml:59">
P1: grep check always matches when no PR exists, preventing bump PRs from ever being created.
When `gh pr list` returns no PRs, it outputs `[]`. The jq filter `.[0].number` on an empty array yields `null`. The pipe passes the literal string "null" to `grep -q .`, which matches ("null" contains characters), so the condition is truthy and the script exits early with "Bump PR for $latest already open" — even when no PR exists.</violation>
</file>
<file name="apps/orchestrator/src/cli.ts">
<violation number="1" location="apps/orchestrator/src/cli.ts:144">
P2: Fallback default in pinnedOpencodeRepo() points to upstream repo ("anomalyco/opencode") instead of the fork ("different-ai/opencode") from constants.json. If both env vars and the compile-time define are absent, the download URL silently targets the wrong repo, which lacks the fork's release tags.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| set -euo pipefail | ||
| latest="${{ steps.check.outputs.latest }}" | ||
| branch="engine-bump/${latest}" | ||
| if gh pr list --head "$branch" --state open --json number -q '.[0].number' | grep -q .; then |
There was a problem hiding this comment.
P1: grep check always matches when no PR exists, preventing bump PRs from ever being created.
When gh pr list returns no PRs, it outputs []. The jq filter .[0].number on an empty array yields null. The pipe passes the literal string "null" to grep -q ., which matches ("null" contains characters), so the condition is truthy and the script exits early with "Bump PR for $latest already open" — even when no PR exists.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/engine-bump.yml, line 59:
<comment>grep check always matches when no PR exists, preventing bump PRs from ever being created.
When `gh pr list` returns no PRs, it outputs `[]`. The jq filter `.[0].number` on an empty array yields `null`. The pipe passes the literal string "null" to `grep -q .`, which matches ("null" contains characters), so the condition is truthy and the script exits early with "Bump PR for $latest already open" — even when no PR exists.</comment>
<file context>
@@ -0,0 +1,78 @@
+ set -euo pipefail
+ latest="${{ steps.check.outputs.latest }}"
+ branch="engine-bump/${latest}"
+ if gh pr list --head "$branch" --state open --json number -q '.[0].number' | grep -q .; then
+ echo "Bump PR for $latest already open"
+ exit 0
</file context>
| if gh pr list --head "$branch" --state open --json number -q '.[0].number' | grep -q .; then | |
| if gh pr list --head "$branch" --state open --json number -q '.[0].number' | grep -q '^[0-9]\+$'; then |
| : `unzip -o /tmp/${asset} -d "$HOME/.opencode/bin"`; | ||
| return `mkdir -p "$HOME/.opencode/bin" && curl -fsSL ${url} -o /tmp/${asset} && ${extract}`; | ||
| } | ||
| return `Download opencode v${version} from https://github.com/${repo}/releases/tag/v${version}`; |
There was a problem hiding this comment.
P2: Unsupported-architecture fallback returns a non-command string that bash -lc executes and fails immediately.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/electron/runtime.mjs, line 943:
<comment>Unsupported-architecture fallback returns a non-command string that `bash -lc` executes and fails immediately.</comment>
<file context>
@@ -920,6 +920,28 @@ export function createRuntimeManager({ app, desktopRoot, listLocalWorkspacePaths
+ : `unzip -o /tmp/${asset} -d "$HOME/.opencode/bin"`;
+ return `mkdir -p "$HOME/.opencode/bin" && curl -fsSL ${url} -o /tmp/${asset} && ${extract}`;
+ }
+ return `Download opencode v${version} from https://github.com/${repo}/releases/tag/v${version}`;
+ }
return `curl -fsSL https://opencode.ai/install | bash -s -- --version ${version} --no-modify-path`;
</file context>
| const extract = asset.endsWith(".tar.gz") | ||
| ? `tar -xzf /tmp/${asset} -C "$HOME/.opencode/bin"` | ||
| : `unzip -o /tmp/${asset} -d "$HOME/.opencode/bin"`; | ||
| return `mkdir -p "$HOME/.opencode/bin" && curl -fsSL ${url} -o /tmp/${asset} && ${extract}`; |
There was a problem hiding this comment.
P2: Engine download flow skips checksum/signature verification before extracting a binary to the executable path.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/electron/runtime.mjs, line 941:
<comment>Engine download flow skips checksum/signature verification before extracting a binary to the executable path.</comment>
<file context>
@@ -920,6 +920,28 @@ export function createRuntimeManager({ app, desktopRoot, listLocalWorkspacePaths
+ const extract = asset.endsWith(".tar.gz")
+ ? `tar -xzf /tmp/${asset} -C "$HOME/.opencode/bin"`
+ : `unzip -o /tmp/${asset} -d "$HOME/.opencode/bin"`;
+ return `mkdir -p "$HOME/.opencode/bin" && curl -fsSL ${url} -o /tmp/${asset} && ${extract}`;
+ }
+ return `Download opencode v${version} from https://github.com/${repo}/releases/tag/v${version}`;
</file context>
| const { version, repo: opencodeRepo } = resolveOpencodeVersion(); | ||
| const versionLabel = version; |
There was a problem hiding this comment.
P2: Bundle cache invalidation ignores opencodeRepo, so repo changes with the same version can leave a stale binary from the wrong source.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ee/apps/den-worker-runtime/scripts/install-opencode.mjs, line 157:
<comment>Bundle cache invalidation ignores `opencodeRepo`, so repo changes with the same version can leave a stale binary from the wrong source.</comment>
<file context>
@@ -153,7 +154,7 @@ function findBinary(searchRoot) {
}
-const version = resolveOpencodeVersion();
+const { version, repo: opencodeRepo } = resolveOpencodeVersion();
const versionLabel = version;
</file context>
| const { version, repo: opencodeRepo } = resolveOpencodeVersion(); | |
| const versionLabel = version; | |
| const { version, repo: opencodeRepo } = resolveOpencodeVersion(); | |
| const versionLabel = `${opencodeRepo}@${version}`; |
| ) { | ||
| return __OPENWORK_PINNED_OPENCODE_REPO__.trim(); | ||
| } | ||
| return "anomalyco/opencode"; |
There was a problem hiding this comment.
P2: Fallback default in pinnedOpencodeRepo() points to upstream repo ("anomalyco/opencode") instead of the fork ("different-ai/opencode") from constants.json. If both env vars and the compile-time define are absent, the download URL silently targets the wrong repo, which lacks the fork's release tags.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/orchestrator/src/cli.ts, line 144:
<comment>Fallback default in pinnedOpencodeRepo() points to upstream repo ("anomalyco/opencode") instead of the fork ("different-ai/opencode") from constants.json. If both env vars and the compile-time define are absent, the download URL silently targets the wrong repo, which lacks the fork's release tags.</comment>
<file context>
@@ -128,6 +128,21 @@ const FALLBACK_VERSION = "0.1.0";
+ ) {
+ return __OPENWORK_PINNED_OPENCODE_REPO__.trim();
+ }
+ return "anomalyco/opencode";
+}
const DEFAULT_OPENWORK_PORT = 8787;
</file context>
What
Switches the bundled opencode engine from
anomalyco/opencodereleases to our own forkdifferent-ai/opencode, pinned atv1.16.2-openwork.0— an identical rebuild of upstreamv1.16.2(zero patches), built by the fork's ownengine-releaseworkflow with upstream-identical asset names.Why
We want to carry our own engine patches and control rollout timing without waiting on upstream, while still following upstream's release cadence.
How rollout works from now on
constants.jsonnow holds bothopencodeVersionandopencodeRepo. Every consumer reads it: desktop sidecar (prepare-sidecar.mjs), orchestrator runtime downloads (was hardcoded), Daytona/microsandbox images, den-worker bundler, CI engine installs.upstream-syncworkflow (daily cron) fast-forwards itsdevmirror and auto-buildsvX.Y.Z-openwork.0whenever upstream publishes a release.engine-bump.ymlhere opens a PR bumpingopencodeVersionwhen the fork has a newer release; CI on that PR installs the engine from the new release and runs the full suite. Nothing auto-merges. (SetENGINE_BUMP_TOKENPAT secret so bump PRs trigger CI.).patchfiles in the fork'sopenwork/patches/are applied withgit amon top of the upstream tag at build time; release notes record the upstream ref.Signing
prepare-sidecarre-codesigns and the release workflow signs + notarizes the app bundle. No change.Tested (commands + results)
opencode --version→1.16.2-openwork.0;opencode servehealth →{"healthy":true,"version":"1.16.2-openwork.0"}.node scripts/prepare-sidecar.mjs --forceagainst this branch → downloaded from the fork, version check passed, codesigned,versions.jsonwritten (sha256 recorded).{"posthog":{"status":"needs_auth"}}.pnpm typecheckinapps/orchestrator— clean;node --checkon all edited .mjs scripts;bash -non edited shell scripts.ci-tests.ymlnow installs the engine fromdifferent-ai/opencode v1.16.2-openwork.0and runs the suite against it.Known follow-ups
opencode-chrome-devtoolsplugin still fetched unpinned from npm at runtime (pre-existing).opencode.ai/install(docs only).