infernoflow 0.38.3 → 0.38.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/CHANGELOG.md CHANGED
@@ -229,6 +229,27 @@
229
229
  - v0.37.3 — remove Scarf, zero-dep install, no more Windows hang
230
230
  - v0.37.3 — remove @scarf/scarf to fix Windows install hang
231
231
 
232
+ - v0.38.4 — login via GitHub Device Flow, no redirects
233
+ - v0.38.3 — fix login: use implicit flow
234
+ - v0.38.2 — fix login PKCE flow
235
+ - v0.38.1
236
+ - v0.38.0 — cloud login + auto-push to Supabase on every log
237
+ - v0.38.0 — cloud login, auto-push to Supabase on every log
238
+ - v0.37.4
239
+ - v0.37.3 — remove Scarf, zero-dep install, no more Windows hang
240
+ - v0.37.3 — remove @scarf/scarf to fix Windows install hang
241
+
242
+ - v0.38.5 — fix cloud push: use anon key + user_token
243
+ - v0.38.4 — login via GitHub Device Flow, no redirects
244
+ - v0.38.3 — fix login: use implicit flow
245
+ - v0.38.2 — fix login PKCE flow
246
+ - v0.38.1
247
+ - v0.38.0 — cloud login + auto-push to Supabase on every log
248
+ - v0.38.0 — cloud login, auto-push to Supabase on every log
249
+ - v0.37.4
250
+ - v0.37.3 — remove Scarf, zero-dep install, no more Windows hang
251
+ - v0.37.3 — remove @scarf/scarf to fix Windows install hang
252
+
232
253
  ## 0.10.25 — 2026-04-22
233
254
 
234
255
  ### Added
@@ -1 +1 @@
1
- import*as m from"node:https";const n=process.env.INFERNOFLOW_SUPABASE_URL||"https://vscesbbtmrsctfroigyx.supabase.co",f=process.env.INFERNOFLOW_SUPABASE_ANON_KEY||"sb_publishable_yThoZzOisgqLxrH8BOli-Q_yHVqEhUk";function i(t,e,r,s){return new Promise((h,l)=>{const c=new URL(e),a=r?JSON.stringify(r):null,y={hostname:c.hostname,port:443,path:c.pathname+c.search,method:t,headers:{"Content-Type":"application/json",Accept:"application/json","User-Agent":"infernoflow-cli",apikey:f,...s,...a?{"Content-Length":Buffer.byteLength(a)}:{}}},o=m.request(y,u=>{let p="";u.on("data",d=>p+=d),u.on("end",()=>{try{h({status:u.statusCode,body:JSON.parse(p)})}catch{h({status:u.statusCode,body:p})}})});o.on("error",l),o.setTimeout(8e3,()=>{o.destroy(new Error("timeout"))}),a&&o.write(a),o.end()})}async function A(t,e,r){try{const s={project_id:r,ts:t.ts,type:t.type||"note",summary:t.summary,result:t.result||null,source:t.source||null,auto:t.auto||!1,agent:t.agent||null};await i("POST",`${n}/rest/v1/entries`,s,{Authorization:`Bearer ${e}`,Prefer:"return=minimal"})}catch{}}async function S(t){return await i("POST",`${n}/auth/v1/token?grant_type=pkce`,{auth_code:t},{})}function _(t,e){const r=new URLSearchParams({provider:"github",redirect_to:e,state:t});return`${n}/auth/v1/authorize?${r.toString()}`}async function g(t){const e=await i("GET",`${n}/auth/v1/user`,null,{Authorization:`Bearer ${t}`});return e.status===200?e.body:null}async function E(t,e){const r=new URLSearchParams({project_id:`eq.${e}`,order:"ts.asc",limit:"10000"}),s=await i("GET",`${n}/rest/v1/entries?${r.toString()}`,null,{Authorization:`Bearer ${t}`});return s.status===200&&Array.isArray(s.body)?s.body:[]}export{f as SUPABASE_ANON_KEY,n as SUPABASE_URL,S as exchangeCodeForSession,_ as getOAuthUrl,g as getUser,E as pullEntries,A as pushEntry};
1
+ import*as f from"node:https";const n=process.env.INFERNOFLOW_SUPABASE_URL||"https://vscesbbtmrsctfroigyx.supabase.co",h=process.env.INFERNOFLOW_SUPABASE_ANON_KEY||"sb_publishable_yThoZzOisgqLxrH8BOli-Q_yHVqEhUk";function i(t,e,r,s){return new Promise((l,y)=>{const c=new URL(e),a=r?JSON.stringify(r):null,d={hostname:c.hostname,port:443,path:c.pathname+c.search,method:t,headers:{"Content-Type":"application/json",Accept:"application/json","User-Agent":"infernoflow-cli",apikey:h,...s,...a?{"Content-Length":Buffer.byteLength(a)}:{}}},o=f.request(d,u=>{let p="";u.on("data",m=>p+=m),u.on("end",()=>{try{l({status:u.statusCode,body:JSON.parse(p)})}catch{l({status:u.statusCode,body:p})}})});o.on("error",y),o.setTimeout(8e3,()=>{o.destroy(new Error("timeout"))}),a&&o.write(a),o.end()})}async function A(t,e,r){try{const s={project_id:r,user_token:e,ts:t.ts,type:t.type||"note",summary:t.summary,result:t.result||null,source:t.source||null,auto:t.auto||!1,agent:t.agent||null};await i("POST",`${n}/rest/v1/entries`,s,{Authorization:`Bearer ${h}`,apikey:h,Prefer:"return=minimal"})}catch{}}async function S(t){return await i("POST",`${n}/auth/v1/token?grant_type=pkce`,{auth_code:t},{})}function _(t,e){const r=new URLSearchParams({provider:"github",redirect_to:e,state:t});return`${n}/auth/v1/authorize?${r.toString()}`}async function g(t){const e=await i("GET",`${n}/auth/v1/user`,null,{Authorization:`Bearer ${t}`});return e.status===200?e.body:null}async function E(t,e){const r=new URLSearchParams({project_id:`eq.${e}`,order:"ts.asc",limit:"10000"}),s=await i("GET",`${n}/rest/v1/entries?${r.toString()}`,null,{Authorization:`Bearer ${t}`});return s.status===200&&Array.isArray(s.body)?s.body:[]}export{h as SUPABASE_ANON_KEY,n as SUPABASE_URL,S as exchangeCodeForSession,_ as getOAuthUrl,g as getUser,E as pullEntries,A as pushEntry};
@@ -1,18 +1,18 @@
1
- import*as s from"node:fs";import*as f from"node:path";import"node:os";import{bold as N,cyan as v,gray as n,green as j,red as I}from"../ui/output.mjs";import{readCredentials as _,isLoggedIn as R}from"../cloud/credentials.mjs";import{pushEntry as k}from"../cloud/supabase.mjs";const O="inferno",h=f.join(O,"sessions.jsonl");function A(){try{const e=s.existsSync(h)?s.readFileSync(h,"utf8").split(`
2
- `).filter(Boolean).map(t=>{try{return JSON.parse(t)}catch{return null}}).filter(Boolean):[],c=e.filter(t=>t.type==="gotcha"),r=e.filter(t=>t.type==="decision"),l=e.filter(t=>t.type==="attempt"&&(t.result==="failed"||t.result==="partial")),i=["# Project Context (auto-generated by infernoflow)","",`> Last updated: ${new Date().toISOString()}`,""];if(c.length){i.push("## \u26A0\uFE0F Known Gotchas (Read These First)","");for(const t of c)i.push(`- ${t.summary}`);i.push("")}if(r.length){i.push("## \u2713 Decisions In Effect","");for(const t of r)i.push(`- ${t.summary}`);i.push("")}if(l.length){i.push("## \u274C Things That Don't Work (Don't Try These)","");for(const t of l)i.push(`- ${t.summary}`);i.push("")}const p=i.join(`
3
- `),a=process.cwd(),u=f.join(a,"CLAUDE.md");s.existsSync(u)&&s.writeFileSync(u,p,"utf8");const m=f.join(a,".cursorrules");(s.existsSync(m)||s.existsSync(f.join(a,".cursor")))&&s.writeFileSync(m,p,"utf8");const $=f.join(a,".github","copilot-instructions.md");s.existsSync(f.join(a,".github"))&&s.writeFileSync($,p,"utf8")}catch{}}const F=["note","attempt","decision","gotcha","preference","theme","handoff","error"],b=["worked","failed","partial","unknown"];function C(){return s.existsSync(h)?s.readFileSync(h,"utf8").split(`
1
+ import*as s from"node:fs";import*as u from"node:path";import"node:os";import{bold as N,cyan as v,gray as o,green as j,red as I}from"../ui/output.mjs";import{readCredentials as _,isLoggedIn as k}from"../cloud/credentials.mjs";import{pushEntry as R}from"../cloud/supabase.mjs";const O="inferno",h=u.join(O,"sessions.jsonl");function A(){try{const e=s.existsSync(h)?s.readFileSync(h,"utf8").split(`
2
+ `).filter(Boolean).map(t=>{try{return JSON.parse(t)}catch{return null}}).filter(Boolean):[],c=e.filter(t=>t.type==="gotcha"),r=e.filter(t=>t.type==="decision"),l=e.filter(t=>t.type==="attempt"&&(t.result==="failed"||t.result==="partial")),i=["# Project Context (auto-generated by infernoflow)","",`> Last updated: ${new Date().toISOString()}`,""];if(c.length){i.push("## \u26A0\uFE0F Known Gotchas (Read These First)","");for(const t of c)i.push(`- ${t.summary}`);i.push("")}if(r.length){i.push("## \u2713 Decisions In Effect","");for(const t of r)i.push(`- ${t.summary}`);i.push("")}if(l.length){i.push("## \u274C Things That Don't Work (Don't Try These)","");for(const t of l)i.push(`- ${t.summary}`);i.push("")}const d=i.join(`
3
+ `),a=process.cwd(),f=u.join(a,"CLAUDE.md");s.existsSync(f)&&s.writeFileSync(f,d,"utf8");const m=u.join(a,".cursorrules");(s.existsSync(m)||s.existsSync(u.join(a,".cursor")))&&s.writeFileSync(m,d,"utf8");const $=u.join(a,".github","copilot-instructions.md");s.existsSync(u.join(a,".github"))&&s.writeFileSync($,d,"utf8")}catch{}}const F=["note","attempt","decision","gotcha","preference","theme","handoff","error"],b=["worked","failed","partial","unknown"];function C(){return s.existsSync(h)?s.readFileSync(h,"utf8").split(`
4
4
  `).filter(Boolean).map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(Boolean):[]}function U(e,{auto:c=!1,quiet:r=!1}={}){if(!s.existsSync(O)){if(c)return!1;r||console.error(I(` \u2718 inferno/ not found \u2014 run: infernoflow init
5
5
  `)),process.exit(1)}return s.appendFileSync(h,JSON.stringify(e)+`
6
- `,"utf8"),!0}function B(){return process.env.CURSOR_SESSION?"cursor":process.env.COPILOT_SESSION?"copilot":process.env.CLAUDE_CODE_SESSION?"claude":process.env.WINDSURF_SESSION?"windsurf":process.env.INFERNOFLOW_AGENT?process.env.INFERNOFLOW_AGENT:"human"}function J(e,c){const r=new Date(e.ts).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),l=e.type||"note",i=l==="gotcha"?"\x1B[33m":l==="decision"?"\x1B[36m":l==="theme"?"\x1B[35m":l==="preference"?"\x1B[34m":l==="attempt"?"\x1B[90m":l==="error"?"\x1B[31m":"\x1B[0m",p="\x1B[0m",a=e.result?` [${e.result}]`:"",u=e.agent&&e.agent!=="human"?n(` (${e.agent})`):"";return` ${n(String(c+1).padStart(3))} ${n(r)} ${i}${l}${p}${a} ${e.summary}${u}`}async function K(e){const c=o=>e.includes(o),r=(o,g)=>{const d=e.indexOf(o);return d!==-1&&e[d+1]?e[d+1]:g},l=c("--show"),i=c("--clear"),p=c("--json"),a=c("--auto"),u=c("--quiet"),m=r("--source",null);if(l||p){const o=C(),g=e[e.indexOf("--show")+1],d=g&&/^\d+$/.test(g)?parseInt(g):20,w=o.slice(-d);if(p){console.log(JSON.stringify(w,null,2));return}if(console.log(`
7
- `+N("\u{1F525} infernoflow \u2014 session memory")),console.log(" "+"\u2500".repeat(50)),!w.length){console.log(n(`
6
+ `,"utf8"),!0}function B(){return process.env.CURSOR_SESSION?"cursor":process.env.COPILOT_SESSION?"copilot":process.env.CLAUDE_CODE_SESSION?"claude":process.env.WINDSURF_SESSION?"windsurf":process.env.INFERNOFLOW_AGENT?process.env.INFERNOFLOW_AGENT:"human"}function J(e,c){const r=new Date(e.ts).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),l=e.type||"note",i=l==="gotcha"?"\x1B[33m":l==="decision"?"\x1B[36m":l==="theme"?"\x1B[35m":l==="preference"?"\x1B[34m":l==="attempt"?"\x1B[90m":l==="error"?"\x1B[31m":"\x1B[0m",d="\x1B[0m",a=e.result?` [${e.result}]`:"",f=e.agent&&e.agent!=="human"?o(` (${e.agent})`):"";return` ${o(String(c+1).padStart(3))} ${o(r)} ${i}${l}${d}${a} ${e.summary}${f}`}async function K(e){const c=n=>e.includes(n),r=(n,g)=>{const p=e.indexOf(n);return p!==-1&&e[p+1]?e[p+1]:g},l=c("--show"),i=c("--clear"),d=c("--json"),a=c("--auto"),f=c("--quiet"),m=r("--source",null);if(l||d){const n=C(),g=e[e.indexOf("--show")+1],p=g&&/^\d+$/.test(g)?parseInt(g):20,S=n.slice(-p);if(d){console.log(JSON.stringify(S,null,2));return}if(console.log(`
7
+ `+N("\u{1F525} infernoflow \u2014 session memory")),console.log(" "+"\u2500".repeat(50)),!S.length){console.log(o(`
8
8
  No entries yet. Start logging with: infernoflow log "<what happened>"
9
- `));return}console.log(n(` Showing last ${w.length} of ${o.length} entries
10
- `)),w.forEach((D,T)=>console.log(J(D,o.length-w.length+T))),console.log();return}if(i){if(!s.existsSync(h)){console.log(n(` Nothing to clear.
11
- `));return}const o=h.replace(".jsonl",`-archive-${Date.now()}.jsonl`);s.renameSync(h,o),console.log(j(` \u2714 Session log archived \u2192 ${f.basename(o)}
12
- `));return}const $=new Set([r("--type",""),r("--result",""),r("--agent",""),r("--source","")].filter(Boolean)),x=e.filter(o=>!o.startsWith("--")&&!$.has(o)).join(" ").trim();if(!x){console.log(`
9
+ `));return}console.log(o(` Showing last ${S.length} of ${n.length} entries
10
+ `)),S.forEach((D,T)=>console.log(J(D,n.length-S.length+T))),console.log();return}if(i){if(!s.existsSync(h)){console.log(o(` Nothing to clear.
11
+ `));return}const n=h.replace(".jsonl",`-archive-${Date.now()}.jsonl`);s.renameSync(h,n),console.log(j(` \u2714 Session log archived \u2192 ${u.basename(n)}
12
+ `));return}const $=new Set([r("--type",""),r("--result",""),r("--agent",""),r("--source","")].filter(Boolean)),x=e.filter(n=>!n.startsWith("--")&&!$.has(n)).join(" ").trim();if(!x){console.log(`
13
13
  `+N("\u{1F525} infernoflow log")+` \u2014 append to session memory
14
- `),console.log(n(" Usage:")),console.log(n(' infernoflow log "what happened"')),console.log(n(' infernoflow log "tried X, failed because Y" --type attempt --result failed')),console.log(n(' infernoflow log "always use multipart/form-data" --type gotcha')),console.log(n(' infernoflow log "switched to dark mode" --type theme')),console.log(n(" infernoflow log --show Print last 20 entries")),console.log(n(" infernoflow log --json Print as JSON")),console.log(),console.log(n(" Types: note \xB7 attempt \xB7 decision \xB7 gotcha \xB7 preference \xB7 theme \xB7 handoff \xB7 error")),console.log(n(" Results: worked \xB7 failed \xB7 partial \xB7 unknown")),console.log(n(` Auto-capture: --auto (silent skip if no inferno/) \xB7 --quiet \xB7 --source <name>
15
- `));return}const S=r("--type","note"),y=r("--result",null),L=r("--agent",B());F.includes(S)||(u||console.error(I(` \u2718 Invalid type: ${S}. Valid: ${F.join(", ")}
16
- `)),process.exit(1)),y&&!b.includes(y)&&(u||console.error(I(` \u2718 Invalid result: ${y}. Valid: ${b.join(", ")}
17
- `)),process.exit(1));const E={ts:new Date().toISOString(),agent:L,type:S,summary:x,...y?{result:y}:{},...m?{source:m}:{},...a?{auto:!0}:{}};if(U(E,{auto:a,quiet:u})){A();try{if(R()){const o=_(),g=(()=>{try{return JSON.parse(s.readFileSync(f.join(O,"config.json"),"utf8")).projectId||f.basename(process.cwd())}catch{return f.basename(process.cwd())}})();k(E,o.access_token,g).catch(()=>{})}}catch{}if(!u){const o=S!=="note"?v(` [${S}]`):"",g=y?n(` \u2192 ${y}`):"",d=m?n(` (via ${m})`):"";console.log(j(` \u2714 Logged${o}${g}${d}: `)+x+`
14
+ `),console.log(o(" Usage:")),console.log(o(' infernoflow log "what happened"')),console.log(o(' infernoflow log "tried X, failed because Y" --type attempt --result failed')),console.log(o(' infernoflow log "always use multipart/form-data" --type gotcha')),console.log(o(' infernoflow log "switched to dark mode" --type theme')),console.log(o(" infernoflow log --show Print last 20 entries")),console.log(o(" infernoflow log --json Print as JSON")),console.log(),console.log(o(" Types: note \xB7 attempt \xB7 decision \xB7 gotcha \xB7 preference \xB7 theme \xB7 handoff \xB7 error")),console.log(o(" Results: worked \xB7 failed \xB7 partial \xB7 unknown")),console.log(o(` Auto-capture: --auto (silent skip if no inferno/) \xB7 --quiet \xB7 --source <name>
15
+ `));return}const w=r("--type","note"),y=r("--result",null),L=r("--agent",B());F.includes(w)||(f||console.error(I(` \u2718 Invalid type: ${w}. Valid: ${F.join(", ")}
16
+ `)),process.exit(1)),y&&!b.includes(y)&&(f||console.error(I(` \u2718 Invalid result: ${y}. Valid: ${b.join(", ")}
17
+ `)),process.exit(1));const E={ts:new Date().toISOString(),agent:L,type:w,summary:x,...y?{result:y}:{},...m?{source:m}:{},...a?{auto:!0}:{}};if(U(E,{auto:a,quiet:f})){A();try{if(k()){const n=_(),g=n.user_token||n.user?.id||n.user?.login||"anonymous",p=(()=>{try{return JSON.parse(s.readFileSync(u.join(O,"config.json"),"utf8")).projectId||u.basename(process.cwd())}catch{return u.basename(process.cwd())}})();R(E,g,p).catch(()=>{})}}catch{}if(!f){const n=w!=="note"?v(` [${w}]`):"",g=y?o(` \u2192 ${y}`):"",p=m?o(` (via ${m})`):"";console.log(j(` \u2714 Logged${n}${g}${p}: `)+x+`
18
18
  `)}}}export{K as logCommand};
@@ -1,30 +1 @@
1
- import*as S from"node:http";import"node:crypto";import"node:https";import"node:fs";import"node:path";import{bold as a,cyan as g,gray as s,green as d,red as h,yellow as L}from"../ui/output.mjs";import{readCredentials as y,writeCredentials as x,deleteCredentials as v,isLoggedIn as _}from"../cloud/credentials.mjs";import{getUser as T,SUPABASE_URL as w}from"../cloud/supabase.mjs";const m=9242,$="/auth/callback",C=`http://localhost:${m}${$}`;function P(n){try{const{execSync:t}=require("child_process"),c=process.platform==="win32"?`start "" "${n}"`:process.platform==="darwin"?`open "${n}"`:`xdg-open "${n}"`;t(c,{stdio:"ignore"})}catch{}}async function O(){if(w.includes("YOUR_PROJECT")&&(console.log(),console.log(` ${h("\u2718")} Supabase not configured yet.`),console.log(),process.exit(1)),_()){const e=y()?.user;console.log(),console.log(` ${d("\u2714")} Already logged in as ${a(e?.email||e?.user_metadata?.user_name||"unknown")}`),console.log(` Run ${g("infernoflow logout")} to sign out.`),console.log();return}console.log(),console.log(` ${a("\u{1F525} infernoflow login")}`),console.log();let n,t;const c=new Promise((o,e)=>{n=o,t=e}),l=S.createServer((o,e)=>{if(new URL(o.url,`http://localhost:${m}`).pathname!==$){e.writeHead(404),e.end("Not found");return}if(o.method==="GET"){e.writeHead(200,{"Content-Type":"text/html"}),e.end(`<!DOCTYPE html>
2
- <html>
3
- <head><meta charset="utf-8"><title>infernoflow login</title>
4
- <style>body{font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#0f172a;color:#f1f5f9}</style>
5
- </head>
6
- <body>
7
- <script>
8
- const hash = window.location.hash.slice(1);
9
- const params = new URLSearchParams(hash);
10
- const access_token = params.get('access_token');
11
- const refresh_token = params.get('refresh_token');
12
- const expires_in = params.get('expires_in');
13
- const error = params.get('error_description') || params.get('error');
14
- if (access_token) {
15
- fetch('/auth/callback', {
16
- method: 'POST',
17
- headers: {'Content-Type': 'application/json'},
18
- body: JSON.stringify({ access_token, refresh_token, expires_in })
19
- }).then(() => {
20
- document.body.innerHTML = '<div style="text-align:center"><div style="font-size:48px">\u{1F525}</div><h2>Logged in!</h2><p style="color:#94a3b8">You can close this tab and return to the terminal.</p></div>';
21
- });
22
- } else if (error) {
23
- document.body.innerHTML = '<div style="text-align:center"><h2>Login failed</h2><p style="color:#f87171">' + error + '</p></div>';
24
- fetch('/auth/callback', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ error }) });
25
- } else {
26
- document.body.innerHTML = '<div style="text-align:center"><p style="color:#94a3b8">Processing\u2026</p></div>';
27
- }
28
- </script>
29
- </body>
30
- </html>`);return}if(o.method==="POST"){let p="";o.on("data",r=>p+=r),o.on("end",()=>{e.writeHead(200),e.end("ok"),l.close();try{const r=JSON.parse(p);r.error?t(new Error(r.error)):n(r)}catch(r){t(r)}})}});l.listen(m,"127.0.0.1",()=>{const o=new URLSearchParams({provider:"github",redirect_to:C}),e=`${w}/auth/v1/authorize?${o.toString()}`;console.log(" Opening browser for GitHub login\u2026"),console.log(),console.log(` ${s("If the browser doesn't open, visit:")}`),console.log(` ${g(e)}`),console.log(),P(e),console.log(` ${s("Waiting for login\u2026")} ${s("(Ctrl+C to cancel)")}`)});const i=setTimeout(()=>{l.close(),t(new Error("Login timed out after 3 minutes"))},180*1e3);try{const o=await c;clearTimeout(i);const e=await T(o.access_token),u={access_token:o.access_token,refresh_token:o.refresh_token||null,expires_at:o.expires_in?new Date(Date.now()+parseInt(o.expires_in)*1e3).toISOString():null,user:e,logged_in_at:new Date().toISOString()};x(u);const f=e?.user_metadata?.user_name||e?.email||"unknown";console.log(),console.log(` ${d("\u2714")} Logged in as ${a(f)}`),console.log(),console.log(` ${s("Session memory will now sync to the cloud on every")} ${g("infernoflow log")}`),console.log()}catch(o){clearTimeout(i);try{l.close()}catch{}console.log(),console.log(` ${h("\u2718")} Login failed: ${o.message}`),console.log(),process.exit(1)}}function k(){const n=v();console.log(),console.log(n?` ${d("\u2714")} Logged out. Local credentials removed.`:` ${s("Already logged out.")}`),console.log()}async function b(){const n=y();if(console.log(),!n?.access_token){console.log(` ${s("Not logged in.")} Run ${g("infernoflow login")}`),console.log();return}const t=n.user?.user_metadata?.user_name||n.user?.email||"unknown",c=n.user?.email||s("(no email)"),l=n.logged_in_at?new Date(n.logged_in_at).toLocaleDateString():"unknown",i=!_();console.log(` ${a("\u{1F525} infernoflow")} \u2014 logged in as:`),console.log(),console.log(` User: ${a(t)}`),console.log(` Email: ${c}`),console.log(` Since: ${s(l)}`),console.log(i?` Status: ${L("\u26A0 Token expired \u2014 run infernoflow login to refresh")}`:` Status: ${d("\u2714 Active")}`),console.log()}async function B(n){const t=n[1];return t==="logout"?k():t==="whoami"?b():O()}async function j(){return k()}async function J(){return b()}export{B as loginCommand,j as logoutCommand,J as whoamiCommand};
1
+ import*as w from"node:https";import{bold as i,cyan as d,gray as a,green as m,red as f}from"../ui/output.mjs";import{readCredentials as $,writeCredentials as S,deleteCredentials as k,isLoggedIn as b}from"../cloud/credentials.mjs";const h="Ov23liYuUKwDRTzrywsa";function y(o,n,s){return new Promise((r,u)=>{const g=new URLSearchParams(s).toString(),c={hostname:o,port:443,path:n,method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json","User-Agent":"infernoflow-cli","Content-Length":Buffer.byteLength(g)}},l=w.request(c,e=>{let t="";e.on("data",p=>t+=p),e.on("end",()=>{try{r(JSON.parse(t))}catch{r(t)}})});l.on("error",u),l.setTimeout(15e3,()=>l.destroy(new Error("timeout"))),l.write(g),l.end()})}function v(o,n,s){return new Promise((r,u)=>{const g={hostname:o,port:443,path:n,method:"GET",headers:{Accept:"application/json","User-Agent":"infernoflow-cli",Authorization:`Bearer ${s}`}},c=w.request(g,l=>{let e="";l.on("data",t=>e+=t),l.on("end",()=>{try{r(JSON.parse(e))}catch{r(e)}})});c.on("error",u),c.setTimeout(1e4,()=>c.destroy(new Error("timeout"))),c.end()})}function _(o){return new Promise(n=>setTimeout(n,o))}function A(o){try{const{execSync:n}=require("child_process"),s=process.platform==="win32"?`start "" "${o}"`:process.platform==="darwin"?`open "${o}"`:`xdg-open "${o}"`;n(s,{stdio:"ignore"})}catch{}}async function T(){if(b()){const e=$(),t=e?.user?.login||e?.user?.name||"unknown";console.log(),console.log(` ${m("\u2714")} Already logged in as ${i(t)}`),console.log(` Run ${d("infernoflow logout")} to sign out.`),console.log();return}console.log(),console.log(` ${i("\u{1F525} infernoflow login")}`),console.log();let o;try{o=await y("github.com","/login/device/code",{client_id:h,scope:"read:user user:email"})}catch(e){console.log(` ${f("\u2718")} Could not reach GitHub: ${e.message}`),console.log(),process.exit(1)}o.device_code||(console.log(` ${f("\u2718")} GitHub error: ${JSON.stringify(o)}`),console.log(),process.exit(1));const{device_code:n,user_code:s,verification_uri:r,expires_in:u,interval:g}=o,c=(g||5)*1e3;console.log(` ${i("Open this URL in your browser:")}`),console.log(` ${d(r)}`),console.log(),console.log(` ${i("Enter this code:")}`),console.log(` ${i(d(s))}`),console.log(),A(r),console.log(` ${a("Waiting for you to authorize\u2026")} ${a("(Ctrl+C to cancel)")}`),console.log();const l=Date.now()+(u||900)*1e3;for(;Date.now()<l;){await _(c);let e;try{e=await y("github.com","/login/oauth/access_token",{client_id:h,device_code:n,grant_type:"urn:ietf:params:oauth:grant-type:device_code"})}catch{continue}if(e.error!=="authorization_pending"){if(e.error==="slow_down"){await _(5e3);continue}if(e.error==="expired_token"&&(console.log(` ${f("\u2718")} Code expired. Run infernoflow login again.`),process.exit(1)),e.error==="access_denied"&&(console.log(` ${f("\u2718")} Access denied.`),process.exit(1)),e.access_token){const t=await v("api.github.com","/user",e.access_token),p=t?.login||t?.name||"unknown",L={access_token:e.access_token,refresh_token:null,expires_at:null,user:t,logged_in_at:new Date().toISOString()};S(L),console.log(` ${m("\u2714")} Logged in as ${i(p)}`),console.log(),console.log(` ${a("Session memory will now sync to the cloud on every")} ${d("infernoflow log")}`),console.log();return}}}console.log(` ${f("\u2718")} Login timed out. Run infernoflow login to try again.`),process.exit(1)}function x(){const o=k();console.log(),console.log(o?` ${m("\u2714")} Logged out. Local credentials removed.`:` ${a("Already logged out.")}`),console.log()}function C(){const o=$();if(console.log(),!o?.access_token){console.log(` ${a("Not logged in.")} Run ${d("infernoflow login")}`),console.log();return}const n=o.user?.login||o.user?.name||"unknown",s=o.user?.email||a("(no email)"),r=o.logged_in_at?new Date(o.logged_in_at).toLocaleDateString():"unknown";console.log(` ${i("\u{1F525} infernoflow")} \u2014 logged in as:`),console.log(),console.log(` User: ${i(n)}`),console.log(` Email: ${s}`),console.log(` Since: ${a(r)}`),console.log(` Status: ${m("\u2714 Active")}`),console.log()}async function R(o){const n=o[1];return n==="logout"?x():n==="whoami"?C():T()}async function U(){return x()}async function E(){return C()}export{R as loginCommand,U as logoutCommand,E as whoamiCommand};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infernoflow",
3
- "version": "0.38.3",
3
+ "version": "0.38.5",
4
4
  "description": "Persistent memory for AI coding sessions — captures what agents can't infer from code alone. Works with Copilot, Cursor, Claude, and Windsurf.",
5
5
  "type": "module",
6
6
  "bin": {