feat: [AI] bring the desktop app into the fork (Tauri + Electron, fully branded)#961
feat: [AI] bring the desktop app into the fork (Tauri + Electron, fully branded)#961anandgupta42 wants to merge 7 commits into
Conversation
…ly branded)
Adopt the OpenCode desktop app (upstream v1.2.20) into altimate-code as a
fully-branded Altimate Code desktop app with no OpenCode leaks.
- Pull `packages/{app,ui,desktop,desktop-electron}`; add to the workspace
(all `catalog:` deps already satisfied by the root catalog).
- Un-skip these packages in `script/upstream/utils/config.ts`; add `keepOurs`,
`requireMarkers`, and new branding rules (deep-link scheme `altimate://`,
desktop Rust crate names) so future merges stay branded.
- Sidecar wiring: the Tauri shell bundles our `altimate` binary as the
`opencode-cli` sidecar. Adapt `scripts/{utils,predev,prepare,copy-bundles,
finalize-latest-json}.ts` to our build output (`@altimateai/altimate-code-*/
bin/altimate`) and the CI artifact contract.
- Branding (no leaks): rebrand the 3 `tauri.*.conf.json` (productName,
identifier, scheme, NEW updater pubkey + AltimateAI endpoints), `index.html`,
webmanifest, AppStream, Cargo crate names, `cli.rs` WSL install paths
(`~/.altimate/bin`, `www.altimate.sh/install`), window title, and i18n (18
locales). Regenerate all icon sets, favicons, PWA icons, NSIS bitmaps, social
images, and the `Logo`/`Mark` components from the Altimate brand mark.
- Gateway: feature the Altimate LLM Gateway (`altimate-backend`) first in the
provider picker with a branded icon + an in-app credential arm; remove the
OpenCode Zen onboarding and filter the `opencode`/`opencode-go` providers.
- Enhance the generic tool renderer to show full output for custom data tools.
- Add `.github/workflows/publish-desktop.yml` (5-platform signed build + Tauri
updater) and a publishing runbook in `packages/desktop/README.md`.
Verified: typecheck (app/ui/desktop), runtime `/provider` smoke test, native
`tauri build` (.app + .dmg, branded Info.plist), visual inspection in-browser
and of the launched native app, branding audit (0 leaks), marker guard, and a
codex deep review (all findings addressed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Important Review skippedToo many files! This PR contains 737 files, which is 587 over the limit of 150. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1599)
📒 Files selected for processing (737)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
|
…es + drop unused mic entitlement
- `packages/opencode/test/upstream/bridge-merge-e2e.test.ts`: the
"upstream-only packages absent" test still asserted packages/{app,ui,
desktop,desktop-electron} don't exist, but they're now adopted for the
desktop app. Move them out of the absent list and add an
"adopted desktop packages present" block. Fixes the failing CI test job.
- `packages/desktop/src-tauri/entitlements.plist`: remove
`com.apple.security.device.audio-input` — the app has no microphone usage
(no getUserMedia/MediaRecorder), so it only triggered an unnecessary macOS
permission prompt. The JIT/memory entitlements remain (required by the Bun
sidecar).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 79edaf8b32
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (cliHealthCheck) { | ||
| if (needsMigration) await sqliteDone?.promise | ||
| cliHealthCheck?.() |
There was a problem hiding this comment.
Start sqlite listeners before waiting for migration
On a first Electron launch where sqliteFileExists() is false, this branch awaits sqliteDone.promise before cliHealthCheck is invoked. The only events.on("sqlite", ...) registration that resolves that promise is inside cliHealthCheck above, and Node EventEmitter does not replay migration events emitted by the already-spawned sidecar, so initialization can hang forever and never create the main window.
Useful? React with 👍 / 👎.
| let output = std::process::Command::new(&temp_script) | ||
| .arg("--binary") | ||
| .arg(&sidecar) | ||
| .output() |
There was a problem hiding this comment.
Install the copied sidecar as
altimate
For the macOS/Linux desktop “Install CLI” command, this passes the packaged sidecar path whose basename is opencode-cli. The root install script's --binary mode preserves the source basename (install:468-487), so it copies to ~/.altimate/bin/opencode-cli while get_cli_install_path() reports/checks ~/.altimate/bin/altimate; users are told an altimate command was installed even though that file was never created.
Useful? React with 👍 / 👎.
| export const SIDECAR_BINARIES: Array<{ rustTarget: string; ocBinary: string; assetExt: string }> = [ | ||
| { | ||
| rustTarget: "aarch64-apple-darwin", | ||
| ocBinary: "opencode-darwin-arm64", |
There was a problem hiding this comment.
Use the Altimate sidecar artifact names
The CLI build now emits dist/${pkg.name}-.../bin/altimate with pkg.name set to @altimateai/altimate-code (packages/opencode/script/build.ts lines 369-380 and 477), but the Electron sidecar map still points at opencode-* and prepare.ts/predev.ts append /bin/opencode. Running the Electron dev/package scripts after building the current CLI will copy from a non-existent path, so the Electron app cannot start with a sidecar.
Useful? React with 👍 / 👎.
| "plugins": { | ||
| "updater": { | ||
| "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDNDNzA4ODQzNkU0OTk4NjYKUldSbW1FbHVRNGh3UEpIVXFmdGErM0NRcHVaY0ZLZUg3blQxaGlpeWd0THZIOXFpakt3WVNXUjQK", | ||
| "endpoints": ["https://github.com/AltimateAI/altimate-code-beta/releases/latest/download/latest.json"] |
There was a problem hiding this comment.
Publish beta updates to the endpoint the app polls
When publish-desktop is dispatched with channel=beta, line 176 selects this beta config, but the release and finalized latest.json are created/uploaded to github.repository in the same workflow (.github/workflows/publish-desktop.yml lines 54-61 and 186-192). The beta app will instead poll AltimateAI/altimate-code-beta, so the updater will not see the beta artifacts unless a separate sync step exists.
Useful? React with 👍 / 👎.
| - name: Build CLI (all targets) | ||
| run: bun ./packages/opencode/script/build.ts |
There was a problem hiding this comment.
Install workspace dependencies before building
This workflow runs the build script immediately after setup-bun, but the action only installs the Bun binary; it does not populate node_modules. On a fresh GitHub runner, bun ./packages/opencode/script/build.ts imports workspace packages and dependencies before the script can run its own targeted installs, so the release job fails here (and the Tauri build job has the same missing install before bun ./scripts/prepare.ts / bun run build).
Useful? React with 👍 / 👎.
| } | ||
| }; | ||
|
|
||
| Ok(data_home.join("opencode").join("opencode.db")) |
There was a problem hiding this comment.
Check the branded data directory for SQLite
The sidecar stores its database under the branded global data directory (altimate-code/opencode.db), but this desktop migration guard still checks the legacy opencode/opencode.db path. On Tauri builds this makes every launch look like a first-run migration even after the real database exists, forcing the loading/migration path unnecessarily and risking a startup wait for migration events that are not tied to the actual database location.
Useful? React with 👍 / 👎.
|
|
||
| - name: Build, sign, and upload | ||
| uses: tauri-apps/tauri-action@v0 | ||
| env: |
There was a problem hiding this comment.
Provide the release token to tauri-action
This upload step passes releaseId/tagName but does not expose GITHUB_TOKEN in the action environment. The tauri-action release examples require that env token for GitHub release uploads; with only workflow permissions set, the action still has no token value to authenticate asset/latest.json uploads, so the desktop publish job will fail at the release-upload phase once the build reaches this step.
Useful? React with 👍 / 👎.
| { key: "linux-x86_64-deb", asset: "altimate-code-desktop-linux-amd64.deb" }, | ||
| { key: "linux-x86_64-rpm", asset: "altimate-code-desktop-linux-x86_64.rpm" }, | ||
| { key: "linux-aarch64-deb", asset: "altimate-code-desktop-linux-arm64.deb" }, | ||
| { key: "linux-aarch64-rpm", asset: "altimate-code-desktop-linux-aarch64.rpm" }, |
There was a problem hiding this comment.
Match updater asset names to the release pattern
The workflow uploads assets using altimate-code-desktop-[platform]-[arch][ext], where tauri-action's [arch] comes from the target triple (x86_64/aarch64), but these deb lookups use Debian aliases (amd64/arm64). Because missing assets are silently skipped, the finalized latest.json will not add the plain linux-x86_64/linux-aarch64 updater entries sourced from deb packages, breaking Linux updater lookup for those builds.
Useful? React with 👍 / 👎.
| const CLI_INSTALL_DIR = ".opencode/bin" | ||
| const CLI_BINARY_NAME = "opencode" |
There was a problem hiding this comment.
Use the branded CLI install location in Electron
Electron still looks for and reports ~/.opencode/bin/opencode, while the bundled install script in this repo installs altimate under ~/.altimate/bin. After a user runs the Electron “Install CLI” flow, the script succeeds but this code returns/checks a path that was never created; the WSL bootstrap path below has the same mismatch and will try to execute a missing opencode binary.
Useful? React with 👍 / 👎.
| }) | ||
|
|
||
| void app.whenReady().then(async () => { | ||
| // migrate() |
There was a problem hiding this comment.
Run the Tauri-to-Electron data migration
The migration helper is implemented but never invoked because the startup call is commented out. Users moving from the existing Tauri desktop app to the Electron build will start with empty Electron stores instead of their Tauri settings/default server/global data, even though migrate.ts is specifically written to copy those .dat files once.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 086a759327
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| prod: "ai.altimate.code.desktop", | ||
| } | ||
| app.setName(app.isPackaged ? APP_NAMES[CHANNEL] : "Altimate Code Dev") | ||
| app.setPath("userData", join(app.getPath("appData"), app.isPackaged ? APP_IDS[CHANNEL] : "ai.altimate.code.desktop.dev")) |
There was a problem hiding this comment.
Set Electron userData before constructing stores
In the Electron main process, static imports below this line are evaluated before this module body runs; ./server imports ./store, and store.ts constructs new Store() at top level before this app.setPath("userData", ...) call. That means settings such as the default server and WSL flag are read/written under Electron's default userData directory rather than the channel-specific ai.altimate.code.desktop* directory, so packaged/dev/beta/prod settings are not isolated as intended.
Useful? React with 👍 / 👎.
| output: "dist", | ||
| buildResources: "resources", | ||
| }, | ||
| files: ["out/**/*", "resources/**/*"], |
There was a problem hiding this comment.
Package the install script with Electron
The packaged Electron app only includes out/**/* and resources/**/*, but installCli() reads join(app.getAppPath(), "install") before spawning the installer. In a packaged macOS/Linux build that root install script is not in app.asar, so the “Install CLI” action fails with ENOENT before it can copy the bundled sidecar.
Useful? React with 👍 / 👎.
| if (customUrl && (await checkHealthOrAskRetry(customUrl))) { | ||
| serverReady.resolve({ url: customUrl, password: null }) | ||
| return { variant: "existing", url: customUrl } |
There was a problem hiding this comment.
Keep a local sidecar when using a remote default
When the saved/default server URL is a healthy remote host, this branch returns it as the only server and never starts the local sidecar. The renderer then wraps the returned URL as a ServerConnection.Sidecar, which ServerProvider.isLocal() treats as local, so desktop actions like opening a project use local file paths against the remote server and users lose the local sidecar entirely.
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| function injectGlobals(win: BrowserWindow, globals: Globals) { | ||
| win.webContents.on("dom-ready", () => { |
There was a problem hiding this comment.
Inject Electron globals before renderer startup
Because this waits until dom-ready to set window.__OPENCODE__, renderer modules that read it during evaluation have already run: renderer/updater.ts captures UPDATER_ENABLED at import time and renderer/index.tsx reads startup deep links/WSL state at top level. In packaged Electron builds this can leave the updater disabled and initial deep links or WSL state missed; these globals need to be available from preload/initialization before the renderer entry executes.
Useful? React with 👍 / 👎.
| if (normalized === original.http.url) { | ||
| server.add(conn) | ||
| } else { | ||
| replaceServer(original, conn) |
There was a problem hiding this comment.
Preserve the default server when editing URLs
When the URL is changed for a server that is currently marked as the default, this only replaces the entry in the persisted server list and never updates platform.setDefaultServerUrl. On the next launch getSavedServerUrl() still returns the old URL, so the app tries to connect to or prompts about a server the user just edited away.
Useful? React with 👍 / 👎.
| let e = *rx.borrow(); | ||
| let _ = events.send(e); | ||
|
|
||
| while rx.changed().await.is_ok() { |
There was a problem hiding this comment.
Return when initialization is already done
If the Tauri main window calls await_initialization after InitStep::Done is already the current watch value, this sends the current step but then waits for another rx.changed() that will never arrive. The migration/loading-window path sends Done before creating the main window, so ServerGate can remain pending forever even though ServerState.status is already ready.
Useful? React with 👍 / 👎.
| if (mainWindow) sendSqliteMigrationProgress(mainWindow, progress) | ||
| if (progress.type === "Done") sqliteDone?.resolve() | ||
| }) | ||
| await health.wait |
There was a problem hiding this comment.
Propagate sidecar health failures
When the Electron sidecar exits before becoming healthy, health.wait rejects, but this async health check is launched detached and the rejection never resolves or rejects serverReady. In that failure mode the renderer's awaitInitialization() keeps waiting on serverReady.promise forever instead of showing the startup error/logs.
Useful? React with 👍 / 👎.
| <DropdownMenu.ItemLabel>{language.t("dialog.server.menu.edit")}</DropdownMenu.ItemLabel> | ||
| </DropdownMenu.Item> | ||
| <Show when={canDefault() && defaultUrl() !== i.http.url}> | ||
| <DropdownMenu.Item onSelect={() => setDefault(i.http.url)}> |
There was a problem hiding this comment.
Store credentials with default servers
The default-server action persists only i.http.url, while the native startup health checks read only that saved URL and call the server without the username/password from the persisted server entry. If a user marks a Basic-auth-protected server as default, the next desktop launch reports it as unreachable and falls back/prompts even though the same server works from the in-app server list with credentials.
Useful? React with 👍 / 👎.
The Tauri shell's `opencode_db_path()` waited for `~/.local/share/opencode/opencode.db`, but our fork renamed the data dir and DB (`global/index.ts` app = "altimate-code", `index.ts` altimate-code.db), so the server creates `~/.local/share/altimate-code/altimate-code.db`. The shell waited forever for a file at the old path → the window hung on "Just a moment…" indefinitely. - `packages/desktop/src-tauri/src/lib.rs`: point `opencode_db_path()` at the branded path (wrapped in altimate_change markers). - `script/upstream/utils/config.ts`: add lib.rs to `requireMarkers` so future merges keep this functional divergence marked. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When launched via `open`, the .app inherits cwd `/`; the server's startup config/project resolution scans the cwd tree, so `debug config` (and serve) hung indefinitely from `/` or $HOME (the window stuck on 'Just a moment…'). Spawn the sidecar in a dedicated empty dir (state_dir/server-cwd) so startup is bounded — verified: sidecar reaches serve in ~7s and responds 200. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a082192d79
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| void app.whenReady().then(async () => { | ||
| // migrate() | ||
| app.setAsDefaultProtocolClient("opencode") |
There was a problem hiding this comment.
Register the handled deep-link scheme
For Electron builds that rely on setAsDefaultProtocolClient (notably dev runs and Windows/Linux installs), this registers opencode://, but the packaged protocol config and renderer parser only use altimate:// links. In that setup altimate://open-project / altimate://new-session links are never registered with the OS, while any opencode:// links that are registered are ignored by the app's altimate:// filters.
Useful? React with 👍 / 👎.
| function sqliteFileExists() { | ||
| const xdg = process.env.XDG_DATA_HOME | ||
| const base = xdg && xdg.length > 0 ? xdg : join(homedir(), ".local", "share") | ||
| return existsSync(join(base, "opencode", "opencode.db")) |
There was a problem hiding this comment.
Check the branded SQLite location
After the forked sidecar creates its SQLite data under the branded altimate-code data directory, this legacy opencode/opencode.db probe still returns false. That makes every Electron launch look like a first-run migration, so the app unnecessarily enters the loading/migration path and waits for migration progress even when the current database already exists.
Useful? React with 👍 / 👎.
| projectPath: packages/desktop | ||
| releaseId: ${{ needs.prepare-release.outputs.release_id }} | ||
| tagName: ${{ needs.prepare-release.outputs.tag }} | ||
| releaseDraft: true |
There was a problem hiding this comment.
Publish the release after uploading artifacts
This workflow creates a draft release in prepare-release and also passes releaseDraft: true here, but the final job only regenerates/uploads latest.json and never undrafts the release. When publish-desktop is dispatched, the assets remain hidden in a draft release, so the public /releases/latest/download/latest.json updater endpoint and normal release downloads cannot see the build.
Useful? React with 👍 / 👎.
|
|
||
| const { cmd, cmdArgs } = buildCommand(args, envs) | ||
| console.log(`[cli] Executing: ${cmd} ${cmdArgs.join(" ")}`) | ||
| const child = spawn(cmd, cmdArgs, { |
There was a problem hiding this comment.
Start the Electron sidecar from a bounded cwd
When the Electron app is launched by the OS rather than a terminal, this spawn inherits the app process cwd because no cwd is set. The sidecar uses its cwd as the default project/config root during startup, so packaged macOS/Linux launches from / or another large directory can stall startup while the server scans the wrong tree before it ever becomes healthy.
Useful? React with 👍 / 👎.
| - uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: opencode-cli | ||
| path: packages/opencode/dist |
There was a problem hiding this comment.
Restore executable bits on downloaded sidecars
The CLI artifact is uploaded as raw packages/opencode/dist and prepare.ts copies .../bin/altimate directly into src-tauri/sidecars. GitHub's upload-artifact docs state downloaded files are restored as 0644, so after gh run download the macOS/Linux sidecar is not executable; Tauri's external-bin build/spawn can then fail with PermissionDenied even though the file exists.
Useful? React with 👍 / 👎.
| if (normalized === original.http.url) { | ||
| server.add(conn) | ||
| } else { | ||
| replaceServer(original, conn) |
There was a problem hiding this comment.
Recreate SDK clients after same-URL credential edits
When the user edits only the credentials or display name for the active server, this same-URL branch updates persisted server data without changing ServerConnection.key. GlobalSDKProvider and SDKProvider are mounted under a keyed boundary keyed only by that URL, so existing event streams and memoized SDK clients keep the old Authorization header until a reload or server switch, making the saved credentials appear not to work.
Useful? React with 👍 / 👎.
The desktop-app adoption removed `packages/app`, `packages/ui`,
`packages/desktop`, and `packages/desktop-electron` from `skipFiles` in
`config.ts` and these packages now exist on disk. `upstream-guard.test.ts`
still asserted they were skipped and absent, causing 12 failures in the
`TypeScript` CI job:
- `skipFiles contains "packages/{app,desktop,desktop-electron,ui}/**"`
- `skipped packages do not exist on disk` for those four
- `no upstream-only artifacts in repo` for those four
Remove the four adopted packages from `expectedSkipPatterns`,
`skippedPackageDirs`, and `forbiddenPackages`. The 10 genuinely-skipped
upstream packages (console, containers, docs, enterprise, extensions,
function, identity, slack, storybook, web) stay asserted.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two more guard assertions still treated the adopted desktop packages as
upstream-only, failing the `TypeScript` (bun test) CI job:
- `upstream-merge-guard.test.ts` "Deleted packages stay deleted": dropped
`packages/{app,ui,desktop,desktop-electron}` from `forbiddenDirs` (they now
exist on disk).
- `build-integrity.test.ts` "config.ts contains critical skipFiles patterns":
replaced `packages/app/**` and `packages/desktop/**` (no longer skipped) with
still-skipped upstream packages (`console`, `enterprise`, `web`).
Completes the test-side cleanup started for the desktop-app adoption.
All 272 branding tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4768201389
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // .and_then(|v| v.as_bool()) | ||
| // .unwrap_or(false); | ||
|
|
||
| Ok(WslConfig { enabled: false }) |
There was a problem hiding this comment.
When a Windows Tauri user has wslEnabled saved, this hard-coded false ignores the value that set_wsl_config writes below and that cli::is_wsl_enabled uses to decide whether to spawn the server in WSL. As a result the setting reports as disabled after a refetch/restart and startup always launches the Windows sidecar with Windows paths instead of honoring the saved WSL integration.
Useful? React with 👍 / 👎.
| "notification:default", | ||
| { | ||
| "identifier": "http:default", | ||
| "allow": [{ "url": "http://*" }, { "url": "https://*" }, { "url": "http://*:*/*" }] |
There was a problem hiding this comment.
Allow HTTPS servers on custom ports
When a user configures an HTTPS server on a non-default port, the Tauri desktop SDK requests go through the HTTP plugin and must match one of these scoped URL patterns. This adds the custom-port wildcard only for http://*:*/*, so https://host:8443/... health and API calls are rejected by the capability scope before reaching the server; add the equivalent HTTPS port wildcard.
Useful? React with 👍 / 👎.
❌ Tests — Failures DetectedTypeScript — 15 failure(s)
Next StepPlease address the failing cases above and re-run verification. |
What does this PR do?
Brings the desktop app into the fork — it was excluded when altimate-code was forked from OpenCode. Adopts the Tauri shell (
packages/desktop, primary) + Electron shell (packages/desktop-electron, secondary) and the SolidJS web UI (packages/app,packages/ui) from upstream v1.2.20, fully rebranded to Altimate Code with no OpenCode leaks, and wired for one-click CI publishing.Highlights:
altimatebinary as theopencode-clisidecar, so every server-side fork capability (SQL, dbt, FinOps, warehouse, altimate-core, the Altimate gateway) is exposed automatically. Sidecar scripts adapted to our build output (@altimateai/altimate-code-*/bin/altimate) + the CI artifact contract.tauri.*.conf.json(productName, identifier, deep-link schemealtimate://, a new updater keypair + AltimateAI endpoints),index.html, webmanifest, AppStream, Cargo crate names,cli.rsWSL install paths (~/.altimate/bin,www.altimate.sh/install), native window title, and i18n (18 locales). Regenerated all icon sets, favicons, PWA icons, NSIS bitmaps, social images, and theLogo/Markcomponents from the Altimate brand mark.altimate-backend) first in the provider picker with a branded icon + an in-app credential arm; removes the OpenCode Zen onboarding and filters out theopencode/opencode-goproviders.keepOurs/requireMarkersso future merges stay branded; added.github/workflows/publish-desktop.yml+ a publishing runbook.Type of change
Issue for this PR
Closes #960
How did you verify your code works?
bun turbo typecheck— app, ui, desktop, desktop-electron all pass.GET /providerreturns the{all, default, connected}shape withaltimate-backend("Altimate AI") present.tauri buildproducedAltimate Code Dev.app+.dmgwith a brandedInfo.plist(name/id/executable all Altimate); launched it and confirmed the shell + sidecar run.Checklist
altimate_changemarkers)packages/desktop/README.md)🤖 Generated with Claude Code