Skip to content

Commit 9991792

Browse files
authored
fix(install): unblock upgrades from the deprecated @synsci/cli package (#15)
* chore: clean up agent prompt language, launcher install fallback, v1.2.3 - 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. * fix(managed): point default API base at app.syntheticsciences.ai 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). * fix(install): unblock upgrades from the deprecated @synsci/cli package 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.
1 parent 503a932 commit 9991792

16 files changed

Lines changed: 115 additions & 21 deletions

File tree

backend/cli/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
22
"$schema": "https://json.schemastore.org/package.json",
3-
"version": "1.2.2",
3+
"version": "1.2.3",
44
"name": "@synsci/openscience",
55
"type": "module",
66
"description": "AI-powered CLI for ML research and development workflows",
77
"license": "Apache-2.0",
88
"private": true,
99
"scripts": {
10+
"preinstall": "node ./script/preinstall.mjs || exit 0",
1011
"typecheck": "tsgo --noEmit",
1112
"test": "bun test",
1213
"build": "bun run script/build.ts",

backend/cli/script/preinstall.mjs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env node
2+
3+
// Best-effort guard for GLOBAL installs only: the deprecated @synsci/cli
4+
// package links the same `openscience` bin, and npm refuses to overwrite a
5+
// bin file owned by another package (EEXIST) — which dead-ends upgrades.
6+
// Remove the stale package before ours is linked.
7+
//
8+
// This must NEVER fail the install: npm policies that block nested npm calls
9+
// (or script execution entirely) make this best-effort. The `npx synsci`
10+
// launcher and the openscience.sh/install script handle the conflict
11+
// deterministically.
12+
13+
import { execSync } from "child_process"
14+
15+
try {
16+
if (process.env.npm_config_global === "true" || process.env.npm_config_global === "1") {
17+
execSync("npm rm -g @synsci/cli", { stdio: "ignore", timeout: 60000 })
18+
}
19+
} catch {
20+
// swallow everything — the install must proceed
21+
}
22+
process.exit(0)

backend/cli/script/publish.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const version = Object.values(binaries)[0]
2222

2323
await $`mkdir -p ./dist/${pkg.name}`
2424
await $`cp -r ./bin ./dist/${pkg.name}/bin`
25+
await $`cp ./script/preinstall.mjs ./dist/${pkg.name}/preinstall.mjs`
2526
await $`cp ./script/postinstall.mjs ./dist/${pkg.name}/postinstall.mjs`
2627

2728
await Bun.file(`./dist/${pkg.name}/package.json`).write(
@@ -32,6 +33,9 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
3233
openscience: `./bin/openscience`,
3334
},
3435
scripts: {
36+
// best-effort: clears a stale global @synsci/cli whose `openscience`
37+
// bin link would make npm refuse the install (EEXIST); never fails
38+
preinstall: "node ./preinstall.mjs || exit 0",
3539
postinstall: "bun ./postinstall.mjs || node ./postinstall.mjs",
3640
},
3741
version: version,

backend/cli/skills/ml-training/model-economics/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ dependencies: []
1313

1414
## When to Use This Skill
1515

16-
Use this skill as the **first step** of any flywheel assessment to determine:
16+
Use this skill as the **first step** of any model-specialization assessment to determine:
1717
- Is it worth training a specialized model?
1818
- What will the total investment be?
1919
- When will the specialized model break even vs frontier APIs?

backend/cli/skills/research/initialize-atlas-graph/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ clone at a different path resolves to the **same** graph.
1818
Run this skill when:
1919
- The canvas shows "no graph for this project" / the folder isn't linked yet.
2020
- The user asks to "initialize", "set up Atlas", or "start tracking" research.
21-
- You are about to begin research/flywheel work that should be recorded.
21+
- You are about to begin research work that should be recorded.
2222

2323
## Steps
2424

backend/cli/src/agent/prompt/ml.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ evidence: scope the problem, survey prior art, build the data pipeline, design a
77
models, evaluate rigorously against real baselines, ablate what matters, and report honestly.
88
You cover the full spectrum — classical ML, deep learning, LLMs, RL, and multimodal — and the
99
complete experimental lifecycle. When the goal is to replace a frontier API dependency with a
10-
cheaper specialized model, you run the model flywheel (see the Model Specialization track).
10+
cheaper specialized model, you run the specialization loop (see the Model Specialization track).
1111

1212
You are NOT a chatbot. You are an autonomous ML agent. Given a task, dataset, or research
1313
question, you independently produce a complete, reproducible analysis and result.
@@ -141,7 +141,7 @@ Before reporting, run the decoupled critique + reviewer gate. This is a blocking
141141

142142
---
143143

144-
## Model Specialization Track (the flywheel)
144+
## Model Specialization Track
145145
When the goal is to REPLACE a frontier API dependency with a cheaper specialized model, run
146146
this loop instead of the generic pipeline: **production data → train specialized model →
147147
deploy → collect more data → retrain**. This is collaborative — present options with

backend/cli/src/cli/cmd/github.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ export const GithubInstallCommand = cmd({
355355

356356
async function getInstallation() {
357357
return await fetch(
358-
`https://api.syntheticsciences.ai/get_github_app_installation?owner=${app.owner}&repo=${app.repo}`,
358+
`https://app.syntheticsciences.ai/get_github_app_installation?owner=${app.owner}&repo=${app.repo}`,
359359
)
360360
.then((res) => res.json())
361361
.then((data) => data.installation)
@@ -664,7 +664,7 @@ export const GithubRunCommand = cmd({
664664

665665
function normalizeOidcBaseUrl(): string {
666666
const value = process.env["OIDC_BASE_URL"]
667-
if (!value) return "https://api.syntheticsciences.ai"
667+
if (!value) return "https://app.syntheticsciences.ai"
668668
return value.replace(/\/+$/, "")
669669
}
670670

backend/cli/src/endpoints.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*/
1818

1919
/** Neutral public default. Contains no internal codename. */
20-
export const DEFAULT_MANAGED_API_BASE = "https://api.syntheticsciences.ai"
20+
export const DEFAULT_MANAGED_API_BASE = "https://app.syntheticsciences.ai"
2121

2222
function stripTrailingSlashes(url: string): string {
2323
return url.replace(/\/+$/, "")

backend/cli/src/openscience/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ export class InsufficientCreditsError extends Error {
185185

186186
// ── Bundled atlas CLI resolution ─────────────────────────────────────────
187187
// The @synsci/atlas package ships as a dependency; its `atlas` binary lives in
188-
// node_modules. The agent shells out to native `atlas` commands (the research /
189-
// flywheel prompts drive the map + managed-compute path through it), so the
188+
// node_modules. The agent shells out to native `atlas` commands (the research
189+
// prompts drive the map + managed-compute path through it), so the
190190
// binary must be on the subprocess PATH without requiring a separate global
191191
// install. We resolve the package, prefer the npm-generated `.bin/atlas` shim,
192192
// and otherwise synthesize a tiny launcher in the openscience data dir that runs the

backend/cli/src/provider/provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export namespace Provider {
109109
* Blocker (c) — the inverse of requireAtlasProxyForManagedKey.
110110
*
111111
* A prior managed sync may have injected an Atlas proxy `*_BASE_URL` (e.g.
112-
* ANTHROPIC_BASE_URL → api.syntheticsciences.ai). If the resolved key is the
112+
* ANTHROPIC_BASE_URL → app.syntheticsciences.ai). If the resolved key is the
113113
* user's OWN key (BYOK), that key must NEVER be sent to the Atlas proxy — it
114114
* would leak the credential and mis-bill. Pin the base URL back to the
115115
* provider's public endpoint and drop the managed routing.

0 commit comments

Comments
 (0)