niral 0.1.1 → 0.1.3
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 +90 -0
- package/bin/create-niral.js +31 -17
- package/bin/niral.js +11 -3
- package/index.js +158 -37
- package/package.json +3 -2
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Niral CLI
|
|
2
|
+
|
|
3
|
+
Zero-dependency launcher for the Niral full-stack framework. Requires Node.js 22+.
|
|
4
|
+
|
|
5
|
+
## Use
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx niral create myapp
|
|
9
|
+
cd myapp
|
|
10
|
+
npx niral dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install one global launcher:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install -g niral
|
|
17
|
+
niral create myapp --template blog
|
|
18
|
+
niral dev myapp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Both `niral` and `create-niral` packages provide the same two executable names.
|
|
22
|
+
Install only one globally to avoid bin conflicts. Templates: `minimal` (default),
|
|
23
|
+
`blog`, `dashboard`. Create refuses to overwrite a nonempty project directory.
|
|
24
|
+
|
|
25
|
+
The first project command downloads the framework from the official
|
|
26
|
+
[repository](https://github.com/Zyora-Dev/niral) into `~/.niral/framework`.
|
|
27
|
+
Set `NIRAL_HOME` to choose another install directory. Normal commands reuse the
|
|
28
|
+
cached framework without checking the network. Keep projects outside that directory.
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npx niral --help
|
|
34
|
+
npx niral --version
|
|
35
|
+
npx niral check myapp
|
|
36
|
+
npx niral test myapp
|
|
37
|
+
npx niral build myapp
|
|
38
|
+
npx niral start myapp
|
|
39
|
+
npx niral add api myapp
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
All framework commands and arguments are forwarded, with the framework's exit
|
|
43
|
+
status preserved. See the [complete CLI reference](https://niral.site/docs/cli).
|
|
44
|
+
`--version` reports this launcher and, when installed, the framework version.
|
|
45
|
+
The generated direct `niral` shim reports the framework version only.
|
|
46
|
+
|
|
47
|
+
## Update
|
|
48
|
+
|
|
49
|
+
Launcher 0.1.3 supports explicit framework updates:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
npx niral@latest update
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This explicitly updates `NIRAL_HOME/framework` from the repository's `main`
|
|
56
|
+
branch, even when the cached framework has no `update` command. After updating,
|
|
57
|
+
the direct `niral update` command is available too. Updates follow `main`, not
|
|
58
|
+
a version-pinned release. Updating the npm launcher alone does not refresh the
|
|
59
|
+
framework; update a globally installed launcher separately with
|
|
60
|
+
`npm install -g niral@latest`.
|
|
61
|
+
|
|
62
|
+
Git installs must be clean, on `main`, and use the official HTTPS origin. Updates
|
|
63
|
+
are fast-forward only; a Git failure never replaces the checkout with an archive.
|
|
64
|
+
Non-Git replacements are staged and syntax-checked first; the complete previous
|
|
65
|
+
directory is retained at the printed `framework-backup-*/framework` path. Local
|
|
66
|
+
files in that backup are not merged into the new framework. No backups are pruned.
|
|
67
|
+
|
|
68
|
+
Stop active development/build commands before updating. Concurrent installers are
|
|
69
|
+
refused. If interrupted, inspect other installer processes before manually removing
|
|
70
|
+
a stale `NIRAL_HOME/.framework-install-lock` directory. Symlinked installs and
|
|
71
|
+
updates invoked from unmanaged source checkouts are refused; maintain those with
|
|
72
|
+
Git. An update does not migrate apps, publish packages or deploy/restart servers.
|
|
73
|
+
|
|
74
|
+
## Developer Intelligence
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
npx niral di setup --help
|
|
78
|
+
npx niral di status
|
|
79
|
+
npx niral di ask "Explain this project" --root ./myapp --read-only
|
|
80
|
+
npx niral di chat --root ./myapp
|
|
81
|
+
npx niral di new myblog "Customize the home page" --template blog
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Configure your exact protocol, API base, model/deployment and auth with `di setup`.
|
|
85
|
+
Credentials stay in a named environment variable, not config or project files.
|
|
86
|
+
File reads/edits require a terminal and per-tool approval; writes retain backups
|
|
87
|
+
and reject stale hashes. `ask <prompt> --no-tools` permits prompt-only automation.
|
|
88
|
+
No unrestricted shell, automatic tests, transcript persistence or Niral Cloud
|
|
89
|
+
requirement. See the [DI commands and safeguards](https://niral.site/docs/cli).
|
|
90
|
+
Existing cached frameworks need `npx niral@latest update` to acquire DI commands.
|
package/bin/create-niral.js
CHANGED
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// npx create-niral my-app — the beginner's one command.
|
|
3
|
-
import { ensureFramework, installLauncher, runNiral } from "../index.js";
|
|
2
|
+
// npx create-niral my-app [--template blog|dashboard] — the beginner's one command.
|
|
3
|
+
import { ensureFramework, installLauncher, runNiral, runLauncher } from "../index.js";
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const usage = "usage: npx create-niral <app-name|.> [--template minimal|blog|dashboard]";
|
|
6
|
+
|
|
7
|
+
async function main(argv = process.argv.slice(2)) {
|
|
8
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
9
|
+
console.log(usage);
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
if (argv.length === 1 && ["--version", "-v"].includes(argv[0])) return runLauncher(["--version"]);
|
|
13
|
+
const name = argv[0];
|
|
14
|
+
if (!name || (name.startsWith("-") && name !== ".")) throw new Error(usage);
|
|
15
|
+
if (argv.length !== 1 && !(argv.length === 3 && argv[1] === "--template" && ["minimal", "blog", "dashboard"].includes(argv[2]))) throw new Error(usage);
|
|
10
16
|
|
|
11
|
-
await ensureFramework();
|
|
12
|
-
const code = runNiral(["create", name]);
|
|
13
|
-
if (code !== 0)
|
|
17
|
+
await ensureFramework();
|
|
18
|
+
const code = runNiral(["create", name, ...argv.slice(1)]);
|
|
19
|
+
if (code !== 0) return code;
|
|
20
|
+
|
|
21
|
+
const { profile, added } = installLauncher();
|
|
22
|
+
console.log("");
|
|
23
|
+
if (added) console.log(`✓ \`niral\` command installed — PATH updated in ${profile} (open a new terminal)`);
|
|
24
|
+
if (!profile) console.log("Use npx niral <command>, or add NIRAL_HOME/bin to your PATH.");
|
|
25
|
+
const cd = name === "." ? "" : ` cd ${name}\n`;
|
|
26
|
+
console.log(`Next:\n${cd} niral dev # or: npx niral dev\n`);
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
14
29
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
console.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
`);
|
|
30
|
+
main().then((code) => {
|
|
31
|
+
process.exitCode = code;
|
|
32
|
+
}).catch((error) => {
|
|
33
|
+
console.error(`create-niral: ${error?.message || error}`);
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
});
|
package/bin/niral.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// niral <command> — forwards to the framework at ~/.niral/framework.
|
|
3
3
|
// Available globally after `npm i -g create-niral`, or per-call via `npx niral dev`.
|
|
4
|
-
import {
|
|
4
|
+
import { runLauncher } from "../index.js";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
async function main() {
|
|
7
|
+
return runLauncher(process.argv.slice(2));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
main().then((code) => {
|
|
11
|
+
process.exitCode = code;
|
|
12
|
+
}).catch((error) => {
|
|
13
|
+
console.error(`niral: ${error?.message || error}`);
|
|
14
|
+
process.exitCode = 1;
|
|
15
|
+
});
|
package/index.js
CHANGED
|
@@ -6,81 +6,199 @@
|
|
|
6
6
|
* forwards commands to it. The framework itself never touches npm.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { existsSync, mkdirSync, rmSync, writeFileSync, chmodSync, appendFileSync, readFileSync } from "node:fs";
|
|
10
|
-
import { spawnSync
|
|
11
|
-
import { join } from "node:path";
|
|
9
|
+
import { existsSync, mkdirSync, mkdtempSync, renameSync, lstatSync, realpathSync, rmSync, writeFileSync, chmodSync, appendFileSync, readFileSync } from "node:fs";
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { join, resolve } from "node:path";
|
|
12
12
|
import { homedir } from "node:os";
|
|
13
13
|
import { get } from "node:https";
|
|
14
14
|
|
|
15
|
-
export const NIRAL_HOME = process.env.NIRAL_HOME || join(homedir(), ".niral");
|
|
15
|
+
export const NIRAL_HOME = resolve(process.env.NIRAL_HOME || join(homedir(), ".niral"));
|
|
16
16
|
const FRAMEWORK = join(NIRAL_HOME, "framework");
|
|
17
17
|
const TARBALL = "https://codeload.github.com/Zyora-Dev/niral/tar.gz/refs/heads/main";
|
|
18
18
|
const REPO = "https://github.com/Zyora-Dev/niral.git";
|
|
19
19
|
|
|
20
20
|
function download(url, dest) {
|
|
21
21
|
return new Promise((resolve, reject) => {
|
|
22
|
-
get(url, (res) => {
|
|
23
|
-
if (res.statusCode !== 200)
|
|
22
|
+
const request = get(url, (res) => {
|
|
23
|
+
if (res.statusCode !== 200) {
|
|
24
|
+
res.resume();
|
|
25
|
+
return reject(new Error(`download failed: HTTP ${res.statusCode}`));
|
|
26
|
+
}
|
|
24
27
|
const chunks = [];
|
|
25
|
-
|
|
28
|
+
let bytes = 0;
|
|
29
|
+
res.on("data", (chunk) => {
|
|
30
|
+
bytes += chunk.length;
|
|
31
|
+
if (bytes > 64 * 1024 * 1024) {
|
|
32
|
+
request.destroy(new Error("framework download exceeds 64 MiB"));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
chunks.push(chunk);
|
|
36
|
+
});
|
|
26
37
|
res.on("end", () => {
|
|
27
|
-
|
|
28
|
-
|
|
38
|
+
try {
|
|
39
|
+
writeFileSync(dest, Buffer.concat(chunks));
|
|
40
|
+
resolve();
|
|
41
|
+
} catch (error) {
|
|
42
|
+
reject(error);
|
|
43
|
+
}
|
|
29
44
|
});
|
|
30
45
|
res.on("error", reject);
|
|
31
46
|
}).on("error", reject);
|
|
47
|
+
const deadline = setTimeout(() => request.destroy(new Error("framework download timed out")), 60000);
|
|
48
|
+
request.on("close", () => clearTimeout(deadline));
|
|
32
49
|
});
|
|
33
50
|
}
|
|
34
51
|
|
|
35
52
|
function hasGit() {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
53
|
+
const result = spawnSync("git", ["--version"], { stdio: "ignore" });
|
|
54
|
+
return !result.error && result.status === 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function commandError(label, result) {
|
|
58
|
+
const detail = result.error?.message || result.stderr?.trim() || `exit ${result.status ?? "unknown"}`;
|
|
59
|
+
return new Error(`${label} failed: ${detail}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function installFromTarball(stage) {
|
|
63
|
+
const tgz = join(stage, "niral.tgz");
|
|
64
|
+
await download(TARBALL, tgz);
|
|
65
|
+
const target = join(stage, "framework");
|
|
66
|
+
mkdirSync(target);
|
|
67
|
+
const extracted = spawnSync("tar", ["-xzf", tgz, "-C", target, "--strip-components=1"], { encoding: "utf8", timeout: 120000 });
|
|
68
|
+
rmSync(tgz, { force: true });
|
|
69
|
+
if (extracted.error || extracted.status !== 0) throw commandError("framework extraction", extracted);
|
|
42
70
|
}
|
|
43
71
|
|
|
44
72
|
/** Download the framework to ~/.niral/framework (git if available, tarball otherwise). */
|
|
45
73
|
export async function ensureFramework({ update = false } = {}) {
|
|
74
|
+
if (Number(process.versions.node.split(".")[0]) < 22) throw new Error("Niral requires Node.js 22 or newer");
|
|
46
75
|
if (existsSync(join(FRAMEWORK, "bin", "niral.js")) && !update) return FRAMEWORK;
|
|
47
|
-
console.log("niral · downloading the framework (one time, ~1 MB — it has zero dependencies)…");
|
|
48
76
|
mkdirSync(NIRAL_HOME, { recursive: true });
|
|
49
|
-
|
|
77
|
+
const lock = join(NIRAL_HOME, ".framework-install-lock");
|
|
78
|
+
try {
|
|
79
|
+
mkdirSync(lock);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (error.code === "EEXIST") throw new Error(`framework installation is locked: ${lock}. Check for another installer before removing a stale lock.`);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
let stage;
|
|
85
|
+
try {
|
|
86
|
+
if (existsSync(join(FRAMEWORK, "bin", "niral.js")) && !update) return FRAMEWORK;
|
|
87
|
+
const entry = lstatSync(FRAMEWORK, { throwIfNoEntry: false });
|
|
88
|
+
if (entry && (!entry.isDirectory() || entry.isSymbolicLink())) throw new Error("framework path must be a real directory; installation left unchanged");
|
|
89
|
+
console.log(update ? "niral: updating the installed framework..." : "niral: downloading the framework...");
|
|
50
90
|
if (existsSync(join(FRAMEWORK, ".git"))) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
91
|
+
if (!hasGit()) throw new Error("Git is required to update this checkout; installation left unchanged");
|
|
92
|
+
const git = (args) => {
|
|
93
|
+
const result = spawnSync("git", args, { cwd: FRAMEWORK, encoding: "utf8", timeout: 120000, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" } });
|
|
94
|
+
if (result.error || result.status !== 0) throw commandError("Git update (no archive fallback)", result);
|
|
95
|
+
return result.stdout.trim();
|
|
96
|
+
};
|
|
97
|
+
if (git(["status", "--porcelain", "--untracked-files=all"])) throw new Error("framework checkout has local changes; preserve them before updating");
|
|
98
|
+
if (git(["branch", "--show-current"]) !== "main") throw new Error("framework checkout is not on main; update it manually with Git");
|
|
99
|
+
if (![REPO, REPO.replace(/\.git$/, "")].includes(git(["remote", "get-url", "origin"]))) throw new Error("framework origin differs from the official repository; update it manually with Git");
|
|
100
|
+
const previous = git(["rev-parse", "HEAD"]);
|
|
101
|
+
git(["pull", "--ff-only", "origin", "main"]);
|
|
102
|
+
console.log(`niral: framework ${git(["rev-parse", "--short", "HEAD"])} (previous ${previous})`);
|
|
103
|
+
return FRAMEWORK;
|
|
55
104
|
}
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
105
|
+
stage = mkdtempSync(join(NIRAL_HOME, ".framework-stage-"));
|
|
106
|
+
const target = join(stage, "framework");
|
|
107
|
+
let installed = false;
|
|
108
|
+
if (hasGit()) {
|
|
109
|
+
const cloned = spawnSync("git", ["clone", "-q", "--depth", "1", "--branch", "main", REPO, target], {
|
|
110
|
+
encoding: "utf8", timeout: 120000, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
|
|
111
|
+
});
|
|
112
|
+
installed = !cloned.error && cloned.status === 0;
|
|
113
|
+
if (!installed) {
|
|
114
|
+
rmSync(target, { recursive: true, force: true });
|
|
115
|
+
console.warn("niral: Git download unavailable; trying the framework archive...");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!installed) await installFromTarball(stage);
|
|
119
|
+
const manifest = JSON.parse(readFileSync(join(target, "package.json"), "utf8"));
|
|
120
|
+
if (manifest.name !== "niral" || !existsSync(join(target, "src", "index.js"))) throw new Error("download is not a Niral framework");
|
|
121
|
+
const checked = spawnSync(process.execPath, ["--check", join(target, "bin", "niral.js")], { encoding: "utf8", timeout: 10000 });
|
|
122
|
+
if (checked.error || checked.status !== 0) throw commandError("downloaded CLI validation", checked);
|
|
123
|
+
let backup;
|
|
124
|
+
if (entry) {
|
|
125
|
+
backup = join(mkdtempSync(join(NIRAL_HOME, "framework-backup-")), "framework");
|
|
126
|
+
renameSync(FRAMEWORK, backup);
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
renameSync(target, FRAMEWORK);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (backup) renameSync(backup, FRAMEWORK);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
if (backup) console.log(`niral: previous framework preserved at ${backup}`);
|
|
135
|
+
console.log(`niral: framework ${manifest.version} ready`);
|
|
136
|
+
return FRAMEWORK;
|
|
137
|
+
} finally {
|
|
138
|
+
if (stage) rmSync(stage, { recursive: true, force: true });
|
|
139
|
+
rmSync(lock, { recursive: true, force: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function updateFramework({ sourceRoot } = {}) {
|
|
144
|
+
if (sourceRoot && (!existsSync(FRAMEWORK) || realpathSync(sourceRoot) !== realpathSync(FRAMEWORK))) {
|
|
145
|
+
throw new Error("niral update only updates the managed NIRAL_HOME/framework installation. Update source checkouts with Git; deploy servers separately.");
|
|
63
146
|
}
|
|
64
|
-
|
|
65
|
-
|
|
147
|
+
await ensureFramework({ update: true });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function runLauncher(args) {
|
|
151
|
+
if (args[0] === "update") {
|
|
152
|
+
if (args.length === 2 && ["--help", "-h"].includes(args[1])) {
|
|
153
|
+
console.log("usage: niral update\nUpdates NIRAL_HOME/framework from main; does not update npm packages or deploy apps.");
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
if (args.length !== 1) throw new Error("usage: niral update");
|
|
157
|
+
await updateFramework();
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
if (!args.length || ["help", "--help", "-h"].includes(args[0])) {
|
|
161
|
+
if (existsSync(join(FRAMEWORK, "bin", "niral.js"))) return runNiral(args);
|
|
162
|
+
console.log("niral <command> [options]\nCommands: create, dev, build, start, check, test, add, doctor, di, update.\nThe first framework command downloads the framework.\nniral di --help: Developer Intelligence setup, status, ask, chat, new.\nniral update: explicitly refresh the installed framework.\nRun niral --help after installation for the full framework command list.");
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
if (["--version", "-v", "version"].includes(args[0])) {
|
|
166
|
+
const manifest = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8"));
|
|
167
|
+
console.log(`${manifest.name} launcher ${manifest.version}`);
|
|
168
|
+
if (existsSync(join(FRAMEWORK, "package.json"))) {
|
|
169
|
+
const framework = JSON.parse(readFileSync(join(FRAMEWORK, "package.json"), "utf8"));
|
|
170
|
+
console.log(`niral framework ${framework.version}`);
|
|
171
|
+
}
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
await ensureFramework();
|
|
175
|
+
return runNiral(args);
|
|
66
176
|
}
|
|
67
177
|
|
|
68
178
|
/** Put a `niral` launcher on the PATH so every later command is just `niral dev`. */
|
|
69
179
|
export function installLauncher() {
|
|
70
180
|
const binDir = join(NIRAL_HOME, "bin");
|
|
71
181
|
mkdirSync(binDir, { recursive: true });
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
182
|
+
const frameworkCli = join(FRAMEWORK, "bin", "niral.js");
|
|
183
|
+
const shim = join(binDir, process.platform === "win32" ? "niral.cmd" : "niral");
|
|
184
|
+
if (process.platform === "win32") {
|
|
185
|
+
writeFileSync(shim, `@echo off\r\nset "NIRAL_HOME=${NIRAL_HOME.replaceAll("%", "%%")}"\r\n"${process.execPath.replaceAll("%", "%%")}" "${frameworkCli.replaceAll("%", "%%")}" %*\r\n`);
|
|
186
|
+
} else {
|
|
187
|
+
const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
188
|
+
writeFileSync(shim, `#!/usr/bin/env sh\nexport NIRAL_HOME=${quote(NIRAL_HOME)}\nexec ${quote(process.execPath)} ${quote(frameworkCli)} "$@"\n`);
|
|
189
|
+
chmodSync(shim, 0o755);
|
|
190
|
+
}
|
|
75
191
|
|
|
76
192
|
const profile =
|
|
77
193
|
(process.env.SHELL ?? "").endsWith("/zsh") ? join(homedir(), ".zshrc")
|
|
78
194
|
: (process.env.SHELL ?? "").endsWith("/bash") ? join(homedir(), ".bashrc")
|
|
79
195
|
: null;
|
|
80
196
|
if (profile) {
|
|
81
|
-
const line =
|
|
197
|
+
const line = NIRAL_HOME === join(homedir(), ".niral")
|
|
198
|
+
? `export PATH="$HOME/.niral/bin:$PATH"`
|
|
199
|
+
: `export PATH='${binDir.replaceAll("'", "'\\''")}':"$PATH"`;
|
|
82
200
|
const current = existsSync(profile) ? readFileSync(profile, "utf8") : "";
|
|
83
|
-
if (!current.
|
|
201
|
+
if (!current.split(/\r?\n/).includes(line)) {
|
|
84
202
|
appendFileSync(profile, `\n# niral\n${line}\n`);
|
|
85
203
|
return { shim, profile, added: true };
|
|
86
204
|
}
|
|
@@ -91,6 +209,9 @@ export function installLauncher() {
|
|
|
91
209
|
|
|
92
210
|
/** Forward a command to the framework CLI. */
|
|
93
211
|
export function runNiral(args) {
|
|
94
|
-
const r = spawnSync(
|
|
95
|
-
|
|
212
|
+
const r = spawnSync(process.execPath, [join(FRAMEWORK, "bin", "niral.js"), ...args], { stdio: "inherit" });
|
|
213
|
+
if (r.error) throw new Error(`could not start the Niral CLI: ${r.error.message}`);
|
|
214
|
+
if (r.signal) throw new Error(`Niral CLI terminated by ${r.signal}`);
|
|
215
|
+
if (r.status === null) throw new Error("Niral CLI exited without a status");
|
|
216
|
+
return r.status;
|
|
96
217
|
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "niral",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Niral — the zero-dependency full-stack framework by ZyoraLabs. This package is a tiny launcher; the framework itself lives at github.com/Zyora-Dev/niral and has zero dependencies.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"niral": "bin/niral.js",
|
|
7
7
|
"create-niral": "bin/create-niral.js"
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
|
+
"files": ["bin/", "index.js", "README.md"],
|
|
10
11
|
"engines": { "node": ">=22" },
|
|
11
12
|
"keywords": ["niral", "framework", "fullstack", "zero-dependencies", "ssr", "zyoralabs"],
|
|
12
|
-
"homepage": "https://niral.
|
|
13
|
+
"homepage": "https://niral.site",
|
|
13
14
|
"repository": { "type": "git", "url": "git+https://github.com/Zyora-Dev/niral.git" },
|
|
14
15
|
"license": "MIT",
|
|
15
16
|
"dependencies": {}
|