niral 0.1.3 → 0.1.4
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 +29 -10
- package/index.js +54 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,22 +46,37 @@ The generated direct `niral` shim reports the framework version only.
|
|
|
46
46
|
|
|
47
47
|
## Update
|
|
48
48
|
|
|
49
|
-
Launcher 0.1.
|
|
49
|
+
Launcher 0.1.4 supports explicit framework updates and persisted revision pins:
|
|
50
50
|
|
|
51
51
|
```sh
|
|
52
52
|
npx niral@latest update
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
This explicitly updates `NIRAL_HOME/framework
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
framework; update a globally installed launcher separately with
|
|
55
|
+
This explicitly updates `NIRAL_HOME/framework`, even when the cached framework
|
|
56
|
+
has no `update` command. The default is the repository's `main` branch. Updating
|
|
57
|
+
also refreshes an existing generated shell shim so its updater remains independent
|
|
58
|
+
of older pinned framework code. Updating the npm launcher alone does not refresh
|
|
59
|
+
the framework; update a globally installed launcher separately with
|
|
60
60
|
`npm install -g niral@latest`.
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Set `APPROVED_FRAMEWORK_SHA` to a reviewed full 40-character lowercase commit SHA:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
NIRAL_REF="$APPROVED_FRAMEWORK_SHA" npx niral@latest update
|
|
66
|
+
niral update
|
|
67
|
+
NIRAL_REF=main npx niral@latest update
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The selection is saved in `NIRAL_HOME/.framework-ref`; plain updates retain it.
|
|
71
|
+
Existing `v`-prefixed version tags are also accepted, but tags can move: use a
|
|
72
|
+
full commit SHA for reproducibility. `NIRAL_REF=main` explicitly removes the pin.
|
|
73
|
+
Fresh installations accept the same variable; switching a cached install requires
|
|
74
|
+
an explicit update. npm versions and framework revisions are independent.
|
|
75
|
+
|
|
76
|
+
Git installs must be clean, on `main` or a managed detached pin, and use the
|
|
77
|
+
official HTTPS origin. `main` updates
|
|
63
78
|
are fast-forward only; a Git failure never replaces the checkout with an archive.
|
|
64
|
-
|
|
79
|
+
Pin changes and non-Git replacements are staged and syntax-checked first; the complete previous
|
|
65
80
|
directory is retained at the printed `framework-backup-*/framework` path. Local
|
|
66
81
|
files in that backup are not merged into the new framework. No backups are pruned.
|
|
67
82
|
|
|
@@ -74,14 +89,18 @@ Git. An update does not migrate apps, publish packages or deploy/restart servers
|
|
|
74
89
|
## Developer Intelligence
|
|
75
90
|
|
|
76
91
|
```sh
|
|
77
|
-
npx niral di setup
|
|
92
|
+
npx niral di setup
|
|
78
93
|
npx niral di status
|
|
79
94
|
npx niral di ask "Explain this project" --root ./myapp --read-only
|
|
80
95
|
npx niral di chat --root ./myapp
|
|
81
96
|
npx niral di new myblog "Customize the home page" --template blog
|
|
82
97
|
```
|
|
83
98
|
|
|
84
|
-
|
|
99
|
+
`npx niral di setup` offers OpenAI, Claude (Anthropic), Azure OpenAI or Custom.
|
|
100
|
+
Named providers set protocol/auth automatically; Custom keeps manual choices.
|
|
101
|
+
Enter your exact model/deployment and credential variable name. Azure also asks
|
|
102
|
+
for its resource URL or `/openai/v1` base. Bedrock is not yet supported.
|
|
103
|
+
Use `npx niral di setup --help` for noninteractive flags.
|
|
85
104
|
Credentials stay in a named environment variable, not config or project files.
|
|
86
105
|
File reads/edits require a terminal and per-tool approval; writes retain backups
|
|
87
106
|
and reject stale hashes. `ask <prompt> --no-tools` permits prompt-only automation.
|
package/index.js
CHANGED
|
@@ -14,7 +14,6 @@ import { get } from "node:https";
|
|
|
14
14
|
|
|
15
15
|
export const NIRAL_HOME = resolve(process.env.NIRAL_HOME || join(homedir(), ".niral"));
|
|
16
16
|
const FRAMEWORK = join(NIRAL_HOME, "framework");
|
|
17
|
-
const TARBALL = "https://codeload.github.com/Zyora-Dev/niral/tar.gz/refs/heads/main";
|
|
18
17
|
const REPO = "https://github.com/Zyora-Dev/niral.git";
|
|
19
18
|
|
|
20
19
|
function download(url, dest) {
|
|
@@ -59,9 +58,9 @@ function commandError(label, result) {
|
|
|
59
58
|
return new Error(`${label} failed: ${detail}`);
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
async function installFromTarball(stage) {
|
|
61
|
+
async function installFromTarball(stage, ref) {
|
|
63
62
|
const tgz = join(stage, "niral.tgz");
|
|
64
|
-
await download(
|
|
63
|
+
await download(`https://codeload.github.com/Zyora-Dev/niral/tar.gz/${ref === "main" ? "refs/heads/main" : encodeURIComponent(ref)}`, tgz);
|
|
65
64
|
const target = join(stage, "framework");
|
|
66
65
|
mkdirSync(target);
|
|
67
66
|
const extracted = spawnSync("tar", ["-xzf", tgz, "-C", target, "--strip-components=1"], { encoding: "utf8", timeout: 120000 });
|
|
@@ -72,7 +71,14 @@ async function installFromTarball(stage) {
|
|
|
72
71
|
/** Download the framework to ~/.niral/framework (git if available, tarball otherwise). */
|
|
73
72
|
export async function ensureFramework({ update = false } = {}) {
|
|
74
73
|
if (Number(process.versions.node.split(".")[0]) < 22) throw new Error("Niral requires Node.js 22 or newer");
|
|
75
|
-
|
|
74
|
+
const pinFile = join(NIRAL_HOME, ".framework-ref");
|
|
75
|
+
const previousRef = existsSync(pinFile) ? readFileSync(pinFile, "utf8").trim() : "main";
|
|
76
|
+
const ref = process.env.NIRAL_REF ?? previousRef;
|
|
77
|
+
if (!/^(?:main|[a-f0-9]{40}|v\d+\.\d+\.\d+(?:[-.][A-Za-z0-9.-]+)?)$/.test(ref)) throw new Error("NIRAL_REF must be main, a full lowercase commit SHA, or a v-prefixed version tag");
|
|
78
|
+
if (existsSync(join(FRAMEWORK, "bin", "niral.js")) && !update) {
|
|
79
|
+
if (ref !== previousRef) throw new Error("framework pin differs from the installed version; run niral update with NIRAL_REF set");
|
|
80
|
+
return FRAMEWORK;
|
|
81
|
+
}
|
|
76
82
|
mkdirSync(NIRAL_HOME, { recursive: true });
|
|
77
83
|
const lock = join(NIRAL_HOME, ".framework-install-lock");
|
|
78
84
|
try {
|
|
@@ -95,31 +101,45 @@ export async function ensureFramework({ update = false } = {}) {
|
|
|
95
101
|
return result.stdout.trim();
|
|
96
102
|
};
|
|
97
103
|
if (git(["status", "--porcelain", "--untracked-files=all"])) throw new Error("framework checkout has local changes; preserve them before updating");
|
|
98
|
-
|
|
104
|
+
const branch = git(["branch", "--show-current"]);
|
|
105
|
+
if (branch !== "main" && !(previousRef !== "main" && branch === "")) throw new Error("framework checkout is not on main or a managed pin; update it manually with Git");
|
|
99
106
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
107
|
+
if (ref === "main" && previousRef === "main") {
|
|
108
|
+
const previous = git(["rev-parse", "HEAD"]);
|
|
109
|
+
git(["pull", "--ff-only", "origin", "main"]);
|
|
110
|
+
console.log(`niral: framework ${git(["rev-parse", "--short", "HEAD"])} (previous ${previous})`);
|
|
111
|
+
return FRAMEWORK;
|
|
112
|
+
}
|
|
104
113
|
}
|
|
105
114
|
stage = mkdtempSync(join(NIRAL_HOME, ".framework-stage-"));
|
|
106
115
|
const target = join(stage, "framework");
|
|
107
116
|
let installed = false;
|
|
108
117
|
if (hasGit()) {
|
|
109
|
-
const
|
|
118
|
+
const args = ref === "main" ? ["clone", "-q", "--depth", "1", "--branch", "main", REPO, target] : ["clone", "-q", "--no-checkout", REPO, target];
|
|
119
|
+
const cloned = spawnSync("git", args, {
|
|
110
120
|
encoding: "utf8", timeout: 120000, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
|
|
111
121
|
});
|
|
112
122
|
installed = !cloned.error && cloned.status === 0;
|
|
123
|
+
if (installed && ref !== "main") {
|
|
124
|
+
const checkedOut = spawnSync("git", ["checkout", "--detach", ref], { cwd: target, encoding: "utf8", timeout: 120000, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" } });
|
|
125
|
+
if (checkedOut.error || checkedOut.status !== 0) throw commandError("pinned framework checkout", checkedOut);
|
|
126
|
+
if (/^[a-f0-9]{40}$/.test(ref)) {
|
|
127
|
+
const revision = spawnSync("git", ["rev-parse", "HEAD"], { cwd: target, encoding: "utf8", timeout: 10000 });
|
|
128
|
+
if (revision.status !== 0 || revision.stdout.trim() !== ref) throw new Error("pinned framework revision did not match the requested commit");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
113
131
|
if (!installed) {
|
|
114
132
|
rmSync(target, { recursive: true, force: true });
|
|
115
133
|
console.warn("niral: Git download unavailable; trying the framework archive...");
|
|
116
134
|
}
|
|
117
135
|
}
|
|
118
|
-
if (!installed) await installFromTarball(stage);
|
|
136
|
+
if (!installed) await installFromTarball(stage, ref);
|
|
119
137
|
const manifest = JSON.parse(readFileSync(join(target, "package.json"), "utf8"));
|
|
120
138
|
if (manifest.name !== "niral" || !existsSync(join(target, "src", "index.js"))) throw new Error("download is not a Niral framework");
|
|
121
139
|
const checked = spawnSync(process.execPath, ["--check", join(target, "bin", "niral.js")], { encoding: "utf8", timeout: 10000 });
|
|
122
140
|
if (checked.error || checked.status !== 0) throw commandError("downloaded CLI validation", checked);
|
|
141
|
+
const stagedPin = join(stage, "ref");
|
|
142
|
+
if (ref !== "main") writeFileSync(stagedPin, ref + "\n");
|
|
123
143
|
let backup;
|
|
124
144
|
if (entry) {
|
|
125
145
|
backup = join(mkdtempSync(join(NIRAL_HOME, "framework-backup-")), "framework");
|
|
@@ -127,12 +147,15 @@ export async function ensureFramework({ update = false } = {}) {
|
|
|
127
147
|
}
|
|
128
148
|
try {
|
|
129
149
|
renameSync(target, FRAMEWORK);
|
|
150
|
+
if (ref === "main") rmSync(pinFile, { force: true });
|
|
151
|
+
else renameSync(stagedPin, pinFile);
|
|
130
152
|
} catch (error) {
|
|
153
|
+
if (existsSync(FRAMEWORK)) renameSync(FRAMEWORK, target);
|
|
131
154
|
if (backup) renameSync(backup, FRAMEWORK);
|
|
132
155
|
throw error;
|
|
133
156
|
}
|
|
134
157
|
if (backup) console.log(`niral: previous framework preserved at ${backup}`);
|
|
135
|
-
console.log(`niral: framework ${manifest.version} ready`);
|
|
158
|
+
console.log(`niral: framework ${manifest.version} ready (${ref})`);
|
|
136
159
|
return FRAMEWORK;
|
|
137
160
|
} finally {
|
|
138
161
|
if (stage) rmSync(stage, { recursive: true, force: true });
|
|
@@ -150,11 +173,12 @@ export async function updateFramework({ sourceRoot } = {}) {
|
|
|
150
173
|
export async function runLauncher(args) {
|
|
151
174
|
if (args[0] === "update") {
|
|
152
175
|
if (args.length === 2 && ["--help", "-h"].includes(args[1])) {
|
|
153
|
-
console.log("usage: niral update\nUpdates NIRAL_HOME/framework
|
|
176
|
+
console.log("usage: niral update\nUpdates NIRAL_HOME/framework using its saved NIRAL_REF (default main); does not update npm packages or deploy apps.");
|
|
154
177
|
return 0;
|
|
155
178
|
}
|
|
156
179
|
if (args.length !== 1) throw new Error("usage: niral update");
|
|
157
180
|
await updateFramework();
|
|
181
|
+
if (existsSync(join(NIRAL_HOME, "bin", process.platform === "win32" ? "niral.cmd" : "niral"))) installLauncher();
|
|
158
182
|
return 0;
|
|
159
183
|
}
|
|
160
184
|
if (!args.length || ["help", "--help", "-h"].includes(args[0])) {
|
|
@@ -179,7 +203,23 @@ export async function runLauncher(args) {
|
|
|
179
203
|
export function installLauncher() {
|
|
180
204
|
const binDir = join(NIRAL_HOME, "bin");
|
|
181
205
|
mkdirSync(binDir, { recursive: true });
|
|
182
|
-
const
|
|
206
|
+
const launcherDir = join(NIRAL_HOME, "launcher");
|
|
207
|
+
mkdirSync(launcherDir, { recursive: true });
|
|
208
|
+
writeFileSync(join(launcherDir, "index.mjs"), readFileSync(new URL(import.meta.url)));
|
|
209
|
+
const frameworkCli = join(launcherDir, "run.mjs");
|
|
210
|
+
writeFileSync(frameworkCli, `import { ensureFramework, runLauncher, runNiral } from "./index.mjs";
|
|
211
|
+
try {
|
|
212
|
+
const args = process.argv.slice(2);
|
|
213
|
+
if (args[0] === "update") process.exitCode = await runLauncher(args);
|
|
214
|
+
else {
|
|
215
|
+
await ensureFramework();
|
|
216
|
+
process.exitCode = runNiral(args);
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error(error.message);
|
|
220
|
+
process.exitCode = 1;
|
|
221
|
+
}
|
|
222
|
+
`);
|
|
183
223
|
const shim = join(binDir, process.platform === "win32" ? "niral.cmd" : "niral");
|
|
184
224
|
if (process.platform === "win32") {
|
|
185
225
|
writeFileSync(shim, `@echo off\r\nset "NIRAL_HOME=${NIRAL_HOME.replaceAll("%", "%%")}"\r\n"${process.execPath.replaceAll("%", "%%")}" "${frameworkCli.replaceAll("%", "%%")}" %*\r\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "niral",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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",
|