feat: add Windows ARM64 support with x64-baseline CLI workaround (#2537)
* feat: add Windows ARM64 support with x64-baseline CLI workaround Windows ARM64 native opencode.exe fails with a Bun FFI/TinyCC dlopen error (anomalyco/opencode#19130). As a temporary workaround: - Bundle x64-baseline OpenCode CLI on ARM64 instead of native ARM64 (prepare-opencode-cli.mjs, env-runtime.js) - Disable OpenCode self-upgrade on ARM64 in server, VS Code, and UI (upgrade-capability.js, opencode-upgrade-runtime.ts, useUIStore.ts, OpenCodeCliSettings.tsx, search.ts, SettingsView.tsx, platform.ts) - Add ARM64 Windows cross-compile builds to release and smoke workflows (release.yml, release-desktop-smoke.yml) - Refactor Windows latest.yml to use combine-electron-manifests pattern matching macOS, since two arches now produce per-arch manifests The CI ARM64 build itself is permanent; only the x64-baseline CLI bundling and upgrade disablement are temporary and should be reverted when the upstream issue is resolved. * fix(desktop): select Windows updater by architecture --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
5792a3d2e2
commit
09f0c64839
@@ -186,7 +186,7 @@ jobs:
|
||||
|
||||
build-windows-electron:
|
||||
if: ${{ inputs.build_windows }}
|
||||
name: Build Windows Electron (x64)
|
||||
name: Build Windows Electron (${{ matrix.arch }})
|
||||
# Match the production release workflow. windows-latest currently resolves
|
||||
# to a runner with Visual Studio 18, which this Electron/node-gyp stack does
|
||||
# not detect correctly.
|
||||
@@ -198,6 +198,9 @@ jobs:
|
||||
- arch: x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
platform: win32-x64
|
||||
- arch: arm64
|
||||
target: aarch64-pc-windows-msvc
|
||||
platform: win32-arm64
|
||||
steps:
|
||||
- name: Checkout selected ref
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -249,6 +252,9 @@ jobs:
|
||||
- name: Rebuild native modules
|
||||
working-directory: packages/electron
|
||||
shell: bash
|
||||
env:
|
||||
# Cross-compile for ARM64 target from x64 runner.
|
||||
ELECTRON_BUILDER_ARCH: ${{ matrix.arch }}
|
||||
# npmRebuild=false in package.json, so electron-builder won't
|
||||
# recompile native deps on its own. Rebuild against the target
|
||||
# Electron ABI before packaging, matching the release workflow.
|
||||
|
||||
@@ -284,6 +284,9 @@ jobs:
|
||||
- arch: x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
platform: win32-x64
|
||||
- arch: arm64
|
||||
target: aarch64-pc-windows-msvc
|
||||
platform: win32-arm64
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -331,6 +334,9 @@ jobs:
|
||||
- name: Rebuild native modules
|
||||
working-directory: packages/electron
|
||||
shell: bash
|
||||
env:
|
||||
# Cross-compile for ARM64 target from x64 runner.
|
||||
ELECTRON_BUILDER_ARCH: ${{ matrix.arch }}
|
||||
# npmRebuild=false in package.json, so electron-builder won't
|
||||
# recompile native deps on its own — we must rebuild against the
|
||||
# target Electron ABI before packaging.
|
||||
@@ -350,7 +356,6 @@ jobs:
|
||||
files: |
|
||||
packages/electron/dist/*.exe
|
||||
packages/electron/dist/*.blockmap
|
||||
packages/electron/dist/latest.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -504,7 +509,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
combine-electron-manifests:
|
||||
needs: [create-release, build-desktop-electron-macos]
|
||||
needs: [create-release, build-desktop-electron-macos, build-desktop-electron-windows]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -514,13 +519,13 @@ jobs:
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- name: Download per-arch latest-mac.yml
|
||||
- name: Download per-arch update manifests
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
pattern: latest-yml-*-apple-darwin
|
||||
pattern: latest-yml-*
|
||||
path: artifacts
|
||||
|
||||
- name: Finalize combined latest-mac.yml
|
||||
- name: Finalize combined manifests
|
||||
env:
|
||||
LATEST_YML_DIR: ${{ github.workspace }}/artifacts
|
||||
GH_REPO: ${{ github.repository }}
|
||||
@@ -533,6 +538,8 @@ jobs:
|
||||
tag_name: v${{ needs.create-release.outputs.version }}
|
||||
files: |
|
||||
${{ runner.temp }}/latest-mac.yml
|
||||
${{ runner.temp }}/latest.yml
|
||||
${{ runner.temp }}/latest-arm64.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ macOS builds produce `dmg` and `zip` artifacts. Windows builds produce an NSIS i
|
||||
|
||||
macOS packaging needs Xcode/build tools for notarized builds and icon asset compilation.
|
||||
|
||||
Windows packaging needs NSIS support through `electron-builder`. If no Windows signing env is set, `package.mjs` disables code signing and builds an unsigned installer.
|
||||
Windows packaging needs NSIS support through `electron-builder`. If no Windows signing env is set, `package.mjs` disables code signing and builds an unsigned installer. Windows updates use `latest.yml` for x64 and the `latest-arm64.yml` channel for ARM64 so each installation resolves an architecture-matching installer.
|
||||
|
||||
Linux AppImages must be built natively. Set `OPENCHAMBER_TARGET_ARCH=x64` or `OPENCHAMBER_TARGET_ARCH=arm64` when packaging; the build rejects a target that does not match the Linux host. The same target selects the bundled OpenCode CLI, native Electron rebuild, and Electron Builder architecture. Linux identity is stable across architectures: executable `openchamber`, desktop file `openchamber.desktop`, icon `openchamber`, and `StartupWMClass=openchamber`.
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { resolveManagedOpenCodeCwd } from './opencode-cwd.mjs';
|
||||
import { sanitizeRuntimeRequestHeaders } from './runtime-request-headers.mjs';
|
||||
import { assertUpdaterCapability } from './updater-capability.mjs';
|
||||
import { checkForDesktopUpdate } from './updater-check.mjs';
|
||||
import { resolveUpdaterChannel } from './updater-channel.mjs';
|
||||
import { resolveUpdaterFeed } from './updater-feed.mjs';
|
||||
import {
|
||||
buildLinuxInstalledApps,
|
||||
@@ -2927,10 +2928,17 @@ const setupAutoUpdater = () => {
|
||||
const testBuild = typeof __OPENCHAMBER_UPDATER_E2E_BUILD__ !== 'undefined'
|
||||
&& __OPENCHAMBER_UPDATER_E2E_BUILD__ === true;
|
||||
const feed = resolveUpdaterFeed({ testBuild });
|
||||
const updaterChannel = feed.provider === 'github'
|
||||
? resolveUpdaterChannel({ platform: process.platform, architecture: process.arch })
|
||||
: null;
|
||||
if (updaterChannel) {
|
||||
autoUpdater.channel = updaterChannel;
|
||||
}
|
||||
autoUpdater.setFeedURL(feed);
|
||||
log.info('[electron] updater feed configured', {
|
||||
provider: feed.provider,
|
||||
target: feed.provider === 'github' ? `${feed.owner}/${feed.repo}` : feed.url,
|
||||
channel: updaterChannel || 'latest',
|
||||
});
|
||||
|
||||
autoUpdater.on('download-progress', (progress) => {
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"generate:macos-icon": "node ./scripts/generate-macos-icon-assets.cjs",
|
||||
"rebuild:native": "node ./scripts/rebuild-native.mjs",
|
||||
"test:architecture": "node --test ./scripts/target-architecture.test.mjs ./scripts/verify-linux-appimage.test.mjs ./scripts/verify-update-manifest.test.mjs",
|
||||
"test:updater": "node --test ./updater-capability.test.mjs ./updater-check.test.mjs ./updater-feed.test.mjs ./scripts/updater-e2e-fixture.test.mjs",
|
||||
"test:updater": "node --test ./updater-capability.test.mjs ./updater-channel.test.mjs ./updater-check.test.mjs ./updater-feed.test.mjs ./scripts/finalize-latest-yml.test.mjs ./scripts/updater-e2e-fixture.test.mjs",
|
||||
"test:linux-desktop": "node --test ./linux-autostart.test.mjs && node ./scripts/smoke-linux-app-discovery.mjs && node ./scripts/smoke-path-open-utils.mjs",
|
||||
"updater:e2e:fixture": "node ./scripts/updater-e2e-fixture.mjs",
|
||||
"verify:update-manifest": "node ./scripts/verify-update-manifest.mjs",
|
||||
|
||||
@@ -76,14 +76,11 @@ const output = {};
|
||||
|
||||
const winX64 = await read('latest-yml-x86_64-pc-windows-msvc', 'latest.yml');
|
||||
const winArm64 = await read('latest-yml-aarch64-pc-windows-msvc', 'latest.yml');
|
||||
if (winX64 || winArm64) {
|
||||
const base = winArm64 || winX64;
|
||||
output['latest.yml'] = serialize({
|
||||
version: base.version,
|
||||
files: [...(winArm64?.files || []), ...(winX64?.files || [])],
|
||||
releaseDate: base.releaseDate,
|
||||
});
|
||||
if (!winX64 || !winArm64) {
|
||||
throw new Error('Both x64 and arm64 Windows update manifests are required');
|
||||
}
|
||||
output['latest.yml'] = serialize(winX64);
|
||||
output['latest-arm64.yml'] = serialize(winArm64);
|
||||
|
||||
const macX64 = await read('latest-yml-x86_64-apple-darwin', 'latest-mac.yml');
|
||||
const macArm64 = await read('latest-yml-aarch64-apple-darwin', 'latest-mac.yml');
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync, spawnSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const script = fileURLToPath(new URL('./finalize-latest-yml.mjs', import.meta.url));
|
||||
|
||||
const manifest = (architecture) => `version: 1.2.3
|
||||
files:
|
||||
- url: OpenChamber-1.2.3-win-${architecture}.exe
|
||||
sha512: ${architecture}-checksum
|
||||
size: 123
|
||||
releaseDate: '2026-07-30T00:00:00.000Z'
|
||||
`;
|
||||
|
||||
const createFixture = ({ includeArm64 = true } = {}) => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'openchamber-latest-yml-'));
|
||||
const artifacts = path.join(root, 'artifacts');
|
||||
const output = path.join(root, 'output');
|
||||
fs.mkdirSync(path.join(artifacts, 'latest-yml-x86_64-pc-windows-msvc'), { recursive: true });
|
||||
fs.writeFileSync(path.join(artifacts, 'latest-yml-x86_64-pc-windows-msvc', 'latest.yml'), manifest('x64'));
|
||||
if (includeArm64) {
|
||||
fs.mkdirSync(path.join(artifacts, 'latest-yml-aarch64-pc-windows-msvc'), { recursive: true });
|
||||
fs.writeFileSync(path.join(artifacts, 'latest-yml-aarch64-pc-windows-msvc', 'latest.yml'), manifest('arm64'));
|
||||
}
|
||||
fs.mkdirSync(output);
|
||||
return { root, artifacts, output };
|
||||
};
|
||||
|
||||
const environment = ({ artifacts, output }) => ({
|
||||
...process.env,
|
||||
LATEST_YML_DIR: artifacts,
|
||||
RUNNER_TEMP: output,
|
||||
GH_REPO: 'openchamber/openchamber',
|
||||
OPENCHAMBER_VERSION: '1.2.3',
|
||||
});
|
||||
|
||||
test('writes separate x64 and ARM64 Windows update channels', (context) => {
|
||||
const fixture = createFixture();
|
||||
context.after(() => fs.rmSync(fixture.root, { recursive: true, force: true }));
|
||||
|
||||
execFileSync(process.execPath, [script], { env: environment(fixture) });
|
||||
|
||||
const x64 = fs.readFileSync(path.join(fixture.output, 'latest.yml'), 'utf8');
|
||||
const arm64 = fs.readFileSync(path.join(fixture.output, 'latest-arm64.yml'), 'utf8');
|
||||
assert.match(x64, /win-x64\.exe/);
|
||||
assert.doesNotMatch(x64, /win-arm64\.exe/);
|
||||
assert.match(arm64, /win-arm64\.exe/);
|
||||
assert.doesNotMatch(arm64, /win-x64\.exe/);
|
||||
});
|
||||
|
||||
test('fails instead of publishing an incomplete Windows channel set', (context) => {
|
||||
const fixture = createFixture({ includeArm64: false });
|
||||
context.after(() => fs.rmSync(fixture.root, { recursive: true, force: true }));
|
||||
|
||||
const result = spawnSync(process.execPath, [script], { env: environment(fixture), encoding: 'utf8' });
|
||||
|
||||
assert.notEqual(result.status, 0);
|
||||
assert.match(result.stderr, /Both x64 and arm64 Windows update manifests are required/);
|
||||
});
|
||||
@@ -47,7 +47,17 @@ const artifactForPlatform = (platform, targetArchitecture) => {
|
||||
if (arch === 'x64') return { name: 'opencode-darwin-x64-baseline.zip', binary: 'opencode' };
|
||||
}
|
||||
if (platform === 'win32') {
|
||||
if (arch === 'arm64') return { name: 'opencode-windows-arm64.zip', binary: 'opencode.exe' };
|
||||
// TEMPORARY WORKAROUND — Windows ARM64: native opencode.exe fails with a Bun
|
||||
// FFI/TinyCC dlopen error (https://github.com/anomalyco/opencode/issues/19130).
|
||||
// Bundle x64-baseline instead (runs under x64 emulation); OpenCode self-upgrade
|
||||
// is disabled elsewhere so it can't overwrite with the broken ARM64 build.
|
||||
// Remove this block and restore the original below when the upstream issue
|
||||
// is resolved.
|
||||
// --- ORIGINAL (restore when ARM64 is fixed) ---
|
||||
// if (arch === 'arm64') return { name: 'opencode-windows-arm64.zip', binary: 'opencode.exe' };
|
||||
// if (arch === 'x64') return { name: 'opencode-windows-x64-baseline.zip', binary: 'opencode.exe' };
|
||||
// --- END ORIGINAL ---
|
||||
if (arch === 'arm64') return { name: 'opencode-windows-x64-baseline.zip', binary: 'opencode.exe' };
|
||||
if (arch === 'x64') return { name: 'opencode-windows-x64-baseline.zip', binary: 'opencode.exe' };
|
||||
}
|
||||
if (platform === 'linux') {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const resolveUpdaterChannel = ({ platform, architecture }) => (
|
||||
platform === 'win32' && architecture === 'arm64' ? 'latest-arm64' : null
|
||||
);
|
||||
@@ -0,0 +1,13 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { resolveUpdaterChannel } from './updater-channel.mjs';
|
||||
|
||||
test('uses an architecture-specific Windows ARM64 update channel', () => {
|
||||
assert.equal(resolveUpdaterChannel({ platform: 'win32', architecture: 'arm64' }), 'latest-arm64');
|
||||
});
|
||||
|
||||
test('keeps the default channel for other desktop targets', () => {
|
||||
assert.equal(resolveUpdaterChannel({ platform: 'win32', architecture: 'x64' }), null);
|
||||
assert.equal(resolveUpdaterChannel({ platform: 'darwin', architecture: 'arm64' }), null);
|
||||
assert.equal(resolveUpdaterChannel({ platform: 'linux', architecture: 'arm64' }), null);
|
||||
});
|
||||
@@ -16,6 +16,7 @@ import { reloadOpenCodeConfiguration } from '@/stores/useAgentsStore';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { runtimeFetch } from '@/lib/runtime-fetch';
|
||||
import { isWindowsArm64 } from '@/lib/platform';
|
||||
|
||||
export const OpenCodeCliSettings: React.FC = () => {
|
||||
const { t } = useI18n();
|
||||
@@ -151,13 +152,15 @@ export const OpenCodeCliSettings: React.FC = () => {
|
||||
</SettingsFieldRow>
|
||||
|
||||
<SettingsInset className={SETTINGS_OPTION_STACK_CLASS}>
|
||||
<SettingsCheckboxRow
|
||||
settingsItem="sessions.opencode-update-notifications"
|
||||
checked={showOpenCodeUpdateNotifications}
|
||||
onChange={handleShowUpdateNotificationsChange}
|
||||
label={t('settings.openchamber.opencodeCli.field.showUpdateNotifications')}
|
||||
ariaLabel={t('settings.openchamber.opencodeCli.field.showUpdateNotificationsAria')}
|
||||
/>
|
||||
{!isWindowsArm64() && (
|
||||
<SettingsCheckboxRow
|
||||
settingsItem="sessions.opencode-update-notifications"
|
||||
checked={showOpenCodeUpdateNotifications}
|
||||
onChange={handleShowUpdateNotificationsChange}
|
||||
label={t('settings.openchamber.opencodeCli.field.showUpdateNotifications')}
|
||||
ariaLabel={t('settings.openchamber.opencodeCli.field.showUpdateNotificationsAria')}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SettingsCheckboxRow
|
||||
settingsItem="sessions.agent-control-tool"
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
} from '@/components/sections/shared/SettingsSection';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { isDesktopLocalOriginActive, isDesktopShell, isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
|
||||
import { isWindowsArm64 as isWindowsArm64Platform } from '@/lib/platform';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import type { IconName } from "@/components/icon/icons";
|
||||
@@ -278,6 +279,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
return isDesktopShell() && typeof window !== 'undefined'
|
||||
&& (window as unknown as { __OPENCHAMBER_PLATFORM__?: string }).__OPENCHAMBER_PLATFORM__ === 'linux';
|
||||
}, []);
|
||||
const isWindowsArm64 = React.useMemo(() => isWindowsArm64Platform(), []);
|
||||
|
||||
// keep platform check available for future window chrome tweaks
|
||||
|
||||
@@ -421,12 +423,12 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
|
||||
const settingsSearchResults = React.useMemo(() => {
|
||||
return buildSettingsSearchResults({
|
||||
query: settingsSearchQuery,
|
||||
runtimeCtx: { ...runtimeCtx, isDesktopLocalOrigin, isMac, isWindows, isLinux },
|
||||
runtimeCtx: { ...runtimeCtx, isDesktopLocalOrigin, isMac, isWindows, isLinux, isWindowsArm64 },
|
||||
visiblePageSlugs,
|
||||
t,
|
||||
getPageTitle,
|
||||
});
|
||||
}, [getPageTitle, isDesktopLocalOrigin, isMac, isWindows, isLinux, runtimeCtx, settingsSearchQuery, t, visiblePageSlugs]);
|
||||
}, [getPageTitle, isWindowsArm64, isDesktopLocalOrigin, isMac, isWindows, isLinux, runtimeCtx, settingsSearchQuery, t, visiblePageSlugs]);
|
||||
|
||||
const prepareSettingsSearchTarget = React.useCallback((result: SettingsSearchResult): string => {
|
||||
if (result.id.startsWith('agents.')) {
|
||||
|
||||
@@ -7,6 +7,39 @@ export const isCapacitorApp = (): boolean => {
|
||||
return capacitor?.isNativePlatform?.() === true || window.location.protocol === 'capacitor:';
|
||||
};
|
||||
|
||||
// TEMPORARY WORKAROUND — Windows ARM64: native opencode.exe fails with a Bun
|
||||
// FFI/TinyCC dlopen error (https://github.com/anomalyco/opencode/issues/19130).
|
||||
// Suppress OpenCode update UI on ARM64 so it can't self-upgrade to the broken
|
||||
// ARM64 build. Remove this helper and its call sites when resolved upstream.
|
||||
export const isWindowsArm64 = (): boolean => {
|
||||
if (typeof window === 'undefined') return false;
|
||||
|
||||
const electronArch = window.__OPENCHAMBER_ELECTRON__?.arch?.toLowerCase?.();
|
||||
if (electronArch === 'arm64' || electronArch === 'aarch64') {
|
||||
const platform = (navigator.platform || '').toLowerCase();
|
||||
return platform.includes('win');
|
||||
}
|
||||
|
||||
const vscodeArch = (window as { __VSCODE_CONFIG__?: { arch?: string } }).__VSCODE_CONFIG__?.arch?.toLowerCase?.();
|
||||
if (vscodeArch === 'arm64' || vscodeArch === 'aarch64') {
|
||||
const platform = (navigator.platform || '').toLowerCase();
|
||||
return platform.includes('win');
|
||||
}
|
||||
|
||||
const nav = (navigator as Navigator & { userAgentData?: { architecture?: string; platform?: string } }).userAgentData;
|
||||
if (nav?.architecture?.toLowerCase?.() === 'arm64' || nav?.architecture?.toLowerCase?.() === 'aarch64') {
|
||||
const platform = (nav.platform || navigator.platform || '').toLowerCase();
|
||||
return platform.includes('win');
|
||||
}
|
||||
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
if ((ua.includes('aarch64') || ua.includes('arm64') || ua.includes('armv')) && ua.includes('windows')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* True when running inside the native Capacitor shell on an iPad.
|
||||
* Capacitor reports 'ios' for both iPhone and iPad; iPadOS WKWebView
|
||||
|
||||
@@ -26,6 +26,8 @@ interface SettingsSearchAvailabilityContext extends SettingsRuntimeContext {
|
||||
isWindows: boolean;
|
||||
// Linux desktop shell — for controls that only render on linux.
|
||||
isLinux: boolean;
|
||||
// Windows ARM64 — temporary workaround gate (see opencode#19130).
|
||||
isWindowsArm64: boolean;
|
||||
}
|
||||
|
||||
const SETTINGS_SEARCH_ITEMS: readonly SettingsSearchItem[] = [
|
||||
@@ -470,7 +472,7 @@ const SETTINGS_SEARCH_ITEMS: readonly SettingsSearchItem[] = [
|
||||
page: 'general',
|
||||
titleKey: 'settings.openchamber.opencodeCli.field.showUpdateNotifications',
|
||||
keywords: ['opencode', 'cli', 'updates'],
|
||||
isAvailable: (ctx) => !ctx.isVSCode,
|
||||
isAvailable: (ctx) => !ctx.isVSCode && !ctx.isWindowsArm64,
|
||||
},
|
||||
{
|
||||
id: 'sessions.agent-control-tool',
|
||||
|
||||
@@ -10,6 +10,7 @@ import { getStoredMobileKeyboardMode, type MobileKeyboardMode } from '@/lib/mobi
|
||||
import { getRuntimeKey } from '@/lib/runtime-switch';
|
||||
import type { TerminalShell } from '@/lib/api/types';
|
||||
import { useFilesViewTabsStore } from './useFilesViewTabsStore';
|
||||
import { isWindowsArm64 } from '@/lib/platform';
|
||||
|
||||
export type MainTab = 'chat' | 'plan' | 'git' | 'diff' | 'terminal' | 'files' | 'context' | 'diagram';
|
||||
export type PendingDiffScope = 'working' | 'staged' | 'turn';
|
||||
@@ -999,7 +1000,7 @@ export const useUIStore = create<UIStore>()(
|
||||
|
||||
showTerminalQuickKeysOnDesktop: false,
|
||||
persistChatDraft: true,
|
||||
showOpenCodeUpdateNotifications: true,
|
||||
showOpenCodeUpdateNotifications: !isWindowsArm64(),
|
||||
agentControlToolEnabled: true,
|
||||
inputSpellcheckEnabled: false,
|
||||
wideChatLayoutEnabled: false,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
type UpgradeCapability = {
|
||||
supported: boolean;
|
||||
manager: 'opencode' | 'external' | null;
|
||||
reason: 'external' | 'unavailable' | null;
|
||||
manager: 'opencode' | 'external' | 'openchamber' | null;
|
||||
reason: 'external' | 'unavailable' | 'windows-arm64-workaround' | null;
|
||||
};
|
||||
|
||||
export type OpenCodeUpgradeManager = {
|
||||
@@ -15,6 +15,12 @@ type UpgradeResult = { status: number; body: Record<string, unknown> };
|
||||
|
||||
let openCodeUpgradePromise: Promise<UpgradeResult> | null = null;
|
||||
|
||||
// TEMPORARY WORKAROUND — Windows ARM64: native opencode.exe fails with a Bun
|
||||
// FFI/TinyCC dlopen error (https://github.com/anomalyco/opencode/issues/19130).
|
||||
// Disable OpenCode self-upgrade on ARM64 so it can't overwrite the working x64
|
||||
// binary with the broken ARM64 build. Remove when the upstream issue is resolved.
|
||||
const isWindowsArm64 = (): boolean => process.platform === 'win32' && process.arch === 'arm64';
|
||||
|
||||
const parseVersion = (value: unknown): { parts: number[]; prerelease: boolean } => {
|
||||
const normalized = String(value || '').replace(/^v/, '').split('+')[0];
|
||||
const prereleaseIndex = normalized.indexOf('-');
|
||||
@@ -39,6 +45,7 @@ const compareVersions = (left: unknown, right: unknown): number => {
|
||||
};
|
||||
|
||||
const getCapability = (manager?: OpenCodeUpgradeManager): UpgradeCapability => {
|
||||
if (isWindowsArm64()) return { supported: false, manager: 'openchamber', reason: 'windows-arm64-workaround' };
|
||||
if (!manager) return { supported: false, manager: null, reason: 'unavailable' };
|
||||
if (manager.getDebugInfo().mode !== 'managed') return { supported: false, manager: 'external', reason: 'external' };
|
||||
if (!manager.getApiUrl()) return { supported: false, manager: null, reason: 'unavailable' };
|
||||
|
||||
@@ -664,8 +664,15 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
||||
};
|
||||
|
||||
const getWindowsNativeOpencodePackageNames = () => {
|
||||
// TEMPORARY WORKAROUND — Windows ARM64: native opencode.exe fails with a Bun
|
||||
// FFI/TinyCC dlopen error (https://github.com/anomalyco/opencode/issues/19130).
|
||||
// prepare-opencode-cli.mjs bundles x64-baseline instead; match that here so
|
||||
// the runtime resolver looks for the same x64-baseline package. Restore the
|
||||
// arm64 branch below when the upstream issue is resolved.
|
||||
if (process.arch === 'arm64') {
|
||||
return ['opencode-windows-arm64'];
|
||||
// --- ORIGINAL (restore when ARM64 is fixed) ---
|
||||
// return ['opencode-windows-arm64'];
|
||||
return ['opencode-windows-x64-baseline', 'opencode-windows-x64'];
|
||||
}
|
||||
if (process.arch === 'x64') {
|
||||
// Prefer the baseline build when bypassing package-manager wrappers so the
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
// TEMPORARY WORKAROUND — Windows ARM64: native opencode.exe fails with a Bun
|
||||
// FFI/TinyCC dlopen error (https://github.com/anomalyco/opencode/issues/19130).
|
||||
// Disable OpenCode self-upgrade on ARM64 so it can't overwrite the working x64
|
||||
// binary with the broken ARM64 build. Remove when the upstream issue is resolved.
|
||||
const isWindowsArm64 = () => process.platform === 'win32' && process.arch === 'arm64';
|
||||
|
||||
export const resolveOpenCodeUpgradeCapability = ({
|
||||
isExternal,
|
||||
hasManagedProcess,
|
||||
activeBinary,
|
||||
isBundledBinary,
|
||||
}) => {
|
||||
if (isWindowsArm64()) {
|
||||
return {
|
||||
supported: false,
|
||||
manager: 'openchamber',
|
||||
reason: 'windows-arm64-workaround',
|
||||
};
|
||||
}
|
||||
|
||||
if (isExternal) {
|
||||
return {
|
||||
supported: false,
|
||||
|
||||
Reference in New Issue
Block a user