infernoflow 0.43.5 → 0.43.6

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.
@@ -1,35 +0,0 @@
1
- import*as $ from"node:https";import*as k from"node:http";import*as O from"node:crypto";import{execSync as R}from"node:child_process";import{bold as p,cyan as m,gray as a,green as _,red as v,yellow as x}from"../ui/output.mjs";import{readCredentials as L,writeCredentials as A,deleteCredentials as M,isLoggedIn as G}from"../cloud/credentials.mjs";import{SUPABASE_URL as N,getUser as B}from"../cloud/supabase.mjs";const T="Ov23liYuUKwDRTzrywsa",J=[47655,47656,47657,47658,47659],z=300*1e3;function S(e,s,d){return new Promise((t,l)=>{const o=new URLSearchParams(d).toString(),n={hostname:e,port:443,path:s,method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json","User-Agent":"infernoflow-cli","Content-Length":Buffer.byteLength(o)}},i=$.request(n,r=>{let c="";r.on("data",u=>c+=u),r.on("end",()=>{try{t(JSON.parse(c))}catch{t(c)}})});i.on("error",l),i.setTimeout(15e3,()=>i.destroy(new Error("timeout"))),i.write(o),i.end()})}function W(e,s,d){return new Promise((t,l)=>{const o={hostname:e,port:443,path:s,method:"GET",headers:{Accept:"application/json","User-Agent":"infernoflow-cli",Authorization:`Bearer ${d}`}},n=$.request(o,i=>{let r="";i.on("data",c=>r+=c),i.on("end",()=>{try{t(JSON.parse(r))}catch{t(r)}})});n.on("error",l),n.setTimeout(1e4,()=>n.destroy(new Error("timeout"))),n.end()})}function C(e){return new Promise(s=>setTimeout(s,e))}function E(e){try{const s=process.platform==="win32"?`start "" "${e}"`:process.platform==="darwin"?`open "${e}"`:`xdg-open "${e}"`;R(s,{stdio:"ignore"})}catch{}}function F(e){return new Promise((s,d)=>{let t=0;const l=()=>{if(t>=e.length)return d(new Error("no available local port for callback"));const o=e[t++],n=k.createServer();n.on("error",()=>l()),n.listen(o,"127.0.0.1",()=>{n.close(()=>s(o))})};l()})}const Y=`<!DOCTYPE html>
2
- <html><head><meta charset="utf-8"><title>infernoflow login</title>
3
- <style>
4
- body { font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; max-width: 480px; margin: 60px auto; padding: 0 24px; color: #0f1117; }
5
- .ok { color: #16a34a; }
6
- .err { color: #dc2626; }
7
- code { background: #f4f4f5; padding: 2px 6px; border-radius: 4px; font-size: 13px; }
8
- </style></head>
9
- <body>
10
- <h2>\u{1F525} infernoflow</h2>
11
- <p id="status">Completing login\u2026</p>
12
- <script>
13
- (async () => {
14
- const status = document.getElementById('status');
15
- const hash = window.location.hash.substring(1);
16
- if (!hash) {
17
- const params = new URLSearchParams(window.location.search);
18
- const errMsg = params.get('error_description') || params.get('error') || 'No tokens received.';
19
- status.innerHTML = '<span class="err">\u2718 Login failed: ' + errMsg + '</span>';
20
- try { await fetch('/error?msg=' + encodeURIComponent(errMsg)); } catch (_) {}
21
- return;
22
- }
23
- try {
24
- const r = await fetch('/token', { method: 'POST', body: hash, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });
25
- if (r.ok) {
26
- status.innerHTML = '<span class="ok">\u2714 Logged in.</span> You can close this tab and return to your terminal.';
27
- } else {
28
- status.innerHTML = '<span class="err">\u2718 Forwarding failed (HTTP ' + r.status + ').</span>';
29
- }
30
- } catch (e) {
31
- status.innerHTML = '<span class="err">\u2718 Could not reach the local CLI: ' + e.message + '</span>';
32
- }
33
- })();
34
- </script>
35
- </body></html>`;async function K(){const e=await F(J),s=`http://localhost:${e}/callback`,d=O.randomBytes(16).toString("hex"),t=new URL(`${N}/auth/v1/authorize`);return t.searchParams.set("provider","github"),t.searchParams.set("redirect_to",s),t.searchParams.set("scopes","read:user user:email"),new Promise((l,o)=>{let n=!1,i;const r=u=>{if(!n){n=!0,clearTimeout(c);try{i?.close()}catch{}u()}},c=setTimeout(()=>{r(()=>o(new Error("login timed out \u2014 close the browser tab and try again")))},z);i=k.createServer((u,g)=>{const w=new URL(u.url,`http://localhost:${e}`);if(u.method==="GET"&&w.pathname==="/callback"){g.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),g.end(Y);return}if(u.method==="POST"&&w.pathname==="/token"){let h="";u.on("data",f=>h+=f),u.on("end",()=>{const f=new URLSearchParams(h),y=f.get("access_token"),I=f.get("refresh_token"),b=parseInt(f.get("expires_in")||"0",10),D=f.get("provider_token"),H=f.get("provider_refresh_token");if(!y){g.writeHead(400,{"Content-Type":"application/json"}),g.end(JSON.stringify({error:"no access_token in fragment"}));return}g.writeHead(204),g.end(),r(()=>l({access_token:y,refresh_token:I,expires_at:b?new Date(Date.now()+b*1e3).toISOString():null,provider_token:D,provider_refresh_token:H}))});return}if(u.method==="GET"&&w.pathname==="/error"){const h=w.searchParams.get("msg")||"unknown error";g.writeHead(204),g.end(),r(()=>o(new Error(h)));return}g.writeHead(404),g.end()}),i.on("error",u=>r(()=>o(u))),i.listen(e,"127.0.0.1",()=>{console.log(),console.log(` ${p("\u{1F525} infernoflow login")}`),console.log(),console.log(` ${a("Opening your browser to sign in with GitHub via Supabase\u2026")}`),console.log(),console.log(` ${p("If the browser doesn't open, paste this URL:")}`),console.log(` ${m(t.toString())}`),console.log(),console.log(` ${a(`Listening for the callback on http://localhost:${e}/callback`)}`),console.log(` ${a("(this prompt will close automatically when you finish)")}`),console.log(),E(t.toString())})})}async function j(){console.log(),console.log(` ${p("\u{1F525} infernoflow login")} ${a("(device-flow / identity-only)")}`),console.log(),console.log(` ${x("\u26A0")} ${a("Device flow gives us your GitHub identity but no Supabase JWT.")}`),console.log(` ${a("Cloud writes will fall back to anon-key dev mode. Run without --device-flow")}`),console.log(` ${a("for the proper authenticated flow.")}`),console.log();let e;try{e=await S("github.com","/login/device/code",{client_id:T,scope:"read:user user:email"})}catch(r){throw new Error(`could not reach GitHub: ${r.message}`)}if(!e.device_code)throw new Error(`GitHub error: ${JSON.stringify(e)}`);const{device_code:s,user_code:d,verification_uri:t,expires_in:l,interval:o}=e,n=(o||5)*1e3;console.log(` ${p("Open:")} ${m(t)}`),console.log(` ${p("Code:")} ${p(m(d))}`),console.log(),E(t),console.log(` ${a("Waiting for you to authorize\u2026")} ${a("(Ctrl+C to cancel)")}`),console.log();const i=Date.now()+(l||900)*1e3;for(;Date.now()<i;){await C(n);let r;try{r=await S("github.com","/login/oauth/access_token",{client_id:T,device_code:s,grant_type:"urn:ietf:params:oauth:grant-type:device_code"})}catch{continue}if(r.error!=="authorization_pending"){if(r.error==="slow_down"){await C(5e3);continue}if(r.error==="expired_token")throw new Error("code expired \u2014 run infernoflow login again");if(r.error==="access_denied")throw new Error("access denied");if(r.access_token){const c=await W("api.github.com","/user",r.access_token);return{mode:"device-flow",github_access_token:r.access_token,user:{provider:"github",login:c?.login||null,name:c?.name||null,email:c?.email||null,id:c?.id||null,avatar_url:c?.avatar_url||null}}}}}throw new Error("login timed out")}async function Q(e){if(G()){const o=L(),n=o?.user?.login||o?.user?.name||o?.user?.email||"unknown";console.log(),console.log(` ${_("\u2714")} Already logged in as ${p(n)}`),console.log(` Run ${m("infernoflow logout")} to sign out.`),console.log();return}const s=e.includes("--browser"),d=!s;let t;try{if(d){const o=await j();t={mode:"device-flow",github_access_token:o.github_access_token,user:o.user,logged_in_at:new Date().toISOString()}}else{const o=await K(),n=await B(o.access_token).catch(()=>null);t={mode:"supabase",access_token:o.access_token,refresh_token:o.refresh_token,expires_at:o.expires_at,provider_token:o.provider_token,provider_refresh_token:o.provider_refresh_token,user:n?{provider:"github",id:n.id||null,email:n.email||null,login:n.user_metadata?.user_name||n.user_metadata?.preferred_username||n.identities?.[0]?.identity_data?.user_name||null,name:n.user_metadata?.full_name||n.user_metadata?.name||null,avatar_url:n.user_metadata?.avatar_url||null}:{provider:"github"},logged_in_at:new Date().toISOString()}}}catch(o){console.log(),console.log(` ${v("\u2718")} Login failed: ${o.message}`),console.log(s?` ${a("If --browser fails, fall back to the default flow:")} ${m("infernoflow login")}`:` ${a("To try the experimental authenticated browser flow:")} ${m("infernoflow login --browser")}`),console.log(),process.exit(1)}A(t);const l=t.user?.login||t.user?.name||t.user?.email||"unknown";console.log(),console.log(` ${_("\u2714")} Logged in as ${p(l)}`),console.log(),t.mode==="supabase"?console.log(` ${a("Cloud sync is now authenticated. Every")} ${m("infernoflow log")} ${a("writes under your auth.uid().")}`):console.log(` ${a("Identity-only login (device flow). Cloud writes still use the anon-key dev mode.")}`),console.log()}function P(){const e=M();console.log(),console.log(e?` ${_("\u2714")} Logged out. Local credentials removed.`:` ${a("Already logged out.")}`),console.log()}function U(){const e=L();if(console.log(),!e?.access_token&&!e?.github_access_token){console.log(` ${a("Not logged in.")} Run ${m("infernoflow login")}`),console.log();return}const s=e.user?.login||e.user?.name||e.user?.email||"unknown",d=e.user?.email||a("(no email)"),t=e.logged_in_at?new Date(e.logged_in_at).toLocaleDateString():"unknown",l=e.mode==="supabase"?_("\u2714 authenticated (Supabase JWT)"):e.mode==="device-flow"?x("\u26A0 identity-only (device flow)"):a("legacy");if(console.log(` ${p("\u{1F525} infernoflow")} \u2014 logged in as:`),console.log(),console.log(` User: ${p(s)}`),console.log(` Email: ${d}`),console.log(` Since: ${a(t)}`),console.log(` Mode: ${l}`),e.expires_at){const o=new Date(e.expires_at),n=Date.now()>o.getTime();console.log(` Expires: ${a(o.toLocaleString())}${n?" "+v("(expired \u2014 run login again)"):""}`)}console.log()}async function ee(e){const s=e[1];return s==="logout"?P():s==="whoami"?U():Q(e)}async function oe(){return P()}async function ne(){return U()}export{ee as loginCommand,oe as logoutCommand,ne as whoamiCommand};
@@ -1,2 +0,0 @@
1
- // postinstall — intentionally minimal, no network calls, no blocking
2
- // infernoflow telemetry is opt-out and runs at CLI runtime, not install time