synsci 1.2.2 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/synsci.mjs +125 -32
  2. package/package.json +2 -2
package/bin/synsci.mjs CHANGED
@@ -40,18 +40,18 @@ const SHOW_CURSOR = "\x1b[?25h"
40
40
  const CLEAR_LINE = "\x1b[2K\r"
41
41
 
42
42
  const LOGO = [
43
- " ██████╗ ██████╗ ███████╗███╗ ██╗",
44
- "██╔═══██╗██╔══██╗██╔════╝████╗ ██║",
45
- "██║ ██║██████╔╝█████╗ ██╔██╗ ██║",
46
- "██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║",
47
- "╚██████╔╝██║ ███████╗██║ ╚████║",
48
- " ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝",
49
- "███████╗ ██████╗██╗███████╗███╗ ██╗ ██████╗███████╗",
50
- "██╔════╝██╔════╝██║██╔════╝████╗ ██║██╔════╝██╔════╝",
51
- "███████╗██║ ██║█████╗ ██╔██╗ ██║██║ █████╗ ",
52
- "╚════██║██║ ██║██╔══╝ ██║╚██╗██║██║ ██╔══╝ ",
53
- "███████║╚██████╗██║███████╗██║ ╚████║╚██████╗███████╗",
54
- "╚══════╝ ╚═════╝╚═╝╚══════╝╚═╝ ╚═══╝ ╚═════╝╚══════╝",
43
+ "███████╗██╗ ██╗███╗ ██╗████████╗██╗ ██╗███████╗████████╗██╗ ██████╗",
44
+ "██╔════╝╚██╗ ██╔╝████╗ ██║╚══██╔══╝██║ ██║██╔════╝╚══██╔══╝██║██╔════╝",
45
+ "███████╗ ╚████╔╝ ██╔██╗ ██║ ██║ ███████║█████╗ ██║ ██║██║ ",
46
+ "╚════██║ ╚██╔╝ ██║╚██╗██║ ██║ ██╔══██║██╔══╝ ██║ ██║██║ ",
47
+ "███████║ ██║ ██║ ╚████║ ██║ ██║ ██║███████╗ ██║ ██║╚██████╗",
48
+ "╚══════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝",
49
+ "███████╗ ██████╗██╗███████╗███╗ ██╗ ██████╗███████╗███████╗",
50
+ "██╔════╝██╔════╝██║██╔════╝████╗ ██║██╔════╝██╔════╝██╔════╝",
51
+ "███████╗██║ ██║█████╗ ██╔██╗ ██║██║ █████╗ ███████╗",
52
+ "╚════██║██║ ██║██╔══╝ ██║╚██╗██║██║ ██╔══╝ ╚════██║",
53
+ "███████║╚██████╗██║███████╗██║ ╚████║╚██████╗███████╗███████║",
54
+ "╚══════╝ ╚═════╝╚═╝╚══════╝╚═╝ ╚═══╝ ╚═════╝╚══════╝╚══════╝",
55
55
  ]
56
56
 
57
57
  function ok(msg) { console.log(` ${GREEN}✓${RESET} ${msg}`) }
@@ -115,6 +115,19 @@ function resolveCli() {
115
115
  return null
116
116
  }
117
117
 
118
+ // The deprecated `@synsci/cli` package links the same `openscience` bin. npm
119
+ // refuses to overwrite a bin file owned by another package (EEXIST), so a
120
+ // stale global install dead-ends the upgrade — and its old binary shadows the
121
+ // real one on PATH. `npm ls` exits nonzero when the package is absent but
122
+ // still prints JSON, so read stdout either way.
123
+ function hasDeprecatedCli() {
124
+ let out = ""
125
+ try { out = execSync("npm ls -g @synsci/cli --depth=0 --json", { encoding: "utf-8", stdio: "pipe" }) }
126
+ catch (e) { out = e && typeof e.stdout === "string" ? e.stdout : "" }
127
+ try { return Boolean(JSON.parse(out).dependencies["@synsci/cli"]) }
128
+ catch { return false }
129
+ }
130
+
118
131
  function isConnected() {
119
132
  const xdgData = process.env.XDG_DATA_HOME || join(homedir(), ".local", "share")
120
133
  const sessionPath = join(xdgData, "openscience", "openscience-session.json")
@@ -126,8 +139,37 @@ function isConnected() {
126
139
  } catch { return false }
127
140
  }
128
141
 
129
- function hasAtlas() {
130
- return runQuiet("atlas --version") !== null
142
+ function atlasVersion() {
143
+ const raw = runQuiet("atlas --version")
144
+ return raw ? raw.replace(/[^0-9.]/g, "") : null
145
+ }
146
+
147
+ // Install the Atlas CLI, or bring an existing install up to date. Returns
148
+ // true when a working `atlas` binary is on PATH afterwards.
149
+ async function installOrUpdateAtlas() {
150
+ const current = atlasVersion()
151
+ if (current) {
152
+ const s = spinner("Checking Atlas CLI for updates...")
153
+ const latest = runQuiet("npm view @synsci/atlas version")
154
+ if (!latest || current === latest) {
155
+ s.ok(`atlas ${current} ${DIM}(up to date)${RESET}`)
156
+ return true
157
+ }
158
+ s.update(`Upgrading Atlas CLI ${current} → ${latest}...`)
159
+ if (runQuiet("npm i -g @synsci/atlas@latest") !== null) {
160
+ s.ok(`Upgraded Atlas CLI to ${latest}`)
161
+ } else {
162
+ s.warn(`Atlas CLI upgrade failed, continuing with ${current}`)
163
+ }
164
+ return true
165
+ }
166
+ const s = spinner("Installing Atlas CLI...")
167
+ if (runQuiet("npm i -g @synsci/atlas@latest") !== null && atlasVersion()) {
168
+ s.ok("Installed Atlas CLI")
169
+ return true
170
+ }
171
+ s.warn(`Atlas CLI install failed, you can retry later: ${CYAN}npm i -g @synsci/atlas${RESET}`)
172
+ return false
131
173
  }
132
174
 
133
175
  async function ask(question) {
@@ -145,10 +187,19 @@ async function main() {
145
187
  console.log()
146
188
  for (const line of LOGO) console.log(` ${CYAN}${line}${RESET}`)
147
189
  console.log()
148
- console.log(` ${BOLD}OpenScience${RESET} ${DIM}the open-source AI research workspace${RESET}`)
190
+ console.log(` ${BOLD}Synthetic Sciences${RESET} ${DIM}OpenScience, the open-source AI research workspace · Atlas, the research platform${RESET}`)
149
191
  console.log()
150
192
 
151
193
  // --- Step 1: Install or upgrade the OpenScience CLI ---
194
+ if (hasDeprecatedCli()) {
195
+ const s = spinner("Removing the deprecated @synsci/cli so it can't shadow the openscience command...")
196
+ if (runQuiet("npm rm -g @synsci/cli") !== null) {
197
+ s.ok("Removed the deprecated @synsci/cli")
198
+ } else {
199
+ s.warn(`Couldn't remove the deprecated @synsci/cli — if install fails, run: ${CYAN}npm rm -g @synsci/cli${RESET}`)
200
+ }
201
+ }
202
+
152
203
  let cliPath = resolveCli()
153
204
  if (cliPath) {
154
205
  const raw = runFileQuiet(cliPath, ["--version"]) || "unknown"
@@ -174,14 +225,39 @@ async function main() {
174
225
  } else {
175
226
  const s = spinner("Installing OpenScience...")
176
227
  try {
177
- execSync("npm i -g @synsci/openscience@latest", { stdio: "pipe" })
228
+ try {
229
+ execSync("npm i -g @synsci/openscience@latest", { stdio: "pipe" })
230
+ } catch (e) {
231
+ // npm refuses to overwrite a bin file owned by another package
232
+ // (EEXIST). If the conflict is the deprecated @synsci/cli, remove it
233
+ // and retry once before falling back to the standalone installer.
234
+ const stderr = e && e.stderr ? String(e.stderr) : ""
235
+ const conflict = stderr.includes("EEXIST") && (stderr.includes("@synsci/cli") || hasDeprecatedCli())
236
+ if (!conflict) throw e
237
+ s.update("Removing the deprecated @synsci/cli so it can't shadow the openscience command...")
238
+ runQuiet("npm rm -g @synsci/cli")
239
+ s.update("Retrying the OpenScience install...")
240
+ execSync("npm i -g @synsci/openscience@latest", { stdio: "pipe" })
241
+ }
178
242
  cliPath = resolveCli()
179
243
  if (!cliPath) throw new Error("openscience not on PATH after install")
180
244
  s.ok("Installed OpenScience")
181
- } catch (e) {
182
- s.fail(`Install failed${e && e.message ? ": " + e.message : ""}`)
183
- console.log(`\n Try manually: ${CYAN}npm i -g @synsci/openscience${RESET}\n`)
184
- process.exit(1)
245
+ } catch {
246
+ // Global npm installs commonly fail on permissions. Fall back to the
247
+ // standalone installer, which lands in ~/.openscience/bin without sudo
248
+ // (resolveCli already checks that location).
249
+ s.update("npm -g failed, trying the standalone installer...")
250
+ try {
251
+ execSync("curl -fsSL https://openscience.sh/install | bash", { stdio: "pipe" })
252
+ cliPath = resolveCli()
253
+ if (!cliPath) throw new Error("openscience not found after install")
254
+ s.ok("Installed OpenScience")
255
+ } catch (e2) {
256
+ s.fail(`Install failed${e2 && e2.message ? ": " + e2.message : ""}`)
257
+ console.log(`\n Try manually: ${CYAN}npm i -g @synsci/openscience${RESET}`)
258
+ console.log(` or: ${CYAN}curl -fsSL https://openscience.sh/install | bash${RESET}\n`)
259
+ process.exit(1)
260
+ }
185
261
  }
186
262
  }
187
263
 
@@ -191,23 +267,40 @@ async function main() {
191
267
  console.log()
192
268
  console.log(` ${BOLD}1${RESET} ${CYAN}OpenScience${RESET} ${DIM}free and open source, bring your own API keys, no account${RESET}`)
193
269
  console.log(` ${BOLD}2${RESET} ${CYAN}OpenScience + Atlas${RESET} ${DIM}managed models, wallet billing, research graph & compute${RESET}`)
270
+ console.log(` ${BOLD}3${RESET} ${CYAN}Atlas CLI${RESET} ${DIM}just the Atlas research CLI — maps, runs, and compute from the terminal${RESET}`)
194
271
  console.log()
195
272
 
196
- const setup = await ask(` ${DIM}❯${RESET} Choose [1/2]: `)
273
+ const setup = await ask(` ${DIM}❯${RESET} Choose [1/2/3]: `)
197
274
  console.log()
198
275
 
199
- if (setup === "2") {
200
- if (!hasAtlas()) {
201
- const s = spinner("Installing Atlas CLI...")
202
- try {
203
- execSync("npm i -g @synsci/atlas@latest", { stdio: "pipe" })
204
- s.ok("Installed Atlas CLI")
205
- } catch {
206
- s.warn(`Atlas CLI install failed, you can retry later: ${CYAN}npm i -g @synsci/atlas${RESET}`)
207
- }
276
+ if (setup === "3") {
277
+ // Atlas-CLI-only path: install/upgrade, verify, hand off to `atlas`.
278
+ // No workspace launch this option exists for people who want the
279
+ // research CLI on a box without opening the browser app.
280
+ const installed = await installOrUpdateAtlas()
281
+ if (!installed) process.exit(1)
282
+ const s = spinner("Running atlas doctor...")
283
+ if (runQuiet("atlas doctor") !== null) {
284
+ s.ok("atlas doctor passed")
285
+ } else {
286
+ s.warn(`atlas doctor reported issues — run ${CYAN}atlas doctor${RESET} for details`)
287
+ }
288
+ if (isConnected()) {
289
+ ok("Connected to Atlas")
208
290
  } else {
209
- ok("Atlas CLI already installed")
291
+ console.log()
292
+ console.log(` ${DIM}Connect your Atlas account for managed credentials:${RESET} ${CYAN}openscience connect login${RESET}`)
210
293
  }
294
+ console.log()
295
+ console.log(` ${BOLD}Next steps${RESET}`)
296
+ console.log(` ${CYAN}atlas --help${RESET} ${DIM}see everything the CLI can do${RESET}`)
297
+ console.log(` ${CYAN}atlas doctor${RESET} ${DIM}check credentials and environment${RESET}`)
298
+ console.log()
299
+ process.exit(0)
300
+ }
301
+
302
+ if (setup === "2") {
303
+ await installOrUpdateAtlas()
211
304
  if (isConnected()) {
212
305
  ok("Connected to Atlas")
213
306
  } else {
@@ -226,7 +319,7 @@ async function main() {
226
319
  console.log(` ${DIM}Opening the workspace in your browser…${RESET}`)
227
320
  console.log()
228
321
 
229
- const child = spawn(cliPath, ["web"], { stdio: "inherit" })
322
+ const child = spawn(cliPath, ["web", ...process.argv.slice(2)], { stdio: "inherit" })
230
323
  child.on("close", (code) => process.exit(code ?? 0))
231
324
  }
232
325
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synsci",
3
- "version": "1.2.2",
3
+ "version": "1.2.5",
4
4
  "description": "Install wizard for OpenScience, the open-source AI research workspace (optionally with Atlas)",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "repository": {
24
24
  "type": "git",
25
- "url": "git+https://github.com/synthetic-sciences/OpenScience.git"
25
+ "url": "git+https://github.com/synthetic-sciences/openscience.git"
26
26
  },
27
27
  "homepage": "https://syntheticsciences.ai"
28
28
  }