@hyperez/regna-code 0.1.7 → 0.1.9

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 CHANGED
@@ -49,6 +49,7 @@ For CI or scripted use, set `REGNA_API_KEY` directly; it takes priority over the
49
49
  | `REGNA_GENERAL_MODEL` | (none) | Exact model id that `/regna-general` selects. |
50
50
  | `REGNA_EXPOSE_ALIASES` | (unset) | `1` also exposes path-style (slash) model ids. Hidden by default. Ids pinned via `REGNA_MODEL`/`REGNA_CODER_MODEL`/`REGNA_GENERAL_MODEL` are always shown. |
51
51
  | `REGNA_POLICY` | `off` | Network egress guard: `off` / `warn` (notify, allow) / `enforce` (block external network tools). Set `enforce` for locked-down or air-gapped use. |
52
+ | `REGNA_OFFLINE` | (unset) | `1` runs fully offline: the runtime skips all self-network, including the one-time download of search helpers (`fd`/`ripgrep`). They fall back to the built-in search. Use for air-gapped installs. |
52
53
  | `REGNA_ALLOW_HOSTS` | (none) | Extra allowed hosts (comma-separated) when the policy is on. URL or `host:port` accepted. |
53
54
  | `REGNA_DISCOVER` | (unset) | `1` lets the runtime auto-discover extensions instead of loading the verified set explicitly. |
54
55
  | `REGNA_ENGINE` | (bundled) | Override the path to the runtime executable. Resolved from the bundled dependency by default. |
@@ -80,11 +81,12 @@ When enabled, two features turn on. Use them only where the backend's retrieval
80
81
 
81
82
  ## Air-gapped mode
82
83
 
83
- The runtime never phones home (no update checks or self-network), so Regna Code runs fully offline against a self-hosted deployment. Set:
84
+ Regna Code does no update checks and sends no telemetry. By default it is online only for Regna API calls and a one-time download of its search helpers (`fd`/`ripgrep`). For a fully offline, self-hosted deployment, set:
84
85
 
85
86
  ```
86
87
  export REGNA_BASE_URL="http://<your-gateway>/v1"
87
- export REGNA_POLICY=enforce # block external network tools; allow the gateway, localhost, and private/internal ranges
88
+ export REGNA_OFFLINE=1 # skip all engine self-network (search helpers fall back to the built-in search)
89
+ export REGNA_POLICY=enforce # block external network tools; allow the gateway, localhost, and private/internal ranges
88
90
  ```
89
91
 
90
92
  The egress guard is a defense layer, not a hard sandbox. Enforce real boundaries with OS and network controls and an internal mirror.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as v}from"node:child_process";import{fileURLToPath as P}from"node:url";import{dirname as f,join as o}from"node:path";import{existsSync as p,readFileSync as w,writeFileSync as I,mkdirSync as N,unlinkSync as G,chmodSync as L}from"node:fs";import{homedir as k}from"node:os";import{createInterface as U}from"node:readline/promises";var $=f(P(import.meta.url)),c=f($),g=o(c,"extensions"),_=o(c,"themes","regna.json"),m=o(c,"APPEND_SYSTEM.md"),T="https://regnax.ai/v1",D="regna/regna-pro",h=o(k(),".regna"),d=o(h,"auth.json"),x=o(h,"engine"),a=process.argv.slice(2),u=(...e)=>a.some(t=>e.some(n=>!!(t===n||t.startsWith(`${n}=`)||/^-[a-z]$/i.test(n)&&t.startsWith(n)&&t.length>n.length)));function E(){for(let e of[o(c,"package.json"),o(c,"..","package.json")])try{let t=JSON.parse(w(e,"utf8")).version;if(t)return`v${t}`}catch{}return""}var C=`Regna Code ${E()} - a terminal coding agent on Regna
2
+ import{spawn as v}from"node:child_process";import{fileURLToPath as P}from"node:url";import{dirname as m,join as o}from"node:path";import{existsSync as p,readFileSync as N,writeFileSync as G,mkdirSync as w,unlinkSync as L,chmodSync as k}from"node:fs";import{homedir as U}from"node:os";import{createInterface as $}from"node:readline/promises";var T=m(P(import.meta.url)),c=m(T),g=o(c,"extensions"),_=o(c,"themes","regna.json"),f=o(c,"APPEND_SYSTEM.md"),D="https://regnax.ai/v1",C="regna/regna-pro",h=o(U(),".regna"),d=o(h,"auth.json"),x=o(h,"engine"),a=process.argv.slice(2),u=(...e)=>a.some(t=>e.some(n=>!!(t===n||t.startsWith(`${n}=`)||/^-[a-z]$/i.test(n)&&t.startsWith(n)&&t.length>n.length)));function E(){for(let e of[o(c,"package.json"),o(c,"..","package.json")])try{let t=JSON.parse(N(e,"utf8")).version;if(t)return`v${t}`}catch{}return""}var O=`Regna Code ${E()} - a terminal coding agent on Regna
3
3
 
4
4
  Usage:
5
5
  regna [message...] start interactive, optionally with a first message
@@ -25,27 +25,27 @@ Environment:
25
25
  REGNA_POLICY network egress guard: off | warn | enforce
26
26
 
27
27
  Docs: https://github.com/HyperEZ/regna-code
28
- `;function A(){try{let e=JSON.parse(w(d,"utf8"));return e&&typeof e.apiKey=="string"?e:null}catch{return null}}function B(e,t){N(h,{recursive:!0}),I(d,`${JSON.stringify({apiKey:e,baseUrl:t},null,2)}
29
- `);try{L(d,384)}catch{}}function O(){try{return G(d),!0}catch{return!1}}function j(e){try{return`${new URL(e).origin}/console`}catch{return"https://regnax.ai/console"}}function F(e){try{let[t,n]=process.platform==="darwin"?["open",[e]]:process.platform==="win32"?["cmd",["/c","start","",e]]:["xdg-open",[e]];v(t,n,{stdio:"ignore",detached:!0}).unref()}catch{}}async function K(e,t){try{let n=new AbortController,s=setTimeout(()=>n.abort(),8e3),r=await fetch(`${e.replace(/\/+$/,"")}/models`,{headers:{Authorization:`Bearer ${t}`},signal:n.signal});return clearTimeout(s),r.status===401||r.status===403?"invalid":r.ok?"ok":"unknown"}catch{return"unknown"}}async function S(){let e=(process.env.REGNA_BASE_URL||"").trim()||A()?.baseUrl||T,t=j(e);process.stdout.write(`
28
+ `;function A(){try{let e=JSON.parse(N(d,"utf8"));return e&&typeof e.apiKey=="string"?e:null}catch{return null}}function B(e,t){w(h,{recursive:!0}),G(d,`${JSON.stringify({apiKey:e,baseUrl:t},null,2)}
29
+ `);try{k(d,384)}catch{}}function j(){try{return L(d),!0}catch{return!1}}function F(e){try{return`${new URL(e).origin}/console`}catch{return"https://regnax.ai/console"}}function K(e){try{let[t,n]=process.platform==="darwin"?["open",[e]]:process.platform==="win32"?["cmd",["/c","start","",e]]:["xdg-open",[e]];v(t,n,{stdio:"ignore",detached:!0}).unref()}catch{}}async function Y(e,t){try{let n=new AbortController,s=setTimeout(()=>n.abort(),8e3),r=await fetch(`${e.replace(/\/+$/,"")}/models`,{headers:{Authorization:`Bearer ${t}`},signal:n.signal});return clearTimeout(s),r.status===401||r.status===403?"invalid":r.ok?"ok":"unknown"}catch{return"unknown"}}async function S(){let e=(process.env.REGNA_BASE_URL||"").trim()||A()?.baseUrl||D,t=F(e);process.stdout.write(`
30
30
  Regna Code ${E()}
31
31
  Log in to continue.
32
32
 
33
33
  1) Get an API key at ${t}
34
34
  2) Paste it below.
35
35
 
36
- `),F(t);let n=U({input:process.stdin,output:process.stdout}),s="";try{s=(await n.question(" API key: ")).trim()}finally{n.close()}s||(process.stderr.write(` No key entered. Aborting.
37
- `),process.exit(1));let r=await K(e,s);return r==="invalid"&&(process.stderr.write(` That key was rejected. Check it and run: regna login
36
+ `),K(t);let n=$({input:process.stdin,output:process.stdout}),s="";try{s=(await n.question(" API key: ")).trim()}finally{n.close()}s||(process.stderr.write(` No key entered. Aborting.
37
+ `),process.exit(1));let r=await Y(e,s);return r==="invalid"&&(process.stderr.write(` That key was rejected. Check it and run: regna login
38
38
  `),process.exit(1)),B(s,e),process.stdout.write(r==="ok"?` Logged in.
39
39
 
40
40
  `:` Saved (could not verify right now).
41
41
 
42
- `),s}(u("--help","-h")||a[0]==="help")&&(process.stdout.write(C),process.exit(0));(u("--version")||a[0]==="version")&&(process.stdout.write(`Regna Code ${E()}
43
- `),process.exit(0));a[0]==="login"&&(await S(),process.exit(0));a[0]==="logout"&&(process.stdout.write(O()?`Logged out.
42
+ `),s}(u("--help","-h")||a[0]==="help")&&(process.stdout.write(O),process.exit(0));(u("--version")||a[0]==="version")&&(process.stdout.write(`Regna Code ${E()}
43
+ `),process.exit(0));a[0]==="login"&&(await S(),process.exit(0));a[0]==="logout"&&(process.stdout.write(j()?`Logged out.
44
44
  `:`Not logged in.
45
45
  `),process.exit(0));var l=(process.env.REGNA_API_KEY||"").trim();if(!l){let e=A();e?.apiKey&&(l=e.apiKey.trim(),!process.env.REGNA_BASE_URL&&e.baseUrl&&(process.env.REGNA_BASE_URL=e.baseUrl))}if(!l)if(process.stdin.isTTY&&process.stdout.isTTY){if(l=await S(),!process.env.REGNA_BASE_URL){let e=A();e?.baseUrl&&(process.env.REGNA_BASE_URL=e.baseUrl)}}else process.stderr.write(`[regna] Not logged in. Run: regna login
46
- `),process.exit(1);process.env.REGNA_API_KEY=l;var i=[],Y=process.env.REGNA_DISCOVER==="1";if(!Y){i.push("--no-extensions");let e=["provider","policy","branding","model","context","exit","docs-search","docs-analysis"],t=r=>{let R=o(g,`${r}.js`);if(p(R))return R;let y=o(g,`${r}.ts`);return p(y)?y:null},n=e.map(r=>[r,t(r)]),s=n.filter(([,r])=>!r).map(([r])=>r);s.length>0&&(process.stderr.write(`[regna] Missing required extensions: ${s.join(", ")} (in ${g}). Install is corrupted. Aborting.
47
- `),process.exit(1));for(let[,r]of n)i.push("-e",r)}!u("--theme")&&p(_)&&i.push("--theme",_);u("--system-prompt")||(p(m)||(process.stderr.write(`[regna] Missing required prompt: APPEND_SYSTEM.md (${m}). Install is corrupted. Aborting.
46
+ `),process.exit(1);process.env.REGNA_API_KEY=l;var i=[],q=process.env.REGNA_DISCOVER==="1";if(!q){i.push("--no-extensions");let e=["provider","policy","branding","model","context","exit","docs-search","docs-analysis"],t=r=>{let R=o(g,`${r}.js`);if(p(R))return R;let y=o(g,`${r}.ts`);return p(y)?y:null},n=e.map(r=>[r,t(r)]),s=n.filter(([,r])=>!r).map(([r])=>r);s.length>0&&(process.stderr.write(`[regna] Missing required extensions: ${s.join(", ")} (in ${g}). Install is corrupted. Aborting.
47
+ `),process.exit(1));for(let[,r]of n)i.push("-e",r)}!u("--theme")&&p(_)&&i.push("--theme",_);u("--system-prompt")||(p(f)||(process.stderr.write(`[regna] Missing required prompt: APPEND_SYSTEM.md (${f}). Install is corrupted. Aborting.
48
48
  (To set the prompt yourself on purpose, run with --system-prompt.)
49
- `),process.exit(1)),i.push("--append-system-prompt",`@${m}`));u("--model","-m")||i.push("--model",process.env.REGNA_MODEL||D);i.push(...a);try{N(x,{recursive:!0})}catch{}var q={...process.env,PI_OFFLINE:"1",PI_CODING_AGENT_DIR:x};function M(){let e=o("node_modules","@earendil-works","pi-coding-agent","dist","cli.js"),t=c;for(let n=0;n<10;n++){let s=o(t,e);if(p(s))return s;let r=f(t);if(r===t)break;t=r}return null}function H(){let e=process.env.REGNA_ENGINE;if(e)return{cmd:e,prefix:[],label:e};let t=M();return t?{cmd:process.execPath,prefix:[t],label:t}:{cmd:"pi",prefix:[],label:"runtime (PATH)"}}var{cmd:z,prefix:J,label:V}=H(),b=v(z,[...J,...i],{stdio:"inherit",env:q});b.on("error",e=>{e&&e.code==="ENOENT"&&(process.stderr.write(`[regna] Runtime executable not found (${V}). Reinstall (npm i -g @hyperez/regna-code) or set REGNA_ENGINE.
49
+ `),process.exit(1)),i.push("--append-system-prompt",`@${f}`));u("--model","-m")||i.push("--model",process.env.REGNA_MODEL||C);i.push(...a);try{w(x,{recursive:!0})}catch{}var b={...process.env,PI_CODING_AGENT_DIR:x};process.env.REGNA_OFFLINE==="1"&&(b.PI_OFFLINE="1");function M(){let e=o("node_modules","@earendil-works","pi-coding-agent","dist","cli.js"),t=c;for(let n=0;n<10;n++){let s=o(t,e);if(p(s))return s;let r=m(t);if(r===t)break;t=r}return null}function H(){let e=process.env.REGNA_ENGINE;if(e)return{cmd:e,prefix:[],label:e};let t=M();return t?{cmd:process.execPath,prefix:[t],label:t}:{cmd:"pi",prefix:[],label:"runtime (PATH)"}}var{cmd:z,prefix:J,label:V}=H(),I=v(z,[...J,...i],{stdio:"inherit",env:b});I.on("error",e=>{e&&e.code==="ENOENT"&&(process.stderr.write(`[regna] Runtime executable not found (${V}). Reinstall (npm i -g @hyperez/regna-code) or set REGNA_ENGINE.
50
50
  `),process.exit(127)),process.stderr.write(`[regna] Failed to start the runtime: ${e?.message??String(e)}
51
- `),process.exit(1)});b.on("exit",(e,t)=>{if(t){process.kill(process.pid,t);return}process.exit(e??0)});
51
+ `),process.exit(1)});I.on("exit",(e,t)=>{if(t){process.kill(process.pid,t);return}process.exit(e??0)});
@@ -15,6 +15,17 @@
15
15
  * entry from the built-in slash-command menu and neutralize its handler so /quit no longer
16
16
  * triggers shutdown (Ctrl+C/Ctrl+D still exit).
17
17
  *
18
+ * 3) Resume hint. On exit the engine prints "To resume this session: <prog> --session <id>"
19
+ * where <prog> is APP_NAME ("pi"). We rewrite that one program token to "regna" so the
20
+ * printed command actually works as typed. The session dir is the isolated Regna one, so
21
+ * `regna --session <id>` resumes correctly with no extra flags.
22
+ *
23
+ * 4) Disable the update banner and the install telemetry to pi.dev unconditionally. Both are
24
+ * guarded by the same `if (process.env.PI_OFFLINE)` early-return, so we force that guard to
25
+ * always take. This lets us run online by default (so search helpers fd/ripgrep download
26
+ * on first run) without re-enabling the update check or phoning telemetry home. Fully
27
+ * offline air-gapped use is then an explicit REGNA_OFFLINE=1 opt-in in the launcher.
28
+ *
18
29
  * Best effort: every patch is wrapped so a failure (e.g. the engine changed its layout) just
19
30
  * leaves that aspect at the engine default and never fails the install. All patches are
20
31
  * idempotent and safe to run on every install.
@@ -48,8 +59,7 @@ function patchEngineFile(relInsidePackage, re, replacement) {
48
59
  const path = findEngineFile(relInsidePackage);
49
60
  if (!path) return;
50
61
  const src = readFileSync(path, "utf8");
51
- if (!re.test(src)) return; // already patched or layout changed
52
- const out = src.replace(re, replacement);
62
+ const out = src.replace(re, replacement); // no-op if pattern absent (already patched)
53
63
  if (out !== src) writeFileSync(path, out);
54
64
  } catch {
55
65
  /* best effort; never fail the install */
@@ -76,3 +86,18 @@ patchEngineFile(
76
86
  /if\s*\(\s*text\s*===\s*"\/quit"\s*\)/,
77
87
  'if (text === "/__regna_quit_removed__")',
78
88
  );
89
+
90
+ // 3) Resume hint program name: "<pi> --session <id>" -> "regna --session <id>".
91
+ patchEngineFile(
92
+ join("dist", "modes", "interactive", "interactive-mode.js"),
93
+ /const args = \[APP_NAME\];/,
94
+ 'const args = ["regna"];',
95
+ );
96
+
97
+ // 4) Force-disable the update banner + pi.dev install telemetry (both gated by the same guard),
98
+ // independent of PI_OFFLINE, so online-by-default does not re-enable them.
99
+ patchEngineFile(
100
+ join("dist", "modes", "interactive", "interactive-mode.js"),
101
+ /if \(process\.env\.PI_OFFLINE\) \{/g,
102
+ "if (true) {",
103
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperez/regna-code",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
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,