synsci 1.1.135 → 1.2.2

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 +84 -54
  2. package/package.json +7 -5
package/bin/synsci.mjs CHANGED
@@ -1,9 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // Hard guard against recursive invocation. The launcher binary and the real
4
- // @synsci/cli binary share the name `synsci`; if a check ever resolves to
5
- // the launcher itself, this prevents an infinite spawn chain that exhausts
6
- // memory.
3
+ // `npx synsci`: the OpenScience install wizard.
4
+ //
5
+ // The npm package (and this bin) keep the historical `synsci` name so the
6
+ // one-liner everyone knows keeps working; everything it installs is the
7
+ // OpenScience CLI (`@synsci/openscience`, binary `openscience`), optionally with the
8
+ // Atlas managed platform on top.
9
+
10
+ // Hard guard against recursive invocation. If a check ever resolves to the
11
+ // launcher itself, this prevents an infinite spawn chain that exhausts memory.
7
12
  if (process.env.__SYNSCI_LAUNCHER_PID) {
8
13
  process.stderr.write(
9
14
  `synsci: launcher invoked recursively (parent pid ${process.env.__SYNSCI_LAUNCHER_PID}). Exiting.\n`,
@@ -35,19 +40,25 @@ const SHOW_CURSOR = "\x1b[?25h"
35
40
  const CLEAR_LINE = "\x1b[2K\r"
36
41
 
37
42
  const LOGO = [
38
- "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557",
39
- "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255d\u255a\u2588\u2588\u2557 \u2588\u2588\u2554\u255d\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255d\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255d\u2588\u2588\u2551",
40
- "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u255a\u2588\u2588\u2588\u2588\u2554\u255d \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551",
41
- "\u255a\u2550\u2550\u2550\u2550\u2588\u2588\u2551 \u255a\u2588\u2588\u2554\u255d \u2588\u2588\u2551\u255a\u2588\u2588\u2557\u2588\u2588\u2551\u255a\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551",
42
- "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u255a\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u255a\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551",
43
- "\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u255d \u255a\u2550\u255d \u255a\u2550\u255d \u255a\u2550\u2550\u2550\u255d\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u255d \u255a\u2550\u2550\u2550\u2550\u2550\u255d\u255a\u2550\u255d",
43
+ " ██████╗ ██████╗ ███████╗███╗ ██╗",
44
+ "██╔═══██╗██╔══██╗██╔════╝████╗ ██║",
45
+ "██║ ██║██████╔╝█████╗ ██╔██╗ ██║",
46
+ "██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║",
47
+ "╚██████╔╝██║ ███████╗██║ ╚████║",
48
+ " ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝",
49
+ "███████╗ ██████╗██╗███████╗███╗ ██╗ ██████╗███████╗",
50
+ "██╔════╝██╔════╝██║██╔════╝████╗ ██║██╔════╝██╔════╝",
51
+ "███████╗██║ ██║█████╗ ██╔██╗ ██║██║ █████╗ ",
52
+ "╚════██║██║ ██║██╔══╝ ██║╚██╗██║██║ ██╔══╝ ",
53
+ "███████║╚██████╗██║███████╗██║ ╚████║╚██████╗███████╗",
54
+ "╚══════╝ ╚═════╝╚═╝╚══════╝╚═╝ ╚═══╝ ╚═════╝╚══════╝",
44
55
  ]
45
56
 
46
- function ok(msg) { console.log(` ${GREEN}\u2713${RESET} ${msg}`) }
47
- function warn(msg) { console.log(` ${YELLOW}\u26A0${RESET} ${msg}`) }
57
+ function ok(msg) { console.log(` ${GREEN}✓${RESET} ${msg}`) }
58
+ function warn(msg) { console.log(` ${YELLOW}⚠${RESET} ${msg}`) }
48
59
 
49
60
  function spinner(msg) {
50
- const frames = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"]
61
+ const frames = ["", "", "", ""]
51
62
  let i = 0
52
63
  process.stdout.write(HIDE_CURSOR)
53
64
  const id = setInterval(() => {
@@ -56,7 +67,7 @@ function spinner(msg) {
56
67
  return {
57
68
  ok(result) { clearInterval(id); process.stdout.write(`${CLEAR_LINE}${SHOW_CURSOR}`); ok(result) },
58
69
  warn(result) { clearInterval(id); process.stdout.write(`${CLEAR_LINE}${SHOW_CURSOR}`); warn(result) },
59
- fail(result) { clearInterval(id); process.stdout.write(`${CLEAR_LINE}${SHOW_CURSOR}`); console.log(` ${RED}\u2717${RESET} ${result}`) },
70
+ fail(result) { clearInterval(id); process.stdout.write(`${CLEAR_LINE}${SHOW_CURSOR}`); console.log(` ${RED}✗${RESET} ${result}`) },
60
71
  update(m) { msg = m },
61
72
  }
62
73
  }
@@ -80,18 +91,17 @@ function isLauncherPath(p) {
80
91
  } catch { return false }
81
92
  }
82
93
 
83
- // Returns the absolute path to the real @synsci/cli binary, never the launcher
84
- // itself. Only trusts canonical install locations no `$PATH` walk to avoid
85
- // picking up dev shims or workspace symlinks that point at unrunnable sources.
86
- // Each candidate is verified by invoking `--version` and checking the output
87
- // is version-shaped, so half-broken installs are skipped instead of accepted.
94
+ // Returns the absolute path to the real @synsci/openscience binary (`openscience`).
95
+ // Only trusts canonical install locations (no `$PATH` walk) to avoid picking
96
+ // up dev shims or workspace symlinks. Each candidate is verified by invoking
97
+ // `--version` so half-broken installs are skipped instead of accepted.
88
98
  function resolveCli() {
89
99
  const candidates = []
90
- // 1. Global npm prefix (where `npm i -g @synsci/cli` puts it)
100
+ // 1. Global npm prefix (where `npm i -g @synsci/openscience` puts it)
91
101
  const prefix = runQuiet("npm prefix -g")
92
- if (prefix) candidates.push(join(prefix, "bin", "synsci"))
93
- // 2. ~/.synsc/bin/synsci (used by @synsci/cli postinstall on some setups)
94
- candidates.push(join(homedir(), ".synsc", "bin", "synsci"))
102
+ if (prefix) candidates.push(join(prefix, "bin", "openscience"))
103
+ // 2. ~/.openscience/bin/openscience (curl-installer location)
104
+ candidates.push(join(homedir(), ".openscience", "bin", "openscience"))
95
105
 
96
106
  for (const cand of candidates) {
97
107
  if (!existsSync(cand) || isLauncherPath(cand)) continue
@@ -105,11 +115,9 @@ function resolveCli() {
105
115
  return null
106
116
  }
107
117
 
108
- function isInstalled() { return resolveCli() !== null }
109
-
110
118
  function isConnected() {
111
119
  const xdgData = process.env.XDG_DATA_HOME || join(homedir(), ".local", "share")
112
- const sessionPath = join(xdgData, "synsc", "synsci-session.json")
120
+ const sessionPath = join(xdgData, "openscience", "openscience-session.json")
113
121
  if (!existsSync(sessionPath)) return false
114
122
  try {
115
123
  const data = JSON.parse(readFileSync(sessionPath, "utf-8"))
@@ -118,6 +126,10 @@ function isConnected() {
118
126
  } catch { return false }
119
127
  }
120
128
 
129
+ function hasAtlas() {
130
+ return runQuiet("atlas --version") !== null
131
+ }
132
+
121
133
  async function ask(question) {
122
134
  const rl = createInterface({ input: process.stdin, output: process.stdout })
123
135
  return new Promise((resolve) => {
@@ -133,24 +145,24 @@ async function main() {
133
145
  console.log()
134
146
  for (const line of LOGO) console.log(` ${CYAN}${line}${RESET}`)
135
147
  console.log()
136
- console.log(` ${BOLD}Synthetic Sciences CLI${RESET}`)
148
+ console.log(` ${BOLD}OpenScience${RESET} ${DIM}the open-source AI research workspace${RESET}`)
137
149
  console.log()
138
150
 
139
- // --- Step 1: Install or upgrade ---
151
+ // --- Step 1: Install or upgrade the OpenScience CLI ---
140
152
  let cliPath = resolveCli()
141
153
  if (cliPath) {
142
154
  const raw = runFileQuiet(cliPath, ["--version"]) || "unknown"
143
155
  const isDev = raw === "local" || raw.includes("-")
144
156
  if (isDev) {
145
- ok(`synsci ${DIM}(dev build)${RESET}`)
157
+ ok(`openscience ${DIM}(dev build)${RESET}`)
146
158
  } else {
147
159
  const s = spinner("Checking for updates...")
148
160
  const current = raw.replace(/[^0-9.]/g, "")
149
- const latest = runQuiet("npm view @synsci/cli version")
161
+ const latest = runQuiet("npm view @synsci/openscience version")
150
162
  if (!latest || current === latest) {
151
- s.ok(`synsci ${current} ${DIM}(up to date)${RESET}`)
163
+ s.ok(`openscience ${current} ${DIM}(up to date)${RESET}`)
152
164
  } else {
153
- s.update(`Upgrading ${current} \u2192 ${latest}...`)
165
+ s.update(`Upgrading ${current} ${latest}...`)
154
166
  try {
155
167
  execFileSync(cliPath, ["upgrade"], { stdio: "pipe" })
156
168
  s.ok(`Upgraded to ${latest}`)
@@ -160,43 +172,61 @@ async function main() {
160
172
  }
161
173
  }
162
174
  } else {
163
- const s = spinner("Installing synsci...")
175
+ const s = spinner("Installing OpenScience...")
164
176
  try {
165
- execSync("npm i -g @synsci/cli@latest", { stdio: "pipe" })
177
+ execSync("npm i -g @synsci/openscience@latest", { stdio: "pipe" })
166
178
  cliPath = resolveCli()
167
- if (!cliPath) throw new Error("real CLI not on PATH after install")
168
- s.ok("Installed synsci")
179
+ if (!cliPath) throw new Error("openscience not on PATH after install")
180
+ s.ok("Installed OpenScience")
169
181
  } catch (e) {
170
182
  s.fail(`Install failed${e && e.message ? ": " + e.message : ""}`)
171
- console.log(`\n Try manually: ${CYAN}npm i -g @synsci/cli${RESET}\n`)
183
+ console.log(`\n Try manually: ${CYAN}npm i -g @synsci/openscience${RESET}\n`)
172
184
  process.exit(1)
173
185
  }
174
186
  }
175
187
 
176
- // --- Step 2: Connect ---
177
- if (isConnected()) {
178
- ok("Connected to Synthetic Sciences")
179
- } else {
180
- console.log()
181
- try {
182
- execFileSync(cliPath, ["connect", "login"], { stdio: "inherit" })
183
- } catch {}
184
- }
185
-
188
+ // --- Step 2: Choose a setup ---
189
+ console.log()
190
+ console.log(` ${BOLD}How do you want to run it?${RESET}`)
191
+ console.log()
192
+ console.log(` ${BOLD}1${RESET} ${CYAN}OpenScience${RESET} ${DIM}free and open source, bring your own API keys, no account${RESET}`)
193
+ console.log(` ${BOLD}2${RESET} ${CYAN}OpenScience + Atlas${RESET} ${DIM}managed models, wallet billing, research graph & compute${RESET}`)
186
194
  console.log()
187
195
 
188
- // --- Step 3: Launch mode ---
189
- console.log(` ${BOLD}How would you like to use synsci?${RESET}`)
196
+ const setup = await ask(` ${DIM}❯${RESET} Choose [1/2]: `)
190
197
  console.log()
191
- console.log(` ${BOLD}1${RESET} ${CYAN}synsci${RESET} ${DIM}terminal TUI${RESET}`)
192
- console.log(` ${BOLD}2${RESET} ${CYAN}synsci web${RESET} ${DIM}browser UI at localhost${RESET}`)
198
+
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
+ }
208
+ } else {
209
+ ok("Atlas CLI already installed")
210
+ }
211
+ if (isConnected()) {
212
+ ok("Connected to Atlas")
213
+ } else {
214
+ console.log()
215
+ try {
216
+ execFileSync(cliPath, ["connect", "login"], { stdio: "inherit" })
217
+ } catch {}
218
+ }
219
+ } else {
220
+ ok(`BYOK mode ${DIM}add provider keys inside the app (or via env vars like ANTHROPIC_API_KEY)${RESET}`)
221
+ }
222
+
193
223
  console.log()
194
224
 
195
- const choice = await ask(` ${DIM}\u276F${RESET} Choose [1/2]: `)
225
+ // --- Step 3: Launch the workspace ---
226
+ console.log(` ${DIM}Opening the workspace in your browser…${RESET}`)
196
227
  console.log()
197
228
 
198
- const cmd = choice === "2" ? ["web"] : []
199
- const child = spawn(cliPath, cmd, { stdio: "inherit" })
229
+ const child = spawn(cliPath, ["web"], { stdio: "inherit" })
200
230
  child.on("close", (code) => process.exit(code ?? 0))
201
231
  }
202
232
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "synsci",
3
- "version": "1.1.135",
4
- "description": "One command to install, connect, and launch Synthetic Sciences CLI",
5
- "license": "MIT",
3
+ "version": "1.2.2",
4
+ "description": "Install wizard for OpenScience, the open-source AI research workspace (optionally with Atlas)",
5
+ "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "synsci": "bin/synsci.mjs"
@@ -11,8 +11,10 @@
11
11
  "bin"
12
12
  ],
13
13
  "keywords": [
14
+ "openscience",
14
15
  "synsci",
15
16
  "synthetic-sciences",
17
+ "atlas",
16
18
  "ai",
17
19
  "ml",
18
20
  "research",
@@ -20,7 +22,7 @@
20
22
  ],
21
23
  "repository": {
22
24
  "type": "git",
23
- "url": "git+https://github.com/synthetic-sciences/synsci-cli.git"
25
+ "url": "git+https://github.com/synthetic-sciences/OpenScience.git"
24
26
  },
25
27
  "homepage": "https://syntheticsciences.ai"
26
- }
28
+ }