nexvora 0.1.0
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/README.md +144 -0
- package/bin/nexvora.js +77 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# nexvora
|
|
2
|
+
|
|
3
|
+
One binary for everything NexVora: the **MCP server**, the **CLI**, and the
|
|
4
|
+
**donor daemon**. One install, one login, different commands.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install -g nexvora
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
That is the only install command. The native engine ships inside this package
|
|
11
|
+
(via a per-platform optional dependency); `npm` downloads only the binary for
|
|
12
|
+
your OS/CPU. No second package, no separate daemon to install.
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g nexvora
|
|
18
|
+
nexvora "summarise the file at ./report.md" # first run opens your browser to log in once
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For end-to-end-encrypted task results that you can fetch later (and that the
|
|
22
|
+
platform can never read), set an **encryption passphrase** once:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
nexvora vault setup # prompts for a passphrase; creates + caches your key
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
That's it. Everything else is just a different first word.
|
|
29
|
+
|
|
30
|
+
## One command does each job
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
nexvora "summarise RFC 9110" # one-shot: run a prompt, get the answer, done
|
|
34
|
+
cat notes.txt | nexvora # prompt from stdin
|
|
35
|
+
nexvora donor # join the donor pool (long-running)
|
|
36
|
+
nexvora mcp # MCP server over stdio (for Claude Code / Cursor / etc.)
|
|
37
|
+
nexvora vault setup|unlock|lock # manage your encryption vault (see below)
|
|
38
|
+
nexvora logout # clear local credentials
|
|
39
|
+
nexvora daemon stop # stop the background user daemon
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
You never run a separate `login` step. The **first** command that needs an
|
|
43
|
+
account opens your browser for a one-time approval (OAuth Device Grant) and
|
|
44
|
+
stores the token in your OS keychain. Every mode shares that one login.
|
|
45
|
+
|
|
46
|
+
### MCP host config (zero extra commands)
|
|
47
|
+
|
|
48
|
+
Point your MCP host at the same binary — `npx` installs it on first use:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"nexvora": { "command": "npx", "args": ["-y", "nexvora", "mcp"] }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
After a global install you can use `{ "command": "nexvora", "args": ["mcp"] }`.
|
|
59
|
+
|
|
60
|
+
Tasks submitted from an MCP host run on a donor **with your local project
|
|
61
|
+
context** (CLAUDE.md, your MCP servers, hooks) and are end-to-end encrypted.
|
|
62
|
+
`nexvora_submit_task` returns immediately with a Task ID; fetch the answer with
|
|
63
|
+
`nexvora_task_result` (it streams the output so far while the task is still
|
|
64
|
+
running, and serves the final result even after a daemon restart once your vault
|
|
65
|
+
is unlocked).
|
|
66
|
+
|
|
67
|
+
## Encryption vault (end-to-end results)
|
|
68
|
+
|
|
69
|
+
Your task **prompt and result** are sealed so the platform can't read them. The
|
|
70
|
+
key that opens them is protected by a passphrase that never leaves your machine.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
nexvora vault setup # first time: choose a passphrase, creates the vault
|
|
74
|
+
nexvora vault unlock # on a new device: re-derive + cache the key
|
|
75
|
+
nexvora vault lock # drop the cached key from the OS keychain
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- The passphrase is **separate** from your account login (the daemon never sees
|
|
79
|
+
your account password — it logs in via Device Grant).
|
|
80
|
+
- **Account recovery is ON by default**: if you forget the passphrase, an admin
|
|
81
|
+
can recover your key via escrow on a verified, audited request. Turn it off for
|
|
82
|
+
maximum privacy (then a lost passphrase means lost history).
|
|
83
|
+
|
|
84
|
+
## Sessions: single vs long-running
|
|
85
|
+
|
|
86
|
+
Your **login** (the rotating refresh token in the keychain) persists until you
|
|
87
|
+
`logout`. The single-vs-long-running choice controls the **session key** — the
|
|
88
|
+
ephemeral per-task crypto material — and how long the background daemon lives.
|
|
89
|
+
|
|
90
|
+
| Mode | Session key | Idle timeout |
|
|
91
|
+
|------|-------------|--------------|
|
|
92
|
+
| One-shot (`nexvora "…"`) | created, torn down after the task | short (daemon quits soon after) |
|
|
93
|
+
| Donor (`nexvora donor`) | per task you serve, per direction | long-running |
|
|
94
|
+
| MCP (`nexvora mcp`) | warm across tool calls | 10 min (keeps the daemon warm) |
|
|
95
|
+
|
|
96
|
+
- Override the idle window: `nexvora-daemon --mode user --idle 1800` (seconds), or
|
|
97
|
+
the `NEXVORA_IDLE_TIMEOUT_SECONDS` env var. `0` keeps the daemon up until
|
|
98
|
+
`nexvora daemon stop`.
|
|
99
|
+
- End a foreground task immediately with Ctrl-C; stop the background daemon with
|
|
100
|
+
`nexvora daemon stop`.
|
|
101
|
+
|
|
102
|
+
## Admin commands
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
nexvora escrow init # create + install the platform escrow key (once)
|
|
106
|
+
nexvora dispute open <delegation> # decrypt a disputed result via escrow (audited)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Both prompt for the admin credential locally; the server never decrypts.
|
|
110
|
+
|
|
111
|
+
## Updating
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm install -g nexvora@latest # or: npm update -g nexvora
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The native binary updates with the package. Your login and vault stay in the OS
|
|
118
|
+
keychain across updates — no re-login or re-setup needed.
|
|
119
|
+
|
|
120
|
+
## How it ships (for maintainers)
|
|
121
|
+
|
|
122
|
+
This package is a small launcher (`bin/nexvora.js`) that resolves and execs the
|
|
123
|
+
native binary. The binary lives in one of:
|
|
124
|
+
|
|
125
|
+
`@nexvora/cli-darwin-arm64`, `@nexvora/cli-darwin-x64`,
|
|
126
|
+
`@nexvora/cli-linux-x64`, `@nexvora/cli-win32-x64`
|
|
127
|
+
|
|
128
|
+
each gated by `os`/`cpu` so npm installs exactly one. Each platform package
|
|
129
|
+
carries two binaries — `nexvora` (the entry point the launcher execs) and
|
|
130
|
+
`nexvora-daemon` (the engine it finds as a sibling).
|
|
131
|
+
|
|
132
|
+
**Releasing** (see `docs/operations/release-and-deploy.md`): push a tag
|
|
133
|
+
`nexvora-v<version>`. CI (`.github/workflows/nexvora-cli-release.yml`)
|
|
134
|
+
cross-compiles all four targets, runs `scripts/package-binaries.mjs` to stage the
|
|
135
|
+
binaries + sync versions, publishes the platform packages first, then this
|
|
136
|
+
launcher last (`npm publish --provenance`).
|
|
137
|
+
|
|
138
|
+
`@nexvora/mcp-server` (the TypeScript server) keeps working as before; pass
|
|
139
|
+
`--native` or `NEXVORA_MCP_NATIVE=1` to make it delegate to the native
|
|
140
|
+
`nexvora mcp` during cutover.
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
UNLICENSED © NexVora
|
package/bin/nexvora.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Thin launcher for the native `nexvora` binary.
|
|
6
|
+
*
|
|
7
|
+
* The real engine (auth + OS keychain, MCP stdio server, CLI, donor daemon,
|
|
8
|
+
* encrypted tunnel) is a single Rust binary. It ships in exactly one per-platform
|
|
9
|
+
* npm package (`@nexvora/cli-<os>-<cpu>`), each gated by package.json `os`/`cpu`
|
|
10
|
+
* so npm installs only the one matching the host. This launcher resolves that
|
|
11
|
+
* binary and execs it transparently — argv, stdio, signals, and exit code all
|
|
12
|
+
* pass straight through, so `nexvora mcp` speaks MCP over stdio with no
|
|
13
|
+
* interference, and Ctrl-C reaches the binary directly.
|
|
14
|
+
*
|
|
15
|
+
* It is intentionally tiny and does NO work itself: one command — `nexvora` —
|
|
16
|
+
* is all the user ever runs; the binary handles first-run login on its own.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const { spawn } = require("node:child_process");
|
|
20
|
+
|
|
21
|
+
/** os-cpu -> per-platform package that carries the prebuilt binary. */
|
|
22
|
+
const PLATFORM_PACKAGES = {
|
|
23
|
+
"darwin-arm64": "@nexvora/cli-darwin-arm64",
|
|
24
|
+
"darwin-x64": "@nexvora/cli-darwin-x64",
|
|
25
|
+
"linux-x64": "@nexvora/cli-linux-x64",
|
|
26
|
+
"win32-x64": "@nexvora/cli-win32-x64",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function fail(message) {
|
|
30
|
+
process.stderr.write(`nexvora: ${message}\n`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolveBinary() {
|
|
35
|
+
const key = `${process.platform}-${process.arch}`;
|
|
36
|
+
const pkg = PLATFORM_PACKAGES[key];
|
|
37
|
+
if (!pkg) {
|
|
38
|
+
fail(
|
|
39
|
+
`unsupported platform ${key}.\n` +
|
|
40
|
+
`Supported: ${Object.keys(PLATFORM_PACKAGES).join(", ")}.`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const exe = process.platform === "win32" ? "nexvora.exe" : "nexvora";
|
|
44
|
+
try {
|
|
45
|
+
return require.resolve(`${pkg}/bin/${exe}`);
|
|
46
|
+
} catch {
|
|
47
|
+
fail(
|
|
48
|
+
`the native binary for ${key} is missing.\n` +
|
|
49
|
+
`Its package '${pkg}' was not installed — usually because optional\n` +
|
|
50
|
+
`dependencies were skipped. Reinstall with them enabled:\n` +
|
|
51
|
+
` npm install -g nexvora\n` +
|
|
52
|
+
`(remove any --no-optional / --omit=optional flag, and check proxy settings).`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const child = spawn(resolveBinary(), process.argv.slice(2), { stdio: "inherit" });
|
|
58
|
+
|
|
59
|
+
// Forward termination signals to the binary so long-lived modes (donor, mcp)
|
|
60
|
+
// shut down cleanly and tear down the session key (L3) on the way out.
|
|
61
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
62
|
+
process.on(signal, () => {
|
|
63
|
+
if (!child.killed) {
|
|
64
|
+
child.kill(signal);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
child.on("error", (err) => fail(err.message));
|
|
70
|
+
child.on("exit", (code, signal) => {
|
|
71
|
+
if (signal) {
|
|
72
|
+
// Re-raise so the parent reports the same termination cause.
|
|
73
|
+
process.kill(process.pid, signal);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
process.exit(code ?? 1);
|
|
77
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nexvora",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "NexVora — one binary for the MCP server, CLI, and donor daemon. Single login, run anything.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nexvora",
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"claude",
|
|
10
|
+
"cli",
|
|
11
|
+
"donor",
|
|
12
|
+
"ai"
|
|
13
|
+
],
|
|
14
|
+
"license": "UNLICENSED",
|
|
15
|
+
"bin": {
|
|
16
|
+
"nexvora": "bin/nexvora.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"bin",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"//": "The actual native (Rust) binary ships in exactly one of these per-platform packages; npm installs only the one matching the user's os/cpu (see each package's os/cpu fields). bin/nexvora.js resolves and execs it.",
|
|
26
|
+
"optionalDependencies": {
|
|
27
|
+
"@nexvora/cli-darwin-arm64": "0.1.0",
|
|
28
|
+
"@nexvora/cli-darwin-x64": "0.1.0",
|
|
29
|
+
"@nexvora/cli-linux-x64": "0.1.0",
|
|
30
|
+
"@nexvora/cli-win32-x64": "0.1.0"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public",
|
|
34
|
+
"registry": "https://registry.npmjs.org/"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/buntythecoder/nexvora-backend.git",
|
|
39
|
+
"directory": "packages/nexvora"
|
|
40
|
+
}
|
|
41
|
+
}
|