@aipoch/open-science 1.0.1 → 1.0.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 (4) hide show
  1. package/CLI.md +84 -15
  2. package/README.md +95 -8
  3. package/cli.mjs +76 -6
  4. package/package.json +6 -6
package/CLI.md CHANGED
@@ -8,7 +8,29 @@ defines the 1.x contract for commands, machine-readable output and exit codes.
8
8
 
9
9
  ## Installation
10
10
 
11
- ### Standalone Node package
11
+ ### From npm
12
+
13
+ Install the published [standalone CLI](https://www.npmjs.com/package/@aipoch/open-science)
14
+ with Node.js **22.13.0 or later**:
15
+
16
+ ```bash
17
+ npm install --global @aipoch/open-science
18
+ open-science start --no-open
19
+ open-science url
20
+ ```
21
+
22
+ The registry package installs the matching native backend, Web UI and resources through npm
23
+ optional dependencies. It runs without Electron or an installed desktop app. Supported targets
24
+ are macOS ARM64/x64, glibc Linux ARM64/x64 and Windows x64; musl Linux and Windows ARM64 are not
25
+ supported. Do not omit optional dependencies. If startup reports a missing native package, run
26
+ `npm install --global @aipoch/open-science --include=optional`.
27
+
28
+ Open the printed authenticated URL to configure an Agent runtime, model provider and optional
29
+ Python/R Notebook runtime, or follow [First-run setup (Codex)](#first-run-setup-codex) from the
30
+ terminal. See the [standalone runtime guide](https://github.com/aipoch/open-science/blob/main/docs/standalone-runtime.md#linux-prerequisites)
31
+ for Linux credential storage and sandbox prerequisites.
32
+
33
+ ### Locally built standalone tarball
12
34
 
13
35
  Install the target-specific standalone tarball built with `npm run pack:backend`, using ordinary
14
36
  Node >=22.13.0. It includes the backend, Web UI, resources and native dependencies; Electron and
@@ -54,25 +76,58 @@ not require a separate Node.js installation.
54
76
  If the launcher directory is not yet on PATH, the Settings panel shows the line to add. Open a new
55
77
  terminal after updating PATH. Choose **Uninstall command** in the same panel to remove the launcher.
56
78
 
57
- ### From npm
79
+ ### From a source checkout
58
80
 
59
- The lightweight CLI/SDK package requires Node.js 22.5 or later. It controls an existing service.
60
- To start an independent service, install the standalone artifact (Node >=22.13.0); the lightweight
61
- package alone does not contain backend resources.
62
- Install it globally after the package is published:
81
+ Replace `open-science` in the examples below with:
63
82
 
64
83
  ```bash
65
- npm install --global @aipoch/open-science
66
- open-science --help
84
+ node packages/open-science/cli.mjs
67
85
  ```
68
86
 
69
- ### From a source checkout
87
+ ## Local information and help
70
88
 
71
- Replace `open-science` in the examples below with:
89
+ These commands work without starting or connecting to the service:
72
90
 
73
91
  ```bash
74
- node packages/open-science/cli.mjs
75
- ```
92
+ open-science --version
93
+ open-science -v
94
+ open-science version --json
95
+ open-science help
96
+ open-science help run
97
+ open-science paths --json
98
+ open-science paths --profile /absolute/path/to/profile
99
+ ```
100
+
101
+ `--version`, `-v`, and `version` print the invoked CLI's application version, including any
102
+ nightly suffix. `version --json` prints `{ "version": "..." }`. Source checkouts use the root
103
+ application version; installed distributions use their own release metadata. This is not the
104
+ version of a running service. The version flags are standalone commands, not options for `run`,
105
+ `start`, or other commands.
106
+
107
+ `help` is equivalent to `--help`; `help <command>` is equivalent to `<command> --help`.
108
+ For example, `help project` lists Project commands. Unknown command names and unsupported
109
+ arguments fail with the existing usage-error exit code `2`.
110
+
111
+ `paths` prints human-readable locations; `paths --json` prints one object with:
112
+
113
+ | Field | Meaning |
114
+ | ---------------------- | ---------------------------------------------------------------------------------------- |
115
+ | `cliPath` | The CLI implementation file used by this invocation. |
116
+ | `nodePath` | The Node executable running the CLI. |
117
+ | `startupConfigRoot` | The configuration directory selected by the installation's default or explicit override. |
118
+ | `discoveryConfigRoots` | Ordered candidate configuration directories searched for an existing service. |
119
+
120
+ Source checkouts default to `~/.open-science-project` for startup; installations default to
121
+ `~/.open-science`. Without an override, service discovery checks the development directory first,
122
+ then the production directory. `--config-root` (or `--profile`) takes precedence over
123
+ `OPEN_SCIENCE_CONFIG_ROOT`, then `OPEN_SCIENCE_STORAGE_ROOT`; an override selects a single
124
+ absolute directory for both startup and discovery. Paths are reported even when they do not exist.
125
+ The output does not identify an active service or a research-data directory, which may differ.
126
+
127
+ These commands do not create profiles, modify settings, read credentials, or print authenticated
128
+ URLs. Successful queries exit `0`; missing or invalid version metadata produces an error rather
129
+ than a fallback version. `paths` accepts only the profile, JSON, and help options; `version`
130
+ accepts only JSON and help options, and `help` accepts only one optional command name and help flags.
76
131
 
77
132
  ## Service lifecycle
78
133
 
@@ -106,7 +161,7 @@ open-science url
106
161
  `open-science url` is the only command that intentionally prints an authenticated browser URL. Normal
107
162
  human-readable, JSON, and JSONL output never includes the local token.
108
163
 
109
- Use `--port <port>` to override `OPEN_SCIENCE_WEB_PORT`, or the default port of `44100` when neither is set. `start` discovers the standalone
164
+ Use `--port <port>` to override `OPEN_SCIENCE_WEB_PORT`, or the default port of `44100` when neither is set. `start` discovers the matching npm platform package, the standalone
110
165
  Node entry bundled beside the CLI, or `out/backend/index.cjs` in a source checkout. Build the
111
166
  source entry with `npm run build:backend` and Web UI with `npm run build:web` first.
112
167
  `--app-path` and `OPEN_SCIENCE_APP_PATH` no longer select a hidden Electron backend.
@@ -166,7 +221,7 @@ nonzero CLI error codes.
166
221
 
167
222
  ## First-run setup (Codex)
168
223
 
169
- Install the standalone artifact or a desktop package containing the ordinary Node backend first.
224
+ Install the npm package, a standalone artifact or a desktop package containing the ordinary Node backend first.
170
225
  `init` creates a configuration directory only; `--profile` selects an absolute configuration directory.
171
226
 
172
227
  ```bash
@@ -249,9 +304,23 @@ open-science runtime list --json
249
304
  ## Upgrading installations
250
305
 
251
306
  Stop the backend before upgrading an installation, then restart it with the same profile.
307
+ For a global npm installation:
308
+
309
+ ```bash
310
+ open-science stop
311
+ npm install --global @aipoch/open-science@latest
312
+ open-science start --no-open
313
+ ```
314
+
315
+ Stop every profile started by that installation before updating it; use `--config-root` for
316
+ custom profiles. To uninstall, stop those services and run
317
+ `npm uninstall --global @aipoch/open-science`. Research data and credentials are retained.
318
+ See [release compatibility](https://github.com/aipoch/open-science/blob/main/docs/compatibility.md)
319
+ before downgrading; reinstalling an older package does not undo data migrations.
320
+
252
321
  Use the installation channel that owns the CLI: Homebrew for a Formula, the new downloaded
253
322
  package for deb/rpm or a local npm tarball, or a verified new Release archive in a new directory
254
- with PATH/the symlink updated. npm registry publication remains deferred.
323
+ with PATH/the symlink updated.
255
324
  The desktop update UI discovers and downloads packages. Automatic replacement remains closed for
256
325
  macOS ZIP, Linux AppImage and Debian until each channel passes complete installed-package
257
326
  acceptance. Windows automatic installation remains disabled; artifact checks and same-version copy tests do not certify a cross-version upgrade.
package/README.md CHANGED
@@ -1,10 +1,88 @@
1
1
  # @aipoch/open-science
2
2
 
3
- Node.js SDK and command-line client for an Open-Science daemon running on the local machine.
3
+ [AIPOCH Open-Science](https://github.com/aipoch/open-science) is an open-source, local-first,
4
+ model-agnostic AI research workbench. Use scientific Agents, Python and R Notebooks, and
5
+ scientific data connectors to turn research questions into reports, tables and figures.
6
+
7
+ This package provides the **standalone CLI, local Web UI and Node.js SDK**. npm installs the
8
+ matching platform package with the backend and resources; the desktop app is not required.
9
+
10
+ ## Install
11
+
12
+ Install [Node.js](https://nodejs.org/) **22.13.0 or later**, then run:
13
+
14
+ ```bash
15
+ npm install --global @aipoch/open-science
16
+ open-science --help
17
+ ```
18
+
19
+ Supported platforms are macOS Apple Silicon/Intel, Linux ARM64/x64 with glibc, and Windows x64.
20
+ Linux musl (including Alpine) and Windows ARM64 are not supported by the published native packages.
21
+ Keep npm optional dependencies enabled: they contain the platform runtime. If startup reports a
22
+ missing native package, reinstall with `npm install --global @aipoch/open-science --include=optional`.
23
+
24
+ Linux also needs working credential storage and Notebook sandbox prerequisites; see the
25
+ [Linux setup requirements](https://github.com/aipoch/open-science/blob/main/docs/standalone-runtime.md#linux-prerequisites).
26
+ Python/R and an Agent runtime/model provider are configured separately during setup.
27
+ For installations that include Node itself, see the
28
+ [Homebrew and Release archive options](https://github.com/aipoch/open-science/blob/main/docs/standalone-runtime.md).
29
+
30
+ ## Get started
31
+
32
+ Start the local service and request its browser URL:
33
+
34
+ ```bash
35
+ open-science start --no-open
36
+ open-science url
37
+ ```
38
+
39
+ Open the printed URL, complete the setup checks, configure an Agent runtime and model provider,
40
+ and create your first research project. Set up a Python or R Notebook runtime if your work needs
41
+ code execution. The URL contains a local authentication token; keep it private.
42
+ For setup from the terminal, follow the
43
+ [CLI first-run guide](https://github.com/aipoch/open-science/blob/main/packages/open-science/CLI.md#first-run-setup-codex).
44
+
45
+ After setup, submit a task from the terminal:
46
+
47
+ ```bash
48
+ open-science doctor --json
49
+ open-science project create "First research" --json
50
+ open-science run --project "First research" --prompt "Draft a research plan for comparing two analysis methods." --wait --jsonl
51
+ ```
52
+
53
+ Confirm `ready: true` in the doctor output before submitting work. The CLI accepts a Project ID
54
+ or an exact, unambiguous name; SDK calls use IDs. Closing the browser does not stop the service:
55
+
56
+ ```bash
57
+ open-science status --json
58
+ open-science stop
59
+ ```
60
+
61
+ Stopping the service affects all connected clients. For automated tasks, review the
62
+ [permissions and output options](https://github.com/aipoch/open-science/blob/main/packages/open-science/CLI.md)
63
+ and the safety guidance below.
64
+
65
+ ## Update or uninstall
66
+
67
+ Stop services started by this installation before replacing it. Use the same `--config-root`
68
+ when stopping and restarting a custom profile.
69
+
70
+ ```bash
71
+ open-science stop
72
+ npm install --global @aipoch/open-science@latest
73
+ open-science start --no-open
74
+ ```
75
+
76
+ To uninstall, stop the service and run `npm uninstall --global @aipoch/open-science`.
77
+ Uninstalling the npm package retains research data and credentials. Review
78
+ [release compatibility](https://github.com/aipoch/open-science/blob/main/docs/compatibility.md)
79
+ before changing versions; replacing program files does not reverse data migrations.
4
80
 
5
81
  ## Documentation
6
82
 
7
- - [CLI guide](./CLI.md) - installation, daemon lifecycle, task automation, artifacts, and exit codes
83
+ - [CLI guide](https://github.com/aipoch/open-science/blob/main/packages/open-science/CLI.md) - installation, daemon lifecycle, task automation, artifacts, and exit codes
84
+ - [Product overview](https://github.com/aipoch/open-science#readme) - research workflows, screenshots and desktop downloads
85
+ - [Report an issue](https://github.com/aipoch/open-science/issues) - bugs and feature requests
8
86
  - [Release compatibility](https://github.com/aipoch/open-science/blob/main/docs/compatibility.md) - stable interfaces, versioning, and data upgrades
9
87
  - [Intended use](https://github.com/aipoch/open-science/blob/main/docs/usage-scope.md) and [known limitations](https://github.com/aipoch/open-science/blob/main/docs/known-limitations.md) - workflow precautions, data disclosure, and execution boundaries
10
88
 
@@ -25,19 +103,28 @@ Node.js SDK and command-line client for an Open-Science daemon running on the lo
25
103
  ## Browser-held package import
26
104
 
27
105
  Use `@aipoch/open-science/browser-import` to send a downloaded `.science` Blob to the desktop app.
28
- See the [browser integration guide](./BROWSER_IMPORT.md) for the click flow, API, security contract,
106
+ See the [browser integration guide](https://github.com/aipoch/open-science/blob/main/packages/open-science/BROWSER_IMPORT.md) for the click flow, API, security contract,
29
107
  limits, and browser requirements. The desktop never downloads the online package URL.
30
108
 
31
109
  ## SDK quick start
32
110
 
111
+ Install the package in your Node.js project (a global CLI install does not make it importable):
112
+
113
+ ```bash
114
+ npm install @aipoch/open-science
115
+ ```
116
+
117
+ Start and configure the local service as above. Save the example in an `.mjs` file and run it
118
+ with Node.js. It creates a Project and uses its ID for subsequent requests:
119
+
33
120
  ```js
34
121
  import { connectToOpenScience } from '@aipoch/open-science'
35
122
 
36
123
  const client = await connectToOpenScience()
124
+ const project = await client.createProject({ name: 'Evidence review' })
37
125
  const run = await client.startRun({
38
- project: '<project-id-or-exact-name>',
126
+ project: project.id,
39
127
  prompt: 'Summarize the evidence.',
40
- cwd: '/absolute/path/to/research',
41
128
  permissionProfile: 'auto',
42
129
  permissionPrompts: 'none' // Deny requests that would require a human; keep existing permission rules.
43
130
  })
@@ -55,8 +142,8 @@ await client.updateSessionConfiguration(run.sessionId, {
55
142
  memoryEnabled: false
56
143
  })
57
144
 
58
- const defaults = await client.getProjectSessionDefaults('systematic-review')
59
- await client.updateProjectSessionDefaults('systematic-review', {
145
+ const defaults = await client.getProjectSessionDefaults(project.id)
146
+ await client.updateProjectSessionDefaults(project.id, {
60
147
  expectedUpdatedAt: defaults.updatedAt,
61
148
  patch: { permissionProfile: 'auto' }
62
149
  })
@@ -142,7 +229,7 @@ const progress = (async () => {
142
229
  })()
143
230
 
144
231
  const observedRun = await client.startRun({
145
- project: '<project-id-or-exact-name>',
232
+ project: project.id,
146
233
  prompt: 'Summarize the evidence.',
147
234
  permissionProfile: 'auto'
148
235
  })
package/cli.mjs CHANGED
@@ -5,14 +5,19 @@
5
5
  import { randomUUID } from 'node:crypto'
6
6
  import { closeSync, createWriteStream, existsSync, openSync, realpathSync } from 'node:fs'
7
7
  import { chmod, lstat, mkdir, readFile, readlink, rename, rm, stat } from 'node:fs/promises'
8
- import { dirname, join, resolve } from 'node:path'
8
+ import { basename, dirname, join, resolve } from 'node:path'
9
9
  import { Readable } from 'node:stream'
10
10
  import { pipeline } from 'node:stream/promises'
11
11
  import { text as readStreamText } from 'node:stream/consumers'
12
12
  import { spawn } from 'node:child_process'
13
13
  import { fileURLToPath } from 'node:url'
14
14
 
15
- import { findServiceState, readWebToken, resolveConfigRoot } from './config-root.mjs'
15
+ import {
16
+ candidateConfigRoots,
17
+ findServiceState,
18
+ readWebToken,
19
+ resolveConfigRoot
20
+ } from './config-root.mjs'
16
21
  import { codexLoginCommand } from './codex-login.mjs'
17
22
  import { connectToOpenScience, OpenScienceApiError } from './index.mjs'
18
23
  import { locateBackend } from './locate-backend.mjs'
@@ -27,6 +32,9 @@ const MAX_DOWNLOAD_SYMLINK_HOPS = 40
27
32
  const usage = `Usage: open-science <command> [options]
28
33
 
29
34
  Commands:
35
+ version Print the local application version (also --version or -v)
36
+ help [command] Show general or command help
37
+ paths Show local CLI, Node and configuration discovery paths
30
38
  init Create the local CLI configuration directory
31
39
  start Start the headless backend and localhost web UI
32
40
  stop Gracefully stop the backend
@@ -109,6 +117,7 @@ Options:
109
117
  --no-sandbox Disable Chromium's process sandbox (security risk; start only)
110
118
  --force Sign in again even when Codex credentials already exist
111
119
  --json Emit one machine-readable result
120
+ -v, --version Print the local application version and exit
112
121
  -h, --help Show this help`
113
122
 
114
123
  // Flags that take a value, mapped to their camelCase option key (explicit so new hyphenated flags
@@ -186,6 +195,9 @@ const GROUP_COMMANDS = new Set([
186
195
  // Project create, update, and session-defaults intentionally remain unbounded because their
187
196
  // positional Project names may contain multiple unquoted words.
188
197
  const POSITIONAL_LIMITS = new Map([
198
+ ['version', 0],
199
+ ['help', 1],
200
+ ['paths', 0],
189
201
  ['doctor', 0],
190
202
  ['runtime list', 0],
191
203
  ['runtime install', 1],
@@ -255,7 +267,17 @@ const assertPositionalLimit = (command, subcommand, positionals) => {
255
267
 
256
268
  export const parseCliArgs = (argv) => {
257
269
  const args = [...argv]
258
- const command = args.shift()
270
+ const requestedCommand = args.shift()
271
+ const command =
272
+ requestedCommand === '--version' || requestedCommand === '-v' ? 'version' : requestedCommand
273
+ const localOptions =
274
+ command === 'version'
275
+ ? ['--json', '--help', '-h']
276
+ : command === 'paths'
277
+ ? ['--json', '--help', '-h', '--config-root', '--profile']
278
+ : command === 'help'
279
+ ? ['--help', '-h']
280
+ : undefined
259
281
  const subcommand =
260
282
  (GROUP_COMMANDS.has(command) && args[0] && !args[0].startsWith('-')) ||
261
283
  (command === 'run' && (args[0] === 'status' || args[0] === 'cancel'))
@@ -270,6 +292,9 @@ export const parseCliArgs = (argv) => {
270
292
  const positionals = []
271
293
  while (args.length > 0) {
272
294
  const arg = args.shift()
295
+ if (localOptions && arg.startsWith('-') && !localOptions.includes(arg)) {
296
+ throw new CliUsageError(`Unknown option for ${command}: ${arg}`)
297
+ }
273
298
  if (arg.startsWith('--credential-store=')) {
274
299
  if (options.credentialStore !== undefined)
275
300
  throw new CliUsageError('Specify --credential-store only once.')
@@ -347,6 +372,10 @@ export const parseCliArgs = (argv) => {
347
372
  ...(positionals.length ? { positionals } : {}),
348
373
  options
349
374
  }
375
+ if (localOptions) {
376
+ assertPositionalLimit(command, subcommand, positionals)
377
+ return parsed
378
+ }
350
379
  // Help is local: it must not require execution-only options or a running backend.
351
380
  if (options.help) return parsed
352
381
  if (options.port !== undefined) {
@@ -1749,14 +1778,55 @@ ${commands.join('\n')}
1749
1778
  Run "open-science --help" for the complete option reference.`
1750
1779
  }
1751
1780
 
1781
+ const readCliMetadata = async () => {
1782
+ const here = dirname(fileURLToPath(import.meta.url))
1783
+ // Source SDK metadata has its own version; installed CLI manifests carry the release version.
1784
+ const development =
1785
+ basename(here) === 'open-science' &&
1786
+ basename(dirname(here)) === 'packages' &&
1787
+ existsSync(join(here, '../../src/main/node-entry.ts'))
1788
+ const manifestPath = join(development ? join(here, '../..') : here, 'package.json')
1789
+ const manifest = JSON.parse(await readFile(manifestPath, 'utf8'))
1790
+ if (
1791
+ manifest.name !== (development ? 'open-science' : '@aipoch/open-science') ||
1792
+ typeof manifest.version !== 'string' ||
1793
+ !/^\d+\.\d+\.\d+(?:-[\da-zA-Z.-]+)?(?:\+[\da-zA-Z.-]+)?$/.test(manifest.version)
1794
+ ) {
1795
+ throw new Error(`Invalid Open-Science CLI metadata: ${manifestPath}`)
1796
+ }
1797
+ return { version: manifest.version, development }
1798
+ }
1799
+
1800
+ const pathsCommand = async (options) => {
1801
+ const { development } = await readCliMetadata()
1802
+ const paths = {
1803
+ cliPath: fileURLToPath(import.meta.url),
1804
+ nodePath: process.execPath,
1805
+ startupConfigRoot: resolveConfigRoot({
1806
+ packaged: !development,
1807
+ override: options.configRoot
1808
+ }),
1809
+ discoveryConfigRoots: candidateConfigRoots({ override: options.configRoot })
1810
+ }
1811
+ console.log(
1812
+ options.json
1813
+ ? JSON.stringify(paths)
1814
+ : `CLI: ${paths.cliPath}\nNode: ${paths.nodePath}\nStartup configuration: ${paths.startupConfigRoot}\nService discovery (in order):\n${paths.discoveryConfigRoots.map((root) => ` ${root}`).join('\n')}`
1815
+ )
1816
+ }
1817
+
1752
1818
  export const runCli = async (argv = process.argv.slice(2)) => {
1753
1819
  const parsed = parseCliArgs(argv)
1754
1820
  const { command, options } = parsed
1755
- if (options.help || !command || command === '-h' || command === '--help') {
1756
- console.log(commandHelp(command))
1821
+ if (options.help || !command || command === 'help' || command === '-h' || command === '--help') {
1822
+ console.log(commandHelp(command === 'help' ? parsed.positionals?.[0] : command))
1757
1823
  return
1758
1824
  }
1759
- if (command === 'init') await initCommand(options)
1825
+ if (command === 'version') {
1826
+ const { version } = await readCliMetadata()
1827
+ console.log(options.json ? JSON.stringify({ version }) : version)
1828
+ } else if (command === 'paths') await pathsCommand(options)
1829
+ else if (command === 'init') await initCommand(options)
1760
1830
  else if (command === 'start') await startCommand(options)
1761
1831
  else if (command === 'stop') await stopCommand(options)
1762
1832
  else if (command === 'status') await statusCommand(options)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipoch/open-science",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Node.js SDK and CLI for the Open-Science local daemon",
5
5
  "type": "module",
6
6
  "exports": {
@@ -38,10 +38,10 @@
38
38
  "access": "public"
39
39
  },
40
40
  "optionalDependencies": {
41
- "@aipoch/open-science-darwin-arm64": "1.0.1",
42
- "@aipoch/open-science-darwin-x64": "1.0.1",
43
- "@aipoch/open-science-linux-x64-gnu": "1.0.1",
44
- "@aipoch/open-science-linux-arm64-gnu": "1.0.1",
45
- "@aipoch/open-science-win32-x64": "1.0.1"
41
+ "@aipoch/open-science-darwin-arm64": "1.0.2",
42
+ "@aipoch/open-science-darwin-x64": "1.0.2",
43
+ "@aipoch/open-science-linux-x64-gnu": "1.0.2",
44
+ "@aipoch/open-science-linux-arm64-gnu": "1.0.2",
45
+ "@aipoch/open-science-win32-x64": "1.0.2"
46
46
  }
47
47
  }