Skip to content

fix(install): unblock upgrades from the deprecated @synsci/cli package#15

Merged
Aayam Bansal (aayambansal) merged 3 commits into
mainfrom
install-upgrade-path
Jul 4, 2026
Merged

fix(install): unblock upgrades from the deprecated @synsci/cli package#15
Aayam Bansal (aayambansal) merged 3 commits into
mainfrom
install-upgrade-path

Conversation

@aayambansal

Copy link
Copy Markdown
Member

The problem

The npm package was renamed from @synsci/cli (now deprecated with "Renamed to @synsci/openscience. Please install @synsci/openscience instead.") to @synsci/openscience. Both declare the same bin name, openscience, and npm's bin-links check refuses to overwrite a bin file owned by a different package. So everyone with the old package still installed globally dead-ends when upgrading:

npm error code EEXIST
npm error File exists: /opt/homebrew/bin/openscience

The fix (three layers)

1. npx synsci launcher (tooling/launcher/bin/synsci.mjs) — the deterministic path.

  • Before resolving/installing the CLI, detect a stale global @synsci/cli via npm ls -g @synsci/cli --depth=0 --json (reads stdout even on nonzero exit) and remove it, telling the user why: "Removing the deprecated @synsci/cli so it can't shadow the openscience command". This also stops the old package's binary from being picked up by resolveCli() at the global npm prefix and masquerading as an up-to-date install.
  • If the global install still fails and stderr shows an EEXIST bin conflict attributable to @synsci/cli, remove the stale package and retry the install once before falling back to the standalone installer.
  • Extra args passed to npx synsci are now forwarded to the final openscience web invocation.
  • Launcher bumped to 1.2.4.

2. @synsci/openscience preinstall guard (backend/cli/script/preinstall.mjs) — best effort.
When npm_config_global is set, it tries npm rm -g @synsci/cli silently; it swallows every error and always exits 0, so it can never fail an install. Wired into both the source package.json and the published manifest generated by script/publish.ts (which now copies the script into the dist package, next to postinstall.mjs). Note: npm policies that block lifecycle scripts (or nested npm calls) make this layer advisory only — the launcher and the shell installer handle the conflict deterministically.

3. Standalone installer (frontend/landing/public/install, served at openscience.sh/install).
It is a pure binary installer (no npm involved), so instead of remediation it now prints a hint after installing: if command -v openscience resolves to something other than ~/.openscience/bin/openscience, it warns that the other binary may shadow the install and points at npm rm -g @synsci/cli. The root install copy is kept byte-identical.

Also included (re-landed from #10)

The two commits approved in #10 whose squash was lost from main:

  • chore: clean up agent prompt language, launcher install fallback, v1.2.3 — reworded ml-agent prompt language, the npx synsci fallback to curl -fsSL https://openscience.sh/install | bash when the global npm install fails, and the 1.2.3 version bumps. The EEXIST handling above builds on that fallback.
  • fix(managed): point default API base at app.syntheticsciences.ai — the managed/Atlas default pointed at api.syntheticsciences.ai, which never existed as a host, so managed mode failed with DNS/fetch errors; repointed to app.syntheticsciences.ai (plus the github/share/OIDC refs).

Verification

  • node --check tooling/launcher/bin/synsci.mjs — passes
  • node --check backend/cli/script/preinstall.mjs — passes
  • sh -n install frontend/landing/public/install (and bash -n) — passes
  • bun install at repo root — clean (2032 packages)
  • bun run typecheck — 6/6 tasks successful
  • bun run --cwd backend/cli test — 820 pass, 0 fail (61 files)
  • Preinstall guard exercised directly: exits 0 with npm_config_global unset, set, and with a broken PATH
  • Stale-package detection exercised against a sandbox npm prefix containing a fake global @synsci/cli: detected, removed by npm rm -g, and reported absent afterwards

- Reword the ml agent's 'flywheel' phrasing as 'model specialization' across the
  prompt and skills; only Atlas skills remain in the bundle.
- npx synsci: when global npm install fails (common on permissions), fall back
  to the standalone installer at openscience.sh/install instead of dead-ending.
- Bump CLI + launcher to 1.2.3.
The managed/Atlas base defaulted to api.syntheticsciences.ai, which never
existed as a host — so managed mode failed with 'fetch failed' / DNS errors for
everyone. The real backend is app.syntheticsciences.ai (Vercel-fronted, /api/*
proxied to Fly). Repoint the default plus the hardcoded github/share/OIDC refs
(api.dev → app.dev for the preview path).
The package was renamed on npm from @synsci/cli to @synsci/openscience, and
both declare the same `openscience` bin. npm refuses to overwrite a bin file
owned by another package, so anyone with the old package still installed
globally hits `npm error code EEXIST` and dead-ends. Fix it in three layers:

- npx synsci: detect a global @synsci/cli up front and remove it (telling the
  user why) before resolving or installing the CLI; if the global install
  still fails with an EEXIST bin conflict, remove the stale package and retry
  once before falling back to the standalone installer. Also forward extra
  args through to `openscience web` and bump the launcher to 1.2.4.
- @synsci/openscience: ship a best-effort preinstall guard that clears a
  stale global @synsci/cli when npm_config_global is set; it always exits 0
  so it can never fail an install (script-blocking npm policies make this
  advisory only — the launcher and installer handle the conflict
  deterministically).
- openscience.sh/install: after installing the binary, warn when another
  `openscience` earlier on PATH would shadow it and point at
  `npm rm -g @synsci/cli`. Kept the root copy of the script in sync.
@vercel

vercel Bot commented Jul 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openscience Ready Ready Preview, Comment Jul 4, 2026 6:47pm

Request Review

@aayambansal Aayam Bansal (aayambansal) merged commit 9991792 into main Jul 4, 2026
9 checks passed
@aayambansal Aayam Bansal (aayambansal) deleted the install-upgrade-path branch July 4, 2026 18:49
Aayam Bansal (aayambansal) added a commit that referenced this pull request Jul 7, 2026
…k, harden PKCE + push (#122)

- refreshAccessToken no longer treats a 429/408 on the token endpoint as a
  fatal 'sign-in expired'. During a retry storm auth.openai.com rate-limits
  the token endpoint; the whole 4xx range was fatal, forcing a needless full
  re-auth. Retry 429/408 (and 5xx) with backoff; only a genuine 4xx reconnects. (#19)
- Removed the shared-key quota fallback. On 429/403 it forwarded the
  ChatGPT-internal Codex model id + Codex-shaped body to api.openai.com
  (which doesn't know those models → 400s) under process.env.OPENAI_API_KEY,
  and the substring quota-match could misfire on unrelated 403s → silently
  charging the user's personal key. Return the 429/403 as-is instead. (#15)
- generateRandomString uses rejection sampling for a uniform PKCE verifier
  (b % 66 biased toward the first 58 chars). (#45)
- pushTokensToBackend fetch is bounded by OAUTH_HTTP_TIMEOUT_MS so a hung
  backend can't leave 'keys signin' spinning after the browser succeeded. (#35)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant