Repository navigation
Name the macOS app NVIDIA PAIR in Finder, the Dock, and the menu bar - #147
Merged
Merged
Conversation
The macOS app showed as PAIR while Windows and Linux showed NVIDIA PAIR. macOS ignores CFBundleDisplayName unless it is localized, and takes the name from the bundle and CFBundleName, both of which were PAIR. electron-builder names the bundle after mac.executableName, so the executable is renamed with it: the app is now NVIDIA PAIR.app, running NVIDIA PAIR. Linux keeps /opt/PAIR, which its nvpair wrapper embeds. An install updated in place keeps the PAIR.app name, so the uninstaller now finds the bundle it ships in and reads its executable name from the bundle rather than assuming either. The README's uninstall path names both. Signed-off-by: Terve <ntervalon@nvidia.com>
Noah-Tervalon-Nvidia
added this pull request to stack #149
October 7, 2026 18:09
7 tasks done
ckelseynv
approved these changes
Oct 7, 2026
6 of 7 tasks
Merged
Noah-Tervalon-Nvidia
added a commit
that referenced
this pull request
Oct 8, 2026
## Stack Targets `sherief/llamacpp-downloads` (#148), which targets `fix/macos-app-name` (#147). Merge those first; this diff is only what sits on top of them. Rebasing onto the llama.cpp branch added fourteen commits to the sixty-nine here: - **Return the terminal interface to develop's** — removes #148's additions to the old interface, which the rebuild deletes or rewrites, so each rebuild commit lands on the tree it was written against. The broker still fronts every engine by default. - **llama.cpp in the rebuilt interface and in `engine:catalog`** (nine commits) — the engine manager serves llama.cpp's Hugging Face catalogue and its search, the desktop relays the query, and the terminal interface gains llama.cpp's model actions and an upstream search. - **Four fixes from review** — a declared pipeline decides whether a model can chat; GGUF draft heads llama.cpp will not pull are skipped; an over-long search is refused rather than shortened; the model browser no longer downloads a row it has stopped showing. ## Description Rebuilds `nvpair-tui` around a node-first tab layout and closes the gaps against the desktop application, then moves the model catalogue into the backend so both front ends browse one implementation. A node is the unit an operator reasons about, so the tabs become Nodes, Jobs, Service, Errors, and Logs, and everything specific to one machine hangs off its row. The ten tabs this replaces spread one machine's facts across four of them: its engines in one, its models in another, its ports in a third, its cluster standing in a fourth. Much of the rest is about claiming no more than the backend guarantees. A port change reports the port actually bound rather than the one requested. Clearing an error is only offered where clearing sticks. The cluster name is labelled as this machine's own. Node presence follows the broker's snapshot instead of a timestamp that never advanced. ## How to review this Seventeen commits, in dependency order. Four are prerequisites, one is the rewrite, and the rest are separable features and fixes. **The rewrite (`5381e2f1`) is 51 files and is best read in the order below** — it follows the data rather than the alphabet. The five views only exist together: the shell sizes them, they share its status line and frame budget, and the tab set *is* the change. Splitting them further would have meant authoring intermediate versions of `nodedetail.go` and `jobs.go` that never existed, so the guided read is offered instead. ### Read the commits in this order 1. `6b377e78` frame cap single-sourced across four hops — 5 files 2. `e99efb05` `engine:catalog` in the engine manager — the `+109k` is the committed Ollama list; review `catalog.go` and `catalog_test.go` 3. `001b0e4a` desktop served from that catalogue — the `−109k` is the same list leaving Electron 4. `c414dbf0` one `inference-dispatcher`, from `cli-bin` 5. `9a61f3de` broker stream errors reported as disconnects 6. `e70fbd21` shared frame primitives (table, status line, row budget) 7. `5381e2f1` **the rewrite** — see the file order below 8. `ec7a6c84` release version stamped for the update notice 9. `144e472f` engine launch-arguments editor, ports onto the same write 10. `32738dcd` documentation and the architecture rule 11. `953a9ab0` CI's staged-binary check learns about the demo client 12. `eaedf559` light-terminal readability 13. `7f5610da` the `requestId` a settings commit requires 14. `67ca9630` background detected before Bubble Tea takes stdin 15. `fbb37e0a` local settings snapshots kept current 16. `94d041d2` leaving the Nodes tab returns to the list 17. `33b5470c` settings verdict taken from the backend Commits 12–17 came from running the build on real hardware. Four of them fix defects in commit 9; each says what the backend actually does, which is worth reading even where the diff is small. ### Read the rewrite's files in this order **Start with the shape** — 400 lines, and the rest follows from it: 1. `ui/ui.go` — the tab set. The whole change in 27 lines 2. `ui/model.go` — the shell: frame budget, tab switching, the notice row 3. `ui/keys.go` — why the bindings are what they are 4. `ui/view.go` *(unchanged)* — the contract the five views implement **The data, before the screens that render it:** 5. `ui/nodeswire.go` — the discovery, cluster, and manual wire shapes 6. `ui/nodesmodel.go` — merging those three feeds onto one key. The heart of the Nodes tab 7. `ui/nodenames.go` — resolving a node id to a name 8. `ui/engineswire.go` — engine status and model inventory shapes **The tabs, simplest first:** 9. `ui/logs.go`, `ui/errors.go` — smallest, and they establish the view idioms 10. `ui/service.go` — worker liveness, log level, data reset 11. `ui/proxystatus.go` — per-engine facade readiness and ports 12. `ui/jobs.go` — the endpoints and live inference work 13. `ui/nodes.go` — the merged node table, filtering, pairing keys 14. `ui/invite.go` — pairing events scoped to their session **The drill-down and its two satellites** — the largest file, read last: 15. `ui/nodedetail.go` — engines and models for one machine 16. `ui/nodetelemetry.go` — the direct `/v1/node-info` poll 17. `ui/catalog.go` — the downloadable-model browser 18. `ui/demo.go`, `ui/demoschedule.go` — the Inference Demo **Then the tests**, which read as the specification: 19. `ui/framebudget_test.go` — no view overflows its frame at any supported size 20. `ui/table_test.go` — every table has columns at construction 21. `ui/nodesmodel_test.go`, `ui/nodedetail_test.go`, `ui/service_test.go` — the per-area behaviour 22. the remaining `_test.go` files **Deletions need no reading**: `cluster.go`, `engines.go`, `health.go`, `manualnodes.go`, `proxies.go`, `settings.go`, `workloads.go` are the tabs the five replace. ## Scope Included: the terminal interface rewrite, its Inference Demo and update notice; `engine:catalog` replacing the Electron-only model hub; one `inference-dispatcher` in `cli-bin`; an editor for an engine's launch arguments with the two port fields moved onto the same revision-checked write; documentation. Excluded: no change to routing, scheduling, or proxy behaviour; none to pairing or trust; the desktop renderer's settings UI is untouched, only its catalogue source moved. ## Validation Everything CI runs, run locally on macOS arm64, Go 1.26.3, Node 22: - `node scripts/spdx-headers.mjs` — 1039 checked, 0 missing - `npm --prefix desktop run verify:build-scripts`, `service-contracts:check`, `typecheck`, `lint`, `dead-code:check`, `test:unit` (230 passing) - `npm --prefix desktop run build:modular-binaries -- --force` — 13 binaries - Go component tests with `-race` across `shared`, `nvpair-tui`, `nvpair-ui-broker`, `nvpair-engine-manager` - `cd services/tests && go test ./... -count=1 -timeout=20m` — passes, no failures - `services/build.sh`, then the staged-binary comparison this branch updates Two failures are pre-existing and reproduce identically on `develop` in a clean worktree: `TestUninstallTerminatesRunningInstance` in `nvpair-engine-manager`, and `services/shared/splitlisten/splitlisten_test.go` is not `gofmt`-clean. Manual, on a Mac and a DGX Spark over SSH: pairing, engine lifecycle, model download, the demo, and the settings editor. The `ui.ReleaseVersion` stamp was read back out of both binary sets, since a `-X` against a wrong symbol path fails silently. Terminal background detection was checked in a pty against a terminal that answers the query and one that ignores it. ## Risk The engine settings path is the part to look at. The two port fields previously wrote through `engine:set-port` and the proxy's own `set-port`, which meant two writers with no shared revision — the last to finish won and neither could tell it had lost. All three fields are now one revision-checked write through `engine:preview-settings` and `engine:apply-settings`. That also makes them editable on a peer, which the old path refused because it had no remote form; whether a given engine accepts the write is the snapshot's `Editable` answer rather than a rule kept in the front end. `jsonrpc.WorkerFrameBytes` raises the inbound frame cap on three hops that carry large worker replies, from the 1 MiB default to 8 MiB. The Ollama catalogue is roughly 1.9 MiB and could not previously cross them; an over-long frame is a terminal read error rather than a dropped message, so the hops have to agree. No wire-format removals, no persisted-data migrations, no change to how nodes authenticate to each other. ## Release intent <!-- pair-release-intent:v1 --> ### Changelog title A rebuilt terminal interface, and one model catalogue for both front ends ### Changelog body - The terminal interface is organised around machines: Nodes, Jobs, Service, Errors, and Logs, with a per-machine drill-down for engines, models, ports, and hardware. - Engines can be started with your own arguments and environment variables, edited from the terminal interface on this machine or on a paired one. Changes are validated before they are saved. - Downloadable models are served by the engine manager, so the terminal interface and the desktop application browse the same catalogue. - The Inference Demo runs from the terminal interface's Jobs tab, so a headless machine can demonstrate routing. - The terminal interface says when a newer release of PAIR is published, on every tab until dismissed. It installs nothing. - Pairing keys follow the words on screen: `p` pairs, `a` accepts, `f` finds a machine by address. ### Bumps - services: major - nvpair-cluster-manager: none - nvpair-engine-manager: minor - nvpair-errors: none - nvpair-job-scheduler: none - nvpair-manual-nodes: none - nvpair-node-info: none - nvpair-node-scanner: none - nvpair-node-settings: none - nvpair-proxy: none - nvpair-tui: major - nvpair-ui-broker: patch - nvpair-workload-manager: none <!-- /pair-release-intent:v1 --> ## Checklist - [x] I have read the [Contributing Guidelines](https://github.com/NVIDIA/Personal-AI-Router/blob/main/CONTRIBUTING.md). - [x] Every commit is signed off (`git commit -s`), certifying the [Developer Certificate of Origin](https://developercertificate.org/). - [x] New or existing tests cover the change. - [x] Relevant documentation is updated. - [x] I checked the diff, changed filenames, and commit messages for credentials, private data, internal URLs, internal issue identifiers, and generated artifacts. - [x] I recorded the validation commands and results above. - [x] I declared version bumps in the release-intent block above. `services/versions.json` is written by automation — do not edit it by hand. --------- Signed-off-by: Terve <ntervalon@nvidia.com>
kjlubick
pushed a commit
that referenced
this pull request
Oct 8, 2026
<!-- pair-release-intent:v1 --> ### Changelog title Safer engine removal and data resets ### Changelog body - Removing an engine never follows a symlink or Windows junction into another folder. - An LM Studio models folder linked from `~/.lmstudio/models` is kept when LM Studio is removed. - Resetting app data, from Settings or the terminal interface, stops before deleting anything if an engine PAIR installed cannot be removed, and says which one. ### Bumps - services: patch - nvpair-cluster-manager: none - nvpair-engine-manager: patch - nvpair-errors: none - nvpair-job-scheduler: none - nvpair-manual-nodes: none - nvpair-node-info: none - nvpair-node-scanner: none - nvpair-node-settings: none - nvpair-proxy: none - nvpair-tui: patch - nvpair-ui-broker: none - nvpair-workload-manager: none <!-- /pair-release-intent:v1 --> ## Summary Review fixes for the work below this in the stack. **Stacked on** `fix/indeterminate-install-progress` (#145), at the top of #147 → #148 → #117 → #150 → #145; merge it after them, and before the stack ships. ### Engine removal (#150) - **Junctions are never followed.** Since Go 1.23 a Windows junction is reported as an irregular file, not a symlink, so the symlink check let the elevated uninstaller read through one and empty the directory it names. Removal now descends only into what `Lstat` reports as a real directory. Two Windows tests use real junctions; CI runs Go tests on Linux only, so they run on Windows machines. - **The model store is matched by file identity.** A store that is a symlink to another folder under the removal target was deleted, and case handling followed the OS rather than the filesystem. `os.SameFile` replaces both. New tests cover a symlinked store, a store two levels down (the first to exercise the recursion), and a store spelled in another case. - `spec.md` now states what removing an engine may delete. ### Resets (#150) The TUI and desktop resets wiped app data even when an engine could not be removed, deleting the only record that it was PAIR's. Both now stop first and say which engine, or that the service is not running. The orchestrator gains its first tests. ### Smaller fixes - "Personal AI Router" → "NVIDIA PAIR" in the uninstall work's user-visible wording. The data path and firewall rule names keep the old name on purpose. - Catalog tests assert the error itself (#117). - Getting started covers the update that renames the macOS app (#147). - Engine lifecycle documents an LM Studio models folder moved inside `~/.lmstudio`. ## Test plan - `go vet` (darwin, linux, windows) and `go test ./...` in `nvpair-engine-manager` and `nvpair-tui`. - `npm run typecheck`, `npm run lint`, `npm run test:unit` from `desktop/`. - `TestUninstallTerminatesRunningInstance` fails locally on macOS identically on `develop`. The Windows junction tests compile but did not run here. --------- Signed-off-by: Terve <ntervalon@nvidia.com>
Noah-Tervalon-Nvidia
pushed a commit
that referenced
this pull request
Oct 9, 2026
…reenshots (#151) ## Description A documentation pass over the release that landed in `develop` with #147, #148, #117, #150, #145, and #153. It covers llama.cpp everywhere the docs list engines, replaces the terminal-interface screenshots of the old ten-tab interface, and corrects docs that no longer match the code. The branch was replayed onto `develop` after the stack was squash-merged, so it carries only the docs commits. <!-- pair-release-intent:v1 --> ### Changelog title n/a ### Changelog body n/a ### Bumps - services: none - nvpair-cluster-manager: none - nvpair-engine-manager: none - nvpair-errors: none - nvpair-job-scheduler: none - nvpair-manual-nodes: none - nvpair-node-info: none - nvpair-node-scanner: none - nvpair-node-settings: none - nvpair-proxy: none - nvpair-tui: none - nvpair-ui-broker: none - nvpair-workload-manager: none <!-- /pair-release-intent:v1 --> ## Scope **Terminal interface screenshots** (`docs/assets/onboarding/terminal-interface/`) - The three existing images showed the ten-tab interface from before #117. They are replaced with renders of the current Nodes, pairing, and Jobs views, at the same size as the originals. - New `04-tui-node-detail.png`: a machine's Engines and Models panes with Ollama, LM Studio, and llama.cpp. It is placed in "Look at One Machine". - The renders drive the real `ui` views and styles with sample data for two machines, so the layout, columns, and footer are the interface's own output. The harness that produced them is not part of this pull request. **Terminal interface capabilities** - `docs/known-issues.mdx` and `docs/engine-lifecycle.mdx` said the terminal interface cannot list or delete models, change ports, manage another node's engines, or show which node ran a job. It does all of these now. The lists keep only what is still true: updating an engine, sending your own inference request, installing PAIR updates, and surviving a dropped connection. - `docs/engine-lifecycle.mdx`: ports and startup arguments can be edited from a paired machine. Only uninstall, update, and CORS settings stay local. - `README.md`: leaving a cluster is `l` on the **Nodes** tab. There is no Cluster tab any more. - `docs/architecture.mdx`: the broker's `ping` is used by the **Service** tab, not a health view. **Terminal interface docs** (`docs/terminal-interface.mdx`, `services/nvpair-tui/README.md`), checked against the code: - It runs a broker and ten workers, not eleven. - While the interface runs, its own logs go to the Logs tab, not stderr. - The engine port, proxy port, and startup arguments are offered on a peer and written through `engine:apply-settings`. Only restart and uninstall are local-only. A peer's screen lists only its engine port, though `p` still changes that peer's proxy port. - Only a plain `go build` skips the update check. Builds made with `services/build.sh` or `services/build.bat` check. - `r` forgets a machine added with `f` without asking, and the "asks first" rule says so. The catalog filter also matches tags and clears with `c`. **llama.cpp coverage** - `README.md`: removed the claim that first-run setup does not preselect llama.cpp. `WELCOME_ENGINE_DEFAULT_SELECTED` preselects it. The macOS install step names **NVIDIA PAIR**. - `docs/getting-started.mdx`: - "Using an Engine's Own Command Line" gives llama.cpp's install path on Windows, Linux, and macOS, its cache, and querying its router on `8081`. The `OLLAMA_HOST` advice is marked as Ollama-only. - Ollama's path table gains a macOS row. - The endpoint table gains a llama.cpp row: OpenAI-compatible paths on `8080`, no Ollama `/api/...` and no Anthropic Messages API. - `docs/troubleshooting.mdx`: a `llama-server` you started yourself on `8080` hides requests from PAIR. The fix is to stop it and reopen PAIR, because the proxy keeps its fallback port until the broker restarts. - `docs/building.mdx` and `docs/engine-lifecycle.mdx`: model folders and example ports include `~/.llamacpp`, `~/.lmstudio/models`, and `8080`. - `services/installer/linux/INSTALL.md` and `services/installer/macos/INSTALL.md`: - Models live in each engine's own store, so deleting the bundle or configuration leaves them. - The bind-conflict note named `11435`, which is Ollama's relocated engine port, not a proxy port. It now lists the three proxy ports. - Linux: the bundle layout lists `nvpair-tui`, and the requirements say `x86_64` or `arm64`, which `installer_build.sh` produces. - Service and developer references that still assumed two engines: - the `nvpair-proxy` README and spec; - the `nvpair-job-scheduler` spec, including the `scheduler:get-status` shape; - the `nvpair-workload-manager` README and spec; - the `llamacpp-proxy` error IDs in `nvpair-errors`; - the `nvpair-ui-broker` README; - `.cursor/rules/proxy-inference-routing.mdc`; - `desktop/docs/services-backend.md`. - `services/nvpair-engine-manager/README.md` lists `engine:uninstall-managed`. `desktop/docs/services-parity.md` marks the pull `percent` as optional, after #145. **Not included** - Desktop screenshots that still show only Ollama, such as `getting-started/01-first-run-setup.png`. They need a capture from the Electron app. - `CHANGELOG.md`, which release-intent automation writes. ## Validation - Swept every `*.md`, `*.mdx`, and `.cursor/rules/*.mdc` file for engine lists that name Ollama or LM Studio without llama.cpp, and for references to the old terminal interface. Each hit was checked against the code. Statements that PAIR adopts an existing Ollama or LM Studio are left alone, because llama.cpp is never adopted. - Each factual change was checked against its source, including the engine manifests, `engines.All()`, `schedule.go`, `engineproxy.go`, `nodedetail.go`, `nodes.go`, `logoutput.go`, `updatecheck.go`, `services/build.sh`, `installer_build.sh`, and `appdir.go`. - A three-model review pass over the branch. Every finding was verified against the code before it was applied. - Reviewed each rendered screenshot against the doc text and alt text it accompanies. - `go vet ./ui/` in `services/nvpair-tui` after the harness was removed. - `python3 scripts/release-intent/validate_pr.py --description-file <this description> --skip-owned-files-check` ## Risk Documentation and images only. No code, build, or versioned file changes. Found while checking and left for a separate change: - In a colour terminal, an unplaced job's RAN ON cell renders as `c…` instead of `choosing`. The styled text is truncated by its byte width, and the tests render without colour, so they do not see it. - A comment in `ui/updatecheck.go` still says "eleven workers". - `nvpair-manual-nodes` probes hand-added nodes for Ollama and LM Studio only, not llama.cpp. - `installer_build.sh` does not copy `inference-dispatcher` into the services tarball, so the Jobs tab's test traffic cannot start from it. ## Checklist - [x] I have read the [Contributing Guidelines](https://github.com/NVIDIA/Personal-AI-Router/blob/main/CONTRIBUTING.md). - [x] Every commit is signed off (`git commit -s`), certifying the [Developer Certificate of Origin](https://developercertificate.org/). - [ ] New or existing tests cover the change. (Documentation only.) - [x] Relevant documentation is updated. - [x] I checked the diff, changed filenames, and commit messages for credentials, private data, internal URLs, internal issue identifiers, and generated artifacts. - [x] I recorded the validation commands and results above. - [x] I declared version bumps in the release-intent block above. `services/versions.json` is written by automation — do not edit it by hand. --------- Signed-off-by: Chris Kelsey <ckelsey@nvidia.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changelog title
The desktop app is called NVIDIA PAIR
Changelog body
PAIR.Bumps
Summary
The macOS bundle shipped as
PAIR.appwithCFBundleExecutableofPAIR, soFinder, the Dock, and the menu bar all showed the abbreviation rather than the
product name.
Setting
productNametoNVIDIA PAIRrenames the bundle, which means theexecutable inside it and every path that names it move too. This change updates
the three places that hardcoded the old name:
desktop/electron-builder.config.ts— the product name and an explicitmac.executableName, so the binary inside the bundle keeps a stable name eventhough the bundle directory now contains a space.
desktop/scripts/build/macos/uninstall.sh— removes the renamed bundle, andthe previous one, so an uninstall after an in-place upgrade does not leave the
old app behind.
scripts/wipe-app-data.shandREADME.md— the paths and prose that name theapp.
The space in the bundle name is the part most likely to break something
downstream. Signing resolves the executable through
CFBundleExecutableratherthan assuming it matches the bundle, which is handled in the build-and-sign
repository.
No service binary changes, so every component bump is
none.Test plan
npm run typecheck,npm run lint,npm run dead-code:check,npm run test:unit,npm run service-contracts:checkfromdesktop/.NVIDIA PAIR in Finder, the Dock, and the menu bar, and that the bundled
nvpairlauncher still resolves.desktop/scripts/build/macos/uninstall.shagainst an installed build andconfirmed both the current and previously-named bundles are removed.