{"slug": "show-hn-run-claude-frontend-with-non-anthropic-models", "title": "Show HN: Run Claude Frontend with Non-Anthropic Models", "summary": "A developer released a local proxy that translates Anthropic's Messages API to OpenAI-compatible backends, letting the stock Claude Code CLI run against OpenAI keys or on-device models such as Ollama's qwen2.5:7b-instruct. The proxy, run via `node supervise.mjs` on 127.0.0.1:8123, was verified end-to-end with the unmodified CLI on a full agent turn using 221 tools, with logs showing model=claude-opus-4-8->gpt-5.6-sol. The accompanying desktop build repoints Anthropic's unpacked Claude Desktop app under a stock Electron runtime but requires users to supply their own licensed copy of the ~40 MB proprietary bundle, which is not redistributable.", "body_md": "Claude Code and Claude Desktop are good agent harnesses. This repo lets you drive their\n**agent layer** with a backend of your choice — an **OpenAI** key, an **on-device model on your\nown GPU**, or stock **Anthropic** — by translating between the Anthropic and OpenAI APIs\nlocally. The chat window is always the real claude.ai web app; only the agent sub-layer\n(Claude Code) is repointed.\n\n*The desktop build's Code tab. The model picker lists **Composite** (the cross-provider fallback\nchain) plus every keyed provider's models as `provider:model` — Gemini, Mistral, nvidia,\nollama-cloud, llm7, freetoken, cloudflare — each routing that turn to its own upstream through the\nproxy; the local usage dashboard summarises agent activity.*\n\nTwo independent things live here, and **you may only want the first**:\n\n|  | What it is | What you need | \n|---|---|---|\n| **1. The proxy** —[`openai-proxy/`](https://github.com/mkornreich/llm-desktop-electron/blob/main/openai-proxy) | A local server that speaks the **Anthropic Messages API** on the front and calls**OpenAI** (or any OpenAI-compatible server) on the back. Point any Anthropic-API client at it. | Node 22+ (native `fetch` ), and an OpenAI key**or** a local server like Ollama | \n| **2. The desktop build** | Anthropic's **Claude Desktop** app, unpacked and run under a stock Electron runtime, with the proxy wired in, a provider switch, and a settings GUI | Linux or macOS, plus **your own copy** of Claude Desktop | \n\nThe proxy is the generally useful part: it works with the ordinary `claude` CLI on any machine,\nand needs nothing from this repo's `app/` directory. Everything below the\n[Desktop build](#2-the-desktop-build) heading is specific to running Anthropic's Electron app.\n\n**Licensing, read this first.** `app/` is Anthropic's unpacked proprietary bundle (~40 MB of\ntheir JavaScript, native binaries and resources). It is **not** redistributable, which is why\nthis repository is private. For part 2 you must supply that directory from your own licensed\nClaude Desktop install — see [Supplying the bundle](#supplying-the-bundle). Nothing in part 1\ntouches it.\n\n```\nexport OPENAI_API_KEY=sk-...              # or point at a local server; see below\ncd openai-proxy && node supervise.mjs     # listens on 127.0.0.1:8123, restarts on crash\n```\n\n`node proxy.mjs` still works and is right for a one-off. Prefer the supervisor for anything you\nleave running: the proxy has taken itself down on a dropped upstream socket with nothing\nnoticing for hours — the app, the launcher and every agent kept running against a closed port.\nThe supervisor restarts it, logs why, and gives up loudly rather than looping if it cannot start.\n\nPoint the stock Claude Code CLI at it:\n\n```\nANTHROPIC_BASE_URL=http://127.0.0.1:8123 ANTHROPIC_API_KEY=unused claude -p \"hello\"\n```\n\n`ANTHROPIC_API_KEY` must be set to *something* because the CLI insists on it, but the proxy\nignores it and authenticates to OpenAI with `OPENAI_API_KEY`.\n\nVerified end-to-end with the unmodified CLI: a full agent turn, 221 tools, tool calls executed\nand results fed back, with the proxy log showing `model=claude-opus-4-8->gpt-5.6-sol`. The CLI\nstill *says* \"Opus\" because it hardcodes that in its own prompt; the proxy log and the\n`ANTHROPIC_BASE_URL` in the process environment are the ground truth.\n\nSet `OPENAI_BASE_URL` at any OpenAI-compatible server ([Ollama](https://ollama.com),\nllama.cpp, LM Studio, vLLM) and the proxy translates to it exactly as it does to OpenAI. A\n**loopback** base URL is treated as **keyless** — no `OPENAI_API_KEY` needed (a placeholder\nbearer is sent, which local servers ignore):\n\n```\nOPENAI_BASE_URL=http://127.0.0.1:11434/v1 OPENAI_MODEL=qwen2.5:7b-instruct OPENAI_API=chat \\\n  node openai-proxy/proxy.mjs\n```\n\nThe desktop build wires this up as the [`local` provider](#on-device-model-local), which also\nstarts and tunes Ollama for you.\n\nEverything has a default; set only what you want to change.\n\n| Variable | Default | What it does | \n|---|---|---|\n| `OPENAI_API_KEY` | — | Required for a remote endpoint; **optional for a loopback one** . Also read from`.openai-key` (see below). | \n| `OPENAI_MODEL` | `gpt-5.6-sol` | Any model id. Names containing `codex` route to**Responses** automatically; anything else lands on Chat Completions unless`OPENAI_API=responses` is set. | \n| `OPENAI_BASE_URL` | `https://api.openai.com/v1` | Any OpenAI-compatible endpoint, including a local one. | \n| `PORT` | `8123` |  | \n| `OPENAI_API` | `responses` | `responses` or`chat` , overriding the name heuristic.**Required for a non-codex remote model** : this app sends 200+ tools and Chat Completions caps at 128. | \n| `OPENAI_REASONING_EFFORT` | `max` | The proxy steps down to the highest value your model accepts. | \n| `OPENAI_CLASSIFIER_SAFETY_MODEL` | `gpt-5.4-2026-03-05` | Model for Claude Code's auto-mode safety verdict. It has a 60-second deadline and **denies the action** when it expires, so this wants a fast model — see[openai-proxy/README.md](https://github.com/mkornreich/llm-desktop-electron/blob/main/openai-proxy/README.md) for the measurements. | \n\nThe full list — around thirty knobs, each documented with the bug that motivated it — is in\n[openai-proxy/README.md](https://github.com/mkornreich/llm-desktop-electron/blob/main/openai-proxy/README.md).\n\nInstead of environment variables, put `KEY=VALUE` lines in `.openai-model` at the repo root\n(checked in, for non-secrets). The secret **API key** goes in its own gitignored file,\n**`.openai-key`** (` apiKey=…`; copy `.openai-key.example` to start). Precedence is\nenv var → `.openai-model` → `.openai-key` → default.\n\nThat precedence lives in exactly one place, [`openai-proxy/config.mjs`](https://github.com/mkornreich/llm-desktop-electron/blob/main/openai-proxy/config.mjs),\nwhich also produces the config hash below. To see what a launch would actually use:\n\n```\nnode openai-proxy/config.mjs            # the effective config, with the source of each value\n```\n\n`--hash` prints just the config hash; `--validate` checks it and exits non-zero on an error. The\nkey never appears in any of them — only a `sha256:` fingerprint of it.\n\n`/health` reports an identity, not just liveness:\n\n```\n{ \"ok\": true, \"instance\": \"8f3c…\", \"pid\": 82110, \"configHash\": \"38d9c8b2…\",\n  \"codeVersion\": \"a8e08f01…\", \"inflight\": 0, \"model\": \"gpt-5.6-sol\", \"…\": \"…\" }\n```\n\n- **`instance`** is a nonce generated at startup and also written to` proxy-runtime.json` . Only\nthe process that generated it can serve it, so matching both proves ownership — which is what\nlets the launcher restart*its* proxy and refuse to touch anything else.\n- **`configHash`** covers every behaviour-affecting setting plus a fingerprint of the key. The\nlauncher compares it and restarts a proxy serving stale settings, instead of the old behaviour\nwhere any answer on`/health` counted as healthy and a changed model silently did not apply.\n- **`codeVersion`** is a hash of the proxy sources, catching a process running older translation\nlogic with current settings.\n- **`inflight`** is how many turns are being answered right now, so a restart can say what it\nwould interrupt.\n\nSupervisor behaviour is tuned by environment variable; the defaults are meant to be left alone.\n`PROXY_MAX_FAST_FAILURES` (8) bounds immediate restart attempts, `PROXY_RESTART_BASE_MS` (500)\nand `PROXY_RESTART_MAX_MS` (30000) set the backoff, and `PROXY_HEALTH_EVERY_MS` (15000) /\n`PROXY_HEALTH_TIMEOUT_MS` (5000) / `PROXY_HEALTH_MISSES` (3) control the watchdog that replaces a\nproxy holding the port without answering. An externally-killed proxy always restarts and never\ncounts toward the give-up bound.\n\n|  | Chat Completions | Responses | \n|---|---|---|\n| Tool limit | **128** (129 → HTTP 400) | none observed — probed to 512 | \n| Reasoning controls | none | `reasoning.effort` , reasoning summaries | \n| Local-server support | universal | recent Ollama; not all local servers | \n\nClaude Code offers 27 tools; Claude Desktop offers over 200. On the chat surface the 128-tool cap is real, so the proxy keeps the essential tools (read/write/edit/run/search/plan/web, plus renderers) and fills the remainder in the agent's own order, logging what it dropped — a silently truncated tool list is indistinguishable from a model that just declined to use a tool.\n\nText and tool calls in both directions, streaming and non-streaming, plus fixes for behaviour differences between the two model families:\n\n- **Rendering** — GPT models emit`\\(…\\)` /`\\[…\\]` for maths, which Claude's renderer shows\nliterally. Rewritten to`$…$` /`$$…$$` , fence-aware so code samples stay verbatim.\n- **Persistence** — GPT models routinely end a turn to check in (\"If you want, I'll run that\nnow\"), which in an agent loop reads as a stall. An explicit persistence directive plus\nauto-continue when a turn announces an action and calls no tool.\n- **Context overflow** — Claude Code sizes its own auto-compaction from the model it*thinks* it\nis talking to, so it never fires. The proxy compacts and retries, including when the overflow\narrives as a mid-stream event rather than an HTTP 400.\n- **Empty and truncated turns** — retried or resumed rather than surfaced as a blank reply.\n- **Unsupported parameters** — the CLI sends`stop_sequences` , which some models reject. The\nproxy drops whichever parameter the API names and retries, so the next unsupported knob\nself-heals too.\n- **Thinking** — OpenAI reasoning*summaries* are mapped to Anthropic thinking blocks. Raw\nchain-of-thought is not available from the API at any setting.\n\n**Images work.** Anthropic `{type:\"image\", source:{…}}` blocks — base64 or url — become\n`image_url` on Chat Completions and `input_image` on Responses. If the model has no vision the\npicture is dropped and the question kept, with an honest note in its place, rather than failing\nthe turn.\n\nKnown gaps: `/v1/messages/count_tokens` is estimated, and the cost figures the CLI prints are\ncomputed from Anthropic's price list and are meaningless when proxied.\n\n```\nnpm run eval           # run the corpus, diff against the frozen baseline\nnpm run eval:freeze    # overwrite the baseline with what the code does NOW\n```\n\n`eval/` holds a synthetic, **network-free** corpus. Each case runs through the real proxy against\na fake upstream, and what gets recorded is the payload the proxy **decided** to send: resolved\nmodel, tool count, whether a late tool survived, `tool_choice`, hint injection, reasoning,\nverbosity, output cap, cache key, pricing tier. Every one of those decisions is settled before a\ntoken is generated, which is what makes it a baseline rather than a sample. `npm test` checks the\nfrozen baseline on every run, so a behaviour change cannot arrive unnoticed inside an unrelated\ncommit. Alongside it are invariants the baseline is **not allowed to encode away** — a classifier\nis never given tools, a safety verdict never resolves to the main model, an agent turn that\nmerely quotes the classifier contract keeps its full catalogue.\n\n```\nnpm test        # the whole suite: proxy + settings + launcher scripts + eval baseline\n```\n\nMost were written against a specific misbehaviour; the comments say which.\n\nRuns Anthropic's Claude Desktop (`@ant/desktop` v1.24012.9, an Electron Forge + Vite build) from\nits unpacked `app.asar` under a stock Electron runtime, with the proxy wired into the agent\nlayer, a provider switch, and a settings GUI over the launcher's config. Runs on **Linux and\nmacOS**.\n\n`app/` **is** committed in this repository — Anthropic's code — and that is exactly why it is\nprivate and must not be published. `node_modules/` and `app/node_modules/` are **not** committed;\nyou populate them from your own licensed install.\n\nTo rebuild `app/` from your own install:\n\n1. Find `app.asar` in your Claude Desktop installation:\n  - **Linux:**`/usr/lib/claude-desktop/resources/`\n  - **macOS:**`/Applications/Claude.app/Contents/Resources/`\n2. `npx @electron/asar extract app.asar app/`\n3. Merge the sibling `app.asar.unpacked/node_modules/` back in over`app/node_modules/` — the\nnative addons (`@ant/claude-native` ,`node-pty` , and on macOS`@ant/claude-swift` ) live there\nas platform binaries and the app expects them beside their JS. On Linux,`@ant/claude-swift` is absent by design and fails closed.\n\nThe version here is `@ant/desktop` v1.24012.9; a different version will not necessarily match the\nenv-gated patches, which are keyed to strings in this build.\n\n```\nnpm install        # stock Electron 42.9.2, once\n./run.sh\n```\n\nElectron **42.9.2** is pinned to match the native modules harvested from a current Claude Desktop\ninstall. A window opens on the `claude.ai` login screen; sign in as usual. `run.sh` re-creates the\n`resources/app.asar → app/` symlink and reinstalls the `disclaimer` helper on every launch, so\nreinstalling `node_modules` costs nothing.\n\n**Linux note:** `npm install` downloads the Electron binary from GitHub's release CDN. If that host\nis blocked on your network, use a mirror:\n\n```\nELECTRON_MIRROR=https://registry.npmmirror.com/-/binary/electron/ \\\n  ELECTRON_CUSTOM_DIR=\"v{{ version }}\" node node_modules/electron/install.js\n```\n\n`run.sh` is cross-platform (`uname -s`): it resolves the Electron resources dir (` dist/resources`\non Linux, `Electron.app/Contents/Resources` on macOS), the Claude Desktop profile\n(`~/.config/Claude` vs `~/Library/Application Support/Claude`), and the `disclaimer` helper path\nper platform. See [LINUX.md](https://github.com/mkornreich/llm-desktop-electron/blob/main/docs/LINUX.md) for the port's design and the folded-conditional gotchas.\n\nOn modern Ubuntu (`kernel.apparmor_restrict_unprivileged_userns=1`) Electron's namespace sandbox is\nblocked, so it needs a **setuid-root `chrome-sandbox`** — which the downloaded Electron isn't. `run.sh`\nself-heals this by symlinking to the setuid-root `chrome-sandbox` the Claude Desktop `.deb` already\ninstalled (same Electron build, byte-identical), keeping the renderer sandbox **on with no `sudo`**. If\nno system copy matches, it prints the one-time `sudo chown root:root … && sudo chmod 4755 …` fix.\n\nStock Electron has no equivalent of Claude Desktop's TCC-attribution helper, and the app invokes\nit as `disclaimer <command> <args…>` — which makes it the one supported boundary at which this\nrepo can choose the agent's *internal* model identity. The helper is repository-owned source\n([scripts/claude-code-disclaimer.sh](https://github.com/mkornreich/llm-desktop-electron/blob/main/scripts/claude-code-disclaimer.sh)), installed as an absolute\nsymlink by [scripts/install-disclaimer.mjs](https://github.com/mkornreich/llm-desktop-electron/blob/main/scripts/install-disclaimer.mjs) at the per-platform\n`Helpers/` path. In OpenAI/local mode it rewrites the bundled Claude Code executable to the\nconfigured `[1m]` capability identity; in Anthropic mode it is an exact argv passthrough. It also\nexports `CLAUDE_CODE_SUBAGENT_MODEL` so `Task`/` Explore`/teammate spawns — which run inside the\nsession process and have no argv to rewrite — get the right model. Nothing patches the Claude Code\nbinary or the app bundle.\n\nLinux or macOS, and Node ≥ 22 (Electron 42 bundles its own Node for the app itself). The app refuses to start below Electron 34.\n\n```\n./settings.sh\n```\n\nEvery parameter, grouped, each with the reasoning behind it, plus live status and **Save &\nrestart**. The status line separates **configured** from **active**: a proxy serving previous\nsettings is labelled `STALE — restart to apply`; a launch-time `OPENAI_MODEL=x ./run.sh` override\nis shown as an override; and in Anthropic mode the OpenAI-only settings are dimmed as not in\neffect. **Save & restart** stops the app (it holds an exclusive LevelDB lock on the session\nstore), waits for exit, stops the proxy *only if it can prove the proxy is ours*, relaunches, and\nconfirms the new proxy reports the expected config hash before reporting success. It is a local\n`127.0.0.1` page requiring a per-start token, because the API writes config and can restart the\napp.\n\nTwo modes: **`anthropic`** (Claude, direct) or **` proxy`** (via the local translation proxy). In\nproxy mode a single **`DEFAULT_PROVIDER`** (in [`.provider`](https://github.com/mkornreich/llm-desktop-electron/blob/main/.provider)) picks the upstream that\nbacks the default turns, the background classifier and compaction — one of `openai`, `local`\n(on-device Ollama), `openrouter`, `cohere`, `gemini`, `mistral`, `groq`, `ollama` (Ollama Cloud) — and any *individual* turn can run on a\ndifferent provider by picking a `<provider>:<model>` from the Code-tab model dropdown (the proxy\nroutes that turn to the named provider's key in `.openai-key`).\n\n```\n./run-proxy.sh        # via the local translation proxy — upstream = DEFAULT_PROVIDER in .provider\n./run-anthropic.sh    # Claude, calling Anthropic directly (stock behaviour)\n./run.sh              # whichever .provider says (default: proxy, upstream openai)\n```\n\nAn old `PROVIDER=openai|local|openrouter|cohere|gemini|mistral|groq|ollama` (in `.provider` or on the command line)\nstill works — it selects proxy mode with that provider as the default upstream.\n\n|  | `anthropic` | `proxy` | \n|---|---|---|\n| agent's `ANTHROPIC_BASE_URL` | `https://api.anthropic.com` | `http://127.0.0.1:8123` | \n| translation proxy | not started | started, health-checked | \n| backend the proxy calls | — | the `DEFAULT_PROVIDER` upstream (OpenAI / on-device Ollama / OpenRouter / Cohere / Gemini / Mistral / Groq / Ollama Cloud), plus any provider picked per-turn | \n| API key | your Anthropic login | per upstream — `.openai-key` (remote) or none (loopback Ollama) | \n| model | Claude, as shipped | the upstream's model, or a `<provider>:<model>` chosen in the dropdown | \n\nSwitching needs **no un-patching**: every edit to the bundle is env-gated\n(`PROXY_ANTHROPIC_BASE_URL || <original host>`), so with the variable unset the app uses its own\nAnthropic host. This affects only the **agent** — the chat window is the remote claude.ai web app\ntalking to Anthropic in every mode.\n\n`DEFAULT_PROVIDER=local` points the proxy at a local OpenAI-compatible server instead of\napi.openai.com, so the agent runs on **this machine's GPU** with no API key. It uses\n[Ollama](https://ollama.com), configured in [`.local-model`](https://github.com/mkornreich/llm-desktop-electron/blob/main/.local-model). The Code-tab dropdown\nalso lists your installed Ollama *thinking* models as `local:<model>` (discovered at launch), so\nyou can run a single turn on-device from any mode.\n\n**It manages Ollama for you.** The agent's prompt (system + up to 128 tools) is large, and a\nsystem Ollama is usually pinned to a small context by its service unit — which would silently\ntruncate that prompt, and can't be rebound without root. So `run.sh` starts its **own** Ollama on\na side port (`OLLAMA_MANAGED_PORT`, default 11435) with a big context, sharing the models you\nalready pulled, and leaves the system Ollama untouched for its other clients. It reuses that\ninstance across launches and restarts it only when the context changes. The GPU tuning that makes\na big context fit a laptop card is applied automatically: **`q8_0` KV cache** (≈half the VRAM of\n`f16`), **flash attention**, and **` OLLAMA_NUM_PARALLEL=1`** so the *full* context goes to the\nsingle agent request instead of being split across slots. Set `OLLAMA_AUTOSTART=0` to manage the\nserver yourself.\n\n- **Context is per model.**`OLLAMA_CONTEXT_LENGTH` is the default;`CONTEXT_<model>` overrides it\nper model (a bigger native window can take more; a large model needs less to fit VRAM). The\ncompaction window follows automatically —`run.sh` derives`CLAUDE_CODE_AUTO_COMPACT_WINDOW` as\n3/4 of the context in effect, so switching models keeps a sane window (override per model with`COMPACT_<model>` , or globally with an explicit`CLAUDE_CODE_AUTO_COMPACT_WINDOW` ).\n- **A tool-calling model is required.** The agent is tool calls end to end, so the model must emit\nproper OpenAI`tool_calls` , not dump them as text.`qwen2.5:7b-instruct` (32K),`qwen3:8b` (40K)\nand`granite4.1:8b` (128K) do;`qwen2.5-coder:7b` does not. Pull it first (`ollama pull <model>` ).\n\n`local` speaks Chat Completions by default (universal across local servers; its 128-tool cap also\nhelps a small model fit context). A 7-8B model on a laptop GPU is far less capable than the hosted\nmodels — this is for privacy/offline use, not maximum quality.\n\nDot files at the repo root, all read by `run.sh` (or the proxy), each overridable by an env var of\nthe same name.\n\n| File | Setting | Default | Effect | \n|---|---|---|---|\n| [`.provider`](https://github.com/mkornreich/llm-desktop-electron/blob/main/.provider) | `PROVIDER` | `openai` | `openai` = agent via the proxy to api.openai.com;`local` = proxy → on-device GPU model;`anthropic` = agent calls Anthropic directly | \n| [`.openai-model`](https://github.com/mkornreich/llm-desktop-electron/blob/main/.openai-model) | `OPENAI_MODEL` and friends | `gpt-5.6-sol` | Everything in the proxy config table above, for `openai` mode | \n| [`.local-model`](https://github.com/mkornreich/llm-desktop-electron/blob/main/.local-model) | `OPENAI_MODEL` , context, Ollama tuning | Ollama `qwen2.5:7b-instruct` | The local server, model, per-model context and GPU tuning for `local` mode | \n| `.openai-key`*(gitignored)* | `apiKey` | — | The secret OpenAI key for `openai` mode. Not read in`local` /`anthropic` . Copy[`.openai-key.example`](https://github.com/mkornreich/llm-desktop-electron/blob/main/.openai-key.example) to start | \n| [`.privacy`](https://github.com/mkornreich/llm-desktop-electron/blob/main/.privacy) | `DISABLE_TELEMETRY` | `1` | Turns off first-party telemetry and Sentry, and sinkholes the analytics hosts | \n| [`.sync`](https://github.com/mkornreich/llm-desktop-electron/blob/main/.sync) | `SYNC_CLAUDE_SESSIONS` ,`SYNC_CLAUDE_GROUPING` | `1` | Shares Claude Desktop's sessions and sidebar grouping with this build | \n\n`user-data/` is an isolated profile with **one deliberate exception**: `claude-code-sessions` is a\nsymlink to the real install's store, so sessions are genuinely shared and written in both\ndirections ([issue #3](https://github.com/mkornreich/llm-desktop-electron/issues/3)). A session created or renamed in either app is immediately the other's —\nand deleting one deletes it for both. Everything else in `user-data/` (cookies, caches, Local\nStorage) stays private to this build. Local Storage deliberately is **not** shared: LevelDB\npermits one process at a time, so a shared directory would stop the second app from opening its\nown UI state.\n\nThe app runs unpackaged (`app.isPackaged === false`).\n\n**Works:** launches and stays up on Linux and macOS, renders the real Claude web UI, native addons\nload, the desktop↔web IPC bridge is valid on the `claude.ai` origin, the agent runs through the\nproxy on OpenAI or a local GPU model, telemetry is off (the `isolated-segment` beacon shows\n`ERR_BLOCKED_BY_CLIENT`), and sessions sync with the real install.\n\n**Cannot work outside the signed bundle** — auto-update, `claude://` deep links, and anything\ngated on macOS entitlements the stock Electron binary does not carry (the VM/\"virtualization\"\nfeatures, which the app reports as unavailable). On Linux the macOS-only pieces (global hotkeys,\nmemory-pressure telemetry from `@ant/claude-swift`, computer-use frame streaming) are absent by\ndesign and fail closed; the launcher polyfills the few macOS-only Electron APIs the bundle calls\nunconditionally at startup so it boots cleanly.\n\nAlarming lines appear on **every** launch, including healthy ones — startup races, an\n`@ant/claude-swift`/office365 `ERR_MODULE_NOT_FOUND` (macOS-only modules, fail closed), Wayland\nbinding warnings, and `MISSING_TRANSLATION` / \"Cowork not supported\" notices. To tell whether a\nlaunch actually succeeded, grep for these:\n\n```\ngrep -c \"isLoggedOut: false\" boot.log      # signed in\ngrep -c \"loaded .* features\" boot.log       # feature flags fetched\n```\n\nDelete `user-data/`.\n\n| Document | What's in it | \n|---|---|\n| [openai-proxy/README.md](https://github.com/mkornreich/llm-desktop-electron/blob/main/openai-proxy/README.md) | The proxy in depth: every knob, every behaviour fix and the bug that motivated it, the classifier measurements, the compaction ladder | \n| [SESSIONS.md](https://github.com/mkornreich/llm-desktop-electron/blob/main/docs/SESSIONS.md) | How the desktop build relates to your real Claude install: shared memory, session sync, and why grouping behaves as it does | \n| [ARCHITECTURE.md](https://github.com/mkornreich/llm-desktop-electron/blob/main/docs/ARCHITECTURE.md) | How the desktop app is built: which UI is local vs. remote, the IPC bridge the remote page uses, which layers can be modified | \n| [ANTHROPIC_ENDPOINTS.md](https://github.com/mkornreich/llm-desktop-electron/blob/main/docs/ANTHROPIC_ENDPOINTS.md) | Every Anthropic endpoint the app calls, with payloads and what each is for | \n| [FINDINGS.md](https://github.com/mkornreich/llm-desktop-electron/blob/main/docs/FINDINGS.md) | Notable things in the bundle — permission modes, the extension registry, companion-hardware pairing, the auto-approved tool list | \n| [LINUX.md](https://github.com/mkornreich/llm-desktop-electron/blob/main/docs/LINUX.md) | The Linux port: native-module harvest, folded-conditional blockers, and what degrades off macOS | \n| [OPENROUTER.md](https://github.com/mkornreich/llm-desktop-electron/blob/main/docs/OPENROUTER.md) | The OpenRouter provider: catalog analysis on a free-tier key, the 11-model free allowlist, quality/speed ordering, and how the allowlist gates the pickers and gateway discovery | \n\nThe proxy is self-contained (`openai-proxy/`, plus `settings/` for the GUI) and carries no\nAnthropic code, so it can be developed and shared independently of the bundle. If you extend it,\nadd a test naming the behaviour you fixed — that is the convention throughout, and it is what\ncaught several regressions in the fixes themselves.", "url": "https://wpnews.pro/news/show-hn-run-claude-frontend-with-non-anthropic-models", "canonical_source": "https://github.com/mkornreich/llm-desktop-electron", "published_at": "2026-10-09 01:34:15+00:00", "updated_at": "2026-10-09 01:46:57.965700+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "large-language-models", "ai-tools"], "entities": ["Claude Code", "Claude Desktop", "Anthropic", "OpenAI", "Ollama", "Electron", "qwen2.5:7b-instruct", "gpt-5.6-sol"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-run-claude-frontend-with-non-anthropic-models", "markdown": "https://wpnews.pro/news/show-hn-run-claude-frontend-with-non-anthropic-models.md", "text": "https://wpnews.pro/news/show-hn-run-claude-frontend-with-non-anthropic-models.txt", "jsonld": "https://wpnews.pro/news/show-hn-run-claude-frontend-with-non-anthropic-models.jsonld"}}