synsci 1.2.5 → 1.2.7
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 +121 -34
- package/package.json +2 -2
package/bin/synsci.mjs
CHANGED
|
@@ -25,7 +25,11 @@ import { createInterface } from "node:readline"
|
|
|
25
25
|
import { fileURLToPath } from "node:url"
|
|
26
26
|
|
|
27
27
|
const SELF_PATH = (() => {
|
|
28
|
-
try {
|
|
28
|
+
try {
|
|
29
|
+
return realpathSync(fileURLToPath(import.meta.url))
|
|
30
|
+
} catch {
|
|
31
|
+
return ""
|
|
32
|
+
}
|
|
29
33
|
})()
|
|
30
34
|
|
|
31
35
|
const BOLD = "\x1b[1m"
|
|
@@ -54,8 +58,12 @@ const LOGO = [
|
|
|
54
58
|
"╚══════╝ ╚═════╝╚═╝╚══════╝╚═╝ ╚═══╝ ╚═════╝╚══════╝╚══════╝",
|
|
55
59
|
]
|
|
56
60
|
|
|
57
|
-
function ok(msg) {
|
|
58
|
-
|
|
61
|
+
function ok(msg) {
|
|
62
|
+
console.log(` ${GREEN}✓${RESET} ${msg}`)
|
|
63
|
+
}
|
|
64
|
+
function warn(msg) {
|
|
65
|
+
console.log(` ${YELLOW}⚠${RESET} ${msg}`)
|
|
66
|
+
}
|
|
59
67
|
|
|
60
68
|
function spinner(msg) {
|
|
61
69
|
const frames = ["◒", "◐", "◓", "◑"]
|
|
@@ -65,21 +73,50 @@ function spinner(msg) {
|
|
|
65
73
|
process.stdout.write(`${CLEAR_LINE} ${CYAN}${frames[i++ % frames.length]}${RESET} ${msg}`)
|
|
66
74
|
}, 80)
|
|
67
75
|
return {
|
|
68
|
-
ok(result) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
ok(result) {
|
|
77
|
+
clearInterval(id)
|
|
78
|
+
process.stdout.write(`${CLEAR_LINE}${SHOW_CURSOR}`)
|
|
79
|
+
ok(result)
|
|
80
|
+
},
|
|
81
|
+
warn(result) {
|
|
82
|
+
clearInterval(id)
|
|
83
|
+
process.stdout.write(`${CLEAR_LINE}${SHOW_CURSOR}`)
|
|
84
|
+
warn(result)
|
|
85
|
+
},
|
|
86
|
+
fail(result) {
|
|
87
|
+
clearInterval(id)
|
|
88
|
+
process.stdout.write(`${CLEAR_LINE}${SHOW_CURSOR}`)
|
|
89
|
+
console.log(` ${RED}✗${RESET} ${result}`)
|
|
90
|
+
},
|
|
91
|
+
update(m) {
|
|
92
|
+
msg = m
|
|
93
|
+
},
|
|
72
94
|
}
|
|
73
95
|
}
|
|
74
96
|
|
|
75
97
|
function runQuiet(cmd) {
|
|
76
|
-
try {
|
|
77
|
-
|
|
98
|
+
try {
|
|
99
|
+
return execSync(cmd, { encoding: "utf-8", stdio: "pipe" }).trim()
|
|
100
|
+
} catch {
|
|
101
|
+
return null
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Windows global installs expose a .cmd shim, which can't be exec'd
|
|
106
|
+
// directly — it needs a shell (and the path quoted for it).
|
|
107
|
+
const isCmdShim = (p) => process.platform === "win32" && p.toLowerCase().endsWith(".cmd")
|
|
108
|
+
|
|
109
|
+
function execCli(file, args = [], opts = {}) {
|
|
110
|
+
if (isCmdShim(file)) return execSync(['"' + file + '"', ...args].join(" "), opts)
|
|
111
|
+
return execFileSync(file, args, opts)
|
|
78
112
|
}
|
|
79
113
|
|
|
80
114
|
function runFileQuiet(file, args = []) {
|
|
81
|
-
try {
|
|
82
|
-
|
|
115
|
+
try {
|
|
116
|
+
return execCli(file, args, { encoding: "utf-8", stdio: "pipe" }).trim()
|
|
117
|
+
} catch {
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
83
120
|
}
|
|
84
121
|
|
|
85
122
|
function isLauncherPath(p) {
|
|
@@ -88,7 +125,9 @@ function isLauncherPath(p) {
|
|
|
88
125
|
if (SELF_PATH && real === SELF_PATH) return true
|
|
89
126
|
if (real.includes("/_npx/")) return true
|
|
90
127
|
return false
|
|
91
|
-
} catch {
|
|
128
|
+
} catch {
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
92
131
|
}
|
|
93
132
|
|
|
94
133
|
// Returns the absolute path to the real @synsci/openscience binary (`openscience`).
|
|
@@ -97,20 +136,29 @@ function isLauncherPath(p) {
|
|
|
97
136
|
// `--version` so half-broken installs are skipped instead of accepted.
|
|
98
137
|
function resolveCli() {
|
|
99
138
|
const candidates = []
|
|
100
|
-
// 1. Global npm prefix (where `npm i -g @synsci/openscience` puts it)
|
|
139
|
+
// 1. Global npm prefix (where `npm i -g @synsci/openscience` puts it).
|
|
140
|
+
// On Windows the global bin dir is the prefix itself and the entry is an
|
|
141
|
+
// openscience.cmd shim; on POSIX it's <prefix>/bin/openscience.
|
|
101
142
|
const prefix = runQuiet("npm prefix -g")
|
|
102
|
-
if (prefix)
|
|
103
|
-
|
|
104
|
-
|
|
143
|
+
if (prefix) {
|
|
144
|
+
if (process.platform === "win32") candidates.push(join(prefix, "openscience.cmd"))
|
|
145
|
+
else candidates.push(join(prefix, "bin", "openscience"))
|
|
146
|
+
}
|
|
147
|
+
// 2. ~/.openscience/bin/openscience (curl-installer location, POSIX only)
|
|
148
|
+
if (process.platform !== "win32") candidates.push(join(homedir(), ".openscience", "bin", "openscience"))
|
|
105
149
|
|
|
106
150
|
for (const cand of candidates) {
|
|
107
151
|
if (!existsSync(cand) || isLauncherPath(cand)) continue
|
|
108
152
|
try {
|
|
109
|
-
const ver =
|
|
110
|
-
encoding: "utf-8",
|
|
153
|
+
const ver = execCli(cand, ["--version"], {
|
|
154
|
+
encoding: "utf-8",
|
|
155
|
+
stdio: "pipe",
|
|
156
|
+
timeout: 5000,
|
|
111
157
|
}).trim()
|
|
112
158
|
if (/^\d/.test(ver)) return cand
|
|
113
|
-
} catch {
|
|
159
|
+
} catch {
|
|
160
|
+
/* unrunnable candidate, try next */
|
|
161
|
+
}
|
|
114
162
|
}
|
|
115
163
|
return null
|
|
116
164
|
}
|
|
@@ -122,10 +170,16 @@ function resolveCli() {
|
|
|
122
170
|
// still prints JSON, so read stdout either way.
|
|
123
171
|
function hasDeprecatedCli() {
|
|
124
172
|
let out = ""
|
|
125
|
-
try {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
173
|
+
try {
|
|
174
|
+
out = execSync("npm ls -g @synsci/cli --depth=0 --json", { encoding: "utf-8", stdio: "pipe" })
|
|
175
|
+
} catch (e) {
|
|
176
|
+
out = e && typeof e.stdout === "string" ? e.stdout : ""
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
return Boolean(JSON.parse(out).dependencies["@synsci/cli"])
|
|
180
|
+
} catch {
|
|
181
|
+
return false
|
|
182
|
+
}
|
|
129
183
|
}
|
|
130
184
|
|
|
131
185
|
function isConnected() {
|
|
@@ -136,10 +190,17 @@ function isConnected() {
|
|
|
136
190
|
const data = JSON.parse(readFileSync(sessionPath, "utf-8"))
|
|
137
191
|
if (!data.access_token || !data.expires_at) return false
|
|
138
192
|
return new Date(data.expires_at) > new Date()
|
|
139
|
-
} catch {
|
|
193
|
+
} catch {
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
140
196
|
}
|
|
141
197
|
|
|
142
198
|
function atlasVersion() {
|
|
199
|
+
// `atlas` on PATH may be Ariga Atlas or the MongoDB Atlas CLI, whose
|
|
200
|
+
// --version output also survives the digit filter. Only trust the command
|
|
201
|
+
// when the global @synsci/atlas package is present to own it.
|
|
202
|
+
const owned = runQuiet("npm ls -g @synsci/atlas --depth=0")
|
|
203
|
+
if (!owned || !owned.includes("@synsci/atlas")) return null
|
|
143
204
|
const raw = runQuiet("atlas --version")
|
|
144
205
|
return raw ? raw.replace(/[^0-9.]/g, "") : null
|
|
145
206
|
}
|
|
@@ -175,19 +236,27 @@ async function installOrUpdateAtlas() {
|
|
|
175
236
|
async function ask(question) {
|
|
176
237
|
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
177
238
|
return new Promise((resolve) => {
|
|
178
|
-
rl.question(question, (answer) => {
|
|
239
|
+
rl.question(question, (answer) => {
|
|
240
|
+
rl.close()
|
|
241
|
+
resolve(answer.trim())
|
|
242
|
+
})
|
|
179
243
|
})
|
|
180
244
|
}
|
|
181
245
|
|
|
182
246
|
async function main() {
|
|
183
247
|
process.on("exit", () => process.stdout.write(SHOW_CURSOR))
|
|
184
|
-
process.on("SIGINT", () => {
|
|
248
|
+
process.on("SIGINT", () => {
|
|
249
|
+
process.stdout.write(SHOW_CURSOR)
|
|
250
|
+
process.exit(130)
|
|
251
|
+
})
|
|
185
252
|
|
|
186
253
|
// --- Logo ---
|
|
187
254
|
console.log()
|
|
188
255
|
for (const line of LOGO) console.log(` ${CYAN}${line}${RESET}`)
|
|
189
256
|
console.log()
|
|
190
|
-
console.log(
|
|
257
|
+
console.log(
|
|
258
|
+
` ${BOLD}Synthetic Sciences${RESET} ${DIM}OpenScience, the open-source AI research workspace · Atlas, the research platform${RESET}`,
|
|
259
|
+
)
|
|
191
260
|
console.log()
|
|
192
261
|
|
|
193
262
|
// --- Step 1: Install or upgrade the OpenScience CLI ---
|
|
@@ -215,7 +284,7 @@ async function main() {
|
|
|
215
284
|
} else {
|
|
216
285
|
s.update(`Upgrading ${current} → ${latest}...`)
|
|
217
286
|
try {
|
|
218
|
-
|
|
287
|
+
execCli(cliPath, ["upgrade"], { stdio: "pipe" })
|
|
219
288
|
s.ok(`Upgraded to ${latest}`)
|
|
220
289
|
} catch {
|
|
221
290
|
s.warn(`Upgrade failed, continuing with ${current}`)
|
|
@@ -243,6 +312,13 @@ async function main() {
|
|
|
243
312
|
if (!cliPath) throw new Error("openscience not on PATH after install")
|
|
244
313
|
s.ok("Installed OpenScience")
|
|
245
314
|
} catch {
|
|
315
|
+
// The standalone installer is a bash script; on native Windows there's
|
|
316
|
+
// no bash to pipe it into, so don't suggest a fallback that can't run.
|
|
317
|
+
if (process.platform === "win32") {
|
|
318
|
+
s.fail("Install failed")
|
|
319
|
+
console.log(`\n Try manually: ${CYAN}npm i -g @synsci/openscience${RESET}\n`)
|
|
320
|
+
process.exit(1)
|
|
321
|
+
}
|
|
246
322
|
// Global npm installs commonly fail on permissions. Fall back to the
|
|
247
323
|
// standalone installer, which lands in ~/.openscience/bin without sudo
|
|
248
324
|
// (resolveCli already checks that location).
|
|
@@ -265,9 +341,15 @@ async function main() {
|
|
|
265
341
|
console.log()
|
|
266
342
|
console.log(` ${BOLD}How do you want to run it?${RESET}`)
|
|
267
343
|
console.log()
|
|
268
|
-
console.log(
|
|
269
|
-
|
|
270
|
-
|
|
344
|
+
console.log(
|
|
345
|
+
` ${BOLD}1${RESET} ${CYAN}OpenScience${RESET} ${DIM}free and open source, bring your own API keys, no account${RESET}`,
|
|
346
|
+
)
|
|
347
|
+
console.log(
|
|
348
|
+
` ${BOLD}2${RESET} ${CYAN}OpenScience + Atlas${RESET} ${DIM}managed models, wallet billing, research graph & compute${RESET}`,
|
|
349
|
+
)
|
|
350
|
+
console.log(
|
|
351
|
+
` ${BOLD}3${RESET} ${CYAN}Atlas CLI${RESET} ${DIM}just the Atlas research CLI — maps, runs, and compute from the terminal${RESET}`,
|
|
352
|
+
)
|
|
271
353
|
console.log()
|
|
272
354
|
|
|
273
355
|
const setup = await ask(` ${DIM}❯${RESET} Choose [1/2/3]: `)
|
|
@@ -289,7 +371,9 @@ async function main() {
|
|
|
289
371
|
ok("Connected to Atlas")
|
|
290
372
|
} else {
|
|
291
373
|
console.log()
|
|
292
|
-
console.log(
|
|
374
|
+
console.log(
|
|
375
|
+
` ${DIM}Connect your Atlas account for managed credentials:${RESET} ${CYAN}openscience connect login${RESET}`,
|
|
376
|
+
)
|
|
293
377
|
}
|
|
294
378
|
console.log()
|
|
295
379
|
console.log(` ${BOLD}Next steps${RESET}`)
|
|
@@ -306,7 +390,7 @@ async function main() {
|
|
|
306
390
|
} else {
|
|
307
391
|
console.log()
|
|
308
392
|
try {
|
|
309
|
-
|
|
393
|
+
execCli(cliPath, ["connect", "login"], { stdio: "inherit" })
|
|
310
394
|
} catch {}
|
|
311
395
|
}
|
|
312
396
|
} else {
|
|
@@ -319,7 +403,10 @@ async function main() {
|
|
|
319
403
|
console.log(` ${DIM}Opening the workspace in your browser…${RESET}`)
|
|
320
404
|
console.log()
|
|
321
405
|
|
|
322
|
-
const
|
|
406
|
+
const webArgs = ["web", ...process.argv.slice(2)]
|
|
407
|
+
const child = isCmdShim(cliPath)
|
|
408
|
+
? spawn(['"' + cliPath + '"', ...webArgs].join(" "), { stdio: "inherit", shell: true })
|
|
409
|
+
: spawn(cliPath, webArgs, { stdio: "inherit" })
|
|
323
410
|
child.on("close", (code) => process.exit(code ?? 0))
|
|
324
411
|
}
|
|
325
412
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "synsci",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
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",
|
|
@@ -25,4 +25,4 @@
|
|
|
25
25
|
"url": "git+https://github.com/synthetic-sciences/openscience.git"
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://syntheticsciences.ai"
|
|
28
|
-
}
|
|
28
|
+
}
|