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.
Files changed (3) hide show
  1. package/README.md +53 -0
  2. package/bin.js +166 -0
  3. 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
+ }