9remote 0.1.62 → 0.1.63

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 +119 -0
  2. package/dist/cli.cjs +2 -2
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ <div align="center">
2
+
3
+ # 9remote
4
+
5
+ **Your Mac/Linux terminal in your pocket — anywhere, instantly**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/9remote)](https://www.npmjs.com/package/9remote)
8
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+ [![npm downloads](https://img.shields.io/npm/dm/9remote)](https://www.npmjs.com/package/9remote)
10
+
11
+ [🌐 Web App](https://9remote.cc) • [📖 Docs](https://docs.9remote.cc) • [🚀 Get Started](#-quick-start)
12
+
13
+ </div>
14
+
15
+ ---
16
+
17
+ ## Why 9remote?
18
+
19
+ - ❌ **SSH is a hassle** — need to open firewall, configure port forwarding, manage keys
20
+ - ❌ **VPN is overkill** — too complex to set up just to check a terminal
21
+ - ❌ **ngrok / tunnels expire** — lose connection, have to restart everything
22
+
23
+ **9remote solves it:**
24
+ - ✅ **One command** — `npx 9remote`, scan QR, done
25
+ - ✅ **Auto tunnel** — Cloudflare tunnel starts automatically, no port forwarding
26
+ - ✅ **Works on phone** — full terminal from your browser, under 50ms latency
27
+ - ✅ **Remote Desktop** — view and control your screen, mouse & keyboard
28
+ - ✅ **Persistent sessions** — PTY daemon survives server restarts
29
+
30
+ ---
31
+
32
+ ## ⚡ Quick Start
33
+
34
+ ```bash
35
+ npx 9remote
36
+ ```
37
+
38
+ Scan the QR code on your phone → you're in.
39
+
40
+ Or install globally:
41
+
42
+ ```bash
43
+ npm install -g 9remote
44
+ 9remote
45
+ ```
46
+
47
+ > **Ready in 30 seconds. No config needed.**
48
+
49
+ ---
50
+
51
+ ## ✨ Features
52
+
53
+ - 🖥️ **Remote Terminal** — Full PTY shell, always on, persistent across reconnects
54
+ - 🖱️ **Remote Desktop** — Live screen streaming via WebRTC + mouse/keyboard control
55
+ - 📁 **File Explorer** — Browse, upload, download files from your browser
56
+ - 📱 **QR Login** — One-time key (30 min) for quick mobile access
57
+ - 🔒 **E2E Secure** — All traffic through Cloudflare tunnel, no open ports
58
+ - 🔄 **Auto Restart** — Server + tunnel auto-recover on crash
59
+
60
+ ---
61
+
62
+ ## 📖 CLI Commands
63
+
64
+ | Command | Description |
65
+ |---|---|
66
+ | `9remote` | TUI mode — interactive menu with QR |
67
+ | `9remote ui` | Web UI mode — open browser dashboard |
68
+ | `9remote start` | Auto start server + tunnel (headless) |
69
+
70
+ ---
71
+
72
+ <details>
73
+ <summary><b>🔧 How it works</b></summary>
74
+
75
+ 1. **Start** — `9remote` spawns a local server on port `2208`
76
+ 2. **Tunnel** — A Cloudflare Quick Tunnel is created automatically (no account needed)
77
+ 3. **QR** — A one-time login link is generated and shown as QR code
78
+ 4. **Connect** — Scan from your phone → authenticated session via [9remote.cc](https://9remote.cc)
79
+ 5. **Transport** — Terminal uses WebSocket; Remote Desktop uses WebRTC DataChannel for low latency
80
+
81
+ </details>
82
+
83
+ <details>
84
+ <summary><b>📡 Remote Desktop</b></summary>
85
+
86
+ - Screen streaming via WebRTC (adaptive: 60ms active / 400ms idle)
87
+ - Tile-based diff rendering — only changed regions are sent
88
+ - Mouse & keyboard control via `robotjs`
89
+ - Requires macOS permissions: **Screen Recording** + **Accessibility**
90
+
91
+ Enable from TUI menu: `Remote Desktop → Toggle ON`
92
+
93
+ </details>
94
+
95
+ <details>
96
+ <summary><b>🔑 Keys & Security</b></summary>
97
+
98
+ - **Permanent Key** — stored locally, tied to your machine ID
99
+ - **One-Time Key** — 30-minute temporary key for quick phone access
100
+ - Keys are never stored on our servers after session ends
101
+ - Regenerate your key anytime from the TUI menu
102
+
103
+ </details>
104
+
105
+ ---
106
+
107
+ ## 🛠️ Built With
108
+
109
+ - [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) — Zero-config secure tunnel
110
+ - [node-datachannel](https://github.com/murat-dogan/node-datachannel) — WebRTC for low-latency desktop streaming
111
+ - [node-pty](https://github.com/microsoft/node-pty) — Persistent PTY terminal sessions
112
+ - [Socket.IO](https://socket.io/) — Real-time terminal + signaling
113
+ - [Preact](https://preactjs.com/) — Lightweight Web UI
114
+
115
+ ---
116
+
117
+ ## 📝 License
118
+
119
+ MIT © [9remote](https://github.com/decolua/9remote)
package/dist/cli.cjs CHANGED
@@ -9,7 +9,7 @@ var Zn=Object.create;var Dt=Object.defineProperty;var er=Object.getOwnPropertyDe
9
9
  `:`
10
10
  `)+t,e=o+1,o=r.indexOf(`
11
11
  `,e)}while(o!==-1);return i+=r.slice(e),i}var{stdout:Yt,stderr:Vt}=Ht,ft=Symbol("GENERATOR"),Te=Symbol("STYLER"),Ie=Symbol("IS_EMPTY"),Jt=["ansi","ansi","ansi256","ansi16m"],Ee=Object.create(null),hr=(r,n={})=>{if(n.level&&!(Number.isInteger(n.level)&&n.level>=0&&n.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");let t=Yt?Yt.level:0;r.level=n.level===void 0?t:n.level};var dr=r=>{let n=(...t)=>t.join(" ");return hr(n,r),Object.setPrototypeOf(n,Me.prototype),n};function Me(r){return dr(r)}Object.setPrototypeOf(Me.prototype,Function.prototype);for(let[r,n]of Object.entries(Z))Ee[r]={get(){let t=Qe(this,ht(n.open,n.close,this[Te]),this[Ie]);return Object.defineProperty(this,r,{value:t}),t}};Ee.visible={get(){let r=Qe(this,this[Te],!0);return Object.defineProperty(this,"visible",{value:r}),r}};var pt=(r,n,t,...o)=>r==="rgb"?n==="ansi16m"?Z[t].ansi16m(...o):n==="ansi256"?Z[t].ansi256(Z.rgbToAnsi256(...o)):Z[t].ansi(Z.rgbToAnsi(...o)):r==="hex"?pt("rgb",n,t,...Z.hexToRgb(...o)):Z[t][r](...o),mr=["rgb","hex","ansi256"];for(let r of mr){Ee[r]={get(){let{level:t}=this;return function(...o){let e=ht(pt(r,Jt[t],"color",...o),Z.color.close,this[Te]);return Qe(this,e,this[Ie])}}};let n="bg"+r[0].toUpperCase()+r.slice(1);Ee[n]={get(){let{level:t}=this;return function(...o){let e=ht(pt(r,Jt[t],"bgColor",...o),Z.bgColor.close,this[Te]);return Qe(this,e,this[Ie])}}}}var gr=Object.defineProperties(()=>{},{...Ee,level:{enumerable:!0,get(){return this[ft].level},set(r){this[ft].level=r}}}),ht=(r,n,t)=>{let o,e;return t===void 0?(o=r,e=n):(o=t.openAll+r,e=n+t.closeAll),{open:r,close:n,openAll:o,closeAll:e,parent:t}},Qe=(r,n,t)=>{let o=(...e)=>yr(o,e.length===1?""+e[0]:e.join(" "));return Object.setPrototypeOf(o,gr),o[ft]=r,o[Te]=n,o[Ie]=t,o},yr=(r,n)=>{if(r.level<=0||!n)return r[Ie]?"":n;let t=r[Te];if(t===void 0)return n;let{openAll:o,closeAll:e}=t;if(n.includes("\x1B"))for(;t!==void 0;)n=Wt(n,t.close,t.open),t=t.parent;let i=n.indexOf(`
12
- `);return i!==-1&&(n=Qt(n,e,o,i)),o+n+e};Object.defineProperties(Me.prototype,Ee);var vr=Me(),yo=Me({level:Vt?Vt.level:0});var v=vr;var Mt=M(Tn(),1),ke=require("child_process"),ge=M(require("path"),1),Vn=require("url"),st=M(require("fs"),1),Jn=M(require("os"),1);var bn=M(En(),1),Tt=M(require("crypto"),1),{machineIdSync:Rr}=bn.default;async function be(r=null){let n=r||process.env.MACHINE_ID_SALT||"9remote-salt";try{let t=Rr();return Tt.default.createHash("sha256").update(t+n).digest("hex").substring(0,16)}catch(t){return console.error("Error getting machine ID:",t),Tt.default.randomUUID().replace(/-/g,"").substring(0,16)}}var _n=M(require("crypto"),1),Ar=process.env.API_KEY_SECRET||"9remote-api-key-secret";function Or(){let r="abcdefghijklmnopqrstuvwxyz0123456789",n="";for(let t=0;t<4;t++)n+=r.charAt(Math.floor(Math.random()*r.length));return n}function Pr(r,n){return _n.default.createHmac("sha256",Ar).update(r+n).digest("hex").slice(0,6)}function _e(r){let n=r.slice(0,8),t=Or(),o=Pr(n,t);return{key:`sk-${n}-${t}-${o}`,keyId:t}}var Q=M(require("fs"),1),Ne=M(require("path"),1),xn=M(require("os"),1),$e=Ne.default.join(xn.default.homedir(),".9remote"),Et=Ne.default.join($e,"state.json"),bt=Ne.default.join($e,"keys.json"),qe=Ne.default.join($e,"cmd.json");function Xe(){Q.default.existsSync($e)||Q.default.mkdirSync($e,{recursive:!0})}function _t(r){try{Xe(),Q.default.writeFileSync(Et,JSON.stringify(r,null,2))}catch(n){console.error("Error saving state:",n)}}function Sn(){try{Q.default.existsSync(Et)&&Q.default.unlinkSync(Et)}catch{}}function Be(){try{return Xe(),Q.default.existsSync(bt)?JSON.parse(Q.default.readFileSync(bt,"utf8")):{machineId:null,key:null,name:"Default",createdAt:null}}catch{return{machineId:null,key:null,name:"Default",createdAt:null}}}function kn(r){try{Xe(),Q.default.writeFileSync(qe,JSON.stringify({cmd:r,ts:Date.now()}))}catch{}}function Rn(){try{if(!Q.default.existsSync(qe))return null;let r=JSON.parse(Q.default.readFileSync(qe,"utf8"));return Q.default.unlinkSync(qe),r.cmd}catch{return null}}function xe(r,n,t="Default"){try{Xe();let o={machineId:r,key:n,name:t,createdAt:new Date().toISOString()};return Q.default.writeFileSync(bt,JSON.stringify(o,null,2)),o}catch(o){return console.error("Error saving key:",o),null}}async function De(r,n){try{let t=await fetch(`${n}/api/temp-key/create`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:r,expiryMinutes:30})});if(!t.ok){let o=await t.json();throw new Error(o.error||"Failed to create temp key")}return await t.json()}catch(t){return console.error("Error creating temp key:",t),null}}var he=require("fs"),An=require("url"),On=require("child_process"),je=M(require("path"),1),xt=M(require("os"),1),Cr=je.default.dirname((0,An.fileURLToPath)(__importMetaUrl)),Y="9remote",Pn=`https://registry.npmjs.org/${Y}/latest`,Cn=3e3,Ir=8e3;function In(){return"0.1.62"}function Mn(r,n){let t=r.split(".").map(Number),o=n.split(".").map(Number);for(let e=0;e<3;e++){if(o[e]>t[e])return!0;if(o[e]<t[e])return!1}return!1}function Mr(){return process.env.CODESPACES==="true"||process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN?"GitHub Codespaces":(0,he.existsSync)("/.dockerenv")?"Docker":null}async function Ln(){try{let r=In();if(!r)return null;let n=await fetch(Pn,{signal:AbortSignal.timeout(Cn)});if(!n.ok)return null;let{version:t}=await n.json();return t&&Mn(r,t)?{current:r,latest:t}:null}catch{return null}}async function $n(r=!1){if(r)return!1;let n=In();if(!n)return!1;let t=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],o=0,e=null,i=a=>{process.stdout.isTTY&&(process.stdout.write(`\r${t[0]} ${a}`),e=setInterval(()=>{process.stdout.write(`\r${t[o++%t.length]} ${a}`)},80))},s=()=>{e&&(clearInterval(e),e=null),process.stdout.isTTY&&process.stdout.write("\r\x1B[K")};return new Promise(a=>{let c=!1,l=f=>{c||(c=!0,s(),a(f))},u=setTimeout(()=>l(!1),Ir);i("Checking for updates..."),fetch(Pn,{signal:AbortSignal.timeout(Cn)}).then(f=>f.json()).then(f=>{if(c)return;clearTimeout(u);let h=f.version;if(!h||!Mn(n,h)){l(!1);return}s(),console.log(v.green(`\u2705 New version available: ${n} \u2192 ${h}`));let d=Mr();if(d){console.log(v.yellow(` \u26A0\uFE0F ${d} detected - manual update required`)),console.log(v.gray(` Run: npm install -g ${Y}@latest
12
+ `);return i!==-1&&(n=Qt(n,e,o,i)),o+n+e};Object.defineProperties(Me.prototype,Ee);var vr=Me(),yo=Me({level:Vt?Vt.level:0});var v=vr;var Mt=M(Tn(),1),ke=require("child_process"),ge=M(require("path"),1),Vn=require("url"),st=M(require("fs"),1),Jn=M(require("os"),1);var bn=M(En(),1),Tt=M(require("crypto"),1),{machineIdSync:Rr}=bn.default;async function be(r=null){let n=r||process.env.MACHINE_ID_SALT||"9remote-salt";try{let t=Rr();return Tt.default.createHash("sha256").update(t+n).digest("hex").substring(0,16)}catch(t){return console.error("Error getting machine ID:",t),Tt.default.randomUUID().replace(/-/g,"").substring(0,16)}}var _n=M(require("crypto"),1),Ar=process.env.API_KEY_SECRET||"9remote-api-key-secret";function Or(){let r="abcdefghijklmnopqrstuvwxyz0123456789",n="";for(let t=0;t<4;t++)n+=r.charAt(Math.floor(Math.random()*r.length));return n}function Pr(r,n){return _n.default.createHmac("sha256",Ar).update(r+n).digest("hex").slice(0,6)}function _e(r){let n=r.slice(0,8),t=Or(),o=Pr(n,t);return{key:`sk-${n}-${t}-${o}`,keyId:t}}var Q=M(require("fs"),1),Ne=M(require("path"),1),xn=M(require("os"),1),$e=Ne.default.join(xn.default.homedir(),".9remote"),Et=Ne.default.join($e,"state.json"),bt=Ne.default.join($e,"keys.json"),qe=Ne.default.join($e,"cmd.json");function Xe(){Q.default.existsSync($e)||Q.default.mkdirSync($e,{recursive:!0})}function _t(r){try{Xe(),Q.default.writeFileSync(Et,JSON.stringify(r,null,2))}catch(n){console.error("Error saving state:",n)}}function Sn(){try{Q.default.existsSync(Et)&&Q.default.unlinkSync(Et)}catch{}}function Be(){try{return Xe(),Q.default.existsSync(bt)?JSON.parse(Q.default.readFileSync(bt,"utf8")):{machineId:null,key:null,name:"Default",createdAt:null}}catch{return{machineId:null,key:null,name:"Default",createdAt:null}}}function kn(r){try{Xe(),Q.default.writeFileSync(qe,JSON.stringify({cmd:r,ts:Date.now()}))}catch{}}function Rn(){try{if(!Q.default.existsSync(qe))return null;let r=JSON.parse(Q.default.readFileSync(qe,"utf8"));return Q.default.unlinkSync(qe),r.cmd}catch{return null}}function xe(r,n,t="Default"){try{Xe();let o={machineId:r,key:n,name:t,createdAt:new Date().toISOString()};return Q.default.writeFileSync(bt,JSON.stringify(o,null,2)),o}catch(o){return console.error("Error saving key:",o),null}}async function De(r,n){try{let t=await fetch(`${n}/api/temp-key/create`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:r,expiryMinutes:30})});if(!t.ok){let o=await t.json();throw new Error(o.error||"Failed to create temp key")}return await t.json()}catch(t){return console.error("Error creating temp key:",t),null}}var he=require("fs"),An=require("url"),On=require("child_process"),je=M(require("path"),1),xt=M(require("os"),1),Cr=je.default.dirname((0,An.fileURLToPath)(__importMetaUrl)),Y="9remote",Pn=`https://registry.npmjs.org/${Y}/latest`,Cn=3e3,Ir=8e3;function In(){return"0.1.63"}function Mn(r,n){let t=r.split(".").map(Number),o=n.split(".").map(Number);for(let e=0;e<3;e++){if(o[e]>t[e])return!0;if(o[e]<t[e])return!1}return!1}function Mr(){return process.env.CODESPACES==="true"||process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN?"GitHub Codespaces":(0,he.existsSync)("/.dockerenv")?"Docker":null}async function Ln(){try{let r=In();if(!r)return null;let n=await fetch(Pn,{signal:AbortSignal.timeout(Cn)});if(!n.ok)return null;let{version:t}=await n.json();return t&&Mn(r,t)?{current:r,latest:t}:null}catch{return null}}async function $n(r=!1){if(r)return!1;let n=In();if(!n)return!1;let t=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],o=0,e=null,i=a=>{process.stdout.isTTY&&(process.stdout.write(`\r${t[0]} ${a}`),e=setInterval(()=>{process.stdout.write(`\r${t[o++%t.length]} ${a}`)},80))},s=()=>{e&&(clearInterval(e),e=null),process.stdout.isTTY&&process.stdout.write("\r\x1B[K")};return new Promise(a=>{let c=!1,l=f=>{c||(c=!0,s(),a(f))},u=setTimeout(()=>l(!1),Ir);i("Checking for updates..."),fetch(Pn,{signal:AbortSignal.timeout(Cn)}).then(f=>f.json()).then(f=>{if(c)return;clearTimeout(u);let h=f.version;if(!h||!Mn(n,h)){l(!1);return}s(),console.log(v.green(`\u2705 New version available: ${n} \u2192 ${h}`));let d=Mr();if(d){console.log(v.yellow(` \u26A0\uFE0F ${d} detected - manual update required`)),console.log(v.gray(` Run: npm install -g ${Y}@latest
13
13
  `)),l(!1);return}console.log(v.yellow(`\u{1F504} Auto-updating...
14
14
  `));let g=process.argv.slice(2).filter(b=>b!=="--skip-update").join(" "),T=process.platform,k,_;if(T==="win32"){let b=`@echo off
15
15
  echo \u{1F4E5} Downloading update...
@@ -60,7 +60,7 @@ fi
60
60
  `),process.stdout.write(`${S.dim}${r}${S.reset}
61
61
 
62
62
  `),n.forEach((p,g)=>{let T=g===a?l?">":"\u2605":l?" ":"\u2606";console.log(g===a?` \x1B[7m${S.bold}${T} ${p.label}${S.reset}`:` ${T} ${p.label}`)})},f=()=>{if(c){if(c=!1,process.stdin.isTTY)try{process.stdin.setRawMode(!1)}catch{}process.stdin.removeListener("keypress",h),process.stdin.pause()}},h=(d,p)=>{!c||!p||(p.name==="up"?(a=(a-1+n.length)%n.length,u()):p.name==="down"?(a=(a+1)%n.length,u()):p.name==="return"?(f(),s(a)):p.name==="escape"?(f(),s(-1)):p.ctrl&&p.name==="c"&&(f(),i&&i(),process.exit(0)))};if(process.stdin.removeAllListeners("keypress"),Pt.default.emitKeypressEvents(process.stdin),process.stdin.isTTY)try{process.stdin.setRawMode(!0)}catch{s(-1);return}process.stdin.on("keypress",h),process.stdin.resume(),u(),e&&e(u)})}function Gn(r){return new Promise(n=>{if(process.stdin.isTTY)try{process.stdin.setRawMode(!1)}catch{}process.stdin.removeAllListeners("keypress");let t=Pt.default.createInterface({input:process.stdin,output:process.stdout});t.question(`${r} (y/N): `,o=>{t.close(),n(o.trim().toLowerCase()==="y")})})}function Hn(r,n){let t=null,o=!1,e=()=>{o||(t=Fn.default.get(`http://localhost:${r}/api/ui/events`,i=>{let s="";i.on("data",a=>{s+=a.toString();let c=s.split(`
63
- `);s=c.pop();let l="";for(let u of c)if(u.startsWith("data: "))l=u.slice(6);else if(u===""&&l){try{let f=JSON.parse(l);n(f.type,f)}catch{}l=""}}),i.on("end",()=>{o||setTimeout(e,2e3)})}),t.on("error",()=>{o||setTimeout(e,2e3)}))};return e(),()=>{o=!0,t?.destroy()}}var Yr=process.argv.includes("--skip-update"),ct=ge.default.dirname((0,Vn.fileURLToPath)(__importMetaUrl)),Xo=ge.default.resolve(ct,"../.."),Vr=ge.default.resolve(ct,"../dist/server.cjs"),Wn=ge.default.resolve(ct,"../index.js"),U="https://9remote.cc",B=2208,It=10,Qn=6e4,te=v.rgb(230,138,110),qn=v.rgb(200,120,95);function Lt(){return"0.1.62"}function Jr(r,n="\u{1F4F1} Scan QR to connect:"){console.log(te(`
63
+ `);s=c.pop();let l="";for(let u of c)if(u.startsWith("data: "))l=u.slice(6);else if(u===""&&l){try{let f=JSON.parse(l);n(f.type,f)}catch{}l=""}}),i.on("end",()=>{o||setTimeout(e,2e3)})}),t.on("error",()=>{o||setTimeout(e,2e3)}))};return e(),()=>{o=!0,t?.destroy()}}var Yr=process.argv.includes("--skip-update"),ct=ge.default.dirname((0,Vn.fileURLToPath)(__importMetaUrl)),Xo=ge.default.resolve(ct,"../.."),Vr=ge.default.resolve(ct,"../dist/server.cjs"),Wn=ge.default.resolve(ct,"../index.js"),U="https://9remote.cc",B=2208,It=10,Qn=6e4,te=v.rgb(230,138,110),qn=v.rgb(200,120,95);function Lt(){return"0.1.63"}function Jr(r,n="\u{1F4F1} Scan QR to connect:"){console.log(te(`
64
64
  ${n}`)),Mt.default.generate(r,{small:!0,type:"terminal",margin:0},t=>{console.log(t.trim())})}function qr(r){return new Promise(n=>{Mt.default.generate(r,{small:!0,type:"terminal",margin:0},t=>{n(qn("\u{1F4F1} Scan QR to connect:")+`
65
65
  `+t.trim())})})}async function Xn(r,n){let t=await De(r,U);if(!t){console.log(v.red("\u274C Failed to create temp key"));return}let o=`${U}/login?k=${t.tempKey}`,e=Math.min(44,process.stdout.columns||55);D({step:4,tunnelUrl:n,oneTimeKey:t.tempKey,oneTimeKeyExpiresAt:t.expiresAt,permanentKey:r,qrUrl:o,workerUrl:U}),Jr(o),console.log(v.gray(`
66
66
  QR will expire in 30 minutes (one-time use)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "9remote",
3
- "version": "0.1.62",
3
+ "version": "0.1.63",
4
4
  "type": "module",
5
5
  "description": "Remote terminal access from anywhere",
6
6
  "main": "index.js",