@hyperez/regna-code 0.1.9 → 0.2.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 +16 -11
- package/dist/bin/regna.mjs +35 -18
- package/dist/postinstall.mjs +16 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,28 +15,33 @@ Regna Code is a thin client. The models, knowledge, and routing live behind the
|
|
|
15
15
|
npm i -g @hyperez/regna-code
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Then just run it. On first launch it
|
|
18
|
+
Then just run it. On first launch it asks how you want to connect:
|
|
19
19
|
|
|
20
20
|
```
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
Choose how to connect:
|
|
22
|
+
1) Online - Regna cloud (regnax.ai)
|
|
23
|
+
2) Offline - air-gapped / self-hosted gateway
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
- **Online**: opens https://regnax.ai/console, you paste an API key once. Default model `regna-pro`.
|
|
27
|
+
- **Offline**: you enter your internal gateway URL (OpenAI-compatible `/v1`) and key, then pick a default model from whatever the gateway serves. Offline runs with no engine self-network and the egress guard enforced.
|
|
28
|
+
|
|
29
|
+
Your choice and credentials are stored at `~/.regna/auth.json`. After that:
|
|
27
30
|
|
|
28
31
|
```
|
|
29
|
-
regna
|
|
30
|
-
regna
|
|
32
|
+
regna # starts interactive
|
|
33
|
+
regna "explain this repo" # with a prompt
|
|
34
|
+
regna -p "summarize today's changes" < /dev/null # headless/scripted (close stdin)
|
|
31
35
|
```
|
|
32
36
|
|
|
33
|
-
By default Regna Code talks to the Regna cloud (`https://regnax.ai/v1`). To use a self-hosted or air-gapped deployment, point it at your own endpoint before logging in:
|
|
34
|
-
|
|
35
37
|
```
|
|
36
|
-
|
|
38
|
+
regna login # re-run the Online/Offline chooser
|
|
39
|
+
regna login online # set up Online directly
|
|
40
|
+
regna login offline # set up Offline directly
|
|
41
|
+
regna logout # remove stored credentials
|
|
37
42
|
```
|
|
38
43
|
|
|
39
|
-
For CI or scripted use, set `REGNA_API_KEY` directly; it takes priority over the stored login.
|
|
44
|
+
For CI or scripted use, set `REGNA_API_KEY` directly; it takes priority over the stored login. Add `REGNA_OFFLINE=1` and `REGNA_BASE_URL` for non-interactive air-gapped use.
|
|
40
45
|
|
|
41
46
|
## Environment variables
|
|
42
47
|
|
package/dist/bin/regna.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{spawn as
|
|
2
|
+
import{spawn as x}from"node:child_process";import{fileURLToPath as D}from"node:url";import{dirname as R,join as i}from"node:path";import{existsSync as m,readFileSync as I,writeFileSync as F,mkdirSync as $,unlinkSync as M,chmodSync as j}from"node:fs";import{homedir as B}from"node:os";import{createInterface as K}from"node:readline";var Y=R(D(import.meta.url)),u=R(Y),E=i(u,"extensions"),_=i(u,"themes","regna.json"),A=i(u,"APPEND_SYSTEM.md"),k="https://regnax.ai/v1",q="regna/regna-pro",v=i(B(),".regna"),y=i(v,"auth.json"),P=i(v,"engine"),l=process.argv.slice(2),h=(...e)=>l.some(t=>e.some(n=>!!(t===n||t.startsWith(`${n}=`)||/^-[a-z]$/i.test(n)&&t.startsWith(n)&&t.length>n.length)));function N(){for(let e of[i(u,"package.json"),i(u,"..","package.json")])try{let t=JSON.parse(I(e,"utf8")).version;if(t)return`v${t}`}catch{}return""}var z=`Regna Code ${N()} - a terminal coding agent on Regna
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
5
|
regna [message...] start interactive, optionally with a first message
|
|
@@ -7,7 +7,9 @@ Usage:
|
|
|
7
7
|
regna -p "summarize changes" < /dev/null headless/scripted (close stdin)
|
|
8
8
|
|
|
9
9
|
Auth:
|
|
10
|
-
regna login
|
|
10
|
+
regna login choose Online/Offline and store credentials
|
|
11
|
+
regna login online set up Online (Regna cloud) directly
|
|
12
|
+
regna login offline set up Offline (air-gapped gateway) directly
|
|
11
13
|
regna logout remove stored credentials
|
|
12
14
|
|
|
13
15
|
In-session commands:
|
|
@@ -25,27 +27,42 @@ Environment:
|
|
|
25
27
|
REGNA_POLICY network egress guard: off | warn | enforce
|
|
26
28
|
|
|
27
29
|
Docs: https://github.com/HyperEZ/regna-code
|
|
28
|
-
`;function
|
|
29
|
-
`);try{
|
|
30
|
-
Regna Code ${
|
|
31
|
-
|
|
30
|
+
`;function H(){try{let e=JSON.parse(I(y,"utf8"));return e&&typeof e.apiKey=="string"?e:null}catch{return null}}function J(e){$(v,{recursive:!0}),F(y,`${JSON.stringify(e,null,2)}
|
|
31
|
+
`);try{j(y,384)}catch{}}function V(){try{return M(y),!0}catch{return!1}}function W(e){try{return`${new URL(e).origin}/console`}catch{return"https://regnax.ai/console"}}function Z(e){try{let[t,n]=process.platform==="darwin"?["open",[e]]:process.platform==="win32"?["cmd",["/c","start","",e]]:["xdg-open",[e]];x(t,n,{stdio:"ignore",detached:!0}).unref()}catch{}}async function b(e,t){try{let n=new AbortController,r=setTimeout(()=>n.abort(),8e3),s=await fetch(`${e.replace(/\/+$/,"")}/models`,{headers:{Authorization:`Bearer ${t}`},signal:n.signal});if(clearTimeout(r),s.status===401||s.status===403)return{status:"invalid",ids:[]};if(!s.ok)return{status:"unknown",ids:[]};let o=await s.json().catch(()=>({}));return{status:"ok",ids:Array.isArray(o?.data)?o.data.map(p=>p&&typeof p.id=="string"?p.id:"").filter(Boolean):[]}}catch{return{status:"unknown",ids:[]}}}function Q(e){let t=(e||"").trim();if(!t)return"";/^https?:\/\//i.test(t)||(t=`http://${t}`);try{let n=new URL(t),r=`${n.origin}${n.pathname}`.replace(/\/+$/,"");return/\/v\d+$/.test(r)||(r=`${r}/v1`),r}catch{return""}}function X(){let e=K({input:process.stdin}),t=[],n=[],r=!1;return e.on("line",o=>{n.length?n.shift()(o):t.push(o)}),e.on("close",()=>{for(r=!0;n.length;)n.shift()(null)}),{ask:o=>(process.stdout.write(o),new Promise(a=>{t.length?a(t.shift()):r?a(null):n.push(a)})),close:()=>e.close()}}async function ee(e){return process.stdout.write(`
|
|
32
|
+
Regna Code ${N()}
|
|
33
|
+
Choose how to connect:
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
1) Online - Regna cloud (regnax.ai)
|
|
36
|
+
2) Offline - air-gapped / self-hosted gateway
|
|
37
|
+
|
|
38
|
+
`),(await e(" Select [1]: ")||"").trim()==="2"?"offline":"online"}async function te(e){let t=k,n=W(t);process.stdout.write(`
|
|
39
|
+
Online mode (Regna cloud).
|
|
40
|
+
1) Get an API key at ${n}
|
|
34
41
|
2) Paste it below.
|
|
35
42
|
|
|
36
|
-
`),
|
|
37
|
-
`),process.exit(1));let
|
|
38
|
-
`),process.exit(1)),
|
|
43
|
+
`),Z(n);let r=(await e(" API key: ")||"").trim();r||(process.stderr.write(` No key entered. Aborting.
|
|
44
|
+
`),process.exit(1));let{status:s}=await b(t,r);return s==="invalid"&&(process.stderr.write(` That key was rejected. Run: regna login
|
|
45
|
+
`),process.exit(1)),{mode:"online",baseUrl:t,apiKey:r}}async function ne(e){process.stdout.write(`
|
|
46
|
+
Offline mode (air-gapped).
|
|
47
|
+
Point Regna Code at your internal gateway (OpenAI-compatible /v1).
|
|
39
48
|
|
|
40
|
-
|
|
49
|
+
`);let t=(await e(" Gateway URL [http://localhost:8077/v1]: ")||"").trim(),n=Q(t)||"http://localhost:8077/v1",r=(await e(" API key: ")||"").trim();r||(process.stderr.write(` No key entered. Aborting.
|
|
50
|
+
`),process.exit(1));let{status:s,ids:o}=await b(n,r);s==="invalid"&&(process.stderr.write(` That key was rejected by the gateway. Run: regna login
|
|
51
|
+
`),process.exit(1));let a="regna/default";if(s==="ok"&&o.length>0){let p=o.filter(w=>w==="default"||!w.includes("/")),f=p.length?p:o;if(f.length===1)a=`regna/${f[0]}`;else{process.stdout.write(`
|
|
52
|
+
Models served by the gateway:
|
|
53
|
+
`),f.forEach((T,U)=>process.stdout.write(` ${U+1}) ${T}
|
|
54
|
+
`));let w=(await e(`
|
|
55
|
+
Default model [1]: `)||"").trim(),C=Math.max(1,Math.min(f.length,parseInt(w||"1",10)||1))-1;a=`regna/${f[C]}`}}else process.stdout.write(` (Could not list models now; will use the gateway's active model.)
|
|
56
|
+
`);return{mode:"offline",baseUrl:n,apiKey:r,model:a}}async function G(e){let{ask:t,close:n}=X();try{let s=(e||await ee(t))==="offline"?await ne(t):await te(t);return J(s),process.stdout.write(`
|
|
57
|
+
Saved. Mode: ${s.mode}${s.model?`, default model: ${s.model}`:""}.
|
|
41
58
|
|
|
42
|
-
`),s}(
|
|
43
|
-
`),process.exit(0));
|
|
59
|
+
`),s}finally{n()}}(h("--help","-h")||l[0]==="help")&&(process.stdout.write(z),process.exit(0));(h("--version")||l[0]==="version")&&(process.stdout.write(`Regna Code ${N()}
|
|
60
|
+
`),process.exit(0));if(l[0]==="login"){let e=l[1]==="online"||l[1]==="offline"?l[1]:void 0;await G(e),process.exit(0)}l[0]==="logout"&&(process.stdout.write(V()?`Logged out.
|
|
44
61
|
`:`Not logged in.
|
|
45
|
-
`),process.exit(0));var
|
|
46
|
-
`),process.exit(1);process.env.REGNA_API_KEY=
|
|
47
|
-
`),process.exit(1));for(let[,
|
|
62
|
+
`),process.exit(0));var c=null,g=(process.env.REGNA_API_KEY||"").trim();g?c={mode:process.env.REGNA_OFFLINE==="1"?"offline":"online",apiKey:g}:(c=H(),c?.apiKey?g=c.apiKey.trim():process.stdin.isTTY&&process.stdout.isTTY?(c=await G(),g=c.apiKey.trim()):(process.stderr.write(`[regna] Not logged in. Run: regna login
|
|
63
|
+
`),process.exit(1)));var O=process.env.REGNA_OFFLINE==="1"||c?.mode==="offline",se=(process.env.REGNA_BASE_URL||"").trim()||c?.baseUrl||k,re=(process.env.REGNA_MODEL||"").trim()||c?.model||(O?"regna/default":q);process.env.REGNA_API_KEY=g;process.env.REGNA_BASE_URL=se;O&&(process.env.REGNA_OFFLINE||(process.env.REGNA_OFFLINE="1"),process.env.REGNA_POLICY||(process.env.REGNA_POLICY="enforce"));var d=[],oe=process.env.REGNA_DISCOVER==="1";if(!oe){d.push("--no-extensions");let e=["provider","policy","branding","model","context","exit","docs-search","docs-analysis"],t=s=>{let o=i(E,`${s}.js`);if(m(o))return o;let a=i(E,`${s}.ts`);return m(a)?a:null},n=e.map(s=>[s,t(s)]),r=n.filter(([,s])=>!s).map(([s])=>s);r.length>0&&(process.stderr.write(`[regna] Missing required extensions: ${r.join(", ")} (in ${E}). Install is corrupted. Aborting.
|
|
64
|
+
`),process.exit(1));for(let[,s]of n)d.push("-e",s)}!h("--theme")&&m(_)&&d.push("--theme",_);h("--system-prompt")||(m(A)||(process.stderr.write(`[regna] Missing required prompt: APPEND_SYSTEM.md (${A}). Install is corrupted. Aborting.
|
|
48
65
|
(To set the prompt yourself on purpose, run with --system-prompt.)
|
|
49
|
-
`),process.exit(1)),
|
|
66
|
+
`),process.exit(1)),d.push("--append-system-prompt",`@${A}`));h("--model","-m")||d.push("--model",re);d.push(...l);try{$(P,{recursive:!0})}catch{}var L={...process.env,PI_CODING_AGENT_DIR:P};process.env.REGNA_OFFLINE==="1"&&(L.PI_OFFLINE="1");function ie(){let e=i("node_modules","@earendil-works","pi-coding-agent","dist","cli.js"),t=u;for(let n=0;n<10;n++){let r=i(t,e);if(m(r))return r;let s=R(t);if(s===t)break;t=s}return null}function ae(){let e=process.env.REGNA_ENGINE;if(e)return{cmd:e,prefix:[],label:e};let t=ie();return t?{cmd:process.execPath,prefix:[t],label:t}:{cmd:"pi",prefix:[],label:"runtime (PATH)"}}var{cmd:ce,prefix:le,label:de}=ae(),S=x(ce,[...le,...d],{stdio:"inherit",env:L});S.on("error",e=>{e&&e.code==="ENOENT"&&(process.stderr.write(`[regna] Runtime executable not found (${de}). Reinstall (npm i -g @hyperez/regna-code) or set REGNA_ENGINE.
|
|
50
67
|
`),process.exit(127)),process.stderr.write(`[regna] Failed to start the runtime: ${e?.message??String(e)}
|
|
51
|
-
`),process.exit(1)});
|
|
68
|
+
`),process.exit(1)});S.on("exit",(e,t)=>{if(t){process.kill(process.pid,t);return}process.exit(e??0)});
|
package/dist/postinstall.mjs
CHANGED
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
* on first run) without re-enabling the update check or phoning telemetry home. Fully
|
|
27
27
|
* offline air-gapped use is then an explicit REGNA_OFFLINE=1 opt-in in the launcher.
|
|
28
28
|
*
|
|
29
|
+
* 5) Hide the "Extensions" and "Themes" sections of the startup resource panel (internal
|
|
30
|
+
* implementation detail). Context and Skills still show.
|
|
31
|
+
*
|
|
29
32
|
* Best effort: every patch is wrapped so a failure (e.g. the engine changed its layout) just
|
|
30
33
|
* leaves that aspect at the engine default and never fails the install. All patches are
|
|
31
34
|
* idempotent and safe to run on every install.
|
|
@@ -101,3 +104,16 @@ patchEngineFile(
|
|
|
101
104
|
/if \(process\.env\.PI_OFFLINE\) \{/g,
|
|
102
105
|
"if (true) {",
|
|
103
106
|
);
|
|
107
|
+
|
|
108
|
+
// 5) Hide the "Extensions" and "Themes" sections from the startup resource panel. These are
|
|
109
|
+
// Regna Code internals the user does not need to see (Context and Skills still show).
|
|
110
|
+
patchEngineFile(
|
|
111
|
+
join("dist", "modes", "interactive", "interactive-mode.js"),
|
|
112
|
+
/addLoadedSection\("Extensions", extensionCompactList, extList, "mdHeading"\);/,
|
|
113
|
+
"void 0;",
|
|
114
|
+
);
|
|
115
|
+
patchEngineFile(
|
|
116
|
+
join("dist", "modes", "interactive", "interactive-mode.js"),
|
|
117
|
+
/addLoadedSection\("Themes", themeCompactList, themeList\);/,
|
|
118
|
+
"void 0;",
|
|
119
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperez/regna-code",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Regna Code. A terminal coding agent on Regna. Install with `npm i -g @hyperez/regna-code` and run `regna`. Works with the Regna cloud API and on-premise deployments.",
|
|
5
5
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
6
6
|
"private": false,
|