reley 0.1.4 → 0.1.5
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/dist/index.js +8 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var Ct=Object.defineProperty;var Et=(t,e)=>()=>(t&&(e=t(t=0)),e);var _t=(t,e)=>{for(var o in e)Ct(t,o,{get:e[o],enumerable:!0})};var st={};_t(st,{clearConfig:()=>Lt,getConfigDir:()=>Dt,getConfigPath:()=>$e,isConfigured:()=>je,loadConfig:()=>nt,saveConfig:()=>Be});import{existsSync as tt,readFileSync as It,writeFileSync as ot,mkdirSync as Kt}from"node:fs";import{join as rt}from"node:path";import{homedir as Ut}from"node:os";function Dt(){return Me}function $e(){return le}function nt(){try{if(!tt(le))return null;let t=It(le,"utf-8");return JSON.parse(t)}catch{return null}}function Be(t){Kt(Me,{recursive:!0}),ot(le,JSON.stringify(t,null,2)+`
|
|
3
3
|
`,"utf-8")}function Lt(){try{tt(le)&&ot(le,`{}
|
|
4
|
-
`,"utf-8")}catch{}}function je(){let t=nt();return!!(t?.reley_url&&t?.device_token)}var Me,le,we=Et(()=>{"use strict";Me=rt(Ut(),".reley"),le=rt(Me,"config.json")});import{Command as
|
|
4
|
+
`,"utf-8")}catch{}}function je(){let t=nt();return!!(t?.reley_url&&t?.device_token)}var Me,le,we=Et(()=>{"use strict";Me=rt(Ut(),".reley"),le=rt(Me,"config.json")});import{Command as po}from"commander";import{createServer as Ht}from"node:http";import{createServer as at}from"node:net";import{fork as Mt}from"node:child_process";import{existsSync as Ge,readFileSync as ct,writeFileSync as Je,unlinkSync as de}from"node:fs";import{fileURLToPath as $t}from"node:url";import{dirname as Bt,join as ze}from"node:path";import{homedir as jt}from"node:os";import Fe from"node:crypto";import R,{WebSocketServer as Ft}from"ws";import{spawn as lt}from"node-pty";import{createRequire as Tt}from"node:module";var Pt=Tt(import.meta.url),Rt=Pt("libsodium-wrappers-sumo"),Oe=Rt;var Ve=!1;async function f(){return Ve||(await Oe.ready,Ve=!0),Oe}async function Ne(){let e=(await f()).crypto_sign_keypair();return{publicKey:e.publicKey,secretKey:e.privateKey,keyType:"ed25519"}}async function re(){let e=(await f()).crypto_kx_keypair();return{publicKey:e.publicKey,secretKey:e.privateKey,keyType:"x25519"}}async function Ae(t=32){return(await f()).randombytes_buf(t)}async function J(t,e){return(await f()).crypto_scalarmult(t,e)}var ve=32;async function Ot(t,e){let o=await f(),r=t.length>0?t:new Uint8Array(ve);return o.crypto_auth_hmacsha256(e,r)}async function Nt(t,e,o){let r=await f(),n=Math.ceil(o/ve),s=new Uint8Array(n*ve),i=new Uint8Array(0);for(let c=1;c<=n;c++){let a=new Uint8Array(i.length+e.length+1);a.set(i,0),a.set(e,i.length),a[i.length+e.length]=c,i=new Uint8Array(r.crypto_auth_hmacsha256(a,t)),s.set(i,(c-1)*ve)}return s.slice(0,o)}async function W(t,e=new Uint8Array(0),o="reley-v1"){let r=new TextEncoder().encode(o),n=await Ot(e,t),s=await Nt(n,r,64);return{sendKey:s.slice(0,32),recvKey:s.slice(32,64)}}async function be(t,e="reley-chain"){let o=await f(),r=new TextEncoder().encode(e+"-msg"),n=new TextEncoder().encode(e+"-chain"),s=o.crypto_auth_hmacsha256(r,t),i=o.crypto_auth_hmacsha256(n,t);return{messageKey:s,nextChainKey:i}}var qe=12;var me=32;async function Ie(t,e,o=new Uint8Array(0)){let r=await f();if(e.length!==me)throw new Error(`Key must be ${me} bytes, got ${e.length}`);let n=r.randombytes_buf(qe),s=r.crypto_aead_xchacha20poly1305_ietf_encrypt(t,o.length>0?o:null,null,Xe(n,r),e);return{nonce:n,ciphertext:s}}async function Ke(t,e,o,r=new Uint8Array(0)){let n=await f();if(o.length!==me)throw new Error(`Key must be ${me} bytes, got ${o.length}`);try{return n.crypto_aead_xchacha20poly1305_ietf_decrypt(null,t,r.length>0?r:null,Xe(e,n),o)}catch{throw new Error("Decryption failed: invalid ciphertext or key")}}function Xe(t,e){let o=new Uint8Array(e.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);return o.set(t,0),o}var Qe=50;function Y(t,e){return{sendChainKey:t,recvChainKey:e,sendCounter:0,recvCounter:0,maxRecvCounter:-1}}async function V(t,e){let{messageKey:o,nextChainKey:r}=await be(t.sendChainKey),n=t.sendCounter,s=Ze(1,n),{nonce:i,ciphertext:c}=await Ie(e,o,s),a={...t,sendChainKey:r,sendCounter:n+1};return{ciphertext:c,nonce:i,counter:n,state:a}}async function se(t,e,o,r){if(r<=t.maxRecvCounter)throw new Error(`Replay attack detected: counter ${r} <= ${t.maxRecvCounter}`);let n=t.recvChainKey,s=t.recvCounter,i;for(;s<=r;){let m=await be(n);s===r&&(i=m.messageKey),n=m.nextChainKey,s++}if(!i)throw new Error("Failed to derive message key");let c=Ze(1,r),a=await Ke(e,o,i,c),u={...t,recvChainKey:n,recvCounter:s,maxRecvCounter:r};return{plaintext:a,state:u}}function Ue(t){return t.sendCounter>0&&t.sendCounter%Qe===0}function Ze(t,e){let o=new Uint8Array(6);return o[0]=t,o[1]=1,o[2]=e>>>24&255,o[3]=e>>>16&255,o[4]=e>>>8&255,o[5]=e&255,o}var De="CB1";async function Le(t){let e=await f(),o=new TextEncoder().encode(t.releyUrl),r=new TextEncoder().encode(t.jwt),n=5+o.length+32+32+2+r.length,s=new Uint8Array(n),i=0;return s[i++]=De.charCodeAt(0),s[i++]=De.charCodeAt(1),s[i++]=De.charCodeAt(2),s[i++]=o.length>>>8&255,s[i++]=o.length&255,s.set(o,i),i+=o.length,s.set(t.publicKey,i),i+=32,s.set(t.oneTimeCode,i),i+=32,s[i++]=r.length>>>8&255,s[i++]=r.length&255,s.set(r,i),e.to_base64(s,e.base64_variants.URLSAFE_NO_PADDING)}async function He(t){return(await f()).crypto_generichash(32,t,null)}var q={VERSION:1,TYPE:1,COUNTER:4,NONCE:12,TAG:16,HEADER:18},K={TERMINAL_DATA:1,TERMINAL_RESIZE:2,TERMINAL_INPUT:3,HOOK_EVENT:16,HOOK_RESPONSE:17,SESSION_PING:32,SESSION_PONG:33,SESSION_CLOSE:34,KEY_ROTATION:48,KEY_EXCHANGE:49},ie={PAIRING_EXPIRY:5*60*1e3,JWT_EXPIRY:24*60*60*1e3,PING_INTERVAL:30*1e3,PONG_TIMEOUT:10*1e3,WS_RECONNECT_BASE:1e3,WS_RECONNECT_MAX:30*1e3,HOOK_RESPONSE_TIMEOUT:5*60*1e3};var et={terminal_data:K.TERMINAL_DATA,terminal_resize:K.TERMINAL_RESIZE,terminal_input:K.TERMINAL_INPUT,hook_event:K.HOOK_EVENT,hook_response:K.HOOK_RESPONSE,ping:K.SESSION_PING,pong:K.SESSION_PONG,session_close:K.SESSION_CLOSE,key_rotation:K.KEY_ROTATION,key_exchange:K.KEY_EXCHANGE},At=new Map;for(let[t,e]of Object.entries(et))At.set(e,t);function X(t){let e=et[t];if(e===void 0)throw new Error(`Unknown message type: ${t}`);return e}function Q(t){let e=q.HEADER+t.ciphertext.length,o=new Uint8Array(e),r=0;return o[r++]=t.version,o[r++]=t.type,o[r++]=t.counter>>>24&255,o[r++]=t.counter>>>16&255,o[r++]=t.counter>>>8&255,o[r++]=t.counter&255,o.set(t.nonce,r),r+=q.NONCE,o.set(t.ciphertext,r),o}function ae(t){if(t.length<q.HEADER+q.TAG)throw new Error(`Envelope too short: ${t.length} bytes`);let e=0,o=t[e++];if(o!==1)throw new Error(`Unsupported protocol version: ${o}`);let r=t[e++],n=t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3];e+=4;let s=t.slice(e,e+q.NONCE);e+=q.NONCE;let i=t.slice(e);return{version:o,type:r,counter:n,nonce:s,ciphertext:i}}function Z(t){return new TextEncoder().encode(JSON.stringify(t))}function ce(t){return JSON.parse(new TextDecoder().decode(t))}var it=Bt($t(import.meta.url)),We=!0;function E(t,e){if(!We)return;console.error(`${{RELEY:"\x1B[34m",PTY:"\x1B[32m",WEB:"\x1B[35m",CRYPTO:"\x1B[33m",HOOKS:"\x1B[36m"}[t]??""}[${t}]\x1B[0m ${e}`)}function zt(){let t=[ze(it,"../../../reley/dist/server.js"),ze(it,"../../../../apps/reley/dist/server.js")];for(let e of t)if(Ge(e))return e;throw new Error("Not logged in. Run `reley login` first, or use `reley` to connect to the remote server.")}async function dt(t){try{return(await fetch(`${t}/health`)).ok}catch{return!1}}async function Gt(t){for(let e=0;e<50;e++){if(await dt(t))return;await new Promise(o=>setTimeout(o,200))}throw new Error("Reley server failed to start")}function Jt(){return`<!DOCTYPE html>
|
|
5
5
|
<html lang="ko"><head>
|
|
6
6
|
<meta charset="utf-8">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -684,10 +684,11 @@ main().catch((err) => {
|
|
|
684
684
|
// Default to approve on error
|
|
685
685
|
console.log(JSON.stringify({ action: 'approve' }));
|
|
686
686
|
});
|
|
687
|
-
`}we();import{createServer as oo}from"node:http";import{
|
|
688
|
-
`);let{token:n,email:s}=await
|
|
689
|
-
`),console.log("\x1B[36m[Reley]\x1B[0m Registering device...");let i=
|
|
690
|
-
`),console.log(
|
|
687
|
+
`}we();import{createServer as oo}from"node:http";import{createServer as ro}from"node:net";import{URL as no}from"node:url";import{hostname as so}from"node:os";var io="https://api.reley.sh",ao=[54321,54322,54323];function co(t){return new Promise((e,o)=>{let r=0;function n(){if(r>=t.length){o(new Error(`All callback ports (${t.join(", ")}) are in use.`));return}let s=t[r++],i=ro();i.once("error",()=>n()),i.once("listening",()=>{i.close(()=>e(s))}),i.listen(s,"127.0.0.1")}n()})}async function kt(t){let e=t.releyUrl||process.env.RELEY_URL||io,o=process.env.SUPABASE_URL,r=process.env.SUPABASE_ANON_KEY;if(!o||!r)try{let m=await fetch(`${e}/api/v1/auth/config`);if(!m.ok)throw new Error(`HTTP ${m.status}`);let p=await m.json();o=p.supabaseUrl,r=p.supabaseAnonKey}catch{console.error("\x1B[31mError:\x1B[0m Could not connect to reley server at "+e),console.error("Check that the server is running and the URL is correct."),process.exit(1)}console.log(`\x1B[36m[Reley]\x1B[0m Starting Google login...
|
|
688
|
+
`);let{token:n,email:s}=await lo(o,r);console.log(`\x1B[32m\u2713\x1B[0m Logged in as \x1B[1m${s}\x1B[0m
|
|
689
|
+
`),console.log("\x1B[36m[Reley]\x1B[0m Registering device...");let i=so()||"Unknown Device",c=await fetch(`${e}/api/v1/devices/register`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify({name:i})});if(!c.ok){let m=await c.json().catch(()=>({}));console.error(`\x1B[31mError:\x1B[0m Failed to register device: ${m.error||c.statusText}`),process.exit(1)}let{deviceId:a,deviceToken:u}=await c.json();Be({reley_url:e,device_token:u,device_id:a,user_email:s}),console.log(`\x1B[32m\u2713\x1B[0m Device registered: \x1B[1m${i}\x1B[0m`),console.log(`\x1B[32m\u2713\x1B[0m Config saved to ${$e()}
|
|
690
|
+
`),console.log(`You can now run \x1B[1mreley\x1B[0m to start a session.
|
|
691
|
+
`),process.exit(0)}async function lo(t,e){let o=await co(ao);return new Promise((r,n)=>{let s=`http://localhost:${o}/auth/callback`,i=oo((c,a)=>{let u=new no(c.url,`http://localhost:${o}`);if(u.pathname==="/auth/callback"){a.writeHead(200,{"Content-Type":"text/html"}),a.end(`<!DOCTYPE html>
|
|
691
692
|
<html><body>
|
|
692
693
|
<script>
|
|
693
694
|
// Supabase puts tokens in the hash fragment
|
|
@@ -708,7 +709,7 @@ main().catch((err) => {
|
|
|
708
709
|
}
|
|
709
710
|
</script>
|
|
710
711
|
<h2 style="font-family:system-ui;text-align:center;margin-top:100px">Processing login...</h2>
|
|
711
|
-
</body></html>`);return}if(u.pathname==="/auth/token"&&c.method==="POST"){let m="";c.on("data",p=>{m+=p.toString()}),c.on("end",async()=>{a.writeHead(200,{"Content-Type":"application/json"}),a.end(JSON.stringify({ok:!0}));try{let{access_token:p}=JSON.parse(m),v=await fetch(`${t}/auth/v1/user`,{headers:{Authorization:`Bearer ${p}`,apikey:e}});if(!v.ok){
|
|
712
|
-
`);let a=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";import("node:child_process").then(({spawn:u})=>{u(a,[c],{stdio:"ignore",detached:!0}).unref()}),setTimeout(()=>{i.close(),
|
|
712
|
+
</body></html>`);return}if(u.pathname==="/auth/token"&&c.method==="POST"){let m="";c.on("data",p=>{m+=p.toString()}),c.on("end",async()=>{a.writeHead(200,{"Content-Type":"application/json"}),a.end(JSON.stringify({ok:!0}));try{let{access_token:p}=JSON.parse(m),v=await fetch(`${t}/auth/v1/user`,{headers:{Authorization:`Bearer ${p}`,apikey:e}});if(!v.ok){n(new Error("Failed to verify token"));return}let y=await v.json();i.close(),r({token:p,email:y.email})}catch(p){n(p)}});return}a.writeHead(404),a.end("Not found")});i.listen(o,()=>{let c=`${t}/auth/v1/authorize?`+new URLSearchParams({provider:"google",redirect_to:s}).toString();console.log("\x1B[36m[Reley]\x1B[0m Opening browser for Google login..."),console.log(`\x1B[2m${c}\x1B[0m
|
|
713
|
+
`);let a=process.platform==="darwin"?"open":process.platform==="win32"?"start":"xdg-open";import("node:child_process").then(({spawn:u})=>{u(a,[c],{stdio:"ignore",detached:!0}).unref()}),setTimeout(()=>{i.close(),n(new Error("Login timed out (5 minutes)"))},5*60*1e3)}),i.on("error",c=>{n(new Error(`Failed to start callback server: ${c.message}`))})})}we();var _=new po;_.name("reley").description("Bridge your terminal to mobile/web with E2E encryption").version("0.1.5");_.option("--reley-url <url>","Reley server URL","https://api.reley.sh").option("--config-dir <path>","Configuration directory",`${process.env.HOME}/.reley`);_.command("run [command...]",{isDefault:!0}).description("Run a command with web terminal sync (default)").option("--web-port <port>","Web UI port","3200").option("--reley-port <port>","Reley server port","3100").option("--online","Use online reley server (requires `reley login` first)").action(async(t,e)=>{let o=e.online??je();await mt(t,{webPort:e.webPort,releyPort:e.releyPort,online:o})});_.command("login").description("Login with Google and register this device").option("--reley-url <url>","Reley server URL").action(async t=>{let e=_.opts();await kt({releyUrl:t.releyUrl||e.releyUrl})});_.command("pair").description("Pair this device with a mobile client").action(async()=>{let t=_.opts();await ft({releyUrl:t.releyUrl,configDir:t.configDir})});_.command("wrap <command...>").description("Wrap a command with existing session (requires prior pairing)").action(async t=>{let e=_.opts();await wt(t,{releyUrl:e.releyUrl,configDir:e.configDir})});_.command("status").description("Show pairing and connection status").action(async()=>{let t=_.opts();await xt({releyUrl:t.releyUrl,configDir:t.configDir})});_.command("install-hooks").description("Install Claude Code hooks for Reley integration").action(async()=>{let t=_.opts();await St({releyUrl:t.releyUrl,configDir:t.configDir})});_.parseAsync(process.argv).catch(t=>{let e=t instanceof Error?t.message:String(t);console.error(`
|
|
713
714
|
\x1B[31m\x1B[1mError:\x1B[0m ${e}
|
|
714
715
|
`),process.exit(1)});
|