nur-cli 0.29.1
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 +53 -0
- package/bin.js +166 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# nur-cli (npm shim)
|
|
2
|
+
|
|
3
|
+
One command installs NurCLI on Windows, macOS, or Linux:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx nur-cli
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
or, to keep it on PATH permanently:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -g nur-cli
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
1. Downloads the **prebuilt native binary** from
|
|
18
|
+
[GitHub Releases](https://github.com/nuroctane/nur-cli/releases/latest)
|
|
19
|
+
(`nur-windows-x86_64.exe`, `nur-macos-aarch64`, `nur-macos-x86_64`,
|
|
20
|
+
`nur-linux-x86_64`). No rustup, no clone, no `cargo build`.
|
|
21
|
+
2. Installs it to `~/.local/bin` (`%USERPROFILE%\.local\bin\nur.exe` on Windows).
|
|
22
|
+
3. Runs `nur install` - the same one-stop setup as the release EXE: user PATH,
|
|
23
|
+
prerequisites (best-effort), ecosystem packs (Graphify / PLUR / Ruflo /
|
|
24
|
+
Executor / omp / browser / skills), and first-run auth hints.
|
|
25
|
+
|
|
26
|
+
The npm package is a zero-dependency downloader (~4 KB). All real logic lives in
|
|
27
|
+
the Rust binary, so npm users get exactly the same product as the one-liner.
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
- Node.js 18+ (only for the download step; `nur` itself is a native binary)
|
|
32
|
+
- Internet access to `github.com`
|
|
33
|
+
|
|
34
|
+
## Publish
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cd npm
|
|
38
|
+
npm publish
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Bump `version` in `npm/package.json` to match the release tag before publishing.
|
|
42
|
+
`FALLBACK_VERSION` inside `bin.js` should match too (it is only used when the
|
|
43
|
+
`latest/download` redirect is unreachable).
|
|
44
|
+
|
|
45
|
+
## Notes
|
|
46
|
+
|
|
47
|
+
- `postinstall` runs `node bin.js --ensure`, so a plain `npm i -g nur-cli`
|
|
48
|
+
performs the full install without a second command. It skips re-downloading
|
|
49
|
+
when the binary is already present.
|
|
50
|
+
- Re-running `npx nur-cli` always fetches the latest release binary, then
|
|
51
|
+
refreshes the stack via `nur install`.
|
|
52
|
+
- `nur update` continues to self-update the native binary directly from GitHub
|
|
53
|
+
Releases; the npm package never needs to be involved again after install.
|
package/bin.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* nur-cli npm shim - `npx nur-cli` in one command.
|
|
4
|
+
*
|
|
5
|
+
* Downloads the prebuilt native `nur` binary from GitHub Releases (no rustup,
|
|
6
|
+
* no clone, no cargo build), drops it in ~/.local/bin, and hands off to
|
|
7
|
+
* `nur install` so the full ecosystem stack provisions exactly as the
|
|
8
|
+
* one-liner / release-EXE paths do.
|
|
9
|
+
*
|
|
10
|
+
* Zero runtime dependencies: Node 18+ builtins only (https, fs, os, child_process).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
"use strict";
|
|
14
|
+
|
|
15
|
+
const https = require("https");
|
|
16
|
+
const http = require("http");
|
|
17
|
+
const fs = require("fs");
|
|
18
|
+
const path = require("path");
|
|
19
|
+
const os = require("os");
|
|
20
|
+
const { spawnSync } = require("child_process");
|
|
21
|
+
|
|
22
|
+
const REPO = "nuroctane/nur-cli";
|
|
23
|
+
// Falls back to this pinned version when `latest/download` is unreachable.
|
|
24
|
+
const FALLBACK_VERSION = "0.29.1";
|
|
25
|
+
|
|
26
|
+
function fail(msg) {
|
|
27
|
+
process.stderr.write(`nur-cli: ${msg}\n`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function platformAsset() {
|
|
32
|
+
const plat = process.platform;
|
|
33
|
+
const arch = process.arch;
|
|
34
|
+
if (plat === "win32") {
|
|
35
|
+
if (arch !== "x64") fail(`unsupported Windows arch: ${arch} (x64 only today)`);
|
|
36
|
+
// Newer releases use nur-windows-x86_64.exe; older ones shipped nur.exe.
|
|
37
|
+
return { names: ["nur-windows-x86_64.exe", "nur.exe"], execBit: false };
|
|
38
|
+
}
|
|
39
|
+
if (plat === "darwin") {
|
|
40
|
+
const a = arch === "arm64" ? "aarch64" : "x86_64";
|
|
41
|
+
return { names: [`nur-macos-${a}`, `nur-darwin-${a}`], execBit: true };
|
|
42
|
+
}
|
|
43
|
+
if (plat === "linux") {
|
|
44
|
+
if (arch !== "x64") fail(`unsupported Linux arch: ${arch} (x64 only today)`);
|
|
45
|
+
return { names: ["nur-linux-x86_64"], execBit: true };
|
|
46
|
+
}
|
|
47
|
+
fail(`unsupported platform: ${plat}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Follow redirects; resolve with the body Buffer, reject on non-200. */
|
|
51
|
+
function fetchBuffer(url, redirects) {
|
|
52
|
+
const maxRedirects = redirects == null ? 6 : redirects;
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const mod = url.startsWith("http://") ? http : https;
|
|
55
|
+
const req = mod.get(
|
|
56
|
+
url,
|
|
57
|
+
{ headers: { "user-agent": "nur-cli-npm-shim" } },
|
|
58
|
+
(res) => {
|
|
59
|
+
const status = res.statusCode || 0;
|
|
60
|
+
if (status >= 300 && status < 400 && res.headers.location) {
|
|
61
|
+
res.resume();
|
|
62
|
+
if (maxRedirects <= 0) return reject(new Error("too many redirects"));
|
|
63
|
+
const next = new URL(res.headers.location, url).toString();
|
|
64
|
+
return resolve(fetchBuffer(next, maxRedirects - 1));
|
|
65
|
+
}
|
|
66
|
+
if (status !== 200) {
|
|
67
|
+
res.resume();
|
|
68
|
+
return reject(new Error(`HTTP ${status} for ${url}`));
|
|
69
|
+
}
|
|
70
|
+
const chunks = [];
|
|
71
|
+
res.on("data", (c) => chunks.push(c));
|
|
72
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
73
|
+
res.on("error", reject);
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
req.on("error", reject);
|
|
77
|
+
req.setTimeout(120000, () => {
|
|
78
|
+
req.destroy(new Error("download timed out after 120s"));
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function installDir() {
|
|
84
|
+
// Keep parity with the Rust installer default (~/.local/bin).
|
|
85
|
+
return process.env.NUR_INSTALL_DIR || path.join(os.homedir(), ".local", "bin");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function installedBinaryPath() {
|
|
89
|
+
const dir = installDir();
|
|
90
|
+
return path.join(dir, process.platform === "win32" ? "nur.exe" : "nur");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function downloadAndInstall() {
|
|
94
|
+
const asset = platformAsset();
|
|
95
|
+
const dir = installDir();
|
|
96
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
97
|
+
|
|
98
|
+
// Try every (asset, version) combination: latest first, then the pinned
|
|
99
|
+
// fallback version. Covers legacy release layouts too.
|
|
100
|
+
const versions = ["latest/download", `download/v${FALLBACK_VERSION}`];
|
|
101
|
+
const urls = [];
|
|
102
|
+
for (const v of versions) {
|
|
103
|
+
for (const name of asset.names) {
|
|
104
|
+
urls.push(`https://github.com/${REPO}/releases/${v}/${name}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let buf = null;
|
|
109
|
+
let lastErr = null;
|
|
110
|
+
let picked = null;
|
|
111
|
+
for (const url of urls) {
|
|
112
|
+
try {
|
|
113
|
+
process.stdout.write(`nur-cli: downloading ${url.split("/").pop()} ...\n`);
|
|
114
|
+
buf = await fetchBuffer(url);
|
|
115
|
+
picked = url;
|
|
116
|
+
break;
|
|
117
|
+
} catch (e) {
|
|
118
|
+
lastErr = e;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!buf) fail(`download failed (${lastErr}). Check https://github.com/${REPO}/releases`);
|
|
122
|
+
|
|
123
|
+
if (buf.length < 1000000) {
|
|
124
|
+
fail(`downloaded asset too small (${buf.length} bytes) - aborting`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const dest = installedBinaryPath();
|
|
128
|
+
fs.writeFileSync(dest, buf);
|
|
129
|
+
if (asset.execBit) fs.chmodSync(dest, 0o755);
|
|
130
|
+
process.stdout.write(`nur-cli: installed ${dest}\n`);
|
|
131
|
+
return dest;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function runNurInstall(bin) {
|
|
135
|
+
// Full one-stop install: PATH wiring, prereqs, ecosystem packs, browser.
|
|
136
|
+
// The binary owns all of it - same as double-clicking a release EXE.
|
|
137
|
+
const r = spawnSync(bin, ["install"], { stdio: "inherit" });
|
|
138
|
+
if (r.error) {
|
|
139
|
+
process.stdout.write(
|
|
140
|
+
`nur-cli: could not run "${bin} install" (${r.error.message}).\n` +
|
|
141
|
+
`Binary is installed - run it manually once to finish setup.\n`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function main() {
|
|
147
|
+
const args = process.argv.slice(2);
|
|
148
|
+
|
|
149
|
+
// --ensure: postinstall hook mode. Skip when the binary already exists so
|
|
150
|
+
// `npm i -g nur-cli` upgrades do not re-download on every version bump.
|
|
151
|
+
if (args.includes("--ensure")) {
|
|
152
|
+
if (fs.existsSync(installedBinaryPath())) {
|
|
153
|
+
process.stdout.write("nur-cli: native binary already installed\n");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const bin = await downloadAndInstall();
|
|
157
|
+
runNurInstall(bin);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Default: always fetch latest, then hand off to `nur install`.
|
|
162
|
+
const bin = await downloadAndInstall();
|
|
163
|
+
runNurInstall(bin);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
main().catch((e) => fail(e && e.stack ? e.stack : String(e)));
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nur-cli",
|
|
3
|
+
"version": "0.29.1",
|
|
4
|
+
"description": "NurCLI - fully loaded multi-provider coding agent (TUI, vision, tools, sandbox, skills). One command installs the native binary: npx nur-cli",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/nuroctane/nur-cli.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://www.nuroctane.xyz/cli",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"nur",
|
|
13
|
+
"nurcli",
|
|
14
|
+
"ai",
|
|
15
|
+
"agent",
|
|
16
|
+
"coding-agent",
|
|
17
|
+
"cli",
|
|
18
|
+
"tui"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"nur-cli": "bin.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"bin.js",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"postinstall": "node bin.js --ensure || exit 0"
|
|
32
|
+
}
|
|
33
|
+
}
|