infernoflow 0.43.5 → 0.43.7

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,20 +1,20 @@
1
- import*as l from"node:fs";import*as p from"node:path";import"node:os";import{bold as N,cyan as A,gray as t,green as D,red as b}from"../ui/output.mjs";import{readCredentials as R,isLoggedIn as _}from"../cloud/credentials.mjs";import{pushEntry as C}from"../cloud/supabase.mjs";import{ampPaths as P,appendEntry as U,readEntries as F,AMP_MARKERS as E}from"../amp/io.mjs";function L(){return P(process.cwd())}function W(){try{const e=F(process.cwd()),a=e.filter(n=>n.type==="gotcha"),c=e.filter(n=>n.type==="decision"),r=e.filter(n=>n.type==="attempt"&&(n.result==="failed"||n.result==="partial")),s=["# Project Context (auto-generated by infernoflow)","",`> Last updated: ${new Date().toISOString()}`,""];if(a.length){s.push("## \u26A0\uFE0F Known Gotchas (Read These First)","");for(const n of a)s.push(`- ${n.summary}`);s.push("")}if(c.length){s.push("## \u2713 Decisions In Effect","");for(const n of c)s.push(`- ${n.summary}`);s.push("")}if(r.length){s.push("## \u274C Things That Don't Work (Don't Try These)","");for(const n of r)s.push(`- ${n.summary}`);s.push("")}const d=s.join(`
2
- `),m=`${E.start}
3
- ${d}
1
+ import*as f from"node:fs";import*as m from"node:path";import"node:os";import{bold as j,cyan as A,gray as o,green as D,red as b}from"../ui/output.mjs";import{ampPaths as R,appendEntry as k,readEntries as N,AMP_MARKERS as E}from"../amp/io.mjs";function _(){return R(process.cwd())}function C(){try{const e=N(process.cwd()),l=e.filter(n=>n.type==="gotcha"),i=e.filter(n=>n.type==="decision"),s=e.filter(n=>n.type==="attempt"&&(n.result==="failed"||n.result==="partial")),t=["# Project Context (auto-generated by infernoflow)","",`> Last updated: ${new Date().toISOString()}`,""];if(l.length){t.push("## \u26A0\uFE0F Known Gotchas (Read These First)","");for(const n of l)t.push(`- ${n.summary}`);t.push("")}if(i.length){t.push("## \u2713 Decisions In Effect","");for(const n of i)t.push(`- ${n.summary}`);t.push("")}if(s.length){t.push("## \u274C Things That Don't Work (Don't Try These)","");for(const n of s)t.push(`- ${n.summary}`);t.push("")}const p=t.join(`
2
+ `),g=`${E.start}
3
+ ${p}
4
4
  ${E.end}
5
- `,u=process.cwd(),h=n=>{let i="";try{i=l.readFileSync(n,"utf8")}catch{}const $=i.indexOf(E.start),w=i.indexOf(E.end);let x;$!==-1&&w!==-1&&w>$?x=i.slice(0,$)+m+i.slice(w+E.end.length).replace(/^\n+/,""):i?x=i.replace(/\s+$/,"")+`
5
+ `,a=process.cwd(),d=n=>{let c="";try{c=f.readFileSync(n,"utf8")}catch{}const w=c.indexOf(E.start),S=c.indexOf(E.end);let $;w!==-1&&S!==-1&&S>w?$=c.slice(0,w)+g+c.slice(S+E.end.length).replace(/^\n+/,""):c?$=c.replace(/\s+$/,"")+`
6
6
 
7
- `+m:x=m,l.writeFileSync(n,x,"utf8")},I=p.join(u,"CLAUDE.md");l.existsSync(I)&&h(I);const j=p.join(u,".cursorrules");(l.existsSync(j)||l.existsSync(p.join(u,".cursor")))&&h(j);const S=p.join(u,".github","copilot-instructions.md");l.existsSync(p.join(u,".github"))&&h(S)}catch{}}const T=["note","attempt","decision","gotcha","preference","theme","handoff","error"],v=["worked","failed","partial","unknown"];function V(){return F(process.cwd())}function q(e,{auto:a=!1,quiet:c=!1}={}){const r=process.cwd(),s=p.join(r,".ai-memory"),d=p.join(r,"inferno");return a&&!l.existsSync(s)&&!l.existsSync(d)?!1:(!l.existsSync(s)&&!l.existsSync(d)&&(c||console.error(b(` \u2718 no .ai-memory/ or inferno/ \u2014 run: infernoflow init
8
- `)),process.exit(1)),U(r,e),!0)}function G(){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 M(e,a){const c=new Date(e.ts).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),r=e.type||"note",s=r==="gotcha"?"\x1B[33m":r==="decision"?"\x1B[36m":r==="theme"?"\x1B[35m":r==="preference"?"\x1B[34m":r==="attempt"?"\x1B[90m":r==="error"?"\x1B[31m":"\x1B[0m",d="\x1B[0m",m=e.result?` [${e.result}]`:"",u=e.agent&&e.agent!=="human"?t(` (${e.agent})`):"";return` ${t(String(a+1).padStart(3))} ${t(c)} ${s}${r}${d}${m} ${e.summary}${u}`}async function H(e){const a=o=>e.includes(o),c=(o,f)=>{const g=e.indexOf(o);return g!==-1&&e[g+1]?e[g+1]:f},r=a("--show"),s=a("--clear"),d=a("--json"),m=a("--auto"),u=a("--quiet"),h=c("--source",null);if(r||d){const o=V(),f=e[e.indexOf("--show")+1],g=f&&/^\d+$/.test(f)?parseInt(f):20,y=o.slice(-g);if(d){console.log(JSON.stringify(y,null,2));return}if(console.log(`
9
- `+N("\u{1F525} infernoflow \u2014 session memory")),console.log(" "+"\u2500".repeat(50)),!y.length){console.log(t(`
7
+ `+g:$=g,f.writeFileSync(n,$,"utf8")},O=m.join(a,"CLAUDE.md");f.existsSync(O)&&d(O);const I=m.join(a,".cursorrules");(f.existsSync(I)||f.existsSync(m.join(a,".cursor")))&&d(I);const y=m.join(a,".github","copilot-instructions.md");f.existsSync(m.join(a,".github"))&&d(y)}catch{}}const F=["note","attempt","decision","gotcha","preference","theme","handoff","error"],L=["worked","failed","partial","unknown"];function P(){return N(process.cwd())}function U(e,{auto:l=!1,quiet:i=!1}={}){const s=process.cwd(),t=m.join(s,".ai-memory"),p=m.join(s,"inferno");return l&&!f.existsSync(t)&&!f.existsSync(p)?!1:(!f.existsSync(t)&&!f.existsSync(p)&&(i||console.error(b(` \u2718 no .ai-memory/ or inferno/ \u2014 run: infernoflow init
8
+ `)),process.exit(1)),k(s,e),!0)}function W(){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 V(e,l){const i=new Date(e.ts).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),s=e.type||"note",t=s==="gotcha"?"\x1B[33m":s==="decision"?"\x1B[36m":s==="theme"?"\x1B[35m":s==="preference"?"\x1B[34m":s==="attempt"?"\x1B[90m":s==="error"?"\x1B[31m":"\x1B[0m",p="\x1B[0m",g=e.result?` [${e.result}]`:"",a=e.agent&&e.agent!=="human"?o(` (${e.agent})`):"";return` ${o(String(l+1).padStart(3))} ${o(i)} ${t}${s}${p}${g} ${e.summary}${a}`}async function J(e){const l=r=>e.includes(r),i=(r,u)=>{const h=e.indexOf(r);return h!==-1&&e[h+1]?e[h+1]:u},s=l("--show"),t=l("--clear"),p=l("--json"),g=l("--auto"),a=l("--quiet"),d=i("--source",null);if(s||p){const r=P(),u=e[e.indexOf("--show")+1],h=u&&/^\d+$/.test(u)?parseInt(u):20,x=r.slice(-h);if(p){console.log(JSON.stringify(x,null,2));return}if(console.log(`
9
+ `+j("\u{1F525} infernoflow \u2014 session memory")),console.log(" "+"\u2500".repeat(50)),!x.length){console.log(o(`
10
10
  No entries yet. Start logging with: infernoflow log "<what happened>"
11
- `));return}console.log(t(` Showing last ${y.length} of ${o.length} entries
12
- `)),y.forEach((O,k)=>console.log(M(O,o.length-y.length+k))),console.log();return}if(s){const{sessions:o}=L();if(!l.existsSync(o)){console.log(t(` Nothing to clear.
13
- `));return}const f=o.replace(".jsonl",`-archive-${Date.now()}.jsonl`);l.renameSync(o,f),console.log(D(` \u2714 Session log archived \u2192 ${p.basename(f)}
14
- `));return}const I=new Set([c("--type",""),c("--result",""),c("--agent",""),c("--source","")].filter(Boolean)),S=e.slice(1).filter(o=>!o.startsWith("--")&&!I.has(o)).join(" ").trim();if(!S){console.log(`
15
- `+N("\u{1F525} infernoflow log")+` \u2014 append to session memory
16
- `),console.log(t(" Usage:")),console.log(t(' infernoflow log "what happened"')),console.log(t(' infernoflow log "tried X, failed because Y" --type attempt --result failed')),console.log(t(' infernoflow log "always use multipart/form-data" --type gotcha')),console.log(t(' infernoflow log "switched to dark mode" --type theme')),console.log(t(" infernoflow log --show Print last 20 entries")),console.log(t(" infernoflow log --json Print as JSON")),console.log(),console.log(t(" Types: note \xB7 attempt \xB7 decision \xB7 gotcha \xB7 preference \xB7 theme \xB7 handoff \xB7 error")),console.log(t(" Results: worked \xB7 failed \xB7 partial \xB7 unknown")),console.log(t(` Auto-capture: --auto (silent skip if no inferno/) \xB7 --quiet \xB7 --source <name>
17
- `));return}const n=c("--type","note"),i=c("--result",null),$=c("--agent",G());T.includes(n)||(u||console.error(b(` \u2718 Invalid type: ${n}. Valid: ${T.join(", ")}
18
- `)),process.exit(1)),i&&!v.includes(i)&&(u||console.error(b(` \u2718 Invalid result: ${i}. Valid: ${v.join(", ")}
19
- `)),process.exit(1));const w={ts:new Date().toISOString(),agent:$,type:n,summary:S,...i?{result:i}:{},...h?{source:h}:{},...m?{auto:!0}:{}};if(q(w,{auto:m,quiet:u})){W();try{if(_()){const o=R(),f=o.user_token||o.user?.id||o.user?.login||"anonymous",g=(()=>{try{const{config:y}=L(),O=JSON.parse(l.readFileSync(y,"utf8"));return O.project||O.projectId||p.basename(process.cwd())}catch{return p.basename(process.cwd())}})();await C(w,f,g)}}catch{}if(!u){const o=n!=="note"?A(` [${n}]`):"",f=i?t(` \u2192 ${i}`):"",g=h?t(` (via ${h})`):"";console.log(D(` \u2714 Logged${o}${f}${g}: `)+S+`
20
- `)}}}export{H as logCommand};
11
+ `));return}console.log(o(` Showing last ${x.length} of ${r.length} entries
12
+ `)),x.forEach((v,T)=>console.log(V(v,r.length-x.length+T))),console.log();return}if(t){const{sessions:r}=_();if(!f.existsSync(r)){console.log(o(` Nothing to clear.
13
+ `));return}const u=r.replace(".jsonl",`-archive-${Date.now()}.jsonl`);f.renameSync(r,u),console.log(D(` \u2714 Session log archived \u2192 ${m.basename(u)}
14
+ `));return}const O=new Set([i("--type",""),i("--result",""),i("--agent",""),i("--source","")].filter(Boolean)),y=e.slice(1).filter(r=>!r.startsWith("--")&&!O.has(r)).join(" ").trim();if(!y){console.log(`
15
+ `+j("\u{1F525} infernoflow log")+` \u2014 append to session memory
16
+ `),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>
17
+ `));return}const n=i("--type","note"),c=i("--result",null),w=i("--agent",W());F.includes(n)||(a||console.error(b(` \u2718 Invalid type: ${n}. Valid: ${F.join(", ")}
18
+ `)),process.exit(1)),c&&!L.includes(c)&&(a||console.error(b(` \u2718 Invalid result: ${c}. Valid: ${L.join(", ")}
19
+ `)),process.exit(1));const S={ts:new Date().toISOString(),agent:w,type:n,summary:y,...c?{result:c}:{},...d?{source:d}:{},...g?{auto:!0}:{}};if(U(S,{auto:g,quiet:a})&&(C(),!a)){const r=n!=="note"?A(` [${n}]`):"",u=c?o(` \u2192 ${c}`):"",h=d?o(` (via ${d})`):"";console.log(D(` \u2714 Logged${r}${u}${h}: `)+y+`
20
+ `)}}export{J as logCommand};
package/package.json CHANGED
@@ -1,63 +1,61 @@
1
- {
2
- "name": "infernoflow",
3
- "version": "0.43.5",
4
- "description": "Persistent memory for AI coding sessions \u2014 captures what agents can't infer from code alone. Works with Copilot, Cursor, Claude, and Windsurf.",
5
- "type": "module",
6
- "bin": {
7
- "infernoflow": "dist/bin/infernoflow.mjs"
8
- },
9
- "engines": {
10
- "node": ">=18"
11
- },
12
- "files": [
13
- "dist/bin",
14
- "dist/lib",
15
- "dist/templates",
16
- "README.md",
17
- "scripts/postinstall.js"
18
- ],
19
- "scripts": {
20
- "test": "node scripts/smoke.mjs && node scripts/json-smoke.mjs && node scripts/json-negative-smoke.mjs && node scripts/implement-smoke.mjs && node scripts/adopt-smoke.mjs && node scripts/pr-impact-smoke.mjs && node scripts/sync-smoke.mjs && node scripts/run-smoke.mjs",
21
- "test:help": "node bin/infernoflow.mjs --help",
22
- "build": "node build.mjs",
23
- "prepublishOnly": "echo skipping build",
24
- "inferno:promote-draft": "node scripts/inferno-promote-draft.mjs",
25
- "postinstall": "node scripts/postinstall.js"
26
- },
27
- "dependencies": {},
28
- "keywords": [
29
- "ai",
30
- "ai-memory",
31
- "ai-context",
32
- "ai-coding",
33
- "session-memory",
34
- "persistent-memory",
35
- "agent-memory",
36
- "agent-handoff",
37
- "copilot",
38
- "cursor",
39
- "claude",
40
- "windsurf",
41
- "mcp",
42
- "mcp-server",
43
- "context-switching",
44
- "developer-tools",
45
- "cli",
46
- "infernoflow",
47
- "gotchas",
48
- "coding-assistant"
49
- ],
50
- "author": "infernoflow",
51
- "license": "MIT",
52
- "repository": {
53
- "type": "git",
54
- "url": "git+https://github.com/ronmiz/infernoflow.git"
55
- },
56
- "homepage": "https://infernoflow.dev",
57
- "bugs": {
58
- "url": "https://github.com/ronmiz/infernoflow/issues"
59
- },
60
- "devDependencies": {
61
- "esbuild": "^0.28.0"
62
- }
63
- }
1
+ {
2
+ "name": "infernoflow",
3
+ "version": "0.43.7",
4
+ "description": "Persistent memory for AI coding sessions \u2014 captures what agents can't infer from code alone. Works with Copilot, Cursor, Claude, and Windsurf.",
5
+ "type": "module",
6
+ "bin": {
7
+ "infernoflow": "dist/bin/infernoflow.mjs"
8
+ },
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
12
+ "files": [
13
+ "dist/bin",
14
+ "dist/lib",
15
+ "dist/templates",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "test": "node scripts/smoke.mjs && node scripts/json-smoke.mjs && node scripts/json-negative-smoke.mjs && node scripts/implement-smoke.mjs && node scripts/adopt-smoke.mjs && node scripts/pr-impact-smoke.mjs && node scripts/sync-smoke.mjs && node scripts/run-smoke.mjs",
20
+ "test:help": "node bin/infernoflow.mjs --help",
21
+ "build": "node build.mjs",
22
+ "prepublishOnly": "echo skipping build",
23
+ "inferno:promote-draft": "node scripts/inferno-promote-draft.mjs"
24
+ },
25
+ "dependencies": {},
26
+ "keywords": [
27
+ "ai",
28
+ "ai-memory",
29
+ "ai-context",
30
+ "ai-coding",
31
+ "session-memory",
32
+ "persistent-memory",
33
+ "agent-memory",
34
+ "agent-handoff",
35
+ "copilot",
36
+ "cursor",
37
+ "claude",
38
+ "windsurf",
39
+ "mcp",
40
+ "mcp-server",
41
+ "context-switching",
42
+ "developer-tools",
43
+ "cli",
44
+ "infernoflow",
45
+ "gotchas",
46
+ "coding-assistant"
47
+ ],
48
+ "author": "infernoflow",
49
+ "license": "MIT",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/ronmiz/infernoflow.git"
53
+ },
54
+ "homepage": "https://infernoflow.dev",
55
+ "bugs": {
56
+ "url": "https://github.com/ronmiz/infernoflow/issues"
57
+ },
58
+ "devDependencies": {
59
+ "esbuild": "^0.28.0"
60
+ }
61
+ }
@@ -1,2 +0,0 @@
1
- import*as r from"node:fs";import*as i from"node:path";import*as c from"node:os";const n=i.join(c.homedir(),".infernoflow"),t=i.join(n,"credentials.json");function o(){try{return r.existsSync(t)?JSON.parse(r.readFileSync(t,"utf8")):null}catch{return null}}function u(e){r.existsSync(n)||r.mkdirSync(n,{recursive:!0}),r.writeFileSync(t,JSON.stringify(e,null,2)+`
2
- `,{mode:384})}function a(){try{return r.existsSync(t)&&r.unlinkSync(t),!0}catch{return!1}}function f(){const e=o();return e?!!(e.mode==="supabase"&&e.access_token||e.mode==="device-flow"&&e.github_access_token||!e.mode&&e.access_token):!1}function l(){const e=o();if(!e||e.mode!=="supabase"||!e.access_token)return null;if(e.expires_at){const s=Date.parse(e.expires_at);if(!Number.isNaN(s)&&Date.now()>s-6e4)return null}return e.access_token}export{a as deleteCredentials,l as getSupabaseAccessToken,f as isLoggedIn,o as readCredentials,u as writeCredentials};
@@ -1 +0,0 @@
1
- import*as A from"node:https";import{readCredentials as S,writeCredentials as w,getSupabaseAccessToken as d}from"./credentials.mjs";const o=process.env.INFERNOFLOW_SUPABASE_URL||"https://vscesbbtmrsctfroigyx.supabase.co",i=process.env.INFERNOFLOW_SUPABASE_ANON_KEY||"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZzY2VzYmJ0bXJzY3Rmcm9pZ3l4Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzc0ODAxMjcsImV4cCI6MjA5MzA1NjEyN30.4WCXr0aGBlqC2m29DnlCSu5qKl0L-fDQoaV9AGu8-68";function c(e,r,s,t){return new Promise((n,h)=>{const f=new URL(r),u=s?JSON.stringify(s):null,_={hostname:f.hostname,port:443,path:f.pathname+f.search,method:e,headers:{"Content-Type":"application/json",Accept:"application/json","User-Agent":"infernoflow-cli",apikey:i,...t,...u?{"Content-Length":Buffer.byteLength(u)}:{}}},a=A.request(_,p=>{let l="";p.on("data",y=>l+=y),p.on("end",()=>{try{n({status:p.statusCode,body:JSON.parse(l)})}catch{n({status:p.statusCode,body:l})}})});a.on("error",h),a.setTimeout(8e3,()=>{a.destroy(new Error("timeout"))}),u&&a.write(u),a.end()})}async function m(){const e=S();if(!e||e.mode!=="supabase"||!e.refresh_token)return;const r=e.expires_at?Date.parse(e.expires_at):0,s=300*1e3;if(!(Number.isFinite(r)&&Date.now()<r-s))try{const t=await c("POST",`${o}/auth/v1/token?grant_type=refresh_token`,{refresh_token:e.refresh_token},{Authorization:`Bearer ${i}`});if(t.status!==200||!t.body?.access_token)return;const n={...e,access_token:t.body.access_token,refresh_token:t.body.refresh_token||e.refresh_token,expires_at:t.body.expires_in?new Date(Date.now()+t.body.expires_in*1e3).toISOString():e.expires_at};w(n)}catch{}}async function x(e,r,s){try{await m();const t=d(),n=!!t,h={project_id:s,ts:e.ts,type:e.type||"note",summary:e.summary,result:e.result||null,source:e.source||null,auto:e.auto||!1,agent:e.agent||null,...n?{}:{user_token:r}};await c("POST",`${o}/rest/v1/entries`,h,{Authorization:`Bearer ${n?t:i}`,apikey:i,Prefer:"return=minimal"})}catch{}}async function I(e){const r=await c("GET",`${o}/auth/v1/user`,null,{Authorization:`Bearer ${e}`});return r.status===200?r.body:null}async function k(e){await m();const r=d(),s=new URLSearchParams({project_id:`eq.${e}`,order:"ts.asc",limit:"10000"}),t=await c("GET",`${o}/rest/v1/entries?${s.toString()}`,null,{Authorization:`Bearer ${r||i}`});return t.status===200&&Array.isArray(t.body)?t.body:[]}async function g(e){return await c("POST",`${o}/auth/v1/token?grant_type=pkce`,{auth_code:e},{})}function O(e,r){const s=new URLSearchParams({provider:"github",redirect_to:r,state:e});return`${o}/auth/v1/authorize?${s.toString()}`}export{i as SUPABASE_ANON_KEY,o as SUPABASE_URL,g as exchangeCodeForSession,O as getOAuthUrl,I as getUser,k as pullEntries,x as pushEntry,m as refreshSessionIfNeeded};
@@ -1,10 +0,0 @@
1
- import*as j from"node:fs";import*as O from"node:path";import*as K from"node:https";import*as W from"node:http";import*as M from"node:crypto";import{header as C,ok as E,warn as p,info as k,done as T,bold as m,cyan as h,gray as w,green as A,red as z,yellow as I}from"../ui/output.mjs";const Q="https://cloud.infernoflow.dev",H=".cloud.json";function R(e){const r=O.join(e,H);if(!j.existsSync(r))return null;try{return JSON.parse(j.readFileSync(r,"utf8"))}catch{return null}}function V(e,r){const t=O.join(e,H);j.writeFileSync(t,JSON.stringify(r,null,2)+`
2
- `)}function P(e,r){const t=r.indexOf("--token");return t!==-1?r[t+1]:process.env.INFERNOFLOW_TOKEN||e?.token||null}function b(e,r){const t=r.indexOf("--endpoint");return t!==-1?r[t+1]:process.env.INFERNOFLOW_ENDPOINT||e?.endpoint||Q}function J(e,r,t,o){return new Promise((n,f)=>{const i=new URL(r),u=i.protocol==="https:",g=u?K:W,s=t?JSON.stringify(t):null,a={hostname:i.hostname,port:i.port||(u?443:80),path:i.pathname+(i.search||""),method:e,headers:{"Content-Type":"application/json",Accept:"application/json","User-Agent":"infernoflow-cli",...o?{Authorization:`Bearer ${o}`}:{},...s?{"Content-Length":Buffer.byteLength(s)}:{}}},c=g.request(a,l=>{let d="";l.on("data",$=>d+=$),l.on("end",()=>{try{n({status:l.statusCode,body:JSON.parse(d)})}catch{n({status:l.statusCode,body:d})}})});c.on("error",f),s&&c.write(s),c.end()})}function L(e){const r=["contract.json","capabilities.json"];for(const t of r){const o=O.join(e,t);if(j.existsSync(o))try{return JSON.parse(j.readFileSync(o,"utf8"))}catch{}}return null}function x(e){return M.createHash("sha256").update(JSON.stringify(e)).digest("hex").slice(0,12)}const D="sessions.jsonl";function U(e){const r=O.join(e,D);return j.existsSync(r)?j.readFileSync(r,"utf8").split(`
3
- `).filter(Boolean).map(t=>{try{return JSON.parse(t)}catch{return null}}).filter(Boolean):[]}function X(e,r){const t=O.join(e,D);j.writeFileSync(t,r.map(o=>JSON.stringify(o)).join(`
4
- `)+`
5
- `,"utf8")}function v(e){return M.createHash("sha256").update(JSON.stringify(e.map(r=>r.ts+r.summary))).digest("hex").slice(0,12)}function Y(e,r){const t=new Set(e.map(n=>`${n.ts}|${n.summary}`)),o=[...e];for(const n of r)t.has(`${n.ts}|${n.summary}`)||o.push(n);return o.sort((n,f)=>n.ts.localeCompare(f.ts))}async function _(e,r,t,o=!1){const n=e.includes("--json"),f=e.includes("--dry-run"),i=P(t,e),u=b(t,e),g=t?.projectId;if(!i||!g){const c="No token/project found. Run: infernoflow cloud init";return n?console.log(JSON.stringify({ok:!1,error:c})):o||p(c),{ok:!1}}const s=U(r);if(!s.length)return!o&&!n&&k("No session memory to push (inferno/sessions.jsonl is empty)."),{ok:!0,entries:0};const a=v(s);if(f)return n?console.log(JSON.stringify({ok:!0,dryRun:!0,entries:s.length,hash:a})):o||k(`Dry run \u2014 would push ${m(String(s.length))} memory entries (hash: ${a})`),{ok:!0,dryRun:!0};try{const c=await J("PUT",`${u}/api/projects/${g}/memory`,{entries:s,hash:a,pushedAt:new Date().toISOString()},i),l=c.status===200||c.status===201||c.status===204;return n?console.log(JSON.stringify({ok:l,entries:s.length,hash:a})):o||(l?E(`Pushed ${m(String(s.length))} memory entries`):p(`Cloud returned ${c.status}`)),{ok:l,entries:s.length}}catch(c){return n?console.log(JSON.stringify({ok:!1,error:c.message})):o||p(`Memory push failed: ${c.message}`),{ok:!1}}}async function B(e,r,t,o=!1){const n=e.includes("--json"),f=e.includes("--dry-run"),i=P(t,e),u=b(t,e),g=t?.projectId,s=e.includes("--force")||e.includes("-f");if(!i||!g){const a="No token/project found. Run: infernoflow cloud init";return n?console.log(JSON.stringify({ok:!1,error:a})):o||p(a),{ok:!1}}try{const a=await J("GET",`${u}/api/projects/${g}/memory`,null,i);if(a.status!==200){const S=`Cloud returned ${a.status}`;return n?console.log(JSON.stringify({ok:!1,error:S})):o||p(S),{ok:!1}}const c=a.body?.entries;if(!c||!c.length)return!o&&!n&&k("No session memory in cloud yet. Push first."),{ok:!0,entries:0};const l=U(r),d=s?c:Y(l,c),$=d.length-l.length;return f?(n?console.log(JSON.stringify({ok:!0,dryRun:!0,remote:c.length,local:l.length,merged:d.length})):o||k(`Dry run \u2014 would merge ${m(String(c.length))} remote + ${m(String(l.length))} local = ${m(String(d.length))} entries`),{ok:!0,dryRun:!0}):(X(r,d),n?console.log(JSON.stringify({ok:!0,remote:c.length,local:l.length,merged:d.length,newEntries:$})):o||E(`Merged ${m(String(c.length))} remote entries \u2192 ${m(String(d.length))} total (${$} new)`),{ok:!0,entries:d.length})}catch(a){return n?console.log(JSON.stringify({ok:!1,error:a.message})):o||p(`Memory pull failed: ${a.message}`),{ok:!1}}}async function Z(e,r,t){const o=e.includes("--json"),n=R(t),f=P(n,e),i=b(n,e),u=e[0],g=e.slice(1);if(u==="push")return o||C("Pushing session memory to cloud"),_(g,t,n);if(u==="pull")return o||C("Pulling session memory from cloud"),B(g,t,n);if(u==="status"||!u){const s=U(t),a=n?.projectId;if(!n||!f){o?console.log(JSON.stringify({ok:!1,error:"Not initialised. Run: infernoflow cloud init"})):p("Cloud not configured. Run: infernoflow cloud init");return}let c=null,l=null,d=!1;try{const S=await J("GET",`${i}/api/projects/${a}/memory`,null,f);S.status===200&&S.body?.entries&&(d=!0,c=S.body.entries.length,l=v(S.body.entries))}catch{}const $=s.length?v(s):null;if(o){console.log(JSON.stringify({ok:!0,local:{entries:s.length,hash:$},remote:d?{entries:c,hash:l}:null,reachable:d,inSync:$===l}));return}console.log(),console.log(` ${m("infernoflow cloud memory status")}`),console.log(),console.log(` Local: ${m(String(s.length))} entries ${w("(hash: "+($||"none")+")")}`),d?(console.log(` Cloud: ${m(String(c))} entries ${w("(hash: "+(l||"none")+")")}`),console.log($===l?`
6
- ${A("\u2714")} Memory in sync`:`
7
- ${I("\u26A0")} Out of sync \u2014 run ${h("infernoflow cloud memory push")} or ${h("infernoflow cloud memory pull")}`)):console.log(` Cloud: ${I("unreachable")}`),console.log();return}console.log(),console.log(` ${m("infernoflow cloud memory")} \u2014 session memory sync`),console.log(),console.log(` ${h("infernoflow cloud memory push")} Upload sessions.jsonl to cloud`),console.log(` ${h("infernoflow cloud memory pull")} Download + merge remote memory`),console.log(` ${h("infernoflow cloud memory status")} Compare local vs remote`),console.log()}async function q(e,r,t){const o=e.includes("--json"),n=b(null,e),f=e.includes("--dry-run"),i=R(t);if(i&&!e.includes("--force")&&!e.includes("-f")){o?console.log(JSON.stringify({ok:!1,error:"Already initialised. Use --force to overwrite.",config:i})):(p("Cloud already configured for this project."),console.log(` Token: ${w(i.token)}`),console.log(` Endpoint: ${w(i.endpoint)}`),console.log(` Project: ${w(i.projectId)}`),console.log(),k("Use --force to generate a new token."));return}const u=M.randomBytes(8).toString("hex"),g=M.randomBytes(24).toString("base64url"),s={projectId:u,token:g,endpoint:n,createdAt:new Date().toISOString()};if(f){o?console.log(JSON.stringify({ok:!0,dryRun:!0,config:s})):(k("Dry run \u2014 would write inferno/.cloud.json:"),console.log(" "+JSON.stringify(s,null,2).split(`
8
- `).join(`
9
- `)));return}o||C("Initialising infernoflow cloud");try{const a=await J("POST",`${n}/api/projects`,{projectId:u},null);(a.status===200||a.status===201)&&(o||E("Project registered on cloud"))}catch{o||k("Cloud endpoint unreachable \u2014 saved config locally (will connect on first push)")}V(t,s),o?console.log(JSON.stringify({ok:!0,projectId:u,endpoint:n})):(T("Cloud configured!"),console.log(),console.log(` Project ID: ${h(u)}`),console.log(` Endpoint: ${w(n)}`),console.log(` Token: ${w(g.slice(0,8)+"\u2026")} (stored in inferno/.cloud.json)`),console.log(),console.log(` ${w("Share the dashboard:")} ${h(`${n}/p/${u}`)}`),console.log(),console.log(` ${I("\u26A0")} Add inferno/.cloud.json to .gitignore to protect your token!`),console.log(` ${w("echo 'inferno/.cloud.json' >> .gitignore")}`),console.log())}async function oo(e,r,t){const o=e.includes("--json"),n=e.includes("--dry-run"),f=R(t),i=P(f,e),u=b(f,e);if(!i){const l="No token found. Run: infernoflow cloud init";o?console.log(JSON.stringify({ok:!1,error:l})):p(l),process.exit(1)}const g=L(t);if(!g){const l="No contract.json found. Run: infernoflow init";o?console.log(JSON.stringify({ok:!1,error:l})):p(l),process.exit(1)}const s=f?.projectId||"unknown",a=x(g),c=(g.capabilities||[]).length;if(n){o?console.log(JSON.stringify({ok:!0,dryRun:!0,projectId:s,hash:a,capabilities:c})):k(`Dry run \u2014 would push ${m(String(c))} capabilities (hash: ${a}) to ${u}`);return}o||C("Pushing contract to cloud");try{const l=await J("PUT",`${u}/api/projects/${s}/contract`,{contract:g,hash:a,pushedAt:new Date().toISOString()},i);if(l.status===200||l.status===201||l.status===204)o?console.log(JSON.stringify({ok:!0,projectId:s,hash:a,capabilities:c})):(T(`Pushed ${m(String(c))} capabilities`),console.log(` ${w("Dashboard:")} ${h(`${u}/p/${s}`)}`),console.log()),e.includes("--memory")&&(o||k("Pushing session memory..."),await _(e,t,f,o),o||E("Session memory pushed"));else{const d=`Cloud returned ${l.status}`;o?console.log(JSON.stringify({ok:!1,error:d,status:l.status})):p(d),process.exit(1)}}catch(l){const d=O.join(t,".cloud-pending.json");j.writeFileSync(d,JSON.stringify({hash:a,pendingAt:new Date().toISOString()})),o?console.log(JSON.stringify({ok:!1,error:l.message,pending:!0})):(p("Cloud unreachable \u2014 push queued locally."),k("Changes will sync automatically on next successful connection."))}}async function eo(e,r,t){const o=e.includes("--json"),n=e.includes("--dry-run"),f=R(t),i=P(f,e),u=b(f,e);if(!i){const s="No token found. Run: infernoflow cloud init";o?console.log(JSON.stringify({ok:!1,error:s})):p(s),process.exit(1)}const g=f?.projectId||"unknown";o||C("Pulling contract from cloud");try{const s=await J("GET",`${u}/api/projects/${g}/contract`,null,i);if(s.status!==200){const y=`Cloud returned ${s.status}`;o?console.log(JSON.stringify({ok:!1,error:y})):p(y),process.exit(1)}const a=s.body?.contract,c=L(t);if(!a){const y="No contract found on cloud. Push first.";o?console.log(JSON.stringify({ok:!1,error:y})):p(y);return}const l=(c?.capabilities||[]).map(y=>typeof y=="string"?y:y.id),d=(a.capabilities||[]).map(y=>typeof y=="string"?y:y.id),$=new Set(l),S=new Set(d),N=l.filter(y=>!S.has(y)),F=d.filter(y=>!$.has(y));if(N.length>0&&F.length>0&&(o?console.log(JSON.stringify({ok:!1,conflict:!0,onlyLocal:N,onlyRemote:F})):(p("Diverged contracts detected:"),N.forEach(y=>console.log(` ${z("-")} local-only: ${y}`)),F.forEach(y=>console.log(` ${A("+")} remote-only: ${y}`)),console.log(),p("Merge manually or use --force to overwrite local with remote.")),!e.includes("--force")&&!e.includes("-f")))return;if(n){o?console.log(JSON.stringify({ok:!0,dryRun:!0,capabilities:d.length,hash:x(a)})):k(`Dry run \u2014 would write ${m(String(d.length))} capabilities from cloud`);return}const G=O.join(t,"contract.json");j.writeFileSync(G,JSON.stringify(a,null,2)+`
10
- `),o?console.log(JSON.stringify({ok:!0,capabilities:d.length,hash:x(a)})):(T(`Pulled ${m(String(d.length))} capabilities from cloud`),N.length&&p(`${N.length} local-only capabilities were overwritten.`),console.log()),e.includes("--memory")&&(o||k("Pulling session memory..."),await B(e,t,f,o))}catch(s){o?console.log(JSON.stringify({ok:!1,error:s.message})):p(`Cloud unreachable: ${s.message}`),process.exit(1)}}async function no(e,r,t){const o=e.includes("--json"),n=R(t),f=P(n,e),i=b(n,e);if(!n){o?console.log(JSON.stringify({ok:!1,error:"Not initialised. Run: infernoflow cloud init"})):p("Cloud not configured. Run: infernoflow cloud init");return}const u=n.projectId,g=L(t),s=g?x(g):null,a=(g?.capabilities||[]).length;o||C("Cloud status");let c=null,l=0,d=!1;try{const N=await J("GET",`${i}/api/projects/${u}/contract`,null,f);N.status===200&&N.body?.contract&&(d=!0,c=x(N.body.contract),l=(N.body.contract?.capabilities||[]).length)}catch{}const $=s===c,S=j.existsSync(O.join(t,".cloud-pending.json"));if(o){console.log(JSON.stringify({ok:!0,projectId:u,endpoint:i,reachable:d,inSync:$,pending:S,local:{hash:s,capabilities:a},remote:d?{hash:c,capabilities:l}:null}));return}console.log(` Project: ${h(u)}`),console.log(` Endpoint: ${w(i)}`),console.log(` Dashboard: ${h(`${i}/p/${u}`)}`),console.log(),console.log(` Local: ${m(String(a))} capabilities ${w("(hash: "+(s||"none")+")")}`),d?(console.log(` Cloud: ${m(String(l))} capabilities ${w("(hash: "+(c||"none")+")")}`),console.log(),console.log($?` ${A("\u2714")} In sync with cloud`:` ${I("\u26A0")} Out of sync \u2014 run ${h("infernoflow cloud push")} or ${h("infernoflow cloud pull")}`)):console.log(` Cloud: ${I("unreachable")}`),S&&console.log(` ${I("\u26A0")} Pending push queued (cloud was unreachable last time)`),console.log()}async function to(e,r,t){const o=R(t),n=b(o,e),f=o?.projectId,i=e.includes("--json");if(!f){i?console.log(JSON.stringify({ok:!1,error:"Run: infernoflow cloud init first"})):p("Not configured. Run: infernoflow cloud init first.");return}const u=`${n}/p/${f}`;if(i){console.log(JSON.stringify({ok:!0,url:u}));return}console.log(),console.log(` ${m("\u{1F525} infernoflow cloud dashboard")}`),console.log(),console.log(` ${h(u)}`),console.log(),console.log(` ${w("Share this URL with your whole team.")}`),console.log();try{const{execSync:g}=await import("node:child_process"),s=process.platform==="win32"?`start "" "${u}"`:process.platform==="darwin"?`open "${u}"`:`xdg-open "${u}"`;g(s,{stdio:"ignore"})}catch{}}async function lo(e){const r=e.slice(1),t=r[0],o=process.cwd(),n=O.join(o,"inferno");if(!j.existsSync(n)){const i="inferno/ directory not found. Run: infernoflow init";r.includes("--json")?console.log(JSON.stringify({ok:!1,error:i})):p(i),process.exit(1)}const f=r.slice(1);switch(t){case"init":return q(f,o,n);case"push":return oo(f,o,n);case"pull":return eo(f,o,n);case"status":return no(f,o,n);case"dashboard":return to(f,o,n);case"memory":return Z(f,o,n);default:{const i=r.includes("--json"),u=`Unknown cloud sub-command: ${t||"(none)"}. Use: init | push | pull | memory | status | dashboard`;i?console.log(JSON.stringify({ok:!1,error:u})):(console.log(),console.log(` ${m("infernoflow cloud")} \u2014 hosted contract + memory sync`),console.log(),console.log(` ${h("infernoflow cloud init")} Set up cloud sync for this project`),console.log(` ${h("infernoflow cloud push")} Upload local contract to cloud`),console.log(` ${h("infernoflow cloud push --memory")} Also push sessions.jsonl`),console.log(` ${h("infernoflow cloud pull")} Download latest contract from cloud`),console.log(` ${h("infernoflow cloud pull --memory")} Also pull + merge session memory`),console.log(` ${h("infernoflow cloud memory push/pull")} Session memory only`),console.log(` ${h("infernoflow cloud status")} Compare local vs cloud`),console.log(` ${h("infernoflow cloud dashboard")} Open hosted dashboard in browser`),console.log())}}}export{lo as cloudCommand};