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.
- package/bin/synsci.mjs +84 -54
- package/package.json +7 -5
package/bin/synsci.mjs
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
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
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
43
|
+
" ██████╗ ██████╗ ███████╗███╗ ██╗",
|
|
44
|
+
"██╔═══██╗██╔══██╗██╔════╝████╗ ██║",
|
|
45
|
+
"██║ ██║██████╔╝█████╗ ██╔██╗ ██║",
|
|
46
|
+
"██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║",
|
|
47
|
+
"╚██████╔╝██║ ███████╗██║ ╚████║",
|
|
48
|
+
" ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝",
|
|
49
|
+
"███████╗ ██████╗██╗███████╗███╗ ██╗ ██████╗███████╗",
|
|
50
|
+
"██╔════╝██╔════╝██║██╔════╝████╗ ██║██╔════╝██╔════╝",
|
|
51
|
+
"███████╗██║ ██║█████╗ ██╔██╗ ██║██║ █████╗ ",
|
|
52
|
+
"╚════██║██║ ██║██╔══╝ ██║╚██╗██║██║ ██╔══╝ ",
|
|
53
|
+
"███████║╚██████╗██║███████╗██║ ╚████║╚██████╗███████╗",
|
|
54
|
+
"╚══════╝ ╚═════╝╚═╝╚══════╝╚═╝ ╚═══╝ ╚═════╝╚══════╝",
|
|
44
55
|
]
|
|
45
56
|
|
|
46
|
-
function ok(msg) { console.log(` ${GREEN}
|
|
47
|
-
function warn(msg) { console.log(` ${YELLOW}
|
|
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 = ["
|
|
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}
|
|
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/
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
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/
|
|
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", "
|
|
93
|
-
// 2. ~/.
|
|
94
|
-
candidates.push(join(homedir(), ".
|
|
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, "
|
|
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}
|
|
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(`
|
|
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/
|
|
161
|
+
const latest = runQuiet("npm view @synsci/openscience version")
|
|
150
162
|
if (!latest || current === latest) {
|
|
151
|
-
s.ok(`
|
|
163
|
+
s.ok(`openscience ${current} ${DIM}(up to date)${RESET}`)
|
|
152
164
|
} else {
|
|
153
|
-
s.update(`Upgrading ${current}
|
|
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
|
|
175
|
+
const s = spinner("Installing OpenScience...")
|
|
164
176
|
try {
|
|
165
|
-
execSync("npm i -g @synsci/
|
|
177
|
+
execSync("npm i -g @synsci/openscience@latest", { stdio: "pipe" })
|
|
166
178
|
cliPath = resolveCli()
|
|
167
|
-
if (!cliPath) throw new Error("
|
|
168
|
-
s.ok("Installed
|
|
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/
|
|
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:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
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
|
-
|
|
192
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
4
|
-
"description": "
|
|
5
|
-
"license": "
|
|
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/
|
|
25
|
+
"url": "git+https://github.com/synthetic-sciences/OpenScience.git"
|
|
24
26
|
},
|
|
25
27
|
"homepage": "https://syntheticsciences.ai"
|
|
26
|
-
}
|
|
28
|
+
}
|