mirror of
https://github.com/alphaXiv/OpenResearch.git
synced 2026-10-02 09:44:51 +08:00
dccc6a2ce0fff38c47cd2264d1e71b0b649ac15b
8
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
76c671c3b2 |
OR-215 Use GitHub CLI for publishing (#254)
* OR-215 use GitHub CLI for publishing * Isolate dev slot caches * Color GitHub CLI connection states |
||
|
|
f27c858c78 |
Simplify the DMG installer window to a plain drag-to-install (#228)
Drop the brand mark from the DMG background so the window is just the app icon, an arrow, and the /Applications alias. With the mark gone the window shrinks 640x400 -> 640x320 and the icon row recenters (86px above the icons, 86px below the labels). The arrow now sits level with the icon centers (ICON_Y) and is centered on the icons' artwork gap rather than their 128px cells, since the two icons inset their art by different amounts. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
45eb162e19 |
Focus the existing dashboard tab on a Dock click (#212)
The macOS app's Dock-icon handler called `open <url>` on every click. Once the dashboard SPA has navigated off `/` the browser has no exact-URL match, so the click opened a new tab instead of returning to the dashboard already open. The handler now tries, best first: the exact tab via a JXA script over the scriptable browsers; the browser window itself, when a tab still holds its `/api/events` stream (the only way to infer an open dashboard in Firefox, which exposes no tab API); and finally the plain open it did before. A repeat click within five seconds takes the plain open, so a raise that did not surface the dashboard is always recoverable — the app's port is ephemeral, and the user has no other route back to it. JXA rather than AppleScript because it resolves each browser's terminology at run time: an AppleScript `using terms from` block fails to compile on a Mac without Chrome installed. Signing now passes macos/entitlements.plist, since the hardened runtime denies the Apple event outright without it — a failure that appears only in signed builds, never in a local one. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e9d322e547 |
Add lightweight project briefs and refresh onboarding (#200)
* feat: add lightweight project briefs and onboarding refresh * fix: refine onboarding and project workspace interactions * fix: harden demo onboarding state * fix: satisfy current clippy lint * chore: bump version to 0.1.105 |
||
|
|
46d359ff5e |
OR-173 Make the macOS app find harnesses, and stop it fighting a CLI install (#195)
* fix(app): detect harnesses in the macOS app by adopting the shell PATH Finder launches OpenResearch.app through launchd, so the bundle starts with PATH=/usr/bin:/bin:/usr/sbin:/sbin and no shell rc ever sourced. Harness detection resolves `claude`/`codex`/`opencode` off that PATH, so a DMG user with a Homebrew, nvm, or npm-global install saw every harness missing -- including an already-signed-in Claude Code session -- while a terminal `orx up` on the same machine detected them all. App mode now probes the user's shell once at startup and installs the result via `local::search_path`, which harness lookup and harness children consult instead of the process PATH. The probe is `$SHELL -ilc`: interactive, because zsh reads .zshrc only for interactive shells and that is where PATH edits live -- a login-only probe missed the ~/.local/bin holding `claude`. It runs an inner `sh -c` so fish reports a colon-separated PATH rather than its own list-valued $PATH, fences the value between per-probe nonce markers so rc-file chatter cannot forge one, and requires an absolute directory in the result. Best-effort throughout: a 5s cap, and every outcome logged. The five detection probes that spawned a harness binary with no env prep (`bin_version`, `claude_accepts_ultracode`, `claude_list_models`, `codex_model_list`, `run_models`) now go through `prepare_env` like `probe_auth` already did. Without that, a node-shebang install resolves via the hydrated PATH but its `--version` fails for want of `node`, and `gate_oauth_version` turns the missing version into `Unknown` -- telling a signed-in user to sign in. Verified end to end: a bundle run under launchd's PATH with an isolated ORX_DATA_DIR reports Claude Code agentReady, and finds a `codex` reachable only through the hydrated PATH -- codex has no fallback drop-location, so that resolution can only have come from the override. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(app): stop the bundle and a CLI install from conflicting Three ways the DMG app and a curl-installed `orx` stepped on each other. `orx delete` takes an exclusive lifecycle lock and refuses when another OpenResearch process holds it -- "Close `orx up`, `orx serve`, and any active runs before trying again." CLI `orx up` holds the matching read lock via `dispatch`, but app mode returns from `main` before `dispatch` runs, so it held nothing: `orx delete` from a CLI install would wipe the store out from under a live app. App mode now takes the same read lock for the life of the process. Agents shell out to `orx`, and `chat::prepare_env` prepends the running executable's directory so they get THIS build. In the bundle that directory held only `OpenResearch`, so `orx` fell through to whatever CLI the user had -- a different version, or nothing at all for a DMG-only user. The bundle now ships `Contents/MacOS/orx` as a symlink to the executable. Verified the symlink survives `codesign --options runtime` + `--verify --strict`. That alias would otherwise open a second dashboard, since a bare `orx` reaches app mode's no-arguments condition. `launched_as_app_bundle` now also requires that the process was invoked under the bundle executable's own name, which keeps the alias a plain CLI while leaving a direct run of the executable path -- the documented way to watch the app's logs -- in GUI mode. macOS `current_exe` reports the launch path symlink and all, so it is canonicalized before the comparison. Verified safe already, and left alone: the app binds an ephemeral port rather than 4791; the store is WAL with a 5s busy timeout; and run supervisors hold a per-run exclusive lock, so a second server recovering the same active run exits instead of double-driving it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(app): address review of the PATH and coexistence work Blocker: `launched_as_app_bundle` had grown real logic -- four distinct outcomes turning on a subtle platform fact -- with no coverage, and being macOS-gated it could not get any, since CI runs ubuntu only. The decision is now the pure `is_bundle_exe_launch(exe, argv0)`, compiled everywhere and table-tested for all four cases; only the process-state read stays gated. Same seam `extract_path` uses. The lifecycle lock moves ahead of the PATH probe. Taking it afterwards left the store unprotected for however long the probe ran -- up to 5s -- which is exactly when an `orx delete` racing app startup would land. A failed `read()` is now logged too; it was the one outcome in this module that could pass silently, and silently is how the app would then run unlocked. `orx delete` blocks on a running app now, so its message says so instead of naming three things the user isn't running. Probe diagnostics split the timeout from the spawn failure and name the shell, since "which shell, and did it hang or fail to start" is the whole question a report needs to answer. `bin_version` gets the `NO_COLOR` guard its siblings already carry: it feeds `parse_version`, an escape-laden version line parses as `None`, and `gate_oauth_version` turns that into `Unknown` -- reproducing the bug this branch exists to fix, via the synced env the probe now inherits. `find_opencode` skips empty PATH components, matching `find_on_path`. Packaging asserts the `orx` symlink survived staging. Every step preserves it today, but a dereferencing copy would fail silently -- the app builds, signs, notarizes and runs, and agents just quietly fall back to the user's own CLI. Docs: corrected the same overstatement in `search_path`'s module doc that DISTRIBUTION.md already fixed, and recorded the known gap that the probe imports only PATH, so `ORX_DATA_DIR`/`XDG_*` from a shell rc reach the CLI but not a Finder-launched app. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(app): import the shell's data and config dirs, not just PATH `ORX_DATA_DIR`, `XDG_DATA_HOME`, and `XDG_CONFIG_HOME` exported from a shell rc reached the CLI but never a Finder-launched app, so the two resolved different directories: the dashboard opened the default database while `orx` on the same machine used the user's, and the lifecycle lock added last commit guarded a file neither of them shared. Same root cause as the PATH bug, one layer down. `search_path` generalizes to `shell_env`: an allowlist (`IMPORTED`) the startup probe reads, NUL-separated so a directory can contain anything but NUL, behind `var()` -- process environment when no override is installed, so nothing changes for a terminal `orx`. `store::env_path` and `config::config_dir` are the only two places that resolve these, and both now go through it. The allowlist stays short on purpose: these are the variables whose divergence makes the app and the CLI act like separate installs, while credentials already reach harness children via `prepare_env`. This moves the lifecycle lock back after the probe, undoing last commit's reordering. The reason is now structural rather than incidental: the lock path is derived from `config_dir()`, so taking it before the probe would lock the default path while the CLI locks the user's -- protecting nothing. A correct lock a few hundred milliseconds late beats an immediate lock on the wrong file. Verified with the app's process environment stripped of both variables while the probe shell exported them: `orx.db` lands in the shell's data dir and `orx.lifecycle.lock` in the shell's config dir, and the HOME-derived defaults are never created. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(app): carry the imported environment into orx child processes Importing the shell's directories fixed the app's own resolution but not its children's, which made things worse rather than better for anyone exporting `ORX_DATA_DIR`. `spawn_detached_supervise` and the harness adapters spawn plain CLI processes; those never probe, so they re-resolved to the default store while the app read the user's. A run launched from the app would have been supervised against a different database than the dashboard was showing — before this branch both landed on the default and at least agreed. `shell_env::export_to` hands the imported variables to a child. `prepare_env` applies it, so an agent's `orx exp run` shares the dashboard's store, and `spawn_detached_supervise` applies it plus PATH, which also reaches the run payload it launches (`localbox` spawns `bash run.sh` with an inherited env, so it was losing the user's PATH entirely — no `python`, no `uv`, no `conda`). `opencode::spawn_agent` had hand-rolled a copy of `prepare_env`; it now calls it rather than growing a third copy of the same fix. Verified with the app's process environment stripped of both variables while the probe shell exported them: a harness child is handed ORX_DATA_DIR and XDG_CONFIG_HOME, and an `orx` run with that environment creates no default store. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(cli): print a runnable command in the hints the bundle breaks A DMG user has no `orx` anywhere on PATH, so every hint telling them to run `orx exp wait …` names a command their shell cannot find. Agents are already covered -- `prepare_env` puts the bundle's `orx` first on their PATH -- but a human driving the bundle's CLI is not. `invocation::orx()` resolves the spelling once: plain `orx` when it is on PATH, which is every CLI install and every agent, otherwise this binary's own path, shell-quoted when it needs it. Nothing changes for anyone who installed the CLI. Applied to the three hints the report named, each of which had been copied per backend: the post-launch "Follow it with …" line (nine copies), the missing run-command error (eight), and the outdated-version warning. Both hint bodies move into `invocation`, so the eight backends now share one line each -- a net deletion, and one place to fix the next time the wording moves. `warning_for` takes the spelling as an argument rather than reading process state, keeping its tests deterministic. Not covered: the long tail of `orx login`-style hints in error strings, which still print a literal `orx`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(app): close the environment gaps review found A dashboard-synced `ORX_DATA_DIR`/`XDG_*` silently beat the shell-imported one for harness children: `prepare_env` exported the imported vars, then the synced loop overwrote any whose key was absent from the *process* env -- which in app mode is exactly these. The app read one store while claude and opencode wrote another. The guard now consults `shell_env::var`, so the imported value counts as present. codex escaped this only because it re-pins `ORX_DATA_DIR` after `prepare_env`; claude and opencode have no such pin. Local run payloads were never actually fixed. The previous commit claimed the supervisor passed its environment to `bash run.sh`, but the supervisor does not spawn it -- `localbox::run_job` does, in the launching process, from `LocalJobSpec::env`. A run started from the app still executed the user's script under launchd's PATH, with no python, uv, or conda. The env now carries the imported PATH and directories, and the supervisor comment drops the wrong claim. Three more readers of these same variables were still going to the process env, and two of them made harness detection itself diverge between app and terminal: `harness::xdg_config_home` (whose doc claims to mirror `config::config_dir`) and `opencode_auth_path`, which would report opencode signed out in the app while the CLI saw it signed in. `invocation::resolves_on_path` was the third -- it decides whether a hint can say plain `orx`, and the reader pastes that into the shell whose PATH we imported. Also: `orx login` -- the first hint a fresh DMG user meets -- now resolves like the rest; `export_to` iterates in `IMPORTED` order so the startup log is diffable; and the doc drift is repaired, including the octal-escape invariant that makes the nonce's leading underscore load-bearing, the reopened `orx delete` window, and the scope note listing what still inherits the process environment (`git`, `gh`, `kubectl`, `ssh`, `publish-branch`). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(app): allow dead_code for the launch check off macOS `is_bundle_exe_launch` is deliberately un-gated so its tests run on CI's Linux runner, but its only non-test caller is `#[cfg(target_os = "macos")]`, so the Linux bin target sees it as dead. `mod local` carries a blanket `#[allow(dead_code)]`, which is why `shell_env::parse_probe` -- same shape -- did not trip; `mod commands` does not. Verified by rebuilding with every `target_os = "macos"` in `src/` swapped to `"linux"`, which reproduces exactly what the runner cfgs out. Clean, and no other item on this branch is dead off macOS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8c17722d88 |
feat(macos): styled drag-to-Applications DMG installer window (#188)
Opening OpenResearch.dmg now shows OpenResearch.app and an /Applications alias to drag it onto, over a branded background — instead of a bare app in the DMG root. - scripts/generate-dmg-background.mjs: pure-Node PNG background (no image deps, mirroring generate-icon.mjs) with the brand mark + a drag arrow, rendered at 1x and 2x and combined into a HiDPI background.tiff. - scripts/package-macos-app.sh: stage app + Applications symlink + background, lay out the Finder window (size, icon positions, background) via AppleScript on a read-write image, then flatten to the compressed DMG. Styling is best-effort: if the Finder step fails (e.g. headless), a functional but unstyled DMG still ships, and the temp/mount cleanup runs regardless. - release-macos-app.yml: bound the 10x-billed macOS job with a timeout. - DISTRIBUTION.md: document the installer window and its constants. Verified locally: styled DMG builds with the app + Applications alias + branded background, 640x400 window; happy-path cleanup leaves no leaked temp dirs or mounts. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
dccc37c4e8 |
feat: downloadable macOS app (OpenResearch.app) + signed release hosting (#180)
* feat: scaffold downloadable macOS app (OpenResearch.app) Adds a native macOS `.app` bundle whose executable IS the `orx` binary. Launched from the bundle (double-click, no args) it enters GUI "app mode": an AppKit NSApplication + delegate run loop on the main thread — Dock icon and "OpenResearch" name from the bundle's Info.plist + .icns, and a Dock-icon click that reopens the dashboard in the browser — while the `orx up` server runs on background tokio worker threads. App mode is entered only when the executable lives in `.app/Contents/MacOS` AND argv is empty, so the bundled binary is still usable as a CLI. - src/commands/app.rs: bundle detection + AppKit delegate + background server - macos/Info.plist: bundle metadata (name, icon, identifier, version) - scripts/generate-icon.mjs: transparent-PNG rasterizer (from favicon.svg) - scripts/build-macos-app.sh: builds release orx, generates .icns, assembles OpenResearch.app into dist/ - objc2/objc2-app-kit gated under cfg(target_os = "macos") so musl Linux is untouched The bundle is unsigned; code-signing + notarization is a follow-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: sign, notarize, and host OpenResearch.app via GitHub Releases Automates distribution of the macOS app as a signed, notarized DMG attached to each GitHub Release: - scripts/build-macos-app.sh: ORX_APP_UNIVERSAL=1 builds a universal (arm64 + x86_64) binary via lipo for distribution. - scripts/package-macos-app.sh: codesigns (hardened runtime, inside-out), notarizes + staples the .app (so it launches offline once dragged out of the DMG), packages a DMG, then notarizes + staples the DMG. Env-gated: runs unsigned locally, fully signed in CI. - .github/workflows/release-macos-app.yml: on the "Release" workflow completing (workflow_run — a GITHUB_TOKEN-created release: published event can't trigger workflows), a cheap ubuntu job gates on a real dispatched release + all signing secrets + the release existing, then a macOS job builds/signs/notarizes and uploads OpenResearch.dmg to that release. - macos/DISTRIBUTION.md: the one-time Apple setup + the six repo secrets. Inert until the signing secrets are configured, so it is safe to merge before the Apple Developer account exists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: gate macOS signing behind CODEOWNERS + a required-reviewer environment Hardens the release-signing pipeline for a public repo: - .github/CODEOWNERS: marks the release workflows and macOS signing scripts as owned so they can't change unreviewed (with branch protection's "Require review from Code Owners"). - release-macos-app.yml: the cert-using job now runs in the `release-signing` environment, so adding required reviewers to it pauses signing for human approval — the Developer ID cert is never used by an unreviewed change. - DISTRIBUTION.md: documents creating the environment + branch protection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: add @sox8502 to CODEOWNERS Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * security: environment-scoped signing secrets + SHA-pinned actions Hardens the release-signing pipeline: - The 6 signing secrets move from repo secrets to the `release-signing` environment, so only the reviewed, environment-gated macos-app job can read them — the cert secrets never exist in the cheap ubuntu gate. - The gate now keys on a non-secret repo variable MACOS_SIGNING_ENABLED instead of reading the secrets to detect configuration. - actions/checkout and actions/setup-node are pinned to commit SHAs so a moved tag can't inject code into the job that holds the Developer ID cert. - DISTRIBUTION.md updated: create the environment (required reviewers + main-only deployment branches), add environment secrets, set the variable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: trim DISTRIBUTION.md to repo-specific config Drop the generic Apple-portal walkthrough (enrolment, cert creation, export click-by-click) — Apple documents that. Keep only what's specific to this repo: the environment/secrets/variable, the local build+sign commands, and the download URL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
417828221b | Add isolated dev slot helper (#166) |